mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
test(execution): chain test state check, execution bug fixes/upgrades (#472)
* temp: header stage backoff stand-in * feat(execution): Check chain post state, fix StateProviderLatest and evm return * Disable receipt merkle tree check * update and merge * Fix storage double values in dup table * fmt * Update bin/reth/src/test_eth_chain/runner.rs Co-authored-by: Georgios Konstantopoulos <me@gakonst.com> * Enable receipt root check after byzantium * Receipt inner rlp without header for proof root * some cleanup nits * nit Co-authored-by: Oliver Nordbjerg <hi@notbjerg.me> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
@ -21,7 +21,7 @@ pub struct BlockchainTestData {
|
||||
/// Blocks.
|
||||
pub blocks: Vec<Block>,
|
||||
/// Post state.
|
||||
pub post_state: Option<State>,
|
||||
pub post_state: Option<RootOrState>,
|
||||
/// Pre state.
|
||||
pub pre: State,
|
||||
/// Hash of best block.
|
||||
@ -112,6 +112,18 @@ pub struct Block {
|
||||
pub transactions: Option<Vec<Transaction>>,
|
||||
/// Uncle/ommer headers
|
||||
pub uncle_headers: Option<Vec<Header>>,
|
||||
/// Transaction Sequence
|
||||
pub transaction_sequence: Option<Vec<TransactionSequence>>,
|
||||
}
|
||||
|
||||
/// Transaction Sequence in block
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionSequence {
|
||||
exception: String,
|
||||
raw_bytes: Bytes,
|
||||
valid: String,
|
||||
}
|
||||
|
||||
/// Ethereum blockchain test data State.
|
||||
@ -191,6 +203,9 @@ pub enum ForkSpec {
|
||||
/// After Merge Init Code test
|
||||
#[serde(alias = "Merge+3860")]
|
||||
MergeMeterInitCode,
|
||||
/// After Merge plus new PUSH0 opcode
|
||||
#[serde(alias = "Merge+3855")]
|
||||
MergePush0,
|
||||
}
|
||||
|
||||
impl From<ForkSpec> for reth_executor::SpecUpgrades {
|
||||
@ -214,6 +229,7 @@ impl From<ForkSpec> for reth_executor::SpecUpgrades {
|
||||
ForkSpec::Merge => Self::new_paris_activated(),
|
||||
ForkSpec::MergeEOF => Self::new_paris_activated(),
|
||||
ForkSpec::MergeMeterInitCode => Self::new_paris_activated(),
|
||||
ForkSpec::MergePush0 => Self::new_paris_activated(),
|
||||
ForkSpec::ByzantiumToConstantinopleAt5 | ForkSpec::Constantinople => {
|
||||
panic!("Overriden with PETERSBURG")
|
||||
}
|
||||
|
||||
@ -1,22 +1,27 @@
|
||||
use super::models::Test;
|
||||
use crate::test_eth_chain::models::ForkSpec;
|
||||
use crate::test_eth_chain::models::{ForkSpec, RootOrState};
|
||||
use eyre::eyre;
|
||||
use reth_db::{
|
||||
cursor::DbCursorRO,
|
||||
database::Database,
|
||||
mdbx::{test_utils::create_test_rw_db, WriteMap},
|
||||
tables,
|
||||
transaction::{DbTx, DbTxMut},
|
||||
Error as DbError,
|
||||
};
|
||||
use reth_executor::SpecUpgrades;
|
||||
use reth_primitives::{
|
||||
keccak256, Account as RethAccount, BigEndianHash, SealedBlock, SealedHeader, StorageEntry, H256,
|
||||
keccak256, Account as RethAccount, Address, JsonU256, SealedBlock, SealedHeader, StorageEntry,
|
||||
H256, U256,
|
||||
};
|
||||
use reth_rlp::Decodable;
|
||||
use reth_stages::{stages::execution::ExecutionStage, ExecInput, Stage, Transaction};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, info};
|
||||
|
||||
/// Tests are test edge cases that are not possible to happen on mainnet, so we are skipping them.
|
||||
pub fn should_skip(path: &Path) -> bool {
|
||||
@ -81,7 +86,8 @@ pub async fn run_test(path: PathBuf) -> eyre::Result<()> {
|
||||
ForkSpec::ByzantiumToConstantinopleAt5 |
|
||||
ForkSpec::Constantinople |
|
||||
ForkSpec::MergeEOF |
|
||||
ForkSpec::MergeMeterInitCode
|
||||
ForkSpec::MergeMeterInitCode |
|
||||
ForkSpec::MergePush0,
|
||||
) {
|
||||
continue
|
||||
}
|
||||
@ -126,10 +132,10 @@ pub async fn run_test(path: PathBuf) -> eyre::Result<()> {
|
||||
tx.put::<tables::Bytecodes>(code_hash, account.code.to_vec())?;
|
||||
}
|
||||
account.storage.iter().try_for_each(|(k, v)| {
|
||||
tx.put::<tables::PlainStorageState>(
|
||||
address,
|
||||
StorageEntry { key: H256::from_uint(&k.0), value: v.0 },
|
||||
)
|
||||
tracing::trace!("Update storage: {address} key:{:?} val:{:?}", k.0, v.0);
|
||||
let mut key = H256::zero();
|
||||
k.0.to_big_endian(&mut key.0);
|
||||
tx.put::<tables::PlainStorageState>(address, StorageEntry { key, value: v.0 })
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
@ -138,17 +144,120 @@ pub async fn run_test(path: PathBuf) -> eyre::Result<()> {
|
||||
// Commit the pre suite state
|
||||
tx.commit()?;
|
||||
|
||||
let storage = db.view(|tx| -> Result<_, DbError> {
|
||||
let mut cursor = tx.cursor_dup::<tables::PlainStorageState>()?;
|
||||
let walker = cursor.first()?.map(|first| cursor.walk(first.0)).transpose()?;
|
||||
Ok(walker.map(|mut walker| {
|
||||
let mut map: HashMap<Address, HashMap<U256, U256>> = HashMap::new();
|
||||
while let Some(Ok((address, slot))) = walker.next() {
|
||||
let key = U256::from_big_endian(&slot.key.0);
|
||||
map.entry(address).or_default().insert(key, slot.value);
|
||||
}
|
||||
map
|
||||
}))
|
||||
})??;
|
||||
tracing::trace!("Pre state :{:?}", storage);
|
||||
|
||||
// Initialize the execution stage
|
||||
// Hardcode the chain_id to Ethereums 1.
|
||||
// Hardcode the chain_id to Ethereum 1.
|
||||
let mut stage =
|
||||
ExecutionStage::new(reth_executor::Config { chain_id: 1.into(), spec_upgrades });
|
||||
|
||||
// Call execution stage
|
||||
let input = ExecInput::default();
|
||||
stage.execute(&mut Transaction::new(db.as_ref())?, input).await?;
|
||||
{
|
||||
let mut transaction = Transaction::new(db.as_ref())?;
|
||||
|
||||
// ignore error
|
||||
let _ = stage.execute(&mut transaction, input).await;
|
||||
transaction.commit()?;
|
||||
}
|
||||
|
||||
// Validate post state
|
||||
//for post in
|
||||
match suite.post_state {
|
||||
Some(RootOrState::Root(root)) => {
|
||||
info!("Post state is root: #{root:?}")
|
||||
}
|
||||
Some(RootOrState::State(state)) => db.view(|tx| -> eyre::Result<()> {
|
||||
let mut cursor = tx.cursor_dup::<tables::PlainStorageState>()?;
|
||||
let walker = cursor.first()?.map(|first| cursor.walk(first.0)).transpose()?;
|
||||
let storage = walker.map(|mut walker| {
|
||||
let mut map: HashMap<Address, HashMap<U256, U256>> = HashMap::new();
|
||||
while let Some(Ok((address, slot))) = walker.next() {
|
||||
let key = U256::from_big_endian(&slot.key.0);
|
||||
map.entry(address).or_default().insert(key, slot.value);
|
||||
}
|
||||
map
|
||||
});
|
||||
tracing::trace!("Our storage:{:?}", storage);
|
||||
for (address, test_account) in state.iter() {
|
||||
// check account
|
||||
let our_account = tx
|
||||
.get::<tables::PlainAccountState>(*address)?
|
||||
.ok_or(eyre!("Account is missing:{address} expected:{:?}", test_account))?;
|
||||
if test_account.balance.0 != our_account.balance {
|
||||
return Err(eyre!(
|
||||
"Account {address} balance diff, expected {} got{}",
|
||||
test_account.balance.0,
|
||||
our_account.balance
|
||||
))
|
||||
}
|
||||
if test_account.nonce.0.as_u64() != our_account.nonce {
|
||||
return Err(eyre!(
|
||||
"Account {address} nonce diff, expected {} got {}",
|
||||
test_account.nonce.0,
|
||||
our_account.nonce
|
||||
))
|
||||
}
|
||||
if let Some(our_bytecode) = our_account.bytecode_hash {
|
||||
let test_bytecode = keccak256(test_account.code.as_ref());
|
||||
if our_bytecode != test_bytecode {
|
||||
return Err(eyre!(
|
||||
"Account {address} bytecode diff, expected: {} got: {:?}",
|
||||
test_account.code,
|
||||
our_account.bytecode_hash
|
||||
))
|
||||
}
|
||||
} else if !test_account.code.is_empty() {
|
||||
return Err(eyre!(
|
||||
"Account {address} bytecode diff, expected {} got empty bytecode",
|
||||
test_account.code,
|
||||
))
|
||||
}
|
||||
|
||||
// get walker if present
|
||||
if let Some(storage) = storage.as_ref() {
|
||||
// iterate over storages
|
||||
for (JsonU256(key), JsonU256(value)) in test_account.storage.iter() {
|
||||
let our_value = storage
|
||||
.get(address)
|
||||
.ok_or(eyre!(
|
||||
"Missing storage from test {storage:?} got {:?}",
|
||||
test_account.storage
|
||||
))?
|
||||
.get(key)
|
||||
.ok_or(eyre!(
|
||||
"Slot is missing from table {storage:?} got:{:?}",
|
||||
test_account.storage
|
||||
))?;
|
||||
if value != our_value {
|
||||
return Err(eyre!(
|
||||
"Storage diff we got {address}: {storage:?} but expect: {:?}",
|
||||
test_account.storage
|
||||
))
|
||||
}
|
||||
}
|
||||
} else if !test_account.storage.is_empty() {
|
||||
return Err(eyre!(
|
||||
"Walker is not present, but storage is not empty.{:?}",
|
||||
test_account.storage
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})??,
|
||||
None => info!("Post state is none"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user