Rust Subsystem (Non-Soroban) — Technical Summary
Overview
The rust subsystem provides a Rust static library (rust_stellar_core) 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
i128 to C++.
- Base64 encoding/decoding — used for XDR serialization interop.
- Ed25519 signature verification — using
ed25519-dalek for faster verification.
- Logging bridge — routing Rust
log crate output to the C++ spdlog system.
- Quorum intersection checking — using the
stellar-quorum-analyzer SAT 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; get_rustc_version, current_exe, capture_cxx_backtrace, check_xdr_version_identities |
src/b64.rs |
to_base64 / from_base64 |
src/ed25519_verify.rs |
verify_ed25519_signature_dalek (unsafe raw-pointer FFI) |
src/i128.rs |
i128_add, i128_sub, overflow/underflow checks, conversion |
src/log.rs |
StellarLogger implementing log::Log, routes to C++ spdlog |
src/quorum_checker.rs |
network_enjoys_quorum_intersection wrapping stellar-quorum-analyzer |
src/soroban_invoke.rs |
invoke_host_function, fee computation, transaction parsing dispatchers |
src/soroban_module_cache.rs |
SorobanModuleCache struct; per-protocol caches |
src/soroban_proto_all.rs |
Protocol-versioned host modules (p21–p26), dispatch table, adaptors |
src/soroban_proto_any.rs |
Protocol-agnostic host invocation code, mounted inside each pN module |
CppShims.h |
Thin C++ shim functions (shim_isLogLevelAtLeast, shim_logAtPartitionAndLevel) |
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:
shim_isLogLevelAtLeast(partition: &CxxString, level: LogLevel) -> Result<bool>
shim_logAtPartitionAndLevel(partition: &CxxString, level: LogLevel, msg: &CxxString) -> Result<()>
Data passing convention
- C++ → Rust: Data is passed as
CxxBuf containing UniquePtr<CxxVector<u8>> (a C++-allocated std::vector<uint8_t>). The Rust side reads from it via data.as_slice().
- Rust → C++: Data is returned as
RustBuf containing Vec<u8> (Rust-allocated). The C++ side reads from data (a rust::Vec<uint8_t>).
- XDR serialization/deserialization is done with
ReadXdr/WriteXdr using non_metered_xdr_from_cxx_buf and non_metered_xdr_to_rust_buf helper functions with a depth limit of 1000 and length limit matching the buffer size.
RustVecXdrMarshal.h allows xdrpp to directly unmarshal from rust::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_assert checks ensuring LogLevel enum values match between C++ and Rust.
extern "C" function declarations for the mangled bridge symbols.
- C++ wrapper functions in
namespace stellar::rust_bridge that 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::isLogLevelAtLeast
shim_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::unique_ptr<std::vector<uint8_t>> (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 int128_helpers::{i128_from_pieces, i128_hi, i128_lo}.
InvokeHostFunctionOutput
Return value of invoke_host_function. Contains:
success: bool, is_internal_error: bool
diagnostic_events: Vec<RustBuf> (XDR-encoded DiagnosticEvent)
cpu_insns, mem_bytes, time_nsecs (and excluding-VM-instantiation variants)
result_value: RustBuf, contract_events: Vec<RustBuf>, modified_ledger_entries: 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.
evict_contract_code(&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]) -> bool
get_mem_bytes_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 max_proto, invoke_host_function, compute_transaction_resource_fee, compute_rent_fee, compute_rent_write_fee_per_1kb, contract_code_memory_size_for_rent, can_parse_transaction, and get_soroban_version_info. The static array HOST_MODULES holds one entry per protocol version (p21–p25/p26), populated via the proto_versioned_functions_for_module! macro.
ProtocolSpecificModuleCache
Per-protocol cache wrapper (defined in soroban_proto_any.rs). Wraps a ModuleCache from the protocol's soroban-env-host and a CoreCompilationContext (unlimited budget for compilation). Supports compile, evict, clear, contains_module, get_mem_bytes_consumed, 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
invoke_host_function(config_max_protocol: u32, enable_diagnostics: bool, instruction_limit: u32, hf_buf: &CxxBuf, resources: CxxBuf, restored_rw_entry_indices: &Vec<u32>, source_account: &CxxBuf, auth_entries: &Vec<CxxBuf>, ledger_info: CxxLedgerInfo, ledger_entries: &Vec<CxxBuf>, ttl_entries: &Vec<CxxBuf>, base_prng_seed: &CxxBuf, rent_fee_configuration: CxxRentFeeConfiguration, module_cache: &SorobanModuleCache) -> Result<InvokeHostFunctionOutput> — Dispatches to the correct protocol-versioned host via get_host_module_for_protocol. Wraps the call in panic::catch_unwind.
Fee Computation
compute_transaction_resource_fee(config_max_protocol: u32, protocol_version: u32, tx_resources: CxxTransactionResources, fee_config: CxxFeeConfiguration) -> Result<FeePair> — Returns (non_refundable_fee, refundable_fee).
compute_rent_fee(config_max_protocol: u32, protocol_version: u32, changed_entries: &Vec<CxxLedgerEntryRentChange>, fee_config: CxxRentFeeConfiguration, current_ledger_seq: u32) -> Result<i64>
compute_rent_write_fee_per_1kb(config_max_protocol: u32, protocol_version: u32, bucket_list_size: i64, fee_config: CxxRentWriteFeeConfiguration) -> Result<i64>
contract_code_memory_size_for_rent(config_max_protocol: u32, protocol_version: u32, contract_code_entry: &CxxBuf, cpu_cost_params: &CxxBuf, mem_cost_params: &CxxBuf) -> Result<u32> — Only valid for protocol ≥ 23.
Transaction Parsing
can_parse_transaction(config_max_protocol: u32, protocol_version: u32, xdr: &CxxBuf, depth_limit: u32) -> Result<bool> — Checks if a TransactionEnvelope XDR 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>
i128_add_will_overflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>
i128_sub_will_underflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>
i128_from_i64(val: i64) -> Result<CxxI128>
i128_is_negative(val: &CxxI128) -> Result<bool>
i128_i64_eq(lhs: &CxxI128, rhs: i64) -> Result<bool>
Ed25519 Verification
verify_ed25519_signature_dalek(public_key_ptr: *const u8, signature_ptr: *const u8, message_ptr: *const u8, message_len: usize) -> bool — Unsafe raw-pointer interface. Uses ed25519-dalek's verify_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 the StellarLogger as the global Rust logger, routing to C++ spdlog. Uses AtomicBool for one-time initialization. Log partitions (e.g., TX, Ledger, SCP) are defined in log::partition and must match util/LogPartitions.def on the C++ side.
Quorum Checker
network_enjoys_quorum_intersection(nodes: &Vec<CxxBuf>, quorum_set: &Vec<CxxBuf>, potential_split: &mut QuorumSplit, resource_limit: &QuorumCheckerResource, resource_usage: &mut QuorumCheckerResource) -> Result<QuorumCheckerStatus> — Returns UNSAT (quorum intersection holds), SAT (split found, populates potential_split), or UNKNOWN. Time limit enforced internally; memory limit is a hard abort via global allocator.
Module Cache
new_module_cache() -> Result<Box<SorobanModuleCache>>
- Methods on
SorobanModuleCache: compile, shallow_clone, evict_contract_code, clear, contains_module, get_mem_bytes_consumed.
Utility
get_rustc_version() -> String
current_exe() -> Result<String>
capture_cxx_backtrace() -> String — Uses backtrace crate; filters out initial Rust frames and libc frames.
get_soroban_version_info(core_max_proto: u32) -> Vec<SorobanVersionInfo> — Returns version info for all linked soroban hosts. Panics if no host supports the given protocol.
check_sensible_soroban_config_for_protocol(core_max_proto: u32) — Validates HOST_MODULES are in ascending order and cover the max protocol.
check_xdr_version_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, get_host_module_for_protocol(config_max_proto, ledger_protocol) selects the appropriate host.
Implementation pattern
soroban_proto_all.rs defines adaptor modules p21, p22, p23, p24, p25, and conditionally p26 (behind next feature). Each adaptor:
- Imports its specific
soroban_env_host_pNN crate and re-exports it as soroban_env_host.
- Provides adapter functions for API differences between host versions (e.g., different field names in
TransactionResources, RentFeeConfiguration).
- Mounts
soroban_proto_any.rs as a child module — this file is the same source but "sees" a different super::soroban_env_host in 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 HOST_MODULES static array maps protocol ranges to HostModule structs containing function pointers. get_host_module_for_protocol iterates this array: each entry's implied minimum protocol is one more than the previous entry's max_proto (first entry starts at 0).
Aliases
soroban_curr — alias for the latest non-next host (p25, or p26 with next).
protocol_agnostic — re-exports from p24 that are stable across versions (e.g., int128_helpers, make_error).
Key Data Flows
C++ → Soroban Invocation → C++
- C++ constructs
CxxBuf objects containing XDR-serialized data (host function, resources, ledger entries, etc.) and a CxxLedgerInfo.
- Calls
stellar::rust_bridge::invoke_host_function(...) which crosses the FFI boundary.
- Rust dispatches to the correct
HostModule based on (config_max_protocol, ledger_info.protocol_version).
- The protocol-specific
invoke_host_function in soroban_proto_any.rs deserializes XDR, creates a Budget, optional trace hook, and calls through to soroban_env_host::e2e_invoke::invoke_host_function.
- Results are re-serialized to
RustBuf vectors and returned as InvokeHostFunctionOutput.
- The C++ wrapper in
RustBridge.cpp unwraps the result or throws rust::Error on failure.
Logging (Rust → C++)
- Rust code calls
log::info!() etc.
StellarLogger::log() converts the level and calls shim_logAtPartitionAndLevel via the extern "C++" bridge.
- The shim calls
Logging::logAtPartitionAndLevel in C++.
Module Cache Lifecycle
- C++ calls
new_module_cache() to get a rust::Box<SorobanModuleCache>.
- Calls
compile(protocol, wasm_bytes) to cache WASM modules (typically on startup and during catchup).
- The cache is passed by reference to
invoke_host_function.
shallow_clone() creates shared-ownership handles for multithreaded use.
evict_contract_code(key) removes entries; clear() empties all caches.
Error Handling
- All fallible Rust functions return
Result<T, Box<dyn std::error::Error>> (or Result<T, HostError>).
- cxx converts Rust
Err returns into C++ rust::Error exceptions.
invoke_host_function and network_enjoys_quorum_intersection additionally wrap their core logic in panic::catch_unwind to 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.
CoreHostError enum wraps either a HostError from soroban or a general String message.
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-pNN deps from Cargo.toml. Instead, each host is built as a separate cargo invocation and linked in (see src/Makefile.am). This avoids Cargo's dependency unification.
- The
unified feature enables all hosts as direct dependencies for IDE usage. This perturbs Cargo.lock — changes should not be committed.
- Tracy feature flags must match between the Rust crate and the C++
lib/tracy submodule version.
1---2name: subsystem-summary-of-rust3description: read this skill for a token-efficient summary of the rust subsystem4---56# Rust Subsystem (Non-Soroban) — Technical Summary78## Overview910The rust subsystem provides a Rust static library (`rust_stellar_core`) 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:11121. **Soroban host function invocation** — dispatching to the correct protocol-versioned soroban host.132. **Fee computation** — transaction resource fees, rent fees, and rent write fees.143. **Module caching** — pre-compiled WASM module cache for Soroban contracts.154. **128-bit integer arithmetic** — exposing Rust's native `i128` to C++.165. **Base64 encoding/decoding** — used for XDR serialization interop.176. **Ed25519 signature verification** — using `ed25519-dalek` for faster verification.187. **Logging bridge** — routing Rust `log` crate output to the C++ spdlog system.198. **Quorum intersection checking** — using the `stellar-quorum-analyzer` SAT solver.209. **Utility functions** — rustc version, executable path, backtrace capture, XDR version checks.2122The 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).2324## File Layout2526| File | Role |27|------|------|28| `Cargo.toml` | Crate metadata, multi-host soroban dependencies, feature flags |29| `src/lib.rs` | Crate root; declares modules, re-exports bridge symbols, defines `tracy_span!` macro |30| `src/bridge.rs` | `#[cxx::bridge]` module — all FFI type/function declarations |31| `src/common.rs` | `RustBuf`/`CxxBuf`/`BridgeError` impls; `get_rustc_version`, `current_exe`, `capture_cxx_backtrace`, `check_xdr_version_identities` |32| `src/b64.rs` | `to_base64` / `from_base64` |33| `src/ed25519_verify.rs` | `verify_ed25519_signature_dalek` (unsafe raw-pointer FFI) |34| `src/i128.rs` | `i128_add`, `i128_sub`, overflow/underflow checks, conversion |35| `src/log.rs` | `StellarLogger` implementing `log::Log`, routes to C++ spdlog |36| `src/quorum_checker.rs` | `network_enjoys_quorum_intersection` wrapping `stellar-quorum-analyzer` |37| `src/soroban_invoke.rs` | `invoke_host_function`, fee computation, transaction parsing dispatchers |38| `src/soroban_module_cache.rs` | `SorobanModuleCache` struct; per-protocol caches |39| `src/soroban_proto_all.rs` | Protocol-versioned host modules (p21–p26), dispatch table, adaptors |40| `src/soroban_proto_any.rs` | Protocol-agnostic host invocation code, mounted inside each pN module |41| `CppShims.h` | Thin C++ shim functions (`shim_isLogLevelAtLeast`, `shim_logAtPartitionAndLevel`) |42| `RustBridge.h` | cxx-generated C++ header with all bridge types and function declarations |43| `RustBridge.cpp` | cxx-generated C++ implementation (extern "C" thunks, Vec/Box specializations) |44| `RustVecXdrMarshal.h` | Declares `rust::Vec<uint8_t>` as valid xdrpp byte buffer type |4546## The CXX Bridge Mechanism4748### How it works4950The bridge is defined in `src/bridge.rs` inside a `#[cxx::bridge]` attribute macro on `mod rust_bridge`. This module contains three sections:51521. **Shared types** — structs and enums visible to both sides, defined once:53 - `CxxBuf` (C++→Rust data: wraps `UniquePtr<CxxVector<u8>>`)54 - `RustBuf` (Rust→C++ data: wraps `Vec<u8>`)55 - `XDRFileHash`, `InvokeHostFunctionOutput`, `CxxLedgerInfo`, `CxxTransactionResources`, `CxxFeeConfiguration`, `CxxLedgerEntryRentChange`, `CxxRentFeeConfiguration`, `CxxRentWriteFeeConfiguration`, `CxxI128`, `FeePair`, `SorobanVersionInfo`56 - Enums: `LogLevel` (shared with `stellar::LogLevel`), `BridgeError`, `QuorumCheckerStatus`57 - `QuorumSplit`, `QuorumCheckerResource`58592. **`extern "Rust"` block** (`#[namespace = "stellar::rust_bridge"]`) — Rust functions callable from C++:60 - All functions listed in the "Key Functions" section below.61 - The opaque type `SorobanModuleCache` with its methods.62633. **`extern "C++"` block** (`#[namespace = "stellar"]`) — C++ functions callable from Rust:64 - `shim_isLogLevelAtLeast(partition: &CxxString, level: LogLevel) -> Result<bool>`65 - `shim_logAtPartitionAndLevel(partition: &CxxString, level: LogLevel, msg: &CxxString) -> Result<()>`6667### Data passing convention6869- **C++ → Rust**: Data is passed as `CxxBuf` containing `UniquePtr<CxxVector<u8>>` (a C++-allocated `std::vector<uint8_t>`). The Rust side reads from it via `data.as_slice()`.70- **Rust → C++**: Data is returned as `RustBuf` containing `Vec<u8>` (Rust-allocated). The C++ side reads from `data` (a `rust::Vec<uint8_t>`).71- XDR serialization/deserialization is done with `ReadXdr`/`WriteXdr` using `non_metered_xdr_from_cxx_buf` and `non_metered_xdr_to_rust_buf` helper functions with a depth limit of 1000 and length limit matching the buffer size.72- `RustVecXdrMarshal.h` allows xdrpp to directly unmarshal from `rust::Vec<uint8_t>`.7374### Generated files7576`RustBridge.h` and `RustBridge.cpp` are generated by the `cxxbridge` tool. They contain:77- Full implementations of `rust::String`, `rust::Slice<T>`, `rust::Box<T>`, `rust::Vec<T>`, `rust::Opaque`, `rust::Error`.78- C struct definitions mirroring the shared types.79- `static_assert` checks ensuring `LogLevel` enum values match between C++ and Rust.80- `extern "C"` function declarations for the mangled bridge symbols.81- C++ wrapper functions in `namespace stellar::rust_bridge` that call through extern "C" thunks and translate Rust errors to C++ exceptions (`rust::Error`).82- Template specializations for `rust::Vec<RustBuf>`, `rust::Vec<XDRFileHash>`, `rust::Vec<CxxBuf>`, etc.83- `rust::Box<SorobanModuleCache>` alloc/dealloc/drop specializations.8485### CppShims.h8687Provides 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):88- `shim_isLogLevelAtLeast` → `Logging::isLogLevelAtLeast`89- `shim_logAtPartitionAndLevel` → `Logging::logAtPartitionAndLevel`9091## Key Data Structures9293### `CxxBuf` / `RustBuf`94Directional byte-buffer wrappers for passing XDR-serialized data across the FFI boundary. `CxxBuf` owns a `std::unique_ptr<std::vector<uint8_t>>` (C++ allocated). `RustBuf` owns a `Vec<u8>` (Rust allocated). Both implement `AsRef<[u8]>`.9596### `CxxI128`97Split representation of 128-bit integer: `{ hi: i64, lo: u64 }`. Used because C++ lacks native `i128` on all platforms. Converted to/from Rust `i128` via `int128_helpers::{i128_from_pieces, i128_hi, i128_lo}`.9899### `InvokeHostFunctionOutput`100Return value of `invoke_host_function`. Contains:101- `success: bool`, `is_internal_error: bool`102- `diagnostic_events: Vec<RustBuf>` (XDR-encoded `DiagnosticEvent`)103- `cpu_insns`, `mem_bytes`, `time_nsecs` (and excluding-VM-instantiation variants)104- `result_value: RustBuf`, `contract_events: Vec<RustBuf>`, `modified_ledger_entries: Vec<RustBuf>`, `rent_fee: i64`105106### `SorobanModuleCache`107An 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:108- `compile(&mut self, ledger_protocol: u32, wasm: &[u8])` — parse and cache a WASM module for the given protocol.109- `shallow_clone(&self) -> Box<SorobanModuleCache>` — clone shared ownership handles for multithreaded compilation.110- `evict_contract_code(&mut self, key: &[u8])` — remove a module from all protocol caches by 32-byte hash.111- `clear(&mut self)` — clear all protocol caches.112- `contains_module(&self, protocol: u32, key: &[u8]) -> bool`113- `get_mem_bytes_consumed(&self, protocol: u32) -> u64`114115### `HostModule`116A dispatch table struct (not crossing FFI) containing function pointers for a specific protocol version's soroban host. Fields include `max_proto`, `invoke_host_function`, `compute_transaction_resource_fee`, `compute_rent_fee`, `compute_rent_write_fee_per_1kb`, `contract_code_memory_size_for_rent`, `can_parse_transaction`, and `get_soroban_version_info`. The static array `HOST_MODULES` holds one entry per protocol version (p21–p25/p26), populated via the `proto_versioned_functions_for_module!` macro.117118### `ProtocolSpecificModuleCache`119Per-protocol cache wrapper (defined in `soroban_proto_any.rs`). Wraps a `ModuleCache` from the protocol's soroban-env-host and a `CoreCompilationContext` (unlimited budget for compilation). Supports `compile`, `evict`, `clear`, `contains_module`, `get_mem_bytes_consumed`, and `shallow_clone`.120121### `CoreCompilationContext`122Implements `CompilationContext` (= `ErrorHandler + AsBudget`) with an unlimited budget, used for compiling WASM modules outside of transaction execution.123124## Key Functions (Exported Rust → C++)125126### Soroban Host Invocation127- `invoke_host_function(config_max_protocol: u32, enable_diagnostics: bool, instruction_limit: u32, hf_buf: &CxxBuf, resources: CxxBuf, restored_rw_entry_indices: &Vec<u32>, source_account: &CxxBuf, auth_entries: &Vec<CxxBuf>, ledger_info: CxxLedgerInfo, ledger_entries: &Vec<CxxBuf>, ttl_entries: &Vec<CxxBuf>, base_prng_seed: &CxxBuf, rent_fee_configuration: CxxRentFeeConfiguration, module_cache: &SorobanModuleCache) -> Result<InvokeHostFunctionOutput>` — Dispatches to the correct protocol-versioned host via `get_host_module_for_protocol`. Wraps the call in `panic::catch_unwind`.128129### Fee Computation130- `compute_transaction_resource_fee(config_max_protocol: u32, protocol_version: u32, tx_resources: CxxTransactionResources, fee_config: CxxFeeConfiguration) -> Result<FeePair>` — Returns `(non_refundable_fee, refundable_fee)`.131- `compute_rent_fee(config_max_protocol: u32, protocol_version: u32, changed_entries: &Vec<CxxLedgerEntryRentChange>, fee_config: CxxRentFeeConfiguration, current_ledger_seq: u32) -> Result<i64>`132- `compute_rent_write_fee_per_1kb(config_max_protocol: u32, protocol_version: u32, bucket_list_size: i64, fee_config: CxxRentWriteFeeConfiguration) -> Result<i64>`133- `contract_code_memory_size_for_rent(config_max_protocol: u32, protocol_version: u32, contract_code_entry: &CxxBuf, cpu_cost_params: &CxxBuf, mem_cost_params: &CxxBuf) -> Result<u32>` — Only valid for protocol ≥ 23.134135### Transaction Parsing136- `can_parse_transaction(config_max_protocol: u32, protocol_version: u32, xdr: &CxxBuf, depth_limit: u32) -> Result<bool>` — Checks if a `TransactionEnvelope` XDR can be deserialized in the given protocol.137138### 128-bit Integer Arithmetic139- `i128_add(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>`140- `i128_sub(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>`141- `i128_add_will_overflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>`142- `i128_sub_will_underflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>`143- `i128_from_i64(val: i64) -> Result<CxxI128>`144- `i128_is_negative(val: &CxxI128) -> Result<bool>`145- `i128_i64_eq(lhs: &CxxI128, rhs: i64) -> Result<bool>`146147### Ed25519 Verification148- `verify_ed25519_signature_dalek(public_key_ptr: *const u8, signature_ptr: *const u8, message_ptr: *const u8, message_len: usize) -> bool` — Unsafe raw-pointer interface. Uses `ed25519-dalek`'s `verify_strict` (rejects small-order points, matching libsodium). Never panics; returns false for invalid input.149150### Base64151- `to_base64(b: &CxxVector<u8>, s: Pin<&mut CxxString>)` — Encode bytes to base64.152- `from_base64(s: &CxxString, b: Pin<&mut CxxVector<u8>>)` — Decode base64 with error-tolerant stripping of invalid characters.153154### Logging155- `init_logging(maxLevel: LogLevel) -> Result<()>` — Initializes the `StellarLogger` as the global Rust logger, routing to C++ spdlog. Uses `AtomicBool` for one-time initialization. Log partitions (e.g., `TX`, `Ledger`, `SCP`) are defined in `log::partition` and must match `util/LogPartitions.def` on the C++ side.156157### Quorum Checker158- `network_enjoys_quorum_intersection(nodes: &Vec<CxxBuf>, quorum_set: &Vec<CxxBuf>, potential_split: &mut QuorumSplit, resource_limit: &QuorumCheckerResource, resource_usage: &mut QuorumCheckerResource) -> Result<QuorumCheckerStatus>` — Returns `UNSAT` (quorum intersection holds), `SAT` (split found, populates `potential_split`), or `UNKNOWN`. Time limit enforced internally; memory limit is a hard abort via global allocator.159160### Module Cache161- `new_module_cache() -> Result<Box<SorobanModuleCache>>`162- Methods on `SorobanModuleCache`: `compile`, `shallow_clone`, `evict_contract_code`, `clear`, `contains_module`, `get_mem_bytes_consumed`.163164### Utility165- `get_rustc_version() -> String`166- `current_exe() -> Result<String>`167- `capture_cxx_backtrace() -> String` — Uses `backtrace` crate; filters out initial Rust frames and libc frames.168- `get_soroban_version_info(core_max_proto: u32) -> Vec<SorobanVersionInfo>` — Returns version info for all linked soroban hosts. Panics if no host supports the given protocol.169- `check_sensible_soroban_config_for_protocol(core_max_proto: u32)` — Validates HOST_MODULES are in ascending order and cover the max protocol.170- `check_xdr_version_identities() -> Result<()>` — Compares XDR file SHA256 hashes across crates.171172## Multi-Protocol Soroban Host Architecture173174### Design175176stellar-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, `get_host_module_for_protocol(config_max_proto, ledger_protocol)` selects the appropriate host.177178### Implementation pattern179180`soroban_proto_all.rs` defines adaptor modules `p21`, `p22`, `p23`, `p24`, `p25`, and conditionally `p26` (behind `next` feature). Each adaptor:1811. Imports its specific `soroban_env_host_pNN` crate and re-exports it as `soroban_env_host`.1822. Provides adapter functions for API differences between host versions (e.g., different field names in `TransactionResources`, `RentFeeConfiguration`).1833. Mounts `soroban_proto_any.rs` as a child module — this file is the same source but "sees" a different `super::soroban_env_host` in each context.1844. Defines stub types (`ModuleCache`, `ErrorHandler`, `CompilationContext`) for older protocols (p21, p22) that don't support the reusable module cache API.185186### Protocol dispatch187188The `HOST_MODULES` static array maps protocol ranges to `HostModule` structs containing function pointers. `get_host_module_for_protocol` iterates this array: each entry's implied minimum protocol is one more than the previous entry's `max_proto` (first entry starts at 0).189190### Aliases191192- `soroban_curr` — alias for the latest non-next host (p25, or p26 with `next`).193- `protocol_agnostic` — re-exports from p24 that are stable across versions (e.g., `int128_helpers`, `make_error`).194195## Key Data Flows196197### C++ → Soroban Invocation → C++1981. C++ constructs `CxxBuf` objects containing XDR-serialized data (host function, resources, ledger entries, etc.) and a `CxxLedgerInfo`.1992. Calls `stellar::rust_bridge::invoke_host_function(...)` which crosses the FFI boundary.2003. Rust dispatches to the correct `HostModule` based on `(config_max_protocol, ledger_info.protocol_version)`.2014. The protocol-specific `invoke_host_function` in `soroban_proto_any.rs` deserializes XDR, creates a `Budget`, optional trace hook, and calls through to `soroban_env_host::e2e_invoke::invoke_host_function`.2025. Results are re-serialized to `RustBuf` vectors and returned as `InvokeHostFunctionOutput`.2036. The C++ wrapper in `RustBridge.cpp` unwraps the result or throws `rust::Error` on failure.204205### Logging (Rust → C++)2061. Rust code calls `log::info!()` etc.2072. `StellarLogger::log()` converts the level and calls `shim_logAtPartitionAndLevel` via the extern "C++" bridge.2083. The shim calls `Logging::logAtPartitionAndLevel` in C++.209210### Module Cache Lifecycle2111. C++ calls `new_module_cache()` to get a `rust::Box<SorobanModuleCache>`.2122. Calls `compile(protocol, wasm_bytes)` to cache WASM modules (typically on startup and during catchup).2133. The cache is passed by reference to `invoke_host_function`.2144. `shallow_clone()` creates shared-ownership handles for multithreaded use.2155. `evict_contract_code(key)` removes entries; `clear()` empties all caches.216217## Error Handling218219- All fallible Rust functions return `Result<T, Box<dyn std::error::Error>>` (or `Result<T, HostError>`).220- cxx converts Rust `Err` returns into C++ `rust::Error` exceptions.221- `invoke_host_function` and `network_enjoys_quorum_intersection` additionally wrap their core logic in `panic::catch_unwind` to convert Rust panics into errors rather than unwinding across the FFI boundary.222- The quorum checker's memory limit is a hard abort (non-catchable) by design.223- `CoreHostError` enum wraps either a `HostError` from soroban or a general `String` message.224225## Dependencies226227| Crate | Purpose |228|-------|---------|229| `cxx` 1.0.97 | C++/Rust FFI bridge framework |230| `base64` 0.13.1 | Base64 encode/decode |231| `log` 0.4.19 | Rust logging facade |232| `ed25519-dalek` 2.1.1 | Ed25519 signature verification |233| `itertools` 0.10.5 | Iterator utilities |234| `backtrace` 0.3.76 | C++ backtrace capture (with `cpp_demangle`) |235| `rand` 0.8.5 | RNG (must match soroban's version) |236| `rustc-simple-version` 0.1.0 | Compile-time rustc version string |237| `tracy-client` 0.17.0 | Tracy profiling (optional) |238| `stellar-quorum-analyzer` | SAT-based quorum intersection checking |239| `soroban-env-host-pNN` | Protocol-specific Soroban hosts (p21–p26) |240| `soroban-test-wasms` | Pre-compiled test WASM binaries |241| `soroban-synth-wasm` | Random WASM generation for testing |242243## Build Notes244245- The default build does **not** use the optional `soroban-env-host-pNN` deps from Cargo.toml. Instead, each host is built as a separate cargo invocation and linked in (see `src/Makefile.am`). This avoids Cargo's dependency unification.246- The `unified` feature enables all hosts as direct dependencies for IDE usage. This perturbs `Cargo.lock` — changes should not be committed.247- Tracy feature flags must match between the Rust crate and the C++ `lib/tracy` submodule version.