diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 54375c80d..199483d04 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -15,6 +15,7 @@ scale-codec = { package = "parity-scale-codec", version = "3.2", default-feature scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } sha3 = { version = "0.10", default-features = false } +auto_impl = "1.1" [dev-dependencies] hex = "0.4" diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index 163c6ee9b..7dbce8a49 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -75,6 +75,7 @@ pub struct Log { pub data: Vec, } +#[auto_impl::auto_impl(&, Box)] pub trait RuntimeEnvironment { /// Get environmental block hash. fn block_hash(&self, number: U256) -> H256; @@ -96,6 +97,7 @@ pub trait RuntimeEnvironment { fn chain_id(&self) -> U256; } +#[auto_impl::auto_impl(&, Box)] pub trait RuntimeBaseBackend { /// Get balance of address. fn balance(&self, address: H160) -> U256; diff --git a/jsontests/src/hash.rs b/jsontests/src/hash.rs index e803ab842..7a7e91e52 100644 --- a/jsontests/src/hash.rs +++ b/jsontests/src/hash.rs @@ -66,7 +66,6 @@ impl rlp::Decodable for TrieAccount { pub fn state_root(backend: &InMemoryBackend) -> H256 { let tree = backend - .current_layer() .state .iter() .map(|(address, account)| { diff --git a/jsontests/src/in_memory.rs b/jsontests/src/in_memory.rs index 1a94b08b5..05fc87e69 100644 --- a/jsontests/src/in_memory.rs +++ b/jsontests/src/in_memory.rs @@ -1,12 +1,6 @@ -use evm::{ - ExitError, ExitException, Log, MergeStrategy, RuntimeBackend, RuntimeBaseBackend, - RuntimeEnvironment, TransactionalBackend, -}; +use evm::{backend::OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; use primitive_types::{H160, H256, U256}; -use std::{ - collections::{BTreeMap, BTreeSet}, - mem, -}; +use std::collections::BTreeMap; #[derive(Clone, Debug)] pub struct InMemoryEnvironment { @@ -27,7 +21,6 @@ pub struct InMemoryAccount { pub code: Vec, pub nonce: U256, pub storage: BTreeMap, - pub original_storage: BTreeMap, } #[derive(Clone, Debug)] @@ -36,38 +29,42 @@ pub struct InMemorySuicideInfo { } #[derive(Clone, Debug)] -pub struct InMemoryLayer { +pub struct InMemoryBackend { + pub environment: InMemoryEnvironment, pub state: BTreeMap, - pub logs: Vec, - pub suicides: Vec, - pub hots: BTreeSet<(H160, Option)>, } -impl InMemoryLayer { - pub fn clear_pending(&mut self) { - self.hots.clear(); +impl InMemoryBackend { + pub fn apply_overlayed(&mut self, changeset: &OverlayedChangeSet) { + for (address, balance) in changeset.balances.clone() { + self.state.entry(address).or_default().balance = balance; + } - let mut suicides = Vec::new(); - mem::swap(&mut suicides, &mut self.suicides); - for suicide in suicides { - self.state.remove(&suicide.address); + for (address, code) in changeset.codes.clone() { + self.state.entry(address).or_default().code = code; } - } -} -#[derive(Clone, Debug)] -pub struct InMemoryBackend { - pub environment: InMemoryEnvironment, - pub layers: Vec, -} + for (address, nonce) in changeset.nonces.clone() { + self.state.entry(address).or_default().nonce = nonce; + } -impl InMemoryBackend { - pub fn current_layer(&self) -> &InMemoryLayer { - self.layers.last().expect("current layer exists") - } + 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(); - pub fn current_layer_mut(&mut self) -> &mut InMemoryLayer { - self.layers.last_mut().expect("current layer exists") + if value == H256::default() { + account.storage.remove(&key); + } else { + account.storage.insert(key, value); + } + } + + for address in changeset.deletes.clone() { + self.state.remove(&address); + } } } @@ -115,8 +112,7 @@ impl RuntimeEnvironment for InMemoryBackend { impl RuntimeBaseBackend for InMemoryBackend { fn balance(&self, address: H160) -> U256 { - self.current_layer() - .state + self.state .get(&address) .cloned() .unwrap_or(Default::default()) @@ -124,8 +120,7 @@ impl RuntimeBaseBackend for InMemoryBackend { } fn code(&self, address: H160) -> Vec { - self.current_layer() - .state + self.state .get(&address) .cloned() .unwrap_or(Default::default()) @@ -133,12 +128,11 @@ impl RuntimeBaseBackend for InMemoryBackend { } fn exists(&self, address: H160) -> bool { - self.current_layer().state.get(&address).is_some() + self.state.get(&address).is_some() } fn storage(&self, address: H160, index: H256) -> H256 { - self.current_layer() - .state + self.state .get(&address) .cloned() .unwrap_or(Default::default()) @@ -149,133 +143,10 @@ impl RuntimeBaseBackend for InMemoryBackend { } fn nonce(&self, address: H160) -> U256 { - self.current_layer() - .state + self.state .get(&address) .cloned() .unwrap_or(Default::default()) .nonce } } - -impl RuntimeBackend for InMemoryBackend { - fn original_storage(&self, address: H160, index: H256) -> H256 { - self.current_layer() - .state - .get(&address) - .cloned() - .unwrap_or(Default::default()) - .original_storage - .get(&index) - .cloned() - .unwrap_or(H256::default()) - } - - fn deleted(&self, address: H160) -> bool { - self.current_layer() - .suicides - .iter() - .any(|suicide| suicide.address == address) - } - - fn is_cold(&self, address: H160, index: Option) -> bool { - !self.current_layer().hots.contains(&(address, index)) - } - - fn mark_hot(&mut self, address: H160, index: Option) { - self.current_layer_mut().hots.insert((address, index)); - } - - fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError> { - let entry = self.current_layer_mut().state.entry(address).or_default(); - - if value == H256::default() { - entry.storage.remove(&index); - } else { - entry.storage.insert(index, value); - } - Ok(()) - } - - fn log(&mut self, log: Log) -> Result<(), ExitError> { - self.current_layer_mut().logs.push(log); - Ok(()) - } - - fn mark_delete(&mut self, address: H160) { - self.current_layer_mut() - .suicides - .push(InMemorySuicideInfo { address }); - } - - fn reset_storage(&mut self, address: H160) { - self.current_layer_mut() - .state - .entry(address) - .or_default() - .storage = Default::default(); - } - - fn set_code(&mut self, address: H160, code: Vec) -> Result<(), ExitError> { - self.current_layer_mut() - .state - .entry(address) - .or_default() - .code = code; - - Ok(()) - } - - fn reset_balance(&mut self, address: H160) { - self.current_layer_mut() - .state - .entry(address) - .or_default() - .balance = U256::zero(); - } - - fn withdrawal(&mut self, source: H160, value: U256) -> Result<(), ExitError> { - let source = self.current_layer_mut().state.entry(source).or_default(); - if source.balance < value { - return Err(ExitException::OutOfFund.into()); - } - source.balance -= value; - Ok(()) - } - - fn deposit(&mut self, target: H160, value: U256) { - if value == U256::zero() { - return; - } - - self.current_layer_mut() - .state - .entry(target) - .or_default() - .balance += value; - } - - fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> { - let entry = self.current_layer_mut().state.entry(address).or_default(); - entry.nonce = entry.nonce.saturating_add(U256::one()); - Ok(()) - } -} - -impl TransactionalBackend for InMemoryBackend { - fn push_substate(&mut self) { - let layer = self.current_layer().clone(); - self.layers.push(layer); - } - - fn pop_substate(&mut self, strategy: MergeStrategy) { - let layer = self.layers.pop().expect("current layer exist"); - - match strategy { - MergeStrategy::Commit => { - *self.current_layer_mut() = layer; - } - MergeStrategy::Discard | MergeStrategy::Revert => (), - } - } -} diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 835abbdeb..5fa6e8c6a 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -1,6 +1,7 @@ use crate::error::{Error, TestError}; -use crate::in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment, InMemoryLayer}; +use crate::in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment}; use crate::types::{Fork, TestCompletionStatus, TestData, TestExpectException, TestMulti}; +use evm::backend::OverlayedBackend; use evm::standard::{Config, Etable, EtableResolver, Invoker, TransactArgs}; use evm::utils::u256_to_h256; use evm::Capture; @@ -136,7 +137,6 @@ pub fn run_test( balance: account.balance, code: account.code.0, nonce: account.nonce, - original_storage: storage.clone(), storage, }, ) @@ -164,26 +164,27 @@ pub fn run_test( .collect(), }; - let mut run_backend = InMemoryBackend { + let initial_accessed = { + let mut hots = BTreeSet::new(); + for i in 1..10 { + hots.insert((u256_to_h256(U256::from(i)).into(), None)); + } + hots + }; + + let base_backend = InMemoryBackend { environment: env, - layers: vec![InMemoryLayer { - state, - logs: Vec::new(), - suicides: Vec::new(), - hots: { - let mut hots = BTreeSet::new(); - for i in 1..10 { - hots.insert((u256_to_h256(U256::from(i)).into(), None)); - } - hots - }, - }], + state, }; - let mut step_backend = run_backend.clone(); + + let mut run_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone()); + let mut step_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone()); // Run let run_result = evm::transact(args.clone(), Some(4), &mut run_backend, &invoker); - run_backend.layers[0].clear_pending(); + let run_changeset = run_backend.deconstruct().1; + let mut run_backend = base_backend.clone(); + run_backend.apply_overlayed(&run_changeset); // Step if debug { @@ -204,7 +205,9 @@ pub fn run_test( } }, ); - step_backend.layers[0].clear_pending(); + let step_changeset = step_backend.deconstruct().1; + let mut step_backend = base_backend.clone(); + step_backend.apply_overlayed(&step_changeset); } let state_root = crate::hash::state_root(&run_backend); @@ -219,7 +222,7 @@ pub fn run_test( if state_root != test.post.hash { if debug { - for (address, account) in &run_backend.layers[0].state { + for (address, account) in &run_backend.state { println!( "address: {:?}, balance: {}, nonce: {}, code: 0x{}, storage: {:?}", address, diff --git a/src/backend/mod.rs b/src/backend/mod.rs index f925ec027..96436d450 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -13,7 +13,7 @@ mod overlayed; -pub use self::overlayed::OverlayedBackend; +pub use self::overlayed::{OverlayedBackend, OverlayedChangeSet}; pub use evm_interpreter::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment}; /// Backend with layers that can transactionally be committed or discarded. diff --git a/src/backend/overlayed.rs b/src/backend/overlayed.rs index f7385941c..7ffd43df9 100644 --- a/src/backend/overlayed.rs +++ b/src/backend/overlayed.rs @@ -1,11 +1,26 @@ use crate::{ ExitError, ExitException, Log, MergeStrategy, RuntimeBackend, RuntimeBaseBackend, - TransactionalBackend, + RuntimeEnvironment, TransactionalBackend, +}; +use alloc::{ + boxed::Box, + collections::{BTreeMap, BTreeSet}, + vec::Vec, }; -use alloc::collections::{BTreeMap, BTreeSet}; use core::mem; use primitive_types::{H160, H256, U256}; +#[derive(Clone, Debug)] +pub struct OverlayedChangeSet { + pub logs: Vec, + pub balances: BTreeMap, + pub codes: BTreeMap>, + pub nonces: BTreeMap, + pub storage_resets: BTreeSet, + pub storages: BTreeMap<(H160, H256), H256>, + pub deletes: BTreeSet, +} + pub struct OverlayedBackend { backend: B, substate: Box, @@ -13,13 +28,66 @@ pub struct OverlayedBackend { } impl OverlayedBackend { - pub fn new(backend: B) -> Self { + pub fn new(backend: B, accessed: BTreeSet<(H160, Option)>) -> Self { Self { backend, substate: Box::new(Substate::new()), - accessed: BTreeSet::new(), + accessed, } } + + pub fn deconstruct(self) -> (B, OverlayedChangeSet) { + ( + self.backend, + OverlayedChangeSet { + logs: self.substate.logs, + balances: self.substate.balances, + codes: self.substate.codes, + nonces: self.substate.nonces, + storage_resets: self.substate.storage_resets, + storages: self.substate.storages, + deletes: self.substate.deletes, + }, + ) + } +} + +impl RuntimeEnvironment for OverlayedBackend { + fn block_hash(&self, number: U256) -> H256 { + self.backend.block_hash(number) + } + + fn block_number(&self) -> U256 { + self.backend.block_number() + } + + fn block_coinbase(&self) -> H160 { + self.backend.block_coinbase() + } + + fn block_timestamp(&self) -> U256 { + self.backend.block_timestamp() + } + + fn block_difficulty(&self) -> U256 { + self.backend.block_difficulty() + } + + fn block_randomness(&self) -> Option { + self.backend.block_randomness() + } + + fn block_gas_limit(&self) -> U256 { + self.backend.block_gas_limit() + } + + fn block_base_fee_per_gas(&self) -> U256 { + self.backend.block_base_fee_per_gas() + } + + fn chain_id(&self) -> U256 { + self.backend.chain_id() + } } impl RuntimeBaseBackend for OverlayedBackend { @@ -109,6 +177,10 @@ impl RuntimeBackend for OverlayedBackend { } fn deposit(&mut self, target: H160, value: U256) { + if value == U256::zero() { + return; + } + let current_balance = self.balance(target); self.substate .balances @@ -116,6 +188,10 @@ impl RuntimeBackend for OverlayedBackend { } fn withdrawal(&mut self, source: H160, value: U256) -> Result<(), ExitError> { + if value == U256::zero() { + return Ok(()); + } + let current_balance = self.balance(source); if current_balance < value { return Err(ExitException::OutOfFund.into());