Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
c3632eb
make contract spec lazy-loaded on type usage
leighmcculloch Jan 9, 2026
23993bd
implement lazy spec loading with markers
leighmcculloch Jan 9, 2026
1b2a428
replace xdr encoding with magic prefix in spec markers
leighmcculloch Jan 9, 2026
63bae73
simplify spec marker encoding to use sha256 hash
leighmcculloch Jan 9, 2026
99ff52e
rename __include_spec to __include_spec_marker
leighmcculloch Jan 9, 2026
a4d4c7b
refactor spec marker generation and usage
leighmcculloch Jan 9, 2026
3b7ef81
Simplify IncludeSpecMarker trait and rename from IncludeSpec
leighmcculloch Jan 9, 2026
1028afc
simplify spec marker hash computation
leighmcculloch Jan 9, 2026
437eb77
simplify spec marker generation and remove unused constants
leighmcculloch Jan 9, 2026
11ef622
extract link_section_attr into separate variable
leighmcculloch Jan 9, 2026
c708308
rename link_section_attr to export_gen
leighmcculloch Jan 9, 2026
9e1d20f
inline spec export attribute and simplify spec_xdr call
leighmcculloch Jan 9, 2026
5b41e20
add conditional spec marker attribute for events
leighmcculloch Jan 9, 2026
1a15573
remove soroban-token-sdk dependency from empty test
leighmcculloch Jan 9, 2026
09b83d0
token test
leighmcculloch Jan 9, 2026
652e56f
spec test
leighmcculloch Jan 9, 2026
0d5d52a
remove token test directory
leighmcculloch Jan 9, 2026
bd1698e
remove test_token package from lock file
leighmcculloch Jan 9, 2026
cb50f07
add check_c function and import ErrorC
leighmcculloch Jan 9, 2026
ca04d3b
use #[used] instead of volatile read
leighmcculloch Jan 9, 2026
1d38b9e
update contract wasm and spec markers
leighmcculloch Jan 9, 2026
3e7bbc2
replace used attribute with volatile read for markers
leighmcculloch Jan 9, 2026
4788682
collect field types for spec marker inclusion
leighmcculloch Jan 13, 2026
3320fa3
Merge main into spec-markers
leighmcculloch Jan 13, 2026
41ece64
update contract spec marker hashes
leighmcculloch Jan 13, 2026
de9e494
add bn254 crypto type markers
leighmcculloch Jan 13, 2026
dfe60be
typo
leighmcculloch Jan 13, 2026
265daec
deduplicate variant field types in spec marker generation
leighmcculloch Jan 13, 2026
751e0a0
Merge branch 'main' into spec-markers
leighmcculloch Jan 30, 2026
2775493
add warning when building wasm without spec optimization
leighmcculloch Jan 30, 2026
3c57623
move spec marker to soroban-spec crate
leighmcculloch Jan 30, 2026
e0ba97d
add SpecMarker type alias and shorten const name
leighmcculloch Jan 30, 2026
7e6c3db
rename generate to generate_for_xdr and add marker functions
leighmcculloch Jan 30, 2026
326099e
extract spec marker impl generation to shared module
leighmcculloch Jan 30, 2026
15c0d29
gate spec markers behind experimental_spec_resolver_v2 feature
leighmcculloch Feb 6, 2026
7e60b00
Merge branch 'main' into spec-markers
leighmcculloch Feb 22, 2026
9f85bcc
Merge branch 'main' into spec-markers
leighmcculloch Feb 27, 2026
de063be
update warning message for missing or old stellar-cli
leighmcculloch Feb 27, 2026
7708b53
change spec marker prefix from SpEc to SpEcV1
leighmcculloch Feb 27, 2026
77b5f38
rename feature to experimental_spec_shaking_v2
leighmcculloch Mar 2, 2026
cb27875
replace cargo:error with panic for spec shaking warning
leighmcculloch Mar 2, 2026
e47a55d
add doc cfg attribute to IncludeSpecMarker
leighmcculloch Mar 2, 2026
4f26c95
rename IncludeSpecMarker to SpecShakingMarker
leighmcculloch Mar 2, 2026
57f5040
add contractmeta entry for spec shaking v2 feature flag
leighmcculloch Mar 2, 2026
ae4ed17
rename `generate_impl` to `generate_marker_impl`
leighmcculloch Mar 2, 2026
d69ca9b
Prompt is too long
leighmcculloch Mar 2, 2026
b6e838c
delete spec_marking_v2_test.md
leighmcculloch Mar 2, 2026
a6fa69b
add docs for experimental_spec_shaking_v2 feature
leighmcculloch Mar 2, 2026
8224652
update stellar-cli version requirement to v25.2.0
leighmcculloch Mar 2, 2026
fffca3d
add spec for non-pub contracttype/contracterror with spec shaking
leighmcculloch Mar 2, 2026
c2d0dc6
add spec_shaking_v1 test crate
leighmcculloch Mar 2, 2026
556dba7
add tuple and non-contract fn type tests
leighmcculloch Mar 2, 2026
c90abf6
add option and result tests
leighmcculloch Mar 2, 2026
409fe23
revert unnecessary contract_docs.rs changes
leighmcculloch Mar 2, 2026
62236fe
rename env var to include experimental prefix
leighmcculloch Mar 2, 2026
5f658bc
fix event generate tests to expect export = false
leighmcculloch Mar 2, 2026
e745b20
update expanded test outputs
leighmcculloch Mar 2, 2026
53852a5
implement SpecShakingMarker for crate::Error
leighmcculloch Mar 3, 2026
3b64a8c
assert fixed marker value in spec marker test
leighmcculloch Mar 3, 2026
2eb2c7f
extract spec gen and marker logic into shared helper
leighmcculloch Mar 3, 2026
35d7493
Revert "extract spec gen and marker logic into shared helper"
leighmcculloch Mar 3, 2026
2a4722b
update readme and fuzz lockfile
leighmcculloch Mar 3, 2026
700a427
snapshots
leighmcculloch Mar 3, 2026
dcf0f03
rework tests
leighmcculloch Mar 3, 2026
6fed342
use byte string literal in marker assertion
leighmcculloch Mar 3, 2026
88bf5c3
rename env var to remove experimental prefix
leighmcculloch Mar 3, 2026
89124f7
update expand-tests
leighmcculloch Mar 3, 2026
eeb0224
rename spec_wasm test package to spec_import
leighmcculloch Mar 3, 2026
ebc4cbc
rename test_spec_wasm files to test_spec_import
leighmcculloch Mar 3, 2026
af27378
Update soroban-spec-rust/src/types.rs
leighmcculloch Mar 3, 2026
085ea00
Update soroban-sdk/src/lib.rs
leighmcculloch Mar 3, 2026
8401389
Update soroban-sdk/build.rs
leighmcculloch Mar 3, 2026
95fe2ec
Update soroban-spec/src/shaking.rs
leighmcculloch Mar 3, 2026
4dd03a8
Update soroban-sdk/src/spec_shaking.rs
leighmcculloch Mar 3, 2026
fb2108a
Update soroban-sdk-macros/src/shaking.rs
leighmcculloch Mar 3, 2026
11995a1
Update soroban-spec/src/shaking.rs
leighmcculloch Mar 3, 2026
36af8eb
test CI workflows with experimental_spec_shaking_v2 matrix
leighmcculloch Mar 3, 2026
601cfa3
narrow sed patterns to path-patched soroban-sdk entries
leighmcculloch Mar 3, 2026
5af448c
change filter to return an iterator
leighmcculloch Mar 3, 2026
6121af0
update test comments to reflect 14-byte marker format
leighmcculloch Mar 3, 2026
5cd7f3b
expand experimental_spec_shaking_v2 feature doc
leighmcculloch Mar 4, 2026
8055e79
add spec shaking version detection via contract meta
leighmcculloch Mar 4, 2026
fd34e32
update test WASM with new contract meta key
leighmcculloch Mar 5, 2026
73c3ba5
fix typo in TryIntoVal doc comment
leighmcculloch Mar 5, 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
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 61 additions & 12 deletions soroban-sdk-macros/src/derive_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use stellar_xdr::{
ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0, StringM, VecM, WriteXdr, SCSYMBOL_LIMIT,
};

