diff --git a/Cargo.toml b/Cargo.toml index 7b0a5fea0..10f8b177e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,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/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index 6d2e07459..a9b7ce71f 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -362,9 +362,7 @@ pub fn suicide, H: RuntimeEnvironment + RuntimeBackend, T value: balance, })?; - handler.mark_delete(address); - handler.reset_balance(address); - + handler.mark_delete_reset(address); Ok(((), ())) }) { Ok(()) => Control::Exit(ExitSucceed::Suicided.into()), diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index 63d85dd6c..6d91006bb 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -139,6 +139,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 { @@ -158,8 +160,10 @@ 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 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); /// Set code of an account. diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index a5b8cecd1..28c267b8c 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,5 +1,3 @@ -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"; @@ -135,6 +134,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!() } @@ -157,7 +161,11 @@ 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!() + } + + fn mark_create(&mut self, _address: H160) { unimplemented!() } diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 3f113652a..5211cd98f 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 638fbc85b..cf8077bf9 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,11 +151,15 @@ 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) } + fn created(&self, address: H160) -> bool { + self.substate.created(address) + } + fn deleted(&self, address: H160) -> bool { self.substate.deleted(address) } @@ -184,8 +194,20 @@ 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) { + self.substate.creates.insert(address); } fn reset_storage(&mut self, address: H160) { @@ -238,7 +260,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); @@ -278,6 +300,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 => {} } @@ -294,6 +319,7 @@ struct Substate { storages: BTreeMap<(H160, H256), H256>, transient_storage: BTreeMap<(H160, H256), H256>, deletes: BTreeSet, + creates: BTreeSet, } impl Substate { @@ -308,6 +334,7 @@ impl Substate { storages: Default::default(), transient_storage: Default::default(), deletes: Default::default(), + creates: Default::default(), } } @@ -385,4 +412,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/config.rs b/src/standard/config.rs index da2be1409..470059615 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -103,6 +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, + /// 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 { @@ -159,6 +161,7 @@ impl Config { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, + suicide_only_in_same_tx: false, } } @@ -215,6 +218,7 @@ impl Config { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, + suicide_only_in_same_tx: false, } } @@ -257,6 +261,7 @@ impl Config { eip_1153_enabled, eip_5656_enabled, eip_1559_enabled, + suicide_only_in_same_tx, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -322,6 +327,7 @@ impl Config { eip_1153_enabled, eip_5656_enabled, eip_1559_enabled, + suicide_only_in_same_tx, } } } @@ -344,6 +350,7 @@ struct DerivedConfigInputs { eip_1153_enabled: bool, eip_5656_enabled: bool, eip_1559_enabled: bool, + suicide_only_in_same_tx: bool, } impl DerivedConfigInputs { @@ -361,6 +368,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: false, + suicide_only_in_same_tx: false, } } @@ -378,6 +386,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, + suicide_only_in_same_tx: false, } } @@ -395,6 +404,7 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, + suicide_only_in_same_tx: false, } } @@ -413,9 +423,9 @@ impl DerivedConfigInputs { eip_1153_enabled: false, eip_5656_enabled: false, eip_1559_enabled: true, + suicide_only_in_same_tx: false, } } - const fn cancun() -> Self { Self { gas_storage_read_warm: 100, @@ -428,9 +438,10 @@ impl DerivedConfigInputs { warm_coinbase_address: true, // 2 * (MAX_CODE_SIZE = `24576`) = (0xC000 = 49152) as per EIP-3860 max_initcode_size: Some(0xC000), - eip_1153_enabled: true, - eip_5656_enabled: true, + eip_1153_enabled: false, + eip_5656_enabled: false, eip_1559_enabled: true, + suicide_only_in_same_tx: true, } } } diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index cdd3909cb..52313de48 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -75,6 +75,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) } diff --git a/tests/contract/DeployAndDestroy.sol b/tests/contract/DeployAndDestroy.sol new file mode 100644 index 000000000..764fdd119 --- /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 000000000..18ba1e6e3 --- /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 000000000..ffb019210 --- /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 000000000..958fa0d72 --- /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 000000000..5f847217d --- /dev/null +++ b/tests/eip_6780.rs @@ -0,0 +1,188 @@ +mod mock; +use evm::{ + backend::{OverlayedBackend, RuntimeBaseBackend}, + interpreter::error::ExitError, + standard::{Config, Etable, EtableResolver, Invoker, TransactArgs, TransactValue}, +}; +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(), + transient_storage: 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 { + 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![], + }; + + // 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(), &config); + 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(), + transient_storage: 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"); + 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![], + }; + + // 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(), &config); + // 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(), + transient_storage: 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"); + 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![], + }; + + // 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 000000000..30829e5b2 --- /dev/null +++ b/tests/mock.rs @@ -0,0 +1,143 @@ +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, + pub transient_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 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) + .cloned() + .unwrap_or(Default::default()) + .nonce + } +}