use std::sync::Arc; use eyre::OptionExt; use reth_chainspec::{ChainSpec, ChainSpecBuilder, EthereumHardfork, MAINNET}; use reth_evm::execute::{ BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor, }; use reth_evm_ethereum::execute::EthExecutorProvider; use reth_primitives::{ b256, constants::ETH_TO_WEI, Address, Block, BlockWithSenders, Genesis, GenesisAccount, Header, Receipt, Requests, SealedBlockWithSenders, Transaction, TxEip2930, TxKind, U256, }; use reth_provider::{BlockWriter as _, ExecutionOutcome, LatestStateProviderRef, ProviderFactory}; use reth_revm::database::StateProviderDatabase; use reth_testing_utils::generators::sign_tx_with_key_pair; use secp256k1::Keypair; pub(crate) fn to_execution_outcome( block_number: u64, block_execution_output: &BlockExecutionOutput, ) -> ExecutionOutcome { ExecutionOutcome { bundle: block_execution_output.state.clone(), receipts: block_execution_output.receipts.clone().into(), first_block: block_number, requests: vec![Requests(block_execution_output.requests.clone())], } } pub(crate) fn chain_spec(address: Address) -> Arc { // Create a chain spec with a genesis state that contains the // provided sender Arc::new( ChainSpecBuilder::default() .chain(MAINNET.chain) .genesis(Genesis { alloc: [( address, GenesisAccount { balance: U256::from(ETH_TO_WEI), ..Default::default() }, )] .into(), ..MAINNET.genesis.clone() }) .paris_activated() .build(), ) } pub(crate) fn execute_block_and_commit_to_database( provider_factory: &ProviderFactory, chain_spec: Arc, block: &BlockWithSenders, ) -> eyre::Result> where DB: reth_db_api::database::Database, { let provider = provider_factory.provider()?; // Execute the block to produce a block execution output let mut block_execution_output = EthExecutorProvider::ethereum(chain_spec) .executor(StateProviderDatabase::new(LatestStateProviderRef::new( provider.tx_ref(), provider.static_file_provider().clone(), ))) .execute(BlockExecutionInput { block, total_difficulty: U256::ZERO })?; block_execution_output.state.reverts.sort(); // Convert the block execution output to an execution outcome for committing to the database let execution_outcome = to_execution_outcome(block.number, &block_execution_output); // Commit the block's execution outcome to the database let provider_rw = provider_factory.provider_rw()?; let block = block.clone().seal_slow(); provider_rw.append_blocks_with_state( vec![block], execution_outcome, Default::default(), Default::default(), )?; provider_rw.commit()?; Ok(block_execution_output) } fn blocks( chain_spec: Arc, key_pair: Keypair, ) -> eyre::Result<(BlockWithSenders, BlockWithSenders)> { // First block has a transaction that transfers some ETH to zero address let block1 = Block { header: Header { parent_hash: chain_spec.genesis_hash(), receipts_root: b256!( "d3a6acf9a244d78b33831df95d472c4128ea85bf079a1d41e32ed0b7d2244c9e" ), difficulty: chain_spec.fork(EthereumHardfork::Paris).ttd().expect("Paris TTD"), number: 1, gas_limit: 21000, gas_used: 21000, ..Default::default() }, body: vec![sign_tx_with_key_pair( key_pair, Transaction::Eip2930(TxEip2930 { chain_id: chain_spec.chain.id(), nonce: 0, gas_limit: 21000, gas_price: 1_500_000_000, to: TxKind::Call(Address::ZERO), value: U256::from(0.1 * ETH_TO_WEI as f64), ..Default::default() }), )], ..Default::default() } .with_recovered_senders() .ok_or_eyre("failed to recover senders")?; // Second block resends the same transaction with increased nonce let block2 = Block { header: Header { parent_hash: block1.header.hash_slow(), receipts_root: b256!( "d3a6acf9a244d78b33831df95d472c4128ea85bf079a1d41e32ed0b7d2244c9e" ), difficulty: chain_spec.fork(EthereumHardfork::Paris).ttd().expect("Paris TTD"), number: 2, gas_limit: 21000, gas_used: 21000, ..Default::default() }, body: vec![sign_tx_with_key_pair( key_pair, Transaction::Eip2930(TxEip2930 { chain_id: chain_spec.chain.id(), nonce: 1, gas_limit: 21000, gas_price: 1_500_000_000, to: TxKind::Call(Address::ZERO), value: U256::from(0.1 * ETH_TO_WEI as f64), ..Default::default() }), )], ..Default::default() } .with_recovered_senders() .ok_or_eyre("failed to recover senders")?; Ok((block1, block2)) } pub(crate) fn blocks_and_execution_outputs( provider_factory: ProviderFactory, chain_spec: Arc, key_pair: Keypair, ) -> eyre::Result)>> where DB: reth_db_api::database::Database, { let (block1, block2) = blocks(chain_spec.clone(), key_pair)?; let block_output1 = execute_block_and_commit_to_database(&provider_factory, chain_spec.clone(), &block1)?; let block_output2 = execute_block_and_commit_to_database(&provider_factory, chain_spec, &block2)?; let block1 = block1.seal_slow(); let block2 = block2.seal_slow(); Ok(vec![(block1, block_output1), (block2, block_output2)]) } pub(crate) fn blocks_and_execution_outcome( provider_factory: ProviderFactory, chain_spec: Arc, key_pair: Keypair, ) -> eyre::Result<(Vec, ExecutionOutcome)> where DB: reth_db_api::database::Database, { let (block1, block2) = blocks(chain_spec.clone(), key_pair)?; let provider = provider_factory.provider()?; let executor = EthExecutorProvider::ethereum(chain_spec).batch_executor(StateProviderDatabase::new( LatestStateProviderRef::new(provider.tx_ref(), provider.static_file_provider().clone()), )); let mut execution_outcome = executor.execute_and_verify_batch(vec![ (&block1, U256::ZERO).into(), (&block2, U256::ZERO).into(), ])?; execution_outcome.state_mut().reverts.sort(); let block1 = block1.seal_slow(); let block2 = block2.seal_slow(); // Commit the block's execution outcome to the database let provider_rw = provider_factory.provider_rw()?; provider_rw.append_blocks_with_state( vec![block1.clone(), block2.clone()], execution_outcome.clone(), Default::default(), Default::default(), )?; provider_rw.commit()?; Ok((vec![block1, block2], execution_outcome)) }