mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +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
|
# Note: Requires cloning https://github.com/ethereum/tests
|
||||||
#
|
#
|
||||||
# cd testing/ef-tests && git clone https://github.com/ethereum/tests 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`.
|
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>
|
//! Test runners for `BlockchainTests` in <https://github.com/ethereum/tests>
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::{BlockchainTest, ForkSpec, RootOrState},
|
models::{BlockchainTest, ForkSpec},
|
||||||
Case, Error, Suite,
|
Case, Error, Suite,
|
||||||
};
|
};
|
||||||
use alloy_rlp::Decodable;
|
use alloy_rlp::Decodable;
|
||||||
use reth_db::test_utils::create_test_rw_db;
|
use reth_db::test_utils::create_test_rw_db;
|
||||||
use reth_primitives::{BlockBody, SealedBlock};
|
use reth_primitives::{BlockBody, SealedBlock};
|
||||||
use reth_provider::{BlockWriter, ProviderFactory};
|
use reth_provider::{BlockWriter, HashingWriter, ProviderFactory};
|
||||||
use reth_stages::{stages::ExecutionStage, ExecInput, Stage};
|
use reth_stages::{stages::ExecutionStage, ExecInput, Stage};
|
||||||
use std::{collections::BTreeMap, fs, path::Path, sync::Arc};
|
use std::{collections::BTreeMap, fs, path::Path, sync::Arc};
|
||||||
|
|
||||||
@ -88,8 +88,8 @@ impl Case for BlockchainTestCase {
|
|||||||
let mut last_block = None;
|
let mut last_block = None;
|
||||||
for block in case.blocks.iter() {
|
for block in case.blocks.iter() {
|
||||||
let decoded = SealedBlock::decode(&mut block.rlp.as_ref())?;
|
let decoded = SealedBlock::decode(&mut block.rlp.as_ref())?;
|
||||||
last_block = Some(decoded.number);
|
provider.insert_block(decoded.clone(), None, None)?;
|
||||||
provider.insert_block(decoded, None, None)?;
|
last_block = Some(decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call execution stage
|
// Call execution stage
|
||||||
@ -98,31 +98,36 @@ impl Case for BlockchainTestCase {
|
|||||||
Arc::new(case.network.clone().into()),
|
Arc::new(case.network.clone().into()),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let target = last_block.as_ref().map(|b| b.number);
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not build tokio RT")
|
.expect("Could not build tokio RT")
|
||||||
.block_on(async {
|
.block_on(async {
|
||||||
// ignore error
|
// ignore error
|
||||||
let _ = stage
|
let _ =
|
||||||
.execute(&provider, ExecInput { target: last_block, checkpoint: None })
|
stage.execute(&provider, ExecInput { target, checkpoint: None }).await;
|
||||||
.await;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate post state
|
// Validate post state
|
||||||
match &case.post_state {
|
if let Some(state) = &case.post_state {
|
||||||
Some(RootOrState::Root(root)) => {
|
|
||||||
// TODO: We should really check the state root here...
|
|
||||||
println!("Post-state root: #{root:?}")
|
|
||||||
}
|
|
||||||
Some(RootOrState::State(state)) => {
|
|
||||||
for (&address, account) in state.iter() {
|
for (&address, account) in state.iter() {
|
||||||
account.assert_db(address, provider.tx_ref())?;
|
account.assert_db(address, provider.tx_ref())?;
|
||||||
}
|
}
|
||||||
}
|
} else if let Some(expected_state_root) = case.post_state_hash {
|
||||||
None => println!("No post-state"),
|
// `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);
|
drop(provider);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -26,7 +26,9 @@ pub struct BlockchainTest {
|
|||||||
/// Block data.
|
/// Block data.
|
||||||
pub blocks: Vec<Block>,
|
pub blocks: Vec<Block>,
|
||||||
/// The expected post state.
|
/// 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.
|
/// The test pre-state.
|
||||||
pub pre: State,
|
pub pre: State,
|
||||||
/// Hash of the best block.
|
/// Hash of the best block.
|
||||||
@ -153,23 +155,28 @@ impl State {
|
|||||||
Tx: DbTxMut<'a>,
|
Tx: DbTxMut<'a>,
|
||||||
{
|
{
|
||||||
for (&address, account) in self.0.iter() {
|
for (&address, account) in self.0.iter() {
|
||||||
|
let hashed_address = keccak256(address);
|
||||||
let has_code = !account.code.is_empty();
|
let has_code = !account.code.is_empty();
|
||||||
let code_hash = has_code.then(|| keccak256(&account.code));
|
let code_hash = has_code.then(|| keccak256(&account.code));
|
||||||
tx.put::<tables::PlainAccountState>(
|
let reth_account = RethAccount {
|
||||||
address,
|
|
||||||
RethAccount {
|
|
||||||
balance: account.balance.0,
|
balance: account.balance.0,
|
||||||
nonce: account.nonce.0.to::<u64>(),
|
nonce: account.nonce.0.to::<u64>(),
|
||||||
bytecode_hash: code_hash,
|
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 {
|
if let Some(code_hash) = code_hash {
|
||||||
tx.put::<tables::Bytecodes>(code_hash, Bytecode::new_raw(account.code.clone()))?;
|
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>(
|
tx.put::<tables::PlainStorageState>(
|
||||||
address,
|
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.
|
/// An account.
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, Deserialize, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
|
|||||||
@ -17,6 +17,9 @@ pub enum Error {
|
|||||||
/// The test was skipped
|
/// The test was skipped
|
||||||
#[error("Test was skipped")]
|
#[error("Test was skipped")]
|
||||||
Skipped,
|
Skipped,
|
||||||
|
/// No post state found in test
|
||||||
|
#[error("No post state found for validation")]
|
||||||
|
MissingPostState,
|
||||||
/// An IO error occurred
|
/// An IO error occurred
|
||||||
#[error("An error occurred interacting with the file system at {path}: {error}")]
|
#[error("An error occurred interacting with the file system at {path}: {error}")]
|
||||||
Io {
|
Io {
|
||||||
|
|||||||
Reference in New Issue
Block a user