From 7652e1ad87439ec835cb5dc8a051e6aab3fc0e1d Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Wed, 27 May 2026 20:46:31 -0700 Subject: [PATCH 1/3] Add --enable-fastdev-unsafe-for-production, subsuming unified buid --- CONTRIBUTING.md | 26 +++++------ Cargo.lock | 26 ++++++++--- Cargo.toml | 7 +++ configure.ac | 30 ++++++++---- src/Makefile.am | 42 +++++++++++------ src/rust/Cargo.toml | 21 ++++----- src/rust/src/soroban_module_cache.rs | 51 ++++++++++++++++----- src/rust/src/soroban_proto_all.rs | 42 +++++++++++++---- src/rust/src/soroban_test_extra_protocol.rs | 6 ++- 9 files changed, 175 insertions(+), 76 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6cd924cd2f..55016a2a04 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,7 +116,7 @@ See https://clang.llvm.org/docs/AddressSanitizer.html for more information. *Note*: ASan will ignore any memory errors in Rust code unless you build with Rust's ASan support. And building with Rust's ASan support requires configuring -with `--enable-unified-rust-unsafe-for-production`. See below on "unified Rust +with `--enable-fastdev-unsafe-for-production`. See below on "fastdev Rust builds". *Note*: Rust's ASan support also requires a nightly compiler and the rust-src @@ -156,7 +156,7 @@ See https://clang.llvm.org/docs/ThreadSanitizer.html for more information. *Note*: Since Rust code is run on multiple threads and those threads are launched _from C++_ TSan will report races in Rust code unless you build with Rust's TSan support. And building with Rust's TSan support requires configuring -with `--enable-unified-rust-unsafe-for-production`. +with `--enable-fastdev-unsafe-for-production`. *Note*: Rust's ASan support also requires a nightly compiler and the rust-src component. Install these with: @@ -294,7 +294,7 @@ files. You should then inspect to see that only the transactions you expected to see change did so. If so, commit the changes as a new set of baselines for future tests. -## Unified and non-unified Rust builds +## Fastdev and non-unified Rust builds As of protocol 20, some components of stellar-core are written in Rust (notably soroban). @@ -331,21 +331,21 @@ and it _usually_ works. But there are two cases you might not want it. the stdlib and producing some sort of link-time dependency on crates that are only used as procedural macros). -For both of these cases, we've added the ability to (optionally) switch back to -the normal way Rust expects you to build a crate that links multiple versions of -a dependency: with a single "unified" cargo invocation, at the top level. There -are two different ways to enable this: +For both of these cases, we've added a fastdev mode that switches back to the +normal way Rust expects you to build a crate, with a single cargo invocation at +the top level and only the current and next Soroban hosts compiled in. There are +two different ways to enable this: - - By configuring with `--enable-unified-rust-unsafe-for-production`, if one - wants to _build_ a stellar-core with unified rust. + - By configuring with `--enable-fastdev-unsafe-for-production`, if one wants + to _build_ a stellar-core with fastdev rust. - - By toggling the "unified" feature flag in the IDE (eg. using the "Rust + - By toggling the "fastdev" feature flag in the IDE (eg. using the "Rust Feature Toggler" editor extension in VS code) if one merely wants to _edit_ - a stellar-core with unified rust. + a stellar-core with fastdev rust. The configure flag has got such a long and unwieldy name because _it will build -soroban with slightly different versions of transitive dependencies_, a -configuration we do _not_ want to ship in production builds. +soroban with fewer host versions and slightly different versions of transitive +dependencies_, a configuration we do _not_ want to ship in production builds. It is fine for debugging though. In practice those different versions of transitive dependencies are rarely "all that different". You will _probably_ not diff --git a/Cargo.lock b/Cargo.lock index 4e034af7aa..ec1fdfa81f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1435,7 +1435,7 @@ dependencies = [ [[package]] name = "soroban-builtin-sdk-macros" version = "26.1.2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=c0e58f94ff2983a09440cef6a54253349fd3c4db#c0e58f94ff2983a09440cef6a54253349fd3c4db" +source = "git+https://github.com/stellar/rs-soroban-env?rev=7fb0a840812fe8921bb48bebf7b4aa7160467ec8#7fb0a840812fe8921bb48bebf7b4aa7160467ec8" dependencies = [ "itertools 0.13.0", "proc-macro2", @@ -1543,7 +1543,7 @@ dependencies = [ [[package]] name = "soroban-env-common" version = "26.1.2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=c0e58f94ff2983a09440cef6a54253349fd3c4db#c0e58f94ff2983a09440cef6a54253349fd3c4db" +source = "git+https://github.com/stellar/rs-soroban-env?rev=7fb0a840812fe8921bb48bebf7b4aa7160467ec8#7fb0a840812fe8921bb48bebf7b4aa7160467ec8" dependencies = [ "crate-git-revision", "ethnum", @@ -1552,7 +1552,7 @@ dependencies = [ "soroban-env-macros 26.1.2", "soroban-wasmi", "static_assertions", - "stellar-xdr 26.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stellar-xdr 26.0.0 (git+https://github.com/stellar/rs-stellar-xdr?rev=a749b69b3471aec0c20ec431b0297f3414d66421)", "wasmparser", ] @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "soroban-env-host" version = "26.1.2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=c0e58f94ff2983a09440cef6a54253349fd3c4db#c0e58f94ff2983a09440cef6a54253349fd3c4db" +source = "git+https://github.com/stellar/rs-soroban-env?rev=7fb0a840812fe8921bb48bebf7b4aa7160467ec8#7fb0a840812fe8921bb48bebf7b4aa7160467ec8" dependencies = [ "ark-bls12-381 0.5.0", "ark-bn254 0.5.0", @@ -1887,14 +1887,14 @@ dependencies = [ [[package]] name = "soroban-env-macros" version = "26.1.2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=c0e58f94ff2983a09440cef6a54253349fd3c4db#c0e58f94ff2983a09440cef6a54253349fd3c4db" +source = "git+https://github.com/stellar/rs-soroban-env?rev=7fb0a840812fe8921bb48bebf7b4aa7160467ec8#7fb0a840812fe8921bb48bebf7b4aa7160467ec8" dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", "serde", "serde_json", - "stellar-xdr 26.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stellar-xdr 26.0.0 (git+https://github.com/stellar/rs-stellar-xdr?rev=a749b69b3471aec0c20ec431b0297f3414d66421)", "syn 2.0.39", ] @@ -2123,6 +2123,20 @@ dependencies = [ "stellar-strkey 0.0.13", ] +[[package]] +name = "stellar-xdr" +version = "26.0.0" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=a749b69b3471aec0c20ec431b0297f3414d66421#a749b69b3471aec0c20ec431b0297f3414d66421" +dependencies = [ + "base64 0.22.1", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "sha2", + "stellar-strkey 0.0.13", +] + [[package]] name = "stellar-xdr" version = "26.0.0" diff --git a/Cargo.toml b/Cargo.toml index b4017c6187..d1e2639498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,10 @@ lto = true # (or you can build with `make RUST_PROFILE=dev` to get them built with # debug-and-no-opt). debug = true + +[profile.fastdev] +inherits = "release" +lto = false +debug = "line-tables-only" +codegen-units = 16 +split-debuginfo = "unpacked" diff --git a/configure.ac b/configure.ac index 5b83514d7e..dadf670512 100644 --- a/configure.ac +++ b/configure.ac @@ -97,10 +97,24 @@ AX_APPEND_COMPILE_FLAGS($WFLAGS) AX_APPEND_COMPILE_FLAGS([-pthread]) AC_LANG_POP(C) -AC_ARG_ENABLE(unified-rust-unsafe-for-production, - AS_HELP_STRING([--enable-unified-rust-unsafe-for-production], - [Build rust crates as a single cargo library, risking version drift])) -AM_CONDITIONAL(UNIFIED_RUST, [test "x$enable_unified_rust_unsafe_for_production" = "xyes"]) +AC_ARG_ENABLE(fastdev-unsafe-for-production, + AS_HELP_STRING([--enable-fastdev-unsafe-for-production], + [Build in fast development mode UNSAFE FOR PRODUCTION])) +AS_IF([test "x$enable_fastdev_unsafe_for_production" = "xyes"], [ + AC_MSG_NOTICE([enabling fastdev build profile UNSAFE FOR PRODUCTION]) + fastdev_cxx_version=`$CXX --version 2>/dev/null` + case "$fastdev_cxx_version" in + *clang*) + CXXFLAGS="$CXXFLAGS -gline-tables-only" + AC_MSG_NOTICE([added -gline-tables-only to CXXFLAGS]) + ;; + *) + AC_MSG_ERROR([fastdev build requires clang compiler]) + ;; + esac +]) +AM_CONDITIONAL(ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION, + [test "x$enable_fastdev_unsafe_for_production" = "xyes"]) unset sanitizeopts @@ -110,8 +124,8 @@ AC_ARG_ENABLE([asan], AS_IF([test "x$enable_asan" = "xyes"], [ AC_MSG_NOTICE([ Enabling asan, see https://clang.llvm.org/docs/AddressSanitizer.html ]) - AS_IF([test "xyes" != "x$enable_unified_rust_unsafe_for_production"], [ - AC_MSG_WARN(Asan will not instrument rust without --enable-unified-rust-unsafe-for-production) + AS_IF([test "xyes" != "x$enable_fastdev_unsafe_for_production"], [ + AC_MSG_WARN(Asan will not instrument rust without --enable-fastdev-unsafe-for-production) ]) sanitizeopts="address" @@ -134,8 +148,8 @@ AC_ARG_ENABLE([threadsanitizer], AS_IF([test "x$enable_threadsanitizer" = "xyes"], [ AC_MSG_NOTICE([ enabling thread-sanitizer, see https://clang.llvm.org/docs/ThreadSanitizer.html ]) - AS_IF([test "xyes" != "x$enable_unified_rust_unsafe_for_production"], [ - AC_MSG_ERROR(Enabling tsan requires --enable-unified-rust-unsafe-for-production) + AS_IF([test "xyes" != "x$enable_fastdev_unsafe_for_production"], [ + AC_MSG_ERROR(Enabling tsan requires --enable-fastdev-unsafe-for-production) ]) AS_IF([test x != "x$sanitizeopts"], [ AC_MSG_ERROR(Cannot enable multiple sanitizers at once) diff --git a/src/Makefile.am b/src/Makefile.am index b9c0593caf..8211efb2d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -77,6 +77,12 @@ else CARGO_FEATURE_NEXT = endif +if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION +CARGO_FEATURE_FASTDEV = --features fastdev +else +CARGO_FEATURE_FASTDEV = +endif + main/XDRFilesSha256.cpp: $(SRC_X_FILES) Makefile $(top_srcdir)/hash-xdrs.sh $(top_srcdir)/hash-xdrs.sh $(top_srcdir)/src/protocol-curr >$@ @@ -131,7 +137,7 @@ stellar_core_SOURCES += $(GENERATED_XDRQUERY_SOURCES) util/xdrquery/XDRQueryPars BUILT_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES) stellar_core_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES) -if UNIFIED_RUST +if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION RUST_TOOLCHAIN_CHANNEL=nightly else RUST_TOOLCHAIN_FILE=$(top_srcdir)/rust-toolchain.toml @@ -151,22 +157,27 @@ RUST_BUILD_DIR=$(top_builddir)/src/rust RUST_BIN_DIR=$(RUST_BUILD_DIR)/bin RUST_TARGET_DIR=$(top_builddir)/target RUST_CXXBRIDGE=$(RUST_BIN_DIR)/cxxbridge + +if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION +RUST_PROFILE=fastdev +else RUST_PROFILE=release +endif check-rust-profile: Makefile @case "$(RUST_PROFILE)" in \ - release|dev) ;; \ - *) echo "Error: RUST_PROFILE must be 'release' or 'dev', got '$(RUST_PROFILE)'" >&2; exit 1;; \ + release|dev|fastdev) ;; \ + *) echo "Error: RUST_PROFILE must be 'release', 'dev', or 'fastdev', got '$(RUST_PROFILE)'" >&2; exit 1;; \ esac # Of course, RUST_PROFILE can't be used as an argument directly because cargo # doesn't let you pass --debug, that's the default! you can only pass --release. # So we have to derive a new variable here. -RUST_PROFILE_ARG := $(if $(findstring release,$(RUST_PROFILE)),--release,) +RUST_PROFILE_ARG := $(if $(findstring fastdev,$(RUST_PROFILE)),--profile fastdev,$(if $(findstring release,$(RUST_PROFILE)),--release,)) # Also for even more nonsense reasons the debug profile name is actually `dev` # but only in the command line, the target subdirectory gets called `debug`. -RUST_PROFILE_DIR := $(if $(findstring release,$(RUST_PROFILE)),release,debug) +RUST_PROFILE_DIR := $(if $(findstring fastdev,$(RUST_PROFILE)),fastdev,$(if $(findstring release,$(RUST_PROFILE)),release,debug)) RUST_DEP_TREE_STAMP=$(RUST_BUILD_DIR)/src/dep-trees/equal-trees.stamp SOROBAN_LIBS_STAMP=$(RUST_BUILD_DIR)/soroban/soroban-libs.stamp @@ -201,7 +212,11 @@ SOROBAN_BUILD_DIR=$(abspath $(RUST_BUILD_DIR))/soroban # variable empty (and include or exclude submodules from the list of # ALL_SOROBAN_PROTOCOLS as you see fit). +if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION +ALL_SOROBAN_PROTOCOLS=p26 +else ALL_SOROBAN_PROTOCOLS=p21 p22 p23 p24 p25 p26 +endif WIP_SOROBAN_PROTOCOL=p27 CARGO_XDR_FEATURE_FLAGS = @@ -287,23 +302,23 @@ $(SOROBAN_BUILD_DIR)/%/target/git-state.txt: $(top_srcdir)/.git/modules/src/rust printf '%s\n' "$$state" > $@.tmp; \ if cmp -s $@.tmp $@; then rm -f $@.tmp; else mv -f $@.tmp $@; fi -# The "unified" rust build is a special non-production mode that builds all of +# The fastdev rust build is a special non-production mode that builds all of # the rust dependencies of librust_stellar_core.a through a single cargo # invocation, which is actually the "normal" way cargo operates, but which also # has the negative side effect of resolving (merging) different point releases # and pre-release minor versions across transitive dependencies, which means we # can't control the _exact_ transitive dependencies as well as we'd like. # -# So we only use the unified rust build for certain special cases such as +# So we only use the fastdev rust build for certain special cases such as # testing with asan/tsan (they seem to only work well when built this way) and # use the non-unified build (with separate .a files for each separate soroban # version) for production builds. -if UNIFIED_RUST +if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION -# In the unified build, we have to pass the --target flag. This actually breaks +# In the fastdev build, we have to pass the --target flag. This actually breaks # the non-unified build, so we wind up setting LIBRUST_STELLAR_CORE separately -# in unified and non-unified builds. +# in fastdev and non-unified builds. RUST_TARGET=$(shell rustc -vV | sed -n 's/host: //p') LIBRUST_STELLAR_CORE=$(RUST_TARGET_DIR)/$(RUST_TARGET)/$(RUST_PROFILE_DIR)/librust_stellar_core.a @@ -323,14 +338,13 @@ $(LIBRUST_STELLAR_CORE): $(RUST_HOST_DEPFILES) $(SRC_RUST_FILES) Makefile $(RUST $(CARGOFLAGS_BUILDSTD) \ --package stellar-core \ --target $(RUST_TARGET) \ - --features unified \ $(RUST_PROFILE_ARG) \ --locked \ --target-dir $(abspath $(RUST_TARGET_DIR)) \ - $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS) + $(CARGO_FEATURE_FASTDEV) $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS) ranlib $@ -else # !UNIFIED_RUST +else # !ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION # This next build command looks a little weird but it's necessary. We have to # provide an auxiliary metadata string (using RUSTFLAGS=-Cmetadata=$*) @@ -444,7 +458,7 @@ $(LIBRUST_STELLAR_CORE): $(RUST_HOST_DEPFILES) $(SRC_RUST_FILES) $(ALL_SOROBAN_L $(ALL_SOROBAN_DEPEND_ARGS) ranlib $@ -endif # UNIFIED_RUST +endif # ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION stellar_core_LDADD += $(LIBRUST_STELLAR_CORE) -ldl diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 6d10b738e7..e9ba28ebe8 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -93,19 +93,19 @@ tracy-client = { version = "=0.17.0", features = [ # makes it difficult to use an IDE to work on this crate since the IDE will not # be able to resolve the hosts to anything at all. So we keep some _optional_ # copies of the host dependencies here, and allow enabling them with the -# `unified` feature here. +# `fastdev` feature here. # # To reiterate: the soroban-env-host-p{21,22,23}... dependency blocks listed # here are NOT part of the default build. The default build relies on the # versions pinned as git submodules. # -# However, you can _manually_ switch the default build by enabling the `unified` +# However, you can _manually_ switch the default build by enabling the `fastdev` # feature using a feature-toggle in an IDE (eg. using the VS Code "Rust Feature # Toggler" extension), and the build system (src/Makefile.am) can be configured -# to use the `unified` feature if you configure with -# --enable-unified-rust-unsafe-for-production. +# to use the `fastdev` feature if you configure with +# --enable-fastdev-unsafe-for-production. # -# If you do a unified build, be careful to reset any changes the IDE makes to +# If you do a fastdev build, be careful to reset any changes the IDE makes to # Cargo.lock! The unified build will re-resolve transitive dependencies and # unify them, perturbing the contents of the lockfile. @@ -113,7 +113,7 @@ tracy-client = { version = "=0.17.0", features = [ version = "=26.1.2" git = "https://github.com/stellar/rs-soroban-env" package = "soroban-env-host" -rev = "c0e58f94ff2983a09440cef6a54253349fd3c4db" +rev = "7fb0a840812fe8921bb48bebf7b4aa7160467ec8" optional = true [dependencies.soroban-env-host-p26] @@ -189,14 +189,9 @@ rev = "8b04a2b7a92b96ad90c09a9151ef97aa9a04a9f8" [features] -# Turn on the optional unified build. This is typically only useful in an IDE or when +# Turn on the optional fastdev build. This is typically only useful in an IDE or when # orchestrated by the build system. See note above and in docs/CONTRIBUTING.md. -unified = ["dep:soroban-env-host-p21", - "dep:soroban-env-host-p22", - "dep:soroban-env-host-p23", - "dep:soroban-env-host-p24", - "dep:soroban-env-host-p25", - "dep:soroban-env-host-p26", +fastdev = ["dep:soroban-env-host-p26", "dep:soroban-env-host-p27"] tracy = ["dep:tracy-client"] diff --git a/src/rust/src/soroban_module_cache.rs b/src/rust/src/soroban_module_cache.rs index a96c31e931..88b39fc880 100644 --- a/src/rust/src/soroban_module_cache.rs +++ b/src/rust/src/soroban_module_cache.rs @@ -16,15 +16,21 @@ use crate::{ rust_bridge::CxxBuf, - soroban_proto_all::{get_host_module_for_protocol, p23, p24, p25, p26, protocol_agnostic}, + soroban_proto_all::{get_host_module_for_protocol, p26, protocol_agnostic}, }; +#[cfg(not(feature = "fastdev"))] +use crate::soroban_proto_all::{p23, p24, p25}; + #[cfg(feature = "next")] use crate::soroban_proto_all::p27; pub(crate) struct SorobanModuleCache { + #[cfg(not(feature = "fastdev"))] pub(crate) p23_cache: p23::soroban_proto_any::ProtocolSpecificModuleCache, + #[cfg(not(feature = "fastdev"))] pub(crate) p24_cache: p24::soroban_proto_any::ProtocolSpecificModuleCache, + #[cfg(not(feature = "fastdev"))] pub(crate) p25_cache: p25::soroban_proto_any::ProtocolSpecificModuleCache, pub(crate) p26_cache: p26::soroban_proto_any::ProtocolSpecificModuleCache, #[cfg(feature = "next")] @@ -34,8 +40,11 @@ pub(crate) struct SorobanModuleCache { impl SorobanModuleCache { fn new() -> Result> { Ok(Self { + #[cfg(not(feature = "fastdev"))] p23_cache: p23::soroban_proto_any::ProtocolSpecificModuleCache::new()?, + #[cfg(not(feature = "fastdev"))] p24_cache: p24::soroban_proto_any::ProtocolSpecificModuleCache::new()?, + #[cfg(not(feature = "fastdev"))] p25_cache: p25::soroban_proto_any::ProtocolSpecificModuleCache::new()?, p26_cache: p26::soroban_proto_any::ProtocolSpecificModuleCache::new()?, #[cfg(feature = "next")] @@ -47,9 +56,13 @@ impl SorobanModuleCache { ledger_protocol: u32, _wasm: &[u8], ) -> Result<(), Box> { - match ledger_protocol { + let hm = get_host_module_for_protocol(ledger_protocol, ledger_protocol)?; + match hm.max_proto { + #[cfg(not(feature = "fastdev"))] 23 => self.p23_cache.compile(_wasm), + #[cfg(not(feature = "fastdev"))] 24 => self.p24_cache.compile(_wasm), + #[cfg(not(feature = "fastdev"))] 25 => self.p25_cache.compile(_wasm), 26 => self.p26_cache.compile(_wasm), #[cfg(feature = "next")] @@ -60,8 +73,11 @@ impl SorobanModuleCache { } pub fn shallow_clone(&self) -> Result, Box> { Ok(Box::new(Self { + #[cfg(not(feature = "fastdev"))] p23_cache: self.p23_cache.shallow_clone()?, + #[cfg(not(feature = "fastdev"))] p24_cache: self.p24_cache.shallow_clone()?, + #[cfg(not(feature = "fastdev"))] p25_cache: self.p25_cache.shallow_clone()?, p26_cache: self.p26_cache.shallow_clone()?, #[cfg(feature = "next")] @@ -74,8 +90,11 @@ impl SorobanModuleCache { .as_ref() .try_into() .map_err(|_| "Invalid contract-code key length")?; + #[cfg(not(feature = "fastdev"))] self.p23_cache.evict(&_hash)?; + #[cfg(not(feature = "fastdev"))] self.p24_cache.evict(&_hash)?; + #[cfg(not(feature = "fastdev"))] self.p25_cache.evict(&_hash)?; self.p26_cache.evict(&_hash)?; #[cfg(feature = "next")] @@ -83,8 +102,11 @@ impl SorobanModuleCache { Ok(()) } pub fn clear(&self) -> Result<(), Box> { + #[cfg(not(feature = "fastdev"))] self.p23_cache.clear()?; + #[cfg(not(feature = "fastdev"))] self.p24_cache.clear()?; + #[cfg(not(feature = "fastdev"))] self.p25_cache.clear()?; self.p26_cache.clear()?; #[cfg(feature = "next")] @@ -101,9 +123,13 @@ impl SorobanModuleCache { .as_ref() .try_into() .map_err(|_| "Invalid contract-code key length")?; - match protocol { + let hm = get_host_module_for_protocol(protocol, protocol)?; + match hm.max_proto { + #[cfg(not(feature = "fastdev"))] 23 => self.p23_cache.contains_module(&_hash), + #[cfg(not(feature = "fastdev"))] 24 => self.p24_cache.contains_module(&_hash), + #[cfg(not(feature = "fastdev"))] 25 => self.p25_cache.contains_module(&_hash), 26 => self.p26_cache.contains_module(&_hash), #[cfg(feature = "next")] @@ -115,18 +141,19 @@ impl SorobanModuleCache { &self, ledger_protocol: u32, ) -> Result> { - #[allow(unused_mut)] - let mut bytes = 0; - match ledger_protocol { - 23 => bytes = bytes.max(self.p23_cache.get_wasm_bytes_input()?), - 24 => bytes = bytes.max(self.p24_cache.get_wasm_bytes_input()?), - 25 => bytes = bytes.max(self.p25_cache.get_wasm_bytes_input()?), - 26 => bytes = bytes.max(self.p26_cache.get_wasm_bytes_input()?), + let hm = get_host_module_for_protocol(ledger_protocol, ledger_protocol)?; + match hm.max_proto { + #[cfg(not(feature = "fastdev"))] + 23 => self.p23_cache.get_wasm_bytes_input(), + #[cfg(not(feature = "fastdev"))] + 24 => self.p24_cache.get_wasm_bytes_input(), + #[cfg(not(feature = "fastdev"))] + 25 => self.p25_cache.get_wasm_bytes_input(), + 26 => self.p26_cache.get_wasm_bytes_input(), #[cfg(feature = "next")] - 27 => bytes = bytes.max(self.p27_cache.get_wasm_bytes_input()?), + 27 => self.p27_cache.get_wasm_bytes_input(), _ => return Err(protocol_agnostic::make_error("unsupported protocol")), } - Ok(bytes) } } diff --git a/src/rust/src/soroban_proto_all.rs b/src/rust/src/soroban_proto_all.rs index f3f8dee7b0..7f09b6c28a 100644 --- a/src/rust/src/soroban_proto_all.rs +++ b/src/rust/src/soroban_proto_all.rs @@ -43,13 +43,13 @@ pub(crate) use p27 as soroban_curr; // only compatible with with, say, a rust Dyn interface like Box). pub(crate) mod protocol_agnostic { pub(crate) fn make_error(msg: &'static str) -> Box { - super::p24::soroban_proto_any::CoreHostError::General(msg.into()).into() + super::soroban_curr::soroban_proto_any::CoreHostError::General(msg.into()).into() } // The i128 functions are protocol-agnostic because they're too simple to // ever plausibly change. If they ever _do_ change we can switch this (and // the callers) to pass a protocol number but it seems unlikely. - pub(crate) use super::p24::soroban_env_host::xdr::int128_helpers; + pub(crate) use super::soroban_curr::soroban_env_host::xdr::int128_helpers; } #[cfg(feature = "next")] @@ -393,6 +393,7 @@ pub(crate) mod p26 { } } +#[cfg(not(feature = "fastdev"))] #[path = "."] pub(crate) mod p25 { pub(crate) extern crate soroban_env_host_p25; @@ -566,6 +567,7 @@ pub(crate) mod p25 { } } +#[cfg(not(feature = "fastdev"))] #[path = "."] pub(crate) mod p24 { pub(crate) extern crate soroban_env_host_p24; @@ -739,6 +741,7 @@ pub(crate) mod p24 { } } +#[cfg(not(feature = "fastdev"))] #[path = "."] pub(crate) mod p23 { pub(crate) extern crate soroban_env_host_p23; @@ -912,6 +915,7 @@ pub(crate) mod p23 { } } +#[cfg(not(feature = "fastdev"))] #[path = "."] pub(crate) mod p22 { pub(crate) extern crate soroban_env_host_p22; @@ -1119,6 +1123,7 @@ pub(crate) mod p22 { } } +#[cfg(not(feature = "fastdev"))] #[path = "."] pub(crate) mod p21 { pub(crate) extern crate soroban_env_host_p21; @@ -1463,10 +1468,15 @@ macro_rules! proto_versioned_functions_for_module { // NB: this list should be in ascending order. Out of order will cause // an assert to fail in the by-protocol-number lookup function below. const HOST_MODULES: &'static [HostModule] = &[ + #[cfg(not(feature = "fastdev"))] proto_versioned_functions_for_module!(p21), + #[cfg(not(feature = "fastdev"))] proto_versioned_functions_for_module!(p22), + #[cfg(not(feature = "fastdev"))] proto_versioned_functions_for_module!(p23), + #[cfg(not(feature = "fastdev"))] proto_versioned_functions_for_module!(p24), + #[cfg(not(feature = "fastdev"))] proto_versioned_functions_for_module!(p25), proto_versioned_functions_for_module!(p26), #[cfg(feature = "next")] @@ -1497,13 +1507,27 @@ pub(crate) fn get_host_module_for_protocol( #[test] fn protocol_dispatches_as_expected() { - assert_eq!(get_host_module_for_protocol(20, 20).unwrap().max_proto, 21); - assert_eq!(get_host_module_for_protocol(21, 21).unwrap().max_proto, 21); - assert_eq!(get_host_module_for_protocol(22, 22).unwrap().max_proto, 22); - assert_eq!(get_host_module_for_protocol(23, 23).unwrap().max_proto, 23); - assert_eq!(get_host_module_for_protocol(24, 24).unwrap().max_proto, 24); - assert_eq!(get_host_module_for_protocol(25, 25).unwrap().max_proto, 25); - assert_eq!(get_host_module_for_protocol(26, 26).unwrap().max_proto, 26); + #[cfg(not(feature = "fastdev"))] + { + assert_eq!(get_host_module_for_protocol(20, 20).unwrap().max_proto, 21); + assert_eq!(get_host_module_for_protocol(21, 21).unwrap().max_proto, 21); + assert_eq!(get_host_module_for_protocol(22, 22).unwrap().max_proto, 22); + assert_eq!(get_host_module_for_protocol(23, 23).unwrap().max_proto, 23); + assert_eq!(get_host_module_for_protocol(24, 24).unwrap().max_proto, 24); + assert_eq!(get_host_module_for_protocol(25, 25).unwrap().max_proto, 25); + assert_eq!(get_host_module_for_protocol(26, 26).unwrap().max_proto, 26); + } + + #[cfg(feature = "fastdev")] + { + assert_eq!(get_host_module_for_protocol(20, 20).unwrap().max_proto, 26); + assert_eq!(get_host_module_for_protocol(26, 26).unwrap().max_proto, 26); + } + + #[cfg(all(feature = "fastdev", feature = "next"))] + { + assert_eq!(get_host_module_for_protocol(27, 27).unwrap().max_proto, 27); + } // No protocols past the max known. let last_proto = HOST_MODULES.last().unwrap().max_proto; diff --git a/src/rust/src/soroban_test_extra_protocol.rs b/src/rust/src/soroban_test_extra_protocol.rs index ec5e8c787b..7cc99bd5d1 100644 --- a/src/rust/src/soroban_test_extra_protocol.rs +++ b/src/rust/src/soroban_test_extra_protocol.rs @@ -8,10 +8,13 @@ use std::str::FromStr; use crate::{ log::partition::TX, - soroban_proto_all::{get_host_module_for_protocol, p22, HostModule}, + soroban_proto_all::{get_host_module_for_protocol, HostModule}, CxxBuf, CxxLedgerInfo, CxxRentFeeConfiguration, InvokeHostFunctionOutput, RustBuf, SorobanModuleCache, }; + +#[cfg(not(feature = "fastdev"))] +use crate::soroban_proto_all::p22; use log::{info, warn}; pub(super) fn maybe_invoke_host_function_again_and_compare_outputs( @@ -86,6 +89,7 @@ fn modify_resources_for_extra_test_execution( new_protocol: u32, ) -> Result<(), Box> { match new_protocol { + #[cfg(not(feature = "fastdev"))] 22 => { use p22::soroban_proto_any::{ inplace_modify_cxxbuf_encoded_type, xdr::SorobanResources, From 7eb687fefb99fe153422e9020d2f9f372fc1a7b3 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 28 May 2026 21:28:36 -0700 Subject: [PATCH 2/3] Make fastdev a superset of next --- CONTRIBUTING.md | 6 ++++++ configure.ac | 6 ++++++ src/rust/Cargo.toml | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55016a2a04..0109554547 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -351,3 +351,9 @@ It is fine for debugging though. In practice those different versions of transitive dependencies are rarely "all that different". You will _probably_ not be able to observe any differences. We just don't want to chance it in production. + +To reduce the set of possible configurations and flags, fastdev also acts as +a superset of `--enable-next-protocol-version-unsafe-for-production` (i.e. it +also turns on the `next` feature and links in whatever the next-protocol soroban +host is). + diff --git a/configure.ac b/configure.ac index dadf670512..8cf2e18d30 100644 --- a/configure.ac +++ b/configure.ac @@ -572,6 +572,12 @@ AM_CONDITIONAL(USE_SPDLOG, [test x$enable_spdlog != xno]) AC_ARG_ENABLE(next-protocol-version-unsafe-for-production, AS_HELP_STRING([--enable-next-protocol-version-unsafe-for-production], [Enable next protocol version UNSAFE FOR PRODUCTION])) +AS_IF([test "x$enable_fastdev_unsafe_for_production" = "xyes"], [ + AS_IF([test "x$enable_next_protocol_version_unsafe_for_production" != "xyes"], [ + AC_MSG_NOTICE([enabling next protocol version due to fastdev build profile]) + ]) + enable_next_protocol_version_unsafe_for_production=yes +]) AM_CONDITIONAL(ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION, [test x$enable_next_protocol_version_unsafe_for_production = xyes]) diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index e9ba28ebe8..7ef3efb238 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -192,7 +192,8 @@ rev = "8b04a2b7a92b96ad90c09a9151ef97aa9a04a9f8" # Turn on the optional fastdev build. This is typically only useful in an IDE or when # orchestrated by the build system. See note above and in docs/CONTRIBUTING.md. fastdev = ["dep:soroban-env-host-p26", - "dep:soroban-env-host-p27"] + "dep:soroban-env-host-p27", + "next"] tracy = ["dep:tracy-client"] From 1cc328cf5db7330a780e5395deff55e14c18bd81 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 28 May 2026 21:29:09 -0700 Subject: [PATCH 3/3] Set +nightly only when fastdev _and_ sanitizers are on. --- src/Makefile.am | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 8211efb2d1..ccc55252c5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -137,11 +137,12 @@ stellar_core_SOURCES += $(GENERATED_XDRQUERY_SOURCES) util/xdrquery/XDRQueryPars BUILT_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES) stellar_core_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES) +RUST_TOOLCHAIN_FILE=$(top_srcdir)/rust-toolchain.toml +RUST_TOOLCHAIN_FILE_CHANNEL=$(shell sed -n 's/channel *= *"\([^"]*\)"/\1/p' $(RUST_TOOLCHAIN_FILE)) if ENABLE_FASTDEV_UNSAFE_FOR_PRODUCTION -RUST_TOOLCHAIN_CHANNEL=nightly +RUST_TOOLCHAIN_CHANNEL=$(if $(findstring -fsanitize=,$(CXXFLAGS)),nightly,$(RUST_TOOLCHAIN_FILE_CHANNEL)) else -RUST_TOOLCHAIN_FILE=$(top_srcdir)/rust-toolchain.toml -RUST_TOOLCHAIN_CHANNEL=$(shell sed -n 's/channel *= *"\([^"]*\)"/\1/p' $(RUST_TOOLCHAIN_FILE)) +RUST_TOOLCHAIN_CHANNEL=$(RUST_TOOLCHAIN_FILE_CHANNEL) endif CARGO=cargo +$(RUST_TOOLCHAIN_CHANNEL) RUSTC_WRAPPER=@RUSTC_WRAPPER@