SKILL.md
Rust Subsystem (Non-Soroban) — Technical Summary
Overview
The rust subsystem provides a Rust static library (ruststellarcore) that is linked into the stellar-core C++ binary. It uses the cxx crate (v1.0.97) to define a bidirectional FFI bridge between C++ and Rust. The subsystem's primary responsibilities are:
- Soroban host function invocation — dispatching to the correct protocol-versioned soroban host.
- Fee computation — transaction resource fees, rent fees, and rent write fees.
- Module caching — pre-compiled WASM module cache for Soroban contracts.
- 128-bit integer arithmetic — exposing Rust's native
i128to C++. - Base64 encoding/decoding — used for XDR serialization interop.
- Ed25519 signature verification — using
ed25519-dalekfor faster verification. - Logging bridge — routing Rust
logcrate output to the C++ spdlog system. - Quorum intersection checking — using the
stellar-quorum-analyzerSAT solver. - Utility functions — rustc version, executable path, backtrace capture, XDR version checks.
The crate is built as crate-type = ["staticlib"] (edition 2021, rust-version 1.82.0). Optional features include tracy (profiling), next (pre-release protocol), testutils (test-only code), and unified (IDE-friendly single cargo build).
File Layout
| File | Role |
|---|---|
Cargo.toml |
Crate metadata, multi-host soroban dependencies, feature flags |
src/lib.rs |
Crate root; declares modules, re-exports bridge symbols, defines tracy_span! macro |
src/bridge.rs |
#[cxx::bridge] module — all FFI type/function declarations |
src/common.rs |
RustBuf/CxxBuf/BridgeError impls; getrustcversion, currentexe, capturecxxbacktrace, checkxdrversionidentities |
src/b64.rs |
tobase64 / frombase64 |
src/ed25519_verify.rs |
verifyed25519signature_dalek (unsafe raw-pointer FFI) |
src/i128.rs |
i128add, i128sub, overflow/underflow checks, conversion |
src/log.rs |
StellarLogger implementing log::Log, routes to C++ spdlog |
src/quorum_checker.rs |
networkenjoysquorum_intersection wrapping stellar-quorum-analyzer |
src/soroban_invoke.rs |
invokehostfunction, fee computation, transaction parsing dispatchers |
src/sorobanmodulecache.rs |
SorobanModuleCache struct; per-protocol caches |
src/sorobanprotoall.rs |
Protocol-versioned host modules (p21–p26), dispatch table, adaptors |
src/sorobanprotoany.rs |
Protocol-agnostic host invocation code, mounted inside each pN module |
CppShims.h |
Thin C++ shim functions (shimisLogLevelAtLeast, shimlogAtPartitionAndLevel) |
RustBridge.h |
cxx-generated C++ header with all bridge types and function declarations |
RustBridge.cpp |
cxx-generated C++ implementation (extern "C" thunks, Vec/Box specializations) |
RustVecXdrMarshal.h |
Declares rust::Vec<uint8_t> as valid xdrpp byte buffer type |
The CXX Bridge Mechanism
How it works
The bridge is defined in src/bridge.rs inside a #[cxx::bridge] attribute macro on mod rust_bridge. This module contains three sections:
- Shared types — structs and enums visible to both sides, defined once:
- CxxBuf (C++→Rust data: wraps UniquePtr<CxxVector<u8>>) - RustBuf (Rust→C++ data: wraps Vec<u8>) - XDRFileHash, InvokeHostFunctionOutput, CxxLedgerInfo, CxxTransactionResources, CxxFeeConfiguration, CxxLedgerEntryRentChange, CxxRentFeeConfiguration, CxxRentWriteFeeConfiguration, CxxI128, FeePair, SorobanVersionInfo - Enums: LogLevel (shared with stellar::LogLevel), BridgeError, QuorumCheckerStatus - QuorumSplit, QuorumCheckerResource
extern "Rust"block (#[namespace = "stellar::rust_bridge"]) — Rust functions callable from C++:
- All functions listed in the "Key Functions" section below. - The opaque type SorobanModuleCache with its methods.
extern "C++"block (#[namespace = "stellar"]) — C++ functions callable from Rust:
- shimisLogLevelAtLeast(partition: &CxxString, level: LogLevel) -> Result<bool> - shimlogAtPartitionAndLevel(partition: &CxxString, level: LogLevel, msg: &CxxString) -> Result<()>
Data passing convention
- C++ → Rust: Data is passed as
CxxBufcontainingUniquePtr<CxxVector<u8>>(a C++-allocatedstd::vector<uint8t>). The Rust side reads from it viadata.asslice(). - Rust → C++: Data is returned as
RustBufcontainingVec<u8>(Rust-allocated). The C++ side reads fromdata(arust::Vec<uint8_t>). - XDR serialization/deserialization is done with
ReadXdr/WriteXdrusingnonmeteredxdrfromcxxbufandnonmeteredxdrtorustbufhelper functions with a depth limit of 1000 and length limit matching the buffer size. RustVecXdrMarshal.hallows xdrpp to directly unmarshal fromrust::Vec<uint8_t>.
Generated files
RustBridge.h and RustBridge.cpp are generated by the cxxbridge tool. They contain:
- Full implementations of
rust::String,rust::Slice<T>,rust::Box<T>,rust::Vec<T>,rust::Opaque,rust::Error. - C struct definitions mirroring the shared types.
static_assertchecks ensuringLogLevelenum values match between C++ and Rust.extern "C"function declarations for the mangled bridge symbols.- C++ wrapper functions in
namespace stellar::rust_bridgethat call through extern "C" thunks and translate Rust errors to C++ exceptions (rust::Error). - Template specializations for
rust::Vec<RustBuf>,rust::Vec<XDRFileHash>,rust::Vec<CxxBuf>, etc. rust::Box<SorobanModuleCache>alloc/dealloc/drop specializations.
CppShims.h
Provides simple inline wrapper functions that cxx.rs can call, bridging to C++ APIs that are too complex for cxx to handle directly (e.g., static member functions):
shim_isLogLevelAtLeast→Logging::isLogLevelAtLeastshim_logAtPartitionAndLevel→Logging::logAtPartitionAndLevel
Key Data Structures
CxxBuf / RustBuf
Directional byte-buffer wrappers for passing XDR-serialized data across the FFI boundary. CxxBuf owns a std::uniqueptr<std::vector<uint8t>> (C++ allocated). RustBuf owns a Vec<u8> (Rust allocated). Both implement AsRef<[u8]>.
CxxI128
Split representation of 128-bit integer: { hi: i64, lo: u64 }. Used because C++ lacks native i128 on all platforms. Converted to/from Rust i128 via int128helpers::{i128frompieces, i128hi, i128_lo}.
InvokeHostFunctionOutput
Return value of invokehostfunction. Contains:
success: bool,isinternalerror: booldiagnostic_events: Vec<RustBuf>(XDR-encodedDiagnosticEvent)cpuinsns,membytes,time_nsecs(and excluding-VM-instantiation variants)resultvalue: RustBuf,contractevents: Vec<RustBuf>,modifiedledgerentries: Vec<RustBuf>,rent_fee: i64
SorobanModuleCache
An opaque Rust type exposed to C++ via rust::Box<SorobanModuleCache>. Holds per-protocol ProtocolSpecificModuleCache instances (p23, p24, p25, and optionally p26 with next feature). Each ProtocolSpecificModuleCache contains a ModuleCache (from soroban-env-host, threadsafe via internal locking) and an AtomicU64 tracking memory consumption. Methods:
compile(&mut self, ledger_protocol: u32, wasm: &[u8])— parse and cache a WASM module for the given protocol.shallow_clone(&self) -> Box<SorobanModuleCache>— clone shared ownership handles for multithreaded compilation.evictcontractcode(&mut self, key: &[u8])— remove a module from all protocol caches by 32-byte hash.clear(&mut self)— clear all protocol caches.contains_module(&self, protocol: u32, key: &[u8]) -> boolgetmembytes_consumed(&self, protocol: u32) -> u64
HostModule
A dispatch table struct (not crossing FFI) containing function pointers for a specific protocol version's soroban host. Fields include maxproto, invokehostfunction, computetransactionresourcefee, computerentfee, computerentwritefeeper1kb, contractcodememorysizeforrent, canparsetransaction, and getsorobanversioninfo. The static array HOSTMODULES holds one entry per protocol version (p21–p25/p26), populated via the protoversionedfunctionsformodule! macro.
ProtocolSpecificModuleCache
Per-protocol cache wrapper (defined in sorobanprotoany.rs). Wraps a ModuleCache from the protocol's soroban-env-host and a CoreCompilationContext (unlimited budget for compilation). Supports compile, evict, clear, containsmodule, getmembytesconsumed, and shallow_clone.
CoreCompilationContext
Implements CompilationContext (= ErrorHandler + AsBudget) with an unlimited budget, used for compiling WASM modules outside of transaction execution.
Key Functions (Exported Rust → C++)
Soroban Host Invocation
invokehostfunction(configmaxprotocol: u32, enablediagnostics: bool, instructionlimit: u32, hfbuf: &CxxBuf, resources: CxxBuf, restoredrwentryindices: &Vec<u32>, sourceaccount: &CxxBuf, authentries: &Vec<CxxBuf>, ledgerinfo: CxxLedgerInfo, ledgerentries: &Vec<CxxBuf>, ttlentries: &Vec<CxxBuf>, baseprngseed: &CxxBuf, rentfeeconfiguration: CxxRentFeeConfiguration, modulecache: &SorobanModuleCache) -> Result<InvokeHostFunctionOutput>— Dispatches to the correct protocol-versioned host viagethostmoduleforprotocol. Wraps the call inpanic::catch_unwind.
Fee Computation
computetransactionresourcefee(configmaxprotocol: u32, protocolversion: u32, txresources: CxxTransactionResources, feeconfig: CxxFeeConfiguration) -> Result<FeePair>— Returns(nonrefundablefee, refundable_fee).computerentfee(configmaxprotocol: u32, protocolversion: u32, changedentries: &Vec<CxxLedgerEntryRentChange>, feeconfig: CxxRentFeeConfiguration, currentledger_seq: u32) -> Result<i64>computerentwritefeeper1kb(configmaxprotocol: u32, protocolversion: u32, bucketlistsize: i64, fee_config: CxxRentWriteFeeConfiguration) -> Result<i64>contractcodememorysizeforrent(configmaxprotocol: u32, protocolversion: u32, contractcodeentry: &CxxBuf, cpucostparams: &CxxBuf, memcostparams: &CxxBuf) -> Result<u32>— Only valid for protocol ≥ 23.
Transaction Parsing
canparsetransaction(configmaxprotocol: u32, protocolversion: u32, xdr: &CxxBuf, depthlimit: u32) -> Result<bool>— Checks if aTransactionEnvelopeXDR can be deserialized in the given protocol.
128-bit Integer Arithmetic
i128_add(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>i128_sub(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>i128addwill_overflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>i128subwill_underflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>i128fromi64(val: i64) -> Result<CxxI128>i128isnegative(val: &CxxI128) -> Result<bool>i128i64eq(lhs: &CxxI128, rhs: i64) -> Result<bool>
Ed25519 Verification
verifyed25519signaturedalek(publickeyptr: const u8, signatureptr: const u8, messageptr: *const u8, messagelen: usize) -> bool— Unsafe raw-pointer interface. Usesed25519-dalek'sverify_strict(rejects small-order points, matching libsodium). Never panics; returns false for invalid input.
Base64
to_base64(b: &CxxVector<u8>, s: Pin<&mut CxxString>)— Encode bytes to base64.from_base64(s: &CxxString, b: Pin<&mut CxxVector<u8>>)— Decode base64 with error-tolerant stripping of invalid characters.
Logging
init_logging(maxLevel: LogLevel) -> Result<()>— Initializes theStellarLoggeras the global Rust logger, routing to C++ spdlog. UsesAtomicBoolfor one-time initialization. Log partitions (e.g.,TX,Ledger,SCP) are defined inlog::partitionand must matchutil/LogPartitions.defon the C++ side.
Quorum Checker
networkenjoysquorumintersection(nodes: &Vec<CxxBuf>, quorumset: &Vec<CxxBuf>, potentialsplit: &mut QuorumSplit, resourcelimit: &QuorumCheckerResource, resourceusage: &mut QuorumCheckerResource) -> Result<QuorumCheckerStatus>— ReturnsUNSAT(quorum intersection holds),SAT(split found, populatespotentialsplit), orUNKNOWN. Time limit enforced internally; memory limit is a hard abort via global allocator.
Module Cache
newmodulecache() -> Result<Box<SorobanModuleCache>>- Methods on
SorobanModuleCache:compile,shallowclone,evictcontractcode,clear,containsmodule,getmembytes_consumed.
Utility
getrustcversion() -> Stringcurrent_exe() -> Result<String>capturecxxbacktrace() -> String— Usesbacktracecrate; filters out initial Rust frames and libc frames.getsorobanversioninfo(coremax_proto: u32) -> Vec<SorobanVersionInfo>— Returns version info for all linked soroban hosts. Panics if no host supports the given protocol.checksensiblesorobanconfigforprotocol(coremaxproto: u32)— Validates HOSTMODULES are in ascending order and cover the max protocol.checkxdrversion_identities() -> Result<()>— Compares XDR file SHA256 hashes across crates.
Multi-Protocol Soroban Host Architecture
Design
stellar-core links multiple versions of soroban-env-host simultaneously, one per protocol version range. Each is labeled by its maximum supported protocol (e.g., soroban-env-host-p21 supports protocols up to 21). At runtime, gethostmoduleforprotocol(configmaxproto, ledger_protocol) selects the appropriate host.
Implementation pattern
sorobanprotoall.rs defines adaptor modules p21, p22, p23, p24, p25, and conditionally p26 (behind next feature). Each adaptor:
- Imports its specific
sorobanenvhostpNNcrate and re-exports it assorobanenv_host. - Provides adapter functions for API differences between host versions (e.g., different field names in
TransactionResources,RentFeeConfiguration). - Mounts
sorobanprotoany.rsas a child module — this file is the same source but "sees" a differentsuper::sorobanenvhostin each context. - Defines stub types (
ModuleCache,ErrorHandler,CompilationContext) for older protocols (p21, p22) that don't support the reusable module cache API.
Protocol dispatch
The HOSTMODULES static array maps protocol ranges to HostModule structs containing function pointers. gethostmoduleforprotocol iterates this array: each entry's implied minimum protocol is one more than the previous entry's maxproto (first entry starts at 0).
Aliases
soroban_curr— alias for the latest non-next host (p25, or p26 withnext).protocolagnostic— re-exports from p24 that are stable across versions (e.g.,int128helpers,make_error).
Key Data Flows
C++ → Soroban Invocation → C++
- C++ constructs
CxxBufobjects containing XDR-serialized data (host function, resources, ledger entries, etc.) and aCxxLedgerInfo. - Calls
stellar::rustbridge::invokehost_function(...)which crosses the FFI boundary. - Rust dispatches to the correct
HostModulebased on(configmaxprotocol, ledgerinfo.protocolversion). - The protocol-specific
invokehostfunctioninsorobanprotoany.rsdeserializes XDR, creates aBudget, optional trace hook, and calls through tosorobanenvhost::e2einvoke::invokehost_function. - Results are re-serialized to
RustBufvectors and returned asInvokeHostFunctionOutput. - The C++ wrapper in
RustBridge.cppunwraps the result or throwsrust::Erroron failure.
Logging (Rust → C++)
- Rust code calls
log::info!()etc. StellarLogger::log()converts the level and callsshim_logAtPartitionAndLevelvia the extern "C++" bridge.- The shim calls
Logging::logAtPartitionAndLevelin C++.
Module Cache Lifecycle
- C++ calls
newmodulecache()to get arust::Box<SorobanModuleCache>. - Calls
compile(protocol, wasm_bytes)to cache WASM modules (typically on startup and during catchup). - The cache is passed by reference to
invokehostfunction. shallow_clone()creates shared-ownership handles for multithreaded use.evictcontractcode(key)removes entries;clear()empties all caches.
Error Handling
- All fallible Rust functions return
Result<T, Box<dyn std::error::Error>>(orResult<T, HostError>). - cxx converts Rust
Errreturns into C++rust::Errorexceptions. invokehostfunctionandnetworkenjoysquorumintersectionadditionally wrap their core logic inpanic::catchunwindto convert Rust panics into errors rather than unwinding across the FFI boundary.- The quorum checker's memory limit is a hard abort (non-catchable) by design.
CoreHostErrorenum wraps either aHostErrorfrom soroban or a generalStringmessage.
Dependencies
| Crate | Purpose |
|---|---|
cxx 1.0.97 |
C++/Rust FFI bridge framework |
base64 0.13.1 |
Base64 encode/decode |
log 0.4.19 |
Rust logging facade |
ed25519-dalek 2.1.1 |
Ed25519 signature verification |
itertools 0.10.5 |
Iterator utilities |
backtrace 0.3.76 |
C++ backtrace capture (with cpp_demangle) |
rand 0.8.5 |
RNG (must match soroban's version) |
rustc-simple-version 0.1.0 |
Compile-time rustc version string |
tracy-client 0.17.0 |
Tracy profiling (optional) |
stellar-quorum-analyzer |
SAT-based quorum intersection checking |
soroban-env-host-pNN |
Protocol-specific Soroban hosts (p21–p26) |
soroban-test-wasms |
Pre-compiled test WASM binaries |
soroban-synth-wasm |
Random WASM generation for testing |
Build Notes
- The default build does not use the optional
soroban-env-host-pNNdeps from Cargo.toml. Instead, each host is built as a separate cargo invocation and linked in (seesrc/Makefile.am). This avoids Cargo's dependency unification. - The
unifiedfeature enables all hosts as direct dependencies for IDE usage. This perturbsCargo.lock— changes should not be committed. - Tracy feature flags must match between the Rust crate and the C++
lib/tracysubmodule version.