feat(ef-tests): validate state root (#4995)

This commit is contained in:
Roman Krasiuk
2023-10-12 16:44:41 +03:00
committed by GitHub
parent 422f38ac06
commit d6ea90fd33
4 changed files with 44 additions and 39 deletions

View File

@ -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(())

View File

@ -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)]

View File

@ -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 {