Skip to content

Commit

Permalink
feat: State with account status (#499)
Browse files Browse the repository at this point in the history
* wip separate initial checks

* tests passing, consolidate some checks

* test

* feat: add transfer test

* temp

* Update crates/interpreter/src/gas/calc.rs

* / Initial gas that is deducted from the transaction gas limit.

* fmt

* account states

* temp

* Global account BlockState. wip

* few comments

* wip

* Tests working

* wip changes

* work on update and revert

* AccountState as a simple enum flag

* Add storage for EmptyEip161 account

* split state to component files

* continue spliting, wip State

* feat: Add initcode limit to be double of bytecode limit (#491)

* bigger cleanup, structure it

* wip reverts and the flow

* increments balance and some wip on reverts

* feat: Run CI on release branches (#506)

* feat: Add initcode limit to be double of bytecode limit (#503)

* chore: Move Precompiles to EVMData so Inspector can access it (#504)

* fix previous commit (#507)

* wip cache account, changeset and revert

* plain state wip, reverts compiles now

* working changes

* working and, added timestamps and println

* Add reverts

* clippy happy, some cleanup, work on reverts

* wip on spliting and creating BundleState

* needed things for integration

* Few utilities, emptydb with typed error

* wip

* fix some things

* cleanup

* refactoring

* clippy and some cleanup

* add state clear flag

* bugs fixes

* add state builder

* some cleanup and being more strict

* cleanup, and simplification

* check nonce for account

* make storage was_destroyed

* dont store storage when not needed

* dont ask db for destroyed accounts

* debug logs

* test account

* more debug

* save previous state on double touch

* check if pre eip161 touched account is already empty

* Changr address

* diferent debug account

* set wipe flag for destroyed

* remove some test clones

* nit

* clippy

* make state behind std

* lto

* cleanup

* set bigger stack size in tests

* fmt
  • Loading branch information
rakita authored Aug 8, 2023
1 parent 1478724 commit ef57a46
Show file tree
Hide file tree
Showing 35 changed files with 2,455 additions and 148 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions bins/revme/src/statetest/merkle_trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use hash_db::Hasher;
use plain_hasher::PlainHasher;
use primitive_types::{H160, H256};
use revm::{
db::DbAccount,
db::PlainAccount,
primitives::{keccak256, Log, B160, B256, U256},
};
use rlp::RlpStream;
Expand All @@ -30,10 +30,13 @@ pub fn log_rlp_hash(logs: Vec<Log>) -> B256 {
keccak256(&out)
}

pub fn state_merkle_trie_root(accounts: impl Iterator<Item = (B160, DbAccount)>) -> B256 {
pub fn state_merkle_trie_root<'a>(
accounts: impl IntoIterator<Item = (B160, &'a PlainAccount)>,
) -> B256 {
let vec = accounts
.into_iter()
.map(|(address, info)| {
let acc_root = trie_account_rlp(&info);
let acc_root = trie_account_rlp(info);
(H160::from(address.0), acc_root)
})
.collect();
Expand All @@ -42,7 +45,7 @@ pub fn state_merkle_trie_root(accounts: impl Iterator<Item = (B160, DbAccount)>)
}

/// Returns the RLP for this account.
pub fn trie_account_rlp(acc: &DbAccount) -> Bytes {
pub fn trie_account_rlp(acc: &PlainAccount) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&acc.info.nonce);
stream.append(&acc.info.balance);
Expand Down
4 changes: 2 additions & 2 deletions bins/revme/src/statetest/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bytes::Bytes;
use revm::primitives::{B160, B256, U256};
use std::collections::{BTreeMap, HashMap};
use revm::primitives::{HashMap, B160, B256, U256};
use std::collections::BTreeMap;
mod deserializer;
mod spec;

Expand Down
95 changes: 52 additions & 43 deletions bins/revme/src/statetest/runner.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
use std::io::stdout;
use std::{
collections::HashMap,
ffi::OsStr,
path::{Path, PathBuf},
sync::{atomic::AtomicBool, Arc, Mutex},
time::{Duration, Instant},
};

use indicatif::ProgressBar;

use revm::inspectors::TracerEip3155;
use revm::{
db::AccountState,
interpreter::CreateScheme,
primitives::{Bytecode, Env, ExecutionResult, SpecId, TransactTo, B160, B256, U256},
};
use std::sync::atomic::Ordering;
use walkdir::{DirEntry, WalkDir};

use super::{
merkle_trie::{log_rlp_hash, state_merkle_trie_root},
models::{SpecName, TestSuit},
};
use hex_literal::hex;
use indicatif::ProgressBar;
use revm::inspectors::TracerEip3155;
use revm::primitives::keccak256;
use revm::{
interpreter::CreateScheme,
primitives::{Bytecode, Env, ExecutionResult, HashMap, SpecId, TransactTo, B160, B256, U256},
};
use std::sync::atomic::Ordering;
use thiserror::Error;
use walkdir::{DirEntry, WalkDir};

#[derive(Debug, Error)]
pub enum TestError {
Expand Down Expand Up @@ -62,10 +58,19 @@ pub fn execute_test_suit(
if path.file_name() == Some(OsStr::new("ValueOverflow.json")) {
return Ok(());
}

// precompiles having storage is not possible
if path.file_name() == Some(OsStr::new("RevertPrecompiledTouch_storage.json"))
|| path.file_name() == Some(OsStr::new("RevertPrecompiledTouch.json"))
{
return Ok(());
}

// txbyte is of type 02 and we dont parse tx bytes for this test to fail.
if path.file_name() == Some(OsStr::new("typeTwoBerlin.json")) {
return Ok(());
}

// Test checks if nonce overflows. We are handling this correctly but we are not parsing exception in testsuite
// There are more nonce overflow tests that are in internal call/create, and those tests are passing and are enabled.
if path.file_name() == Some(OsStr::new("CreateTransactionHighNonce.json")) {
Expand All @@ -78,7 +83,10 @@ pub fn execute_test_suit(
}

// Test check if gas price overflows, we handle this correctly but does not match tests specific exception.
if path.file_name() == Some(OsStr::new("HighGasPrice.json")) {
if path.file_name() == Some(OsStr::new("HighGasPrice.json"))
|| path.file_name() == Some(OsStr::new("CREATE_HighNonce.json"))
|| path.file_name() == Some(OsStr::new("CREATE_HighNonceMinus1.json"))
{
return Ok(());
}

Expand Down Expand Up @@ -153,19 +161,15 @@ pub fn execute_test_suit(

for (name, unit) in suit.0.into_iter() {
// Create database and insert cache
let mut database = revm::InMemoryDB::default();
for (address, info) in unit.pre.iter() {
let mut cache_state = revm::CacheState::new(false);
for (address, info) in unit.pre.into_iter() {
let acc_info = revm::primitives::AccountInfo {
balance: info.balance,
code_hash: keccak256(&info.code), // try with dummy hash.
code: Some(Bytecode::new_raw(info.code.clone())),
nonce: info.nonce,
};
database.insert_account_info(*address, acc_info);
// insert storage:
for (&slot, &value) in info.storage.iter() {
let _ = database.insert_account_storage(*address, slot, value);
}
cache_state.insert_account_with_storage(address, acc_info, info.storage.clone());
}
let mut env = Env::default();
// cfg env. SpecId is set down the road
Expand Down Expand Up @@ -247,9 +251,16 @@ pub fn execute_test_suit(
};
env.tx.transact_to = to;

let mut database_cloned = database.clone();
let mut cache = cache_state.clone();
cache.set_state_clear_flag(SpecId::enabled(
env.cfg.spec_id,
revm::primitives::SpecId::SPURIOUS_DRAGON,
));
let mut state = revm::db::StateBuilder::default()
.with_cached_prestate(cache)
.build();
let mut evm = revm::new();
evm.database(&mut database_cloned);
evm.database(&mut state);
evm.env = env.clone();
// do the deed

Expand All @@ -264,22 +275,8 @@ pub fn execute_test_suit(

*elapsed.lock().unwrap() += timer;

let is_legacy = !SpecId::enabled(
evm.env.cfg.spec_id,
revm::primitives::SpecId::SPURIOUS_DRAGON,
);
let db = evm.db().unwrap();
let state_root = state_merkle_trie_root(
db.accounts
.iter()
.filter(|(_address, acc)| {
(is_legacy && !matches!(acc.account_state, AccountState::NotExisting))
|| (!is_legacy
&& (!(acc.info.is_empty())
|| matches!(acc.account_state, AccountState::None)))
})
.map(|(k, v)| (*k, v.clone())),
);
let state_root = state_merkle_trie_root(db.cache.trie_account());
let logs = match &exec_result {
Ok(ExecutionResult::Success { logs, .. }) => logs.clone(),
_ => Vec::new(),
Expand All @@ -290,8 +287,16 @@ pub fn execute_test_suit(
"Roots did not match:\nState root: wanted {:?}, got {state_root:?}\nLogs root: wanted {:?}, got {logs_root:?}",
test.hash, test.logs
);
let mut database_cloned = database.clone();
evm.database(&mut database_cloned);

let mut cache = cache_state.clone();
cache.set_state_clear_flag(SpecId::enabled(
env.cfg.spec_id,
revm::primitives::SpecId::SPURIOUS_DRAGON,
));
let mut state = revm::db::StateBuilder::default()
.with_cached_prestate(cache)
.build();
evm.database(&mut state);
let _ =
evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false));
let db = evm.db().unwrap();
Expand Down Expand Up @@ -319,8 +324,12 @@ pub fn execute_test_suit(
println!("Output: {out:?} {path:?} UNIT_TEST:{name}\n");
}
}
println!("\nApplied state:\n{db:#?}\n");
println!(" TEST NAME: {:?}", name);
println!("\nApplied state:\n{:#?}\n", db.cache);
println!("\nState root: {state_root:?}\n");
println!("env.tx: {:?}\n", env.tx);
println!("env.block: {:?}\n", env.block);
println!("env.cfg: {:?}\n", env.cfg);
return Err(TestError::RootMismatch {
spec_id: env.cfg.spec_id,
id,
Expand Down Expand Up @@ -358,9 +367,9 @@ pub fn run(
let mut thread = std::thread::Builder::new();

// Allow bigger stack in debug mode to prevent stack overflow errors
if cfg!(debug_assertions) {
thread = thread.stack_size(4 * 1024 * 1024);
}
//if cfg!(debug_assertions) {
thread = thread.stack_size(4 * 1024 * 1024);
//}

joins.push(
thread
Expand Down
4 changes: 2 additions & 2 deletions crates/interpreter/src/instructions/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ pub fn selfdestruct<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Ho

pub fn prepare_create_inputs<const IS_CREATE2: bool, SPEC: Spec>(
interpreter: &mut Interpreter,
create_inputs: &mut Option<Box<CreateInputs>>,
host: &mut dyn Host,
create_inputs: &mut Option<Box<CreateInputs>>,
) {
check_staticcall!(interpreter);
if IS_CREATE2 {
Expand Down Expand Up @@ -328,7 +328,7 @@ pub fn create<const IS_CREATE2: bool, SPEC: Spec>(
host: &mut dyn Host,
) {
let mut create_input: Option<Box<CreateInputs>> = None;
prepare_create_inputs::<IS_CREATE2, SPEC>(interpreter, &mut create_input, host);
prepare_create_inputs::<IS_CREATE2, SPEC>(interpreter, host, &mut create_input);

let Some(mut create_input) = create_input else {
return;
Expand Down
11 changes: 1 addition & 10 deletions crates/interpreter/src/interpreter/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::opcode;
use crate::primitives::{Bytecode, BytecodeState, Bytes, B256};
use crate::primitives::{Bytecode, BytecodeState, Bytes};
use alloc::sync::Arc;
// use bitvec::order::Lsb0;
// use bitvec::prelude::bitvec;
Expand All @@ -15,7 +15,6 @@ use revm_primitives::{
///
/// If the bytecode is already analyzed, it is returned as-is.
pub fn to_analysed(bytecode: Bytecode) -> Bytecode {
let hash = bytecode.hash;
let (bytecode, len) = match bytecode.state {
BytecodeState::Raw => {
let len = bytecode.bytecode.len();
Expand All @@ -29,7 +28,6 @@ pub fn to_analysed(bytecode: Bytecode) -> Bytecode {

Bytecode {
bytecode,
hash,
state: BytecodeState::Analysed { len, jump_map },
}
}
Expand Down Expand Up @@ -67,7 +65,6 @@ fn analyze(code: &[u8]) -> JumpMap {
pub struct BytecodeLocked {
bytecode: Bytes,
len: usize,
hash: B256,
jump_map: JumpMap,
}

Expand All @@ -87,7 +84,6 @@ impl TryFrom<Bytecode> for BytecodeLocked {
Ok(BytecodeLocked {
bytecode: bytecode.bytecode,
len,
hash: bytecode.hash,
jump_map,
})
} else {
Expand All @@ -104,18 +100,13 @@ impl BytecodeLocked {
self.len
}

pub fn hash(&self) -> B256 {
self.hash
}

pub fn is_empty(&self) -> bool {
self.len == 0
}

pub fn unlock(self) -> Bytecode {
Bytecode {
bytecode: self.bytecode,
hash: self.hash,
state: BytecodeState::Analysed {
len: self.len,
jump_map: self.jump_map,
Expand Down
4 changes: 3 additions & 1 deletion crates/interpreter/src/interpreter/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ pub struct Memory {

impl Default for Memory {
fn default() -> Self {
Memory::new()
Self {
data: Vec::with_capacity(4 * 1024), // took it from evmone
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ readme = "../../README.md"
[dependencies]
bytes = { version = "1.4", default-features = false }
hashbrown = { version = "0.14" }
hex = { version = "0.4", default-features = false }
primitive-types = { version = "0.12", default-features = false }
rlp = { version = "0.5", default-features = false } # used for create2 address calculation
ruint = { version = "1.8.0", features = ["primitive-types", "rlp"] }
Expand All @@ -28,6 +27,8 @@ fixed-hash = { version = "0.8", default-features = false, features = [

#utility
hex-literal = "0.4"
hex = { version = "0.4", default-features = false }
to-binary = "0.4"
derive_more = "0.99"
enumn = "0.1"

Expand Down
Loading

0 comments on commit ef57a46

Please sign in to comment.