Skip to content
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e32a88a
reduce qualified udt names in contract info output
leighmcculloch Sep 3, 2026
14dd574
use soroban-spec reduce instead of cli duplicate
leighmcculloch Sep 4, 2026
be26582
reduce spec type names on contract build
leighmcculloch Sep 4, 2026
3c430e3
show raw spec type names in contract info
leighmcculloch Sep 8, 2026
301e40e
remove comment
leighmcculloch Sep 23, 2026
85ed88e
format the reduce spec test path
leighmcculloch Sep 23, 2026
faa89be
merge the 2751 branch with main
leighmcculloch Sep 24, 2026
428bab3
merge the 2751 pin bump
leighmcculloch Sep 24, 2026
f6f15d2
merge the 2751 pin bump
leighmcculloch Sep 24, 2026
cfb958d
merge the 2751 pin bump
leighmcculloch Sep 24, 2026
4b4c32d
merge the 2751 pin bump
leighmcculloch Sep 24, 2026
5d8677a
merge the 2751 pin bump
leighmcculloch Sep 24, 2026
df7c51a
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
b69b822
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
3944ba7
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
7b17557
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
dd7a843
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
a2ca371
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
1a7e40d
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
836d42d
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
2d58326
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
0138676
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
4fee183
merge the 2751 pin bump
leighmcculloch Sep 25, 2026
9691f0a
merge update-xdr-and-sdk-crates into patch-xdr-and-spec-crates
leighmcculloch Sep 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions cmd/soroban-cli/src/commands/contract/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl Cmd {

self.inject_meta(&target_file_path)?;
Self::filter_spec(&target_file_path)?;
Self::reduce_spec(&print, &p.name, &target_file_path)?;
Comment thread
leighmcculloch marked this conversation as resolved.

let final_path = if let Some(out_dir) = &self.out_dir {
fs::create_dir_all(out_dir).map_err(Error::CreatingOutDir)?;
Expand Down Expand Up @@ -569,6 +570,65 @@ impl Cmd {
fs::write(target_file_path, new_wasm).map_err(Error::WritingWasmFile)
}

/// Reduces user-defined type names in the contract spec to their simple
/// form, rewriting the `contractspecv0` section in place.
///
/// Contract specs may name user-defined types by their fully qualified path
/// (e.g. `my_contract::inner::State`). This follows each to its simple name
/// (e.g. `State`), rewriting every reference, so the on-chain spec and every
/// downstream tool see the short names. Names that would collide are
/// disambiguated with a numeric suffix, which is warned about.
///
/// Runs after `filter_spec` so only the entries that survive shaking are
/// reduced, but is otherwise independent of spec shaking.
fn reduce_spec(print: &Print, name: &str, target_file_path: &PathBuf) -> Result<(), Error> {
use soroban_spec_tools::contract::Spec;
use soroban_spec_tools::wasm::replace_custom_section;

let wasm_bytes = fs::read(target_file_path).map_err(Error::ReadingWasmFile)?;
let spec = Spec::new(&wasm_bytes)?;

let reduced = soroban_spec::reduce::reduce(&spec.spec);

// If every name was already simple, leave the wasm untouched.
if reduced.renames().all(|r| !r.renamed()) {
return Ok(());
}
Comment on lines +594 to +596

let collisions: Vec<_> = reduced.renames().filter(|r| r.collision()).collect();
if !collisions.is_empty() {
use std::fmt::Write as _;
let mut msg = format!(
"{name}: reduced type names collided and were disambiguated with a numeric suffix:"
);
for rename in collisions {
let _ = write!(
msg,
"\n {} -> {}",
String::from_utf8_lossy(&rename.from),
String::from_utf8_lossy(&rename.to),
Comment on lines +608 to +609
);
}
print.warnln(msg);
}

// Encode the reduced entries and replace the contractspecv0 section.
let mut reduced_xdr = Vec::new();
let mut writer = Limited::new(
Cursor::new(&mut reduced_xdr),
Limits::depth(XDR_DEPTH_LIMIT),
);
for entry in reduced.into_entries() {
entry.write_xdr(&mut writer)?;
}

let new_wasm = replace_custom_section(&wasm_bytes, "contractspecv0", &reduced_xdr)
.map_err(|e| Error::WasmParsing(e.to_string()))?;

fs::remove_file(target_file_path).map_err(Error::DeletingArtifact)?;
fs::write(target_file_path, new_wasm).map_err(Error::WritingWasmFile)
}

fn encoded_new_meta(&self) -> Result<Vec<u8>, Error> {
let mut new_meta: Vec<ScMetaEntry> = Vec::new();

Expand Down Expand Up @@ -970,6 +1030,45 @@ mod tests {
assert!(Cmd::try_parse_from(["build", "--pull"]).is_err());
}

#[test]
fn reduce_spec_shortens_qualified_names_in_wasm() {
use soroban_spec_tools::contract::Spec;
use soroban_spec_tools::wasm::replace_custom_section;
use stellar_xdr::{ScSpecEntry, ScSpecUdtStructV0, VecM};

// A spec with a single struct whose name is fully qualified.
let entry = ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 {
doc: StringM::default(),
lib: StringM::default(),
name: "mycrate::mymod::MyType".to_string().try_into().unwrap(),
fields: VecM::default(),
});
let mut spec_xdr = Vec::new();
entry
.write_xdr(&mut Limited::new(
Cursor::new(&mut spec_xdr),
Limits::depth(XDR_DEPTH_LIMIT),
))
.unwrap();

// 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()));
fs::write(&path, &wasm).unwrap();

Cmd::reduce_spec(&Print::new(true), "pkg", &path).unwrap();

let out = fs::read(&path).unwrap();
fs::remove_file(&path).ok();
let spec = Spec::new(&out).unwrap();

assert_eq!(spec.spec.len(), 1);
let ScSpecEntry::UdtStructV0(s) = &spec.spec[0] else {
panic!("expected a struct entry, got {:?}", spec.spec[0]);
};
assert_eq!(s.name.to_vec(), b"MyType".to_vec());
}

#[test]
fn serialize_command_shell_escapes_args_with_metacharacters() {
let raw_arg = "--manifest-path=/path/to/contract;touch PWNED;#/Cargo.toml";
Expand Down
Loading