diff --git a/Cargo.lock b/Cargo.lock index 28ed659fc..4e1b1059f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5530,7 +5530,7 @@ version = "28.0.0" [[package]] name = "soroban-ledger-snapshot" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "serde", "serde_json", @@ -5543,7 +5543,7 @@ dependencies = [ [[package]] name = "soroban-sdk" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "arbitrary", "bytes-lit", @@ -5568,7 +5568,7 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "darling 0.20.11", "heck 0.5.0", @@ -5586,7 +5586,7 @@ dependencies = [ [[package]] name = "soroban-spec" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "base64 0.22.1", "stellar-xdr", @@ -5597,7 +5597,7 @@ dependencies = [ [[package]] name = "soroban-spec-rust" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "prettyplease", "proc-macro2", @@ -5695,7 +5695,7 @@ dependencies = [ [[package]] name = "soroban-token-sdk" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "soroban-sdk", ] @@ -5703,7 +5703,7 @@ dependencies = [ [[package]] name = "soroban-token-spec" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "soroban-sdk", "soroban-token-sdk", @@ -5752,7 +5752,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stellar-asset-spec" version = "28.0.0-rc.2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=d131d67b5ebbf1346fabdc6e6a86141e25ece720#d131d67b5ebbf1346fabdc6e6a86141e25ece720" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=ce45d9f19c692d3812645f11119971a0fa33dd84#ce45d9f19c692d3812645f11119971a0fa33dd84" dependencies = [ "soroban-sdk", "soroban-token-sdk", diff --git a/Cargo.toml b/Cargo.toml index e2930a13f..f52409be0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,36 +52,38 @@ version = "28.0.0-rc.1" # Dependencies from the rs-soroban-sdk repo, pinned to # https://github.com/stellar/rs-soroban-sdk/pull/1970, which names user-defined -# types in contract specs by their fully qualified path: +# types in contract specs by their fully qualified path, and the PR stacked on +# it, which stops marking types for spec shaking and shakes them by +# reachability instead: [workspace.dependencies.soroban-spec] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" [workspace.dependencies.soroban-spec-rust] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" [workspace.dependencies.soroban-sdk] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" [workspace.dependencies.soroban-token-sdk] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" [workspace.dependencies.stellar-asset-spec] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" [workspace.dependencies.soroban-ledger-snapshot] version = "28.0.0-rc.1" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "d131d67b5ebbf1346fabdc6e6a86141e25ece720" +rev = "ce45d9f19c692d3812645f11119971a0fa33dd84" # Dependencies from the rs-stellar-rpc-client repo: [workspace.dependencies.soroban-rpc] diff --git a/Makefile b/Makefile index 9f1ddbd4e..c17377848 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,11 @@ build: cargo build build-test-wasms: - SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_REDUCING_FULL_NAMES=1 cargo build --package 'test_*' --profile test-wasms --target wasm32v1-none + # Announce the same spec shaking support `stellar contract build` announces, + # so the test contracts build against any soroban-sdk this repo patches in. + SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 \ + SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V3=1 \ + cargo build --package 'test_*' --profile test-wasms --target wasm32v1-none build-test: build-test-wasms build-fixtures install diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index 0e1605269..c2e6efe87 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -512,7 +512,7 @@ fn filter_and_dedup_spec_removes_duplicates() { .unwrap(), }); - // Build markers for the struct so it passes the filter + // Build markers for the struct so it passes the version 2 filter let mut markers = std::collections::HashSet::new(); markers.insert(soroban_spec::shaking::generate_marker_for_entry( &used_struct, @@ -527,7 +527,8 @@ fn filter_and_dedup_spec_removes_duplicates() { used_struct.clone(), ]; - let result_xdr = filter_and_dedup_spec(entries, &markers).unwrap(); + let result_xdr = + filter_and_dedup_spec(entries, &markers, soroban_spec::shaking::Version::V2).unwrap(); // Parse back the entries from the XDR let result_entries: Vec = @@ -556,8 +557,13 @@ fn build_with_spec_shaking_has_feature_meta() { let version = soroban_spec::shaking::spec_shaking_version_for_meta(&meta); + // The fixture builds against a published soroban-sdk, which records + // version 2. The workspace's `[patch.crates-io]` does not reach a contract + // built in a temp dir, so this covers the version 2 rules end to end, and + // the SDK's own test contract covers version 3. assert_eq!( - version, 2, + version, + soroban_spec::shaking::Version::V2, "contractmeta should indicate spec shaking version 2" ); } @@ -736,10 +742,10 @@ fn parent_path() -> String { } fn with_flags(expected: &str) -> String { - // Serialized in sorted key order, so REDUCING_FULL_NAMES precedes SPEC_SHAKING_V2. + // Serialized in sorted key order, so SPEC_SHAKING_V2 precedes SPEC_SHAKING_V3. const ENV_VARS: &str = concat!( - "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_REDUCING_FULL_NAMES=1 ", - "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1" + "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 ", + "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V3=1" ); let cargo_home = home::cargo_home().unwrap(); diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs index ab0e5f57a..05c574c00 100644 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ b/cmd/soroban-cli/src/commands/contract/build.rs @@ -342,14 +342,14 @@ impl Cmd { cmd.env("CARGO_BUILD_RUSTFLAGS", rustflags); } - // Set env var to inform the SDK that this CLI supports spec - // optimization using markers. + // Set env vars to inform the SDK that this CLI supports spec + // optimization. Each var names a set of shaking rules, and this + // CLI announces every set it can apply, because the SDK refuses to + // build a contract whose rules the build system has not claimed. + // Which rules a given contract is shaken by comes from the version + // that contract records in its meta. cmd.env("SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2", "1"); - - // Set env var to inform the SDK that this CLI reduces fully - // qualified user-defined type names in the spec down to their - // simple names. - cmd.env("SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_REDUCING_FULL_NAMES", "1"); + cmd.env("SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V3", "1"); let cmd_str = serialize_command(&cmd); @@ -503,14 +503,17 @@ impl Cmd { /// Filters unused types and events from the contract spec. /// - /// This removes: - /// - Type definitions that are not referenced by any function - /// - Events that don't have corresponding markers in the WASM data section - /// (events that are defined but never published) + /// The contract records which spec shaking version it was built with, and + /// that version says which entries carry a marker and so what a missing + /// marker means. Read it and shake by its rules rather than the newest + /// known: a version 2 contract marks every used type, so markers alone say + /// what is used, while a version 3 contract marks only the events it + /// publishes and the errors it panics with, and every other type is + /// settled by following the references to it. /// - /// The SDK embeds markers in the data section for types/events that are - /// actually used. These markers survive dead code elimination, so we can - /// detect which spec entries are truly needed. + /// A version this CLI does not recognise reads as version 1, and a + /// version 1 contract carries no markers at all, so in both cases there is + /// nothing to shake by and the spec is left alone. fn filter_spec(target_file_path: &PathBuf) -> Result<(), Error> { use soroban_spec_tools::contract::Spec; use soroban_spec_tools::wasm::replace_custom_section; @@ -520,17 +523,19 @@ impl Cmd { // Parse the spec from the wasm let spec = Spec::new(&wasm_bytes)?; - // Check if the contract meta indicates spec shaking v2 is enabled. - if soroban_spec::shaking::spec_shaking_version_for_meta(&spec.meta) != 2 { + // Read the version the contract was built with, which selects the + // rules below. Nothing to shake by at version 1, so leave it alone. + let version = soroban_spec::shaking::spec_shaking_version_for_meta(&spec.meta); + if version == soroban_spec::shaking::Version::V1 { return Ok(()); } // Extract markers from the WASM data section let markers = soroban_spec::shaking::find_all(&wasm_bytes); - // Filter spec entries (types, events) based on markers, and + // Filter spec entries (types, events) by that version's rules, and // deduplicate any exact duplicate entries. - let filtered_xdr = filter_and_dedup_spec(spec.spec.clone(), &markers)?; + let filtered_xdr = filter_and_dedup_spec(spec.spec.clone(), &markers, version)?; // Replace the contractspecv0 section with the filtered version let new_wasm = replace_custom_section(&wasm_bytes, "contractspecv0", &filtered_xdr) @@ -926,15 +931,17 @@ fn check_overflow_checks(doc: &toml_edit::DocumentMut, profile: &str) -> Result< } } -/// Filters spec entries based on markers and deduplicates exact duplicates. +/// Filters spec entries down to those the contract needs, by the rules of the +/// spec shaking version it was built with, and deduplicates exact duplicates. /// -/// Functions are always kept. Other entries (types, events) are kept only if a -/// matching marker exists. Exact duplicate entries (identical XDR) are collapsed +/// Which entries survive is decided by `soroban_spec::shaking::filter` for the +/// given version. Exact duplicate entries (identical XDR) are then collapsed /// to a single occurrence. #[allow(clippy::implicit_hasher)] pub fn filter_and_dedup_spec( entries: Vec, markers: &HashSet, + version: soroban_spec::shaking::Version, ) -> Result, Error> { let mut seen = HashSet::new(); let mut filtered_xdr = Vec::new(); @@ -942,7 +949,7 @@ pub fn filter_and_dedup_spec( Cursor::new(&mut filtered_xdr), Limits::depth(XDR_DEPTH_LIMIT), ); - for entry in soroban_spec::shaking::filter(entries, markers) { + for entry in soroban_spec::shaking::filter(entries, markers, version) { let entry_xdr = entry.to_xdr(Limits::depth(XDR_DEPTH_LIMIT))?; if seen.insert(entry_xdr) { entry.write_xdr(&mut writer)?; @@ -1024,8 +1031,7 @@ mod tests { // Embed the spec in a minimal (empty) wasm module and write it out. let wasm = replace_custom_section(b"\0asm\x01\0\0\0", "contractspecv0", &spec_xdr).unwrap(); - let path = - env::temp_dir().join(format!("reduce_spec_test_{}.wasm", std::process::id())); + let path = env::temp_dir().join(format!("reduce_spec_test_{}.wasm", std::process::id())); fs::write(&path, &wasm).unwrap(); Cmd::reduce_spec(&Print::new(true), "pkg", &path).unwrap();