Created
October 29, 2025 00:18
-
-
Save jsign/7ebad931597f77dbfd5af2c97543d301 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![warn(unused_crate_dependencies)] | |
| use core::panic; | |
| use std::sync::Arc; | |
| use alloy_primitives::{keccak256, Bytes, U256}; | |
| use alloy_rlp::Encodable; | |
| use reth_ethereum::{ | |
| chainspec::ChainSpecBuilder, | |
| consensus::validate_block_post_execution, | |
| evm::{ | |
| primitives::{execute::Executor, ConfigureEvm}, | |
| revm::{database::StateProviderDatabase, db::AccountState, State}, | |
| EthEvmConfig, | |
| }, | |
| network::types::BlockHashOrNumber, | |
| node::EthereumNode, | |
| primitives::AlloyBlockHeader, | |
| provider::{providers::ReadOnlyConfig, BlockReader}, | |
| TransactionSigned, | |
| }; | |
| use reth_execution_types::witness::FlatWitnessRecord; | |
| use reth_stateless::{flat_witness::FlatExecutionWitness, UncompressedPublicKey}; | |
| use reth_storage_api::BlockNumReader; | |
| use reth_storage_api::HeaderProvider; | |
| use revm::Database; | |
| fn main() -> eyre::Result<()> { | |
| let datadir = std::env::var("RETH_DATADIR")?; | |
| let chain_spec = Arc::new(ChainSpecBuilder::mainnet().build()); | |
| let factory = EthereumNode::provider_factory_builder() | |
| .open_read_only(chain_spec.clone(), ReadOnlyConfig::from_datadir(datadir))?; | |
| let provider = factory.provider()?; | |
| let block_num = provider.best_block_number().unwrap(); | |
| let block = provider | |
| .recovered_block(BlockHashOrNumber::Number(block_num), Default::default()) | |
| .unwrap() | |
| .unwrap(); | |
| let evm_config = EthEvmConfig::new(chain_spec.clone()); | |
| let block_number = block.header().number(); | |
| println!("Executing block number: {block_number}"); | |
| let (witness_record, lowest_block_number) = { | |
| let state_provider = provider.history_by_block_hash(block.parent_hash()).unwrap(); | |
| let db = StateProviderDatabase::new(state_provider); | |
| let block_executor = evm_config.executor(db); | |
| let mut witness_record = FlatWitnessRecord::default(); | |
| let mut lowest_block_number = Default::default(); | |
| let output = block_executor | |
| .execute_with_state_closure(&block, |statedb: &State<_>| { | |
| witness_record.record_executed_state(statedb); | |
| lowest_block_number = statedb.block_hashes.keys().next().copied() | |
| }) | |
| .expect("Stateful execution failed"); | |
| validate_block_post_execution(&block, &chain_spec, &output.receipts, &output.requests) | |
| .expect("Stateful execution post-execution checks failed"); | |
| (witness_record, lowest_block_number) | |
| }; | |
| { | |
| let state_provider = provider.history_by_block_hash(block.parent_hash()).unwrap(); | |
| let pre_state = state_provider.flat_witness(witness_record).unwrap(); | |
| let smallest = match lowest_block_number { | |
| Some(smallest) => smallest, | |
| None => { | |
| // Return only the parent header, if there were no calls to the | |
| // BLOCKHASH opcode. | |
| block_number.saturating_sub(1) | |
| } | |
| }; | |
| let range = smallest..block_number; | |
| let headers: Vec<Bytes> = provider | |
| .headers_range(range.clone()) | |
| .unwrap() | |
| .into_iter() | |
| .map(|header| { | |
| let mut serialized_header = Vec::new(); | |
| header.encode(&mut serialized_header); | |
| serialized_header.into() | |
| }) | |
| .collect(); | |
| let block_hashes = headers | |
| .iter() | |
| .zip(range) | |
| .map(|(bytes, num)| (U256::from(num), keccak256(bytes))) | |
| .collect(); | |
| let parent = headers.last().unwrap().clone(); | |
| let flat_witness = FlatExecutionWitness::new(pre_state, block_hashes, parent); | |
| let mut db = StateProviderDatabase::new(state_provider); | |
| for (address, flatdb_account) in flat_witness.state.accounts.iter() { | |
| let trie_account = db.basic(*address).unwrap(); | |
| let flatdb_account_info = (flatdb_account.account_state != AccountState::NotExisting) | |
| .then_some(flatdb_account.clone().info); | |
| if trie_account != flatdb_account_info { | |
| panic!( | |
| "Mismatch in account info for address {:?}: trie={:?}, flatdb={:?}", | |
| address, trie_account, flatdb_account_info | |
| ); | |
| } | |
| for (slot, value) in flatdb_account.storage.iter() { | |
| let trie_value = db.storage(*address, *slot).unwrap(); | |
| if trie_value != *value { | |
| panic!( | |
| "Mismatch in storage for address {:?} at slot {:?}: trie={:?}, flatdb={:?}", | |
| address, slot, trie_value, value | |
| ); | |
| } | |
| } | |
| } | |
| let public_keys = recover_signers(block.body().transactions()) | |
| .expect("Failed to recover public keys from transaction signatures"); | |
| reth_stateless::stateless_validation_with_flatdb( | |
| block.into_block(), | |
| public_keys, | |
| flat_witness, | |
| chain_spec, | |
| evm_config, | |
| ) | |
| .unwrap(); | |
| } | |
| Ok(()) | |
| } | |
| fn recover_signers<'a, I>(txs: I) -> Result<Vec<UncompressedPublicKey>, Box<dyn std::error::Error>> | |
| where | |
| I: IntoIterator<Item = &'a TransactionSigned>, | |
| { | |
| txs.into_iter() | |
| .enumerate() | |
| .map(|(i, tx)| { | |
| tx.signature() | |
| .recover_from_prehash(&tx.signature_hash()) | |
| .map(|keys| { | |
| UncompressedPublicKey( | |
| keys.to_encoded_point(false).as_bytes().try_into().unwrap(), | |
| ) | |
| }) | |
| .map_err(|e| format!("failed to recover signature for tx #{i}: {e}").into()) | |
| }) | |
| .collect::<Result<Vec<UncompressedPublicKey>, _>>() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment