From d1ce22022ec2b853ee78ef1177e3b927ba040711 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Mon, 12 Aug 2024 14:30:15 -0700 Subject: [PATCH] generalize curr-and-prev, use a single shared module --- Cargo.toml | 8 +- cmd/soroban-rpc/lib/preflight/Cargo.toml | 8 +- cmd/soroban-rpc/lib/preflight/src/lib.rs | 781 +++-------------------- 3 files changed, 87 insertions(+), 710 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 800ae1ff..04d0627e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,21 +7,21 @@ members = [ [workspace.package] rust-version = "1.74.0" -[workspace.dependencies.soroban-env-host-21] +[workspace.dependencies.soroban-env-host-prev] package = "soroban-env-host" version = "=21.2.0" -[workspace.dependencies.soroban-env-host] +[workspace.dependencies.soroban-env-host-curr] package = "soroban-env-host" version = "=22.0.0" git = "https://github.com/stellar/rs-soroban-env" rev = "b01e79899ceeeb3d39f9531bbc09b39bf0149da8" -[workspace.dependencies.soroban-simulation-21] +[workspace.dependencies.soroban-simulation-prev] package = "soroban-simulation" version = "=21.2.0" -[workspace.dependencies.soroban-simulation] +[workspace.dependencies.soroban-simulation-curr] package = "soroban-simulation" version = "=22.0.0" git = "https://github.com/stellar/rs-soroban-env" diff --git a/cmd/soroban-rpc/lib/preflight/Cargo.toml b/cmd/soroban-rpc/lib/preflight/Cargo.toml index fd3674fd..414d8015 100644 --- a/cmd/soroban-rpc/lib/preflight/Cargo.toml +++ b/cmd/soroban-rpc/lib/preflight/Cargo.toml @@ -12,9 +12,9 @@ libc = { workspace = true } sha2 = { workspace = true } # we need the testutils feature in order to get backtraces in the preflight library # when soroban rpc is configured to run with --preflight-enable-debug -soroban-env-host-21 = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} -soroban-simulation-21 = { workspace = true } -soroban-env-host = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} -soroban-simulation = { workspace = true } +soroban-env-host-prev = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} +soroban-simulation-prev = { workspace = true } +soroban-env-host-curr = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} +soroban-simulation-curr = { workspace = true } anyhow = { workspace = true } rand = { workspace = true } diff --git a/cmd/soroban-rpc/lib/preflight/src/lib.rs b/cmd/soroban-rpc/lib/preflight/src/lib.rs index 86d45583..d73bdc2b 100644 --- a/cmd/soroban-rpc/lib/preflight/src/lib.rs +++ b/cmd/soroban-rpc/lib/preflight/src/lib.rs @@ -2,49 +2,48 @@ extern crate anyhow; extern crate base64; extern crate libc; extern crate sha2; -extern crate soroban_env_host; -extern crate soroban_env_host_21; -extern crate soroban_simulation; -extern crate soroban_simulation_21; - -use anyhow::{anyhow, bail, Result}; -use sha2::{Digest, Sha256}; -use soroban_env_host::storage::EntryWithLiveUntil; -use soroban_env_host::xdr::{ - AccountId, ExtendFootprintTtlOp, Hash, InvokeHostFunctionOp, LedgerEntry, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyTtl, OperationBody, ReadXdr, ScErrorCode, ScErrorType, - SorobanTransactionData, TtlEntry, WriteXdr, -}; -use soroban_env_host::{HostError, LedgerInfo, DEFAULT_XDR_RW_LIMITS}; -use soroban_simulation::simulation::{ - simulate_extend_ttl_op, simulate_invoke_host_function_op, simulate_restore_op, - InvokeHostFunctionSimulationResult, LedgerEntryDiff, RestoreOpSimulationResult, - SimulationAdjustmentConfig, -}; -use soroban_simulation::{AutoRestoringSnapshotSource, NetworkConfig, SnapshotSourceWithArchive}; - -// Imports for the legacy protocol-21 version; these (and everything that uses -// them) can be deleted once the network transitions fully to protocol 22. -use soroban_env_host_21::{HostError as HostError21, LedgerInfo as LedgerInfo21}; -use soroban_simulation_21::simulation::{ - simulate_extend_ttl_op as simulate_extend_ttl_op_21, - simulate_invoke_host_function_op as simulate_invoke_host_function_op_21, - simulate_restore_op as simulate_restore_op_21, - InvokeHostFunctionSimulationResult as InvokeHostFunctionSimulationResult21, - LedgerEntryDiff as LedgerEntryDiff21, RestoreOpSimulationResult as RestoreOpSimulationResult21, - SimulationAdjustmentConfig as SimulationAdjustmentConfig21, -}; -use soroban_simulation_21::{ - AutoRestoringSnapshotSource as AutoRestoringSnapshotSource21, NetworkConfig as NetworkConfig21, - SnapshotSourceWithArchive as SnapshotSourceWithArchive21, -}; +extern crate soroban_env_host_curr; +extern crate soroban_env_host_prev; +extern crate soroban_simulation_curr; +extern crate soroban_simulation_prev; + +pub(crate) use anyhow::{anyhow, bail, Result}; +pub(crate) use sha2::{Digest, Sha256}; + +// We support two different versions of soroban simutlaneously, switching on the +// protocol version each supports. This is the exact same mechanism we use in +// stellar-core to switch soroban hosts on protocol boundaries, and allows +// synchronously cutting over between significantly different versions of the +// host (or VM) without having to do fine-grained versioning within the VM. +// +// The way it is _accomplished_ is by mounting the same adaptor code (in +// `shared.rs`) at two different paths in the module tree, and then providing +// each with a different binding for the soroban host and simulation code. Any +// function that mentions a type from the soroban host or simulation code must +// be placed in the `shared.rs` file. Code that is host-version-agnostic can +// continue to live in this file. +// +// This is a bit of a hack, but it works well enough for our purposes and works +// around the absence of parametric modules in the Rust language. + +#[path = "."] +mod curr { + pub(crate) use soroban_env_host_curr as soroban_env_host; + pub(crate) use soroban_simulation_curr as soroban_simulation; + pub(crate) mod shared; +} + +#[path = "."] +mod prev { + pub(crate) use soroban_env_host_prev as soroban_env_host; + pub(crate) use soroban_simulation_prev as soroban_simulation; + pub(crate) mod shared; +} use std::cell::RefCell; -use std::convert::TryFrom; use std::ffi::{CStr, CString}; use std::panic; use std::ptr::null_mut; -use std::rc::Rc; use std::{mem, slice}; #[repr(C)] @@ -58,20 +57,6 @@ pub struct CLedgerInfo { pub bucket_list_size: u64, } -fn fill_ledger_info(c_ledger_info: CLedgerInfo, network_config: &NetworkConfig) -> LedgerInfo { - let network_passphrase = from_c_string(c_ledger_info.network_passphrase); - let mut ledger_info = LedgerInfo { - protocol_version: c_ledger_info.protocol_version, - sequence_number: c_ledger_info.sequence_number, - timestamp: c_ledger_info.timestamp, - network_id: Sha256::digest(network_passphrase).into(), - base_reserve: c_ledger_info.base_reserve, - ..Default::default() - }; - network_config.fill_config_fields_in_ledger_info(&mut ledger_info); - ledger_info -} - #[repr(C)] #[derive(Copy, Clone)] pub struct CXDR { @@ -178,83 +163,6 @@ impl Default for CPreflightResult { } } -impl CPreflightResult { - fn new_from_invoke_host_function( - invoke_hf_result: InvokeHostFunctionSimulationResult, - restore_preamble: Option, - error: String, - ) -> Self { - let mut result = Self { - error: string_to_c(error), - auth: xdr_vec_to_c(&invoke_hf_result.auth), - result: option_xdr_to_c(invoke_hf_result.invoke_result.ok().as_ref()), - min_fee: invoke_hf_result - .transaction_data - .as_ref() - .map_or_else(|| 0, |r| r.resource_fee), - transaction_data: option_xdr_to_c(invoke_hf_result.transaction_data.as_ref()), - // TODO: Diagnostic and contract events should be separated in the response - events: xdr_vec_to_c(&invoke_hf_result.diagnostic_events), - cpu_instructions: u64::from(invoke_hf_result.simulated_instructions), - memory_bytes: u64::from(invoke_hf_result.simulated_memory), - ledger_entry_diff: ledger_entry_diff_vec_to_c(&invoke_hf_result.modified_entries), - ..Default::default() - }; - if let Some(p) = restore_preamble { - result.pre_restore_min_fee = p.transaction_data.resource_fee; - result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); - }; - result - } - - fn new_from_invoke_host_function_21( - invoke_hf_result: InvokeHostFunctionSimulationResult21, - restore_preamble: Option, - error: String, - ) -> Self { - let mut result = Self { - error: string_to_c(error), - auth: xdr_vec_to_c(&invoke_hf_result.auth), - result: option_xdr_to_c(invoke_hf_result.invoke_result.ok().as_ref()), - min_fee: invoke_hf_result - .transaction_data - .as_ref() - .map_or_else(|| 0, |r| r.resource_fee), - transaction_data: option_xdr_to_c(invoke_hf_result.transaction_data.as_ref()), - // TODO: Diagnostic and contract events should be separated in the response - events: xdr_vec_to_c(&invoke_hf_result.diagnostic_events), - cpu_instructions: u64::from(invoke_hf_result.simulated_instructions), - memory_bytes: u64::from(invoke_hf_result.simulated_memory), - ledger_entry_diff: ledger_entry_diff_vec_to_c_21(&invoke_hf_result.modified_entries), - ..Default::default() - }; - if let Some(p) = restore_preamble { - result.pre_restore_min_fee = p.transaction_data.resource_fee; - result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); - }; - result - } - - fn new_from_transaction_data( - transaction_data: Option<&SorobanTransactionData>, - restore_preamble: Option<&RestoreOpSimulationResult>, - error: String, - ) -> Self { - let min_fee = transaction_data.map_or(0, |d| d.resource_fee); - let mut result = Self { - error: string_to_c(error), - transaction_data: option_xdr_to_c(transaction_data), - min_fee, - ..Default::default() - }; - if let Some(p) = restore_preamble { - result.pre_restore_min_fee = p.transaction_data.resource_fee; - result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); - }; - result - } -} - #[no_mangle] pub extern "C" fn preflight_invoke_hf_op( handle: libc::uintptr_t, // Go Handle to forward to SnapshotSourceGet and SnapshotSourceHas @@ -264,182 +172,32 @@ pub extern "C" fn preflight_invoke_hf_op( resource_config: CResourceConfig, enable_debug: bool, ) -> *mut CPreflightResult { + let proto = ledger_info.protocol_version; catch_preflight_panic(Box::new(move || { - preflight_invoke_hf_op_or_maybe_panic( - handle, - invoke_hf_op, - source_account, - ledger_info, - resource_config, - enable_debug, - ) + if proto <= prev::shared::PROTOCOL { + prev::shared::preflight_invoke_hf_op_or_maybe_panic( + handle, + invoke_hf_op, + source_account, + ledger_info, + resource_config, + enable_debug, + ) + } else if proto == curr::shared::PROTOCOL { + curr::shared::preflight_invoke_hf_op_or_maybe_panic( + handle, + invoke_hf_op, + source_account, + ledger_info, + resource_config, + enable_debug, + ) + } else { + Err(anyhow!("Unsupported protocol version: {proto}")) + } })) } -fn preflight_invoke_hf_op_or_maybe_panic( - handle: libc::uintptr_t, - invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64 - source_account: CXDR, // AccountId XDR in base64 - c_ledger_info: CLedgerInfo, - resource_config: CResourceConfig, - enable_debug: bool, -) -> Result { - // When the network transitions fully to protocol 22, delete this function - // and the _21 version and rename preflight_invoke_hf_op_or_maybe_panic_22 as - // preflight_invoke_hf_op_or_maybe_panic. - if c_ledger_info.protocol_version == 21 { - preflight_invoke_hf_op_or_maybe_panic_21( - handle, - invoke_hf_op, - source_account, - c_ledger_info, - resource_config, - enable_debug, - ) - } else { - preflight_invoke_hf_op_or_maybe_panic_22( - handle, - invoke_hf_op, - source_account, - c_ledger_info, - resource_config, - enable_debug, - ) - } -} - -fn preflight_invoke_hf_op_or_maybe_panic_21( - handle: libc::uintptr_t, - invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64 - source_account: CXDR, // AccountId XDR in base64 - c_ledger_info: CLedgerInfo, - resource_config: CResourceConfig, - enable_debug: bool, -) -> Result { - let invoke_hf_op = - InvokeHostFunctionOp::from_xdr(from_c_xdr(invoke_hf_op), DEFAULT_XDR_RW_LIMITS).unwrap(); - let source_account = - AccountId::from_xdr(from_c_xdr(source_account), DEFAULT_XDR_RW_LIMITS).unwrap(); - let go_storage = Rc::new(GoLedgerStorage::new(handle)); - let network_config = - NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; - let ledger_info = fill_ledger_info(c_ledger_info, &network_config); - let auto_restore_snapshot = Rc::new(AutoRestoringSnapshotSource::new( - go_storage.clone(), - &ledger_info, - )?); - - let mut adjustment_config = SimulationAdjustmentConfig::default_adjustment(); - // It would be reasonable to extend `resource_config` to be compatible with `adjustment_config` - // in order to let the users customize the resource/fee adjustments in a more granular fashion. - - let instruction_leeway = u32::try_from(resource_config.instruction_leeway)?; - adjustment_config.instructions.additive_factor = adjustment_config - .instructions - .additive_factor - .max(instruction_leeway); - // Here we assume that no input auth means that the user requests the recording auth. - let auth_entries = if invoke_hf_op.auth.is_empty() { - None - } else { - Some(invoke_hf_op.auth.to_vec()) - }; - // Invoke the host function. The user errors should normally be captured in `invoke_hf_result.invoke_result` and - // this should return Err result for misconfigured ledger. - let invoke_hf_result = simulate_invoke_host_function_op( - auto_restore_snapshot.clone(), - &network_config, - &adjustment_config, - &ledger_info, - invoke_hf_op.host_function, - auth_entries, - &source_account, - rand::Rng::gen(&mut rand::thread_rng()), - enable_debug, - )?; - let maybe_restore_result = match &invoke_hf_result.invoke_result { - Ok(_) => auto_restore_snapshot.simulate_restore_keys_op( - &network_config, - &SimulationAdjustmentConfig::default_adjustment(), - &ledger_info, - ), - Err(e) => Err(e.clone().into()), - }; - let error_str = extract_error_string(&maybe_restore_result, go_storage.as_ref()); - Ok(CPreflightResult::new_from_invoke_host_function( - invoke_hf_result, - maybe_restore_result.unwrap_or(None), - error_str, - )) -} - -fn preflight_invoke_hf_op_or_maybe_panic_22( - handle: libc::uintptr_t, - invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64 - source_account: CXDR, // AccountId XDR in base64 - c_ledger_info: CLedgerInfo, - resource_config: CResourceConfig, - enable_debug: bool, -) -> Result { - let invoke_hf_op = - InvokeHostFunctionOp::from_xdr(from_c_xdr(invoke_hf_op), DEFAULT_XDR_RW_LIMITS).unwrap(); - let source_account = - AccountId::from_xdr(from_c_xdr(source_account), DEFAULT_XDR_RW_LIMITS).unwrap(); - let go_storage = Rc::new(GoLedgerStorage::new(handle)); - let network_config = - NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; - let ledger_info = fill_ledger_info(c_ledger_info, &network_config); - let ledger_info = ledger_info_21(&ledger_info); - let network_config = network_config_21(&network_config); - let auto_restore_snapshot = Rc::new(AutoRestoringSnapshotSource21::new( - go_storage.clone(), - &ledger_info, - )?); - - let mut adjustment_config = SimulationAdjustmentConfig21::default_adjustment(); - // It would be reasonable to extend `resource_config` to be compatible with `adjustment_config` - // in order to let the users customize the resource/fee adjustments in a more granular fashion. - - let instruction_leeway = u32::try_from(resource_config.instruction_leeway)?; - adjustment_config.instructions.additive_factor = adjustment_config - .instructions - .additive_factor - .max(instruction_leeway); - // Here we assume that no input auth means that the user requests the recording auth. - let auth_entries = if invoke_hf_op.auth.is_empty() { - None - } else { - Some(invoke_hf_op.auth.to_vec()) - }; - // Invoke the host function. The user errors should normally be captured in `invoke_hf_result.invoke_result` and - // this should return Err result for misconfigured ledger. - let invoke_hf_result = simulate_invoke_host_function_op_21( - auto_restore_snapshot.clone(), - &network_config, - &adjustment_config, - &ledger_info, - invoke_hf_op.host_function, - auth_entries, - &source_account, - rand::Rng::gen(&mut rand::thread_rng()), - enable_debug, - )?; - let maybe_restore_result = match &invoke_hf_result.invoke_result { - Ok(_) => auto_restore_snapshot.simulate_restore_keys_op( - &network_config, - &SimulationAdjustmentConfig21::default_adjustment(), - &ledger_info, - ), - Err(e) => Err(e.clone().into()), - }; - let error_str = extract_error_string(&maybe_restore_result, go_storage.as_ref()); - Ok(CPreflightResult::new_from_invoke_host_function_21( - invoke_hf_result, - maybe_restore_result.unwrap_or(None), - error_str, - )) -} - #[no_mangle] pub extern "C" fn preflight_footprint_ttl_op( handle: libc::uintptr_t, // Go Handle to forward to SnapshotSourceGet and SnapshotSourceHas @@ -447,265 +205,26 @@ pub extern "C" fn preflight_footprint_ttl_op( footprint: CXDR, // LedgerFootprint XDR ledger_info: CLedgerInfo, ) -> *mut CPreflightResult { + let proto = ledger_info.protocol_version; catch_preflight_panic(Box::new(move || { - preflight_footprint_ttl_op_or_maybe_panic(handle, op_body, footprint, ledger_info) - })) -} - -fn preflight_footprint_ttl_op_or_maybe_panic( - handle: libc::uintptr_t, - op_body: CXDR, - footprint: CXDR, - c_ledger_info: CLedgerInfo, -) -> Result { - let op_body = OperationBody::from_xdr(from_c_xdr(op_body), DEFAULT_XDR_RW_LIMITS)?; - let footprint = LedgerFootprint::from_xdr(from_c_xdr(footprint), DEFAULT_XDR_RW_LIMITS)?; - let go_storage = Rc::new(GoLedgerStorage::new(handle)); - let network_config = - NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; - let ledger_info = fill_ledger_info(c_ledger_info, &network_config); - // TODO: It would make for a better UX if the user passed only the necessary fields for every operation. - // That would remove a possibility of providing bad operation body, or a possibility of filling wrong footprint - // field. - match op_body { - OperationBody::ExtendFootprintTtl(extend_op) => { - preflight_extend_ttl_op(&extend_op, footprint.read_only.as_slice(), &go_storage, &network_config, &ledger_info) - } - OperationBody::RestoreFootprint(_) => { - Ok(preflight_restore_op(footprint.read_write.as_slice(), &go_storage, &network_config, &ledger_info)) - } - _ => Err(anyhow!("encountered unsupported operation type: '{:?}', instead of 'ExtendFootprintTtl' or 'RestoreFootprint' operations.", - op_body.discriminant())) - } -} - -fn fee_configuration_21( - fee_configuration: &soroban_env_host::fees::FeeConfiguration, -) -> soroban_env_host_21::fees::FeeConfiguration { - soroban_env_host_21::fees::FeeConfiguration { - fee_per_instruction_increment: fee_configuration.fee_per_instruction_increment, - fee_per_read_entry: fee_configuration.fee_per_read_entry, - fee_per_write_entry: fee_configuration.fee_per_write_entry, - fee_per_read_1kb: fee_configuration.fee_per_read_1kb, - fee_per_write_1kb: fee_configuration.fee_per_write_1kb, - fee_per_historical_1kb: fee_configuration.fee_per_historical_1kb, - fee_per_contract_event_1kb: fee_configuration.fee_per_contract_event_1kb, - fee_per_transaction_size_1kb: fee_configuration.fee_per_transaction_size_1kb, - } -} - -fn rent_fee_configuration_21( - rent_fee_configuration: &soroban_env_host::fees::RentFeeConfiguration, -) -> soroban_env_host_21::fees::RentFeeConfiguration { - soroban_env_host_21::fees::RentFeeConfiguration { - fee_per_write_1kb: rent_fee_configuration.fee_per_write_1kb, - fee_per_write_entry: rent_fee_configuration.fee_per_write_entry, - persistent_rent_rate_denominator: rent_fee_configuration.persistent_rent_rate_denominator, - temporary_rent_rate_denominator: rent_fee_configuration.temporary_rent_rate_denominator, - } -} - -fn network_config_21(network_config: &NetworkConfig) -> NetworkConfig21 { - NetworkConfig21 { - fee_configuration: fee_configuration_21(&network_config.fee_configuration), - rent_fee_configuration: rent_fee_configuration_21(&network_config.rent_fee_configuration), - cpu_cost_params: network_config.cpu_cost_params.clone(), - memory_cost_params: network_config.memory_cost_params.clone(), - min_temp_entry_ttl: network_config.min_temp_entry_ttl, - min_persistent_entry_ttl: network_config.min_persistent_entry_ttl, - tx_max_instructions: network_config.tx_max_instructions, - tx_memory_limit: network_config.tx_memory_limit, - max_entry_ttl: network_config.max_entry_ttl, - } -} - -fn ledger_info_21(ledger_info: &LedgerInfo) -> LedgerInfo21 { - LedgerInfo21 { - protocol_version: ledger_info.protocol_version, - sequence_number: ledger_info.sequence_number, - timestamp: ledger_info.timestamp, - network_id: ledger_info.network_id, - base_reserve: ledger_info.base_reserve, - min_temp_entry_ttl: ledger_info.min_temp_entry_ttl, - min_persistent_entry_ttl: ledger_info.min_persistent_entry_ttl, - max_entry_ttl: ledger_info.max_entry_ttl, - } -} - -fn restore_op_simulation_result_from_21( - result: Result>, -) -> Result> { - match result { - Ok(Some(r)) => Ok(Some(RestoreOpSimulationResult { - transaction_data: r.transaction_data, - })), - Ok(None) => Ok(None), - Err(e) => Err(e), - } -} - -fn preflight_extend_ttl_op( - extend_op: &ExtendFootprintTtlOp, - keys_to_extend: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> Result { - // When the network transitions fully to protocol 22, delete this function - // and the _21 version and rename preflight_extend_ttl_op_22 as - // preflight_extend_ttl_op. - if ledger_info.protocol_version == 21 { - preflight_extend_ttl_op_21( - extend_op, - keys_to_extend, - go_storage, - network_config, - ledger_info, - ) - } else { - preflight_extend_ttl_op_22( - extend_op, - keys_to_extend, - go_storage, - network_config, - ledger_info, - ) - } -} - -fn preflight_extend_ttl_op_21( - extend_op: &ExtendFootprintTtlOp, - keys_to_extend: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> Result { - let ledger_info: LedgerInfo21 = ledger_info_21(ledger_info); - let network_config = network_config_21(network_config); - let auto_restore_snapshot = - AutoRestoringSnapshotSource21::new(go_storage.clone(), &ledger_info)?; - let simulation_result = simulate_extend_ttl_op_21( - &auto_restore_snapshot, - &network_config, - &SimulationAdjustmentConfig21::default_adjustment(), - &ledger_info, - keys_to_extend, - extend_op.extend_to, - ); - let (maybe_transaction_data, maybe_restore_result) = match simulation_result { - Ok(r) => ( - Some(r.transaction_data), - auto_restore_snapshot.simulate_restore_keys_op( - &network_config, - &SimulationAdjustmentConfig21::default_adjustment(), - &ledger_info, - ), - ), - Err(e) => (None, Err(e)), - }; - let maybe_restore_result = restore_op_simulation_result_from_21(maybe_restore_result); - let error_str = extract_error_string(&maybe_restore_result, go_storage); - Ok(CPreflightResult::new_from_transaction_data( - maybe_transaction_data.as_ref(), - maybe_restore_result.ok().flatten().as_ref(), - error_str, - )) -} - -fn preflight_extend_ttl_op_22( - extend_op: &ExtendFootprintTtlOp, - keys_to_extend: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> Result { - let auto_restore_snapshot = AutoRestoringSnapshotSource::new(go_storage.clone(), ledger_info)?; - let simulation_result = simulate_extend_ttl_op( - &auto_restore_snapshot, - network_config, - &SimulationAdjustmentConfig::default_adjustment(), - ledger_info, - keys_to_extend, - extend_op.extend_to, - ); - let (maybe_transaction_data, maybe_restore_result) = match simulation_result { - Ok(r) => ( - Some(r.transaction_data), - auto_restore_snapshot.simulate_restore_keys_op( - network_config, - &SimulationAdjustmentConfig::default_adjustment(), + if proto <= prev::shared::PROTOCOL { + prev::shared::preflight_footprint_ttl_op_or_maybe_panic( + handle, + op_body, + footprint, ledger_info, - ), - ), - Err(e) => (None, Err(e)), - }; - - let error_str = extract_error_string(&maybe_restore_result, go_storage); - Ok(CPreflightResult::new_from_transaction_data( - maybe_transaction_data.as_ref(), - maybe_restore_result.ok().flatten().as_ref(), - error_str, - )) -} - -fn preflight_restore_op( - keys_to_restore: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> CPreflightResult { - // When the network transitions fully to protocol 22, delete this function - // and the _21 version and rename preflight_restore_op_22 as - // preflight_restore_op. - if ledger_info.protocol_version == 21 { - preflight_restore_op_21(keys_to_restore, go_storage, network_config, ledger_info) - } else { - preflight_restore_op_22(keys_to_restore, go_storage, network_config, ledger_info) - } -} - -fn preflight_restore_op_21( - keys_to_restore: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> CPreflightResult { - let network_config = network_config_21(network_config); - let ledger_info: LedgerInfo21 = ledger_info_21(ledger_info); - let simulation_result = simulate_restore_op_21( - go_storage.as_ref(), - &network_config, - &SimulationAdjustmentConfig21::default_adjustment(), - &ledger_info, - keys_to_restore, - ); - let error_str = extract_error_string(&simulation_result, go_storage.as_ref()); - CPreflightResult::new_from_transaction_data( - simulation_result.ok().map(|r| r.transaction_data).as_ref(), - None, - error_str, - ) -} - -fn preflight_restore_op_22( - keys_to_restore: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> CPreflightResult { - let simulation_result = simulate_restore_op( - go_storage.as_ref(), - network_config, - &SimulationAdjustmentConfig::default_adjustment(), - ledger_info, - keys_to_restore, - ); - let error_str = extract_error_string(&simulation_result, go_storage.as_ref()); - CPreflightResult::new_from_transaction_data( - simulation_result.ok().map(|r| r.transaction_data).as_ref(), - None, - error_str, - ) + ) + } else if proto == curr::shared::PROTOCOL { + curr::shared::preflight_footprint_ttl_op_or_maybe_panic( + handle, + op_body, + footprint, + ledger_info, + ) + } else { + Err(anyhow!("Unsupported protocol version: {proto}")) + } + })) } fn preflight_error(str: String) -> CPreflightResult { @@ -734,44 +253,6 @@ fn catch_preflight_panic(op: Box Result>) -> *mut Box::into_raw(Box::new(c_preflight_result)) } -// TODO: We could use something like https://github.com/sonos/ffi-convert-rs -// to replace all the free_* , *_to_c and from_c_* functions by implementations of CDrop, -// CReprOf and AsRust -fn xdr_to_c(v: &impl WriteXdr) -> CXDR { - let (xdr, len) = vec_to_c_array(v.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap()); - CXDR { xdr, len } -} - -fn option_xdr_to_c(v: Option<&impl WriteXdr>) -> CXDR { - v.map_or( - CXDR { - xdr: null_mut(), - len: 0, - }, - xdr_to_c, - ) -} - -fn ledger_entry_diff_to_c(v: &LedgerEntryDiff) -> CXDRDiff { - CXDRDiff { - before: option_xdr_to_c(v.state_before.as_ref()), - after: option_xdr_to_c(v.state_after.as_ref()), - } -} - -fn ledger_entry_diff_to_c_21(v: &LedgerEntryDiff21) -> CXDRDiff { - CXDRDiff { - before: option_xdr_to_c(v.state_before.as_ref()), - after: option_xdr_to_c(v.state_after.as_ref()), - } -} - -fn xdr_vec_to_c(v: &[impl WriteXdr]) -> CXDRVector { - let c_v = v.iter().map(xdr_to_c).collect(); - let (array, len) = vec_to_c_array(c_v); - CXDRVector { array, len } -} - fn string_to_c(str: String) -> *mut libc::c_char { CString::new(str).unwrap().into_raw() } @@ -791,24 +272,6 @@ fn vec_to_c_array(mut v: Vec) -> (*mut T, libc::size_t) { (ptr, len) } -fn ledger_entry_diff_vec_to_c(modified_entries: &[LedgerEntryDiff]) -> CXDRDiffVector { - let c_diffs = modified_entries - .iter() - .map(ledger_entry_diff_to_c) - .collect(); - let (array, len) = vec_to_c_array(c_diffs); - CXDRDiffVector { array, len } -} - -fn ledger_entry_diff_vec_to_c_21(modified_entries: &[LedgerEntryDiff21]) -> CXDRDiffVector { - let c_diffs = modified_entries - .iter() - .map(ledger_entry_diff_to_c_21) - .collect(); - let (array, len) = vec_to_c_array(c_diffs); - CXDRDiffVector { array, len } -} - /// . /// /// # Safety @@ -891,8 +354,8 @@ extern "C" { } struct GoLedgerStorage { - golang_handle: libc::uintptr_t, - internal_error: RefCell>, + pub(crate) golang_handle: libc::uintptr_t, + pub(crate) internal_error: RefCell>, } impl GoLedgerStorage { @@ -917,92 +380,6 @@ impl GoLedgerStorage { unsafe { FreeGoXDR(res) }; Some(v) } - - // Gets a ledger entry by key, including the archived/removed entries. - // The failures of this function are not recoverable and should only happen when - // the underlying storage is somehow corrupted. - fn get_fallible(&self, key: &LedgerKey) -> Result> { - let mut key_xdr = key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; - let Some(xdr) = self.get_xdr_internal(&mut key_xdr) else { - return Ok(None); - }; - - let live_until_ledger_seq = match key { - // TODO: it would probably be more efficient to do all of this in the Go side - // (e.g. it would allow us to query multiple entries at once) - LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { - let key_hash: [u8; 32] = Sha256::digest(key_xdr).into(); - let ttl_key = LedgerKey::Ttl(LedgerKeyTtl { - key_hash: Hash(key_hash), - }); - let mut ttl_key_xdr = ttl_key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; - let ttl_entry_xdr = self.get_xdr_internal(&mut ttl_key_xdr).ok_or_else(|| { - anyhow!( - "TTL entry is missing for an entry that should have TTL with key: '{key:?}'" - ) - })?; - let ttl_entry = LedgerEntry::from_xdr(ttl_entry_xdr, DEFAULT_XDR_RW_LIMITS)?; - let LedgerEntryData::Ttl(TtlEntry { - live_until_ledger_seq, - .. - }) = ttl_entry.data - else { - bail!( - "unexpected non-TTL entry '{:?}' has been fetched for TTL key '{:?}'", - ttl_entry, - ttl_key - ); - }; - Some(live_until_ledger_seq) - } - _ => None, - }; - - let entry = LedgerEntry::from_xdr(xdr, DEFAULT_XDR_RW_LIMITS)?; - Ok(Some((Rc::new(entry), live_until_ledger_seq))) - } -} - -impl SnapshotSourceWithArchive for GoLedgerStorage { - fn get_including_archived( - &self, - key: &Rc, - ) -> std::result::Result, HostError> { - let res = self.get_fallible(key.as_ref()); - match res { - Ok(res) => Ok(res), - Err(e) => { - // Store the internal error in the storage as the info won't be propagated from simulation. - if let Ok(mut err) = self.internal_error.try_borrow_mut() { - *err = Some(e); - } - // Errors that occur in storage are not recoverable, so we force host to halt by passing - // it an internal error. - Err((ScErrorType::Storage, ScErrorCode::InternalError).into()) - } - } - } -} - -impl SnapshotSourceWithArchive21 for GoLedgerStorage { - fn get_including_archived( - &self, - key: &Rc, - ) -> std::result::Result, HostError21> { - let res = self.get_fallible(key.as_ref()); - match res { - Ok(res) => Ok(res), - Err(e) => { - // Store the internal error in the storage as the info won't be propagated from simulation. - if let Ok(mut err) = self.internal_error.try_borrow_mut() { - *err = Some(e); - } - // Errors that occur in storage are not recoverable, so we force host to halt by passing - // it an internal error. - Err((ScErrorType::Storage, ScErrorCode::InternalError).into()) - } - } - } } fn extract_error_string(simulation_result: &Result, go_storage: &GoLedgerStorage) -> String {