diff --git a/Cargo.lock b/Cargo.lock index 5104eb1da..c0445ff50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1693,7 +1693,7 @@ dependencies = [ [[package]] name = "stellar-xdr" version = "22.1.0" -source = "git+https://github.com/stellar/rs-stellar-xdr?rev=28391e81229ad20a3d9ec56e1e1f382205335085#28391e81229ad20a3d9ec56e1e1f382205335085" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=c356a8cdcf59e91bd2224edda45eff337335ccfb#c356a8cdcf59e91bd2224edda45eff337335ccfb" dependencies = [ "arbitrary", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index 04e1eae3f..4914a039e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ wasmparser = "=0.116.1" [workspace.dependencies.stellar-xdr] version = "=22.1.0" git = "https://github.com/stellar/rs-stellar-xdr" -rev = "28391e81229ad20a3d9ec56e1e1f382205335085" +rev = "c356a8cdcf59e91bd2224edda45eff337335ccfb" default-features = false [workspace.dependencies.wasmi] diff --git a/deny.toml b/deny.toml index 7d42af17b..72f57d370 100644 --- a/deny.toml +++ b/deny.toml @@ -95,7 +95,11 @@ ignore = [ # This ignores an error due to the now unmaintained derivative crate. This # crate is a dependency for some of the ark crates, so we need to ignore the # advisory. - "RUSTSEC-2024-0388" + "RUSTSEC-2024-0388", + + # This ignores the advisory re unmaintained `paste` crate that is a + # transitive dependency of several crates we depend on. + "RUSTSEC-2024-0436" ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories diff --git a/soroban-env-common/Cargo.toml b/soroban-env-common/Cargo.toml index cd58077ff..05b935223 100644 --- a/soroban-env-common/Cargo.toml +++ b/soroban-env-common/Cargo.toml @@ -15,7 +15,7 @@ crate-git-revision = "0.0.6" [dependencies] soroban-env-macros = { workspace = true } -stellar-xdr = { workspace = true, default-features = false, features = [ "curr" ] } +stellar-xdr = { workspace = true, default-features = false, features = [ "next" ] } wasmi = { workspace = true, optional = true } wasmparser = { workspace = true, optional = true} serde = { version = "1.0.192", features = ["derive"], optional = true } diff --git a/soroban-env-common/src/lib.rs b/soroban-env-common/src/lib.rs index 9ff21e563..86234784a 100644 --- a/soroban-env-common/src/lib.rs +++ b/soroban-env-common/src/lib.rs @@ -104,7 +104,7 @@ pub use storage_type::StorageType; // Re-export the XDR definitions of a specific version -- curr or next -- of the xdr crate. #[cfg(not(feature = "next"))] -pub use stellar_xdr::curr as xdr; +pub use stellar_xdr::next as xdr; #[cfg(feature = "next")] pub use stellar_xdr::next as xdr; diff --git a/soroban-env-common/src/num.rs b/soroban-env-common/src/num.rs index cd58a5127..029d4f2c7 100644 --- a/soroban-env-common/src/num.rs +++ b/soroban-env-common/src/num.rs @@ -429,8 +429,8 @@ pub fn is_small_i256_parts(i: &Int256Parts) -> bool { } pub fn u256_from_pieces(hi_hi: u64, hi_lo: u64, lo_hi: u64, lo_lo: u64) -> U256 { - let high = (u128::from(hi_hi)) << 64 | u128::from(hi_lo); - let low = (u128::from(lo_hi)) << 64 | u128::from(lo_lo); + let high = (u128::from(hi_hi) << 64) | u128::from(hi_lo); + let low = (u128::from(lo_hi) << 64) | u128::from(lo_lo); U256::from_words(high, low) } diff --git a/soroban-env-host/Cargo.toml b/soroban-env-host/Cargo.toml index 1c819af64..7da6f88b7 100644 --- a/soroban-env-host/Cargo.toml +++ b/soroban-env-host/Cargo.toml @@ -85,7 +85,7 @@ p256 = {version = "0.13.2", default-features = false, features = ["alloc"]} [dev-dependencies.stellar-xdr] version = "=22.1.0" git = "https://github.com/stellar/rs-stellar-xdr" -rev = "28391e81229ad20a3d9ec56e1e1f382205335085" +rev = "c356a8cdcf59e91bd2224edda45eff337335ccfb" default-features = false features = ["arbitrary"] diff --git a/soroban-env-host/src/budget.rs b/soroban-env-host/src/budget.rs index 453f77f55..006502a16 100644 --- a/soroban-env-host/src/budget.rs +++ b/soroban-env-host/src/budget.rs @@ -207,6 +207,15 @@ impl BudgetImpl { }) } + pub(crate) fn get_memory_cost( + &self, + ty: ContractCostType, + iterations: u64, + input: Option, + ) -> Result { + self.mem_bytes.get_cost(ty, iterations, input) + } + pub fn charge( &mut self, ty: ContractCostType, @@ -1155,6 +1164,16 @@ impl Budget { self.0.try_borrow_mut_or_err()?.charge(ty, 1, input) } + pub(crate) fn get_memory_cost( + &self, + ty: ContractCostType, + input: Option, + ) -> Result { + self.0 + .try_borrow_mut_or_err()? + .get_memory_cost(ty, 1, input) + } + /// Runs a user provided closure in shadow mode -- all metering is done /// through the shadow budget. /// diff --git a/soroban-env-host/src/budget/dimension.rs b/soroban-env-host/src/budget/dimension.rs index f7781934f..78f78c9a9 100644 --- a/soroban-env-host/src/budget/dimension.rs +++ b/soroban-env-host/src/budget/dimension.rs @@ -187,6 +187,15 @@ impl BudgetDimension { Ok(amount) } + pub(crate) fn get_cost( + &self, + ty: ContractCostType, + iterations: u64, + input: Option, + ) -> Result { + self.get_cost_model(ty)?.evaluate(iterations, input) + } + // Resets all model parameters to zero (so that we can override and test individual ones later). #[cfg(any(test, feature = "testutils", feature = "bench"))] pub(crate) fn reset_models(&mut self) { diff --git a/soroban-env-host/src/e2e_invoke.rs b/soroban-env-host/src/e2e_invoke.rs index 9bcb2952a..f9384f618 100644 --- a/soroban-env-host/src/e2e_invoke.rs +++ b/soroban-env-host/src/e2e_invoke.rs @@ -4,7 +4,6 @@ /// host functions. use std::{cmp::max, rc::Rc}; -use crate::storage::EntryWithLiveUntil; #[cfg(any(test, feature = "recording_mode"))] use crate::{ auth::RecordedAuthPayload, @@ -31,6 +30,7 @@ use crate::{ DiagnosticLevel, Error, Host, HostError, LedgerInfo, MeteredOrdMap, }; use crate::{ledger_info::get_key_durability, ModuleCache}; +use crate::{storage::EntryWithLiveUntil, vm::wasm_module_memory_cost}; #[cfg(any(test, feature = "recording_mode"))] use sha2::{Digest, Sha256}; @@ -89,20 +89,24 @@ pub struct InvokeHostFunctionRecordingModeResult { pub struct LedgerEntryChange { /// Whether the ledger entry is read-only, as defined by the footprint. pub read_only: bool, - /// Entry key encoded as `LedgerKey` XDR. pub encoded_key: Vec, - /// Size of the old entry in bytes. This is size of `LedgerEntry` encoded - /// XDR. - pub old_entry_size_bytes: u32, + /// Size of the 'old' entry to use in the rent computations. + /// This is the size of the encoded entry XDR for all of the entries besides + /// contract code, for which the module in-memory size is used instead. + pub old_entry_size_bytes_for_rent: u32, /// New value of the ledger entry encoded as `LedgerEntry` XDR. /// Only set for non-removed, non-readonly values, otherwise `None`. pub encoded_new_value: Option>, + /// Size of the 'new' entry to use in the rent computations. + /// This is the size of the encoded entry XDR (i.e. length of `encoded_new_value`) + /// for all of the entries besides contract code, for which the module + /// in-memory size is used instead. + pub new_entry_size_bytes_for_rent: u32, /// Change of the live until state of the entry. /// Only set for entries that have a TTL, otherwise `None`. pub ttl_change: Option, } - /// Represents the live until-related state of the entry. #[derive(Debug, Eq, PartialEq, Clone)] pub struct LedgerEntryLiveUntilChange { @@ -162,7 +166,9 @@ pub fn get_ledger_changes( if let Some((old_entry, old_live_until_ledger)) = entry_with_live_until { let mut buf = vec![]; metered_write_xdr(budget, old_entry.as_ref(), &mut buf)?; - entry_change.old_entry_size_bytes = buf.len() as u32; + + entry_change.old_entry_size_bytes_for_rent = + entry_size_for_rent(budget, &old_entry, buf.len() as u32)?; if let Some(ref mut ttl_change) = &mut entry_change.ttl_change { ttl_change.old_live_until_ledger = @@ -188,6 +194,8 @@ pub fn get_ledger_changes( if let Some((entry, _)) = entry_with_live_until_ledger { let mut entry_buf = vec![]; metered_write_xdr(budget, entry.as_ref(), &mut entry_buf)?; + entry_change.new_entry_size_bytes_for_rent = + entry_size_for_rent(budget, &entry, entry_buf.len() as u32)?; entry_change.encoded_new_value = Some(entry_buf); } } @@ -243,15 +251,15 @@ pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec= ttl_change.new_live_until_ledger - && entry_change.old_entry_size_bytes >= new_size_bytes + && entry_change.old_entry_size_bytes_for_rent >= new_size_bytes_for_rent { return None; } @@ -260,8 +268,8 @@ pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec Vec Result { + Ok(match &entry.data { + LedgerEntryData::ContractCode(contract_code_entry) => { + wasm_module_memory_cost(budget, contract_code_entry)?.min(u32::MAX as u64) as u32 + } + _ => entry_xdr_size, + }) +} + /// Invokes a host function within a fresh host instance. /// /// This collects the necessary inputs as encoded XDR and returns the outputs @@ -644,18 +673,25 @@ pub fn invoke_host_function_in_recording_mode( } } - let (footprint, read_bytes, init_ttl_map) = host.with_mut_storage(|storage| { + let (footprint, disk_read_bytes, init_ttl_map) = host.with_mut_storage(|storage| { let footprint = storage_footprint_to_ledger_footprint(&storage.footprint)?; let _footprint_from_xdr = build_storage_footprint_from_xdr(&budget, footprint.clone())?; let mut encoded_ledger_entries = Vec::with_capacity(storage.footprint.0.len()); let mut encoded_ttl_entries = Vec::with_capacity(storage.footprint.0.len()); - let mut read_bytes = 0_u32; + let mut disk_read_bytes = 0_u32; for (lk, _) in &storage.footprint.0 { let entry_with_live_until = ledger_snapshot.get(lk)?; if let Some((le, live_until)) = entry_with_live_until { let encoded_le = host.to_xdr_non_metered(&*le)?; - read_bytes = read_bytes.saturating_add(encoded_le.len() as u32); + match &le.data { + LedgerEntryData::ContractData(_) | LedgerEntryData::ContractCode(_) => (), + _ => { + // Non-Soroban entries are counted towards disk read bytes. + disk_read_bytes = disk_read_bytes.saturating_add(encoded_le.len() as u32); + } + } + encoded_ledger_entries.push(encoded_le); if let Some(live_until_ledger) = live_until { let key_xdr = host.to_xdr_non_metered(lk.as_ref())?; @@ -680,12 +716,12 @@ pub fn invoke_host_function_in_recording_mode( ledger_seq, )?; let _init_storage_clone = init_storage.metered_clone(budget)?; - Ok((footprint, read_bytes, init_ttl_map)) + Ok((footprint, disk_read_bytes, init_ttl_map)) })?; let mut resources = SorobanResources { footprint, instructions: 0, - read_bytes, + read_bytes: disk_read_bytes, write_bytes: 0, }; let _resources_roundtrip: SorobanResources = @@ -850,13 +886,11 @@ fn build_storage_map_from_xdr_ledger_entries, I: ExactSizeIterato budget, )?; let key = Rc::metered_new(ledger_entry_to_ledger_key(&le, budget)?, budget)?; - if !ttl_buf.as_ref().is_empty() { let ttl_entry = Rc::metered_new( metered_from_xdr_with_budget::(ttl_buf.as_ref(), budget)?, budget, )?; - if ttl_entry.live_until_ledger_seq < ledger_num { return Err(Error::from_type_and_code( ScErrorType::Storage, diff --git a/soroban-env-host/src/e2e_testutils.rs b/soroban-env-host/src/e2e_testutils.rs index 7c64c8647..b461643e0 100644 --- a/soroban-env-host/src/e2e_testutils.rs +++ b/soroban-env-host/src/e2e_testutils.rs @@ -67,10 +67,10 @@ pub fn wasm_entry(wasm: &[u8]) -> LedgerEntry { wasm_entry_with_refined_contract_cost_inputs(wasm, true) } -pub(crate) fn wasm_entry_with_refined_contract_cost_inputs( +pub(crate) fn contract_code_entry_with_refined_contract_cost_inputs( wasm: &[u8], add_refined_cost_inputs: bool, -) -> LedgerEntry { +) -> ContractCodeEntry { let ext = if !add_refined_cost_inputs { ContractCodeEntryExt::V0 } else { @@ -82,11 +82,20 @@ pub(crate) fn wasm_entry_with_refined_contract_cost_inputs( ext: ExtensionPoint::V0, }) }; - ledger_entry(LedgerEntryData::ContractCode(ContractCodeEntry { + ContractCodeEntry { ext, hash: get_wasm_hash(wasm).try_into().unwrap(), code: wasm.try_into().unwrap(), - })) + } +} + +pub(crate) fn wasm_entry_with_refined_contract_cost_inputs( + wasm: &[u8], + add_refined_cost_inputs: bool, +) -> LedgerEntry { + ledger_entry(LedgerEntryData::ContractCode( + contract_code_entry_with_refined_contract_cost_inputs(wasm, add_refined_cost_inputs), + )) } pub fn e2e_test_protocol_version() -> u32 { diff --git a/soroban-env-host/src/fees.rs b/soroban-env-host/src/fees.rs index 93c2c87cc..378d25a84 100644 --- a/soroban-env-host/src/fees.rs +++ b/soroban-env-host/src/fees.rs @@ -14,19 +14,19 @@ pub const INSTRUCTIONS_INCREMENT: i64 = 10000; pub const DATA_SIZE_1KB_INCREMENT: i64 = 1024; // minimum effective write fee per 1KB -pub const MINIMUM_WRITE_FEE_PER_1KB: i64 = 1000; +pub const MINIMUM_RENT_WRITE_FEE_PER_1KB: i64 = 1000; /// These are the resource upper bounds specified by the Soroban transaction. pub struct TransactionResources { /// Number of CPU instructions. pub instructions: u32, /// Number of ledger entries the transaction reads. - pub read_entries: u32, + pub disk_read_entries: u32, /// Number of ledger entries the transaction writes (these are also counted /// as entries that are being read for the sake of the respective fees). pub write_entries: u32, /// Number of bytes read from ledger. - pub read_bytes: u32, + pub disk_read_bytes: u32, /// Number of bytes written to ledger. pub write_bytes: u32, /// Size of the contract events XDR. @@ -46,13 +46,12 @@ pub struct FeeConfiguration { /// Fee per `INSTRUCTIONS_INCREMENT=10000` instructions. pub fee_per_instruction_increment: i64, /// Fee per 1 entry read from ledger. - pub fee_per_read_entry: i64, + pub fee_per_disk_read_entry: i64, /// Fee per 1 entry written to ledger. pub fee_per_write_entry: i64, /// Fee per 1KB read from ledger. - pub fee_per_read_1kb: i64, - /// Fee per 1KB written to ledger. This has to be computed via - /// `compute_write_fee_per_1kb`. + pub fee_per_disk_read_1kb: i64, + /// Fee per 1KB of state written to the ledger. pub fee_per_write_1kb: i64, /// Fee per 1KB written to history (the history write size is based on /// transaction size and `TX_BASE_RESULT_SIZE`). @@ -63,21 +62,22 @@ pub struct FeeConfiguration { pub fee_per_transaction_size_1kb: i64, } -/// Network configuration used to determine the ledger write fee. +/// Network configuration used to determine the ledger write fee used in rent +/// computations for the Soroban state. /// /// This should be normally loaded from the ledger. #[derive(Debug, Default, PartialEq, Eq)] -pub struct WriteFeeConfiguration { - // Write fee grows linearly until bucket list reaches this size. - pub bucket_list_target_size_bytes: i64, - // Fee per 1KB write when the bucket list is empty. - pub write_fee_1kb_bucket_list_low: i64, - // Fee per 1KB write when the bucket list has reached - // `bucket_list_target_size_bytes`. - pub write_fee_1kb_bucket_list_high: i64, +pub struct RentWriteFeeConfiguration { + // Write fee grows linearly until the Soroban state reaches this size. + pub state_target_size_bytes: i64, + // Fee per 1KB write when the state size is 0. + pub rent_fee_1kb_state_size_low: i64, + // Fee per 1KB write when the Soroban state has reached + // `state_target_size_bytes`. + pub rent_fee_1kb_state_size_high: i64, // Write fee multiplier for any additional data past the first - // `bucket_list_target_size_bytes`. - pub bucket_list_write_fee_growth_factor: u32, + // `state_target_size_bytes`. + pub state_size_rent_fee_growth_factor: u32, } /// Change in a single ledger entry with parameters relevant for rent fee @@ -88,41 +88,41 @@ pub struct WriteFeeConfiguration { pub struct LedgerEntryRentChange { /// Whether this is persistent or temporary entry. pub is_persistent: bool, - /// Size of the entry in bytes before it has been modified, including the - /// key. - /// `0` for newly-created entires. + /// In-memory size of the entry in bytes before it has been modified. + /// Should be `0` for newly-created entires. pub old_size_bytes: u32, - /// Size of the entry in bytes after it has been modified, including the - /// key. + /// In-memory size of the entry in bytes after it has been modified. + /// Should be `0` for removed entries. pub new_size_bytes: u32, /// Live until ledger of the entry before it has been modified. - /// Should be less than the current ledger for newly-created entires. + /// Should be '0' for newly-created entires. pub old_live_until_ledger: u32, /// Live until ledger of the entry after it has been modified. + /// Should be `0` for removed entries. pub new_live_until_ledger: u32, } /// Rent fee-related network configuration. /// /// This should be normally loaded from the ledger, with exception of the -/// `fee_per_write_1kb`, that has to be computed via `compute_write_fee_per_1kb` -/// function. +/// `fee_per_rent_1kb`, that has to be computed via +/// `compute_rent_write_fee_per_1kb` function. #[derive(Debug, Default, PartialEq, Eq)] pub struct RentFeeConfiguration { - /// Fee per 1KB written to ledger. - /// This is the same field as in `FeeConfiguration` and it has to be - /// computed via `compute_write_fee_per_1kb`. + // Fee per 1KB written to the ledger. + // This is the same value as `fee_per_write_1kb` in `FeeConfiguration`. pub fee_per_write_1kb: i64, + /// Fee per 1KB of rented ledger space. + /// This has to be computed via `compute_rent_write_fee_per_1kb`. + pub fee_per_rent_1kb: i64, /// Fee per 1 entry written to ledger. /// This is the same field as in `FeeConfiguration`. pub fee_per_write_entry: i64, /// Denominator for the total rent fee for persistent storage. /// - /// This can be thought of as the number of ledgers of rent that costs as - /// much, as writing the entry for the first time (i.e. if the value is - /// `1000`, then we would charge the entry write fee for every 1000 ledgers - /// of rent). + /// 1 KB of ledger space gets charged `fee_per_write_1kb` every + /// `persistent_rent_rate_denominator` ledgers. pub persistent_rent_rate_denominator: i64, /// Denominator for the total rent fee for temporary storage. /// @@ -147,9 +147,9 @@ pub fn compute_transaction_resource_fee( fee_config.fee_per_instruction_increment, INSTRUCTIONS_INCREMENT, ); - let ledger_read_entry_fee: i64 = fee_config.fee_per_read_entry.saturating_mul( + let ledger_read_entry_fee: i64 = fee_config.fee_per_disk_read_entry.saturating_mul( tx_resources - .read_entries + .disk_read_entries .saturating_add(tx_resources.write_entries) .into(), ); @@ -157,8 +157,8 @@ pub fn compute_transaction_resource_fee( .fee_per_write_entry .saturating_mul(tx_resources.write_entries.into()); let ledger_read_bytes_fee = compute_fee_per_increment( - tx_resources.read_bytes, - fee_config.fee_per_read_1kb, + tx_resources.disk_read_bytes, + fee_config.fee_per_disk_read_1kb, DATA_SIZE_1KB_INCREMENT, ); let ledger_write_bytes_fee = compute_fee_per_increment( @@ -231,48 +231,47 @@ impl ClampFee for i128 { } } -/// Computes the effective write fee per 1 KB of data written to ledger. +/// Computes the effective rent fee per 1 KB of ledger space. /// -/// The computed fee should be used in fee configuration for -/// `compute_transaction_resource_fee` function. +/// The computed fee should be used in rent fee configuration for +/// `compute_rent_fee` function. /// -/// This depends only on the current ledger (more specifically, bucket list) -/// size. -pub fn compute_write_fee_per_1kb( - bucket_list_size_bytes: i64, - fee_config: &WriteFeeConfiguration, +/// This depends only on the current Soroban in-memory state size. +pub fn compute_rent_write_fee_per_1kb( + soroban_state_size_bytes: i64, + fee_config: &RentWriteFeeConfiguration, ) -> i64 { let fee_rate_multiplier = fee_config - .write_fee_1kb_bucket_list_high - .saturating_sub(fee_config.write_fee_1kb_bucket_list_low) + .rent_fee_1kb_state_size_high + .saturating_sub(fee_config.rent_fee_1kb_state_size_low) .clamp_fee(); - let mut write_fee_per_1kb: i64; - if bucket_list_size_bytes < fee_config.bucket_list_target_size_bytes { + let mut rent_write_fee_per_1kb: i64; + if soroban_state_size_bytes < fee_config.state_target_size_bytes { // Convert multipliers to i128 to make sure we can handle large bucket list // sizes. - write_fee_per_1kb = num_integer::div_ceil( - (fee_rate_multiplier as i128).saturating_mul(bucket_list_size_bytes as i128), - (fee_config.bucket_list_target_size_bytes as i128).max(1), + rent_write_fee_per_1kb = num_integer::div_ceil( + (fee_rate_multiplier as i128).saturating_mul(soroban_state_size_bytes as i128), + (fee_config.state_target_size_bytes as i128).max(1), ) .clamp_fee(); // no clamp_fee here - write_fee_per_1kb = - write_fee_per_1kb.saturating_add(fee_config.write_fee_1kb_bucket_list_low); + rent_write_fee_per_1kb = + rent_write_fee_per_1kb.saturating_add(fee_config.rent_fee_1kb_state_size_low); } else { - write_fee_per_1kb = fee_config.write_fee_1kb_bucket_list_high; + rent_write_fee_per_1kb = fee_config.rent_fee_1kb_state_size_high; let bucket_list_size_after_reaching_target = - bucket_list_size_bytes.saturating_sub(fee_config.bucket_list_target_size_bytes); + soroban_state_size_bytes.saturating_sub(fee_config.state_target_size_bytes); let post_target_fee = num_integer::div_ceil( (fee_rate_multiplier as i128) .saturating_mul(bucket_list_size_after_reaching_target as i128) - .saturating_mul(fee_config.bucket_list_write_fee_growth_factor as i128), - (fee_config.bucket_list_target_size_bytes as i128).max(1), + .saturating_mul(fee_config.state_size_rent_fee_growth_factor as i128), + (fee_config.state_target_size_bytes as i128).max(1), ) .clamp_fee(); - write_fee_per_1kb = write_fee_per_1kb.saturating_add(post_target_fee); + rent_write_fee_per_1kb = rent_write_fee_per_1kb.saturating_add(post_target_fee); } - write_fee_per_1kb.max(MINIMUM_WRITE_FEE_PER_1KB) + rent_write_fee_per_1kb.max(MINIMUM_RENT_WRITE_FEE_PER_1KB) } /// Computes the total rent-related fee for the provided ledger entry changes. @@ -280,9 +279,9 @@ pub fn compute_write_fee_per_1kb( /// The rent-related fees consist of the fees for TTL extensions and fees for /// increasing the entry size (with or without TTL extensions). /// -/// This cannot handle unsantized inputs and relies on sane configuration and +/// This cannot handle unsanitized inputs and relies on sane configuration and /// ledger changes. This is due to the fact that rent is managed automatically -/// wihtout user-provided inputs. +/// without user-provided inputs. pub fn compute_rent_fee( changed_entries: &[LedgerEntryRentChange], fee_config: &RentFeeConfiguration, @@ -397,7 +396,7 @@ fn rent_fee_for_size_and_ledgers( // on sane input parameters as rent fee computation does not depend on any // user inputs. let num = (entry_size as i64) - .saturating_mul(fee_config.fee_per_write_1kb) + .saturating_mul(fee_config.fee_per_rent_1kb) .saturating_mul(rent_ledgers as i64); let storage_coef = if is_persistent { fee_config.persistent_rent_rate_denominator diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 8dc0c20ff..800fd5efa 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -3410,7 +3410,7 @@ impl Host { // Testing interface to create values directly for later use via Env functions. // It needs to be a `pub` method because benches are considered a separate crate. pub fn inject_val(&self, v: &ScVal) -> Result { - self.to_host_val(v).map(Into::into) + self.to_host_val(v) } } diff --git a/soroban-env-host/src/host/invocation_metering.rs b/soroban-env-host/src/host/invocation_metering.rs index 376bfbd7c..8b5ff6373 100644 --- a/soroban-env-host/src/host/invocation_metering.rs +++ b/soroban-env-host/src/host/invocation_metering.rs @@ -3,11 +3,11 @@ use std::cell::RefMut; use soroban_env_common::Env; use crate::{ - e2e_invoke::encode_contract_events, + e2e_invoke::{encode_contract_events, entry_size_for_rent}, fees::{FeeConfiguration, DATA_SIZE_1KB_INCREMENT, INSTRUCTIONS_INCREMENT, TTL_ENTRY_SIZE}, ledger_info::get_key_durability, storage::{AccessType, Storage}, - xdr::{ContractDataDurability, ScErrorCode, ScErrorType}, + xdr::{ContractDataDurability, LedgerKey, ScErrorCode, ScErrorType}, }; use super::{metered_xdr::metered_write_xdr, Host, HostError}; @@ -22,18 +22,38 @@ pub struct InvocationResources { /// Number of modelled CPU instructions. pub instructions: i64, /// Size of modelled memory in bytes. + /// + /// Note, that the used memory does not affect the fees. It only has an + /// upper bound. pub mem_bytes: i64, - /// Number of entries that need to be read from the ledger (without - /// modification, i.e. excluding the entries that need to be written). - pub read_entries: u32, + /// Number of entries that need to be read from the disk. + /// + /// This is the total number of restored Soroban ledger entries and + /// non-Soroban entries (such as 'classic' account balances). + /// + /// Live Soroban state is stored in-memory and most of the time this + /// is going to be 0 or almost 0. + pub disk_read_entries: u32, + /// Number of in-memory ledger entries accessed by the invocation. + /// + /// This includes all the live Soroban entries, i.e. most of the entries + /// that a contract interacts with. + /// + /// Note, that this value does not affect the fees. It only has an upper + /// bound. + pub memory_read_entries: u32, /// Number of entries that need to be written to the ledger due to /// modification. pub write_entries: u32, - /// Total number of bytes that need to be read from the ledger. This is - /// the total size of the 'initial' states all the storage entries accessed. - pub read_bytes: u32, - /// Total number of bytes that need to be written to the ledger. This is - /// the total size of all the entries accounted in `write_entries`. + /// Total number of bytes that need to be read from disk. + /// + /// This is the total size of restored Soroban ledger entries and + /// non-Soroban entries (such as 'classic' account balances). + /// + /// Live Soroban state is stored in-memory and most of the time this + /// is going to be 0 or almost 0. + pub disk_read_bytes: u32, + /// Total number of bytes that need to be written to the ledger. pub write_bytes: u32, /// Total size of the contract events emitted. pub contract_events_size_bytes: u32, @@ -63,11 +83,11 @@ pub struct FeeEstimate { /// Fee for instructions. pub instructions: i64, /// Fee for ledger entry reads. - pub read_entries: i64, + pub disk_read_entries: i64, /// Fee for ledger entry writes. pub write_entries: i64, - /// Fee for the overall size of ledger reads. - pub read_bytes: i64, + /// Fee for the overall size of ledger disk reads. + pub disk_read_bytes: i64, /// Fee for the overall size of ledger writes. pub write_bytes: i64, /// Fee for the contract events emitted. @@ -79,7 +99,7 @@ pub struct FeeEstimate { } impl InvocationResources { - /// Estimates the fees necesary for the current resources based on the + /// Estimates the fees necessary for the current resources based on the /// provided fee configuration. /// /// This is only an estimate and it can't be used for the actual transaction @@ -90,6 +110,7 @@ impl InvocationResources { pub fn estimate_fees( &self, fee_config: &FeeConfiguration, + fee_per_rent_1kb: i64, persistent_rent_rate_denominator: i64, temporary_rent_rate_denominator: i64, ) -> FeeEstimate { @@ -98,15 +119,17 @@ impl InvocationResources { fee_config.fee_per_instruction_increment, INSTRUCTIONS_INCREMENT, ); - let read_entries = fee_config - .fee_per_read_entry - .saturating_mul(self.read_entries.saturating_add(self.write_entries).into()); + let disk_read_entries = fee_config.fee_per_disk_read_entry.saturating_mul( + self.disk_read_entries + .saturating_add(self.write_entries) + .into(), + ); let write_entries = fee_config .fee_per_write_entry .saturating_mul(self.write_entries.into()); - let read_bytes = compute_fee_per_increment( - self.read_bytes.into(), - fee_config.fee_per_read_1kb, + let disk_read_bytes = compute_fee_per_increment( + self.disk_read_bytes.into(), + fee_config.fee_per_disk_read_1kb, DATA_SIZE_1KB_INCREMENT, ); let write_bytes = compute_fee_per_increment( @@ -141,20 +164,20 @@ impl InvocationResources { let persistent_entry_rent = compute_fee_per_increment( self.persistent_rent_ledger_bytes, - fee_config.fee_per_write_1kb, + fee_per_rent_1kb, DATA_SIZE_1KB_INCREMENT.saturating_mul(persistent_rent_rate_denominator), ) .saturating_add(persistent_entry_ttl_entry_writes); let temporary_entry_rent = compute_fee_per_increment( self.temporary_rent_ledger_bytes, - fee_config.fee_per_write_1kb, + fee_per_rent_1kb, DATA_SIZE_1KB_INCREMENT.saturating_mul(temporary_rent_rate_denominator), ) .saturating_add(temp_entry_ttl_entry_writes); let total = instructions - .saturating_add(read_entries) + .saturating_add(disk_read_entries) .saturating_add(write_entries) - .saturating_add(read_bytes) + .saturating_add(disk_read_bytes) .saturating_add(write_bytes) .saturating_add(contract_events) .saturating_add(persistent_entry_rent) @@ -162,9 +185,9 @@ impl InvocationResources { FeeEstimate { total, instructions, - read_entries, + disk_read_entries, write_entries, - read_bytes, + disk_read_bytes, write_bytes, contract_events, persistent_entry_rent, @@ -269,45 +292,57 @@ impl InvocationMeter { let curr_ledger_seq: u32 = host.get_ledger_sequence()?.into(); for (key, access_type) in footprint.0.iter(host.budget_ref())? { let maybe_init_entry = prev_storage.try_get_full_with_host(key, host, None)?; - let mut init_entry_size = 0; + let mut init_entry_size_for_rent = 0; let mut init_live_until_ledger = curr_ledger_seq; + let is_disk_read = match key.as_ref() { + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => false, + _ => true, + }; if let Some((init_entry, init_entry_live_until)) = maybe_init_entry { let mut buf = Vec::::new(); metered_write_xdr(host.budget_ref(), init_entry.as_ref(), &mut buf)?; - init_entry_size = buf.len() as u32; - invocation_resources.read_bytes += init_entry_size; + if is_disk_read { + invocation_resources.disk_read_bytes += buf.len() as u32; + } + init_entry_size_for_rent = + entry_size_for_rent(host.budget_ref(), &init_entry, buf.len() as u32)?; + if let Some(live_until) = init_entry_live_until { init_live_until_ledger = live_until; } } let mut entry_size = 0; + let mut new_entry_size_for_rent = 0; let mut entry_live_until_ledger = None; let maybe_entry = curr_storage.try_get_full_with_host(key, host, None)?; if let Some((entry, entry_live_until)) = maybe_entry { let mut buf = Vec::::new(); metered_write_xdr(host.budget_ref(), entry.as_ref(), &mut buf)?; entry_size = buf.len() as u32; + new_entry_size_for_rent = + entry_size_for_rent(host.budget_ref(), &entry, entry_size)?; entry_live_until_ledger = entry_live_until; } - match access_type { - AccessType::ReadOnly => { - invocation_resources.read_entries += 1; - } - AccessType::ReadWrite => { - invocation_resources.write_entries += 1; - invocation_resources.write_bytes += entry_size; - } + if is_disk_read { + invocation_resources.disk_read_entries += 1; + } else { + invocation_resources.memory_read_entries += 1; + } + if matches!(access_type, AccessType::ReadWrite) { + invocation_resources.write_entries += 1; + invocation_resources.write_bytes += entry_size; } + if let Some(new_live_until) = entry_live_until_ledger { let extension_ledgers = (new_live_until - init_live_until_ledger) as i64; - let size_delta = if entry_size > init_entry_size { - (entry_size - init_entry_size) as i64 + let rent_size_delta = if new_entry_size_for_rent > init_entry_size_for_rent { + (new_entry_size_for_rent - init_entry_size_for_rent) as i64 } else { 0 }; let existing_ledgers = (init_live_until_ledger - curr_ledger_seq) as i64; - let rent_ledger_bytes = - existing_ledgers * size_delta + extension_ledgers * (entry_size as i64); + let rent_ledger_bytes = existing_ledgers * rent_size_delta + + extension_ledgers * (new_entry_size_for_rent as i64); if rent_ledger_bytes > 0 { match get_key_durability(key.as_ref()) { Some(ContractDataDurability::Temporary) => { @@ -383,7 +418,7 @@ mod test { } // run `UPDATE_EXPECT=true cargo test` to update this test. - // The exact values of don't matter too much here (unless the diffs are + // The exact values don't matter too much here (unless the diffs are // produced without a protocol upgrade), but the presence/absence of certain // resources is important (comments clarify which ones). #[test] @@ -406,12 +441,13 @@ mod test { InvocationResources { instructions: 4196698, mem_bytes: 2863076, - read_entries: 0, + disk_read_entries: 0, + memory_read_entries: 2, write_entries: 2, - read_bytes: 0, + disk_read_bytes: 0, write_bytes: 3132, contract_events_size_bytes: 0, - persistent_rent_ledger_bytes: 3128868, + persistent_rent_ledger_bytes: 77506416, persistent_entry_rent_bumps: 2, temporary_rent_ledger_bytes: 0, temporary_entry_rent_bumps: 0, @@ -433,9 +469,10 @@ mod test { InvocationResources { instructions: 314913, mem_bytes: 1134843, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3132, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -458,9 +495,10 @@ mod test { InvocationResources { instructions: 318203, mem_bytes: 1135306, - read_entries: 2, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 1, - read_bytes: 3132, + disk_read_bytes: 0, write_bytes: 84, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 83916, @@ -483,9 +521,10 @@ mod test { InvocationResources { instructions: 314214, mem_bytes: 1134691, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3216, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -508,9 +547,10 @@ mod test { InvocationResources { instructions: 319806, mem_bytes: 1135662, - read_entries: 2, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 1, - read_bytes: 3132, + disk_read_bytes: 0, write_bytes: 84, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -533,9 +573,10 @@ mod test { InvocationResources { instructions: 314576, mem_bytes: 1134759, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3216, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -558,9 +599,10 @@ mod test { InvocationResources { instructions: 315621, mem_bytes: 1135111, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3216, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 336084, @@ -583,9 +625,10 @@ mod test { InvocationResources { instructions: 315741, mem_bytes: 1135111, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3216, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -608,9 +651,10 @@ mod test { InvocationResources { instructions: 315602, mem_bytes: 1135179, - read_entries: 3, + disk_read_entries: 0, + memory_read_entries: 3, write_entries: 0, - read_bytes: 3132, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, @@ -629,36 +673,38 @@ mod test { InvocationResources { instructions: 0, mem_bytes: 100_000, - read_entries: 0, + disk_read_entries: 0, + memory_read_entries: 100, write_entries: 0, - read_bytes: 0, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, persistent_rent_ledger_bytes: 0, persistent_entry_rent_bumps: 0, temporary_rent_ledger_bytes: 0, - temporary_entry_rent_bumps: 0 + temporary_entry_rent_bumps: 0, } .estimate_fees( &FeeConfiguration { fee_per_instruction_increment: 100, - fee_per_read_entry: 100, + fee_per_disk_read_entry: 100, fee_per_write_entry: 100, - fee_per_read_1kb: 100, + fee_per_disk_read_1kb: 100, fee_per_write_1kb: 100, fee_per_historical_1kb: 100, fee_per_contract_event_1kb: 100, fee_per_transaction_size_1kb: 100, }, + 100, 1, 1 ), FeeEstimate { total: 0, instructions: 0, - read_entries: 0, + disk_read_entries: 0, write_entries: 0, - read_bytes: 0, + disk_read_bytes: 0, write_bytes: 0, contract_events: 0, persistent_entry_rent: 0, @@ -671,9 +717,10 @@ mod test { InvocationResources { instructions: 1, mem_bytes: 100_000, - read_entries: 1, + disk_read_entries: 1, + memory_read_entries: 100, write_entries: 1, - read_bytes: 1, + disk_read_bytes: 1, write_bytes: 1, contract_events_size_bytes: 1, persistent_rent_ledger_bytes: 1, @@ -684,23 +731,24 @@ mod test { .estimate_fees( &FeeConfiguration { fee_per_instruction_increment: 100, - fee_per_read_entry: 100, + fee_per_disk_read_entry: 100, fee_per_write_entry: 100, - fee_per_read_1kb: 100, + fee_per_disk_read_1kb: 100, fee_per_write_1kb: 100, fee_per_historical_1kb: 100, fee_per_contract_event_1kb: 100, fee_per_transaction_size_1kb: 100, }, + 100, 1, 1 ), FeeEstimate { total: 516, instructions: 1, - read_entries: 200, + disk_read_entries: 200, write_entries: 100, - read_bytes: 1, + disk_read_bytes: 1, write_bytes: 1, contract_events: 1, persistent_entry_rent: 106, @@ -714,9 +762,10 @@ mod test { InvocationResources { instructions: 10_123_456, mem_bytes: 100_000, - read_entries: 30, + disk_read_entries: 30, + memory_read_entries: 100, write_entries: 10, - read_bytes: 25_600, + disk_read_bytes: 25_600, write_bytes: 10_340, contract_events_size_bytes: 321_654, persistent_rent_ledger_bytes: 1_000_000_000, @@ -727,28 +776,29 @@ mod test { .estimate_fees( &FeeConfiguration { fee_per_instruction_increment: 1000, - fee_per_read_entry: 2000, - fee_per_write_entry: 4000, - fee_per_read_1kb: 1500, + fee_per_disk_read_entry: 2000, fee_per_write_1kb: 3000, + fee_per_write_entry: 4000, + fee_per_disk_read_1kb: 1500, fee_per_historical_1kb: 300, fee_per_contract_event_1kb: 200, fee_per_transaction_size_1kb: 900, }, + 6000, 1000, 2000 ), FeeEstimate { // 1_200_139 + event fees + rent fees - total: 10_089_292, + total: 18_878_354, instructions: 1_012_346, - read_entries: 80000, + disk_read_entries: 80000, write_entries: 40000, - read_bytes: 37500, + disk_read_bytes: 37500, write_bytes: 30293, contract_events: 62824, - persistent_entry_rent: 2942110, - temporary_entry_rent: 5884219 + persistent_entry_rent: 5871797, + temporary_entry_rent: 11743594 } ); @@ -757,9 +807,10 @@ mod test { InvocationResources { instructions: i64::MAX, mem_bytes: i64::MAX, - read_entries: u32::MAX, + disk_read_entries: u32::MAX, + memory_read_entries: 100, write_entries: u32::MAX, - read_bytes: u32::MAX, + disk_read_bytes: u32::MAX, write_bytes: u32::MAX, contract_events_size_bytes: u32::MAX, persistent_rent_ledger_bytes: i64::MAX, @@ -770,23 +821,24 @@ mod test { .estimate_fees( &FeeConfiguration { fee_per_instruction_increment: i64::MAX, - fee_per_read_entry: i64::MAX, + fee_per_disk_read_entry: i64::MAX, fee_per_write_entry: i64::MAX, - fee_per_read_1kb: i64::MAX, + fee_per_disk_read_1kb: i64::MAX, fee_per_write_1kb: i64::MAX, fee_per_historical_1kb: i64::MAX, fee_per_contract_event_1kb: i64::MAX, fee_per_transaction_size_1kb: i64::MAX, }, i64::MAX, + i64::MAX, i64::MAX ), FeeEstimate { total: i64::MAX, instructions: 922337203685478, - read_entries: i64::MAX, + disk_read_entries: i64::MAX, write_entries: i64::MAX, - read_bytes: 9007199254740992, + disk_read_bytes: 9007199254740992, write_bytes: 9007199254740992, contract_events: 9007199254740992, persistent_entry_rent: i64::MAX, diff --git a/soroban-env-host/src/test/e2e_tests.rs b/soroban-env-host/src/test/e2e_tests.rs index 2c2be5359..c476adf5b 100644 --- a/soroban-env-host/src/test/e2e_tests.rs +++ b/soroban-env-host/src/test/e2e_tests.rs @@ -1,9 +1,12 @@ use crate::builtin_contracts::testutils::AccountContractSigner; use crate::crypto::sha256_hash_from_bytes_raw; use crate::e2e_invoke::RecordingInvocationAuthMode; -use crate::e2e_testutils::{account_entry, bytes_sc_val, upload_wasm_host_fn}; +use crate::e2e_testutils::{ + account_entry, bytes_sc_val, contract_code_entry_with_refined_contract_cost_inputs, + upload_wasm_host_fn, +}; use crate::testutils::simple_account_sign_fn; -use crate::vm::VersionedContractCodeCostInputs; +use crate::vm::{wasm_module_memory_cost, VersionedContractCodeCostInputs}; use crate::{ budget::{AsBudget, Budget}, builtin_contracts::testutils::TestSigner, @@ -54,10 +57,6 @@ use std::rc::Rc; // [1 - RECORDING_MODE_INSTRUCTIONS_RANGE, 1 + RECORDING_MODE_INSTRUCTIONS_RANGE] * real_instructions const RECORDING_MODE_INSTRUCTIONS_RANGE: f64 = 0.02; -fn wasm_entry_size(wasm: &[u8]) -> u32 { - wasm_entry(wasm).to_xdr(Limits::none()).unwrap().len() as u32 -} - fn prng_seed() -> [u8; 32] { [0; 32] } @@ -150,7 +149,7 @@ impl Eq for HostError {} struct LedgerEntryChangeHelper { read_only: bool, key: LedgerKey, - old_entry_size_bytes: u32, + old_entry_size_bytes_for_rent: u32, new_value: Option, ttl_change: Option, } @@ -160,7 +159,7 @@ impl From for LedgerEntryChangeHelper { Self { read_only: c.read_only, key: LedgerKey::from_xdr(c.encoded_key, Limits::none()).unwrap(), - old_entry_size_bytes: c.old_entry_size_bytes, + old_entry_size_bytes_for_rent: c.old_entry_size_bytes_for_rent, new_value: c .encoded_new_value .map(|v| LedgerEntry::from_xdr(v, Limits::none()).unwrap()), @@ -177,10 +176,16 @@ impl LedgerEntryChangeHelper { LedgerKey::ContractCode(_) => Some(ContractDataDurability::Persistent), _ => None, }; + let old_entry_size_bytes_for_rent = match &entry.data { + LedgerEntryData::ContractCode(contract_code_entry) => { + wasm_module_memory_cost(&Budget::default(), contract_code_entry).unwrap() as u32 + } + _ => entry.to_xdr(Limits::none()).unwrap().len() as u32, + }; Self { read_only: true, key: ledger_key.clone(), - old_entry_size_bytes: entry.to_xdr(Limits::none()).unwrap().len() as u32, + old_entry_size_bytes_for_rent, new_value: None, ttl_change: if let Some(durability) = durability { Some(LedgerEntryLiveUntilChange { @@ -670,7 +675,7 @@ fn test_wasm_upload_success() { vec![LedgerEntryChangeHelper { read_only: false, key: ledger_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(wasm_entry(ADD_I32)), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&ledger_key), @@ -738,7 +743,7 @@ fn test_wasm_upload_success_in_recording_mode() { vec![LedgerEntryChangeHelper { read_only: false, key: ledger_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(wasm_entry(ADD_I32)), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&ledger_key), @@ -953,12 +958,17 @@ fn test_wasm_reupload_is_no_op() { res.invoke_result.unwrap(), bytes_sc_val(&get_wasm_hash(ADD_I32)) ); + assert_eq!( res.ledger_changes, vec![LedgerEntryChangeHelper { read_only: false, key: get_wasm_key(ADD_I32), - old_entry_size_bytes: wasm_entry_size(ADD_I32), + old_entry_size_bytes_for_rent: wasm_module_memory_cost( + &Budget::default(), + &contract_code_entry_with_refined_contract_cost_inputs(ADD_I32, true), + ) + .unwrap() as u32, new_value: Some(wasm_entry(ADD_I32)), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&get_wasm_key(ADD_I32)), @@ -1004,7 +1014,7 @@ fn test_wasm_upload_success_with_extra_footprint_entries() { LedgerEntryChangeHelper { read_only: false, key: get_wasm_key(ADD_I32), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(wasm_entry(ADD_I32)), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&get_wasm_key(ADD_I32)), @@ -1018,7 +1028,11 @@ fn test_wasm_upload_success_with_extra_footprint_entries() { LedgerEntryChangeHelper { read_only: false, key: get_wasm_key(LINEAR_MEMORY), - old_entry_size_bytes: wasm_entry_size(LINEAR_MEMORY), + old_entry_size_bytes_for_rent: wasm_module_memory_cost( + &Budget::default(), + &contract_code_entry_with_refined_contract_cost_inputs(LINEAR_MEMORY, true), + ) + .unwrap() as u32, new_value: Some(wasm_entry(LINEAR_MEMORY)), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&get_wasm_key(LINEAR_MEMORY)), @@ -1030,7 +1044,7 @@ fn test_wasm_upload_success_with_extra_footprint_entries() { LedgerEntryChangeHelper { read_only: true, key: get_wasm_key(CONTRACT_STORAGE), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: None, ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&get_wasm_key(CONTRACT_STORAGE)), @@ -1078,7 +1092,7 @@ fn test_create_contract_success() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(cd.contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1157,7 +1171,7 @@ fn test_create_contract_with_no_argument_constructor_success() { LedgerEntryChangeHelper { read_only: false, key: temp_entry_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(contract_data_entry( &cd.contract_address, &symbol_sc_val("key"), @@ -1176,7 +1190,7 @@ fn test_create_contract_with_no_argument_constructor_success() { LedgerEntryChangeHelper { read_only: false, key: persistent_entry_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(contract_data_entry( &cd.contract_address, &symbol_sc_val("key"), @@ -1195,7 +1209,7 @@ fn test_create_contract_with_no_argument_constructor_success() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(expected_contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1245,7 +1259,7 @@ fn test_create_contract_success_in_recording_mode() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(cd.contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1271,7 +1285,7 @@ fn test_create_contract_success_in_recording_mode() { read_write: vec![cd.contract_key].try_into().unwrap() }, instructions: 661402, - read_bytes: 684, + read_bytes: 0, write_bytes: 104, } ); @@ -1350,7 +1364,7 @@ fn test_create_contract_success_in_recording_mode_with_custom_account() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(cd.contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1368,7 +1382,7 @@ fn test_create_contract_success_in_recording_mode_with_custom_account() { LedgerEntryChangeHelper { read_only: false, key: nonce_entry_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(ledger_entry(LedgerEntryData::ContractData( ContractDataEntry { ext: ExtensionPoint::V0, @@ -1412,7 +1426,7 @@ fn test_create_contract_success_in_recording_mode_with_custom_account() { read_write: vec![cd.contract_key, nonce_entry_key].try_into().unwrap() }, instructions: 1066372, - read_bytes: 3816, + read_bytes: 0, write_bytes: 176, } ); @@ -1447,7 +1461,7 @@ fn test_create_contract_success_in_recording_mode_with_enforced_auth() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(cd.contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1473,7 +1487,7 @@ fn test_create_contract_success_in_recording_mode_with_enforced_auth() { read_write: vec![cd.contract_key].try_into().unwrap() }, instructions: 662847, - read_bytes: 684, + read_bytes: 0, write_bytes: 104, } ); @@ -1540,7 +1554,7 @@ fn test_create_contract_success_with_extra_footprint_entries() { LedgerEntryChangeHelper { read_only: false, key: cd.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(cd.contract_entry), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd.contract_key), @@ -1554,7 +1568,7 @@ fn test_create_contract_success_with_extra_footprint_entries() { LedgerEntryChangeHelper { read_only: false, key: cd2.contract_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: None, ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&cd2.contract_key), @@ -1747,7 +1761,7 @@ fn test_invoke_contract_with_storage_ops_success() { LedgerEntryChangeHelper { read_only: false, key: data_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(new_entry.clone()), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&data_key), @@ -1805,7 +1819,8 @@ fn test_invoke_contract_with_storage_ops_success() { LedgerEntryChangeHelper { read_only: true, key: data_key.clone(), - old_entry_size_bytes: new_entry.to_xdr(Limits::none()).unwrap().len() as u32, + old_entry_size_bytes_for_rent: new_entry.to_xdr(Limits::none()).unwrap().len() + as u32, new_value: None, ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&data_key), @@ -1878,7 +1893,7 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() { LedgerEntryChangeHelper { read_only: false, key: data_key.clone(), - old_entry_size_bytes: 0, + old_entry_size_bytes_for_rent: 0, new_value: Some(new_entry.clone()), ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&data_key), @@ -1903,7 +1918,7 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() { read_write: vec![data_key.clone()].try_into().unwrap(), }, instructions: 896256, - read_bytes: 3132, + read_bytes: 0, write_bytes: 80, } ); @@ -1942,7 +1957,8 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() { LedgerEntryChangeHelper { read_only: true, key: data_key.clone(), - old_entry_size_bytes: new_entry.to_xdr(Limits::none()).unwrap().len() as u32, + old_entry_size_bytes_for_rent: new_entry.to_xdr(Limits::none()).unwrap().len() + as u32, new_value: None, ttl_change: Some(LedgerEntryLiveUntilChange { key_hash: compute_key_hash(&data_key), @@ -1969,7 +1985,7 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() { read_write: Default::default(), }, instructions: 1008294, - read_bytes: 3212, + read_bytes: 0, write_bytes: 0, } ); diff --git a/soroban-env-host/src/vm.rs b/soroban-env-host/src/vm.rs index 2f8757586..138c80a32 100644 --- a/soroban-env-host/src/vm.rs +++ b/soroban-env-host/src/vm.rs @@ -36,7 +36,9 @@ use fuel_refillable::FuelRefillable; use func_info::HOST_FUNCTIONS; pub use module_cache::ModuleCache; -pub use parsed_module::{CompilationContext, ParsedModule, VersionedContractCodeCostInputs}; +pub use parsed_module::{ + wasm_module_memory_cost, CompilationContext, ParsedModule, VersionedContractCodeCostInputs, +}; use crate::VmCaller; use wasmi::{Caller, StoreContextMut}; diff --git a/soroban-env-host/src/vm/module_cache.rs b/soroban-env-host/src/vm/module_cache.rs index 6bc8badc2..d8a609aaa 100644 --- a/soroban-env-host/src/vm/module_cache.rs +++ b/soroban-env-host/src/vm/module_cache.rs @@ -24,7 +24,7 @@ pub struct ModuleCache { modules: ModuleCacheMap, } -// We may use the ModuleCache from multiple C++ theads where +// We may use the ModuleCache from multiple C++ threads where // there's no checking of Send+Sync but we can at least ensure // Rust thinks its API is thread-safe. static_assertions::assert_impl_all!(ModuleCache: Send, Sync); diff --git a/soroban-env-host/src/vm/parsed_module.rs b/soroban-env-host/src/vm/parsed_module.rs index 2ab62c813..e59f2881a 100644 --- a/soroban-env-host/src/vm/parsed_module.rs +++ b/soroban-env-host/src/vm/parsed_module.rs @@ -1,11 +1,11 @@ use crate::{ - budget::AsBudget, + budget::{AsBudget, Budget}, err, host::metered_clone::MeteredContainer, meta, xdr::{ - ContractCostType, Limited, ReadXdr, ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion, - ScErrorCode, ScErrorType, + ContractCodeEntry, ContractCodeEntryExt, ContractCostType, Limited, ReadXdr, + ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion, ScErrorCode, ScErrorType, }, ErrorHandler, Host, HostError, Val, DEFAULT_XDR_RW_LIMITS, }; @@ -26,6 +26,7 @@ impl VersionedContractCodeCostInputs { Self::V1(_) => false, } } + pub fn charge_for_parsing(&self, budget: &impl AsBudget) -> Result<(), HostError> { let budget = budget.as_budget(); match self { @@ -77,6 +78,7 @@ impl VersionedContractCodeCostInputs { } Ok(()) } + pub fn charge_for_instantiation(&self, _host: &Host) -> Result<(), HostError> { match self { Self::V0 { wasm_bytes } => { @@ -151,6 +153,63 @@ pub struct ParsedModule { pub cost_inputs: VersionedContractCodeCostInputs, } +pub fn wasm_module_memory_cost( + budget: &Budget, + contract_code_entry: &ContractCodeEntry, +) -> Result { + match &contract_code_entry.ext { + ContractCodeEntryExt::V0 => budget.get_memory_cost( + ContractCostType::VmInstantiation, + Some(contract_code_entry.code.len() as u64), + ), + ContractCodeEntryExt::V1(contract_code_entry_v1) => { + let cost_inputs = &contract_code_entry_v1.cost_inputs; + let mut res = 0_u64; + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmInstructions, + Some(cost_inputs.n_instructions as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmFunctions, + Some(cost_inputs.n_functions as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmGlobals, + Some(cost_inputs.n_globals as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmTableEntries, + Some(cost_inputs.n_table_entries as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmTypes, + Some(cost_inputs.n_types as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmDataSegments, + Some(cost_inputs.n_data_segments as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmElemSegments, + Some(cost_inputs.n_elem_segments as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmImports, + Some(cost_inputs.n_imports as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmExports, + Some(cost_inputs.n_exports as u64), + )?); + res = res.saturating_add(budget.get_memory_cost( + ContractCostType::ParseWasmDataSegmentBytes, + Some(cost_inputs.n_data_segment_bytes as u64), + )?); + Ok(res) + } + } +} + impl ParsedModule { pub fn new( context: &Ctx, diff --git a/soroban-env-host/tests/fees.rs b/soroban-env-host/tests/fees.rs index 0fe4ac80b..869a751d1 100644 --- a/soroban-env-host/tests/fees.rs +++ b/soroban-env-host/tests/fees.rs @@ -1,9 +1,9 @@ use soroban_env_common::xdr::{Hash, LedgerEntry, LedgerEntryData, LedgerEntryExt, WriteXdr}; use soroban_env_host::{ fees::{ - compute_rent_fee, compute_transaction_resource_fee, compute_write_fee_per_1kb, - FeeConfiguration, LedgerEntryRentChange, RentFeeConfiguration, TransactionResources, - WriteFeeConfiguration, MINIMUM_WRITE_FEE_PER_1KB, TTL_ENTRY_SIZE, + compute_rent_fee, compute_rent_write_fee_per_1kb, compute_transaction_resource_fee, + FeeConfiguration, LedgerEntryRentChange, RentFeeConfiguration, RentWriteFeeConfiguration, + TransactionResources, MINIMUM_RENT_WRITE_FEE_PER_1KB, TTL_ENTRY_SIZE, }, xdr::TtlEntry, DEFAULT_XDR_RW_LIMITS, @@ -34,9 +34,9 @@ where { let mut resources = TransactionResources { instructions: 0, - read_entries: 0, + disk_read_entries: 0, write_entries: 0, - read_bytes: 0, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, transaction_size_bytes: 0, @@ -52,9 +52,9 @@ fn resource_fee_computation_with_single_resource() { const BASE_HISTORICAL_FEE: i64 = 1758; let fee_config = FeeConfiguration { fee_per_instruction_increment: 1000, - fee_per_read_entry: 2000, + fee_per_disk_read_entry: 2000, fee_per_write_entry: 3000, - fee_per_read_1kb: 4000, + fee_per_disk_read_1kb: 4000, fee_per_write_1kb: 5000, fee_per_historical_1kb: 6000, fee_per_contract_event_1kb: 7000, @@ -103,7 +103,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_entries = 1; + res.disk_read_entries = 1; }), &fee_config, ), @@ -112,7 +112,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_entries = 5; + res.disk_read_entries = 5; }), &fee_config, ), @@ -121,7 +121,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_entries = u32::MAX; + res.disk_read_entries = u32::MAX; }), &fee_config, ), @@ -165,7 +165,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_bytes = 1; + res.disk_read_bytes = 1; }), &fee_config, ), @@ -175,7 +175,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_bytes = 5 * 1024; + res.disk_read_bytes = 5 * 1024; }), &fee_config, ), @@ -184,7 +184,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_bytes = 5 * 1024 + 1; + res.disk_read_bytes = 5 * 1024 + 1; }), &fee_config, ), @@ -193,7 +193,7 @@ fn resource_fee_computation_with_single_resource() { assert_eq!( compute_transaction_resource_fee( &change_resource(|res: &mut TransactionResources| { - res.read_bytes = u32::MAX; + res.disk_read_bytes = u32::MAX; }), &fee_config, ), @@ -331,18 +331,18 @@ fn resource_fee_computation() { compute_transaction_resource_fee( &TransactionResources { instructions: 0, - read_entries: 0, + disk_read_entries: 0, write_entries: 0, - read_bytes: 0, + disk_read_bytes: 0, write_bytes: 0, contract_events_size_bytes: 0, transaction_size_bytes: 0, }, &FeeConfiguration { fee_per_instruction_increment: 100, - fee_per_read_entry: 100, + fee_per_disk_read_entry: 100, fee_per_write_entry: 100, - fee_per_read_1kb: 100, + fee_per_disk_read_1kb: 100, fee_per_write_1kb: 100, fee_per_historical_1kb: 100, fee_per_contract_event_1kb: 100, @@ -358,18 +358,18 @@ fn resource_fee_computation() { compute_transaction_resource_fee( &TransactionResources { instructions: 1, - read_entries: 1, + disk_read_entries: 1, write_entries: 1, - read_bytes: 1, + disk_read_bytes: 1, write_bytes: 1, contract_events_size_bytes: 1, transaction_size_bytes: 1, }, &FeeConfiguration { fee_per_instruction_increment: 100, - fee_per_read_entry: 100, + fee_per_disk_read_entry: 100, fee_per_write_entry: 100, - fee_per_read_1kb: 100, + fee_per_disk_read_1kb: 100, fee_per_write_1kb: 100, fee_per_historical_1kb: 100, fee_per_contract_event_1kb: 100, @@ -386,18 +386,18 @@ fn resource_fee_computation() { compute_transaction_resource_fee( &TransactionResources { instructions: 10_123_456, - read_entries: 30, + disk_read_entries: 30, write_entries: 10, - read_bytes: 25_600, + disk_read_bytes: 25_600, write_bytes: 10_340, contract_events_size_bytes: 321_654, transaction_size_bytes: 35_721, }, &FeeConfiguration { fee_per_instruction_increment: 1000, - fee_per_read_entry: 2000, + fee_per_disk_read_entry: 2000, fee_per_write_entry: 4000, - fee_per_read_1kb: 1500, + fee_per_disk_read_1kb: 1500, fee_per_write_1kb: 3000, fee_per_historical_1kb: 300, fee_per_contract_event_1kb: 200, @@ -412,18 +412,18 @@ fn resource_fee_computation() { compute_transaction_resource_fee( &TransactionResources { instructions: u32::MAX, - read_entries: u32::MAX, + disk_read_entries: u32::MAX, write_entries: u32::MAX, - read_bytes: u32::MAX, + disk_read_bytes: u32::MAX, write_bytes: u32::MAX, contract_events_size_bytes: u32::MAX, transaction_size_bytes: u32::MAX, }, &FeeConfiguration { fee_per_instruction_increment: i64::MAX, - fee_per_read_entry: i64::MAX, + fee_per_disk_read_entry: i64::MAX, fee_per_write_entry: i64::MAX, - fee_per_read_1kb: i64::MAX, + fee_per_disk_read_1kb: i64::MAX, fee_per_write_1kb: i64::MAX, fee_per_historical_1kb: i64::MAX, fee_per_contract_event_1kb: i64::MAX, @@ -442,7 +442,8 @@ fn resource_fee_computation() { fn test_rent_extend_fees_with_only_extend() { let fee_config = RentFeeConfiguration { fee_per_write_entry: 10, - fee_per_write_1kb: 1000, + fee_per_rent_1kb: 1000, + fee_per_write_1kb: 500, persistent_rent_rate_denominator: 10_000, temporary_rent_rate_denominator: 100_000, }; @@ -461,9 +462,9 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(1 * 1000 * 200_000 / (10_000 * 1024)) (=20) + - // Expiration entry write bytes: ceil(1000 * 48 / 1024) (=47) + - // Expiration entry write: 10 - 20 + 47 + 10 + // TTL entry write bytes: ceil(500 * 48 / 1024) (=24) + + // TTL entry write: 10 + 20 + 24 + 10 ); // Minimal ledgers @@ -480,8 +481,8 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(10 * 1024 * 1000 * 1 / (10_000 * 1024)) (=1) + - // Expiration entry write entry/bytes: 57 - 1 + 57 + // Expiration entry write entry/bytes: 34 + 1 + 34 ); // Minimal ledgers & size @@ -498,8 +499,8 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(1 * 1000 * 1 / (10_000 * 1024)) - // Expiration entry write entry/bytes: 57 - 1 + 57 + // Expiration entry write entry/bytes: 34 + 1 + 34 ); // No size change @@ -516,8 +517,8 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(10 * 1024 * 1000 * 200_000 / (10_000 * 1024)) (=200_000) - // Expiration entry write entry/bytes: 57 - 200_000 + 57 + // Expiration entry write entry/bytes: 34 + 200_000 + 34 ); // Size decrease @@ -534,8 +535,8 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(5 * 1024 * 1000 * 200_000 / (10_000 * 1024)) (=100_000) + - // Expiration entry write entry/bytes: 57 - 100_000 + 57 + // Expiration entry write entry/bytes: 34 + 100_000 + 34 ); // Temp storage rate @@ -552,8 +553,8 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: ceil(10 * 1024 * 1000 * 200_000 / (100_000 * 1024)) (=20_000) + - // Expiration entry write entry/bytes: 57 - 20_000 + 57 + // Expiration entry write entry/bytes: 34 + 20_000 + 34 ); // Multiple entries @@ -607,9 +608,9 @@ fn test_rent_extend_fees_with_only_extend() { 50_000, ), // Rent: 20_000 + 200_000 + 1 + 20 + 200_000 + 20_000 (=440_021) + - // Expiration entry write bytes: ceil(6 * 1000 * 48 / 1024) (=282) + + // Expiration entry write bytes: ceil(6 * 500 * 48 / 1024) (=141) + // Expiration entry write: 10 * 6 - 440_021 + 282 + 60 + 440_021 + 141 + 60 ); } @@ -617,7 +618,8 @@ fn test_rent_extend_fees_with_only_extend() { fn test_rent_extend_fees_with_only_size_change() { let fee_config = RentFeeConfiguration { fee_per_write_entry: 100, - fee_per_write_1kb: 1000, + fee_per_rent_1kb: 1000, + fee_per_write_1kb: 500, persistent_rent_rate_denominator: 10_000, temporary_rent_rate_denominator: 100_000, }; @@ -721,7 +723,8 @@ fn test_rent_extend_fees_with_only_size_change() { fn test_rent_extend_with_size_change_and_extend() { let fee_config = RentFeeConfiguration { fee_per_write_entry: 10, - fee_per_write_1kb: 1000, + fee_per_rent_1kb: 1000, + fee_per_write_1kb: 500, persistent_rent_rate_denominator: 10_000, temporary_rent_rate_denominator: 100_000, }; @@ -741,8 +744,8 @@ fn test_rent_extend_with_size_change_and_extend() { ), // Rent: 100_000 * 1000 * 200_000 / (10_000 * 1024) + // 99_999 * 1000 * (100_000 - 25_000 + 1) / (10_000 * 1024) - // Expiration entry write entry/bytes: 57 - 2_685_550 + 57 + // Expiration entry write entry/bytes: 34 + 2_685_550 + 34 ); // Temp entry @@ -760,8 +763,8 @@ fn test_rent_extend_with_size_change_and_extend() { ), // Rent: 100_000 * 1000 * 200_000 / (10_000 * 1024) + // 99_999 * 1000 * (100_000 - 25_000 + 1) / (10_000 * 1024) - // Expiration entry write entry/bytes: 57 - 268_556 + 57 + // Expiration entry write entry/bytes: 34 + 268_556 + 34 ); // Multiple entries @@ -787,9 +790,9 @@ fn test_rent_extend_with_size_change_and_extend() { 25_000, ), // Rent: 2_685_550 + 268_556 - // Expiration entry write bytes: ceil(2 * 1000 * 48 / 1024) (=94) + + // Expiration entry write bytes: ceil(2 * 500 * 48 / 1024) (=47) + // Expiration entry write: 10 * 2 - 2_954_106 + 94 + 20 + 2_954_106 + 47 + 20 ); // Small increments @@ -807,8 +810,8 @@ fn test_rent_extend_with_size_change_and_extend() { ), // Rent: ceil(2 * 1000 * 1 / (10_000 * 1024)) + // ceil(1 * 1000 * (100_000 - 99_999 + 1) / (10_000 * 1024)) (=2) - // Expiration entry write entry/bytes: 57 - 2 + 57 + // Expiration entry write entry/bytes: 34 + 2 + 34 ); } @@ -816,7 +819,8 @@ fn test_rent_extend_with_size_change_and_extend() { fn test_rent_extend_without_old_entry() { let fee_config = RentFeeConfiguration { fee_per_write_entry: 10, - fee_per_write_1kb: 1000, + fee_per_rent_1kb: 1000, + fee_per_write_1kb: 500, persistent_rent_rate_denominator: 10_000, temporary_rent_rate_denominator: 100_000, }; @@ -835,8 +839,8 @@ fn test_rent_extend_without_old_entry() { 25_000, ), // Rent: 100_000 * 1000 * (100_000 - 25_000) / (10_000 * 1024) - // Expiration entry write entry/bytes: 57 - 732_432 + 57 + // Expiration entry write entry/bytes: 34 + 732_432 + 34 ); // Temp storage @@ -853,101 +857,101 @@ fn test_rent_extend_without_old_entry() { 25_000, ), // Rent: 100_000 * 1000 * (100_000 - 25_000) / (10_000 * 1024) - // Expiration entry write entry/bytes: 57 - 73_244 + 57 + // Expiration entry write entry/bytes: 34 + 73_244 + 34 ); } #[test] -fn test_compute_write_fee() { - let fee_config = WriteFeeConfiguration { - bucket_list_target_size_bytes: 100_000, - write_fee_1kb_bucket_list_low: 100, - write_fee_1kb_bucket_list_high: 10_000, - bucket_list_write_fee_growth_factor: 50, +fn test_compute_rent_write_fee() { + let fee_config = RentWriteFeeConfiguration { + state_target_size_bytes: 100_000, + rent_fee_1kb_state_size_low: 100, + rent_fee_1kb_state_size_high: 10_000, + state_size_rent_fee_growth_factor: 50, }; - // Empty bucket list + // Empty state assert_eq!( - compute_write_fee_per_1kb(0, &fee_config), - MINIMUM_WRITE_FEE_PER_1KB + compute_rent_write_fee_per_1kb(0, &fee_config), + MINIMUM_RENT_WRITE_FEE_PER_1KB ); - // Partially filled bucket list + // Non-empty state below target assert_eq!( - compute_write_fee_per_1kb(50_000, &fee_config), + compute_rent_write_fee_per_1kb(50_000, &fee_config), 100 + (10_000 - 100) / 2 ); - assert_eq!(compute_write_fee_per_1kb(56_789, &fee_config), 5723); - // Full bucket list - assert_eq!(compute_write_fee_per_1kb(100_000, &fee_config), 10_000); - // Bucket list bigger than target + assert_eq!(compute_rent_write_fee_per_1kb(56_789, &fee_config), 5723); + // State size is at target + assert_eq!(compute_rent_write_fee_per_1kb(100_000, &fee_config), 10_000); + // State size is bigger than target assert_eq!( - compute_write_fee_per_1kb(150_000, &fee_config), + compute_rent_write_fee_per_1kb(150_000, &fee_config), 10_000 + 50 * (10_000 - 100) / 2 ); - // Bucket list several times bigger than target + // State size is several times bigger than target assert_eq!( - compute_write_fee_per_1kb(580_000, &fee_config), + compute_rent_write_fee_per_1kb(580_000, &fee_config), 10_000 + 2_376_000 ); - let large_fee_config = WriteFeeConfiguration { - bucket_list_target_size_bytes: 100_000_000_000_000, - write_fee_1kb_bucket_list_low: 1_000_000, - write_fee_1kb_bucket_list_high: 1_000_000_000, - bucket_list_write_fee_growth_factor: 50, + let large_fee_config = RentWriteFeeConfiguration { + state_target_size_bytes: 100_000_000_000_000, + rent_fee_1kb_state_size_low: 1_000_000, + rent_fee_1kb_state_size_high: 1_000_000_000, + state_size_rent_fee_growth_factor: 50, }; // Large bucket list size and fees, half-filled bucket list assert_eq!( - compute_write_fee_per_1kb(50_000_000_000_000, &large_fee_config), + compute_rent_write_fee_per_1kb(50_000_000_000_000, &large_fee_config), 1_000_000 + (1_000_000_000 - 1_000_000) / 2 ); // Large bucket list size and fees, over target bucket list assert_eq!( - compute_write_fee_per_1kb(150_000_000_000_000, &large_fee_config), + compute_rent_write_fee_per_1kb(150_000_000_000_000, &large_fee_config), 1_000_000_000 + 50 * (1_000_000_000 - 1_000_000) / 2 ); } #[test] fn test_compute_write_fee_with_negative_low() { - let fee_config = WriteFeeConfiguration { - bucket_list_target_size_bytes: 100_000, - write_fee_1kb_bucket_list_low: -1_000, - write_fee_1kb_bucket_list_high: 10_000, - bucket_list_write_fee_growth_factor: 50, + let fee_config = RentWriteFeeConfiguration { + state_target_size_bytes: 100_000, + rent_fee_1kb_state_size_low: -1_000, + rent_fee_1kb_state_size_high: 10_000, + state_size_rent_fee_growth_factor: 50, }; // clamping before target assert_eq!( - compute_write_fee_per_1kb(18_181, &fee_config), - MINIMUM_WRITE_FEE_PER_1KB + compute_rent_write_fee_per_1kb(18_181, &fee_config), + MINIMUM_RENT_WRITE_FEE_PER_1KB ); //ceil(11_000 * 18_182 / 100_000) - 1000 = 1001 assert_eq!( - compute_write_fee_per_1kb(18_182, &fee_config), - MINIMUM_WRITE_FEE_PER_1KB + 1 + compute_rent_write_fee_per_1kb(18_182, &fee_config), + MINIMUM_RENT_WRITE_FEE_PER_1KB + 1 ); // Bucket list bigger than target. assert_eq!( - compute_write_fee_per_1kb(150_000, &fee_config), + compute_rent_write_fee_per_1kb(150_000, &fee_config), 10_000 + 50 * (10_000 + 1_000) / 2 ); } #[test] fn test_compute_write_fee_clamp_after_target() { - let fee_config = WriteFeeConfiguration { - bucket_list_target_size_bytes: 100_000, - write_fee_1kb_bucket_list_low: 10, - write_fee_1kb_bucket_list_high: 20, - bucket_list_write_fee_growth_factor: 50, + let fee_config = RentWriteFeeConfiguration { + state_target_size_bytes: 100_000, + rent_fee_1kb_state_size_low: 10, + rent_fee_1kb_state_size_high: 20, + state_size_rent_fee_growth_factor: 50, }; // Bucket list bigger than target. assert_eq!( - compute_write_fee_per_1kb(100_001, &fee_config), - MINIMUM_WRITE_FEE_PER_1KB + compute_rent_write_fee_per_1kb(100_001, &fee_config), + MINIMUM_RENT_WRITE_FEE_PER_1KB ); } diff --git a/soroban-env-macros/Cargo.toml b/soroban-env-macros/Cargo.toml index d4ad528d1..9d6993f0d 100644 --- a/soroban-env-macros/Cargo.toml +++ b/soroban-env-macros/Cargo.toml @@ -13,7 +13,7 @@ rust-version.workspace = true proc-macro = true [dependencies] -stellar-xdr = { workspace = true, features = ["curr", "std"] } +stellar-xdr = { workspace = true, features = ["next", "std"] } syn = {version="2.0.39",features=["full"]} quote = "1.0.33" proc-macro2 = "1.0.69" diff --git a/soroban-env-macros/src/lib.rs b/soroban-env-macros/src/lib.rs index 3a3e7b568..cf04f62e0 100644 --- a/soroban-env-macros/src/lib.rs +++ b/soroban-env-macros/src/lib.rs @@ -13,7 +13,7 @@ use syn::{parse::Parse, parse_macro_input, Ident, LitInt, LitStr, Token}; // Import the XDR definitions of a specific version -- curr or next -- of the xdr crate. #[cfg(not(feature = "next"))] -use stellar_xdr::curr as xdr; +use stellar_xdr::next as xdr; #[cfg(feature = "next")] use stellar_xdr::next as xdr; diff --git a/soroban-simulation/src/network_config.rs b/soroban-simulation/src/network_config.rs index 94eb4ef49..d5cd9be6c 100644 --- a/soroban-simulation/src/network_config.rs +++ b/soroban-simulation/src/network_config.rs @@ -2,7 +2,8 @@ use crate::SnapshotSourceWithArchive; use anyhow::{anyhow, bail, Context, Result}; use soroban_env_host::budget::Budget; use soroban_env_host::fees::{ - compute_write_fee_per_1kb, FeeConfiguration, RentFeeConfiguration, WriteFeeConfiguration, + compute_rent_write_fee_per_1kb, FeeConfiguration, RentFeeConfiguration, + RentWriteFeeConfiguration, }; use soroban_env_host::xdr::{ ConfigSettingEntry, ConfigSettingId, ContractCostParams, LedgerEntry, LedgerEntryData, @@ -72,6 +73,7 @@ impl NetworkConfig { ) -> Result { let compute = load_setting!(snapshot, ContractComputeV0); let ledger_cost = load_setting!(snapshot, ContractLedgerCostV0); + let ledger_cost_ext = load_setting!(snapshot, ContractLedgerCostExtV0); let historical_data = load_setting!(snapshot, ContractHistoricalDataV0); let events = load_setting!(snapshot, ContractEventsV0); let bandwidth = load_setting!(snapshot, ContractBandwidthV0); @@ -79,30 +81,31 @@ impl NetworkConfig { let cpu_cost_params = load_setting!(snapshot, ContractCostParamsCpuInstructions); let memory_cost_params = load_setting!(snapshot, ContractCostParamsMemoryBytes); - let write_fee_configuration = WriteFeeConfiguration { - bucket_list_target_size_bytes: ledger_cost.bucket_list_target_size_bytes, - write_fee_1kb_bucket_list_low: ledger_cost.write_fee1_kb_bucket_list_low, - write_fee_1kb_bucket_list_high: ledger_cost.write_fee1_kb_bucket_list_high, - bucket_list_write_fee_growth_factor: ledger_cost.bucket_list_write_fee_growth_factor, + let write_fee_configuration = RentWriteFeeConfiguration { + state_target_size_bytes: ledger_cost.bucket_list_target_size_bytes, + rent_fee_1kb_state_size_low: ledger_cost.write_fee1_kb_bucket_list_low, + rent_fee_1kb_state_size_high: ledger_cost.write_fee1_kb_bucket_list_high, + state_size_rent_fee_growth_factor: ledger_cost.bucket_list_write_fee_growth_factor, }; let bucket_list_size: i64 = bucket_list_size .try_into() .context("bucket list size exceeds i64::MAX")?; - let write_fee_per_1kb = - compute_write_fee_per_1kb(bucket_list_size, &write_fee_configuration); + let rent_fee_per_1kb = + compute_rent_write_fee_per_1kb(bucket_list_size, &write_fee_configuration); let fee_configuration = FeeConfiguration { fee_per_instruction_increment: compute.fee_rate_per_instructions_increment, - fee_per_read_entry: ledger_cost.fee_read_ledger_entry, + fee_per_disk_read_entry: ledger_cost.fee_read_ledger_entry, fee_per_write_entry: ledger_cost.fee_write_ledger_entry, - fee_per_read_1kb: ledger_cost.fee_read1_kb, - fee_per_write_1kb: write_fee_per_1kb, + fee_per_disk_read_1kb: ledger_cost.fee_read1_kb, + fee_per_write_1kb: ledger_cost_ext.fee_write1_kb, fee_per_historical_1kb: historical_data.fee_historical1_kb, fee_per_contract_event_1kb: events.fee_contract_events1_kb, fee_per_transaction_size_1kb: bandwidth.fee_tx_size1_kb, }; let rent_fee_configuration = RentFeeConfiguration { - fee_per_write_1kb: write_fee_per_1kb, + fee_per_rent_1kb: rent_fee_per_1kb, + fee_per_write_1kb: ledger_cost_ext.fee_write1_kb, fee_per_write_entry: ledger_cost.fee_write_ledger_entry, persistent_rent_rate_denominator: state_archival.persistent_rent_rate_denominator, temporary_rent_rate_denominator: state_archival.temp_rent_rate_denominator, diff --git a/soroban-simulation/src/resources.rs b/soroban-simulation/src/resources.rs index 77bd3b078..4f463f531 100644 --- a/soroban-simulation/src/resources.rs +++ b/soroban-simulation/src/resources.rs @@ -3,8 +3,9 @@ use crate::network_config::NetworkConfig; use crate::simulation::{SimulationAdjustmentConfig, SimulationAdjustmentFactor}; use anyhow::{anyhow, ensure, Context, Result}; +use soroban_env_host::e2e_invoke::entry_size_for_rent; +use soroban_env_host::xdr::SorobanTransactionDataExt; use soroban_env_host::{ - e2e_invoke::{extract_rent_changes, LedgerEntryChange}, fees::{ compute_rent_fee, compute_transaction_resource_fee, LedgerEntryRentChange, TransactionResources, @@ -12,12 +13,12 @@ use soroban_env_host::{ ledger_info::get_key_durability, storage::SnapshotSource, xdr::{ - BytesM, ContractDataDurability, DecoratedSignature, Duration, ExtensionPoint, Hash, - LedgerBounds, LedgerFootprint, LedgerKey, Limits, Memo, MuxedAccount, MuxedAccountMed25519, - Operation, OperationBody, Preconditions, PreconditionsV2, ReadXdr, SequenceNumber, - Signature, SignatureHint, SignerKey, SignerKeyEd25519SignedPayload, SorobanResources, - SorobanTransactionData, TimeBounds, TimePoint, Transaction, TransactionExt, - TransactionV1Envelope, Uint256, WriteXdr, + BytesM, ContractDataDurability, DecoratedSignature, Duration, Hash, LedgerBounds, + LedgerFootprint, LedgerKey, Memo, MuxedAccount, MuxedAccountMed25519, Operation, + OperationBody, Preconditions, PreconditionsV2, SequenceNumber, Signature, SignatureHint, + SignerKey, SignerKeyEd25519SignedPayload, SorobanResources, SorobanTransactionData, + TimeBounds, TimePoint, Transaction, TransactionExt, TransactionV1Envelope, Uint256, + WriteXdr, }, LedgerInfo, DEFAULT_XDR_RW_LIMITS, }; @@ -89,13 +90,28 @@ pub(crate) fn compute_adjusted_transaction_resources( contract_events_and_return_value_size: u32, ) -> Result { adjustment_config.adjust_resources(simulated_operation_resources); + let mut disk_read_entries = 0; + for k in simulated_operation_resources.footprint.read_only.iter() { + match k { + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => (), + _ => { + disk_read_entries += 1; + } + } + } + for k in simulated_operation_resources.footprint.read_write.iter() { + match k { + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => (), + _ => { + disk_read_entries += 1; + } + } + } Ok(TransactionResources { instructions: simulated_operation_resources.instructions, - read_entries: (simulated_operation_resources.footprint.read_only.len() - + simulated_operation_resources.footprint.read_write.len()) - as u32, + disk_read_entries, write_entries: simulated_operation_resources.footprint.read_write.len() as u32, - read_bytes: simulated_operation_resources.read_bytes, + disk_read_bytes: simulated_operation_resources.read_bytes, write_bytes: simulated_operation_resources.write_bytes, transaction_size_bytes: adjustment_config.tx_size.adjust_u32( estimate_max_transaction_size_for_operation(operation, &simulated_operation_resources) @@ -105,53 +121,16 @@ pub(crate) fn compute_adjusted_transaction_resources( }) } -pub(crate) fn simulate_invoke_host_function_op_resources( - ledger_changes: &[LedgerEntryChange], - simulated_instructions: u32, -) -> Result<(SorobanResources, Vec)> { - let mut read_bytes = 0; - let mut write_bytes = 0; - let mut read_only_footprint = vec![]; - let mut read_write_footprint = vec![]; - for ledger_change in ledger_changes { - read_bytes += ledger_change.old_entry_size_bytes; - // It should be safe to decode values returned encoded by host without limits. - let key = LedgerKey::from_xdr(&ledger_change.encoded_key, Limits::none())?; - if ledger_change.read_only { - read_only_footprint.push(key); - } else { - read_write_footprint.push(key); - if let Some(new_value) = &ledger_change.encoded_new_value { - write_bytes += new_value.len() as u32; - } - } - } - read_only_footprint.sort(); - read_write_footprint.sort(); - let footprint = LedgerFootprint { - read_only: read_only_footprint.try_into()?, - read_write: read_write_footprint.try_into()?, - }; - let resources = SorobanResources { - footprint, - instructions: simulated_instructions, - read_bytes, - write_bytes, - }; - let rent_changes = extract_rent_changes(ledger_changes); - Ok((resources, rent_changes)) -} - pub(crate) fn simulate_extend_ttl_op_resources( keys_to_extend: &[LedgerKey], snapshot: &impl SnapshotSource, + network_config: &NetworkConfig, current_ledger_seq: u32, extend_to: u32, ) -> Result<(SorobanResources, Vec)> { - let mut read_bytes = 0; let mut rent_changes = Vec::::with_capacity(keys_to_extend.len()); let mut extended_keys = Vec::::with_capacity(keys_to_extend.len()); - + let budget = network_config.create_budget()?; let new_live_until_ledger = current_ledger_seq + extend_to; for key in keys_to_extend { let durability = get_key_durability(key).ok_or_else(|| anyhow!("Can't extend TTL for ledger entry with key `{:?}`. Only entries with TTL (contract data or code entries) can have it extended", key))?; @@ -159,7 +138,6 @@ pub(crate) fn simulate_extend_ttl_op_resources( let Some((entry, live_until)) = entry_with_live_until else { continue; }; - let entry_size: u32 = entry.to_xdr(DEFAULT_XDR_RW_LIMITS)?.len().try_into()?; let current_live_until_ledger = live_until.ok_or_else(|| { anyhow!("Internal error: missing TTL for ledger key that must have TTL: `{key:?}`") })?; @@ -167,7 +145,8 @@ pub(crate) fn simulate_extend_ttl_op_resources( continue; } extended_keys.push(key.clone()); - read_bytes += entry_size; + let entry_xdr_size = entry.to_xdr(DEFAULT_XDR_RW_LIMITS)?.len().try_into()?; + let entry_size: u32 = entry_size_for_rent(&budget, &entry, entry_xdr_size)?; rent_changes.push(LedgerEntryRentChange { is_persistent: durability == ContractDataDurability::Persistent, old_size_bytes: entry_size, @@ -183,7 +162,7 @@ pub(crate) fn simulate_extend_ttl_op_resources( read_write: Default::default(), }, instructions: 0, - read_bytes, + read_bytes: 0, write_bytes: 0, }; Ok((resources, rent_changes)) @@ -192,6 +171,7 @@ pub(crate) fn simulate_extend_ttl_op_resources( pub(crate) fn simulate_restore_op_resources( keys_to_restore: &[LedgerKey], snapshot_source: &impl SnapshotSourceWithArchive, + network_config: &NetworkConfig, ledger_info: &LedgerInfo, ) -> Result<(SorobanResources, Vec)> { let restored_live_until_ledger = ledger_info @@ -202,6 +182,7 @@ pub(crate) fn simulate_restore_op_resources( let mut restored_bytes = 0_u32; let mut rent_changes: Vec = Vec::with_capacity(keys_to_restore.len()); let mut restored_keys = Vec::::with_capacity(keys_to_restore.len()); + let budget = network_config.create_budget()?; for key in keys_to_restore { let durability = get_key_durability(key); @@ -223,12 +204,13 @@ pub(crate) fn simulate_restore_op_resources( } restored_keys.push(key.clone()); - let entry_size: u32 = entry.to_xdr(DEFAULT_XDR_RW_LIMITS)?.len().try_into()?; - restored_bytes = restored_bytes.saturating_add(entry_size); + let entry_xdr_size: u32 = entry.to_xdr(DEFAULT_XDR_RW_LIMITS)?.len().try_into()?; + let entry_rent_size: u32 = entry_size_for_rent(&budget, &entry, entry_xdr_size)?; + restored_bytes = restored_bytes.saturating_add(entry_xdr_size); rent_changes.push(LedgerEntryRentChange { is_persistent: true, old_size_bytes: 0, - new_size_bytes: entry_size, + new_size_bytes: entry_rent_size, old_live_until_ledger: 0, new_live_until_ledger: restored_live_until_ledger, }); @@ -299,7 +281,7 @@ fn estimate_max_transaction_size_for_operation( write_bytes: 0, }, resource_fee: 0, - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, }), }, signatures: signatures.try_into()?, diff --git a/soroban-simulation/src/simulation.rs b/soroban-simulation/src/simulation.rs index ef3de2807..347765bd3 100644 --- a/soroban-simulation/src/simulation.rs +++ b/soroban-simulation/src/simulation.rs @@ -1,12 +1,13 @@ use crate::network_config::NetworkConfig; use crate::resources::{ compute_adjusted_transaction_resources, compute_resource_fee, simulate_extend_ttl_op_resources, - simulate_invoke_host_function_op_resources, simulate_restore_op_resources, + simulate_restore_op_resources, }; use crate::snapshot_source::{ SimulationSnapshotSource, SimulationSnapshotSourceWithArchive, SnapshotSourceWithArchive, }; use anyhow::Result; +use soroban_env_host::e2e_invoke::extract_rent_changes; use soroban_env_host::{ e2e_invoke::invoke_host_function_in_recording_mode, e2e_invoke::{LedgerEntryChange, RecordingInvocationAuthMode}, @@ -173,11 +174,8 @@ pub fn simulate_invoke_host_function_op( simulation_result.contract_events = recording_result.contract_events; simulation_result.modified_entries = extract_modified_entries(&*snapshot_source, &recording_result.ledger_changes)?; - - let (mut resources, rent_changes) = simulate_invoke_host_function_op_resources( - &recording_result.ledger_changes, - simulation_result.simulated_instructions, - )?; + let mut resources = recording_result.resources; + let rent_changes = extract_rent_changes(&recording_result.ledger_changes); let operation = OperationBody::InvokeHostFunction(InvokeHostFunctionOp { host_function: host_fn, auth: simulation_result.auth.clone().try_into()?, @@ -227,6 +225,7 @@ pub fn simulate_extend_ttl_op( let (mut resources, rent_changes) = simulate_extend_ttl_op_resources( keys_to_extend, &snapshot_source, + network_config, ledger_info.sequence_number, extend_to, )?; @@ -272,8 +271,12 @@ pub fn simulate_restore_op( keys_to_restore: &[LedgerKey], ) -> Result { let snapshot_source = SimulationSnapshotSourceWithArchive::new(snapshot_source); - let (mut resources, rent_changes) = - simulate_restore_op_resources(keys_to_restore, &snapshot_source, ledger_info)?; + let (mut resources, rent_changes) = simulate_restore_op_resources( + keys_to_restore, + &snapshot_source, + network_config, + ledger_info, + )?; let operation = OperationBody::RestoreFootprint(RestoreFootprintOp { ext: ExtensionPoint::V0, }); @@ -338,7 +341,7 @@ fn create_transaction_data( SorobanTransactionData { resources, resource_fee, - ext: ExtensionPoint::V0, + ext: soroban_env_host::xdr::SorobanTransactionDataExt::V0, } } diff --git a/soroban-simulation/src/test/network_config.rs b/soroban-simulation/src/test/network_config.rs index b1bf90169..2aecddcbb 100644 --- a/soroban-simulation/src/test/network_config.rs +++ b/soroban-simulation/src/test/network_config.rs @@ -5,15 +5,15 @@ use soroban_env_host::{ xdr::{ ConfigSettingContractBandwidthV0, ConfigSettingContractComputeV0, ConfigSettingContractEventsV0, ConfigSettingContractHistoricalDataV0, - ConfigSettingContractLedgerCostV0, ConfigSettingEntry, ContractCostParamEntry, - ContractCostParams, ExtensionPoint, LedgerEntry, LedgerEntryData, StateArchivalSettings, + ConfigSettingContractLedgerCostExtV0, ConfigSettingContractLedgerCostV0, + ConfigSettingEntry, ContractCostParamEntry, ContractCostParams, ContractCostType, + ExtensionPoint, LedgerEntry, LedgerEntryData, StateArchivalSettings, }, LedgerInfo, }; use crate::testutils::MockSnapshotSource; use pretty_assertions::assert_eq; -use soroban_env_host::xdr::ContractCostType; fn config_entry(entry: ConfigSettingEntry) -> (LedgerEntry, Option) { (ledger_entry(LedgerEntryData::ConfigSetting(entry)), None) @@ -88,6 +88,12 @@ fn test_load_config_from_snapshot() { bucket_list_write_fee_growth_factor: 50, }, )), + config_entry(ConfigSettingEntry::ContractLedgerCostExtV0( + ConfigSettingContractLedgerCostExtV0 { + tx_max_in_memory_read_entries: 16, + fee_write1_kb: 17, + }, + )), config_entry(ConfigSettingEntry::ContractHistoricalDataV0( ConfigSettingContractHistoricalDataV0 { fee_historical1_kb: 20, @@ -132,22 +138,23 @@ fn test_load_config_from_snapshot() { let network_config = NetworkConfig::load_from_snapshot(&snapshot_source, 150_000_000_000_000).unwrap(); // From tests/resources `test_compute_write_fee` - let write_fee = 1_000_000_000 + 50 * (1_000_000_000_i64 - 1_000_000) / 2; + let rent_fee_per_1kb = 1_000_000_000 + 50 * (1_000_000_000_i64 - 1_000_000) / 2; assert_eq!( network_config, NetworkConfig { fee_configuration: FeeConfiguration { fee_per_instruction_increment: 3, - fee_per_read_entry: 13, + fee_per_disk_read_entry: 13, fee_per_write_entry: 14, - fee_per_read_1kb: 15, - fee_per_write_1kb: write_fee, + fee_per_disk_read_1kb: 15, + fee_per_write_1kb: 17, fee_per_historical_1kb: 20, fee_per_contract_event_1kb: 22, fee_per_transaction_size_1kb: 25, }, rent_fee_configuration: RentFeeConfiguration { - fee_per_write_1kb: write_fee, + fee_per_rent_1kb: rent_fee_per_1kb, + fee_per_write_1kb: 17, fee_per_write_entry: 14, persistent_rent_rate_denominator: 29, temporary_rent_rate_denominator: 30, diff --git a/soroban-simulation/src/test/simulation.rs b/soroban-simulation/src/test/simulation.rs index 1b93fe729..d0553844d 100644 --- a/soroban-simulation/src/test/simulation.rs +++ b/soroban-simulation/src/test/simulation.rs @@ -22,7 +22,8 @@ use soroban_env_host::xdr::{ ScContractInstance, ScErrorCode, ScErrorType, ScMap, ScNonceKey, ScString, ScSymbol, ScVal, SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanAuthorizedInvocation, SorobanCredentials, SorobanResources, SorobanTransactionData, - TrustLineAsset, TrustLineEntry, TrustLineEntryExt, TrustLineFlags, Uint256, VecM, + SorobanTransactionDataExt, TrustLineAsset, TrustLineEntry, TrustLineEntryExt, TrustLineFlags, + Uint256, VecM, }; use soroban_env_host::HostError; use soroban_test_wasms::{ADD_I32, AUTH_TEST_CONTRACT, TRY_CALL_SAC}; @@ -49,15 +50,16 @@ fn default_network_config() -> NetworkConfig { NetworkConfig { fee_configuration: FeeConfiguration { fee_per_instruction_increment: 10, - fee_per_read_entry: 20, + fee_per_disk_read_entry: 20, fee_per_write_entry: 30, - fee_per_read_1kb: 40, + fee_per_disk_read_1kb: 40, fee_per_write_1kb: 50, fee_per_historical_1kb: 60, fee_per_contract_event_1kb: 70, fee_per_transaction_size_1kb: 80, }, rent_fee_configuration: RentFeeConfiguration { + fee_per_rent_1kb: 100, fee_per_write_1kb: 50, fee_per_write_entry: 30, persistent_rent_rate_denominator: 100, @@ -135,7 +137,7 @@ fn test_simulate_upload_wasm() { assert_eq!( res.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -145,7 +147,7 @@ fn test_simulate_upload_wasm() { read_bytes: 0, write_bytes: expected_write_bytes, }, - resource_fee: 35548, + resource_fee: 14073223, }) ); assert_eq!(res.simulated_instructions, expected_instructions); @@ -189,7 +191,7 @@ fn test_simulate_upload_wasm() { assert_eq!( res_with_adjustments.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -199,7 +201,7 @@ fn test_simulate_upload_wasm() { read_bytes: 0, write_bytes: expected_write_bytes + 300, }, - resource_fee: 135867, + resource_fee: 21109107, }) ); } @@ -322,17 +324,17 @@ fn test_simulate_create_contract() { assert_eq!( res.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: vec![contract.wasm_key.clone()].try_into().unwrap(), read_write: vec![contract.contract_key.clone()].try_into().unwrap() }, instructions: expected_instructions, - read_bytes: 684, + read_bytes: 0, write_bytes: 104, }, - resource_fee: 8149, + resource_fee: 13160, }) ); assert_eq!(res.simulated_instructions, expected_instructions); @@ -468,7 +470,7 @@ fn test_simulate_invoke_contract_with_auth() { assert_eq!( res.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: vec![ @@ -490,10 +492,10 @@ fn test_simulate_invoke_contract_with_auth() { .unwrap() }, instructions: expected_instructions, - read_bytes: 7540, + read_bytes: 144, write_bytes: 76, }, - resource_fee: 78557, + resource_fee: 115257, }) ); assert_eq!(res.simulated_instructions, expected_instructions); @@ -556,7 +558,7 @@ fn test_simulate_extend_ttl_op() { no_op_extension, ExtendTtlOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -585,7 +587,7 @@ fn test_simulate_extend_ttl_op() { extension_for_some_entries, ExtendTtlOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: vec![ @@ -600,10 +602,10 @@ fn test_simulate_extend_ttl_op() { read_write: Default::default() }, instructions: 0, - read_bytes: 7808, + read_bytes: 0, write_bytes: 0, }, - resource_fee: 341657, + resource_fee: 17929120, } } ); @@ -617,22 +619,21 @@ fn test_simulate_extend_ttl_op() { 1_000_001, ) .unwrap(); - let expected_read_bytes = 8040; assert_eq!( extension_for_all_entries, ExtendTtlOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: keys.clone().tap_mut(|v| v.sort()).try_into().unwrap(), read_write: Default::default() }, instructions: 0, - read_bytes: expected_read_bytes, + read_bytes: 0, write_bytes: 0, }, - resource_fee: 3741533, + resource_fee: 306142974, } } ); @@ -667,17 +668,17 @@ fn test_simulate_extend_ttl_op() { extension_for_all_entries_with_adjustment, ExtendTtlOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: extension_for_all_entries .transaction_data .resources .footprint, instructions: 0, - read_bytes: (expected_read_bytes as f64 * 1.2) as u32, + read_bytes: 0, write_bytes: 0, }, - resource_fee: 5612108, + resource_fee: 459214435, } } ); @@ -723,7 +724,7 @@ fn test_simulate_restore_op() { no_op_restoration, RestoreOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -753,7 +754,7 @@ fn test_simulate_restore_op() { restoration_for_some_entries, RestoreOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -766,7 +767,7 @@ fn test_simulate_restore_op() { read_bytes: expected_rw_bytes, write_bytes: expected_rw_bytes, }, - resource_fee: 375389, + resource_fee: 32017829, } } ); @@ -785,7 +786,7 @@ fn test_simulate_restore_op() { extension_for_all_entries, RestoreOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -795,7 +796,7 @@ fn test_simulate_restore_op() { read_bytes: expected_rw_bytes, write_bytes: expected_rw_bytes, }, - resource_fee: 383433, + resource_fee: 32615676, } } ); @@ -813,7 +814,7 @@ fn test_simulate_restore_op() { extension_for_all_entries_with_adjustment, RestoreOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -823,7 +824,7 @@ fn test_simulate_restore_op() { read_bytes: (expected_rw_bytes as f64 * 1.2) as u32, write_bytes: (expected_rw_bytes as f64 * 1.3) as u32, }, - resource_fee: 574827, + resource_fee: 48923231, } } ); @@ -1029,7 +1030,7 @@ fn test_simulate_successful_sac_call() { assert_eq!( res.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: vec![ledger_entry_to_ledger_key(&contract_instance_le).unwrap(),] @@ -1040,10 +1041,10 @@ fn test_simulate_successful_sac_call() { .unwrap() }, instructions: 3302139, - read_bytes: 532, + read_bytes: 116, write_bytes: 116, }, - resource_fee: 28345, + resource_fee: 52859, }) ); } @@ -1125,7 +1126,7 @@ fn test_simulate_unsuccessful_sac_call_with_try_call() { assert_eq!( res.transaction_data, Some(SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: vec![ @@ -1143,10 +1144,10 @@ fn test_simulate_unsuccessful_sac_call_with_try_call() { read_write: Default::default(), }, instructions: 5352005, - read_bytes: 1196, + read_bytes: 0, write_bytes: 0, }, - resource_fee: 5808, + resource_fee: 5701, }) ); } diff --git a/soroban-simulation/src/test/snapshot_source.rs b/soroban-simulation/src/test/snapshot_source.rs index 4279786b3..41fdd89f1 100644 --- a/soroban-simulation/src/test/snapshot_source.rs +++ b/soroban-simulation/src/test/snapshot_source.rs @@ -12,8 +12,8 @@ use soroban_env_host::xdr::{ AccountEntry, AccountEntryExt, AccountEntryExtensionV1, AccountEntryExtensionV1Ext, AccountEntryExtensionV2, AccountEntryExtensionV2Ext, AccountEntryExtensionV3, ExtensionPoint, LedgerEntryData, LedgerFootprint, Liabilities, SequenceNumber, Signer, SignerKey, - SorobanResources, SorobanTransactionData, SponsorshipDescriptor, Thresholds, TimePoint, - Uint256, + SorobanResources, SorobanTransactionData, SorobanTransactionDataExt, SponsorshipDescriptor, + Thresholds, TimePoint, Uint256, }; use soroban_env_host::LedgerInfo; use std::rc::Rc; @@ -44,16 +44,17 @@ fn test_automatic_restoration() { let network_config = NetworkConfig { fee_configuration: FeeConfiguration { fee_per_instruction_increment: 8000, - fee_per_read_entry: 500, + fee_per_disk_read_entry: 500, fee_per_write_entry: 1000, - fee_per_read_1kb: 30, + fee_per_disk_read_1kb: 30, fee_per_write_1kb: 60, fee_per_historical_1kb: 200, fee_per_contract_event_1kb: 500, fee_per_transaction_size_1kb: 300, }, rent_fee_configuration: RentFeeConfiguration { - fee_per_write_1kb: 5000, + fee_per_rent_1kb: 5000, + fee_per_write_1kb: 60, fee_per_write_entry: 1000, persistent_rent_rate_denominator: 10, temporary_rent_rate_denominator: 100, @@ -156,7 +157,7 @@ fn test_automatic_restoration() { .unwrap(), Some(RestoreOpSimulationResult { transaction_data: SorobanTransactionData { - ext: ExtensionPoint::V0, + ext: SorobanTransactionDataExt::V0, resources: SorobanResources { footprint: LedgerFootprint { read_only: Default::default(), @@ -171,7 +172,7 @@ fn test_automatic_restoration() { read_bytes: 112, write_bytes: 112, }, - resource_fee: 62192, + resource_fee: 6041, } }) );