Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initital implementation of overlayed backend #271

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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