Skip to content

Commit

Permalink
Merge pull request #271 from rust-ethereum/sp-overlayed
Browse files Browse the repository at this point in the history
Initital implementation of overlayed backend
  • Loading branch information
sorpaas authored Jan 23, 2024
2 parents 9fdc7fe + def71a6 commit 0d2ce88
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 185 deletions.
1 change: 1 addition & 0 deletions interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions interpreter/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub struct Log {
pub data: Vec<u8>,
}

#[auto_impl::auto_impl(&, Box)]
pub trait RuntimeEnvironment {
/// Get environmental block hash.
fn block_hash(&self, number: U256) -> H256;
Expand All @@ -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;
Expand Down
1 change: 0 additions & 1 deletion jsontests/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)| {
Expand Down
199 changes: 35 additions & 164 deletions jsontests/src/in_memory.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -27,7 +21,6 @@ pub struct InMemoryAccount {
pub code: Vec<u8>,
pub nonce: U256,
pub storage: BTreeMap<H256, H256>,
pub original_storage: BTreeMap<H256, H256>,
}

#[derive(Clone, Debug)]
Expand All @@ -36,38 +29,42 @@ pub struct InMemorySuicideInfo {
}

#[derive(Clone, Debug)]
pub struct InMemoryLayer {
pub struct InMemoryBackend {
pub environment: InMemoryEnvironment,
pub state: BTreeMap<H160, InMemoryAccount>,
pub logs: Vec<Log>,
pub suicides: Vec<InMemorySuicideInfo>,
pub hots: BTreeSet<(H160, Option<H256>)>,
}

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<InMemoryLayer>,
}
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);
}
}
}

Expand Down Expand Up @@ -115,30 +112,27 @@ 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())
.balance
}

fn code(&self, address: H160) -> Vec<u8> {
self.current_layer()
.state
self.state
.get(&address)
.cloned()
.unwrap_or(Default::default())
.code
}

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())
Expand All @@ -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<H256>) -> bool {
!self.current_layer().hots.contains(&(address, index))
}

fn mark_hot(&mut self, address: H160, index: Option<H256>) {
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<u8>) -> 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 => (),
}
}
}
41 changes: 22 additions & 19 deletions jsontests/src/run.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -136,7 +137,6 @@ pub fn run_test(
balance: account.balance,
code: account.code.0,
nonce: account.nonce,
original_storage: storage.clone(),
storage,
},
)
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "nightly"
channel = "1.75.0"
profile = "minimal"
components = [ "rustfmt", "clippy" ]
3 changes: 3 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
//! pushing/poping layers are dealt by extern functions), layers are handled
//! internally inside a backend.

mod overlayed;

pub use self::overlayed::{OverlayedBackend, OverlayedChangeSet};
pub use evm_interpreter::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment};

/// Backend with layers that can transactionally be committed or discarded.
Expand Down
Loading

0 comments on commit 0d2ce88

Please sign in to comment.