mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 02:49:55 +00:00
feat(ef-tests): validate state root (#4995)
This commit is contained in:
@ -100,7 +100,7 @@ cargo test --workspace --features geth-tests
|
||||
# Note: Requires cloning https://github.com/ethereum/tests
|
||||
#
|
||||
# cd testing/ef-tests && git clone https://github.com/ethereum/tests ethereum-tests
|
||||
cargo test --workspace --features ef-tests
|
||||
cargo test -p ef-tests --features ef-tests
|
||||
```
|
||||
|
||||
We recommend using [`cargo nextest`](https://nexte.st/) to speed up testing. With nextest installed, simply substitute `cargo test` with `cargo nextest run`.
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
//! Test runners for `BlockchainTests` in <https://github.com/ethereum/tests>
|
||||
|
||||
use crate::{
|
||||
models::{BlockchainTest, ForkSpec, RootOrState},
|
||||
models::{BlockchainTest, ForkSpec},
|
||||
Case, Error, Suite,
|
||||
};
|
||||
use alloy_rlp::Decodable;
|
||||
use reth_db::test_utils::create_test_rw_db;
|
||||
use reth_primitives::{BlockBody, SealedBlock};
|
||||
use reth_provider::{BlockWriter, ProviderFactory};
|
||||
use reth_provider::{BlockWriter, HashingWriter, ProviderFactory};
|
||||
use reth_stages::{stages::ExecutionStage, ExecInput, Stage};
|
||||
use std::{collections::BTreeMap, fs, path::Path, sync::Arc};
|
||||
|
||||
@ -88,8 +88,8 @@ impl Case for BlockchainTestCase {
|
||||
let mut last_block = None;
|
||||
for block in case.blocks.iter() {
|
||||
let decoded = SealedBlock::decode(&mut block.rlp.as_ref())?;
|
||||
last_block = Some(decoded.number);
|
||||
provider.insert_block(decoded, None, None)?;
|
||||
provider.insert_block(decoded.clone(), None, None)?;
|
||||
last_block = Some(decoded);
|
||||
}
|
||||
|
||||
// Call execution stage
|
||||
@ -98,31 +98,36 @@ impl Case for BlockchainTestCase {
|
||||
Arc::new(case.network.clone().into()),
|
||||
));
|
||||
|
||||
let target = last_block.as_ref().map(|b| b.number);
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.expect("Could not build tokio RT")
|
||||
.block_on(async {
|
||||
// ignore error
|
||||
let _ = stage
|
||||
.execute(&provider, ExecInput { target: last_block, checkpoint: None })
|
||||
.await;
|
||||
let _ =
|
||||
stage.execute(&provider, ExecInput { target, checkpoint: None }).await;
|
||||
});
|
||||
}
|
||||
|
||||
// Validate post state
|
||||
match &case.post_state {
|
||||
Some(RootOrState::Root(root)) => {
|
||||
// TODO: We should really check the state root here...
|
||||
println!("Post-state root: #{root:?}")
|
||||
if let Some(state) = &case.post_state {
|
||||
for (&address, account) in state.iter() {
|
||||
account.assert_db(address, provider.tx_ref())?;
|
||||
}
|
||||
Some(RootOrState::State(state)) => {
|
||||
for (&address, account) in state.iter() {
|
||||
account.assert_db(address, provider.tx_ref())?;
|
||||
}
|
||||
}
|
||||
None => println!("No post-state"),
|
||||
} else if let Some(expected_state_root) = case.post_state_hash {
|
||||
// `insert_hashes` will insert hashed data, compute the state root and match it to
|
||||
// expected internally
|
||||
let last_block = last_block.unwrap_or_default();
|
||||
provider.insert_hashes(
|
||||
0..=last_block.number,
|
||||
last_block.hash,
|
||||
expected_state_root,
|
||||
)?;
|
||||
} else {
|
||||
return Err(Error::MissingPostState)
|
||||
}
|
||||
|
||||
// Drop provider without committing to the database.
|
||||
drop(provider);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@ -26,7 +26,9 @@ pub struct BlockchainTest {
|
||||
/// Block data.
|
||||
pub blocks: Vec<Block>,
|
||||
/// The expected post state.
|
||||
pub post_state: Option<RootOrState>,
|
||||
pub post_state: Option<BTreeMap<Address, Account>>,
|
||||
/// The expected post state merkle root.
|
||||
pub post_state_hash: Option<B256>,
|
||||
/// The test pre-state.
|
||||
pub pre: State,
|
||||
/// Hash of the best block.
|
||||
@ -153,23 +155,28 @@ impl State {
|
||||
Tx: DbTxMut<'a>,
|
||||
{
|
||||
for (&address, account) in self.0.iter() {
|
||||
let hashed_address = keccak256(address);
|
||||
let has_code = !account.code.is_empty();
|
||||
let code_hash = has_code.then(|| keccak256(&account.code));
|
||||
tx.put::<tables::PlainAccountState>(
|
||||
address,
|
||||
RethAccount {
|
||||
balance: account.balance.0,
|
||||
nonce: account.nonce.0.to::<u64>(),
|
||||
bytecode_hash: code_hash,
|
||||
},
|
||||
)?;
|
||||
let reth_account = RethAccount {
|
||||
balance: account.balance.0,
|
||||
nonce: account.nonce.0.to::<u64>(),
|
||||
bytecode_hash: code_hash,
|
||||
};
|
||||
tx.put::<tables::PlainAccountState>(address, reth_account)?;
|
||||
tx.put::<tables::HashedAccount>(hashed_address, reth_account)?;
|
||||
if let Some(code_hash) = code_hash {
|
||||
tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(account.code.clone()))?;
|
||||
}
|
||||
account.storage.iter().try_for_each(|(k, v)| {
|
||||
account.storage.iter().filter(|(_, v)| v.0 != U256::ZERO).try_for_each(|(k, v)| {
|
||||
let storage_key = B256::from_slice(&k.0.to_be_bytes::<32>());
|
||||
tx.put::<tables::PlainStorageState>(
|
||||
address,
|
||||
StorageEntry { key: B256::from_slice(&k.0.to_be_bytes::<32>()), value: v.0 },
|
||||
StorageEntry { key: storage_key, value: v.0 },
|
||||
)?;
|
||||
tx.put::<tables::HashedStorage>(
|
||||
hashed_address,
|
||||
StorageEntry { key: keccak256(storage_key), value: v.0 },
|
||||
)
|
||||
})?;
|
||||
}
|
||||
@ -186,16 +193,6 @@ impl Deref for State {
|
||||
}
|
||||
}
|
||||
|
||||
/// Merkle root hash or storage accounts.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RootOrState {
|
||||
/// If state is too big, only state root is present
|
||||
Root(B256),
|
||||
/// State
|
||||
State(BTreeMap<Address, Account>),
|
||||
}
|
||||
|
||||
/// An account.
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
|
||||
@ -17,6 +17,9 @@ pub enum Error {
|
||||
/// The test was skipped
|
||||
#[error("Test was skipped")]
|
||||
Skipped,
|
||||
/// No post state found in test
|
||||
#[error("No post state found for validation")]
|
||||
MissingPostState,
|
||||
/// An IO error occurred
|
||||
#[error("An error occurred interacting with the file system at {path}: {error}")]
|
||||
Io {
|
||||
|
||||
Reference in New Issue
Block a user