diff --git a/Cargo.lock b/Cargo.lock index 57774d3..7b1deb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3122,7 +3122,7 @@ dependencies = [ [[package]] name = "mega-evm" -version = "1.4.0" +version = "1.5.0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3138,7 +3138,7 @@ dependencies = [ "delegate", "derive_more", "hex", - "mega-evm 1.4.0", + "mega-evm 1.5.0", "mega-system-contracts", "once_cell", "op-alloy-consensus", @@ -3156,7 +3156,7 @@ dependencies = [ [[package]] name = "mega-evme" -version = "1.4.0" +version = "1.5.0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3170,7 +3170,7 @@ dependencies = [ "alloy-transport-http", "clap", "mega-evm 1.0.1", - "mega-evm 1.4.0", + "mega-evm 1.5.0", "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", @@ -3187,7 +3187,7 @@ dependencies = [ [[package]] name = "mega-system-contracts" -version = "1.4.0" +version = "1.5.0" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -3468,8 +3468,7 @@ dependencies = [ [[package]] name = "op-revm" version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce1dc7533f4e5716c55cd3d62488c6200cb4dfda96e0c75a7e484652464343b" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "auto_impl", "once_cell", @@ -4163,8 +4162,7 @@ dependencies = [ [[package]] name = "revm" version = "27.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6bf82101a1ad8a2b637363a37aef27f88b4efc8a6e24c72bf5f64923dc5532" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "revm-bytecode", "revm-context", @@ -4181,11 +4179,11 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d800e6c2119457ded5b0af71634eb2468040bf97de468eee5a730272a106da0" +version = "6.1.0" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "bitvec", + "once_cell", "phf", "revm-primitives", "serde", @@ -4194,8 +4192,7 @@ dependencies = [ [[package]] name = "revm-context" version = "8.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd508416a35a4d8a9feaf5ccd06ac6d6661cd31ee2dc0252f9f7316455d71f9" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "cfg-if", "derive-where", @@ -4210,8 +4207,7 @@ dependencies = [ [[package]] name = "revm-context-interface" version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90302642d21c8f93e0876e201f3c5f7913c4fcb66fb465b0fd7b707dfe1c79" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -4225,9 +4221,8 @@ dependencies = [ [[package]] name = "revm-database" -version = "7.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40000c7d917c865f6c232a78581b78e70c43f52db17282bd1b52d4f0565bc8a2" +version = "7.1.0" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "alloy-eips", "alloy-provider", @@ -4242,9 +4237,8 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "7.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ccea7a168cba1196b1e57dd3e22c36047208c135f600f8e58cbe7d49957dba" +version = "7.1.0" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "auto_impl", "either", @@ -4257,8 +4251,7 @@ dependencies = [ [[package]] name = "revm-handler" version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1529c8050e663be64010e80ec92bf480315d21b1f2dbf65540028653a621b27d" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "auto_impl", "derive-where", @@ -4276,8 +4269,7 @@ dependencies = [ [[package]] name = "revm-inspector" version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78db140e332489094ef314eaeb0bd1849d6d01172c113ab0eb6ea8ab9372926" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "auto_impl", "either", @@ -4312,8 +4304,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "24.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff9d7d9d71e8a33740b277b602165b6e3d25fff091ba3d7b5a8d373bf55f28a7" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -4324,8 +4315,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "25.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cee3f336b83621294b4cfe84d817e3eef6f3d0fce00951973364cc7f860424d" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "ark-bls12-381 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "ark-bn254", @@ -4351,21 +4341,18 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "20.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa29d9da06fe03b249b6419b33968ecdf92ad6428e2f012dc57bcd619b5d94e" +version = "20.1.0" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "alloy-primitives", "num_enum", - "once_cell", "serde", ] [[package]] name = "revm-state" -version = "7.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d7f39ea56df3bfbb3c81c99b1f028d26f205b6004156baffbf1a4f84b46cfa" +version = "7.0.2" +source = "git+https://github.com/megaeth-labs/revm.git?rev=975fba4c85ea37e4e3b58d7cef60be8ace5cc22f#975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" dependencies = [ "bitflags", "revm-bytecode", @@ -5035,7 +5022,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "state-test" -version = "1.4.0" +version = "1.5.0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -5045,7 +5032,7 @@ dependencies = [ "hash-db", "indicatif", "k256", - "mega-evm 1.4.0", + "mega-evm 1.5.0", "plain_hasher", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 4f27b9d..ede3510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [] # https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html resolver = "2" [workspace.package] -version = "1.4.0" +version = "1.5.0" edition = "2021" rust-version = "1.86" license = "MIT OR Apache-2.0" @@ -177,6 +177,12 @@ tracing-subscriber = { version = "0.3", default-features = false } triehash = { version = "0.8", default-features = false } walkdir = { version = "2.5", default-features = false } +[patch.crates-io] +op-revm = { git = "https://github.com/megaeth-labs/revm.git", rev = "975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" } +revm = { git = "https://github.com/megaeth-labs/revm.git", rev = "975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" } +revm-database-interface = { git = "https://github.com/megaeth-labs/revm.git", rev = "975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" } +revm-database = { git = "https://github.com/megaeth-labs/revm.git", rev = "975fba4c85ea37e4e3b58d7cef60be8ace5cc22f" } + # Speed up compilation time for dev builds by reducing emitted debug info. # NOTE: Debuggers may provide less useful information with this setting. # Uncomment this section if you're using a debugger. diff --git a/bin/mega-evme/src/common/env.rs b/bin/mega-evme/src/common/env.rs index ce81a83..e59affa 100644 --- a/bin/mega-evme/src/common/env.rs +++ b/bin/mega-evme/src/common/env.rs @@ -10,7 +10,7 @@ use mega_evm::{ context::{block::BlockEnv, cfg::CfgEnv}, primitives::eip4844, }, - MegaContext, MegaSpecId, TestExternalEnvs, + BucketId, MegaContext, MegaSpecId, TestExternalEnvs, }; use tracing::{debug, trace}; @@ -127,18 +127,19 @@ pub struct ExtEnvArgs { } impl ExtEnvArgs { - /// Creates [`TestExternalEnvs`]. - pub fn create_external_envs(&self) -> Result { - let mut external_envs = TestExternalEnvs::new(); + /// Creates [`TestExternalEnvs`] and returns bucket capacity configuration. + pub fn create_external_envs(&self) -> Result<(TestExternalEnvs, Vec<(BucketId, u64)>)> { + let external_envs = TestExternalEnvs::new(); - // Parse and configure bucket capacities + // Parse bucket capacities + let mut bucket_capacities = Vec::new(); for bucket_capacity_str in &self.bucket_capacity { let (bucket_id, capacity) = parse_bucket_capacity(bucket_capacity_str)?; - external_envs = external_envs.with_bucket_capacity(bucket_id, capacity); + bucket_capacities.push((bucket_id, capacity)); } - debug!(external_envs = ?external_envs, "Evm TestExternalEnvs created"); + debug!(external_envs = ?external_envs, bucket_capacities = ?bucket_capacities, "Evm TestExternalEnvs created"); - Ok(external_envs) + Ok((external_envs, bucket_capacities)) } } @@ -174,8 +175,8 @@ impl EnvArgs { self.block.create_block_env() } - /// Creates [`TestExternalEnvs`]. - pub fn create_external_envs(&self) -> Result { + /// Creates [`TestExternalEnvs`] and returns bucket capacity configuration. + pub fn create_external_envs(&self) -> Result<(TestExternalEnvs, Vec<(BucketId, u64)>)> { self.ext.create_external_envs() } @@ -186,7 +187,7 @@ impl EnvArgs { ) -> Result> { let cfg = self.create_cfg_env()?; let block = self.create_block_env()?; - let external_envs = self.create_external_envs()?; + let (external_envs, _bucket_capacities) = self.create_external_envs()?; Ok(MegaContext::new(db, cfg.spec) .with_cfg(cfg) diff --git a/bin/mega-evme/src/replay/cmd.rs b/bin/mega-evme/src/replay/cmd.rs index 89ba12a..a90c6ae 100644 --- a/bin/mega-evme/src/replay/cmd.rs +++ b/bin/mega-evme/src/replay/cmd.rs @@ -226,7 +226,7 @@ impl Cmd { let transaction_index = preceding_transactions.len() as u64; debug!(transaction_index, "Setting up block executor"); - let external_env_factory = self.ext_args.create_external_envs()?; + let (external_env_factory, _bucket_capacities) = self.ext_args.create_external_envs()?; let evm_factory = MegaEvmFactory::new().with_external_env_factory(external_env_factory); let block_executor_factory = MegaBlockExecutorFactory::new( &hardforks, diff --git a/crates/mega-evm/src/constants.rs b/crates/mega-evm/src/constants.rs index a06d361..be5452f 100644 --- a/crates/mega-evm/src/constants.rs +++ b/crates/mega-evm/src/constants.rs @@ -2,6 +2,18 @@ //! //! It groups the constants for different EVM specs as sub-modules. +// SALT bucket constants + +/// Number of bits to represent the minimum bucket size (8 bits = 256 slots). +pub const MIN_BUCKET_SIZE_BITS: usize = 8; + +/// Minimum capacity of a SALT bucket in number of slots (256). +/// +/// Buckets hold accounts or storage slots and can grow beyond this size. The gas cost +/// multiplier is calculated as `capacity / MIN_BUCKET_SIZE`, so a bucket at minimum +/// capacity has a 1x multiplier. +pub const MIN_BUCKET_SIZE: usize = 1 << MIN_BUCKET_SIZE_BITS; + /// Constants for the `EQUIVALENCE` spec. pub mod equivalence { use revm::interpreter::gas; diff --git a/crates/mega-evm/src/evm/context.rs b/crates/mega-evm/src/evm/context.rs index 060059e..973f1e0 100644 --- a/crates/mega-evm/src/evm/context.rs +++ b/crates/mega-evm/src/evm/context.rs @@ -54,7 +54,7 @@ pub struct MegaContext { pub additional_limit: Rc>, /// Calculator for dynamic gas costs during transaction execution. - pub dynamic_storage_gas_cost: Rc>>, + pub dynamic_storage_gas_cost: Rc>, /// The oracle environment. pub oracle_env: Rc>, @@ -109,7 +109,6 @@ impl MegaContext { additional_limit: Rc::new(RefCell::new(AdditionalLimit::new(spec, tx_limits))), dynamic_storage_gas_cost: Rc::new(RefCell::new(DynamicGasCost::new( spec, - EmptyExternalEnv, inner.block.number.to::().saturating_sub(1), ))), oracle_env: Rc::new(RefCell::new(EmptyExternalEnv)), @@ -169,7 +168,6 @@ impl MegaContext { additional_limit: Rc::new(RefCell::new(AdditionalLimit::new(spec, tx_limits))), dynamic_storage_gas_cost: Rc::new(RefCell::new(DynamicGasCost::new( spec, - external_envs.salt_env, inner.block.number.to::() - 1, ))), oracle_env: Rc::new(RefCell::new(external_envs.oracle_env)), @@ -274,10 +272,9 @@ impl MegaContext { /// Sets the external environments for the EVM. /// - /// This method updates the external environments used for gas cost calculations, - /// including the salt environment and oracle environment. When setting new - /// external environments, the dynamic gas cost calculator and oracle environment - /// are reinitialized with the new configurations. + /// This method updates the external environments used for oracle environment. + /// When setting new external environments, the oracle environment is reinitialized + /// with the new configuration. /// /// # Arguments /// @@ -290,18 +287,12 @@ impl MegaContext { self, external_envs: ExternalEnvs, ) -> MegaContext { - let parent_block_number = self.inner.block.number.to::().saturating_sub(1); - let spec = self.spec; MegaContext { inner: self.inner, - spec, + spec: self.spec, disable_beneficiary: self.disable_beneficiary, additional_limit: self.additional_limit, - dynamic_storage_gas_cost: Rc::new(RefCell::new(DynamicGasCost::new( - spec, - external_envs.salt_env, - parent_block_number, - ))), + dynamic_storage_gas_cost: self.dynamic_storage_gas_cost, oracle_env: Rc::new(RefCell::new(external_envs.oracle_env)), volatile_data_tracker: self.volatile_data_tracker, disable_sandbox: self.disable_sandbox, diff --git a/crates/mega-evm/src/evm/factory.rs b/crates/mega-evm/src/evm/factory.rs index eb40100..6b328eb 100644 --- a/crates/mega-evm/src/evm/factory.rs +++ b/crates/mega-evm/src/evm/factory.rs @@ -141,10 +141,9 @@ impl alloy_evm::EvmFactory evm_env: EvmEnv, ) -> Self::Evm { let spec_id = *evm_env.spec_id(); - let block_number = evm_env.block_env.number.to(); let runtime_limits = EvmTxRuntimeLimits::from_spec(spec_id); let ctx = MegaContext::new(db, spec_id) - .with_external_envs(self.external_env_factory.external_envs(block_number)) + .with_external_envs(self.external_env_factory.external_envs()) .with_tx(MegaTransaction::default()) .with_block(evm_env.block_env) .with_cfg(evm_env.cfg_env) diff --git a/crates/mega-evm/src/evm/host.rs b/crates/mega-evm/src/evm/host.rs index c5939d6..c35673b 100644 --- a/crates/mega-evm/src/evm/host.rs +++ b/crates/mega-evm/src/evm/host.rs @@ -222,7 +222,8 @@ impl HostExt for MegaContext Option { debug_assert!(self.spec.is_enabled(MegaSpecId::MINI_REX)); - let result = self.dynamic_storage_gas_cost.borrow_mut().sstore_set_gas(address, key); + let result = + self.dynamic_storage_gas_cost.borrow_mut().sstore_set_gas(self.db(), address, key); result .map_err(|e| { *self.error() = Err(ContextError::Custom(format!("{e}"))); @@ -233,7 +234,7 @@ impl HostExt for MegaContext Option { debug_assert!(self.spec.is_enabled(MegaSpecId::MINI_REX)); - let result = self.dynamic_storage_gas_cost.borrow_mut().new_account_gas(address); + let result = self.dynamic_storage_gas_cost.borrow_mut().new_account_gas(self.db(), address); result .map_err(|e| { *self.error() = Err(ContextError::Custom(format!("{e}"))); @@ -244,7 +245,8 @@ impl HostExt for MegaContext Option { debug_assert!(self.spec.is_enabled(MegaSpecId::REX)); - let result = self.dynamic_storage_gas_cost.borrow_mut().create_contract_gas(address); + let result = + self.dynamic_storage_gas_cost.borrow_mut().create_contract_gas(self.db(), address); result .map_err(|e| { *self.error() = Err(ContextError::Custom(format!("{e}"))); diff --git a/crates/mega-evm/src/external/factory.rs b/crates/mega-evm/src/external/factory.rs index 3a1062d..d83cb49 100644 --- a/crates/mega-evm/src/external/factory.rs +++ b/crates/mega-evm/src/external/factory.rs @@ -1,41 +1,34 @@ -use alloy_primitives::BlockNumber; use auto_impl::auto_impl; use crate::{ExternalEnvTypes, ExternalEnvs}; -/// Factory for creating block-specific external environment instances. +/// Factory for creating external environment instances. /// -/// This trait is responsible for instantiating external oracles at a specific block height, +/// This trait is responsible for instantiating external oracles, /// ensuring all oracle queries during EVM execution operate on a consistent snapshot of state. /// /// # Design Pattern /// -/// External environments (SALT and Oracle) do not take block parameters in their methods. -/// Instead, the factory creates block-aware instances that encapsulate the block context, +/// External environments (Oracle) do not take block parameters in their methods. +/// Instead, the factory creates instances that encapsulate the necessary context, /// allowing implementations to: -/// - Read state from the appropriate block height -/// - Cache block-specific data for the execution lifetime +/// - Read state from the appropriate source +/// - Cache data for the execution lifetime /// - Ensure deterministic behavior across repeated executions /// /// # Usage /// -/// This factory is typically called once per block when initializing the EVM. The returned -/// [`ExternalEnvs`] are then used throughout transaction execution within that block. +/// This factory is typically called when initializing the EVM. The returned +/// [`ExternalEnvs`] are then used throughout transaction execution. #[auto_impl(&, Box, Arc)] pub trait ExternalEnvFactory { - /// The concrete types for SALT and Oracle environments this factory produces. + /// The concrete types for Oracle environments this factory produces. type EnvTypes: ExternalEnvTypes; - /// Creates external environment instances for executing EVM operations at the specified block. - /// - /// # Arguments - /// - /// * `block` - The block number at which EVM execution will occur. This is the block height - /// specified in [`BlockEnv`](revm::context::BlockEnv), and oracle queries should read state - /// from the parent block (block - 1). + /// Creates external environment instances for executing EVM operations. /// /// # Returns /// - /// A container with SALT and Oracle environment instances configured for the given block. - fn external_envs(&self, block: BlockNumber) -> ExternalEnvs; + /// A container with Oracle environment instances. + fn external_envs(&self) -> ExternalEnvs; } diff --git a/crates/mega-evm/src/external/gas.rs b/crates/mega-evm/src/external/gas.rs index 8bfadfc..8cd804f 100644 --- a/crates/mega-evm/src/external/gas.rs +++ b/crates/mega-evm/src/external/gas.rs @@ -8,26 +8,24 @@ use revm::primitives::hash_map::Entry; use alloy_primitives::{Address, BlockNumber, U256}; use revm::{context::BlockEnv, primitives::HashMap}; -use crate::{constants, BucketId, MegaSpecId, SaltEnv, MIN_BUCKET_SIZE}; +use crate::{constants, BucketId, MegaSpecId, MIN_BUCKET_SIZE}; /// Calculator for dynamic gas costs based on bucket capacity. #[derive(Debug)] -pub struct DynamicGasCost { +pub struct DynamicGasCost { /// The spec id. spec: MegaSpecId, /// The parent block number. parent_block: BlockNumber, - /// The external environment for SALT bucket information. - salt_env: SaltEnvImpl, /// Cache of the bucket cost multiplier for each bucket Id. The multiplier will be used to /// multiply [`SSTORE_SET_GAS`] to get the actual gas cost for setting a storage slot. bucket_cost_mulitipers: HashMap, } -impl DynamicGasCost { +impl DynamicGasCost { /// Creates a new [`DynamicGasCost`]. - pub fn new(spec: MegaSpecId, salt_env: SaltEnvImpl, parent_block: BlockNumber) -> Self { - Self { spec, parent_block, salt_env, bucket_cost_mulitipers: HashMap::default() } + pub fn new(spec: MegaSpecId, parent_block: BlockNumber) -> Self { + Self { spec, parent_block, bucket_cost_mulitipers: HashMap::default() } } /// Resets the cache of the bucket cost multiplier. @@ -43,14 +41,15 @@ impl DynamicGasCost { /// Calculates the gas cost for setting a storage slot to a non-zero value. This overrides the /// [`SSTORE_SET`](revm::interpreter::gas::SSTORE_SET) gas cost in the original EVM. - pub fn sstore_set_gas( + pub fn sstore_set_gas( &mut self, + db: &DB, address: Address, key: U256, - ) -> Result { + ) -> Result { // increase the gas cost according to the bucket capacity - let bucket_id = SaltEnvImpl::bucket_id_for_slot(address, key); - let multiplier = self.load_bucket_cost_multiplier(bucket_id)?; + let (bucket_id, capacity) = db.salt_bucket_capacity(address, Some(key))?; + let multiplier = self.load_bucket_cost_multiplier(bucket_id as BucketId, capacity)?; let gas = if self.spec.is_enabled(MegaSpecId::REX) { constants::rex::SSTORE_SET_STORAGE_GAS_BASE * (multiplier - 1) @@ -63,10 +62,14 @@ impl DynamicGasCost { /// Calculates the gas cost for creating a new account. This overrides the /// [`NEWACCOUNT`](revm::interpreter::gas::NEWACCOUNT) gas cost in the original EVM. - pub fn new_account_gas(&mut self, address: Address) -> Result { + pub fn new_account_gas( + &mut self, + db: &DB, + address: Address, + ) -> Result { // increase the gas cost according to the bucket capacity - let bucket_id = SaltEnvImpl::bucket_id_for_account(address); - let multiplier = self.load_bucket_cost_multiplier(bucket_id)?; + let (bucket_id, capacity) = db.salt_bucket_capacity(address, None)?; + let multiplier = self.load_bucket_cost_multiplier(bucket_id as BucketId, capacity)?; let gas = if self.spec.is_enabled(MegaSpecId::REX) { constants::rex::NEW_ACCOUNT_STORAGE_GAS_BASE * (multiplier - 1) @@ -79,10 +82,14 @@ impl DynamicGasCost { /// Calculates the gas cost for creating a new contract. This overrides the /// [`CREATE`](revm::interpreter::gas::CREATE) gas cost in the original EVM. - pub fn create_contract_gas(&mut self, address: Address) -> Result { + pub fn create_contract_gas( + &mut self, + db: &DB, + address: Address, + ) -> Result { // increase the gas cost according to the bucket capacity - let bucket_id = SaltEnvImpl::bucket_id_for_account(address); - let multiplier = self.load_bucket_cost_multiplier(bucket_id)?; + let (bucket_id, capacity) = db.salt_bucket_capacity(address, None)?; + let multiplier = self.load_bucket_cost_multiplier(bucket_id as BucketId, capacity)?; let gas = if self.spec.is_enabled(MegaSpecId::REX) { constants::rex::CONTRACT_CREATION_STORAGE_GAS_BASE * (multiplier - 1) @@ -94,14 +101,14 @@ impl DynamicGasCost { } /// Loads the bucket cost multiplier for a given bucket Id. - fn load_bucket_cost_multiplier( + fn load_bucket_cost_multiplier( &mut self, bucket_id: BucketId, - ) -> Result { + capacity: u64, + ) -> Result { match self.bucket_cost_mulitipers.entry(bucket_id) { Entry::Occupied(occupied_entry) => Ok(*occupied_entry.get()), Entry::Vacant(vacant_entry) => { - let capacity = self.salt_env.get_bucket_capacity(bucket_id)?; let multiplier = capacity / MIN_BUCKET_SIZE as u64; vacant_entry.insert(multiplier); Ok(multiplier) @@ -110,7 +117,7 @@ impl DynamicGasCost { } } -impl DynamicGasCost { +impl DynamicGasCost { pub(crate) fn on_new_block(&mut self, block: &BlockEnv) { self.reset(block.number.to::().saturating_sub(1)); } diff --git a/crates/mega-evm/src/external/mod.rs b/crates/mega-evm/src/external/mod.rs index d3425a8..ec856e0 100644 --- a/crates/mega-evm/src/external/mod.rs +++ b/crates/mega-evm/src/external/mod.rs @@ -1,92 +1,72 @@ //! External environment for EVM execution. //! //! This module provides interfaces for accessing external data sources during EVM execution: -//! - **SALT**: Bucket capacity information for dynamic gas pricing //! - **Oracle**: Storage from the `MegaETH` oracle contract //! //! # Architecture //! //! External environments follow a factory pattern: -//! 1. [`ExternalEnvFactory`] creates block-specific environment instances -//! 2. [`ExternalEnvs`] bundles SALT and Oracle implementations -//! 3. Individual oracle methods (e.g., [`SaltEnv::get_bucket_capacity`]) provide data -//! -//! Block context is established at factory creation time, not per oracle call, ensuring -//! consistent state snapshots throughout execution. +//! 1. [`ExternalEnvFactory`] creates environment instances +//! 2. [`ExternalEnvs`] bundles Oracle implementations +//! 3. Individual oracle methods (e.g., [`OracleEnv::get_oracle_storage`]) provide data -use alloy_primitives::BlockNumber; use auto_impl::auto_impl; use core::fmt::Debug; mod factory; mod gas; mod oracle; -mod salt; #[cfg(any(test, feature = "test-utils"))] mod test_utils; pub use factory::*; pub use gas::*; pub use oracle::*; -pub use salt::*; #[cfg(any(test, feature = "test-utils"))] pub use test_utils::*; /// Type-level specification of external environment implementations. /// -/// This trait associates concrete SALT and Oracle types, allowing generic code to work +/// This trait associates concrete Oracle types, allowing generic code to work /// with any compatible environment configuration. #[auto_impl(&, Box, Arc)] pub trait ExternalEnvTypes { - /// SALT environment implementation for bucket capacity queries. - type SaltEnv: SaltEnv; /// Oracle environment implementation for system contract storage queries. type OracleEnv: OracleEnv; } -/// Tuple implementation for convenient pairing of SALT and Oracle environments. -impl ExternalEnvTypes for (A, B) { - type SaltEnv = A; - type OracleEnv = B; -} - /// Bundle of external environment instances for a specific execution context. /// -/// This struct holds concrete SALT and Oracle implementations that are used during +/// This struct holds concrete Oracle implementations that are used during /// EVM execution. Typically created by [`ExternalEnvFactory::external_envs`] at the /// start of block processing. #[derive(Debug, Clone)] pub struct ExternalEnvs { - /// SALT environment for bucket capacity queries and dynamic gas calculation. - pub salt_env: T::SaltEnv, /// Oracle environment for reading system contract storage. pub oracle_env: T::OracleEnv, } impl Default for ExternalEnvs { fn default() -> Self { - Self { salt_env: EmptyExternalEnv, oracle_env: EmptyExternalEnv } + Self { oracle_env: EmptyExternalEnv } } } /// No-op external environment for testing or when oracle functionality is disabled. /// /// This implementation: -/// - Returns minimum bucket capacity for all SALT queries /// - Returns `None` for all Oracle storage queries -/// - Assigns all accounts and storage slots to bucket 0 #[derive(Debug, Default, Clone, Copy)] pub struct EmptyExternalEnv; impl ExternalEnvTypes for EmptyExternalEnv { - type SaltEnv = Self; type OracleEnv = Self; } impl ExternalEnvFactory for EmptyExternalEnv { type EnvTypes = Self; - fn external_envs(&self, _block: BlockNumber) -> ExternalEnvs { - ExternalEnvs { salt_env: *self, oracle_env: *self } + fn external_envs(&self) -> ExternalEnvs { + ExternalEnvs { oracle_env: *self } } } diff --git a/crates/mega-evm/src/external/salt.rs b/crates/mega-evm/src/external/salt.rs deleted file mode 100644 index 10b52ef..0000000 --- a/crates/mega-evm/src/external/salt.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! This module defines the `SaltEnv` trait, which provides bucket capacity information for dynamic -//! gas pricing. Storage slots and accounts are organized into buckets, and the gas cost scales -//! with bucket capacity to incentivize efficient resource allocation. - -use core::{ - convert::Infallible, - fmt::{Debug, Display}, -}; - -use alloy_primitives::{Address, U256}; -use auto_impl::auto_impl; - -use crate::EmptyExternalEnv; - -/// SALT bucket identifier. Accounts and storage slots are mapped to buckets, which have -/// dynamic capacities that affect gas costs. -pub type BucketId = u32; - -/// Number of bits to represent the minimum bucket size (8 bits = 256 slots). -pub const MIN_BUCKET_SIZE_BITS: usize = 8; - -/// Minimum capacity of a SALT bucket in number of slots (256). -/// -/// Buckets hold accounts or storage slots and can grow beyond this size. The gas cost -/// multiplier is calculated as `capacity / MIN_BUCKET_SIZE`, so a bucket at minimum -/// capacity has a 1x multiplier. -pub const MIN_BUCKET_SIZE: usize = 1 << MIN_BUCKET_SIZE_BITS; - -/// Interface for SALT bucket capacity information. -/// -/// This trait provides bucket capacity data needed for dynamic gas pricing. Implementations -/// typically read from the underlying blockchain database to ensure deterministic execution. -/// -/// # Block-Awareness -/// -/// This trait does not take a block parameter. Block context is provided when the environment -/// is created via [`ExternalEnvFactory::external_envs`](crate::ExternalEnvFactory::external_envs), -/// allowing implementations to snapshot state at a specific block. -/// -/// # Bucket ID Calculation -/// -/// The trait provides default methods [`bucket_id_for_account`](SaltEnv::bucket_id_for_account) -/// and [`bucket_id_for_slot`](SaltEnv::bucket_id_for_slot) that can be overridden by -/// implementations to customize bucket assignment logic. -#[auto_impl(&, Box, Arc)] -pub trait SaltEnv: Debug + Unpin { - /// Error type returned when bucket capacity cannot be retrieved. - type Error: Display; - - /// Returns the current capacity of the specified bucket. - /// - /// # Gas Cost Calculation - /// - /// The returned capacity is used to calculate a gas multiplier: - /// ```text - /// multiplier = capacity / MIN_BUCKET_SIZE - /// ``` - /// This multiplier scales the base storage gas costs, making operations more expensive - /// as buckets grow. - /// - /// # Arguments - /// - /// * `bucket_id` - The bucket to query - /// - /// # Returns - /// - /// The bucket's capacity in number of slots, or an error if unavailable. - fn get_bucket_capacity(&self, bucket_id: BucketId) -> Result; - - /// Maps an account address to its bucket ID. - /// - /// This method determines which bucket tracks the account creation gas costs. - /// The default implementation can be overridden to customize bucket assignment. - /// - /// # Arguments - /// - /// * `account` - The account address to map - fn bucket_id_for_account(account: Address) -> BucketId; - - /// Maps a storage slot to its bucket ID. - /// - /// This method determines which bucket tracks the storage slot's gas costs. - /// The default implementation can be overridden to customize bucket assignment. - /// - /// # Arguments - /// - /// * `address` - The contract address owning the storage - /// * `key` - The storage slot key - fn bucket_id_for_slot(address: Address, key: U256) -> BucketId; -} - -/// No-op implementation that returns minimum bucket size for all buckets. -/// -/// This implementation assigns all accounts and storage slots to bucket 0 with minimum -/// capacity, effectively disabling dynamic gas pricing. Useful for testing or when SALT -/// functionality is not needed. -impl SaltEnv for EmptyExternalEnv { - type Error = Infallible; - - fn get_bucket_capacity(&self, _bucket_id: BucketId) -> Result { - Ok(MIN_BUCKET_SIZE as u64) - } - - fn bucket_id_for_account(_account: Address) -> BucketId { - 0 as BucketId - } - - fn bucket_id_for_slot(_address: Address, _key: U256) -> BucketId { - 0 as BucketId - } -} diff --git a/crates/mega-evm/src/external/test_utils.rs b/crates/mega-evm/src/external/test_utils.rs index 834f3ec..011ea58 100644 --- a/crates/mega-evm/src/external/test_utils.rs +++ b/crates/mega-evm/src/external/test_utils.rs @@ -1,18 +1,18 @@ //! Test utilities for external environment implementations. //! -//! Provides [`TestExternalEnvs`], a configurable mock implementation of SALT and Oracle -//! environments for use in tests. Unlike [`EmptyExternalEnv`](crate::EmptyExternalEnv), -//! this implementation allows setting specific bucket capacities and oracle storage values. +//! Provides [`TestExternalEnvs`], a configurable mock implementation of Oracle +//! environments for use in tests, and [`TestDatabaseWrapper`], a database wrapper +//! that implements `salt_bucket_capacity` for testing. #[cfg(not(feature = "std"))] use alloc as std; use core::{cell::RefCell, convert::Infallible, fmt::Display}; use std::{rc::Rc, vec::Vec}; -use alloy_primitives::{Address, BlockNumber, Bytes, B256, U256}; +use alloy_primitives::{Address, Bytes, B256, U256}; use revm::primitives::HashMap; -use crate::{BucketId, ExternalEnvFactory, ExternalEnvTypes, ExternalEnvs, OracleEnv, SaltEnv}; +use crate::{BucketId, ExternalEnvFactory, ExternalEnvTypes, ExternalEnvs, OracleEnv}; /// A recorded oracle hint from `on_hint` calls. #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,14 +27,12 @@ pub struct RecordedHint { /// Configurable external environment implementation for testing. /// -/// This struct provides mutable state for bucket capacities, oracle storage, and recorded hints, -/// allowing tests to set up specific scenarios and verify hint mechanism behavior. Bucket IDs are -/// calculated using the real SALT hashing logic from the `salt` crate. +/// This struct provides mutable state for oracle storage and recorded hints, +/// allowing tests to set up specific scenarios and verify hint mechanism behavior. /// /// # Example /// ```ignore /// let env = TestExternalEnvs::new() -/// .with_bucket_capacity(123, 512) // Set bucket 123 to 512 capacity /// .with_oracle_storage(U256::ZERO, U256::from(42)); // Set oracle slot 0 to 42 /// ``` #[derive(derive_more::Debug, Clone)] @@ -43,9 +41,6 @@ pub struct TestExternalEnvs { _phantom: core::marker::PhantomData, /// Oracle contract storage values. Maps storage slot keys to their values. oracle_storage: Rc>>, - /// Bucket capacities. Maps bucket IDs to their capacity values. - /// Buckets not in this map default to [`MIN_BUCKET_SIZE`](crate::MIN_BUCKET_SIZE). - bucket_capacity: Rc>>, /// Recorded hints from `on_hint` calls. Used for testing the hint mechanism. recorded_hints: Rc>>, } @@ -58,23 +53,22 @@ impl Default for TestExternalEnvs { impl From for ExternalEnvs { fn from(value: TestExternalEnvs) -> Self { - Self { salt_env: value.clone(), oracle_env: value } + Self { oracle_env: value } } } impl<'a> From<&'a TestExternalEnvs> for ExternalEnvs<&'a TestExternalEnvs> { fn from(value: &'a TestExternalEnvs) -> Self { - ExternalEnvs { salt_env: value.clone(), oracle_env: value.clone() } + ExternalEnvs { oracle_env: value.clone() } } } impl TestExternalEnvs { - /// Creates a new test environment with empty bucket capacity and oracle storage. + /// Creates a new test environment with empty oracle storage. pub fn new() -> Self { Self { _phantom: core::marker::PhantomData, oracle_storage: Rc::new(RefCell::new(HashMap::default())), - bucket_capacity: Rc::new(RefCell::new(HashMap::default())), recorded_hints: Rc::new(RefCell::new(Vec::new())), } } @@ -91,31 +85,6 @@ impl TestExternalEnvs { self.recorded_hints.borrow_mut().clear(); } - /// Configures a bucket to have a specific capacity. - /// - /// This affects the gas multiplier calculation for operations on accounts or storage - /// slots mapped to this bucket. The multiplier will be `capacity / MIN_BUCKET_SIZE`. - /// - /// # Arguments - /// - /// * `bucket_id` - The bucket ID to configure - /// * `capacity` - The bucket capacity in number of slots - /// - /// # Returns - /// - /// `self` for method chaining - pub fn with_bucket_capacity(self, bucket_id: BucketId, capacity: u64) -> Self { - self.bucket_capacity.borrow_mut().insert(bucket_id, capacity); - self - } - - /// Removes all configured bucket capacities. - /// - /// After calling this, all buckets will return the default minimum capacity. - pub fn clear_bucket_capacity(&self) { - self.bucket_capacity.borrow_mut().clear(); - } - /// Configures a storage slot in the oracle contract to have a specific value. /// /// # Arguments @@ -142,17 +111,25 @@ impl TestExternalEnvs { impl ExternalEnvFactory for TestExternalEnvs { type EnvTypes = Self; - fn external_envs(&self, _block: BlockNumber) -> ExternalEnvs { - ExternalEnvs { salt_env: self.clone(), oracle_env: self.clone() } + fn external_envs(&self) -> ExternalEnvs { + ExternalEnvs { oracle_env: self.clone() } } } impl ExternalEnvTypes for TestExternalEnvs { - type SaltEnv = Self; - type OracleEnv = Self; } +impl OracleEnv for TestExternalEnvs { + fn get_oracle_storage(&self, slot: U256) -> Option { + self.oracle_storage.borrow().get(&slot).copied() + } + + fn on_hint(&self, from: Address, topic: B256, data: Bytes) { + self.recorded_hints.borrow_mut().push(RecordedHint { from, topic, data }); + } +} + /// Length of a storage slot key in bytes (32 bytes for U256). const SLOT_KEY_LEN: usize = B256::len_bytes(); /// Length of an account address in bytes (20 bytes). @@ -160,41 +137,100 @@ const PLAIN_ACCOUNT_KEY_LEN: usize = Address::len_bytes(); /// Length of a combined address+slot key (52 bytes = 20 + 32). const PLAIN_STORAGE_KEY_LEN: usize = PLAIN_ACCOUNT_KEY_LEN + SLOT_KEY_LEN; -/// SALT environment implementation using real bucket ID hashing. +/// Database wrapper that adds `salt_bucket_capacity` support for testing. /// -/// Bucket IDs are calculated using the SALT hasher from the `salt` crate, which provides -/// deterministic mapping of accounts and storage slots to buckets. -impl SaltEnv for TestExternalEnvs { - type Error = Error; +/// This wrapper delegates all database operations to the inner database, +/// but adds configurable bucket capacity tracking for testing dynamic gas costs. +#[derive(Debug, Clone)] +pub struct TestDatabaseWrapper { + /// The inner database implementation. + pub inner: DB, + /// Bucket capacities. Maps bucket IDs to their capacity values. + /// Buckets not in this map default to [`MIN_BUCKET_SIZE`](crate::MIN_BUCKET_SIZE). + bucket_capacity: Rc>>, +} - fn get_bucket_capacity(&self, bucket_id: BucketId) -> Result { - Ok(self - .bucket_capacity - .borrow() - .get(&bucket_id) - .copied() - .unwrap_or(salt::constant::MIN_BUCKET_SIZE as u64)) +impl TestDatabaseWrapper { + /// Creates a new test database wrapper. + pub fn new(db: DB) -> Self { + Self { inner: db, bucket_capacity: Rc::new(RefCell::new(HashMap::default())) } } - /// Maps accounts to buckets by hashing the address. - fn bucket_id_for_account(account: Address) -> BucketId { + /// Configures a bucket to have a specific capacity. + /// + /// This affects the gas multiplier calculation for operations on accounts or storage + /// slots mapped to this bucket. The multiplier will be `capacity / MIN_BUCKET_SIZE`. + /// + /// # Arguments + /// + /// * `bucket_id` - The bucket ID to configure + /// * `capacity` - The bucket capacity in number of slots + /// + /// # Returns + /// + /// `self` for method chaining + pub fn with_bucket_capacity(self, bucket_id: BucketId, capacity: u64) -> Self { + self.bucket_capacity.borrow_mut().insert(bucket_id, capacity); + self + } + + /// Removes all configured bucket capacities. + /// + /// After calling this, all buckets will return the default minimum capacity. + pub fn clear_bucket_capacity(&self) { + self.bucket_capacity.borrow_mut().clear(); + } + + /// Maps an account address to its bucket ID using SALT hashing. + pub fn bucket_id_for_account(account: Address) -> BucketId { salt::state::hasher::bucket_id(account.as_slice()) } - /// Maps storage slots to buckets by hashing the concatenation of address and slot key. - fn bucket_id_for_slot(address: Address, key: U256) -> BucketId { + /// Maps a storage slot to its bucket ID using SALT hashing. + pub fn bucket_id_for_slot(address: Address, key: U256) -> BucketId { salt::state::hasher::bucket_id( address.concat_const::(key.into()).as_slice(), ) } } -impl OracleEnv for TestExternalEnvs { - fn get_oracle_storage(&self, slot: U256) -> Option { - self.oracle_storage.borrow().get(&slot).copied() +impl revm::Database for TestDatabaseWrapper { + type Error = DB::Error; + + fn basic(&mut self, address: Address) -> Result, Self::Error> { + self.inner.basic(address) } - fn on_hint(&self, from: Address, topic: B256, data: Bytes) { - self.recorded_hints.borrow_mut().push(RecordedHint { from, topic, data }); + fn code_by_hash(&mut self, code_hash: B256) -> Result { + self.inner.code_by_hash(code_hash) + } + + fn storage(&mut self, address: Address, index: U256) -> Result { + self.inner.storage(address, index) + } + + fn block_hash(&mut self, number: u64) -> Result { + self.inner.block_hash(number) + } + + fn salt_bucket_capacity( + &self, + address: Address, + index: Option, + ) -> Result<(usize, u64), Self::Error> { + let bucket_id = if let Some(key) = index { + Self::bucket_id_for_slot(address, key) + } else { + Self::bucket_id_for_account(address) + }; + + let capacity = self + .bucket_capacity + .borrow() + .get(&bucket_id) + .copied() + .unwrap_or(salt::constant::MIN_BUCKET_SIZE as u64); + + Ok((bucket_id as usize, capacity)) } } diff --git a/crates/mega-evm/src/lib.rs b/crates/mega-evm/src/lib.rs index ce129a5..aba23e0 100644 --- a/crates/mega-evm/src/lib.rs +++ b/crates/mega-evm/src/lib.rs @@ -21,6 +21,7 @@ mod types; pub use access::*; pub use block::*; +pub use constants::{MIN_BUCKET_SIZE, MIN_BUCKET_SIZE_BITS}; pub use evm::*; pub use external::*; pub use limit::*; diff --git a/crates/mega-evm/src/sandbox/state.rs b/crates/mega-evm/src/sandbox/state.rs index 1376745..e8b404e 100644 --- a/crates/mega-evm/src/sandbox/state.rs +++ b/crates/mega-evm/src/sandbox/state.rs @@ -12,7 +12,7 @@ use std::{ string::{String, ToString}, }; -use alloy_primitives::{map::HashMap, Address, B256}; +use alloy_primitives::{map::HashMap, Address, B256, U256}; use core::cell::RefCell; use revm::{ database::DBErrorMarker, @@ -64,6 +64,11 @@ trait ErasedDatabase { fn storage(&self, address: Address, index: StorageKey) -> Result; fn block_hash(&self, number: u64) -> Result; fn code_by_hash(&self, code_hash: B256) -> Result; + fn salt_bucket_capacity( + &self, + address: Address, + index: Option, + ) -> Result<(usize, u64), SandboxDbError>; } /// Wrapper that implements [`ErasedDatabase`] for any [`Database`] type. @@ -96,6 +101,18 @@ where fn code_by_hash(&self, code_hash: B256) -> Result { self.db.borrow_mut().code_by_hash(code_hash).map_err(|e| SandboxDbError(e.to_string())) } + + #[inline] + fn salt_bucket_capacity( + &self, + address: Address, + index: Option, + ) -> Result<(usize, u64), SandboxDbError> { + self.db + .borrow() + .salt_bucket_capacity(address, index) + .map_err(|e| SandboxDbError(e.to_string())) + } } /// A sandbox database for isolated EVM execution. @@ -235,6 +252,15 @@ impl Database for SandboxDb<'_> { // Block hashes are not stored in journal state - query database self.db.block_hash(number) } + + fn salt_bucket_capacity( + &self, + address: Address, + index: Option, + ) -> Result<(usize, u64), Self::Error> { + // Delegate to underlying database + self.db.salt_bucket_capacity(address, index) + } } #[cfg(test)] diff --git a/crates/mega-evm/src/test_utils/database.rs b/crates/mega-evm/src/test_utils/database.rs index f75fad8..9331cce 100644 --- a/crates/mega-evm/src/test_utils/database.rs +++ b/crates/mega-evm/src/test_utils/database.rs @@ -8,6 +8,8 @@ use revm::{ state::{Account, AccountInfo, Bytecode}, }; +use crate::constants::MIN_BUCKET_SIZE; + /// A memory database for testing purposes. #[derive(Debug, Default, Clone, derive_more::Deref, derive_more::DerefMut)] pub struct MemoryDatabase { @@ -99,6 +101,15 @@ impl revm::Database for MemoryDatabase { fn block_hash(&mut self, number: u64) -> Result; } } + + fn salt_bucket_capacity( + &self, + _address: Address, + _index: Option, + ) -> Result<(usize, u64), Self::Error> { + // For testing, return minimum bucket size (multiplier = 1) + Ok((0, MIN_BUCKET_SIZE as u64)) + } } impl revm::DatabaseCommit for MemoryDatabase { @@ -167,6 +178,14 @@ impl revm::Database for ErrorInjectingDatabase { fn block_hash(&mut self, number: u64) -> Result { self.inner.block_hash(number).map_err(|e| match e {}) } + + fn salt_bucket_capacity( + &self, + address: Address, + index: Option, + ) -> Result<(usize, u64), Self::Error> { + self.inner.salt_bucket_capacity(address, index).map_err(|e| match e {}) + } } impl revm::DatabaseCommit for ErrorInjectingDatabase { diff --git a/crates/mega-evm/src/types.rs b/crates/mega-evm/src/types.rs index 2ef9e0c..de9b43f 100644 --- a/crates/mega-evm/src/types.rs +++ b/crates/mega-evm/src/types.rs @@ -2,6 +2,12 @@ use revm::context::TxEnv; +/// SALT bucket identifier. +/// +/// Accounts and storage slots are mapped to buckets, which have dynamic capacities +/// that affect gas costs. +pub type BucketId = u32; + /// `MegaETH` transaction type used in revm. pub type MegaTransaction = op_revm::OpTransaction; /// `MegaETH` transaction builder type used in revm. diff --git a/crates/mega-evm/tests/mini_rex/access_beneficiary_balance.rs b/crates/mega-evm/tests/mini_rex/access_beneficiary_balance.rs index cc3ee88..055aea3 100644 --- a/crates/mega-evm/tests/mini_rex/access_beneficiary_balance.rs +++ b/crates/mega-evm/tests/mini_rex/access_beneficiary_balance.rs @@ -9,6 +9,7 @@ use mega_evm::{ constants::mini_rex::BLOCK_ENV_ACCESS_COMPUTE_GAS, test_utils::{BytecodeBuilder, GasInspector, MsgCallMeta}, EmptyExternalEnv, MegaContext, MegaEvm, MegaHaltReason, MegaSpecId, MegaTransaction, + TestDatabaseWrapper, }; use revm::{ bytecode::opcode::{BALANCE, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE, POP, PUSH0, STOP}, @@ -24,8 +25,9 @@ const CALLER_ADDR: Address = address!("0000000000000000000000000000000000100000" const CONTRACT_ADDR: Address = address!("0000000000000000000000000000000000100001"); const NESTED_CONTRACT: Address = address!("0000000000000000000000000000000000100002"); -fn create_evm() -> MegaEvm, GasInspector, EmptyExternalEnv> { +fn create_evm() -> MegaEvm>, GasInspector, EmptyExternalEnv> { let db = CacheDB::::default(); + let db = TestDatabaseWrapper::new(db); let mut context = MegaContext::new(db, MegaSpecId::MINI_REX); let block_env = @@ -38,15 +40,15 @@ fn create_evm() -> MegaEvm, GasInspector, EmptyExternalEnv> { MegaEvm::new(context).with_inspector(GasInspector::new()) } -fn set_account_code(db: &mut CacheDB, address: Address, code: Bytes) { +fn set_account_code(db: &mut TestDatabaseWrapper>, address: Address, code: Bytes) { let bytecode = Bytecode::new_legacy(code); let code_hash = bytecode.hash_slow(); let account_info = AccountInfo { code: Some(bytecode), code_hash, ..Default::default() }; - db.insert_account_info(address, account_info); + db.inner.insert_account_info(address, account_info); } fn execute_tx( - evm: &mut MegaEvm, GasInspector, EmptyExternalEnv>, + evm: &mut MegaEvm>, GasInspector, EmptyExternalEnv>, caller: Address, to: Option
, value: U256, @@ -75,7 +77,7 @@ fn execute_tx( } fn assert_beneficiary_detection( - evm: &MegaEvm, GasInspector, EmptyExternalEnv>, + evm: &MegaEvm>, GasInspector, EmptyExternalEnv>, result_and_state: &ResultAndState, ) { // Transaction should succeed @@ -117,7 +119,7 @@ fn test_beneficiary_recipient() { let mut evm = create_evm(); // Give caller some balance - evm.ctx().db_mut().insert_account_info( + evm.ctx().db_mut().inner.insert_account_info( CALLER_ADDR, AccountInfo { balance: U256::from(1_000_000_000_000_000_000u64), ..Default::default() }, ); diff --git a/crates/mega-evm/tests/mini_rex/compute_gas_limit.rs b/crates/mega-evm/tests/mini_rex/compute_gas_limit.rs index 7fd2189..5842fd2 100644 --- a/crates/mega-evm/tests/mini_rex/compute_gas_limit.rs +++ b/crates/mega-evm/tests/mini_rex/compute_gas_limit.rs @@ -18,7 +18,6 @@ use revm::{ tx::TxEnvBuilder, TxEnv, }, - database::{CacheDB, EmptyDB}, handler::EvmTr, precompile::{ bn128::pair, @@ -40,9 +39,9 @@ const CONTRACT2: Address = address!("0000000000000000000000000000000000100002"); // ============================================================================ /// Executes a transaction with specified compute gas limit. -fn transact( +fn transact + core::fmt::Debug>( spec: MegaSpecId, - db: &mut CacheDB, + db: &mut DB, compute_gas_limit: u64, tx: TxEnv, ) -> Result<(ResultAndState, u64), EVMError> { diff --git a/crates/mega-evm/tests/mini_rex/gas.rs b/crates/mega-evm/tests/mini_rex/gas.rs index 37bf9ee..083e2da 100644 --- a/crates/mega-evm/tests/mini_rex/gas.rs +++ b/crates/mega-evm/tests/mini_rex/gas.rs @@ -7,7 +7,7 @@ use mega_evm::{ constants::{self, mini_rex::SSTORE_SET_STORAGE_GAS}, test_utils::{BytecodeBuilder, MemoryDatabase}, EVMError, MegaContext, MegaEvm, MegaHaltReason, MegaSpecId, MegaTransaction, - MegaTransactionError, SaltEnv, TestExternalEnvs, + MegaTransactionError, TestDatabaseWrapper, TestExternalEnvs, MIN_BUCKET_SIZE, }; use revm::{ bytecode::opcode::{CALL, CREATE, CREATE2, GAS, LOG0, PUSH0}, @@ -16,16 +16,15 @@ use revm::{ primitives::Address, Inspector, }; -use salt::constant::MIN_BUCKET_SIZE; const CALLER: Address = address!("2000000000000000000000000000000000000002"); const CALLEE: Address = address!("1000000000000000000000000000000000000001"); const NESTED_CALLEE: Address = address!("1000000000000000000000000000000000000002"); /// Executes a transaction on the EVM. -fn transact( +fn transact + core::fmt::Debug>( spec: MegaSpecId, - db: &mut MemoryDatabase, + db: &mut DB, external_envs: &TestExternalEnvs, caller: Address, callee: Option
, @@ -91,10 +90,7 @@ fn sstore_test_case( UpdateMode::Set | UpdateMode::Reset => U256::from(6342), UpdateMode::Clear => U256::from(0), }; - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); - // An external envs with the given bucket capacity - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); // a contract that stores a value to the storage slot let bytecode = BytecodeBuilder::default().sstore(storage_key, storage_value).stop().build(); @@ -103,9 +99,14 @@ fn sstore_test_case( db.set_account_storage(CALLEE, storage_key, U256::from(2333)); } + // An external envs with the given bucket capacity + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); + let res = transact( spec, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -212,9 +213,6 @@ fn test_sstore_cold_then_warm_access() { let mut db = MemoryDatabase::default(); let storage_key = U256::from(0); - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); // Contract that performs two SSTOREs to the same slot: // 1. First SSTORE (cold): should charge SSTORE_SET_GAS + COLD_SLOAD_COST @@ -226,9 +224,14 @@ fn test_sstore_cold_then_warm_access() { .build(); db.set_account_code(CALLEE, bytecode); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); + let external_envs = TestExternalEnvs::new(); + let res = transact( MegaSpecId::MINI_REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -276,11 +279,6 @@ fn ether_transfer_test_case( let mut db = MemoryDatabase::default(); - // Determine the bucket for the callee and set up the external envs with the required capacity. - let bucket_id = TestExternalEnvs::::bucket_id_for_account(CALLEE); - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); - // Allocate initial balance to the caller. db.set_account_balance(CALLER, U256::from(1000)); // If testing transfer to an existing account, allocate balance to the callee as well. @@ -288,10 +286,16 @@ fn ether_transfer_test_case( db.set_account_balance(CALLEE, U256::from(2333)); } + // Determine the bucket for the callee and set up the external envs with the required capacity. + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(CALLEE); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); + // Perform the ether transfer (1 wei) from caller to callee. let res = transact( spec, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -363,12 +367,6 @@ fn nested_ether_transfer_test_case( let mut db = MemoryDatabase::default(); - // Test address and storage slot - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NESTED_CALLEE); - // An external envs with the given bucket capacity - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); - // allocate some balance to callee, which will transfer the ether to the nested callee db.set_account_balance(CALLEE, U256::from(1000)); if matches!(mode, UpdateMode::Reset) { @@ -386,10 +384,17 @@ fn nested_ether_transfer_test_case( .build(); db.set_account_code(CALLEE, bytecode); + // Test address and storage slot + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NESTED_CALLEE); + // An external envs with the given bucket capacity + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); + // transfer 1 wei from caller to callee let res = transact( spec, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -449,13 +454,14 @@ fn test_nested_ether_transfer_gas_unchanged_in_equivalence_spec() { /// # Panics /// Panics if the transaction fails or if the actual gas used does not match `expected_gas_used`. fn create_contract_test_case(spec: MegaSpecId, expansion_times: u64, expected_gas_used: u64) { - let mut db = MemoryDatabase::default(); + let db = MemoryDatabase::default(); // Test address and storage slot let callee = CALLER.create(0); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(callee); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(callee); // An external envs with the given bucket capacity - let external_envs = TestExternalEnvs::new() + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); // constructor code @@ -464,8 +470,9 @@ fn create_contract_test_case(spec: MegaSpecId, expansion_times: u64, expected_ga // println!("calldata_tokens: {:?}", tokens); // create contract - let res = transact(spec, &mut db, &external_envs, CALLER, None, constructor_code, U256::ZERO) - .unwrap(); + let res = + transact(spec, &mut db_wrapper, &external_envs, CALLER, None, constructor_code, U256::ZERO) + .unwrap(); assert!(res.result.is_success()); let gas_used = res.result.gas_used(); assert_eq!(gas_used, expected_gas_used); @@ -528,24 +535,13 @@ fn nested_create_contract_test_case( let constructor_code = BytecodeBuilder::default().return_with_data([0x00]).build(); let constructor_len = constructor_code.len(); - // Test address and storage slot - let nested_callee = if is_create2 { - CALLEE.create2(create2_salt.to_be_bytes(), keccak256(&constructor_code)) - } else { - CALLEE.create(0) - }; - let bucket_id = TestExternalEnvs::::bucket_id_for_account(nested_callee); - // An external envs with the given bucket capacity - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); - // set the code of the calee that transfers ether to the nested callee let mut bytecode = BytecodeBuilder::default(); if is_create2 { bytecode = bytecode.push_u256(create2_salt); } let bytecode = bytecode - .mstore(0, constructor_code) + .mstore(0, constructor_code.clone()) .push_number(constructor_len as u64) // call data .push_number(0u8) .push_number(0u8) // 0 value @@ -554,10 +550,22 @@ fn nested_create_contract_test_case( .build(); db.set_account_code(CALLEE, bytecode); + // Test address and storage slot + let nested_callee = if is_create2 { + CALLEE.create2(create2_salt.to_be_bytes(), keccak256(&constructor_code)) + } else { + CALLEE.create(0) + }; + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(nested_callee); + // An external envs with the given bucket capacity + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * (expansion_times + 1)); + // transfer 1 wei from caller to callee let res = transact( spec, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -929,17 +937,15 @@ fn test_floor_gas_minimal_calldata() { /// This transaction has enough gas for intrinsic costs but not for account creation storage. #[test] fn test_mini_rex_insufficient_storage_gas_for_new_account_oog() { - use mega_evm::SaltEnv; - use std::convert::Infallible; - let mut db = MemoryDatabase::default(); db.set_account_balance(CALLER, U256::from(10_000_000)); let new_account = address!("9000000000000000000000000000000000000009"); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(new_account); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(new_account); let multiplier = 10u64; - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, salt::constant::MIN_BUCKET_SIZE as u64 * multiplier); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * multiplier); + let external_envs = TestExternalEnvs::new(); // MiniRex account creation storage gas: 2,000,000 × 10 = 20,000,000 // Intrinsic: 21,000 @@ -947,8 +953,8 @@ fn test_mini_rex_insufficient_storage_gas_for_new_account_oog() { let insufficient_gas_limit = 22_000; // Override gas limit in the test - let mut context = - MegaContext::new(&mut db, MegaSpecId::MINI_REX).with_external_envs(external_envs.into()); + let mut context = MegaContext::new(&mut db_wrapper, MegaSpecId::MINI_REX) + .with_external_envs(external_envs.into()); context.modify_chain(|chain| { chain.operator_fee_scalar = Some(U256::from(0)); chain.operator_fee_constant = Some(U256::from(0)); @@ -975,27 +981,25 @@ fn test_mini_rex_insufficient_storage_gas_for_new_account_oog() { /// Tests that insufficient gas for contract creation storage gas results in OOG (not invalid tx). #[test] fn test_mini_rex_insufficient_storage_gas_for_contract_creation_oog() { - use mega_evm::SaltEnv; - use std::convert::Infallible; - let mut db = MemoryDatabase::default(); db.set_account_balance(CALLER, U256::from(10_000_000)); // Empty init code to minimize calldata gas let init_code = Bytes::new(); let created_address = CALLER.create(0); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(created_address); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(created_address); let multiplier = 10u64; - let external_envs = TestExternalEnvs::new() - .with_bucket_capacity(bucket_id, salt::constant::MIN_BUCKET_SIZE as u64 * multiplier); + let mut db_wrapper = TestDatabaseWrapper::new(db) + .with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * multiplier); + let external_envs = TestExternalEnvs::new(); // MiniRex contract creation storage gas: 2,000,000 × 10 = 20,000,000 // Base intrinsic for CREATE: 21,000 + 32,000 (CREATE base) = 53,000 // Set gas limit to cover intrinsic but not storage: 60,000 let insufficient_gas_limit = 60_000; - let mut context = - MegaContext::new(&mut db, MegaSpecId::MINI_REX).with_external_envs(external_envs.into()); + let mut context = MegaContext::new(&mut db_wrapper, MegaSpecId::MINI_REX) + .with_external_envs(external_envs.into()); context.modify_chain(|chain| { chain.operator_fee_scalar = Some(U256::from(0)); chain.operator_fee_constant = Some(U256::from(0)); diff --git a/crates/mega-evm/tests/mini_rex/tx_data_and_kv_update_limit.rs b/crates/mega-evm/tests/mini_rex/tx_data_and_kv_update_limit.rs index ceb11ef..3bd8eb8 100644 --- a/crates/mega-evm/tests/mini_rex/tx_data_and_kv_update_limit.rs +++ b/crates/mega-evm/tests/mini_rex/tx_data_and_kv_update_limit.rs @@ -33,9 +33,9 @@ use revm::{ /// Executes a transaction on the `MegaETH` EVM with configurable data limits. /// /// Returns the execution result, generated data size, and number of key-value updates. -fn transact( +fn transact + core::fmt::Debug>( spec: MegaSpecId, - db: &mut CacheDB, + db: &mut DB, data_limit: u64, kv_update_limit: u64, tx: TxEnv, @@ -1221,6 +1221,7 @@ fn test_child_write_and_refund_both_discarded_on_revert() { ..Default::default() }, ); + let mut db = db; let tx = TxEnvBuilder::new().caller(CALLER).call(CALLEE).gas_limit(100_000_000).build_fill(); @@ -1269,6 +1270,7 @@ fn test_tx_intrinsic_data_survives_top_level_revert() { ..Default::default() }, ); + let mut db = db; let tx = TxEnvBuilder::new().caller(CALLER).call(CALLEE).gas_limit(100_000_000).build_fill(); @@ -1313,6 +1315,7 @@ fn test_check_limit_priority_data_size_before_kv_update() { ..Default::default() }, ); + let mut db = db; let tx = TxEnvBuilder::new().caller(CALLER).call(CALLEE).gas_limit(100_000_000).build_fill(); diff --git a/crates/mega-evm/tests/rex/storage_gas.rs b/crates/mega-evm/tests/rex/storage_gas.rs index f292a5e..88090ef 100644 --- a/crates/mega-evm/tests/rex/storage_gas.rs +++ b/crates/mega-evm/tests/rex/storage_gas.rs @@ -7,14 +7,13 @@ use mega_evm::{ constants::{self, rex::*}, test_utils::{BytecodeBuilder, MemoryDatabase}, EVMError, MegaContext, MegaEvm, MegaHaltReason, MegaSpecId, MegaTransaction, - MegaTransactionError, SaltEnv, TestExternalEnvs, + MegaTransactionError, TestDatabaseWrapper, TestExternalEnvs, MIN_BUCKET_SIZE, }; use revm::{ bytecode::opcode::{CALL, CREATE, CREATE2}, context::{result::ResultAndState, TxEnv}, primitives::Address, }; -use salt::constant::MIN_BUCKET_SIZE; const CALLER: Address = address!("2000000000000000000000000000000000000002"); const CALLEE: Address = address!("1000000000000000000000000000000000000001"); @@ -26,9 +25,9 @@ const BASE_CREATE_GAS: u64 = 32_000; /// Executes a transaction on the EVM. #[allow(clippy::too_many_arguments)] -fn transact( +fn transact + core::fmt::Debug>( spec: MegaSpecId, - db: &mut MemoryDatabase, + db: &mut DB, external_envs: &TestExternalEnvs, caller: Address, callee: Option
, @@ -63,7 +62,7 @@ fn test_sstore_minimum_bucket_zero_gas() { let storage_key = U256::ZERO; let storage_value = U256::from(0x42); - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); // Deploy contract with SSTORE operation let bytecode = BytecodeBuilder::default().sstore(storage_key, storage_value).stop().build(); @@ -71,12 +70,13 @@ fn test_sstore_minimum_bucket_zero_gas() { db.set_account_balance(CALLER, U256::from(100_000_000_000u64)); db.set_account_code(CALLEE, bytecode); - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -112,7 +112,7 @@ fn test_sstore_with_multiplier_charges_storage_gas() { let storage_key = U256::ZERO; let storage_value = U256::from(0x42); - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); let bytecode = BytecodeBuilder::default().sstore(storage_key, storage_value).stop().build(); @@ -121,11 +121,13 @@ fn test_sstore_with_multiplier_charges_storage_gas() { // Test with multiplier = 2 (bucket_capacity = 2 * MIN_BUCKET_SIZE) let bucket_capacity = MIN_BUCKET_SIZE as u64 * 2; - let external_envs = TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -161,7 +163,8 @@ fn test_sstore_multiplier_scaling() { let storage_key = U256::ZERO; let storage_value = U256::from(0x42); - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); + let bucket_id = + TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); let bytecode = BytecodeBuilder::default().sstore(storage_key, storage_value).stop().build(); @@ -169,12 +172,13 @@ fn test_sstore_multiplier_scaling() { db.set_account_code(CALLEE, bytecode); let bucket_capacity = MIN_BUCKET_SIZE as u64 * multiplier; - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -210,7 +214,7 @@ fn test_sstore_reset_no_storage_gas() { let mut db = MemoryDatabase::default(); let storage_key = U256::ZERO; - let bucket_id = TestExternalEnvs::::bucket_id_for_slot(CALLEE, storage_key); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_slot(CALLEE, storage_key); // Bytecode that sets a value then resets it to different non-zero value let bytecode = BytecodeBuilder::default() @@ -225,11 +229,13 @@ fn test_sstore_reset_no_storage_gas() { db.set_account_code(CALLEE, bytecode); let bucket_capacity = MIN_BUCKET_SIZE as u64 * 10; // High multiplier - let external_envs = TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -267,13 +273,14 @@ fn test_new_account_minimum_bucket_zero_gas() { db.set_account_balance(CALLER, U256::from(100_000_000_000u64)); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -303,14 +310,15 @@ fn test_new_account_with_multiplier() { db.set_account_balance(CALLER, U256::from(1_000_000_000_000u64)); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); let bucket_capacity = MIN_BUCKET_SIZE as u64 * multiplier; - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -336,13 +344,15 @@ fn test_existing_account_no_storage_gas() { // Pre-create the account db.set_account_balance(NEW_ACCOUNT, U256::from(1_000)); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); let bucket_capacity = MIN_BUCKET_SIZE as u64 * 10; // High multiplier - let external_envs = TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -409,15 +419,17 @@ fn test_contract_creation_with_multiplier() { // Calculate the deterministic contract address (RLP(sender, nonce)) // For nonce=0: keccak256(rlp([sender, 0]))[12:] let created_address = CALLER.create(0); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(created_address); + let bucket_id = + TestDatabaseWrapper::::bucket_id_for_account(created_address); let bucket_capacity = MIN_BUCKET_SIZE as u64 * multiplier; - let external_envs = - TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, None, @@ -455,14 +467,16 @@ fn test_contract_creation_costs_more_than_account() { // Contract creation let created_address = CALLER.create(0); - let contract_bucket_id = TestExternalEnvs::::bucket_id_for_account(created_address); - let contract_external_envs = - TestExternalEnvs::new().with_bucket_capacity(contract_bucket_id, bucket_capacity); + let contract_bucket_id = + TestDatabaseWrapper::::bucket_id_for_account(created_address); + let contract_external_envs = TestExternalEnvs::new(); + let mut db_wrapper_contract = TestDatabaseWrapper::new(db_contract) + .with_bucket_capacity(contract_bucket_id, bucket_capacity); let deployed_bytecode = BytecodeBuilder::default().stop().build(); let contract_result = transact( MegaSpecId::REX, - &mut db_contract, + &mut db_wrapper_contract, &contract_external_envs, CALLER, None, @@ -473,13 +487,15 @@ fn test_contract_creation_costs_more_than_account() { .expect("Contract creation should succeed"); // Account creation - let account_bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); - let account_external_envs = - TestExternalEnvs::new().with_bucket_capacity(account_bucket_id, bucket_capacity); + let account_bucket_id = + TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); + let account_external_envs = TestExternalEnvs::new(); + let mut db_wrapper_account = TestDatabaseWrapper::new(db_account) + .with_bucket_capacity(account_bucket_id, bucket_capacity); let account_result = transact( MegaSpecId::REX, - &mut db_account, + &mut db_wrapper_account, &account_external_envs, CALLER, Some(NEW_ACCOUNT), @@ -520,17 +536,19 @@ fn test_combined_contract_creation_and_sstore() { // Get both bucket IDs let created_address = CALLER.create(0); - let contract_bucket_id = TestExternalEnvs::::bucket_id_for_account(created_address); + let contract_bucket_id = + TestDatabaseWrapper::::bucket_id_for_account(created_address); let storage_bucket_id = - TestExternalEnvs::::bucket_id_for_slot(created_address, storage_key); + TestDatabaseWrapper::::bucket_id_for_slot(created_address, storage_key); - let external_envs = TestExternalEnvs::new() + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = TestDatabaseWrapper::new(db) .with_bucket_capacity(contract_bucket_id, bucket_capacity) .with_bucket_capacity(storage_bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, None, @@ -573,13 +591,18 @@ fn test_rex_vs_minirex_comparison() { let multiplier = 10u64; let bucket_capacity = MIN_BUCKET_SIZE as u64 * multiplier; - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); - let external_envs = TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); + let mut db_wrapper_rex = + TestDatabaseWrapper::new(db_rex).with_bucket_capacity(bucket_id, bucket_capacity); + let mut db_wrapper_minirex = + TestDatabaseWrapper::new(db_minirex).with_bucket_capacity(bucket_id, bucket_capacity); + + let external_envs = TestExternalEnvs::new(); // Account creation comparison let rex_result = transact( MegaSpecId::REX, - &mut db_rex, + &mut db_wrapper_rex, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -591,7 +614,7 @@ fn test_rex_vs_minirex_comparison() { let minirex_result = transact( MegaSpecId::MINI_REX, - &mut db_minirex, + &mut db_wrapper_minirex, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -620,12 +643,14 @@ fn test_large_multiplier_linear_scaling() { let multiplier = 100u64; let bucket_capacity = MIN_BUCKET_SIZE as u64 * multiplier; - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); - let external_envs = TestExternalEnvs::new().with_bucket_capacity(bucket_id, bucket_capacity); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); + let external_envs = TestExternalEnvs::new(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, bucket_capacity); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(NEW_ACCOUNT), @@ -668,13 +693,14 @@ fn test_create_opcode() { db.set_account_code(CALLEE, creator_bytecode); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(CALLEE.create(0)); - let external_envs = - TestExternalEnvs::default().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(CALLEE.create(0)); + let external_envs = TestExternalEnvs::default(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -718,15 +744,16 @@ fn test_create2_opcode() { db.set_account_code(CALLEE, creator_bytecode); - let bucket_id = TestExternalEnvs::::bucket_id_for_account( + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account( CALLEE.create2(salt.to_be_bytes(), keccak256(&deployed_contract)), ); - let external_envs = - TestExternalEnvs::default().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); + let external_envs = TestExternalEnvs::default(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE), @@ -770,13 +797,14 @@ fn test_call_opcode_creates_account() { db.set_account_code(CALLEE, caller_bytecode); - let bucket_id = TestExternalEnvs::::bucket_id_for_account(NEW_ACCOUNT); - let external_envs = - TestExternalEnvs::default().with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); + let bucket_id = TestDatabaseWrapper::::bucket_id_for_account(NEW_ACCOUNT); + let external_envs = TestExternalEnvs::default(); + let mut db_wrapper = + TestDatabaseWrapper::new(db).with_bucket_capacity(bucket_id, MIN_BUCKET_SIZE as u64 * 10); let result = transact( MegaSpecId::REX, - &mut db, + &mut db_wrapper, &external_envs, CALLER, Some(CALLEE),