use crate::{doc::docs_from_attrs, map_type::map_type, DEFAULT_XDR_RW_LIMITS};
use crate::{doc::docs_from_attrs, map_type::map_type, spec_marker, DEFAULT_XDR_RW_LIMITS};

pub fn derive_type_enum(
path: &Path,
Expand All @@ -30,14 +30,15 @@ pub fn derive_type_enum(
format!("enum {} must have variants", enum_ident),
));
}
let (spec_cases, case_name_str_lits, try_froms, try_intos, try_from_xdrs, into_xdrs): (
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
) = variants
let (
spec_cases,
case_name_str_lits,
variant_field_types,
try_froms,
try_intos,
try_from_xdrs,
into_xdrs,
): (Vec<_>, Vec<_>, Vec<Vec<_>>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = variants
.iter()
.enumerate()
.map(|(case_num, variant)| {
Expand Down Expand Up @@ -75,6 +76,10 @@ pub fn derive_type_enum(
}
_ => {}
}

// Collect field types for IncludeSpecMarker
let field_types: Vec<_> = variant.fields.iter().map(|f| &f.ty).collect();

let is_unit_variant = variant.fields == Fields::Unit;
if !is_unit_variant {
let VariantTokens {
Expand All @@ -97,6 +102,7 @@ pub fn derive_type_enum(
(
spec_case,
case_name_str_lit,
field_types,
try_from,
try_into,
try_from_xdr,
Expand All @@ -121,6 +127,7 @@ pub fn derive_type_enum(
(
spec_case,
case_name_str_lit,
field_types,
try_from,
try_into,
try_from_xdr,
Expand All @@ -136,15 +143,21 @@ pub fn derive_type_enum(
return quote! { #(#compile_errors)* };
}

// Generated code spec.
let spec_gen = if spec {
// Compute spec XDR once if spec is enabled.
let spec_xdr = if spec {
let spec_entry = ScSpecEntry::UdtUnionV0(ScSpecUdtUnionV0 {
doc: docs_from_attrs(attrs),
lib: lib.as_deref().unwrap_or_default().try_into().unwrap(),
name: enum_ident.to_string().try_into().unwrap(),
cases: spec_cases.try_into().unwrap(),
});
let spec_xdr = spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap();
Some(spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap())
} else {
None
};

// Generated code spec.
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
let spec_xdr_len = spec_xdr.len();
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
Expand All @@ -162,10 +175,46 @@ pub fn derive_type_enum(
None
};

// IncludeSpecMarker impl - only generated when spec is true.
// Types with export=false should not be used at external boundaries.
let include_spec_impl = if let Some(ref spec_xdr) = spec_xdr {
// Create a marker that identifies this spec entry. The marker is a byte array
// in the data section with a distinctive pattern: "SpEc" + truncated SHA256.
// Post-build tools can scan the data section for "SpEc" markers and match
// against specs in contractspecv0.
let marker = spec_marker::spec_marker(spec_xdr);
let marker_lit = proc_macro2::Literal::byte_string(&marker);
let marker_len = marker.len();
// Flatten all variant field types for include_spec_marker calls
let all_field_types: Vec<_> = variant_field_types.iter().flatten().collect();
Some(quote! {
impl #path::IncludeSpecMarker for #enum_ident {
#[doc(hidden)]
#[inline(always)]
fn include_spec_marker() {
// Include markers for nested variant field types.
#(<#all_field_types as #path::IncludeSpecMarker>::include_spec_marker();)*
#[cfg(target_family = "wasm")]
{
// Marker in data section. Post-build tools can scan for "SpEc"
// patterns and match against specs in contractspecv0.
static MARKER: [u8; #marker_len] = *#marker_lit;
// Volatile read prevents DCE within live function.
let _ = unsafe { ::core::ptr::read_volatile(MARKER.as_ptr()) };
}
}
}
})
} else {
None
};

// Output.
let mut output = quote! {
#spec_gen

#include_spec_impl

impl #path::TryFromVal<#path::Env, #path::Val> for #enum_ident {
type Error = #path::ConversionError;
#[inline(always)]
Expand Down
46 changes: 42 additions & 4 deletions soroban-sdk-macros/src/derive_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Pat

use stellar_xdr::{ScSpecEntry, ScSpecUdtEnumCaseV0, WriteXdr};

use crate::{doc::docs_from_attrs, DEFAULT_XDR_RW_LIMITS};
use crate::{doc::docs_from_attrs, spec_marker, DEFAULT_XDR_RW_LIMITS};

// TODO: Add conversions to/from ScVal types.

Expand Down Expand Up @@ -65,15 +65,21 @@ pub fn derive_type_enum_int(
return quote! { #(#compile_errors)* };
}

// Generated code spec.
let spec_gen = if spec {
// Compute spec XDR once if spec is enabled.
let spec_xdr = if spec {
let spec_entry = ScSpecEntry::UdtEnumV0(ScSpecUdtEnumV0 {
doc: docs_from_attrs(attrs),
lib: lib.as_deref().unwrap_or_default().try_into().unwrap(),
name: enum_ident.to_string().try_into().unwrap(),
cases: spec_cases.try_into().unwrap(),
});
let spec_xdr = spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap();
Some(spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap())
} else {
None
};

// Generated code spec.
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
let spec_xdr_len = spec_xdr.len();
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
Expand All @@ -91,10 +97,42 @@ pub fn derive_type_enum_int(
None
};

// IncludeSpecMarker impl - only generated when spec is true.
// Types with export=false should not be used at external boundaries.
let include_spec_impl = if let Some(ref spec_xdr) = spec_xdr {
// Create a marker that identifies this spec entry. The marker is a byte array
// in the data section with a distinctive pattern: "SpEc" + truncated SHA256.
// Post-build tools can scan the data section for "SpEc" markers and match
// against specs in contractspecv0.
let marker = spec_marker::spec_marker(spec_xdr);
let marker_lit = proc_macro2::Literal::byte_string(&marker);
let marker_len = marker.len();
Some(quote! {
impl #path::IncludeSpecMarker for #enum_ident {
#[doc(hidden)]
#[inline(always)]
fn include_spec_marker() {
#[cfg(target_family = "wasm")]
{
// Marker in data section. Post-build tools can scan for "SpEc"
// patterns and match against specs in contractspecv0.
static MARKER: [u8; #marker_len] = *#marker_lit;
// Volatile read prevents DCE within live function.
let _ = unsafe { ::core::ptr::read_volatile(MARKER.as_ptr()) };
}
}
}
})
} else {
None
};

// Output.
let mut output = quote! {
#spec_gen

#include_spec_impl

impl #path::TryFromVal<#path::Env, #path::Val> for #enum_ident {
type Error = #path::ConversionError;
#[inline(always)]
Expand Down
46 changes: 42 additions & 4 deletions soroban-sdk-macros/src/derive_error_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use stellar_xdr::curr as stellar_xdr;
use stellar_xdr::{ScSpecEntry, ScSpecUdtErrorEnumCaseV0, ScSpecUdtErrorEnumV0, StringM, WriteXdr};
use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path};

use crate::{doc::docs_from_attrs, DEFAULT_XDR_RW_LIMITS};
use crate::{doc::docs_from_attrs, spec_marker, DEFAULT_XDR_RW_LIMITS};

pub fn derive_type_error_enum_int(
path: &Path,
Expand Down Expand Up @@ -63,15 +63,21 @@ pub fn derive_type_error_enum_int(
return quote! { #(#compile_errors)* };
}

// Generated code spec.
let spec_gen = if spec {
// Compute spec XDR once if spec is enabled.
let spec_xdr = if spec {
let spec_entry = ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 {
doc: docs_from_attrs(attrs),
lib: lib.as_deref().unwrap_or_default().try_into().unwrap(),
name: enum_ident.to_string().try_into().unwrap(),
cases: spec_cases.try_into().unwrap(),
});
let spec_xdr = spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap();
Some(spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap())
} else {
None
};

// Generated code spec.
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
let spec_xdr_len = spec_xdr.len();
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
Expand All @@ -89,10 +95,42 @@ pub fn derive_type_error_enum_int(
None
};

// IncludeSpecMarker impl - only generated when spec is true.
// Types with export=false should not be used at external boundaries.
let include_spec_impl = if let Some(ref spec_xdr) = spec_xdr {
// Create a marker that identifies this spec entry. The marker is a byte array
// in the data section with a distinctive pattern: "SpEc" + truncated SHA256.
// Post-build tools can scan the data section for "SpEc" markers and match
// against specs in contractspecv0.
let marker = spec_marker::spec_marker(spec_xdr);
let marker_lit = proc_macro2::Literal::byte_string(&marker);
let marker_len = marker.len();
Some(quote! {
impl #path::IncludeSpecMarker for #enum_ident {
#[doc(hidden)]
#[inline(always)]
fn include_spec_marker() {
#[cfg(target_family = "wasm")]
{
// Marker in data section. Post-build tools can scan for "SpEc"
// patterns and match against specs in contractspecv0.
static MARKER: [u8; #marker_len] = *#marker_lit;
// Volatile read prevents DCE within live function.
let _ = unsafe { ::core::ptr::read_volatile(MARKER.as_ptr()) };
}
}
}
})
} else {
None
};

// Output.
quote! {
#spec_gen

#include_spec_impl

impl TryFrom<#path::Error> for #enum_ident {
type Error = #path::Error;
#[inline(always)]
Expand Down
Loading
Loading