From 0aa19ffc32af54c502df6887b5a4e14356a1fff0 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Fri, 10 May 2024 16:20:03 +0200 Subject: [PATCH 1/7] EIP 6780 --- interpreter/src/eval/system.rs | 31 +++++++++++++++++++++---------- interpreter/src/runtime.rs | 4 ++++ src/backend/overlayed.rs | 23 +++++++++++++++++++++++ src/standard/invoker/routines.rs | 1 + 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index 88256632..5a4c33df 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -337,16 +337,27 @@ pub fn suicide, H: RuntimeEnvironment + RuntimeBackend, T match machine.stack.perform_pop1_push0(|target| { let balance = handler.balance(address); - - handler.transfer(Transfer { - source: address, - target: (*target).into(), - value: balance, - })?; - - handler.mark_delete(address); - handler.reset_balance(address); - + let target = (*target).into(); + + // Cancun EIP-6780 only allow contract deletion within the same transaction that created it + if handler.created(address) { + handler.transfer(Transfer { + source: address, + target, + value: balance, + })?; + + handler.mark_delete(address); + handler.reset_balance(address); + } else { + if address != target { + handler.transfer(Transfer { + source: address, + target, + value: balance, + })?; + } + } Ok(((), ())) }) { Ok(()) => Control::Exit(ExitSucceed::Suicided.into()), diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index 7dbce8a4..fe6ef46c 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -128,6 +128,8 @@ pub trait RuntimeBackend: RuntimeBaseBackend { fn original_storage(&self, address: H160, index: H256) -> H256; /// Check whether an address has already been deleted. fn deleted(&self, address: H160) -> bool; + /// Check whether an address has already been created in the transaction. + fn created(&self, address: H160) -> bool; /// Checks if the address or (address, index) pair has been previously accessed. fn is_cold(&self, address: H160, index: Option) -> bool; fn is_hot(&self, address: H160, index: Option) -> bool { @@ -142,6 +144,8 @@ pub trait RuntimeBackend: RuntimeBaseBackend { fn log(&mut self, log: Log) -> Result<(), ExitError>; /// Mark an address to be deleted. fn mark_delete(&mut self, address: H160); + // Mark an address that is being created in the transaction. + fn mark_create(&mut self, address: H160); /// Fully delete storages of an account. fn reset_storage(&mut self, address: H160); /// Set code of an account. diff --git a/src/backend/overlayed.rs b/src/backend/overlayed.rs index 7ffd43df..a9eeb119 100644 --- a/src/backend/overlayed.rs +++ b/src/backend/overlayed.rs @@ -137,6 +137,10 @@ impl RuntimeBackend for OverlayedBackend { self.backend.storage(address, index) } + fn created(&self, address: H160) -> bool { + self.substate.created(address) + } + fn deleted(&self, address: H160) -> bool { self.substate.deleted(address) } @@ -163,6 +167,10 @@ impl RuntimeBackend for OverlayedBackend { self.substate.deletes.insert(address); } + fn mark_create(&mut self, address: H160) { + self.substate.creates.insert(address); + } + fn reset_storage(&mut self, address: H160) { self.substate.storage_resets.insert(address); } @@ -243,6 +251,9 @@ impl TransactionalBackend for OverlayedBackend { for address in child.deletes { self.substate.deletes.insert(address); } + for address in child.creates { + self.substate.creates.insert(address); + } } MergeStrategy::Revert | MergeStrategy::Discard => {} } @@ -258,6 +269,7 @@ struct Substate { storage_resets: BTreeSet, storages: BTreeMap<(H160, H256), H256>, deletes: BTreeSet, + creates: BTreeSet, } impl Substate { @@ -271,6 +283,7 @@ impl Substate { storage_resets: Default::default(), storages: Default::default(), deletes: Default::default(), + creates: Default::default(), } } @@ -338,4 +351,14 @@ impl Substate { false } } + + pub fn created(&self, address: H160) -> bool { + if self.creates.contains(&address) { + true + } else if let Some(parent) = self.parent.as_ref() { + parent.created(address) + } else { + false + } + } } diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index fbc0ff8a..cd410142 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -68,6 +68,7 @@ where } handler.reset_storage(state.as_ref().context.address); + handler.mark_create(state.as_ref().context.address); resolver.resolve_create(init_code, state, handler) } From 47a68b38f51d49aaf7d62a6aba7430b02c8a7ddc Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Tue, 14 May 2024 08:43:51 +0200 Subject: [PATCH 2/7] add fork enum in the interpreter --- interpreter/src/eval/mod.rs | 10 +++++++--- interpreter/src/eval/system.rs | 25 +++++++++++++++++++++++++ interpreter/src/fork.rs | 11 +++++++++++ interpreter/src/lib.rs | 1 + interpreter/src/runtime.rs | 4 +++- src/standard/config.rs | 12 ++++++++++++ src/standard/invoker/mod.rs | 3 +++ 7 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 interpreter/src/fork.rs diff --git a/interpreter/src/eval/mod.rs b/interpreter/src/eval/mod.rs index ffd7aa29..ffce7118 100644 --- a/interpreter/src/eval/mod.rs +++ b/interpreter/src/eval/mod.rs @@ -6,8 +6,8 @@ mod misc; mod system; use crate::{ - trap::CallCreateTrap, Control, ExitException, ExitSucceed, GasState, Machine, Opcode, - RuntimeBackend, RuntimeEnvironment, RuntimeState, TrapConstruct, + fork::Fork, trap::CallCreateTrap, Control, ExitException, ExitSucceed, GasState, Machine, + Opcode, RuntimeBackend, RuntimeEnvironment, RuntimeState, TrapConstruct, }; use core::ops::{BitAnd, BitOr, BitXor}; use primitive_types::{H256, U256}; @@ -1251,7 +1251,11 @@ pub fn eval_suicide, H: RuntimeEnvironment + RuntimeBacke _opcode: Opcode, _position: usize, ) -> Control { - self::system::suicide(machine, handle) + if machine.state.as_ref().fork >= Fork::CANCUN { + self::system::suicide_eip_6780(machine, handle) + } else { + self::system::suicide(machine, handle) + } } pub fn eval_chainid, H: RuntimeEnvironment + RuntimeBackend, Tr>( diff --git a/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index 5a4c33df..cc425264 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -335,6 +335,31 @@ pub fn suicide, H: RuntimeEnvironment + RuntimeBackend, T ) -> Control { let address = machine.state.as_ref().context.address; + match machine.stack.perform_pop1_push0(|target| { + let balance = handler.balance(address); + + handler.transfer(Transfer { + source: address, + target: (*target).into(), + value: balance, + })?; + + handler.mark_delete(address); + handler.reset_balance(address); + + Ok(((), ())) + }) { + Ok(()) => Control::Exit(ExitSucceed::Suicided.into()), + Err(e) => Control::Exit(Err(e)), + } +} + +pub fn suicide_eip_6780, H: RuntimeEnvironment + RuntimeBackend, Tr>( + machine: &mut Machine, + handler: &mut H, +) -> Control { + let address = machine.state.as_ref().context.address; + match machine.stack.perform_pop1_push0(|target| { let balance = handler.balance(address); let target = (*target).into(); diff --git a/interpreter/src/fork.rs b/interpreter/src/fork.rs new file mode 100644 index 00000000..637faf2c --- /dev/null +++ b/interpreter/src/fork.rs @@ -0,0 +1,11 @@ +/// EVM supported forks +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Fork { + FRONTIER, + ISTANBUL, + BERLIN, + LONDON, + MERGE, + SHANGHAI, + CANCUN, +} diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index 0e6693be..eca8264a 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -9,6 +9,7 @@ extern crate alloc; mod error; mod etable; pub mod eval; +pub mod fork; pub mod interpreter; mod memory; mod opcode; diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index fe6ef46c..5dbd2315 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -1,4 +1,4 @@ -use crate::ExitError; +use crate::{fork::Fork, ExitError}; use alloc::rc::Rc; use alloc::vec::Vec; use primitive_types::{H160, H256, U256}; @@ -17,6 +17,8 @@ pub struct RuntimeState { pub transaction_context: Rc, /// Return data buffer. pub retbuf: Vec, + /// EVM Fork. + pub fork: Fork, } impl AsRef for RuntimeState { diff --git a/src/standard/config.rs b/src/standard/config.rs index 63300bab..bfc629d8 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -1,3 +1,5 @@ +use evm_interpreter::fork::{Fork, Fork::*}; + /// Runtime configuration. #[derive(Clone, Debug)] pub struct Config { @@ -97,6 +99,7 @@ pub struct Config { pub has_base_fee: bool, /// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md) pub has_push0: bool, + pub fork: Fork, } impl Config { @@ -150,6 +153,7 @@ impl Config { has_ext_code_hash: false, has_base_fee: false, has_push0: false, + fork: FRONTIER, } } @@ -203,6 +207,7 @@ impl Config { has_ext_code_hash: true, has_base_fee: false, has_push0: false, + fork: ISTANBUL, } } @@ -237,6 +242,7 @@ impl Config { disallow_executable_format, warm_coinbase_address, max_initcode_size, + fork, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -299,6 +305,7 @@ impl Config { has_ext_code_hash: true, has_base_fee, has_push0, + fork, } } } @@ -315,6 +322,7 @@ struct DerivedConfigInputs { disallow_executable_format: bool, warm_coinbase_address: bool, max_initcode_size: Option, + fork: Fork, } impl DerivedConfigInputs { @@ -329,6 +337,7 @@ impl DerivedConfigInputs { disallow_executable_format: false, warm_coinbase_address: false, max_initcode_size: None, + fork: BERLIN, } } @@ -343,6 +352,7 @@ impl DerivedConfigInputs { disallow_executable_format: true, warm_coinbase_address: false, max_initcode_size: None, + fork: LONDON, } } @@ -357,6 +367,7 @@ impl DerivedConfigInputs { disallow_executable_format: true, warm_coinbase_address: false, max_initcode_size: None, + fork: MERGE, } } @@ -372,6 +383,7 @@ impl DerivedConfigInputs { warm_coinbase_address: true, // 2 * 24576 as per EIP-3860 max_initcode_size: Some(0xC000), + fork: SHANGHAI, } } } diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index e7e6576e..755f2116 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -259,6 +259,7 @@ where context, transaction_context: Rc::new(transaction_context), retbuf: Vec::new(), + fork: self.config.fork, }; let work = || -> Result<(TransactInvoke, _), ExitError> { @@ -468,6 +469,7 @@ where context: call_trap_data.context.clone(), transaction_context, retbuf: Vec::new(), + fork: self.config.fork, }, gas_limit, is_static, @@ -502,6 +504,7 @@ where }, transaction_context, retbuf: Vec::new(), + fork: self.config.fork, }, gas_limit, is_static, From d3065f29136ec38f48fdf04dc255449050a61894 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Tue, 14 May 2024 09:06:58 +0200 Subject: [PATCH 3/7] fix clippy --- interpreter/src/eval/system.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index cc425264..bd991b53 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -374,14 +374,12 @@ pub fn suicide_eip_6780, H: RuntimeEnvironment + RuntimeB handler.mark_delete(address); handler.reset_balance(address); - } else { - if address != target { - handler.transfer(Transfer { - source: address, - target, - value: balance, - })?; - } + } else if address != target { + handler.transfer(Transfer { + source: address, + target, + value: balance, + })?; } Ok(((), ())) }) { From 3e45977fe88f3f7efb70bc9b32873da84062f62b Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Tue, 14 May 2024 09:13:16 +0200 Subject: [PATCH 4/7] clippy --- interpreter/tests/usability.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index 02421a98..d61c14f8 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,3 +1,4 @@ +use evm_interpreter::fork::Fork; use evm_interpreter::interpreter::{EtableInterpreter, RunInterpreter}; use evm_interpreter::{ trap::CallCreateTrap, Capture, Context, Control, Etable, ExitError, ExitSucceed, Log, Machine, @@ -126,6 +127,11 @@ impl RuntimeBackend for UnimplementedHandler { fn deleted(&self, _address: H160) -> bool { unimplemented!() } + + fn created(&self, _address: H160) -> bool { + unimplemented!() + } + fn is_cold(&self, _address: H160, _index: Option) -> bool { unimplemented!() } @@ -144,6 +150,10 @@ impl RuntimeBackend for UnimplementedHandler { unimplemented!() } + fn mark_create(&mut self, _address: H160) { + unimplemented!() + } + fn reset_storage(&mut self, _address: H160) { unimplemented!() } @@ -194,6 +204,7 @@ fn etable_runtime() { } .into(), retbuf: Vec::new(), + fork: Fork::FRONTIER }, ); let mut vm = EtableInterpreter::new(machine, &RUNTIME_ETABLE); From 54ffdeaf1217aeeaa493e7c1aae62166463a1d53 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Tue, 14 May 2024 09:20:04 +0200 Subject: [PATCH 5/7] fix format --- interpreter/tests/usability.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index d61c14f8..2e707123 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -204,7 +204,7 @@ fn etable_runtime() { } .into(), retbuf: Vec::new(), - fork: Fork::FRONTIER + fork: Fork::FRONTIER, }, ); let mut vm = EtableInterpreter::new(machine, &RUNTIME_ETABLE); From 7560c6b4609c8805a3abe90617f9ccd5f169935d Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Thu, 16 May 2024 15:08:09 +0200 Subject: [PATCH 6/7] add tests --- Cargo.toml | 3 + src/standard/config.rs | 19 ++ tests/contract/DeployAndDestroy.sol | 8 + tests/contract/SimpleContract.sol | 16 ++ tests/contract/deploy_and_destroy_init_code | 1 + tests/contract/simple_contract_bytecode.txt | 1 + tests/eip_6780.rs | 188 ++++++++++++++++++++ tests/mock.rs | 131 ++++++++++++++ 8 files changed, 367 insertions(+) create mode 100644 tests/contract/DeployAndDestroy.sol create mode 100644 tests/contract/SimpleContract.sol create mode 100644 tests/contract/deploy_and_destroy_init_code create mode 100644 tests/contract/simple_contract_bytecode.txt create mode 100644 tests/eip_6780.rs create mode 100644 tests/mock.rs diff --git a/Cargo.toml b/Cargo.toml index 2a213477..6ae8f4e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ sha3 = { version = "0.10", default-features = false } evm-interpreter = { version = "1.0.0-dev", path = "interpreter", default-features = false } +[dev-dependencies] +hex = { version = "0.4", features = [ "serde" ] } + [features] default = ["std"] std = [ diff --git a/src/standard/config.rs b/src/standard/config.rs index bfc629d8..5c5de2ed 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -231,6 +231,10 @@ impl Config { Self::config_with_derived_values(DerivedConfigInputs::shanghai()) } + pub const fn cancun() -> Config { + Self::config_with_derived_values(DerivedConfigInputs::cancun()) + } + const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config { let DerivedConfigInputs { gas_storage_read_warm, @@ -386,4 +390,19 @@ impl DerivedConfigInputs { fork: SHANGHAI, } } + const fn cancun() -> Self { + Self { + gas_storage_read_warm: 100, + gas_sload_cold: 2100, + gas_access_list_storage_key: 1900, + decrease_clears_refund: true, + has_base_fee: true, + has_push0: true, + disallow_executable_format: true, + warm_coinbase_address: true, + // 2 * (MAX_CODE_SIZE = `24576`) = (0xC000 = 49152) as per EIP-3860 + max_initcode_size: Some(0xC000), + fork: CANCUN, + } + } } diff --git a/tests/contract/DeployAndDestroy.sol b/tests/contract/DeployAndDestroy.sol new file mode 100644 index 00000000..764fdd11 --- /dev/null +++ b/tests/contract/DeployAndDestroy.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract DeployAndDestroy { + constructor() { + selfdestruct(payable(msg.sender)); + } +} diff --git a/tests/contract/SimpleContract.sol b/tests/contract/SimpleContract.sol new file mode 100644 index 00000000..18ba1e6e --- /dev/null +++ b/tests/contract/SimpleContract.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleContract { + address public owner; + + // Constructor sets the owner of the contract + constructor() { + owner = msg.sender; + } + + // Function to destroy the contract and send the remaining funds to the target address + function destroy(address target) public { + selfdestruct(payable(target)); + } +} diff --git a/tests/contract/deploy_and_destroy_init_code b/tests/contract/deploy_and_destroy_init_code new file mode 100644 index 00000000..ffb01921 --- /dev/null +++ b/tests/contract/deploy_and_destroy_init_code @@ -0,0 +1 @@ +6080604052348015600e575f80fd5b503373ffffffffffffffffffffffffffffffffffffffff16fffe diff --git a/tests/contract/simple_contract_bytecode.txt b/tests/contract/simple_contract_bytecode.txt new file mode 100644 index 00000000..958fa0d7 --- /dev/null +++ b/tests/contract/simple_contract_bytecode.txt @@ -0,0 +1 @@ +608060405234801561000f575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101948061005c5f395ff3fe608060405234801561000f575f80fd5b5060043610610033575f3560e01c8062f55d9d146100375780638da5cb5b14610053575b5f80fd5b610051600480360381019061004c919061010b565b610071565b005b61005b61008a565b6040516100689190610145565b60405180910390f35b8073ffffffffffffffffffffffffffffffffffffffff16ff5b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100da826100b1565b9050919050565b6100ea816100d0565b81146100f4575f80fd5b50565b5f81359050610105816100e1565b92915050565b5f602082840312156101205761011f6100ad565b5b5f61012d848285016100f7565b91505092915050565b61013f816100d0565b82525050565b5f6020820190506101585f830184610136565b9291505056fea26469706673582212201e4165398f9671b365261ccc1129042cbcea13db557b5359b2298c51f2ab274d64736f6c63430008150033 diff --git a/tests/eip_6780.rs b/tests/eip_6780.rs new file mode 100644 index 00000000..2efc2f9c --- /dev/null +++ b/tests/eip_6780.rs @@ -0,0 +1,188 @@ +mod mock; +use evm::{ + backend::OverlayedBackend, + standard::{Config, Etable, EtableResolver, Invoker, TransactArgs, TransactValue}, + RuntimeBaseBackend, +}; +use mock::{MockAccount, MockBackend}; +use primitive_types::{H160, H256, U256}; + +const SIMPLE_CONTRACT_INITCODE: &str = include_str!("./contract/simple_contract_bytecode.txt"); +const DEPLOY_AND_DESTROY_INITCODE: &str = include_str!("./contract/deploy_and_destroy_init_code"); + +fn transact( + config: &Config, + args: TransactArgs, + overlayed_backend: &mut OverlayedBackend, +) -> Result { + let gas_etable = Etable::single(evm::standard::eval_gasometer); + let exec_etable = Etable::runtime(); + let etable = (gas_etable, exec_etable); + let resolver = EtableResolver::new(config, &(), &etable); + let invoker = Invoker::new(config, &resolver); + + evm::transact(args.clone(), Some(4), overlayed_backend, &invoker) +} + +#[test] +fn self_destruct_before_cancun() { + let mut backend = MockBackend::default(); + backend.state.insert( + H160::from_low_u64_be(1), + MockAccount { + balance: U256::from(1_000_000_000), + code: vec![], + nonce: U256::one(), + storage: Default::default(), + }, + ); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + + let init_code = hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).unwrap(); + let args = TransactArgs::Create { + caller: H160::from_low_u64_be(1), + value: U256::zero(), + init_code, + salt: Some(H256::from_low_u64_be(4)), + gas_limit: U256::from(400_000), + gas_price: U256::from(1), + access_list: vec![], + }; + + let config = Config::shanghai(); + + // Create simple contract + let contract_address = match transact(&config, args, &mut overlayed_backend) { + Ok(TransactValue::Create { address, .. }) => address, + _ => panic!("Failed to create contract"), + }; + + // Verify contract creation + assert!(!overlayed_backend.code(contract_address).is_empty()); + assert_eq!(overlayed_backend.nonce(contract_address), U256::one()); + + // Apply overlayed changeset + let (mut backend, changeset) = overlayed_backend.deconstruct(); + backend.apply_overlayed(&changeset); + + // Call Self destruct in anothor transaction + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let args = TransactArgs::Call { + caller: H160::from_low_u64_be(1), + address: contract_address, + value: U256::zero(), + data: hex::decode( + "00f55d9d00000000000000000000000055c41626c84445180eda39bac564606c633dd980", + ) + .unwrap(), + gas_limit: U256::from(400_000), + gas_price: U256::one(), + access_list: vec![], + }; + + let result = transact(&config, args, &mut overlayed_backend); + let changeset = overlayed_backend.deconstruct().1; + + assert!(result.is_ok()); + assert!(changeset.deletes.contains(&contract_address)); +} + +#[test] +fn self_destruct_cancun() { + let mut backend = MockBackend::default(); + backend.state.insert( + H160::from_low_u64_be(1), + MockAccount { + balance: U256::from(1_000_000_000), + code: vec![], + nonce: U256::one(), + storage: Default::default(), + }, + ); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + + let init_code = + hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).expect("Failed to decode contract"); + let args = TransactArgs::Create { + caller: H160::from_low_u64_be(1), + value: U256::zero(), + init_code, + salt: Some(H256::from_low_u64_be(4)), + gas_limit: U256::from(400_000), + gas_price: U256::from(1), + access_list: vec![], + }; + + let config = Config::cancun(); + + // Create simple contract + let contract_address = match transact(&config, args, &mut overlayed_backend) { + Ok(TransactValue::Create { address, .. }) => address, + _ => panic!("Failed to create contract"), + }; + + // Verify contract creation + assert!(!overlayed_backend.code(contract_address).is_empty()); + assert_eq!(overlayed_backend.nonce(contract_address), U256::one()); + + // Apply overlayed changeset + let (mut backend, changeset) = overlayed_backend.deconstruct(); + backend.apply_overlayed(&changeset); + + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + // Self destruct contract in another transaction + let args = TransactArgs::Call { + caller: H160::from_low_u64_be(1), + address: contract_address, + value: U256::zero(), + data: hex::decode( + "00f55d9d00000000000000000000000055c41626c84445180eda39bac564606c633dd980", + ) + .unwrap(), + gas_limit: U256::from(400_000), + gas_price: U256::one(), + access_list: vec![], + }; + + let result = transact(&config, args, &mut overlayed_backend); + let changeset = overlayed_backend.deconstruct().1; + + assert!(result.is_ok()); + assert!(!changeset.deletes.contains(&contract_address)); +} + +#[test] +fn self_destruct_same_tx_cancun() { + let mut backend = MockBackend::default(); + backend.state.insert( + H160::from_low_u64_be(1), + MockAccount { + balance: U256::from(1_000_000_000), + code: vec![], + nonce: U256::one(), + storage: Default::default(), + }, + ); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + + let init_code = + hex::decode(DEPLOY_AND_DESTROY_INITCODE.trim_end()).expect("Failed to decode contract"); + let args = TransactArgs::Create { + caller: H160::from_low_u64_be(1), + value: U256::zero(), + init_code, + salt: Some(H256::from_low_u64_be(4)), + gas_limit: U256::from(400_000), + gas_price: U256::from(1), + access_list: vec![], + }; + + let config = Config::cancun(); + + // Create deploy and destroy contract + let result = transact(&config, args, &mut overlayed_backend); + assert!(result.is_ok()); + + // Verify contract was deleted + assert!(!overlayed_backend.deconstruct().1.deletes.is_empty()); +} diff --git a/tests/mock.rs b/tests/mock.rs new file mode 100644 index 00000000..2c424cc1 --- /dev/null +++ b/tests/mock.rs @@ -0,0 +1,131 @@ +extern crate evm; + +use evm::{backend::OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; +use primitive_types::{H160, H256, U256}; +use std::collections::BTreeMap; + +#[derive(Default, Clone, Debug)] +pub struct MockAccount { + pub balance: U256, + pub code: Vec, + pub nonce: U256, + pub storage: BTreeMap, +} + +#[derive(Clone, Debug, Default)] +pub struct MockBackend { + pub state: BTreeMap, +} + +impl MockBackend { + pub fn apply_overlayed(&mut self, changeset: &OverlayedChangeSet) { + for (address, balance) in changeset.balances.clone() { + self.state.entry(address).or_default().balance = balance; + } + + for (address, code) in changeset.codes.clone() { + self.state.entry(address).or_default().code = code; + } + + for (address, nonce) in changeset.nonces.clone() { + self.state.entry(address).or_default().nonce = nonce; + } + + for address in changeset.storage_resets.clone() { + self.state.entry(address).or_default().storage = BTreeMap::new(); + } + + for ((address, key), value) in changeset.storages.clone() { + let account = self.state.entry(address).or_default(); + + if value == H256::default() { + account.storage.remove(&key); + } else { + account.storage.insert(key, value); + } + } + + for address in changeset.deletes.clone() { + self.state.remove(&address); + } + } +} + +impl RuntimeEnvironment for MockBackend { + fn block_hash(&self, _number: U256) -> H256 { + Default::default() + } + + fn block_number(&self) -> U256 { + Default::default() + } + + fn block_coinbase(&self) -> H160 { + Default::default() + } + + fn block_timestamp(&self) -> U256 { + Default::default() + } + + fn block_difficulty(&self) -> U256 { + Default::default() + } + + fn block_randomness(&self) -> Option { + Default::default() + } + + fn block_gas_limit(&self) -> U256 { + Default::default() + } + + fn block_base_fee_per_gas(&self) -> U256 { + Default::default() + } + + fn chain_id(&self) -> U256 { + Default::default() + } +} + +impl RuntimeBaseBackend for MockBackend { + fn balance(&self, address: H160) -> U256 { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .balance + } + + fn code(&self, address: H160) -> Vec { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .code + } + + fn exists(&self, address: H160) -> bool { + self.state.get(&address).is_some() + } + + fn storage(&self, address: H160, index: H256) -> H256 { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .storage + .get(&index) + .cloned() + .unwrap_or(H256::default()) + } + + fn nonce(&self, address: H160) -> U256 { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .nonce + } +} From c4bad3c32bfd882941be38fb2a0918f7a9552d9a Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk Date: Tue, 25 Jun 2024 12:07:31 +0200 Subject: [PATCH 7/7] resolve comments --- interpreter/src/eval/mod.rs | 7 +------ interpreter/src/eval/system.rs | 38 +--------------------------------- interpreter/src/fork.rs | 11 ---------- interpreter/src/lib.rs | 1 - interpreter/src/runtime.rs | 10 ++++----- interpreter/tests/usability.rs | 6 ++---- jsontests/src/run.rs | 4 ++-- src/backend/overlayed.rs | 34 +++++++++++++++++++++--------- src/standard/config.rs | 25 +++++++++++----------- src/standard/invoker/mod.rs | 3 --- tests/eip_6780.rs | 28 ++++++++++++------------- tests/mock.rs | 14 ++++++++++++- 12 files changed, 73 insertions(+), 108 deletions(-) delete mode 100644 interpreter/src/fork.rs diff --git a/interpreter/src/eval/mod.rs b/interpreter/src/eval/mod.rs index 53e93de7..241bcf96 100644 --- a/interpreter/src/eval/mod.rs +++ b/interpreter/src/eval/mod.rs @@ -15,7 +15,6 @@ use crate::{ machine::Machine, opcode::Opcode, runtime::{GasState, RuntimeBackend, RuntimeEnvironment, RuntimeState}, - fork::Fork, }; pub fn eval_pass( @@ -725,11 +724,7 @@ pub fn eval_suicide, H: RuntimeEnvironment + RuntimeBacke _opcode: Opcode, _position: usize, ) -> Control { - if machine.state.as_ref().fork >= Fork::CANCUN { - self::system::suicide_eip_6780(machine, handle) - } else { - self::system::suicide(machine, handle) - } + self::system::suicide(machine, handle) } pub fn eval_chainid, H: RuntimeEnvironment + RuntimeBackend, Tr>( diff --git a/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index 4a61b0b2..a9b7ce71 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -362,43 +362,7 @@ pub fn suicide, H: RuntimeEnvironment + RuntimeBackend, T value: balance, })?; - handler.mark_delete(address); - handler.reset_balance(address); - - Ok(((), ())) - }) { - Ok(()) => Control::Exit(ExitSucceed::Suicided.into()), - Err(e) => Control::Exit(Err(e)), - } -} - -pub fn suicide_eip_6780, H: RuntimeEnvironment + RuntimeBackend, Tr>( - machine: &mut Machine, - handler: &mut H, -) -> Control { - let address = machine.state.as_ref().context.address; - - match machine.stack.perform_pop1_push0(|target| { - let balance = handler.balance(address); - let target = (*target).into(); - - // Cancun EIP-6780 only allow contract deletion within the same transaction that created it - if handler.created(address) { - handler.transfer(Transfer { - source: address, - target, - value: balance, - })?; - - handler.mark_delete(address); - handler.reset_balance(address); - } else if address != target { - handler.transfer(Transfer { - source: address, - target, - value: balance, - })?; - } + handler.mark_delete_reset(address); Ok(((), ())) }) { Ok(()) => Control::Exit(ExitSucceed::Suicided.into()), diff --git a/interpreter/src/fork.rs b/interpreter/src/fork.rs deleted file mode 100644 index 637faf2c..00000000 --- a/interpreter/src/fork.rs +++ /dev/null @@ -1,11 +0,0 @@ -/// EVM supported forks -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Fork { - FRONTIER, - ISTANBUL, - BERLIN, - LONDON, - MERGE, - SHANGHAI, - CANCUN, -} diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index f9669b42..f29887ab 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -14,6 +14,5 @@ pub mod machine; pub mod opcode; pub mod runtime; pub mod utils; -pub mod fork; pub use self::interpreter::{EtableInterpreter, Interpreter, RunInterpreter, StepInterpreter}; diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index d6072fba..1c49183a 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -3,7 +3,7 @@ use alloc::{rc::Rc, vec::Vec}; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; -use crate::{error::ExitError, fork::Fork}; +use crate::error::ExitError; /// Gas state. pub trait GasState { @@ -19,8 +19,6 @@ pub struct RuntimeState { pub transaction_context: Rc, /// Return data buffer. pub retbuf: Vec, - /// EVM Fork. - pub fork: Fork, } impl AsRef for RuntimeState { @@ -155,9 +153,9 @@ pub trait RuntimeBackend: RuntimeBaseBackend { ) -> Result<(), ExitError>; /// Create a log owned by address with given topics and data. fn log(&mut self, log: Log) -> Result<(), ExitError>; - /// Mark an address to be deleted. - fn mark_delete(&mut self, address: H160); - // Mark an address that is being created in the transaction. + /// Mark an address to be deleted and its balance to be reset. + fn mark_delete_reset(&mut self, address: H160); + // Mark an address as created in the current transaction. fn mark_create(&mut self, address: H160); /// Fully delete storages of an account. fn reset_storage(&mut self, address: H160); diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index caafd785..6d2f2595 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,5 +1,3 @@ -use evm_interpreter::fork::Fork; -use std::rc::Rc; use evm_interpreter::{ error::{CallCreateTrap, Capture, ExitError, ExitSucceed}, etable::{Control, Etable}, @@ -12,6 +10,7 @@ use evm_interpreter::{ EtableInterpreter, RunInterpreter, }; use primitive_types::{H160, H256, U256}; +use std::rc::Rc; const CODE1: &str = "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056"; const DATA1: &str = "2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001"; @@ -162,7 +161,7 @@ impl RuntimeBackend for UnimplementedHandler { fn log(&mut self, _log: Log) -> Result<(), ExitError> { unimplemented!() } - fn mark_delete(&mut self, _address: H160) { + fn mark_delete_reset(&mut self, _address: H160) { unimplemented!() } @@ -220,7 +219,6 @@ fn etable_runtime() { } .into(), retbuf: Vec::new(), - fork: Fork::FRONTIER, }, ); let mut vm = EtableInterpreter::new(machine, &RUNTIME_ETABLE); diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 3f113652..5211cd98 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -184,8 +184,8 @@ pub fn run_test( state, }; - let mut run_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone()); - let mut step_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone()); + let mut run_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config); + let mut step_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config); // Run let run_result = evm::transact(args.clone(), Some(4), &mut run_backend, &invoker); diff --git a/src/backend/overlayed.rs b/src/backend/overlayed.rs index eb1cd343..4f922f1f 100644 --- a/src/backend/overlayed.rs +++ b/src/backend/overlayed.rs @@ -11,7 +11,7 @@ use evm_interpreter::{ }; use primitive_types::{H160, H256, U256}; -use crate::{backend::TransactionalBackend, MergeStrategy}; +use crate::{backend::TransactionalBackend, standard::Config, MergeStrategy}; #[derive(Clone, Debug)] pub struct OverlayedChangeSet { @@ -25,18 +25,24 @@ pub struct OverlayedChangeSet { pub deletes: BTreeSet, } -pub struct OverlayedBackend { +pub struct OverlayedBackend<'config, B> { backend: B, substate: Box, accessed: BTreeSet<(H160, Option)>, + config: &'config Config, } -impl OverlayedBackend { - pub fn new(backend: B, accessed: BTreeSet<(H160, Option)>) -> Self { +impl<'config, B> OverlayedBackend<'config, B> { + pub fn new( + backend: B, + accessed: BTreeSet<(H160, Option)>, + config: &'config Config, + ) -> Self { Self { backend, substate: Box::new(Substate::new()), accessed, + config, } } @@ -57,7 +63,7 @@ impl OverlayedBackend { } } -impl RuntimeEnvironment for OverlayedBackend { +impl RuntimeEnvironment for OverlayedBackend<'_, B> { fn block_hash(&self, number: U256) -> H256 { self.backend.block_hash(number) } @@ -95,7 +101,7 @@ impl RuntimeEnvironment for OverlayedBackend { } } -impl RuntimeBaseBackend for OverlayedBackend { +impl RuntimeBaseBackend for OverlayedBackend<'_, B> { fn balance(&self, address: H160) -> U256 { if let Some(balance) = self.substate.known_balance(address) { balance @@ -145,7 +151,7 @@ impl RuntimeBaseBackend for OverlayedBackend { } } -impl RuntimeBackend for OverlayedBackend { +impl RuntimeBackend for OverlayedBackend<'_, B> { fn original_storage(&self, address: H160, index: H256) -> H256 { self.backend.storage(address, index) } @@ -188,8 +194,16 @@ impl RuntimeBackend for OverlayedBackend { Ok(()) } - fn mark_delete(&mut self, address: H160) { - self.substate.deletes.insert(address); + fn mark_delete_reset(&mut self, address: H160) { + if self.config.suicide_only_in_same_tx { + if self.created(address) { + self.substate.deletes.insert(address); + self.substate.storage_resets.insert(address); + } + } else { + self.substate.deletes.insert(address); + self.substate.storage_resets.insert(address); + } } fn mark_create(&mut self, address: H160) { @@ -241,7 +255,7 @@ impl RuntimeBackend for OverlayedBackend { } } -impl TransactionalBackend for OverlayedBackend { +impl<'config, B: RuntimeBaseBackend> TransactionalBackend for OverlayedBackend<'config, B> { fn push_substate(&mut self) { let mut parent = Box::new(Substate::new()); mem::swap(&mut parent, &mut self.substate); diff --git a/src/standard/config.rs b/src/standard/config.rs index 6d535f4c..47005961 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -1,5 +1,3 @@ -use evm_interpreter::fork::{Fork, Fork::*}; - /// Runtime configuration. #[derive(Clone, Debug)] pub struct Config { @@ -105,7 +103,8 @@ pub struct Config { pub eip_5656_enabled: bool, /// Uses EIP-1559 (Base fee is burned when this flag is enabled) [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) pub eip_1559_enabled: bool, - pub fork: Fork, + /// Selfdestruct deletet contract only if called in the same tx as creation [EIP-6780](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6780.md) + pub suicide_only_in_same_tx: bool, } impl Config { @@ -162,7 +161,7 @@ impl Config { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, - fork: FRONTIER, + suicide_only_in_same_tx: false, } } @@ -219,7 +218,7 @@ impl Config { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, - fork: ISTANBUL, + suicide_only_in_same_tx: false, } } @@ -262,7 +261,7 @@ impl Config { eip_1153_enabled, eip_5656_enabled, eip_1559_enabled, - fork, + suicide_only_in_same_tx, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -328,7 +327,7 @@ impl Config { eip_1153_enabled, eip_5656_enabled, eip_1559_enabled, - fork, + suicide_only_in_same_tx, } } } @@ -351,7 +350,7 @@ struct DerivedConfigInputs { eip_1153_enabled: bool, eip_5656_enabled: bool, eip_1559_enabled: bool, - fork: Fork, + suicide_only_in_same_tx: bool, } impl DerivedConfigInputs { @@ -369,7 +368,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, - fork: BERLIN, + suicide_only_in_same_tx: false, } } @@ -387,7 +386,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, - fork: LONDON, + suicide_only_in_same_tx: false, } } @@ -405,7 +404,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, - fork: MERGE, + suicide_only_in_same_tx: false, } } @@ -424,7 +423,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, - fork: SHANGHAI, + suicide_only_in_same_tx: false, } } const fn cancun() -> Self { @@ -442,7 +441,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, - fork: CANCUN, + suicide_only_in_same_tx: true, } } } diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 27d0c0da..d0529f66 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -270,7 +270,6 @@ where context, transaction_context: Rc::new(transaction_context), retbuf: Vec::new(), - fork: self.config.fork, }; let work = || -> Result<(TransactInvoke, _), ExitError> { @@ -493,7 +492,6 @@ where context: call_trap_data.context.clone(), transaction_context, retbuf: Vec::new(), - fork: self.config.fork, }, gas_limit, is_static, @@ -528,7 +526,6 @@ where }, transaction_context, retbuf: Vec::new(), - fork: self.config.fork, }, gas_limit, is_static, diff --git a/tests/eip_6780.rs b/tests/eip_6780.rs index 2efc2f9c..5f847217 100644 --- a/tests/eip_6780.rs +++ b/tests/eip_6780.rs @@ -1,8 +1,8 @@ mod mock; use evm::{ - backend::OverlayedBackend, + backend::{OverlayedBackend, RuntimeBaseBackend}, + interpreter::error::ExitError, standard::{Config, Etable, EtableResolver, Invoker, TransactArgs, TransactValue}, - RuntimeBaseBackend, }; use mock::{MockAccount, MockBackend}; use primitive_types::{H160, H256, U256}; @@ -14,7 +14,7 @@ fn transact( config: &Config, args: TransactArgs, overlayed_backend: &mut OverlayedBackend, -) -> Result { +) -> Result { let gas_etable = Etable::single(evm::standard::eval_gasometer); let exec_etable = Etable::runtime(); let etable = (gas_etable, exec_etable); @@ -34,9 +34,11 @@ fn self_destruct_before_cancun() { code: vec![], nonce: U256::one(), storage: Default::default(), + transient_storage: Default::default(), }, ); - let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let config = Config::shanghai(); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); let init_code = hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).unwrap(); let args = TransactArgs::Create { @@ -49,8 +51,6 @@ fn self_destruct_before_cancun() { access_list: vec![], }; - let config = Config::shanghai(); - // Create simple contract let contract_address = match transact(&config, args, &mut overlayed_backend) { Ok(TransactValue::Create { address, .. }) => address, @@ -66,7 +66,7 @@ fn self_destruct_before_cancun() { backend.apply_overlayed(&changeset); // Call Self destruct in anothor transaction - let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); let args = TransactArgs::Call { caller: H160::from_low_u64_be(1), address: contract_address, @@ -97,9 +97,11 @@ fn self_destruct_cancun() { code: vec![], nonce: U256::one(), storage: Default::default(), + transient_storage: Default::default(), }, ); - let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let config = Config::cancun(); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); let init_code = hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).expect("Failed to decode contract"); @@ -113,8 +115,6 @@ fn self_destruct_cancun() { access_list: vec![], }; - let config = Config::cancun(); - // Create simple contract let contract_address = match transact(&config, args, &mut overlayed_backend) { Ok(TransactValue::Create { address, .. }) => address, @@ -129,7 +129,7 @@ fn self_destruct_cancun() { let (mut backend, changeset) = overlayed_backend.deconstruct(); backend.apply_overlayed(&changeset); - let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); // Self destruct contract in another transaction let args = TransactArgs::Call { caller: H160::from_low_u64_be(1), @@ -161,9 +161,11 @@ fn self_destruct_same_tx_cancun() { code: vec![], nonce: U256::one(), storage: Default::default(), + transient_storage: Default::default(), }, ); - let mut overlayed_backend = OverlayedBackend::new(backend, Default::default()); + let config = Config::cancun(); + let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); let init_code = hex::decode(DEPLOY_AND_DESTROY_INITCODE.trim_end()).expect("Failed to decode contract"); @@ -177,8 +179,6 @@ fn self_destruct_same_tx_cancun() { access_list: vec![], }; - let config = Config::cancun(); - // Create deploy and destroy contract let result = transact(&config, args, &mut overlayed_backend); assert!(result.is_ok()); diff --git a/tests/mock.rs b/tests/mock.rs index 2c424cc1..30829e5b 100644 --- a/tests/mock.rs +++ b/tests/mock.rs @@ -1,6 +1,6 @@ extern crate evm; -use evm::{backend::OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; +use evm::backend::{OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; use primitive_types::{H160, H256, U256}; use std::collections::BTreeMap; @@ -10,6 +10,7 @@ pub struct MockAccount { pub code: Vec, pub nonce: U256, pub storage: BTreeMap, + pub transient_storage: BTreeMap, } #[derive(Clone, Debug, Default)] @@ -121,6 +122,17 @@ impl RuntimeBaseBackend for MockBackend { .unwrap_or(H256::default()) } + fn transient_storage(&self, address: H160, index: H256) -> H256 { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .transient_storage + .get(&index) + .cloned() + .unwrap_or(H256::default()) + } + fn nonce(&self, address: H160) -> U256 { self.state .get(&address)