From 57d6afbb7bfb1cd91ba1841ef373e155b5905d06 Mon Sep 17 00:00:00 2001 From: Brechtpd Date: Wed, 27 Mar 2024 22:41:12 +0100 Subject: [PATCH] POC booster rollup --- crates/interpreter/src/host.rs | 21 +++--- crates/interpreter/src/host/dummy.rs | 26 ++++--- crates/interpreter/src/inner_models.rs | 51 ++++++++++---- crates/interpreter/src/instructions/host.rs | 46 +++++++++++-- .../interpreter/src/instructions/host_env.rs | 4 +- crates/interpreter/src/instructions/macros.rs | 15 ++++ crates/interpreter/src/instructions/system.rs | 4 +- crates/interpreter/src/interpreter.rs | 11 ++- .../interpreter/src/interpreter/contract.rs | 14 ++-- crates/precompile/src/lib.rs | 2 + crates/precompile/src/xcalloptions.rs | 41 +++++++++++ crates/primitives/src/db.rs | 44 ++++++------ crates/primitives/src/db/components.rs | 26 +++---- .../src/db/components/block_hash.rs | 12 ++-- crates/primitives/src/db/components/state.rs | 30 ++++---- crates/primitives/src/env.rs | 34 ++++++--- crates/primitives/src/precompile.rs | 11 ++- crates/primitives/src/state.rs | 16 ++++- crates/revm/benches/bench.rs | 2 +- crates/revm/src/context/inner_evm_context.rs | 4 +- crates/revm/src/db/emptydb.rs | 22 +++--- crates/revm/src/db/ethersdb.rs | 30 ++++---- crates/revm/src/db/in_memory_db.rs | 64 ++++++++--------- crates/revm/src/db/states/bundle_state.rs | 55 ++++++++------- crates/revm/src/db/states/cache.rs | 17 ++--- crates/revm/src/db/states/changes.rs | 12 ++-- crates/revm/src/db/states/reverts.rs | 7 +- crates/revm/src/db/states/state.rs | 54 ++++++++------- crates/revm/src/db/states/transition_state.rs | 7 +- crates/revm/src/evm.rs | 26 +++---- crates/revm/src/inspector.rs | 4 +- crates/revm/src/inspector/customprinter.rs | 11 +-- crates/revm/src/inspector/gas.rs | 6 +- crates/revm/src/journaled_state.rs | 69 ++++++++++--------- examples/fork_ref_transact.rs | 7 +- 35 files changed, 499 insertions(+), 306 deletions(-) create mode 100644 crates/precompile/src/xcalloptions.rs diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index fff18bb848..9fa6e78846 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -5,6 +5,7 @@ use crate::{ mod dummy; pub use dummy::DummyHost; +use revm_primitives::ChainAddress; /// EVM context host. pub trait Host { @@ -17,39 +18,39 @@ pub trait Host { /// Load an account. /// /// Returns (is_cold, is_new_account) - fn load_account(&mut self, address: Address) -> Option<(bool, bool)>; + fn load_account(&mut self, address: ChainAddress) -> Option<(bool, bool)>; /// Get the block hash of the given block `number`. - fn block_hash(&mut self, number: U256) -> Option; + fn block_hash(&mut self, chain_id: u64, number: U256) -> Option; /// Get balance of `address` and if the account is cold. - fn balance(&mut self, address: Address) -> Option<(U256, bool)>; + fn balance(&mut self, address: ChainAddress) -> Option<(U256, bool)>; /// Get code of `address` and if the account is cold. - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)>; + fn code(&mut self, address: ChainAddress) -> Option<(Bytecode, bool)>; /// Get code hash of `address` and if the account is cold. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)>; + fn code_hash(&mut self, address: ChainAddress) -> Option<(B256, bool)>; /// Get storage value of `address` at `index` and if the account is cold. - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)>; + fn sload(&mut self, address: ChainAddress, index: U256) -> Option<(U256, bool)>; /// Set storage value of account address at index. /// /// Returns (original, present, new, is_cold). - fn sstore(&mut self, address: Address, index: U256, value: U256) -> Option; + fn sstore(&mut self, address: ChainAddress, index: U256, value: U256) -> Option; /// Get the transient storage value of `address` at `index`. - fn tload(&mut self, address: Address, index: U256) -> U256; + fn tload(&mut self, address: ChainAddress, index: U256) -> U256; /// Set the transient storage value of `address` at `index`. - fn tstore(&mut self, address: Address, index: U256, value: U256); + fn tstore(&mut self, address: ChainAddress, index: U256, value: U256); /// Emit a log owned by `address` with given `LogData`. fn log(&mut self, log: Log); /// Mark `address` to be deleted, with funds transferred to `target`. - fn selfdestruct(&mut self, address: Address, target: Address) -> Option; + fn selfdestruct(&mut self, address: ChainAddress, target: ChainAddress) -> Option; } /// Represents the result of an `sstore` operation. diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index 519d3e06ee..53045ca5d6 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -4,6 +4,7 @@ use crate::{ Host, SStoreResult, SelfDestructResult, }; use std::vec::Vec; +use revm_primitives::ChainAddress; /// A dummy [Host] implementation. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -44,32 +45,32 @@ impl Host for DummyHost { } #[inline] - fn load_account(&mut self, _address: Address) -> Option<(bool, bool)> { + fn load_account(&mut self, _address: ChainAddress) -> Option<(bool, bool)> { Some((true, true)) } #[inline] - fn block_hash(&mut self, _number: U256) -> Option { + fn block_hash(&mut self, _chain_id: u64, _number: U256) -> Option { Some(B256::ZERO) } #[inline] - fn balance(&mut self, _address: Address) -> Option<(U256, bool)> { + fn balance(&mut self, _address: ChainAddress) -> Option<(U256, bool)> { Some((U256::ZERO, false)) } #[inline] - fn code(&mut self, _address: Address) -> Option<(Bytecode, bool)> { + fn code(&mut self, _address: ChainAddress) -> Option<(Bytecode, bool)> { Some((Bytecode::default(), false)) } #[inline] - fn code_hash(&mut self, __address: Address) -> Option<(B256, bool)> { + fn code_hash(&mut self, __address: ChainAddress) -> Option<(B256, bool)> { Some((KECCAK_EMPTY, false)) } #[inline] - fn sload(&mut self, __address: Address, index: U256) -> Option<(U256, bool)> { + fn sload(&mut self, __address: ChainAddress, index: U256) -> Option<(U256, bool)> { match self.storage.entry(index) { Entry::Occupied(entry) => Some((*entry.get(), false)), Entry::Vacant(entry) => { @@ -80,7 +81,12 @@ impl Host for DummyHost { } #[inline] - fn sstore(&mut self, _address: Address, index: U256, value: U256) -> Option { + fn sstore( + &mut self, + _address: ChainAddress, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)> { let (present, is_cold) = match self.storage.entry(index) { Entry::Occupied(mut entry) => (entry.insert(value), false), Entry::Vacant(entry) => { @@ -98,7 +104,7 @@ impl Host for DummyHost { } #[inline] - fn tload(&mut self, _address: Address, index: U256) -> U256 { + fn tload(&mut self, _address: ChainAddress, index: U256) -> U256 { self.transient_storage .get(&index) .copied() @@ -106,7 +112,7 @@ impl Host for DummyHost { } #[inline] - fn tstore(&mut self, _address: Address, index: U256, value: U256) { + fn tstore(&mut self, _address: ChainAddress, index: U256, value: U256) { self.transient_storage.insert(index, value); } @@ -116,7 +122,7 @@ impl Host for DummyHost { } #[inline] - fn selfdestruct(&mut self, _address: Address, _target: Address) -> Option { + fn selfdestruct(&mut self, _address: ChainAddress, _target: ChainAddress) -> Option { panic!("Selfdestruct is not supported for this host") } } diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 550fab19c3..d74ba6ff21 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,3 +1,5 @@ +use revm_primitives::ChainAddress; + pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, TransactTo, TxEnv, U256}; use core::ops::Range; @@ -7,8 +9,10 @@ use std::boxed::Box; #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CallInputs { + /// Booster: The target chain of the call + pub chain_id: u64, /// The target of the call. - pub contract: Address, + pub contract: ChainAddress, /// The transfer, if any, in this call. pub transfer: Transfer, /// The call data of the call. @@ -21,6 +25,8 @@ pub struct CallInputs { pub is_static: bool, /// The return memory offset where the output of the call is written. pub return_memory_offset: Range, + /// Booster: Whether this is a sandboxed call. + pub is_sandboxed: bool, } /// Inputs for a create call. @@ -28,7 +34,7 @@ pub struct CallInputs { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CreateInputs { /// Caller address of the EVM. - pub caller: Address, + pub caller: ChainAddress, /// The create scheme. pub scheme: CreateScheme, /// The value to transfer. @@ -97,12 +103,33 @@ impl CreateInputs { /// Returns the address that this create call will create. pub fn created_address(&self, nonce: u64) -> Address { match self.scheme { - CreateScheme::Create => self.caller.create(nonce), + CreateScheme::Create => self.create(nonce), CreateScheme::Create2 { salt } => self - .caller + .caller.1 .create2_from_code(salt.to_be_bytes(), &self.init_code), } } + + /// Returns the address that this create call will create, without calculating the init code hash. + /// + /// Note: `hash` must be `keccak256(&self.init_code)`. + pub fn created_address_with_hash(&self, nonce: u64, hash: &B256) -> Address { + match self.scheme { + CreateScheme::Create => self.create(nonce), + CreateScheme::Create2 { salt } => self.caller.1.create2(salt.to_be_bytes(), hash), + } + } + + /// Modified CREATE address on booster chains + fn create(&self, nonce: u64) -> Address { + // If we're not on L1, change the CREATE address to contain the chain_id + let chain_id = if self.caller.0 != 1 { + Some(self.caller.0) + } else { + None + }; + self.caller.1.create(nonce, chain_id) + } } /// Call schemes. @@ -124,11 +151,11 @@ pub enum CallScheme { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CallContext { /// Execution address. - pub address: Address, + pub address: ChainAddress, /// Caller address of the EVM. - pub caller: Address, + pub caller: ChainAddress, /// The address the contract code was loaded from, if any. - pub code_address: Address, + pub code_address: ChainAddress, /// Apparent value of the EVM. pub apparent_value: U256, /// The scheme used for the call. @@ -138,9 +165,9 @@ pub struct CallContext { impl Default for CallContext { fn default() -> Self { CallContext { - address: Address::default(), - caller: Address::default(), - code_address: Address::default(), + address: ChainAddress::default(), + caller: ChainAddress::default(), + code_address: ChainAddress::default(), apparent_value: U256::default(), scheme: CallScheme::Call, } @@ -152,9 +179,9 @@ impl Default for CallContext { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Transfer { /// The source address. - pub source: Address, + pub source: ChainAddress, /// The target address. - pub target: Address, + pub target: ChainAddress, /// The transfer value. pub value: U256, } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 7357adf4f3..71d54e3ed3 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -10,11 +10,11 @@ use crate::{ SStoreResult, Transfer, MAX_INITCODE_SIZE, }; use core::cmp::min; -use revm_primitives::BLOCK_HASH_HISTORY; +use revm_primitives::{BLOCK_HASH_HISTORY, ChainAddress}; use std::{boxed::Box, vec::Vec}; pub fn balance(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); + pop_chain_address!(interpreter, address); let Some((balance, is_cold)) = host.balance(address) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; @@ -45,7 +45,7 @@ pub fn selfbalance(interpreter: &mut Interpreter, host: &mu } pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); + pop_chain_address!(interpreter, address); let Some((code, is_cold)) = host.code(address) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; @@ -71,7 +71,7 @@ pub fn extcodesize(interpreter: &mut Interpreter, host: &mu /// EIP-1052: EXTCODEHASH opcode pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CONSTANTINOPLE); - pop_address!(interpreter, address); + pop_chain_address!(interpreter, address); let Some((code_hash, is_cold)) = host.code_hash(address) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; @@ -94,7 +94,7 @@ pub fn extcodehash(interpreter: &mut Interpreter, host: &mu } pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); + pop_chain_address!(interpreter, address); pop!(interpreter, memory_offset, code_offset, len_u256); let Some((code, is_cold)) = host.code(address) else { @@ -128,7 +128,7 @@ pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { let diff = as_usize_saturated!(diff); // blockhash should push zero if number is same as current block number. if diff <= BLOCK_HASH_HISTORY && diff != 0 { - let Some(hash) = host.block_hash(*number) else { + let Some(hash) = host.block_hash(interpreter.chain_id, *number) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; }; @@ -229,7 +229,7 @@ pub fn log(interpreter: &mut Interpreter, host: &mut H) pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { check_staticcall!(interpreter); - pop_address!(interpreter, target); + pop_chain_address!(interpreter, target); let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { interpreter.instruction_result = InstructionResult::FatalExternalError; @@ -377,6 +377,36 @@ pub fn call(interpreter: &mut Interpreter, host: &mut H) { pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); + + let call_options = interpreter.call_options.clone().unwrap_or_else(|| + CallOptions{ + chain_id: interpreter.chain_id, + sandbox: false, + tx_origin: host.env().tx.caller, + msg_sender: interpreter.contract.address, + block_hash: None, + proof: Vec::new(), + } + ); + // Consume the values + interpreter.call_options = None; + + let to = ChainAddress(call_options.chain_id, to); + // Default to the code stored on L2 + let mut code_address = ChainAddress(host.env().cfg.chain_id, to.1); + + // load account and calculate gas cost. + let Some((is_cold, exist)) = host.load_account(to) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + let is_new = !exist; + + let l2_code_hash = host.code_hash(code_address); + if l2_code_hash.unwrap_or_else(|| (KECCAK_EMPTY, true)).0 == KECCAK_EMPTY { + code_address = ChainAddress(host.env().cfg.chain_id, to.1); + } + // max gas limit is not possible in real ethereum situation. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); @@ -516,6 +546,8 @@ pub fn static_call(interpreter: &mut Interpreter, host: &mu }, is_static: true, return_memory_offset, + chain_id: call_options.chain_id, + is_sandboxed: call_options.sandbox, }), }; interpreter.instruction_result = InstructionResult::CallOrCreate; diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/host_env.rs index 230bb39b37..a16fe982ab 100644 --- a/crates/interpreter/src/instructions/host_env.rs +++ b/crates/interpreter/src/instructions/host_env.rs @@ -13,7 +13,7 @@ pub fn chainid(interpreter: &mut Interpreter, host: &mut H) pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().block.coinbase.into_word()); + push_b256!(interpreter, host.env().block.coinbase.1.into_word()); } pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { @@ -54,7 +54,7 @@ pub fn basefee(interpreter: &mut Interpreter, host: &mut H) pub fn origin(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().tx.caller.into_word()); + push_b256!(interpreter, host.env().tx.caller.1.into_word()); } // EIP-4844: Shard Blob Transactions diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 861489d38f..c31bf632a0 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -120,6 +120,21 @@ macro_rules! pop_address { }; } +/// Pops `U256` values from the stack. Fails the instruction if the stack is too small. +#[macro_export] +macro_rules! pop_chain_address { + ($interp:expr, $x1:ident) => { + pop_address!($interp, $x1); + let $x1 = ChainAddress($interp.chain_id, $x1); + }; + ($interp:expr, $x1:ident, $x2:ident) => { + pop_address!($interp, $x1, $x2); + let $x1 = ChainAddress($interp.chain_id, $x1); + let $x2 = ChainAddress($interp.chain_id, $x2); + }; +} + + /// Pops `U256` values from the stack. Fails the instruction if the stack is too small. #[macro_export] macro_rules! pop { diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 55109d8004..648e15428b 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -21,12 +21,12 @@ pub fn keccak256(interpreter: &mut Interpreter, _host: &mut H) { pub fn address(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, interpreter.contract.address.into_word()); + push_b256!(interpreter, interpreter.contract.address.1.into_word()); } pub fn caller(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, interpreter.contract.caller.into_word()); + push_b256!(interpreter, interpreter.contract.caller.1.into_word()); } pub fn codesize(interpreter: &mut Interpreter, _host: &mut H) { diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 2a1e99a377..ea89282f4e 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -49,6 +49,12 @@ pub struct Interpreter { /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. pub next_action: InterpreterAction, + /// Booster: chain storage to use + pub chain_id: u64, + /// Booster: call options set using XCALLOPTIONS + pub call_options: Option, + /// Booster: when running sandboxed, always revert the state changes done inside the call + pub is_sandboxed: bool, } /// The result of an interpreter operation. @@ -115,7 +121,7 @@ impl InterpreterAction { impl Interpreter { /// Create new interpreter - pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self { + pub fn new(contract: Contract, gas_limit: u64, is_static: bool, chain_id: u64, sandboxed: bool) -> Self { Self { instruction_pointer: contract.bytecode.as_ptr(), contract, @@ -126,6 +132,9 @@ impl Interpreter { shared_memory: EMPTY_SHARED_MEMORY, stack: Stack::new(), next_action: InterpreterAction::None, + chain_id, + call_options: None, + is_sandboxed: sandboxed, } } diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index a5896cc865..cf7e264211 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -1,5 +1,7 @@ +use revm_primitives::ChainAddress; + use super::analysis::{to_analysed, BytecodeLocked}; -use crate::primitives::{Address, Bytecode, Bytes, Env, TransactTo, B256, U256}; +use crate::primitives::{Bytecode, Bytes, Env, TransactTo, B256, U256}; use crate::CallContext; /// EVM contract information. @@ -13,9 +15,9 @@ pub struct Contract { /// Bytecode hash. pub hash: B256, /// Contract address - pub address: Address, + pub address: ChainAddress, /// Caller of the EVM. - pub caller: Address, + pub caller: ChainAddress, /// Value send to contract. pub value: U256, } @@ -27,8 +29,8 @@ impl Contract { input: Bytes, bytecode: Bytecode, hash: B256, - address: Address, - caller: Address, + address: ChainAddress, + caller: ChainAddress, value: U256, ) -> Self { let bytecode = to_analysed(bytecode).try_into().expect("it is analyzed"); @@ -48,7 +50,7 @@ impl Contract { pub fn new_env(env: &Env, bytecode: Bytecode, hash: B256) -> Self { let contract_address = match env.tx.transact_to { TransactTo::Call(caller) => caller, - TransactTo::Create(..) => Address::ZERO, + TransactTo::Create(..) => ChainAddress::default(), }; Self::new( env.tx.data.clone(), diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index fd34bd6358..3a785418dd 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -18,6 +18,7 @@ pub mod identity; pub mod kzg_point_evaluation; pub mod modexp; pub mod secp256k1; +pub mod xcalloptions; pub mod utilities; use core::hash::Hash; @@ -127,6 +128,7 @@ impl Precompiles { precompiles.extend([ // EIP-2565: ModExp Gas Cost. modexp::BERLIN, + xcalloptions::XCALLOPTIONS, ]); Box::new(precompiles) }) diff --git a/crates/precompile/src/xcalloptions.rs b/crates/precompile/src/xcalloptions.rs new file mode 100644 index 0000000000..e3560d2723 --- /dev/null +++ b/crates/precompile/src/xcalloptions.rs @@ -0,0 +1,41 @@ +use revm_primitives::{Env, CallOptions, Address, ChainAddress}; +use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress, CtxPrecompileFn}; + +pub const XCALLOPTIONS: PrecompileWithAddress = PrecompileWithAddress( + crate::u64_to_address(1000), + Precompile::Ctx(xcalloptions_run as CtxPrecompileFn), +); + +/// Sets the xcall options +fn xcalloptions_run(input: &[u8], _gas_limit: u64, _env: &Env, call_options: &mut Option) -> PrecompileResult { + // Verify input length. + if input.len() < 83 { + return Err(Error::XCallOptionsInvalidInputLength); + } + + // Read the input data + let version = u16::from_le_bytes(input[0..2].try_into().unwrap()); + let chain_id = u64::from_le_bytes(input[2..10].try_into().unwrap()); + let sandbox = input[10] != 0; + let tx_origin = Address(input[11..31].try_into().unwrap()); + let msg_sender = Address(input[31..51].try_into().unwrap()); + let block_hash = Some(input[51..83].try_into().unwrap()); + let proof = &input[83..]; + + // Check the version + if version != 1 { + return Err(Error::XCallOptionsInvalidInputLength); + } + + // Set the call options + *call_options = Some(CallOptions { + chain_id, + sandbox, + tx_origin: ChainAddress(chain_id, tx_origin), + msg_sender: ChainAddress(chain_id, msg_sender), + block_hash, + proof: proof.to_vec(), + }); + + Ok((0, vec![])) +} diff --git a/crates/primitives/src/db.rs b/crates/primitives/src/db.rs index e0db9bda01..debbc3589c 100644 --- a/crates/primitives/src/db.rs +++ b/crates/primitives/src/db.rs @@ -1,4 +1,4 @@ -use crate::{Account, AccountInfo, Address, Bytecode, HashMap, B256, U256}; +use crate::{Account, AccountInfo, ChainAddress, Bytecode, HashMap, B256, U256}; use auto_impl::auto_impl; pub mod components; @@ -13,23 +13,23 @@ pub trait Database { type Error; /// Get basic account information. - fn basic(&mut self, address: Address) -> Result, Self::Error>; + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error>; /// Get account code by its hash. - fn code_by_hash(&mut self, code_hash: B256) -> Result; + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result; /// Get storage value of address at index. - fn storage(&mut self, address: Address, index: U256) -> Result; + fn storage(&mut self, address: ChainAddress, index: U256) -> Result; /// Get block hash by block number. - fn block_hash(&mut self, number: U256) -> Result; + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result; } /// EVM database commit interface. #[auto_impl(&mut, Box)] pub trait DatabaseCommit { /// Commit changes to the database. - fn commit(&mut self, changes: HashMap); + fn commit(&mut self, changes: HashMap); } /// EVM database interface. @@ -44,16 +44,16 @@ pub trait DatabaseRef { type Error; /// Get basic account information. - fn basic_ref(&self, address: Address) -> Result, Self::Error>; + fn basic_ref(&self, address: ChainAddress) -> Result, Self::Error>; /// Get account code by its hash. - fn code_by_hash_ref(&self, code_hash: B256) -> Result; + fn code_by_hash_ref(&self, chain_id: u64, code_hash: B256) -> Result; /// Get storage value of address at index. - fn storage_ref(&self, address: Address, index: U256) -> Result; + fn storage_ref(&self, address: ChainAddress, index: U256) -> Result; /// Get block hash by block number. - fn block_hash_ref(&self, number: U256) -> Result; + fn block_hash_ref(&self, chain_id: u64, number: U256) -> Result; } /// Wraps a [`DatabaseRef`] to provide a [`Database`] implementation. @@ -71,23 +71,23 @@ impl Database for WrapDatabaseRef { type Error = T::Error; #[inline] - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { self.0.basic_ref(address) } #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.0.code_by_hash_ref(code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + self.0.code_by_hash_ref(chain_id, code_hash) } #[inline] - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { self.0.storage_ref(address, index) } #[inline] - fn block_hash(&mut self, number: U256) -> Result { - self.0.block_hash_ref(number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + self.0.block_hash_ref(chain_id, number) } } @@ -118,22 +118,22 @@ impl<'a, E> Database for RefDBWrapper<'a, E> { type Error = E; #[inline] - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { self.db.basic_ref(address) } #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.db.code_by_hash_ref(code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + self.db.code_by_hash_ref(chain_id, code_hash) } #[inline] - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { self.db.storage_ref(address, index) } #[inline] - fn block_hash(&mut self, number: U256) -> Result { - self.db.block_hash_ref(number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + self.db.block_hash_ref(chain_id, number) } } diff --git a/crates/primitives/src/db/components.rs b/crates/primitives/src/db/components.rs index c6348b920a..c33ecfa414 100644 --- a/crates/primitives/src/db/components.rs +++ b/crates/primitives/src/db/components.rs @@ -7,7 +7,7 @@ pub use state::{State, StateRef}; use crate::{ db::{Database, DatabaseRef}, - Account, AccountInfo, Address, Bytecode, HashMap, B256, U256, + Account, AccountInfo, ChainAddress, Bytecode, HashMap, B256, U256, }; use super::DatabaseCommit; @@ -27,25 +27,25 @@ pub enum DatabaseComponentError { impl Database for DatabaseComponents { type Error = DatabaseComponentError; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { self.state.basic(address).map_err(Self::Error::State) } - fn code_by_hash(&mut self, code_hash: B256) -> Result { + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { self.state - .code_by_hash(code_hash) + .code_by_hash(chain_id, code_hash) .map_err(Self::Error::State) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { self.state .storage(address, index) .map_err(Self::Error::State) } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { self.block_hash - .block_hash(number) + .block_hash(chain_id, number) .map_err(Self::Error::BlockHash) } } @@ -53,25 +53,25 @@ impl Database for DatabaseComponents { impl DatabaseRef for DatabaseComponents { type Error = DatabaseComponentError; - fn basic_ref(&self, address: Address) -> Result, Self::Error> { + fn basic_ref(&self, address: ChainAddress) -> Result, Self::Error> { self.state.basic(address).map_err(Self::Error::State) } - fn code_by_hash_ref(&self, code_hash: B256) -> Result { + fn code_by_hash_ref(&self, chain_id: u64, code_hash: B256) -> Result { self.state - .code_by_hash(code_hash) + .code_by_hash(chain_id, code_hash) .map_err(Self::Error::State) } - fn storage_ref(&self, address: Address, index: U256) -> Result { + fn storage_ref(&self, address: ChainAddress, index: U256) -> Result { self.state .storage(address, index) .map_err(Self::Error::State) } - fn block_hash_ref(&self, number: U256) -> Result { + fn block_hash_ref(&self, chain_id: u64, number: U256) -> Result { self.block_hash - .block_hash(number) + .block_hash(chain_id, number) .map_err(Self::Error::BlockHash) } } diff --git a/crates/primitives/src/db/components/block_hash.rs b/crates/primitives/src/db/components/block_hash.rs index a0a0fe21e6..48b79677a7 100644 --- a/crates/primitives/src/db/components/block_hash.rs +++ b/crates/primitives/src/db/components/block_hash.rs @@ -11,7 +11,7 @@ pub trait BlockHash { type Error; /// Get block hash by block number - fn block_hash(&mut self, number: U256) -> Result; + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result; } #[auto_impl(&, &mut, Box, Rc, Arc)] @@ -19,7 +19,7 @@ pub trait BlockHashRef { type Error; /// Get block hash by block number - fn block_hash(&self, number: U256) -> Result; + fn block_hash(&self, chain_id: u64, number: U256) -> Result; } impl BlockHash for &T @@ -28,8 +28,8 @@ where { type Error = ::Error; - fn block_hash(&mut self, number: U256) -> Result { - BlockHashRef::block_hash(*self, number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + BlockHashRef::block_hash(*self, chain_id, number) } } @@ -39,7 +39,7 @@ where { type Error = ::Error; - fn block_hash(&mut self, number: U256) -> Result { - self.deref().block_hash(number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + self.deref().block_hash(chain_id, number) } } diff --git a/crates/primitives/src/db/components/state.rs b/crates/primitives/src/db/components/state.rs index d3b948ddaa..8f76f07bf7 100644 --- a/crates/primitives/src/db/components/state.rs +++ b/crates/primitives/src/db/components/state.rs @@ -1,7 +1,7 @@ //! State database component from [`crate::db::Database`] //! it is used inside [`crate::db::DatabaseComponents`] -use crate::{AccountInfo, Address, Bytecode, B256, U256}; +use crate::{AccountInfo, ChainAddress, Bytecode, B256, U256}; use auto_impl::auto_impl; use core::ops::Deref; use std::sync::Arc; @@ -11,13 +11,13 @@ pub trait State { type Error; /// Get basic account information. - fn basic(&mut self, address: Address) -> Result, Self::Error>; + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error>; /// Get account code by its hash - fn code_by_hash(&mut self, code_hash: B256) -> Result; + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result; /// Get storage value of address at index. - fn storage(&mut self, address: Address, index: U256) -> Result; + fn storage(&mut self, address: ChainAddress, index: U256) -> Result; } #[auto_impl(&, &mut, Box, Rc, Arc)] @@ -25,13 +25,13 @@ pub trait StateRef { type Error; /// Get basic account information. - fn basic(&self, address: Address) -> Result, Self::Error>; + fn basic(&self, address: ChainAddress) -> Result, Self::Error>; /// Get account code by its hash - fn code_by_hash(&self, code_hash: B256) -> Result; + fn code_by_hash(&self, chain_id: u64, code_hash: B256) -> Result; /// Get storage value of address at index. - fn storage(&self, address: Address, index: U256) -> Result; + fn storage(&self, address: ChainAddress, index: U256) -> Result; } impl State for &T @@ -40,15 +40,15 @@ where { type Error = ::Error; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { StateRef::basic(*self, address) } - fn code_by_hash(&mut self, code_hash: B256) -> Result { - StateRef::code_by_hash(*self, code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + StateRef::code_by_hash(*self, chain_id, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { StateRef::storage(*self, address, index) } } @@ -59,15 +59,15 @@ where { type Error = ::Error; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { self.deref().basic(address) } - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.deref().code_by_hash(code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + self.deref().code_by_hash(chain_id, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { self.deref().storage(address, index) } } diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index bd0e52ce52..04313b0505 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -5,7 +5,7 @@ pub use handler_cfg::{CfgEnvWithHandlerCfg, EnvWithHandlerCfg, HandlerCfg}; use crate::{ calc_blob_gasprice, Account, Address, Bytes, InvalidHeader, InvalidTransaction, Spec, SpecId, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256, - VERSIONED_HASH_VERSION_KZG, + VERSIONED_HASH_VERSION_KZG, ChainAddress, }; use core::cmp::{min, Ordering}; use std::boxed::Box; @@ -299,6 +299,8 @@ pub struct CfgEnv { /// By default, it is set to `false`. #[cfg(feature = "optional_beneficiary_reward")] pub disable_beneficiary_reward: bool, + /// Chain ID of the parent chain, `0` for no parent chain + pub parent_chain_id: u64, } impl CfgEnv { @@ -398,7 +400,7 @@ pub struct BlockEnv { /// Coinbase or miner or address that created and signed the block. /// /// This is the receiver address of all the gas spent in the block. - pub coinbase: Address, + pub coinbase: ChainAddress, /// The timestamp of the block in seconds since the UNIX epoch. pub timestamp: U256, @@ -471,7 +473,7 @@ impl Default for BlockEnv { fn default() -> Self { Self { number: U256::ZERO, - coinbase: Address::ZERO, + coinbase: ChainAddress::default(), timestamp: U256::from(1), gas_limit: U256::MAX, basefee: U256::ZERO, @@ -487,7 +489,7 @@ impl Default for BlockEnv { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxEnv { /// Caller aka Author aka transaction signer. - pub caller: Address, + pub caller: ChainAddress, /// The gas limit of the transaction. pub gas_limit: u64, /// The gas price of the transaction. @@ -563,11 +565,11 @@ impl TxEnv { impl Default for TxEnv { fn default() -> Self { Self { - caller: Address::ZERO, + caller: ChainAddress::default(), gas_limit: u64::MAX, gas_price: U256::ZERO, gas_priority_fee: None, - transact_to: TransactTo::Call(Address::ZERO), // will do nothing + transact_to: TransactTo::Call(ChainAddress::default()), // will do nothing value: U256::ZERO, data: Bytes::new(), chain_id: None, @@ -645,7 +647,7 @@ pub struct OptimismFields { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TransactTo { /// Simple call to an address. - Call(Address), + Call(ChainAddress), /// Contract creation. Create(CreateScheme), } @@ -653,7 +655,7 @@ pub enum TransactTo { impl TransactTo { /// Calls the given address. #[inline] - pub fn call(address: Address) -> Self { + pub fn call(address: ChainAddress) -> Self { Self::Call(address) } @@ -708,6 +710,22 @@ pub enum AnalysisKind { Analyse, } +#[derive(Clone, Debug)] +pub struct CallOptions { + /// The target chain id + pub chain_id: u64, + /// If the call needs to persist state changes or not + pub sandbox: bool, + /// Mocked `tx.origin` + pub tx_origin: ChainAddress, + /// Mocked `msg.sender` + pub msg_sender: ChainAddress, + /// The block hash to execute against (None will execute against the latest known blockhash) + pub block_hash: Option, + /// The data necessary to execute the call + pub proof: Vec, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index e585377537..6ec8cf13ae 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -1,4 +1,4 @@ -use crate::{Bytes, Env}; +use crate::{Bytes, Env, CallOptions}; use core::fmt; use dyn_clone::DynClone; use std::{boxed::Box, string::String, sync::Arc}; @@ -10,6 +10,8 @@ pub type PrecompileResult = Result<(u64, Bytes), PrecompileError>; pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult; +/// TODO: the input is a CallOptions struct to avoid having to put the interpreter in the primitives crate +pub type CtxPrecompileFn = fn(&[u8], u64, env: &Env, interpreter: &mut Option) -> PrecompileResult; /// Stateful precompile trait. It is used to create /// a arc precompile Precompile::Stateful. @@ -125,6 +127,11 @@ pub enum PrecompileError { BlobMismatchedVersion, /// The proof verification failed. BlobVerifyKzgProofFailed, + /// XCALLOPTIONS errors + /// The input length is not the expected length + XCallOptionsInvalidInputLength, + /// The version given is not supported + XCallOptionsInvalidVersion, /// Catch-all variant for other errors. Other(String), } @@ -153,6 +160,8 @@ impl fmt::Display for PrecompileError { Self::BlobInvalidInputLength => "invalid blob input length", Self::BlobMismatchedVersion => "mismatched blob version", Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed", + Self::XCallOptionsInvalidInputLength => "XCallOptionsInvalidInputLength", + Self::XCallOptionsInvalidVersion => "XCallOptionsInvalidVersion", Self::Other(s) => s, }; f.write_str(s) diff --git a/crates/primitives/src/state.rs b/crates/primitives/src/state.rs index 8f6736bd79..a98aa899d8 100644 --- a/crates/primitives/src/state.rs +++ b/crates/primitives/src/state.rs @@ -2,15 +2,27 @@ use crate::{Address, Bytecode, HashMap, B256, KECCAK_EMPTY, U256}; use bitflags::bitflags; use core::hash::{Hash, Hasher}; +/// Chain specific address +/// +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ChainAddress(pub u64, pub Address); + /// EVM State is a mapping from addresses to accounts. -pub type State = HashMap; +pub type State = HashMap; /// Structure used for EIP-1153 transient storage. -pub type TransientStorage = HashMap<(Address, U256), U256>; +pub type TransientStorage = HashMap<(ChainAddress, U256), U256>; /// An account's Storage is a mapping from 256-bit integer keys to [StorageSlot]s. pub type Storage = HashMap; +impl Default for ChainAddress { + fn default() -> Self { + ChainAddress(0, Address::default()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Account { diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 9d07334692..d7708167b7 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -114,7 +114,7 @@ fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), B // replace memory with empty memory to use it inside interpreter. // Later return memory back. let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); - let mut interpreter = Interpreter::new(contract.clone(), u64::MAX, false); + let mut interpreter = Interpreter::new(contract.clone(), u64::MAX, false, 0, false); let res = interpreter.run(temp, &instruction_table, &mut host); shared_memory = interpreter.take_memory(); host.clear(); diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs index 98a4d5679b..b943246a2c 100644 --- a/crates/revm/src/context/inner_evm_context.rs +++ b/crates/revm/src/context/inner_evm_context.rs @@ -115,8 +115,8 @@ impl InnerEvmContext { /// Fetch block hash from database. #[inline] - pub fn block_hash(&mut self, number: U256) -> Result> { - self.db.block_hash(number).map_err(EVMError::Database) + pub fn block_hash(&mut self, chain_id: u64, number: U256) -> Result> { + self.db.block_hash(chain_id, number).map_err(EVMError::Database) } /// Mark account as touched as only touched accounts will be added to state. diff --git a/crates/revm/src/db/emptydb.rs b/crates/revm/src/db/emptydb.rs index 6495ce1735..92a6cb815f 100644 --- a/crates/revm/src/db/emptydb.rs +++ b/crates/revm/src/db/emptydb.rs @@ -1,7 +1,7 @@ use core::{convert::Infallible, fmt, marker::PhantomData}; use revm_interpreter::primitives::{ db::{Database, DatabaseRef}, - keccak256, AccountInfo, Address, Bytecode, B256, U256, + keccak256, AccountInfo, ChainAddress, Bytecode, B256, U256, }; use std::string::ToString; @@ -63,23 +63,23 @@ impl Database for EmptyDBTyped { type Error = E; #[inline] - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { ::basic_ref(self, address) } #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - ::code_by_hash_ref(self, code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + ::code_by_hash_ref(self, chain_id, code_hash) } #[inline] - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { ::storage_ref(self, address, index) } #[inline] - fn block_hash(&mut self, number: U256) -> Result { - ::block_hash_ref(self, number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + ::block_hash_ref(self, chain_id, number) } } @@ -87,22 +87,22 @@ impl DatabaseRef for EmptyDBTyped { type Error = E; #[inline] - fn basic_ref(&self, _address: Address) -> Result, Self::Error> { + fn basic_ref(&self, _address: ChainAddress) -> Result, Self::Error> { Ok(None) } #[inline] - fn code_by_hash_ref(&self, _code_hash: B256) -> Result { + fn code_by_hash_ref(&self, _chain_id: u64, _code_hash: B256) -> Result { Ok(Bytecode::new()) } #[inline] - fn storage_ref(&self, _address: Address, _index: U256) -> Result { + fn storage_ref(&self, _address: ChainAddress, _index: U256) -> Result { Ok(U256::default()) } #[inline] - fn block_hash_ref(&self, number: U256) -> Result { + fn block_hash_ref(&self, _chain_id: u64, number: U256) -> Result { Ok(keccak256(number.to_string().as_bytes())) } } diff --git a/crates/revm/src/db/ethersdb.rs b/crates/revm/src/db/ethersdb.rs index d7863f100f..b735381bf5 100644 --- a/crates/revm/src/db/ethersdb.rs +++ b/crates/revm/src/db/ethersdb.rs @@ -4,7 +4,7 @@ use ethers_core::types::{Block, BlockId, TxHash, H160 as eH160, H256, U64 as eU6 use ethers_providers::Middleware; use tokio::runtime::{Builder, Handle, RuntimeFlavor}; -use crate::primitives::{AccountInfo, Address, Bytecode, B256, KECCAK_EMPTY, U256}; +use crate::primitives::{AccountInfo, Address, Bytecode, ChainAddress, B256, KECCAK_EMPTY, U256}; use crate::{Database, DatabaseRef}; #[derive(Debug, Clone)] @@ -69,11 +69,12 @@ impl EthersDB { } } +// TODO: respect chain id and pick the right client impl DatabaseRef for EthersDB { type Error = M::Error; - fn basic_ref(&self, address: Address) -> Result, Self::Error> { - let add = eH160::from(address.0 .0); + fn basic_ref(&self, address: ChainAddress) -> Result, Self::Error> { + let add = eH160::from(address.1.0 .0); let f = async { let nonce = self.client.get_transaction_count(add, self.block_number); @@ -90,20 +91,20 @@ impl DatabaseRef for EthersDB { Ok(Some(AccountInfo::new(balance, nonce, code_hash, bytecode))) } - fn code_by_hash_ref(&self, _code_hash: B256) -> Result { + fn code_by_hash_ref(&self, _chain_id: u64, _code_hash: B256) -> Result { panic!("Should not be called. Code is already loaded"); // not needed because we already load code with basic info } - fn storage_ref(&self, address: Address, index: U256) -> Result { - let add = eH160::from(address.0 .0); + fn storage_ref(&self, address: ChainAddress, index: U256) -> Result { + let add = eH160::from(address.1.0 .0); let index = H256::from(index.to_be_bytes()); let slot_value: H256 = Self::block_on(self.client.get_storage_at(add, index, self.block_number))?; Ok(U256::from_be_bytes(slot_value.to_fixed_bytes())) } - fn block_hash_ref(&self, number: U256) -> Result { + fn block_hash_ref(&self, _chain_id: u64, number: U256) -> Result { // saturate usize if number > U256::from(u64::MAX) { return Ok(KECCAK_EMPTY); @@ -121,23 +122,23 @@ impl Database for EthersDB { type Error = M::Error; #[inline] - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { ::basic_ref(self, address) } #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - ::code_by_hash_ref(self, code_hash) + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { + ::code_by_hash_ref(self, chain_id, code_hash) } #[inline] - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { ::storage_ref(self, address, index) } #[inline] - fn block_hash(&mut self, number: U256) -> Result { - ::block_hash_ref(self, number) + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + ::block_hash_ref(self, chain_id, number) } } @@ -154,6 +155,7 @@ mod tests { ) .unwrap(); let client = Arc::new(client); + let chain_id = 0; let ethersdb = EthersDB::new( Arc::clone(&client), // public infura mainnet @@ -165,7 +167,7 @@ mod tests { let address = "0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852" .parse::() .unwrap(); - let address = address.as_fixed_bytes().into(); + let address = ChainAddress(chain_id, address.as_fixed_bytes().into()); let acc_info = ethersdb.basic_ref(address).unwrap().unwrap(); diff --git a/crates/revm/src/db/in_memory_db.rs b/crates/revm/src/db/in_memory_db.rs index 7ecf5236c0..e71f682c4f 100644 --- a/crates/revm/src/db/in_memory_db.rs +++ b/crates/revm/src/db/in_memory_db.rs @@ -1,6 +1,6 @@ use super::{DatabaseCommit, DatabaseRef, EmptyDB}; use crate::primitives::{ - hash_map::Entry, Account, AccountInfo, Address, Bytecode, HashMap, Log, B256, KECCAK_EMPTY, + hash_map::Entry, Account, AccountInfo, Address, ChainAddress, Bytecode, HashMap, Log, B256, KECCAK_EMPTY, U256, }; use crate::Database; @@ -22,13 +22,13 @@ pub type InMemoryDB = CacheDB; pub struct CacheDB { /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. /// `code` is always `None`, and bytecode can be found in `contracts`. - pub accounts: HashMap, + pub accounts: HashMap, /// Tracks all contracts by their code hash. pub contracts: HashMap, /// All logs that were committed via [DatabaseCommit::commit]. pub logs: Vec, /// All cached block hashes from the [DatabaseRef]. - pub block_hashes: HashMap, + pub block_hashes: HashMap<(u64, U256), B256>, /// The underlying database ([DatabaseRef]) that is used to load data. /// /// Note: this is read-only, data is never written to this database. @@ -77,7 +77,7 @@ impl CacheDB { } /// Insert account info but not override storage - pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) { + pub fn insert_account_info(&mut self, address: ChainAddress, mut info: AccountInfo) { self.insert_contract(&mut info); self.accounts.entry(address).or_default().info = info; } @@ -87,7 +87,7 @@ impl CacheDB { /// Returns the account for the given address. /// /// If the account was not found in the cache, it will be loaded from the underlying database. - pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, ExtDB::Error> { + pub fn load_account(&mut self, address: ChainAddress) -> Result<&mut DbAccount, ExtDB::Error> { let db = &self.db; match self.accounts.entry(address) { Entry::Occupied(entry) => Ok(entry.into_mut()), @@ -105,7 +105,7 @@ impl CacheDB { /// insert account storage without overriding account info pub fn insert_account_storage( &mut self, - address: Address, + address: ChainAddress, slot: U256, value: U256, ) -> Result<(), ExtDB::Error> { @@ -117,7 +117,7 @@ impl CacheDB { /// replace account storage without overriding account info pub fn replace_account_storage( &mut self, - address: Address, + address: ChainAddress, storage: HashMap, ) -> Result<(), ExtDB::Error> { let account = self.load_account(address)?; @@ -128,7 +128,7 @@ impl CacheDB { } impl DatabaseCommit for CacheDB { - fn commit(&mut self, changes: HashMap) { + fn commit(&mut self, changes: HashMap) { for (address, mut account) in changes { if !account.is_touched() { continue; @@ -168,7 +168,7 @@ impl DatabaseCommit for CacheDB { impl Database for CacheDB { type Error = ExtDB::Error; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { let basic = match self.accounts.entry(address) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => entry.insert( @@ -184,12 +184,12 @@ impl Database for CacheDB { Ok(basic.info()) } - fn code_by_hash(&mut self, code_hash: B256) -> Result { + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { match self.contracts.entry(code_hash) { Entry::Occupied(entry) => Ok(entry.get().clone()), Entry::Vacant(entry) => { // if you return code bytes when basic fn is called this function is not needed. - Ok(entry.insert(self.db.code_by_hash_ref(code_hash)?).clone()) + Ok(entry.insert(self.db.code_by_hash_ref(chain_id, code_hash)?).clone()) } } } @@ -197,7 +197,7 @@ impl Database for CacheDB { /// Get the value in an account's storage slot. /// /// It is assumed that account is already loaded. - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { match self.accounts.entry(address) { Entry::Occupied(mut acc_entry) => { let acc_entry = acc_entry.get_mut(); @@ -234,11 +234,11 @@ impl Database for CacheDB { } } - fn block_hash(&mut self, number: U256) -> Result { - match self.block_hashes.entry(number) { + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { + match self.block_hashes.entry((chain_id, number)) { Entry::Occupied(entry) => Ok(*entry.get()), Entry::Vacant(entry) => { - let hash = self.db.block_hash_ref(number)?; + let hash = self.db.block_hash_ref(chain_id, number)?; entry.insert(hash); Ok(hash) } @@ -249,21 +249,21 @@ impl Database for CacheDB { impl DatabaseRef for CacheDB { type Error = ExtDB::Error; - fn basic_ref(&self, address: Address) -> Result, Self::Error> { + fn basic_ref(&self, address: ChainAddress) -> Result, Self::Error> { match self.accounts.get(&address) { Some(acc) => Ok(acc.info()), None => self.db.basic_ref(address), } } - fn code_by_hash_ref(&self, code_hash: B256) -> Result { + fn code_by_hash_ref(&self, chain_id: u64, code_hash: B256) -> Result { match self.contracts.get(&code_hash) { Some(entry) => Ok(entry.clone()), - None => self.db.code_by_hash_ref(code_hash), + None => self.db.code_by_hash_ref(chain_id, code_hash), } } - fn storage_ref(&self, address: Address, index: U256) -> Result { + fn storage_ref(&self, address: ChainAddress, index: U256) -> Result { match self.accounts.get(&address) { Some(acc_entry) => match acc_entry.storage.get(&index) { Some(entry) => Ok(*entry), @@ -282,10 +282,10 @@ impl DatabaseRef for CacheDB { } } - fn block_hash_ref(&self, number: U256) -> Result { - match self.block_hashes.get(&number) { + fn block_hash_ref(&self, chain_id: u64, number: U256) -> Result { + match self.block_hashes.get(&(chain_id, number)) { Some(entry) => Ok(*entry), - None => self.db.block_hash_ref(number), + None => self.db.block_hash_ref(chain_id, number), } } } @@ -372,8 +372,8 @@ impl BenchmarkDB { impl Database for BenchmarkDB { type Error = Infallible; /// Get basic account information. - fn basic(&mut self, address: Address) -> Result, Self::Error> { - if address == Address::ZERO { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { + if address.1 == Address::ZERO { return Ok(Some(AccountInfo { nonce: 1, balance: U256::from(10000000), @@ -381,7 +381,7 @@ impl Database for BenchmarkDB { code_hash: self.1, })); } - if address == Address::with_last_byte(1) { + if address.1 == Address::with_last_byte(1) { return Ok(Some(AccountInfo { nonce: 0, balance: U256::from(10000000), @@ -393,17 +393,17 @@ impl Database for BenchmarkDB { } /// Get account code by its hash - fn code_by_hash(&mut self, _code_hash: B256) -> Result { + fn code_by_hash(&mut self, _chain_id: u64, _code_hash: B256) -> Result { Ok(Bytecode::default()) } /// Get storage value of address at index. - fn storage(&mut self, _address: Address, _index: U256) -> Result { + fn storage(&mut self, _address: ChainAddress, _index: U256) -> Result { Ok(U256::default()) } // History related - fn block_hash(&mut self, _number: U256) -> Result { + fn block_hash(&mut self, _chain_id: u64, _number: U256) -> Result { Ok(B256::default()) } } @@ -411,11 +411,12 @@ impl Database for BenchmarkDB { #[cfg(test)] mod tests { use super::{CacheDB, EmptyDB}; - use crate::primitives::{db::Database, AccountInfo, Address, U256}; + use crate::primitives::{db::Database, AccountInfo, Address, ChainAddress, U256}; #[test] fn test_insert_account_storage() { - let account = Address::with_last_byte(42); + let chain_id = 0; + let account = ChainAddress(chain_id, Address::with_last_byte(42)); let nonce = 42; let mut init_state = CacheDB::new(EmptyDB::default()); init_state.insert_account_info( @@ -438,7 +439,8 @@ mod tests { #[test] fn test_replace_account_storage() { - let account = Address::with_last_byte(42); + let chain_id = 0; + let account = ChainAddress(chain_id, Address::with_last_byte(42)); let nonce = 42; let mut init_state = CacheDB::new(EmptyDB::default()); init_state.insert_account_info( diff --git a/crates/revm/src/db/states/bundle_state.rs b/crates/revm/src/db/states/bundle_state.rs index 0b5a6da573..5a08bc0131 100644 --- a/crates/revm/src/db/states/bundle_state.rs +++ b/crates/revm/src/db/states/bundle_state.rs @@ -6,25 +6,26 @@ use super::{ use core::{mem, ops::RangeInclusive}; use revm_interpreter::primitives::{ hash_map::{self, Entry}, - AccountInfo, Address, Bytecode, HashMap, HashSet, StorageSlot, B256, KECCAK_EMPTY, U256, + AccountInfo, Bytecode, HashMap, HashSet, StorageSlot, B256, KECCAK_EMPTY, U256, }; use std::{ collections::{BTreeMap, BTreeSet}, vec::Vec, }; +use crate::primitives::ChainAddress; /// This builder is used to help to facilitate the initialization of `BundleState` struct #[derive(Debug)] pub struct BundleBuilder { - states: HashSet
, - state_original: HashMap, - state_present: HashMap, - state_storage: HashMap>, + states: HashSet, + state_original: HashMap, + state_present: HashMap, + state_storage: HashMap>, - reverts: BTreeSet<(u64, Address)>, + reverts: BTreeSet<(u64, ChainAddress)>, revert_range: RangeInclusive, - revert_account: HashMap<(u64, Address), Option>>, - revert_storage: HashMap<(u64, Address), Vec<(U256, U256)>>, + revert_account: HashMap<(u64, ChainAddress), Option>>, + revert_storage: HashMap<(u64, ChainAddress), Vec<(U256, U256)>>, contracts: HashMap, } @@ -79,27 +80,27 @@ impl BundleBuilder { } /// Collect address info of BundleState state - pub fn state_address(mut self, address: Address) -> Self { + pub fn state_address(mut self, address: ChainAddress) -> Self { self.states.insert(address); self } /// Collect account info of BundleState state - pub fn state_original_account_info(mut self, address: Address, original: AccountInfo) -> Self { + pub fn state_original_account_info(mut self, address: ChainAddress, original: AccountInfo) -> Self { self.states.insert(address); self.state_original.insert(address, original); self } /// Collect account info of BundleState state - pub fn state_present_account_info(mut self, address: Address, present: AccountInfo) -> Self { + pub fn state_present_account_info(mut self, address: ChainAddress, present: AccountInfo) -> Self { self.states.insert(address); self.state_present.insert(address, present); self } /// Collect storage info of BundleState state - pub fn state_storage(mut self, address: Address, storage: HashMap) -> Self { + pub fn state_storage(mut self, address: ChainAddress, storage: HashMap) -> Self { self.states.insert(address); self.state_storage.insert(address, storage); self @@ -109,7 +110,7 @@ impl BundleBuilder { /// /// `block_number` must respect `revert_range`, or the input /// will be ignored during the final build process - pub fn revert_address(mut self, block_number: u64, address: Address) -> Self { + pub fn revert_address(mut self, block_number: u64, address: ChainAddress) -> Self { self.reverts.insert((block_number, address)); self } @@ -121,7 +122,7 @@ impl BundleBuilder { pub fn revert_account_info( mut self, block_number: u64, - address: Address, + address: ChainAddress, account: Option>, ) -> Self { self.reverts.insert((block_number, address)); @@ -136,7 +137,7 @@ impl BundleBuilder { pub fn revert_storage( mut self, block_number: u64, - address: Address, + address: ChainAddress, storage: Vec<(U256, U256)>, ) -> Self { self.reverts.insert((block_number, address)); @@ -229,7 +230,7 @@ impl BundleBuilder { } /// Getter for `states` field - pub fn get_states(&self) -> &HashSet
{ + pub fn get_states(&self) -> &HashSet { &self.states } } @@ -260,7 +261,7 @@ impl BundleRetention { #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct BundleState { /// Account state. - pub state: HashMap, + pub state: HashMap, /// All created contracts in this block. pub contracts: HashMap, /// Changes to revert. @@ -284,7 +285,7 @@ impl BundleState { pub fn new( state: impl IntoIterator< Item = ( - Address, + ChainAddress, Option, Option, HashMap, @@ -293,7 +294,7 @@ impl BundleState { reverts: impl IntoIterator< Item = impl IntoIterator< Item = ( - Address, + ChainAddress, Option>, impl IntoIterator, ), @@ -366,7 +367,7 @@ impl BundleState { } /// Return reference to the state. - pub fn state(&self) -> &HashMap { + pub fn state(&self) -> &HashMap { &self.state } @@ -381,7 +382,7 @@ impl BundleState { } /// Get account from state - pub fn account(&self, address: &Address) -> Option<&BundleAccount> { + pub fn account(&self, address: &ChainAddress) -> Option<&BundleAccount> { self.state.get(address) } @@ -698,11 +699,13 @@ mod tests { use super::*; use crate::{db::StorageWithOriginalValues, TransitionAccount}; use revm_interpreter::primitives::KECCAK_EMPTY; + use revm_precompile::Address; #[test] fn transition_states() { // dummy data - let address = Address::new([0x01; 20]); + let chain_id = 0; + let address = ChainAddress(chain_id, Address::new([0x01; 20])); let acc1 = AccountInfo { balance: U256::from(10), nonce: 1, @@ -730,12 +733,12 @@ mod tests { ); } - const fn account1() -> Address { - Address::new([0x60; 20]) + const fn account1() -> ChainAddress { + ChainAddress(0, Address::new([0x60; 20])) } - const fn account2() -> Address { - Address::new([0x61; 20]) + const fn account2() -> ChainAddress { + ChainAddress(0, Address::new([0x61; 20])) } fn slot1() -> U256 { diff --git a/crates/revm/src/db/states/cache.rs b/crates/revm/src/db/states/cache.rs index eb1c4577ae..d75f0fd525 100644 --- a/crates/revm/src/db/states/cache.rs +++ b/crates/revm/src/db/states/cache.rs @@ -2,9 +2,10 @@ use super::{ plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount, }; use revm_interpreter::primitives::{ - Account, AccountInfo, Address, Bytecode, HashMap, State as EVMState, B256, + Account, AccountInfo, Bytecode, HashMap, State as EVMState, B256, }; use std::vec::Vec; +use crate::primitives::ChainAddress; /// Cache state contains both modified and original values. /// @@ -15,7 +16,7 @@ use std::vec::Vec; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CacheState { /// Block state account with account state - pub accounts: HashMap, + pub accounts: HashMap, /// created contracts /// TODO add bytecode counter for number of bytecodes added/removed. pub contracts: HashMap, @@ -47,7 +48,7 @@ impl CacheState { /// Helper function that returns all accounts. /// /// Used inside tests to generate merkle tree. - pub fn trie_account(&self) -> impl IntoIterator { + pub fn trie_account(&self) -> impl IntoIterator { self.accounts.iter().filter_map(|(address, account)| { account .account @@ -57,13 +58,13 @@ impl CacheState { } /// Insert not existing account. - pub fn insert_not_existing(&mut self, address: Address) { + pub fn insert_not_existing(&mut self, address: ChainAddress) { self.accounts .insert(address, CacheAccount::new_loaded_not_existing()); } /// Insert Loaded (Or LoadedEmptyEip161 if account is empty) account. - pub fn insert_account(&mut self, address: Address, info: AccountInfo) { + pub fn insert_account(&mut self, address: ChainAddress, info: AccountInfo) { let account = if !info.is_empty() { CacheAccount::new_loaded(info, HashMap::default()) } else { @@ -75,7 +76,7 @@ impl CacheState { /// Similar to `insert_account` but with storage. pub fn insert_account_with_storage( &mut self, - address: Address, + address: ChainAddress, info: AccountInfo, storage: PlainStorage, ) { @@ -88,7 +89,7 @@ impl CacheState { } /// Apply output of revm execution and create account transitions that are used to build BundleState. - pub fn apply_evm_state(&mut self, evm_state: EVMState) -> Vec<(Address, TransitionAccount)> { + pub fn apply_evm_state(&mut self, evm_state: EVMState) -> Vec<(ChainAddress, TransitionAccount)> { let mut transitions = Vec::with_capacity(evm_state.len()); for (address, account) in evm_state { if let Some(transition) = self.apply_account_state(address, account) { @@ -102,7 +103,7 @@ impl CacheState { /// Returns account transition if applicable. fn apply_account_state( &mut self, - address: Address, + address: ChainAddress, account: Account, ) -> Option { // not touched account are never changed. diff --git a/crates/revm/src/db/states/changes.rs b/crates/revm/src/db/states/changes.rs index 555ba1e015..f7c451dde2 100644 --- a/crates/revm/src/db/states/changes.rs +++ b/crates/revm/src/db/states/changes.rs @@ -1,5 +1,5 @@ use super::RevertToSlot; -use revm_interpreter::primitives::{AccountInfo, Address, Bytecode, B256, U256}; +use revm_interpreter::primitives::{AccountInfo, Address, Bytecode, ChainAddress, B256, U256}; use std::vec::Vec; /// accounts/storages/contracts for inclusion into database. @@ -11,7 +11,7 @@ use std::vec::Vec; #[derive(Clone, Debug, Default)] pub struct StateChangeset { /// Vector of **not** sorted accounts information. - pub accounts: Vec<(Address, Option)>, + pub accounts: Vec<(ChainAddress, Option)>, /// Vector of **not** sorted storage. pub storage: Vec, /// Vector of contracts by bytecode hash. **not** sorted. @@ -23,7 +23,7 @@ pub struct StateChangeset { #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct PlainStorageChangeset { /// Address of account - pub address: Address, + pub address: ChainAddress, /// Wipe storage, pub wipe_storage: bool, /// Storage key value pairs. @@ -34,7 +34,7 @@ pub struct PlainStorageChangeset { #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct PlainStorageRevert { /// Address of account - pub address: Address, + pub address: ChainAddress, /// Is storage wiped in this revert. Wiped flag is set on /// first known selfdestruct and would require clearing the /// state of this storage from database (And moving it to revert). @@ -52,7 +52,7 @@ pub struct PlainStateReverts { /// Vector of account with removed contracts bytecode /// /// Note: If AccountInfo is None means that account needs to be removed. - pub accounts: Vec)>>, + pub accounts: Vec)>>, /// Vector of storage with its address. pub storage: Vec>, } @@ -68,4 +68,4 @@ impl PlainStateReverts { } /// Storage reverts -pub type StorageRevert = Vec)>>; +pub type StorageRevert = Vec)>>; diff --git a/crates/revm/src/db/states/reverts.rs b/crates/revm/src/db/states/reverts.rs index 5be2c932fd..31684274f2 100644 --- a/crates/revm/src/db/states/reverts.rs +++ b/crates/revm/src/db/states/reverts.rs @@ -4,14 +4,15 @@ use super::{ }; use core::ops::{Deref, DerefMut}; use revm_interpreter::primitives::{AccountInfo, Address, HashMap, U256}; +use crate::primitives::ChainAddress; use std::vec::Vec; /// Contains reverts of multiple account in multiple transitions (Transitions as a block). #[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct Reverts(Vec>); +pub struct Reverts(Vec>); impl Deref for Reverts { - type Target = Vec>; + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.0 @@ -26,7 +27,7 @@ impl DerefMut for Reverts { impl Reverts { /// Create new reverts - pub fn new(reverts: Vec>) -> Self { + pub fn new(reverts: Vec>) -> Self { Self(reverts) } diff --git a/crates/revm/src/db/states/state.rs b/crates/revm/src/db/states/state.rs index 22a937c5b8..7c2408377f 100644 --- a/crates/revm/src/db/states/state.rs +++ b/crates/revm/src/db/states/state.rs @@ -5,13 +5,14 @@ use super::{ use crate::db::EmptyDB; use revm_interpreter::primitives::{ db::{Database, DatabaseCommit}, - hash_map, Account, AccountInfo, Address, Bytecode, HashMap, B256, BLOCK_HASH_HISTORY, U256, + hash_map, Account, AccountInfo, Bytecode, HashMap, B256, BLOCK_HASH_HISTORY, U256, }; use std::{ boxed::Box, collections::{btree_map, BTreeMap}, vec::Vec, }; +use crate::primitives::ChainAddress; /// Database boxed with a lifetime and Send. pub type DBBox<'a, E> = Box + Send + 'a>; @@ -83,7 +84,7 @@ impl State { /// balances must be filtered out before calling this function. pub fn increment_balances( &mut self, - balances: impl IntoIterator, + balances: impl IntoIterator, ) -> Result<(), DB::Error> { // make transition and update cache state let mut transitions = Vec::new(); @@ -111,7 +112,7 @@ impl State { /// It is used for DAO hardfork state change to move values from given accounts. pub fn drain_balances( &mut self, - addresses: impl IntoIterator, + addresses: impl IntoIterator, ) -> Result, DB::Error> { // make transition and update cache state let mut transitions = Vec::new(); @@ -134,17 +135,17 @@ impl State { self.cache.set_state_clear_flag(has_state_clear); } - pub fn insert_not_existing(&mut self, address: Address) { + pub fn insert_not_existing(&mut self, address: ChainAddress) { self.cache.insert_not_existing(address) } - pub fn insert_account(&mut self, address: Address, info: AccountInfo) { + pub fn insert_account(&mut self, address: ChainAddress, info: AccountInfo) { self.cache.insert_account(address, info) } pub fn insert_account_with_storage( &mut self, - address: Address, + address: ChainAddress, info: AccountInfo, storage: PlainStorage, ) { @@ -153,7 +154,7 @@ impl State { } /// Apply evm transitions to transition state. - pub fn apply_transition(&mut self, transitions: Vec<(Address, TransitionAccount)>) { + pub fn apply_transition(&mut self, transitions: Vec<(ChainAddress, TransitionAccount)>) { // add transition to transition state. if let Some(s) = self.transition_state.as_mut() { s.add_transitions(transitions) @@ -171,7 +172,7 @@ impl State { } } - pub fn load_cache_account(&mut self, address: Address) -> Result<&mut CacheAccount, DB::Error> { + pub fn load_cache_account(&mut self, address: ChainAddress) -> Result<&mut CacheAccount, DB::Error> { match self.cache.accounts.entry(address) { hash_map::Entry::Vacant(entry) => { if self.use_preloaded_bundle { @@ -215,11 +216,11 @@ impl State { impl Database for State { type Error = DB::Error; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: ChainAddress) -> Result, Self::Error> { self.load_cache_account(address).map(|a| a.account_info()) } - fn code_by_hash(&mut self, code_hash: B256) -> Result { + fn code_by_hash(&mut self, chain_id: u64, code_hash: B256) -> Result { let res = match self.cache.contracts.entry(code_hash) { hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()), hash_map::Entry::Vacant(entry) => { @@ -230,7 +231,7 @@ impl Database for State { } } // if not found in bundle ask database - let code = self.database.code_by_hash(code_hash)?; + let code = self.database.code_by_hash(chain_id, code_hash)?; entry.insert(code.clone()); Ok(code) } @@ -238,7 +239,7 @@ impl Database for State { res } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: ChainAddress, index: U256) -> Result { // Account is guaranteed to be loaded. // Note that storage from bundle is already loaded with account. if let Some(account) = self.cache.accounts.get_mut(&address) { @@ -268,13 +269,13 @@ impl Database for State { } } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, chain_id: u64, number: U256) -> Result { // block number is never bigger then u64::MAX. let u64num: u64 = number.to(); match self.block_hashes.entry(u64num) { btree_map::Entry::Occupied(entry) => Ok(*entry.get()), btree_map::Entry::Vacant(entry) => { - let ret = *entry.insert(self.database.block_hash(number)?); + let ret = *entry.insert(self.database.block_hash(chain_id, number)?); // prune all hashes that are older then BLOCK_HASH_HISTORY while let Some(entry) = self.block_hashes.first_entry() { @@ -292,7 +293,7 @@ impl Database for State { } impl DatabaseCommit for State { - fn commit(&mut self, evm_state: HashMap) { + fn commit(&mut self, evm_state: HashMap) { let transitions = self.cache.apply_evm_state(evm_state); self.apply_transition(transitions); } @@ -306,12 +307,14 @@ mod tests { RevertToSlot, }; use revm_interpreter::primitives::{keccak256, StorageSlot}; + use revm_precompile::Address; #[test] fn block_hash_cache() { + let chain_id = 0; let mut state = State::builder().build(); - state.block_hash(U256::from(1)).unwrap(); - state.block_hash(U256::from(2)).unwrap(); + state.block_hash(chain_id, U256::from(1)).unwrap(); + state.block_hash(chain_id, U256::from(2)).unwrap(); let test_number = BLOCK_HASH_HISTORY as u64 + 2; @@ -324,7 +327,7 @@ mod tests { BTreeMap::from([(1, block1_hash), (2, block2_hash)]) ); - state.block_hash(U256::from(test_number)).unwrap(); + state.block_hash(chain_id, U256::from(test_number)).unwrap(); assert_eq!( state.block_hashes, BTreeMap::from([(test_number, block_test_hash), (2, block2_hash)]) @@ -339,13 +342,14 @@ mod tests { /// state of the account before the block. #[test] fn reverts_preserve_old_values() { + let chain_id = 0; let mut state = State::builder().with_bundle_update().build(); let (slot1, slot2, slot3) = (U256::from(1), U256::from(2), U256::from(3)); // Non-existing account for testing account state transitions. // [LoadedNotExisting] -> [Changed] (nonce: 1, balance: 1) -> [Changed] (nonce: 2) -> [Changed] (nonce: 3) - let new_account_address = Address::from_slice(&[0x1; 20]); + let new_account_address = ChainAddress(chain_id, Address::from_slice(&[0x1; 20])); let new_account_created_info = AccountInfo { nonce: 1, balance: U256::from(1), @@ -361,7 +365,7 @@ mod tests { }; // Existing account for testing storage state transitions. - let existing_account_address = Address::from_slice(&[0x2; 20]); + let existing_account_address = ChainAddress(chain_id, Address::from_slice(&[0x2; 20])); let existing_account_initial_info = AccountInfo { nonce: 1, ..Default::default() @@ -563,10 +567,11 @@ mod tests { /// block and reverted to their previous state do not appear in the reverts. #[test] fn bundle_scoped_reverts_collapse() { + let chain_id = 0; let mut state = State::builder().with_bundle_update().build(); // Non-existing account. - let new_account_address = Address::from_slice(&[0x1; 20]); + let new_account_address = ChainAddress(chain_id, Address::from_slice(&[0x1; 20])); let new_account_created_info = AccountInfo { nonce: 1, balance: U256::from(1), @@ -574,7 +579,7 @@ mod tests { }; // Existing account. - let existing_account_address = Address::from_slice(&[0x2; 20]); + let existing_account_address = ChainAddress(chain_id, Address::from_slice(&[0x2; 20])); let existing_account_initial_info = AccountInfo { nonce: 1, ..Default::default() @@ -587,7 +592,7 @@ mod tests { // Existing account with storage. let (slot1, slot2) = (U256::from(1), U256::from(2)); - let existing_account_with_storage_address = Address::from_slice(&[0x3; 20]); + let existing_account_with_storage_address = ChainAddress(chain_id, Address::from_slice(&[0x3; 20])); let existing_account_with_storage_info = AccountInfo { nonce: 1, ..Default::default() @@ -687,10 +692,11 @@ mod tests { /// Checks that the behavior of selfdestruct within the block is correct. #[test] fn selfdestruct_state_and_reverts() { + let chain_id = 0; let mut state = State::builder().with_bundle_update().build(); // Existing account. - let existing_account_address = Address::from_slice(&[0x1; 20]); + let existing_account_address = ChainAddress(chain_id, Address::from_slice(&[0x1; 20])); let existing_account_info = AccountInfo { nonce: 1, ..Default::default() diff --git a/crates/revm/src/db/states/transition_state.rs b/crates/revm/src/db/states/transition_state.rs index 4e3ba16847..68145b7fb7 100644 --- a/crates/revm/src/db/states/transition_state.rs +++ b/crates/revm/src/db/states/transition_state.rs @@ -1,16 +1,17 @@ use super::TransitionAccount; use revm_interpreter::primitives::{hash_map::Entry, Address, HashMap}; use std::vec::Vec; +use crate::primitives::ChainAddress; #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct TransitionState { /// Block state account with account state - pub transitions: HashMap, + pub transitions: HashMap, } impl TransitionState { /// Create new transition state with one transition. - pub fn single(address: Address, transition: TransitionAccount) -> Self { + pub fn single(address: ChainAddress, transition: TransitionAccount) -> Self { let mut transitions = HashMap::new(); transitions.insert(address, transition); TransitionState { transitions } @@ -21,7 +22,7 @@ impl TransitionState { core::mem::take(self) } - pub fn add_transitions(&mut self, transitions: Vec<(Address, TransitionAccount)>) { + pub fn add_transitions(&mut self, transitions: Vec<(ChainAddress, TransitionAccount)>) { for (address, account) in transitions { match self.transitions.entry(address) { Entry::Occupied(entry) => { diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index d9307cccba..4a87be0049 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -7,7 +7,7 @@ use crate::{ SelfDestructResult, SharedMemory, }, primitives::{ - specification::SpecId, Address, BlockEnv, Bytecode, CfgEnv, EVMError, EVMResult, Env, + specification::SpecId, ChainAddress, BlockEnv, Bytecode, CfgEnv, EVMError, EVMResult, Env, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, Log, ResultAndState, TransactTo, TxEnv, B256, U256, }, @@ -338,7 +338,7 @@ impl Evm<'_, EXT, DB> { // load precompiles let precompiles = pre_exec.load_precompiles(); - ctx.evm.set_precompiles(precompiles); + ctx.evm.set_precompiles(ctx.evm.env.cfg.chain_id, precompiles); // deduce caller balance with its limit. pre_exec.deduct_caller(ctx)?; @@ -389,15 +389,15 @@ impl Host for Evm<'_, EXT, DB> { &self.context.evm.env } - fn block_hash(&mut self, number: U256) -> Option { + fn block_hash(&mut self, chain_id: u64, number: U256) -> Option { self.context .evm - .block_hash(number) + .block_hash(chain_id, number) .map_err(|e| self.context.evm.error = Err(e)) .ok() } - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + fn load_account(&mut self, address: ChainAddress) -> Option<(bool, bool)> { self.context .evm .load_account_exist(address) @@ -405,7 +405,7 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + fn balance(&mut self, address: ChainAddress) -> Option<(U256, bool)> { self.context .evm .balance(address) @@ -413,7 +413,7 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { + fn code(&mut self, address: ChainAddress) -> Option<(Bytecode, bool)> { self.context .evm .code(address) @@ -421,7 +421,7 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + fn code_hash(&mut self, address: ChainAddress) -> Option<(B256, bool)> { self.context .evm .code_hash(address) @@ -429,7 +429,7 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + fn sload(&mut self, address: ChainAddress, index: U256) -> Option<(U256, bool)> { self.context .evm .sload(address, index) @@ -437,7 +437,7 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn sstore(&mut self, address: Address, index: U256, value: U256) -> Option { + fn sstore(&mut self, address: ChainAddress, index: U256, value: U256) -> Option { self.context .evm .sstore(address, index, value) @@ -445,11 +445,11 @@ impl Host for Evm<'_, EXT, DB> { .ok() } - fn tload(&mut self, address: Address, index: U256) -> U256 { + fn tload(&mut self, address: ChainAddress, index: U256) -> U256 { self.context.evm.tload(address, index) } - fn tstore(&mut self, address: Address, index: U256, value: U256) { + fn tstore(&mut self, address: ChainAddress, index: U256, value: U256) { self.context.evm.tstore(address, index, value) } @@ -457,7 +457,7 @@ impl Host for Evm<'_, EXT, DB> { self.context.evm.journaled_state.log(log); } - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { + fn selfdestruct(&mut self, address: ChainAddress, target: ChainAddress) -> Option { self.context .evm .inner diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index dbbd1876c9..d6e847f7d9 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -1,6 +1,6 @@ use crate::{ interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::{db::Database, Address, Log, U256}, + primitives::{db::Database, Address, ChainAddress, Log, U256}, EvmContext, }; use auto_impl::auto_impl; @@ -137,7 +137,7 @@ pub trait Inspector { /// Called when a contract has been self-destructed with funds transferred to target. #[inline] - fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + fn selfdestruct(&mut self, contract: ChainAddress, target: ChainAddress, value: U256) { let _ = contract; let _ = target; let _ = value; diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 5e8c948b2e..68b4b5e059 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -7,7 +7,7 @@ use revm_interpreter::CreateOutcome; use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter}, - primitives::{Address, U256}, + primitives::{Address, ChainAddress, U256}, Database, EvmContext, Inspector, }; @@ -101,7 +101,7 @@ impl Inspector for CustomPrintTracer { None } - fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + fn selfdestruct(&mut self, contract: ChainAddress, target: ChainAddress, value: U256) { println!( "SELFDESTRUCT: contract: {:?}, refund target: {:?}, value {:?}", contract, target, value @@ -114,13 +114,14 @@ mod test { use crate::{ inspector_handle_register, inspectors::CustomPrintTracer, - primitives::{address, bytes, SpecId}, + primitives::{address, bytes, SpecId, ChainAddress}, Evm, InMemoryDB, }; #[test] fn gas_calculation_underflow() { - let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); + let chain_id = 0; + let callee = ChainAddress(chain_id, address!("5fdcca53617f4d2b9134b29090c87d01058e27e9")); // https://github.com/bluealloy/revm/issues/277 // checks this use case @@ -137,7 +138,7 @@ mod test { db.insert_account_info(callee, info); }) .modify_tx_env(|tx| { - tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); + tx.caller = ChainAddress(chain_id, address!("5fdcca53617f4d2b9134b29090c87d01058e27e0")); tx.transact_to = crate::primitives::TransactTo::Call(callee); tx.data = crate::primitives::Bytes::new(); tx.value = crate::primitives::U256::ZERO; diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index d22ae0451e..7d67b793bf 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -80,7 +80,7 @@ mod tests { use crate::{ inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::Log, + primitives::{Log, ChainAddress}, Database, EvmContext, Inspector, }; @@ -179,9 +179,9 @@ mod tests { .with_external_context(StackInspector::default()) .modify_tx_env(|tx| { tx.clear(); - tx.caller = address!("1000000000000000000000000000000000000000"); + tx.caller = ChainAddress(0, address!("1000000000000000000000000000000000000000")); tx.transact_to = - TransactTo::Call(address!("0000000000000000000000000000000000000000")); + TransactTo::Call(ChainAddress(0, address!("0000000000000000000000000000000000000000"))); tx.gas_limit = 21100; }) .append_handler_register(inspector_handle_register) diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 9fb0e4cab6..05cade873b 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -7,6 +7,7 @@ use core::mem; use revm_interpreter::primitives::SpecId; use revm_interpreter::SStoreResult; use std::vec::Vec; +use crate::primitives::ChainAddress; /// JournalState is internal EVM state that is used to contain state and track changes to that state. /// It contains journal of changes that happened to state so that they can be reverted. @@ -33,7 +34,7 @@ pub struct JournaledState { /// /// Note that this not include newly loaded accounts, account and storage /// is considered warm if it is found in the `State`. - pub warm_preloaded_addresses: HashSet
, + pub warm_preloaded_addresses: HashSet, } impl JournaledState { @@ -48,7 +49,7 @@ impl JournaledState { /// # Note /// /// - pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet
) -> JournaledState { + pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet) -> JournaledState { Self { state: HashMap::new(), transient_storage: TransientStorage::default(), @@ -76,7 +77,7 @@ impl JournaledState { /// This is especially important for state clear where touched empty accounts needs to /// be removed from state. #[inline] - pub fn touch(&mut self, address: &Address) { + pub fn touch(&mut self, address: &ChainAddress) { if let Some(account) = self.state.get_mut(address) { Self::touch_account(self.journal.last_mut().unwrap(), address, account); } @@ -84,7 +85,7 @@ impl JournaledState { /// Mark account as touched. #[inline] - fn touch_account(journal: &mut Vec, address: &Address, account: &mut Account) { + fn touch_account(journal: &mut Vec, address: &ChainAddress, account: &mut Account) { if !account.is_touched() { journal.push(JournalEntry::AccountTouched { address: *address }); account.mark_touch(); @@ -124,7 +125,7 @@ impl JournaledState { /// /// Panics if the account has not been loaded and is missing from the state set. #[inline] - pub fn account(&self, address: Address) -> &Account { + pub fn account(&self, address: ChainAddress) -> &Account { self.state .get(&address) .expect("Account expected to be loaded") // Always assume that acc is already loaded @@ -139,7 +140,7 @@ impl JournaledState { /// use it only if you know that acc is warm /// Assume account is warm #[inline] - pub fn set_code(&mut self, address: Address, code: Bytecode) { + pub fn set_code(&mut self, address: ChainAddress, code: Bytecode) { let account = self.state.get_mut(&address).unwrap(); Self::touch_account(self.journal.last_mut().unwrap(), &address, account); @@ -153,7 +154,7 @@ impl JournaledState { } #[inline] - pub fn inc_nonce(&mut self, address: Address) -> Option { + pub fn inc_nonce(&mut self, address: ChainAddress) -> Option { let account = self.state.get_mut(&address).unwrap(); // Check if nonce is going to overflow. if account.info.nonce == u64::MAX { @@ -174,8 +175,8 @@ impl JournaledState { #[inline] pub fn transfer( &mut self, - from: &Address, - to: &Address, + from: &ChainAddress, + to: &ChainAddress, balance: U256, db: &mut DB, ) -> Result, EVMError> { @@ -233,8 +234,8 @@ impl JournaledState { #[inline] pub fn create_account_checkpoint( &mut self, - caller: Address, - address: Address, + caller: ChainAddress, + address: ChainAddress, balance: U256, spec_id: SpecId, ) -> Result { @@ -319,7 +320,7 @@ impl JournaledState { state.remove(&address); } JournalEntry::AccountTouched { address } => { - if is_spurious_dragon_enabled && address == PRECOMPILE3 { + if is_spurious_dragon_enabled && address.1 == PRECOMPILE3 { continue; } // remove touched status @@ -456,8 +457,8 @@ impl JournaledState { #[inline] pub fn selfdestruct( &mut self, - address: Address, - target: Address, + address: ChainAddress, + target: ChainAddress, db: &mut DB, ) -> Result> { let (is_cold, target_exists) = self.load_account_exist(target, db)?; @@ -518,7 +519,7 @@ impl JournaledState { #[inline] pub fn initial_account_load( &mut self, - address: Address, + address: ChainAddress, slots: &[U256], db: &mut DB, ) -> Result<&mut Account, EVMError> { @@ -546,7 +547,7 @@ impl JournaledState { #[inline] pub fn load_account( &mut self, - address: Address, + address: ChainAddress, db: &mut DB, ) -> Result<(&mut Account, bool), EVMError> { Ok(match self.state.entry(address) { @@ -579,7 +580,7 @@ impl JournaledState { #[inline] pub fn load_account_exist( &mut self, - address: Address, + address: ChainAddress, db: &mut DB, ) -> Result<(bool, bool), EVMError> { let spec = self.spec; @@ -600,7 +601,7 @@ impl JournaledState { #[inline] pub fn load_code( &mut self, - address: Address, + address: ChainAddress, db: &mut DB, ) -> Result<(&mut Account, bool), EVMError> { let (acc, is_cold) = self.load_account(address, db)?; @@ -610,7 +611,7 @@ impl JournaledState { acc.info.code = Some(empty); } else { let code = db - .code_by_hash(acc.info.code_hash) + .code_by_hash(address.0, acc.info.code_hash) .map_err(EVMError::Database)?; acc.info.code = Some(code); } @@ -626,7 +627,7 @@ impl JournaledState { #[inline] pub fn sload( &mut self, - address: Address, + address: ChainAddress, key: U256, db: &mut DB, ) -> Result<(U256, bool), EVMError> { @@ -670,7 +671,7 @@ impl JournaledState { #[inline] pub fn sstore( &mut self, - address: Address, + address: ChainAddress, key: U256, new: U256, db: &mut DB, @@ -714,7 +715,7 @@ impl JournaledState { /// /// EIP-1153: Transient storage opcodes #[inline] - pub fn tload(&mut self, address: Address, key: U256) -> U256 { + pub fn tload(&mut self, address: ChainAddress, key: U256) -> U256 { self.transient_storage .get(&(address, key)) .copied() @@ -728,7 +729,7 @@ impl JournaledState { /// /// EIP-1153: Transient storage opcodes #[inline] - pub fn tstore(&mut self, address: Address, key: U256, new: U256) { + pub fn tstore(&mut self, address: ChainAddress, key: U256, new: U256) { let had_value = if new == U256::ZERO { // if new values is zero, remove entry from transient storage. // if previous values was some insert it inside journal. @@ -777,13 +778,13 @@ pub enum JournalEntry { /// Used to mark account that is warm inside EVM in regards to EIP-2929 AccessList. /// Action: We will add Account to state. /// Revert: we will remove account from state. - AccountLoaded { address: Address }, + AccountLoaded { address: ChainAddress }, /// Mark account to be destroyed and journal balance to be reverted /// Action: Mark account and transfer the balance /// Revert: Unmark the account and transfer balance back AccountDestroyed { - address: Address, - target: Address, + address: ChainAddress, + target: ChainAddress, was_destroyed: bool, // if account had already been destroyed before this journal entry had_balance: U256, }, @@ -791,31 +792,31 @@ pub enum JournalEntry { /// Only when account is called (to execute contract or transfer balance) only then account is made touched. /// Action: Mark account touched /// Revert: Unmark account touched - AccountTouched { address: Address }, + AccountTouched { address: ChainAddress }, /// Transfer balance between two accounts /// Action: Transfer balance /// Revert: Transfer balance back BalanceTransfer { - from: Address, - to: Address, + from: ChainAddress, + to: ChainAddress, balance: U256, }, /// Increment nonce /// Action: Increment nonce by one /// Revert: Decrement nonce by one NonceChange { - address: Address, //geth has nonce value, + address: ChainAddress, //geth has nonce value, }, /// Create account: /// Actions: Mark account as created /// Revert: Unmart account as created and reset nonce to zero. - AccountCreated { address: Address }, + AccountCreated { address: ChainAddress }, /// It is used to track both storage change and warm load of storage slot. For warm load in regard /// to EIP-2929 AccessList had_value will be None /// Action: Storage change or warm load /// Revert: Revert to previous value or remove slot from storage StorageChange { - address: Address, + address: ChainAddress, key: U256, had_value: Option, //if none, storage slot was cold loaded from db and needs to be removed }, @@ -823,14 +824,14 @@ pub enum JournalEntry { /// Action: Transient storage changed. /// Revert: Revert to previous value. TransientStorageChange { - address: Address, + address: ChainAddress, key: U256, had_value: U256, }, /// Code changed /// Action: Account code changed /// Revert: Revert to previous bytecode. - CodeChange { address: Address }, + CodeChange { address: ChainAddress }, } /// SubRoutine checkpoint that will help us to go back from this diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs index 9086467720..7c49c8b68b 100644 --- a/examples/fork_ref_transact.rs +++ b/examples/fork_ref_transact.rs @@ -3,13 +3,14 @@ use ethers_core::abi::parse_abi; use ethers_providers::{Http, Provider}; use revm::{ db::{CacheDB, EmptyDB, EthersDB}, - primitives::{address, ExecutionResult, Output, TransactTo, U256}, + primitives::{address, ExecutionResult, Output, TransactTo, U256, ChainAddress}, Database, Evm, }; use std::sync::Arc; #[tokio::main] async fn main() -> anyhow::Result<()> { + let chain_id = 1; // create ethers client and wrap it in Arc let client = Provider::::try_from( "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", @@ -32,7 +33,7 @@ async fn main() -> anyhow::Result<()> { let slot = U256::from(8); // ETH/USDT pair on Uniswap V2 - let pool_address = address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852"); + let pool_address = ChainAddress(chain_id, address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852")); // generate abi for the calldata from the human readable interface let abi = BaseContract::from( @@ -70,7 +71,7 @@ async fn main() -> anyhow::Result<()> { .modify_tx_env(|tx| { // fill in missing bits of env struct // change that to whatever caller you want to be - tx.caller = address!("0000000000000000000000000000000000000000"); + tx.caller = ChainAddress(chain_id, address!("0000000000000000000000000000000000000000")); // account you want to transact with tx.transact_to = TransactTo::Call(pool_address); // calldata formed via abigen