Skip to content

Commit f15679b

Browse files
authored
Split up rust bridge (#4707)
This is almost 100% code-motion -- just splitting the content of src/rust/lib.rs into separate files and doing a little bit of reorg/cleanup/commenting. There's more to do but this is at least a step in a direction of greater comprehensibility. Unfortunately the bridge module _itself_ can't easily be split up -- you can have multiple bridge modules but cxx.rs doesn't understand definitions that depend on definitions outside the current bridge module -- so we keep all the common data structures and bridged-function declarations in the bridge.rs module. But we can at least move all the _implementations_ of those functions out into their own separate topic-specific sub-modules.
2 parents eab5baa + 671037a commit f15679b

12 files changed

+1495
-1387
lines changed

src/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ $(RUST_CXXBRIDGE): Makefile $(RUST_TOOLCHAIN_FILE)
162162
mkdir -p $(RUST_BIN_DIR)
163163
CARGO_HTTP_MULTIPLEXING=false $(CARGO) install --force --locked --root $(RUST_BUILD_DIR) cxxbridge-cmd --version 1.0.68
164164

165-
rust/RustBridge.h: rust/src/lib.rs $(SRC_RUST_FILES) Makefile $(RUST_CXXBRIDGE)
165+
rust/RustBridge.h: rust/src/bridge.rs $(SRC_RUST_FILES) Makefile $(RUST_CXXBRIDGE)
166166
$(RUST_CXXBRIDGE) $< --cfg test=false --header --output $@.tmp
167167
if cmp -s $@.tmp $@; then rm -v $@.tmp; else mv -v $@.tmp $@; fi
168168

169-
rust/RustBridge.cpp: rust/src/lib.rs $(SRC_RUST_FILES) Makefile $(RUST_CXXBRIDGE)
169+
rust/RustBridge.cpp: rust/src/bridge.rs $(SRC_RUST_FILES) Makefile $(RUST_CXXBRIDGE)
170170
$(RUST_CXXBRIDGE) $< --cfg test=false --output $@.tmp
171171
if cmp -s $@.tmp $@; then rm -v $@.tmp; else mv -v $@.tmp $@; fi
172172

src/rust/Cargo.toml

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,39 @@ tracy-client = { version = "=0.17.0", features = [
7777
# the build and about 1.5MB of object code to the resulting binary. So we want
7878
# to keep the number of hosts down when and if it's easy to, but having the
7979
# number grow gradually is also not the end of the world.
80+
#
81+
82+
# Note: due to some subtle mis-features in Cargo's unification of transitive
83+
# dependency versions, we do _not_ refer to all the separate soroban crate
84+
# versions directly here. See $(top_srcdir)/src/Makefile.am for the logic that
85+
# builds each crate separately and then provides them to the stellar-core crate
86+
# here.
87+
#
88+
# However this fact makes it difficult to use an IDE to work on this crate since
89+
# the IDE will not be able to resolve the hosts to anything at all. So for the sake
90+
# of making IDE-edits, we keep some commented-out copies of the host dependencies
91+
# here. These are not used by the build system, but if you uncomment them while
92+
# working they will allow your IDE to at least find "nearly correct" versions of
93+
# the host dependencies. Make sure they are commented back out before committing
94+
# (and be careful to reset any changes the IDE makes to Cargo.lock)
95+
96+
# [dependencies.soroban-env-host-p23]
97+
# version = "=23.0.0"
98+
# git = "https://github.com/stellar/rs-soroban-env"
99+
# package = "soroban-env-host"
100+
# rev = "31cb455b87a25a1c049360b5422c411deae63ba2"
80101

81102
# [dependencies.soroban-env-host-p22]
82103
# version = "=22.0.0"
83104
# git = "https://github.com/stellar/rs-soroban-env"
84105
# package = "soroban-env-host"
85-
# rev = "0497816694bef2b103494c8c61b7c8a06a72c7d3"
106+
# rev = "1cd8b8dca9aeeca9ce45b129cd923992b32dc258"
86107

87108
# [dependencies.soroban-env-host-p21]
88-
# version = "=21.2.0"
109+
# version = "=21.2.2"
89110
# git = "https://github.com/stellar/rs-soroban-env"
90111
# package = "soroban-env-host"
91-
# rev = "8809852dcf8489f99407a5ceac12625ee3d14693"
112+
# rev = "7eeddd897cfb0f700f938b0c8d6f0541150d1fcb"
92113

93114
# The test wasms and synth-wasm crate should usually be taken from the highest
94115
# supported host, since test material usually just grows over time.

src/rust/src/bridge.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
// The cxx::bridge attribute says that everything in mod rust_bridge is
2+
// interpreted by cxx.rs.
3+
#[cxx::bridge]
4+
pub(crate) mod rust_bridge {
5+
// When we want to pass owned data _from_ C++, we typically want to pass it
6+
// as a C++-allocated std::vector<uint8_t>, because that's most-compatible
7+
// with all the C++ functions we're likely to be using to build it.
8+
//
9+
// Unfortunately cxx.rs has some limits around this (eg.
10+
// https://github.com/dtolnay/cxx/issues/671) So we need to embed it in a
11+
// struct that, itself, holds a unique_ptr. It's a bit silly but seems
12+
// harmless enough.
13+
struct CxxBuf {
14+
data: UniquePtr<CxxVector<u8>>,
15+
}
16+
17+
// When we want to return owned data _from_ Rust, we typically want to do
18+
// the opposite: allocate on the Rust side as a Vec<u8> and then let the C++
19+
// side parse the data out of it and then drop it.
20+
struct RustBuf {
21+
data: Vec<u8>,
22+
}
23+
24+
// We return these from get_xdr_hashes below.
25+
struct XDRFileHash {
26+
file: String,
27+
hash: String,
28+
}
29+
30+
// Result of invoking a host function.
31+
// When `success` is `false`, the function has failed. The diagnostic events
32+
// and metering data will be populated, but result value and effects won't
33+
// be populated.
34+
struct InvokeHostFunctionOutput {
35+
success: bool,
36+
// In case if `success` is `false` indicates whether the host has
37+
// failed with an internal error.
38+
// We don't otherwise observe the error codes, but internal errors are
39+
// something that should never happen, so it's important to be able
40+
// to act on them in Core.
41+
is_internal_error: bool,
42+
// Diagnostic information concerning the host function execution.
43+
diagnostic_events: Vec<RustBuf>,
44+
cpu_insns: u64,
45+
mem_bytes: u64,
46+
time_nsecs: u64,
47+
cpu_insns_excluding_vm_instantiation: u64,
48+
time_nsecs_excluding_vm_instantiation: u64,
49+
50+
// Effects of the invocation that are only populated in case of success.
51+
result_value: RustBuf,
52+
contract_events: Vec<RustBuf>,
53+
modified_ledger_entries: Vec<RustBuf>,
54+
rent_fee: i64,
55+
}
56+
57+
// LogLevel declares to cxx.rs a shared type that both Rust and C+++ will
58+
// understand.
59+
#[namespace = "stellar"]
60+
enum LogLevel {
61+
#[allow(unused)]
62+
LVL_FATAL = 0,
63+
LVL_ERROR = 1,
64+
LVL_WARNING = 2,
65+
LVL_INFO = 3,
66+
LVL_DEBUG = 4,
67+
LVL_TRACE = 5,
68+
}
69+
70+
struct CxxLedgerInfo {
71+
pub protocol_version: u32,
72+
pub sequence_number: u32,
73+
pub timestamp: u64,
74+
pub network_id: Vec<u8>,
75+
pub base_reserve: u32,
76+
pub memory_limit: u32,
77+
pub min_temp_entry_ttl: u32,
78+
pub min_persistent_entry_ttl: u32,
79+
pub max_entry_ttl: u32,
80+
pub cpu_cost_params: CxxBuf,
81+
pub mem_cost_params: CxxBuf,
82+
}
83+
84+
#[derive(Debug)]
85+
enum BridgeError {
86+
VersionNotYetSupported,
87+
}
88+
89+
struct SorobanVersionInfo {
90+
pub env_max_proto: u32,
91+
pub env_pkg_ver: String,
92+
pub env_git_rev: String,
93+
pub env_pre_release_ver: u32,
94+
95+
pub xdr_pkg_ver: String,
96+
pub xdr_git_rev: String,
97+
pub xdr_base_git_rev: String,
98+
pub xdr_file_hashes: Vec<XDRFileHash>,
99+
}
100+
101+
struct CxxTransactionResources {
102+
instructions: u32,
103+
read_entries: u32,
104+
write_entries: u32,
105+
read_bytes: u32,
106+
write_bytes: u32,
107+
contract_events_size_bytes: u32,
108+
transaction_size_bytes: u32,
109+
}
110+
111+
struct CxxFeeConfiguration {
112+
fee_per_instruction_increment: i64,
113+
fee_per_read_entry: i64,
114+
fee_per_write_entry: i64,
115+
fee_per_read_1kb: i64,
116+
fee_per_write_1kb: i64,
117+
fee_per_historical_1kb: i64,
118+
fee_per_contract_event_1kb: i64,
119+
fee_per_transaction_size_1kb: i64,
120+
}
121+
122+
struct CxxLedgerEntryRentChange {
123+
is_persistent: bool,
124+
old_size_bytes: u32,
125+
new_size_bytes: u32,
126+
old_live_until_ledger: u32,
127+
new_live_until_ledger: u32,
128+
}
129+
130+
struct CxxRentFeeConfiguration {
131+
fee_per_write_1kb: i64,
132+
fee_per_write_entry: i64,
133+
persistent_rent_rate_denominator: i64,
134+
temporary_rent_rate_denominator: i64,
135+
}
136+
137+
struct CxxWriteFeeConfiguration {
138+
bucket_list_target_size_bytes: i64,
139+
write_fee_1kb_bucket_list_low: i64,
140+
write_fee_1kb_bucket_list_high: i64,
141+
bucket_list_write_fee_growth_factor: u32,
142+
}
143+
144+
#[derive(Debug, PartialEq, Eq)]
145+
struct CxxI128 {
146+
hi: i64,
147+
lo: u64,
148+
}
149+
150+
struct FeePair {
151+
non_refundable_fee: i64,
152+
refundable_fee: i64,
153+
}
154+
155+
// The extern "Rust" block declares rust stuff we're going to export to C++.
156+
#[namespace = "stellar::rust_bridge"]
157+
extern "Rust" {
158+
fn to_base64(b: &CxxVector<u8>, mut s: Pin<&mut CxxString>);
159+
fn from_base64(s: &CxxString, mut b: Pin<&mut CxxVector<u8>>);
160+
fn check_sensible_soroban_config_for_protocol(core_max_proto: u32);
161+
fn invoke_host_function(
162+
config_max_protocol: u32,
163+
enable_diagnostics: bool,
164+
instruction_limit: u32,
165+
hf_buf: &CxxBuf,
166+
resources: CxxBuf,
167+
source_account: &CxxBuf,
168+
auth_entries: &Vec<CxxBuf>,
169+
ledger_info: CxxLedgerInfo,
170+
ledger_entries: &Vec<CxxBuf>,
171+
ttl_entries: &Vec<CxxBuf>,
172+
base_prng_seed: &CxxBuf,
173+
rent_fee_configuration: CxxRentFeeConfiguration,
174+
module_cache: &SorobanModuleCache,
175+
) -> Result<InvokeHostFunctionOutput>;
176+
177+
fn init_logging(maxLevel: LogLevel) -> Result<()>;
178+
179+
// Accessors for test wasms, compiled into soroban-test-wasms crate.
180+
fn get_test_wasm_add_i32() -> Result<RustBuf>;
181+
fn get_test_wasm_sum_i32() -> Result<RustBuf>;
182+
fn get_test_wasm_contract_data() -> Result<RustBuf>;
183+
fn get_test_wasm_complex() -> Result<RustBuf>;
184+
fn get_test_wasm_loadgen() -> Result<RustBuf>;
185+
fn get_test_wasm_err() -> Result<RustBuf>;
186+
fn get_test_contract_sac_transfer() -> Result<RustBuf>;
187+
fn get_write_bytes() -> Result<RustBuf>;
188+
fn get_invoke_contract_wasm() -> Result<RustBuf>;
189+
190+
fn get_hostile_large_val_wasm() -> Result<RustBuf>;
191+
192+
fn get_auth_wasm() -> Result<RustBuf>;
193+
194+
fn get_no_arg_constructor_wasm() -> Result<RustBuf>;
195+
fn get_constructor_with_args_p21_wasm() -> Result<RustBuf>;
196+
fn get_constructor_with_args_p22_wasm() -> Result<RustBuf>;
197+
198+
fn get_custom_account_wasm() -> Result<RustBuf>;
199+
200+
// Utility functions for generating wasms using soroban-synth-wasm.
201+
fn get_random_wasm(size: usize, seed: u64) -> Result<RustBuf>;
202+
203+
// Return the rustc version used to build this binary.
204+
fn get_rustc_version() -> String;
205+
206+
// Return the soroban versions linked into this binary. Panics
207+
// if the protocol version is not supported.
208+
fn get_soroban_version_info(core_max_proto: u32) -> Vec<SorobanVersionInfo>;
209+
210+
// Computes the resource fee given the transaction resource consumption
211+
// and network configuration.
212+
fn compute_transaction_resource_fee(
213+
config_max_protocol: u32,
214+
protocol_version: u32,
215+
tx_resources: CxxTransactionResources,
216+
fee_config: CxxFeeConfiguration,
217+
) -> Result<FeePair>;
218+
219+
// Computes the write fee per 1kb written to the ledger given the
220+
// current bucket list size and network configuration.
221+
fn compute_write_fee_per_1kb(
222+
config_max_protocol: u32,
223+
protocol_version: u32,
224+
bucket_list_size: i64,
225+
fee_config: CxxWriteFeeConfiguration,
226+
) -> Result<i64>;
227+
228+
// Computes the rent fee given the ledger entry changes and network
229+
// configuration.
230+
fn compute_rent_fee(
231+
config_max_protocol: u32,
232+
protocol_version: u32,
233+
changed_entries: &Vec<CxxLedgerEntryRentChange>,
234+
fee_config: CxxRentFeeConfiguration,
235+
current_ledger_seq: u32,
236+
) -> Result<i64>;
237+
238+
// Checks if a provided `TransactionEnvelope` XDR can be parsed in the
239+
// provided `protocol_version`.
240+
fn can_parse_transaction(
241+
config_max_protocol: u32,
242+
protocol_version: u32,
243+
xdr: &CxxBuf,
244+
depth_limit: u32,
245+
) -> Result<bool>;
246+
247+
fn i128_add(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>;
248+
249+
fn i128_sub(lhs: &CxxI128, rhs: &CxxI128) -> Result<CxxI128>;
250+
251+
fn i128_add_will_overflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>;
252+
253+
fn i128_sub_will_underflow(lhs: &CxxI128, rhs: &CxxI128) -> Result<bool>;
254+
255+
fn i128_from_i64(val: i64) -> Result<CxxI128>;
256+
257+
type SorobanModuleCache;
258+
259+
fn new_module_cache() -> Result<Box<SorobanModuleCache>>;
260+
fn compile(
261+
self: &mut SorobanModuleCache,
262+
ledger_protocol: u32,
263+
source: &[u8],
264+
) -> Result<()>;
265+
fn shallow_clone(self: &SorobanModuleCache) -> Result<Box<SorobanModuleCache>>;
266+
fn evict_contract_code(self: &mut SorobanModuleCache, key: &[u8]) -> Result<()>;
267+
fn clear(self: &mut SorobanModuleCache) -> Result<()>;
268+
fn contains_module(self: &SorobanModuleCache, protocol: u32, key: &[u8]) -> Result<bool>;
269+
fn get_mem_bytes_consumed(self: &SorobanModuleCache) -> Result<u64>;
270+
}
271+
272+
// And the extern "C++" block declares C++ stuff we're going to import to
273+
// Rust.
274+
#[namespace = "stellar"]
275+
unsafe extern "C++" {
276+
include!("rust/CppShims.h");
277+
// This declares (and asserts) that the external C++ definition of
278+
// stellar::LogLevel must match (in size and discriminant values) the
279+
// shared type declared above.
280+
type LogLevel;
281+
#[cfg(not(test))]
282+
fn shim_isLogLevelAtLeast(partition: &CxxString, level: LogLevel) -> Result<bool>;
283+
#[cfg(not(test))]
284+
fn shim_logAtPartitionAndLevel(
285+
partition: &CxxString,
286+
level: LogLevel,
287+
msg: &CxxString,
288+
) -> Result<()>;
289+
}
290+
}
291+
292+
// Because this file is processed with cxx.rs, we need to "use" the
293+
// definitions of all the symbols we declare above, just to make the
294+
// resulting code compile -- they all get referenced in the generated
295+
// code so they have to be in scope here.
296+
use crate::b64::*;
297+
use crate::common::*;
298+
use crate::i128::*;
299+
use crate::log::*;
300+
use crate::soroban_invoke::*;
301+
use crate::soroban_module_cache::*;
302+
use crate::soroban_proto_all::*;
303+
use crate::soroban_test_wasm::*;

0 commit comments

Comments
 (0)