From 227d7972d757f6f1373e1771df0b90be5c87c454 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Thu, 25 Jan 2024 16:12:29 +0000 Subject: [PATCH 01/33] fix(libmdbx): build without `read-tx-timeouts` feature (#6225) --- crates/storage/libmdbx-rs/src/txn_manager.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/storage/libmdbx-rs/src/txn_manager.rs b/crates/storage/libmdbx-rs/src/txn_manager.rs index 267280369be7..3d4c2f101507 100644 --- a/crates/storage/libmdbx-rs/src/txn_manager.rs +++ b/crates/storage/libmdbx-rs/src/txn_manager.rs @@ -52,7 +52,9 @@ impl TxnManager { /// - [TxnManagerMessage::Abort] aborts a transaction with [ffi::mdbx_txn_abort] /// - [TxnManagerMessage::Commit] commits a transaction with [ffi::mdbx_txn_commit_ex] fn start_message_listener(&self, env: EnvPtr, rx: Receiver) { + #[cfg(feature = "read-tx-timeouts")] let read_transactions = self.read_transactions.clone(); + std::thread::spawn(move || { #[allow(clippy::redundant_locals)] let env = env; From e78ae77685a848c3fbf822d962b527d5a29c3712 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Thu, 25 Jan 2024 17:13:09 +0100 Subject: [PATCH 02/33] feat(cli): local tx propagation toggle (#6224) --- crates/node-core/src/args/txpool_args.rs | 7 ++++- crates/transaction-pool/src/config.rs | 25 ++++++++++++++++- crates/transaction-pool/src/validate/eth.rs | 30 +++------------------ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/crates/node-core/src/args/txpool_args.rs b/crates/node-core/src/args/txpool_args.rs index 7c7d71864932..0b1638e036a6 100644 --- a/crates/node-core/src/args/txpool_args.rs +++ b/crates/node-core/src/args/txpool_args.rs @@ -47,9 +47,12 @@ pub struct TxPoolArgs { /// Flag to disable local transaction exemptions. #[arg(long = "txpool.nolocals")] pub no_locals: bool, - /// Flag to allow certain addresses as local + /// Flag to allow certain addresses as local. #[arg(long = "txpool.locals")] pub locals: Vec
, + /// Flag to toggle local transaction propagation. + #[arg(long = "txpool.no-local-transactions-propagation")] + pub no_local_transactions_propagation: bool, } impl Default for TxPoolArgs { @@ -66,6 +69,7 @@ impl Default for TxPoolArgs { blob_transaction_price_bump: REPLACE_BLOB_PRICE_BUMP, no_locals: false, locals: Default::default(), + no_local_transactions_propagation: false, } } } @@ -77,6 +81,7 @@ impl RethTransactionPoolConfig for TxPoolArgs { local_transactions_config: LocalTransactionConfig { no_exemptions: self.no_locals, local_addresses: self.locals.clone().into_iter().collect(), + propagate_local_transactions: !self.no_local_transactions_propagation, }, pending_limit: SubPoolLimit { max_txs: self.pending_max_count, diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index 8a1aaaad2498..f7e900db2d7b 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -110,7 +110,7 @@ impl Default for PriceBumpConfig { /// Configuration options for the locally received transactions: /// [TransactionOrigin::Local](crate::TransactionOrigin) -#[derive(Debug, Clone, Eq, PartialEq, Default)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct LocalTransactionConfig { /// Apply no exemptions to the locally received transactions. /// @@ -121,6 +121,18 @@ pub struct LocalTransactionConfig { pub no_exemptions: bool, /// Addresses that will be considered as local . Above exemptions apply pub local_addresses: HashSet
, + /// Flag indicating whether local transactions should be propagated. + pub propagate_local_transactions: bool, +} + +impl Default for LocalTransactionConfig { + fn default() -> Self { + Self { + no_exemptions: false, + local_addresses: HashSet::default(), + propagate_local_transactions: true, + } + } } impl LocalTransactionConfig { @@ -146,4 +158,15 @@ impl LocalTransactionConfig { } origin.is_local() || self.contains_local_address(sender) } + + /// Sets toggle to propagate transactions received locally by this client (e.g + /// transactions from eth_sendTransaction to this nodes' RPC server) + /// + /// If set to false, only transactions received by network peers (via + /// p2p) will be marked as propagated in the local transaction pool and returned on a + /// GetPooledTransactions p2p request + pub fn set_propagate_local_transactions(mut self, propagate_local_txs: bool) -> Self { + self.propagate_local_transactions = propagate_local_txs; + self + } } diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 455b49c79aa6..995386d05f97 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -120,8 +120,6 @@ where block_gas_limit: u64, /// Minimum priority fee to enforce for acceptance into the pool. minimum_priority_fee: Option, - /// Toggle to determine if a local transaction should be propagated - propagate_local_transactions: bool, /// Stores the setup and parameters needed for validating KZG proofs. kzg_settings: Arc, /// How to handle [TransactionOrigin::Local](TransactionOrigin) transactions. @@ -441,7 +439,9 @@ where // by this point assume all external transactions should be propagated propagate: match origin { TransactionOrigin::External => true, - TransactionOrigin::Local => self.propagate_local_transactions, + TransactionOrigin::Local => { + self.local_transactions_config.propagate_local_transactions + } TransactionOrigin::Private => false, }, } @@ -481,8 +481,6 @@ pub struct EthTransactionValidatorBuilder { /// /// Default is 1 additional_tasks: usize, - /// Toggle to determine if a local transaction should be propagated - propagate_local_transactions: bool, /// Stores the setup and parameters needed for validating KZG proofs. kzg_settings: Arc, @@ -501,8 +499,6 @@ impl EthTransactionValidatorBuilder { block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, minimum_priority_fee: None, additional_tasks: 1, - // default to true, can potentially take this as a param in the future - propagate_local_transactions: true, kzg_settings: Arc::clone(&MAINNET_KZG_TRUSTED_SETUP), local_transactions_config: Default::default(), @@ -578,24 +574,6 @@ impl EthTransactionValidatorBuilder { self } - /// Sets toggle to propagate transactions received locally by this client (e.g - /// transactions from eth_sendTransaction to this nodes' RPC server) - /// - /// If set to false, only transactions received by network peers (via - /// p2p) will be marked as propagated in the local transaction pool and returned on a - /// GetPooledTransactions p2p request - pub fn set_propagate_local_transactions(mut self, propagate_local_txs: bool) -> Self { - self.propagate_local_transactions = propagate_local_txs; - self - } - /// Disables propagating transactions received locally by this client - /// - /// For more information, check docs for set_propagate_local_transactions - pub fn no_local_transaction_propagation(mut self) -> Self { - self.propagate_local_transactions = false; - self - } - /// Sets a minimum priority fee that's enforced for acceptance into the pool. pub fn with_minimum_priority_fee(mut self, minimum_priority_fee: u128) -> Self { self.minimum_priority_fee = Some(minimum_priority_fee); @@ -636,7 +614,6 @@ impl EthTransactionValidatorBuilder { eip4844, block_gas_limit, minimum_priority_fee, - propagate_local_transactions, kzg_settings, local_transactions_config, .. @@ -654,7 +631,6 @@ impl EthTransactionValidatorBuilder { eip4844, block_gas_limit, minimum_priority_fee, - propagate_local_transactions, blob_store: Box::new(blob_store), kzg_settings, local_transactions_config, From 2de10a15b512d81bfb5ce9de24272db1afad41c7 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Thu, 25 Jan 2024 18:16:18 +0100 Subject: [PATCH 03/33] feat(trie): historical & sidechain state root (#6131) Co-authored-by: Matthias Seitz --- crates/blockchain-tree/src/blockchain_tree.rs | 265 +++++++----------- crates/blockchain-tree/src/chain.rs | 145 +++------- crates/blockchain-tree/src/config.rs | 3 +- crates/consensus/beacon/src/engine/mod.rs | 48 ++-- crates/interfaces/src/blockchain_tree/mod.rs | 42 ++- .../src/providers/state/historical.rs | 46 ++- crates/trie/src/hashed_cursor/post_state.rs | 12 +- crates/trie/src/state.rs | 72 ++++- crates/trie/src/trie.rs | 1 - 9 files changed, 300 insertions(+), 334 deletions(-) diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 537f34164a10..d2e93c6a0d0b 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -2,7 +2,6 @@ use crate::{ canonical_chain::CanonicalChain, - chain::BlockKind, metrics::{MakeCanonicalAction, MakeCanonicalDurationsRecorder, TreeMetrics}, state::{BlockChainId, TreeState}, AppendableChain, BlockIndices, BlockchainTreeConfig, BundleStateData, TreeExternals, @@ -11,7 +10,7 @@ use reth_db::{database::Database, DatabaseError}; use reth_interfaces::{ blockchain_tree::{ error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind}, - BlockStatus, BlockValidationKind, CanonicalOutcome, InsertPayloadOk, + BlockAttachment, BlockStatus, BlockValidationKind, CanonicalOutcome, InsertPayloadOk, }, consensus::{Consensus, ConsensusError}, executor::{BlockExecutionError, BlockValidationError}, @@ -132,7 +131,6 @@ impl BlockchainTree { /// Function will check: /// * if block is inside database returns [BlockStatus::Valid]. /// * if block is inside buffer returns [BlockStatus::Disconnected]. - /// * if block is part of a side chain returns [BlockStatus::Accepted]. /// * if block is part of the canonical returns [BlockStatus::Valid]. /// /// Returns an error if @@ -147,12 +145,12 @@ impl BlockchainTree { if block.number <= last_finalized_block { // check if block is canonical if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid(BlockAttachment::Canonical))) } // check if block is inside database if self.externals.provider_factory.provider()?.block_number(block.hash)?.is_some() { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid(BlockAttachment::Canonical))) } return Err(BlockchainTreeError::PendingBlockIsFinalized { @@ -163,12 +161,12 @@ impl BlockchainTree { // check if block is part of canonical chain if self.is_block_hash_canonical(&block.hash)? { - return Ok(Some(BlockStatus::Valid)) + return Ok(Some(BlockStatus::Valid(BlockAttachment::Canonical))) } // is block inside chain - if let Some(status) = self.is_block_inside_chain(&block) { - return Ok(Some(status)) + if let Some(attachment) = self.is_block_inside_chain(&block) { + return Ok(Some(BlockStatus::Valid(attachment))) } // check if block is disconnected @@ -289,7 +287,7 @@ impl BlockchainTree { &mut self, block: SealedBlockWithSenders, block_validation_kind: BlockValidationKind, - ) -> Result { + ) -> Result { debug_assert!(self.validate_block(&block).is_ok(), "Block must be validated"); let parent = block.parent_num_hash(); @@ -301,11 +299,8 @@ impl BlockchainTree { } // if not found, check if the parent can be found inside canonical chain. - if self - .is_block_hash_canonical(&parent.hash) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))? - { - return self.try_append_canonical_chain(block, block_validation_kind) + if self.is_block_hash_canonical(&parent.hash)? { + return self.try_append_canonical_chain(block.clone(), block_validation_kind) } // this is another check to ensure that if the block points to a canonical block its block @@ -315,22 +310,17 @@ impl BlockchainTree { { // we found the parent block in canonical chain if canonical_parent_number != parent.number { - return Err(InsertBlockError::consensus_error( - ConsensusError::ParentBlockNumberMismatch { - parent_block_number: canonical_parent_number, - block_number: block.number, - }, - block.block, - )) + return Err(ConsensusError::ParentBlockNumberMismatch { + parent_block_number: canonical_parent_number, + block_number: block.number, + } + .into()) } } // if there is a parent inside the buffer, validate against it. if let Some(buffered_parent) = self.state.buffered_blocks.block(&parent.hash) { - self.externals - .consensus - .validate_header_against_parent(&block, buffered_parent) - .map_err(|err| InsertBlockError::consensus_error(err, block.block.clone()))?; + self.externals.consensus.validate_header_against_parent(&block, buffered_parent)?; } // insert block inside unconnected block buffer. Delaying its execution. @@ -341,10 +331,7 @@ impl BlockchainTree { // shouldn't happen right after insertion let lowest_ancestor = self.state.buffered_blocks.lowest_ancestor(&block.hash).ok_or_else(|| { - InsertBlockError::tree_error( - BlockchainTreeError::BlockBufferingFailed { block_hash: block.hash }, - block.block, - ) + BlockchainTreeError::BlockBufferingFailed { block_hash: block.hash } })?; Ok(BlockStatus::Disconnected { missing_ancestor: lowest_ancestor.parent_num_hash() }) @@ -360,91 +347,59 @@ impl BlockchainTree { &mut self, block: SealedBlockWithSenders, block_validation_kind: BlockValidationKind, - ) -> Result { + ) -> Result { let parent = block.parent_num_hash(); let block_num_hash = block.num_hash(); debug!(target: "blockchain_tree", head = ?block_num_hash.hash, ?parent, "Appending block to canonical chain"); - // create new chain that points to that block - //return self.fork_canonical_chain(block.clone()); - // TODO save pending block to database - // https://github.com/paradigmxyz/reth/issues/1713 - let (block_status, chain) = { - let provider = self - .externals - .provider_factory - .provider() - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - - // Validate that the block is post merge - let parent_td = provider - .header_td(&block.parent_hash) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))? - .ok_or_else(|| { - InsertBlockError::tree_error( - BlockchainTreeError::CanonicalChain { block_hash: block.parent_hash }, - block.block.clone(), - ) - })?; - - // Pass the parent total difficulty to short-circuit unnecessary calculations. - if !self - .externals - .provider_factory - .chain_spec() - .fork(Hardfork::Paris) - .active_at_ttd(parent_td, U256::ZERO) - { - return Err(InsertBlockError::execution_error( - BlockValidationError::BlockPreMerge { hash: block.hash }.into(), - block.block, - )) - } + let provider = self.externals.provider_factory.provider()?; - let parent_header = provider - .header(&block.parent_hash) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))? - .ok_or_else(|| { - InsertBlockError::tree_error( - BlockchainTreeError::CanonicalChain { block_hash: block.parent_hash }, - block.block.clone(), - ) - })? - .seal(block.parent_hash); - - let canonical_chain = self.canonical_chain(); - - if block.parent_hash == canonical_chain.tip().hash { - let chain = AppendableChain::new_canonical_head_fork( - block, - &parent_header, - canonical_chain.inner(), - parent, - &self.externals, - block_validation_kind, - )?; - let status = if block_validation_kind.is_exhaustive() { - BlockStatus::Valid - } else { - BlockStatus::Accepted - }; + // Validate that the block is post merge + let parent_td = provider + .header_td(&block.parent_hash)? + .ok_or_else(|| BlockchainTreeError::CanonicalChain { block_hash: block.parent_hash })?; - (status, chain) - } else { - let chain = AppendableChain::new_canonical_fork( - block, - &parent_header, - canonical_chain.inner(), - parent, - &self.externals, - )?; - (BlockStatus::Accepted, chain) - } + // Pass the parent total difficulty to short-circuit unnecessary calculations. + if !self + .externals + .provider_factory + .chain_spec() + .fork(Hardfork::Paris) + .active_at_ttd(parent_td, U256::ZERO) + { + return Err(BlockExecutionError::Validation(BlockValidationError::BlockPreMerge { + hash: block.hash, + }) + .into()) + } + + let parent_header = provider + .header(&block.parent_hash)? + .ok_or_else(|| BlockchainTreeError::CanonicalChain { block_hash: block.parent_hash })? + .seal(block.parent_hash); + + let canonical_chain = self.canonical_chain(); + + let block_attachment = if block.parent_hash == canonical_chain.tip().hash { + BlockAttachment::Canonical + } else { + BlockAttachment::HistoricalFork }; + let chain = AppendableChain::new_canonical_fork( + block, + &parent_header, + canonical_chain.inner(), + parent, + &self.externals, + block_attachment, + block_validation_kind, + )?; + self.insert_chain(chain); self.try_connect_buffered_blocks(block_num_hash); - Ok(block_status) + + Ok(BlockStatus::Valid(block_attachment)) } /// Try inserting a block into the given side chain. @@ -456,7 +411,7 @@ impl BlockchainTree { block: SealedBlockWithSenders, chain_id: BlockChainId, block_validation_kind: BlockValidationKind, - ) -> Result { + ) -> Result { debug!(target: "blockchain_tree", "Inserting block into side chain"); let block_num_hash = block.num_hash(); // Create a new sidechain by forking the given chain, or append the block if the parent @@ -464,37 +419,25 @@ impl BlockchainTree { let block_hashes = self.all_chain_hashes(chain_id); // get canonical fork. - let canonical_fork = match self.canonical_fork(chain_id) { - None => { - return Err(InsertBlockError::tree_error( - BlockchainTreeError::BlockSideChainIdConsistency { chain_id: chain_id.into() }, - block.block, - )) - } - Some(fork) => fork, - }; + let canonical_fork = self.canonical_fork(chain_id).ok_or_else(|| { + BlockchainTreeError::BlockSideChainIdConsistency { chain_id: chain_id.into() } + })?; // get chain that block needs to join to. - let parent_chain = match self.state.chains.get_mut(&chain_id) { - Some(parent_chain) => parent_chain, - None => { - return Err(InsertBlockError::tree_error( - BlockchainTreeError::BlockSideChainIdConsistency { chain_id: chain_id.into() }, - block.block, - )) - } - }; + let parent_chain = self.state.chains.get_mut(&chain_id).ok_or_else(|| { + BlockchainTreeError::BlockSideChainIdConsistency { chain_id: chain_id.into() } + })?; let chain_tip = parent_chain.tip().hash(); let canonical_chain = self.state.block_indices.canonical_chain(); // append the block if it is continuing the side chain. - let status = if chain_tip == block.parent_hash { + let block_attachment = if chain_tip == block.parent_hash { // check if the chain extends the currently tracked canonical head - let block_kind = if canonical_fork.hash == canonical_chain.tip().hash { - BlockKind::ExtendsCanonicalHead + let block_attachment = if canonical_fork.hash == canonical_chain.tip().hash { + BlockAttachment::Canonical } else { - BlockKind::ForksHistoricalBlock + BlockAttachment::HistoricalFork }; debug!(target: "blockchain_tree", "Appending block to side chain"); @@ -506,19 +449,12 @@ impl BlockchainTree { canonical_chain.inner(), &self.externals, canonical_fork, - block_kind, + block_attachment, block_validation_kind, )?; self.block_indices_mut().insert_non_fork_block(block_number, block_hash, chain_id); - - if block_kind.extends_canonical_head() && block_validation_kind.is_exhaustive() { - // if the block can be traced back to the canonical head, we were able to fully - // validate it - Ok(BlockStatus::Valid) - } else { - Ok(BlockStatus::Accepted) - } + block_attachment } else { debug!(target: "blockchain_tree", ?canonical_fork, "Starting new fork from side chain"); // the block starts a new fork @@ -528,15 +464,16 @@ impl BlockchainTree { canonical_chain.inner(), canonical_fork, &self.externals, + block_validation_kind, )?; self.insert_chain(chain); - Ok(BlockStatus::Accepted) + BlockAttachment::HistoricalFork }; // After we inserted the block, we try to connect any buffered blocks self.try_connect_buffered_blocks(block_num_hash); - status + Ok(BlockStatus::Valid(block_attachment)) } /// Get all block hashes from a sidechain that are not part of the canonical chain. @@ -724,21 +661,21 @@ impl BlockchainTree { Ok(()) } - /// Check if block is found inside chain and if the chain extends the canonical chain. + /// Check if block is found inside chain and its attachment. /// - /// if it does extend the canonical chain, return `BlockStatus::Valid` - /// if it does not extend the canonical chain, return `BlockStatus::Accepted` + /// if it is canonical or extends the canonical chain, return [BlockAttachment::Canonical] + /// if it does not extend the canonical chain, return [BlockAttachment::HistoricalFork] #[track_caller] - fn is_block_inside_chain(&self, block: &BlockNumHash) -> Option { + fn is_block_inside_chain(&self, block: &BlockNumHash) -> Option { // check if block known and is already in the tree if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block.hash) { // find the canonical fork of this chain let canonical_fork = self.canonical_fork(chain_id).expect("Chain id is valid"); // if the block's chain extends canonical chain return if canonical_fork == self.block_indices().canonical_tip() { - Some(BlockStatus::Valid) + Some(BlockAttachment::Canonical) } else { - Some(BlockStatus::Accepted) + Some(BlockAttachment::HistoricalFork) } } None @@ -783,9 +720,10 @@ impl BlockchainTree { return Err(InsertBlockError::consensus_error(err, block.block)) } - Ok(InsertPayloadOk::Inserted( - self.try_insert_validated_block(block, block_validation_kind)?, - )) + let status = self + .try_insert_validated_block(block.clone(), block_validation_kind) + .map_err(|kind| InsertBlockError::new(block.block, kind))?; + Ok(InsertPayloadOk::Inserted(status)) } /// Finalize blocks up until and including `finalized_block`, and remove them from the tree. @@ -1587,7 +1525,7 @@ mod tests { assert_eq!( tree.insert_block(canonical_block_1.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Valid) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical)) ); assert_eq!( @@ -1597,12 +1535,12 @@ mod tests { assert_eq!( tree.insert_block(canonical_block_2.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Valid) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical)) ); assert_eq!( tree.insert_block(sidechain_block_1.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); assert_eq!( @@ -1617,7 +1555,7 @@ mod tests { assert_eq!( tree.insert_block(sidechain_block_2.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); assert_eq!( @@ -1627,7 +1565,7 @@ mod tests { assert_eq!( tree.insert_block(canonical_block_3.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); assert_eq!( @@ -1644,7 +1582,7 @@ mod tests { let genesis = data.genesis; // test pops execution results from vector, so order is from last to first. - let externals = setup_externals(vec![exec2.clone(), exec1.clone(), exec2, exec1]); + let externals = setup_externals(vec![exec2.clone(), exec2, exec1]); // last finalized block would be number 9. setup_genesis(&externals.provider_factory, genesis); @@ -1660,12 +1598,12 @@ mod tests { assert_eq!( tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Valid) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical)) ); assert_eq!( tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Valid) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical)) ); // we have one chain that has two blocks. @@ -1690,7 +1628,7 @@ mod tests { assert_eq!( tree.insert_block(block2a.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); // fork chain. @@ -1783,7 +1721,7 @@ mod tests { // insert block1 and buffered block2 is inserted assert_eq!( tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Valid) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical)) ); // Buffered blocks: [] @@ -1806,13 +1744,13 @@ mod tests { // already inserted block will `InsertPayloadOk::AlreadySeen(_)` assert_eq!( tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::AlreadySeen(BlockStatus::Valid) + InsertPayloadOk::AlreadySeen(BlockStatus::Valid(BlockAttachment::Canonical)) ); // block two is already inserted. assert_eq!( tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::AlreadySeen(BlockStatus::Valid) + InsertPayloadOk::AlreadySeen(BlockStatus::Valid(BlockAttachment::Canonical)) ); // make block1 canonical @@ -1852,7 +1790,7 @@ mod tests { // reinsert two blocks that point to canonical chain assert_eq!( tree.insert_block(block1a.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); TreeTester::default() @@ -1867,7 +1805,7 @@ mod tests { assert_eq!( tree.insert_block(block2a.clone(), BlockValidationKind::Exhaustive).unwrap(), - InsertPayloadOk::Inserted(BlockStatus::Accepted) + InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::HistoricalFork)) ); // Trie state: // b2 b2a (side chain) @@ -2069,7 +2007,10 @@ mod tests { // update canonical block to b2, this would make b2a be removed assert_eq!(tree.connect_buffered_blocks_to_canonical_hashes_and_finalize(12), Ok(())); - assert_eq!(tree.is_block_known(block2.num_hash()).unwrap(), Some(BlockStatus::Valid)); + assert_eq!( + tree.is_block_known(block2.num_hash()).unwrap(), + Some(BlockStatus::Valid(BlockAttachment::Canonical)) + ); // Trie state: // b2 (finalized) diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 8e192a4b25c2..578dae720a35 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -8,8 +8,8 @@ use crate::BundleStateDataRef; use reth_db::database::Database; use reth_interfaces::{ blockchain_tree::{ - error::{BlockchainTreeError, InsertBlockError}, - BlockValidationKind, + error::{BlockchainTreeError, InsertBlockErrorKind}, + BlockAttachment, BlockValidationKind, }, consensus::{Consensus, ConsensusError}, RethResult, @@ -59,18 +59,19 @@ impl AppendableChain { self.chain } - /// Create a new chain that forks off the canonical chain. + /// Create a new chain that forks off of the canonical chain. /// - /// if [BlockValidationKind::Exhaustive] is provides this will verify the state root of the - /// block extending the canonical chain. - pub fn new_canonical_head_fork( + /// if [BlockValidationKind::Exhaustive] is specified, the method will verify the state root of + /// the block. + pub fn new_canonical_fork( block: SealedBlockWithSenders, parent_header: &SealedHeader, canonical_block_hashes: &BTreeMap, canonical_fork: ForkBlock, externals: &TreeExternals, + block_attachment: BlockAttachment, block_validation_kind: BlockValidationKind, - ) -> Result + ) -> Result where DB: Database, EF: ExecutorFactory, @@ -90,47 +91,13 @@ impl AppendableChain { parent_header, state_provider, externals, - BlockKind::ExtendsCanonicalHead, + block_attachment, block_validation_kind, - ) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + )?; Ok(Self { chain: Chain::new(vec![block], bundle_state, trie_updates) }) } - /// Create a new chain that forks off of the canonical chain. - pub fn new_canonical_fork( - block: SealedBlockWithSenders, - parent_header: &SealedHeader, - canonical_block_hashes: &BTreeMap, - canonical_fork: ForkBlock, - externals: &TreeExternals, - ) -> Result - where - DB: Database, - EF: ExecutorFactory, - { - let state = BundleStateWithReceipts::default(); - let empty = BTreeMap::new(); - - let state_provider = BundleStateDataRef { - state: &state, - sidechain_block_hashes: &empty, - canonical_block_hashes, - canonical_fork, - }; - - let bundle_state = Self::validate_and_execute_sidechain( - block.clone(), - parent_header, - state_provider, - externals, - ) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - - Ok(Self { chain: Chain::new(vec![block], bundle_state, None) }) - } - /// Create a new chain that forks off of an existing sidechain. /// /// This differs from [AppendableChain::new_canonical_fork] in that this starts a new fork. @@ -141,18 +108,16 @@ impl AppendableChain { canonical_block_hashes: &BTreeMap, canonical_fork: ForkBlock, externals: &TreeExternals, - ) -> Result + block_validation_kind: BlockValidationKind, + ) -> Result where DB: Database, EF: ExecutorFactory, { let parent_number = block.number - 1; - let parent = self.blocks().get(&parent_number).ok_or_else(|| { - InsertBlockError::tree_error( - BlockchainTreeError::BlockNumberNotFoundInChain { block_number: parent_number }, - block.block.clone(), - ) - })?; + let parent = self.blocks().get(&parent_number).ok_or( + BlockchainTreeError::BlockNumberNotFoundInChain { block_number: parent_number }, + )?; let mut state = self.state().clone(); @@ -166,13 +131,14 @@ impl AppendableChain { canonical_block_hashes, canonical_fork, }; - let block_state = Self::validate_and_execute_sidechain( + let (block_state, _) = Self::validate_and_execute( block.clone(), parent, bundle_state_data, externals, - ) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + BlockAttachment::HistoricalFork, + block_validation_kind, + )?; // extending will also optimize few things, mostly related to selfdestruct and wiping of // storage. state.extend(block_state); @@ -193,15 +159,15 @@ impl AppendableChain { /// Note: State root validation is limited to blocks that extend the canonical chain and is /// optional, see [BlockValidationKind]. So this function takes two parameters to determine /// if the state can and should be validated. - /// - [BlockKind] represents if the block extends the canonical chain, and thus if the state - /// root __can__ be validated. + /// - [BlockAttachment] represents if the block extends the canonical chain, and thus we can + /// cache the trie state updates. /// - [BlockValidationKind] determines if the state root __should__ be validated. fn validate_and_execute( block: SealedBlockWithSenders, parent_block: &SealedHeader, bundle_state_data_provider: BSDP, externals: &TreeExternals, - block_kind: BlockKind, + block_attachment: BlockAttachment, block_validation_kind: BlockValidationKind, ) -> RethResult<(BundleStateWithReceipts, Option)> where @@ -226,9 +192,15 @@ impl AppendableChain { // check state root if the block extends the canonical chain __and__ if state root // validation was requested. - if block_kind.extends_canonical_head() && block_validation_kind.is_exhaustive() { + if block_validation_kind.is_exhaustive() { // check state root - let (state_root, trie_updates) = provider.state_root_with_updates(&bundle_state)?; + let (state_root, trie_updates) = if block_attachment.is_canonical() { + provider + .state_root_with_updates(&bundle_state) + .map(|(root, updates)| (root, Some(updates)))? + } else { + (provider.state_root(&bundle_state)?, None) + }; if block.state_root != state_root { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), @@ -236,35 +208,12 @@ impl AppendableChain { .into()) } - Ok((bundle_state, Some(trie_updates))) + Ok((bundle_state, trie_updates)) } else { Ok((bundle_state, None)) } } - /// Validate and execute the given sidechain block, skipping state root validation. - fn validate_and_execute_sidechain( - block: SealedBlockWithSenders, - parent_block: &SealedHeader, - bundle_state_data_provider: BSDP, - externals: &TreeExternals, - ) -> RethResult - where - BSDP: BundleStateDataProvider, - DB: Database, - EF: ExecutorFactory, - { - let (state, _) = Self::validate_and_execute( - block, - parent_block, - bundle_state_data_provider, - externals, - BlockKind::ForksHistoricalBlock, - BlockValidationKind::SkipStateRootValidation, - )?; - Ok(state) - } - /// Validate and execute the given block, and append it to this chain. /// /// This expects that the block's ancestors can be traced back to the `canonical_fork` (the @@ -285,9 +234,9 @@ impl AppendableChain { canonical_block_hashes: &BTreeMap, externals: &TreeExternals, canonical_fork: ForkBlock, - block_kind: BlockKind, + block_attachment: BlockAttachment, block_validation_kind: BlockValidationKind, - ) -> Result<(), InsertBlockError> + ) -> Result<(), InsertBlockErrorKind> where DB: Database, EF: ExecutorFactory, @@ -306,36 +255,12 @@ impl AppendableChain { parent_block, bundle_state_data, externals, - block_kind, + block_attachment, block_validation_kind, - ) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + )?; // extend the state. self.chain.append_block(block, block_state, trie_updates); Ok(()) } } - -/// Represents what kind of block is being executed and validated. -/// -/// This is required because the state root check can only be performed if the targeted block can be -/// traced back to the canonical __head__. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum BlockKind { - /// The `block` is a descendant of the canonical head: - /// - /// [`head..(block.parent)*,block`] - ExtendsCanonicalHead, - /// The block can be traced back to an ancestor of the canonical head: a historical block, but - /// this chain does __not__ include the canonical head. - ForksHistoricalBlock, -} - -impl BlockKind { - /// Returns `true` if the block is a descendant of the canonical head. - #[inline] - pub(crate) fn extends_canonical_head(&self) -> bool { - matches!(self, BlockKind::ExtendsCanonicalHead) - } -} diff --git a/crates/blockchain-tree/src/config.rs b/crates/blockchain-tree/src/config.rs index 89a861206d34..733cb6a1a0e0 100644 --- a/crates/blockchain-tree/src/config.rs +++ b/crates/blockchain-tree/src/config.rs @@ -78,8 +78,7 @@ impl BlockchainTreeConfig { /// It is calculated as the maximum of `max_reorg_depth` (which is the number of blocks required /// for the deepest reorg possible according to the consensus protocol) and /// `num_of_additional_canonical_block_hashes` (which is the number of block hashes needed to - /// satisfy the `BLOCKHASH` opcode in the EVM. See [`crate::BundleStateDataRef`] and - /// [`crate::AppendableChain::new_canonical_head_fork`] where it's used). + /// satisfy the `BLOCKHASH` opcode in the EVM. See [`crate::BundleStateDataRef`]). pub fn num_of_canonical_hashes(&self) -> u64 { self.max_reorg_depth.max(self.num_of_additional_canonical_block_hashes) } diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 89c39f0c1e29..91c26861e8ec 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -1263,14 +1263,19 @@ where let mut latest_valid_hash = None; let block = Arc::new(block); let status = match status { - InsertPayloadOk::Inserted(BlockStatus::Valid) => { + InsertPayloadOk::Inserted(BlockStatus::Valid(attachment)) => { latest_valid_hash = Some(block_hash); - self.listeners.notify(BeaconConsensusEngineEvent::CanonicalBlockAdded(block)); + let event = if attachment.is_canonical() { + BeaconConsensusEngineEvent::CanonicalBlockAdded(block) + } else { + BeaconConsensusEngineEvent::ForkBlockAdded(block) + }; + self.listeners.notify(event); PayloadStatusEnum::Valid } - InsertPayloadOk::Inserted(BlockStatus::Accepted) => { - self.listeners.notify(BeaconConsensusEngineEvent::ForkBlockAdded(block)); - PayloadStatusEnum::Accepted + InsertPayloadOk::AlreadySeen(BlockStatus::Valid(_)) => { + latest_valid_hash = Some(block_hash); + PayloadStatusEnum::Valid } InsertPayloadOk::Inserted(BlockStatus::Disconnected { .. }) | InsertPayloadOk::AlreadySeen(BlockStatus::Disconnected { .. }) => { @@ -1284,11 +1289,6 @@ where // not known to be invalid, but we don't know anything else PayloadStatusEnum::Syncing } - InsertPayloadOk::AlreadySeen(BlockStatus::Valid) => { - latest_valid_hash = Some(block_hash); - PayloadStatusEnum::Valid - } - InsertPayloadOk::AlreadySeen(BlockStatus::Accepted) => PayloadStatusEnum::Accepted, }; Ok(PayloadStatus::new(status, latest_valid_hash)) } @@ -1351,20 +1351,14 @@ where /// /// ## [BlockStatus::Valid] /// - /// The block is connected to the current canonical head and is valid. - /// If the engine is still SYNCING, then we can try again to make the chain canonical. - /// - /// ## [BlockStatus::Accepted] - /// - /// All ancestors are known, but the block is not connected to the current canonical _head_. If - /// the block is an ancestor of the current forkchoice head, then we can try again to make the - /// chain canonical, which would trigger a reorg in this case since the new head is therefore - /// not connected to the current head. + /// The block is connected to the current canonical chain and is valid. + /// If the block is an ancestor of the current forkchoice head, then we can try again to make + /// the chain canonical. /// /// ## [BlockStatus::Disconnected] /// - /// The block is not connected to the canonical head, and we need to download the missing parent - /// first. + /// The block is not connected to the canonical chain, and we need to download the missing + /// parent first. /// /// ## Insert Error /// @@ -1386,12 +1380,10 @@ where { Ok(status) => { match status { - InsertPayloadOk::Inserted(BlockStatus::Valid) => { - // block is connected to the current canonical head and is valid. - self.try_make_sync_target_canonical(downloaded_num_hash); - } - InsertPayloadOk::Inserted(BlockStatus::Accepted) => { - // block is connected to the canonical chain, but not the current head + InsertPayloadOk::Inserted(BlockStatus::Valid(_)) => { + // block is connected to the canonical chain and is valid. + // if it's not connected to current canonical head, the state root + // has not been validated. self.try_make_sync_target_canonical(downloaded_num_hash); } InsertPayloadOk::Inserted(BlockStatus::Disconnected { @@ -1469,7 +1461,7 @@ where /// Attempt to form a new canonical chain based on the current sync target. /// /// This is invoked when we successfully __downloaded__ a new block from the network which - /// resulted in either [BlockStatus::Accepted] or [BlockStatus::Valid]. + /// resulted in [BlockStatus::Valid]. /// /// Note: This will not succeed if the sync target has changed since the block download request /// was issued and the new target is still disconnected and additional missing blocks are diff --git a/crates/interfaces/src/blockchain_tree/mod.rs b/crates/interfaces/src/blockchain_tree/mod.rs index 3d77fef03cf9..0eabb5c763f5 100644 --- a/crates/interfaces/src/blockchain_tree/mod.rs +++ b/crates/interfaces/src/blockchain_tree/mod.rs @@ -182,17 +182,16 @@ impl CanonicalOutcome { /// From Engine API spec, block inclusion can be valid, accepted or invalid. /// Invalid case is already covered by error, but we need to make distinction -/// between if it is valid (extends canonical chain) or just accepted (is side chain). -/// If we don't know the block parent we are returning Disconnected status -/// as we can't make a claim if block is valid or not. +/// between valid blocks that extend canonical chain and the ones that fork off +/// into side chains (see [BlockAttachment]). If we don't know the block +/// parent we are returning Disconnected statusĀ as we can't make a claim if +/// block is valid or not. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum BlockStatus { - /// If block validation is valid and block extends canonical chain. - /// In BlockchainTree sense it forks on canonical tip. - Valid, - /// If the block is valid, but it does not extend canonical chain. - /// (It is side chain) or hasn't been fully validated but ancestors of a payload are known. - Accepted, + /// If block is valid and block extends canonical chain. + /// In BlockchainTree terms, it forks off canonical tip. + Valid(BlockAttachment), + /// If block is valid and block forks off canonical chain. /// If blocks is not connected to canonical chain. Disconnected { /// The lowest ancestor block that is not connected to the canonical chain. @@ -200,6 +199,31 @@ pub enum BlockStatus { }, } +/// Represents what kind of block is being executed and validated. +/// +/// This is required to: +/// - differentiate whether trie state updates should be cached. +/// - inform other +/// This is required because the state root check can only be performed if the targeted block can be +/// traced back to the canonical __head__. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BlockAttachment { + /// The `block` is canonical or a descendant of the canonical head. + /// ([`head..(block.parent)*,block`]) + Canonical, + /// The block can be traced back to an ancestor of the canonical head: a historical block, but + /// this chain does __not__ include the canonical head. + HistoricalFork, +} + +impl BlockAttachment { + /// Returns `true` if the block is canonical or a descendant of the canonical head. + #[inline] + pub const fn is_canonical(&self) -> bool { + matches!(self, BlockAttachment::Canonical) + } +} + /// How a payload was inserted if it was valid. /// /// If the payload was valid, but has already been seen, [`InsertPayloadOk::AlreadySeen(_)`] is diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 85460498d393..f2892ee7dc8d 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -12,9 +12,10 @@ use reth_db::{ }; use reth_interfaces::provider::ProviderResult; use reth_primitives::{ - trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, StorageValue, B256, + constants::EPOCH_SLOTS, trie::AccountProof, Account, Address, BlockNumber, Bytecode, + StorageKey, StorageValue, B256, }; -use reth_trie::updates::TrieUpdates; +use reth_trie::{updates::TrieUpdates, HashedPostState}; /// State provider for a given block number which takes a tx reference. /// @@ -95,6 +96,31 @@ impl<'b, TX: DbTx> HistoricalStateProviderRef<'b, TX> { ) } + /// Retrieve revert hashed state for this history provider. + fn revert_state(&self) -> ProviderResult { + if !self.lowest_available_blocks.is_account_history_available(self.block_number) || + !self.lowest_available_blocks.is_storage_history_available(self.block_number) + { + return Err(ProviderError::StateAtBlockPruned(self.block_number)) + } + + let (tip, _) = self + .tx + .cursor_read::()? + .last()? + .ok_or(ProviderError::BestBlockNotFound)?; + + if tip.saturating_sub(self.block_number) > EPOCH_SLOTS { + tracing::warn!( + target: "provider::historical_sp", + target = self.block_number, + "Attempt to calculate state root for an old block might result in OOM, tread carefully" + ); + } + + Ok(HashedPostState::from_revert_range(self.tx, self.block_number..=tip)?) + } + fn history_info( &self, key: K, @@ -199,15 +225,23 @@ impl<'b, TX: DbTx> BlockHashReader for HistoricalStateProviderRef<'b, TX> { } impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> { - fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult { - Err(ProviderError::StateRootNotAvailableForHistoricalBlock) + fn state_root(&self, state: &BundleStateWithReceipts) -> ProviderResult { + let mut revert_state = self.revert_state()?; + revert_state.extend(state.hash_state_slow()); + revert_state.sort(); + revert_state.state_root(self.tx).map_err(|err| ProviderError::Database(err.into())) } fn state_root_with_updates( &self, - _bundle_state: &BundleStateWithReceipts, + state: &BundleStateWithReceipts, ) -> ProviderResult<(B256, TrieUpdates)> { - Err(ProviderError::StateRootNotAvailableForHistoricalBlock) + let mut revert_state = self.revert_state()?; + revert_state.extend(state.hash_state_slow()); + revert_state.sort(); + revert_state + .state_root_with_updates(self.tx) + .map_err(|err| ProviderError::Database(err.into())) } } diff --git a/crates/trie/src/hashed_cursor/post_state.rs b/crates/trie/src/hashed_cursor/post_state.rs index 81762aba6a99..6a6aebb80b24 100644 --- a/crates/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/src/hashed_cursor/post_state.rs @@ -270,7 +270,7 @@ where // If the storage has been wiped at any point storage.wiped && // and the current storage does not contain any non-zero values - storage.non_zero_valued_storage.is_empty() + storage.non_zero_valued_slots.is_empty() } None => self.cursor.seek_exact(key)?.is_none(), }; @@ -294,12 +294,11 @@ where if let Some(storage) = self.post_state.storages.get(&account) { debug_assert!(storage.sorted, "`HashedStorage` must be pre-sorted"); - post_state_entry = storage.non_zero_valued_storage.get(self.post_state_storage_index); + post_state_entry = storage.non_zero_valued_slots.get(self.post_state_storage_index); while post_state_entry.map(|(slot, _)| slot < &subkey).unwrap_or_default() { self.post_state_storage_index += 1; - post_state_entry = - storage.non_zero_valued_storage.get(self.post_state_storage_index); + post_state_entry = storage.non_zero_valued_slots.get(self.post_state_storage_index); } } @@ -374,11 +373,10 @@ where if let Some(storage) = self.post_state.storages.get(&account) { debug_assert!(storage.sorted, "`HashedStorage` must be pre-sorted"); - post_state_entry = storage.non_zero_valued_storage.get(self.post_state_storage_index); + post_state_entry = storage.non_zero_valued_slots.get(self.post_state_storage_index); while post_state_entry.map(|(slot, _)| slot <= last_slot).unwrap_or_default() { self.post_state_storage_index += 1; - post_state_entry = - storage.non_zero_valued_storage.get(self.post_state_storage_index); + post_state_entry = storage.non_zero_valued_slots.get(self.post_state_storage_index); } } diff --git a/crates/trie/src/state.rs b/crates/trie/src/state.rs index 552d6d5fed7f..620254edad51 100644 --- a/crates/trie/src/state.rs +++ b/crates/trie/src/state.rs @@ -78,7 +78,7 @@ impl HashedPostState { /// NOTE: In order to have the resulting [HashedPostState] be a correct /// overlay of the plain state, the end of the range must be the current tip. pub fn from_revert_range( - tx: TX, + tx: &TX, range: RangeInclusive, ) -> Result { // A single map for aggregating state changes where each map value is a tuple @@ -119,7 +119,7 @@ impl HashedPostState { this.insert_account(hashed_address, account_change); } - // The `wiped`` flag indicates only whether previous storage entries should be looked + // The `wiped` flag indicates only whether previous storage entries should be looked // up in db or not. For reverts it's a noop since all wiped changes had been written as // storage reverts. let mut hashed_storage = HashedStorage::new(false); @@ -132,6 +132,40 @@ impl HashedPostState { Ok(this.sorted()) } + /// Extend this hashed post state with contents of another. + /// Entries in the second hashed post state take precedence. + pub fn extend(&mut self, other: Self) { + // Merge accounts and insert them into extended state. + let mut accounts: HashMap> = HashMap::from_iter( + self.accounts + .drain(..) + .map(|(hashed_address, account)| (hashed_address, Some(account))) + .chain( + self.destroyed_accounts.drain().map(|hashed_address| (hashed_address, None)), + ), + ); + for (hashed_address, account) in other.accounts { + accounts.insert(hashed_address, Some(account)); + } + for hashed_address in other.destroyed_accounts { + accounts.insert(hashed_address, None); + } + for (hashed_address, account) in accounts { + self.insert_account(hashed_address, account); + } + + for (hashed_address, storage) in other.storages { + match self.storages.entry(hashed_address) { + hash_map::Entry::Vacant(entry) => { + entry.insert(storage); + } + hash_map::Entry::Occupied(mut entry) => { + entry.get_mut().extend(storage); + } + } + } + } + /// Sort and return self. pub fn sorted(mut self) -> Self { self.sort(); @@ -204,7 +238,7 @@ impl HashedPostState { account_prefix_set.insert(Nibbles::unpack(hashed_address)); let storage_prefix_set_entry = storage_prefix_set.entry(*hashed_address).or_default(); - for (hashed_slot, _) in &hashed_storage.non_zero_valued_storage { + for (hashed_slot, _) in &hashed_storage.non_zero_valued_slots { storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot)); } for hashed_slot in &hashed_storage.zero_valued_slots { @@ -223,6 +257,7 @@ impl HashedPostState { &self, tx: &'a TX, ) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, '_, TX>> { + assert!(self.sorted, "Hashed post state must be sorted for state root calculation"); let (account_prefix_set, storage_prefix_set) = self.construct_prefix_sets(); let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, self); StateRoot::from_tx(tx) @@ -277,10 +312,10 @@ impl HashedPostState { } /// The post state account storage with hashed slots. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Debug)] pub struct HashedStorage { /// Hashed storage slots with non-zero. - pub(crate) non_zero_valued_storage: Vec<(B256, U256)>, + pub(crate) non_zero_valued_slots: Vec<(B256, U256)>, /// Slots that have been zero valued. pub(crate) zero_valued_slots: AHashSet, /// Whether the storage was wiped or not. @@ -293,13 +328,32 @@ impl HashedStorage { /// Create new instance of [HashedStorage]. pub fn new(wiped: bool) -> Self { Self { - non_zero_valued_storage: Vec::new(), + non_zero_valued_slots: Vec::new(), zero_valued_slots: AHashSet::new(), wiped, sorted: true, // empty is sorted } } + /// Extend hashed storage with contents of other. + /// The entries in second hashed storage take precedence. + pub fn extend(&mut self, other: Self) { + let mut entries: HashMap = + HashMap::from_iter(self.non_zero_valued_slots.drain(..).chain( + self.zero_valued_slots.drain().map(|hashed_slot| (hashed_slot, U256::ZERO)), + )); + for (hashed_slot, value) in other.non_zero_valued_slots { + entries.insert(hashed_slot, value); + } + for hashed_slot in other.zero_valued_slots { + entries.insert(hashed_slot, U256::ZERO); + } + for (hashed_slot, value) in entries { + self.insert_slot(hashed_slot, value); + } + self.wiped |= other.wiped; + } + /// Returns `true` if the storage was wiped. pub fn wiped(&self) -> bool { self.wiped @@ -310,13 +364,13 @@ impl HashedStorage { self.zero_valued_slots .iter() .map(|slot| (*slot, U256::ZERO)) - .chain(self.non_zero_valued_storage.iter().cloned()) + .chain(self.non_zero_valued_slots.iter().cloned()) } /// Sorts the non zero value storage entries. pub fn sort_storage(&mut self) { if !self.sorted { - self.non_zero_valued_storage.sort_unstable_by_key(|(slot, _)| *slot); + self.non_zero_valued_slots.sort_unstable_by_key(|(slot, _)| *slot); self.sorted = true; } } @@ -327,7 +381,7 @@ impl HashedStorage { if value.is_zero() { self.zero_valued_slots.insert(slot); } else { - self.non_zero_valued_storage.push((slot, value)); + self.non_zero_valued_slots.push((slot, value)); self.sorted = false; } } diff --git a/crates/trie/src/trie.rs b/crates/trie/src/trie.rs index 917a8aa214df..1d103a475cab 100644 --- a/crates/trie/src/trie.rs +++ b/crates/trie/src/trie.rs @@ -1164,7 +1164,6 @@ mod tests { } #[test] - fn account_trie_around_extension_node_with_dbtrie() { let factory = create_test_provider_factory(); let tx = factory.provider_rw().unwrap(); From 720815a83465a4c79c0066e466ad53d005cc32c1 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Thu, 25 Jan 2024 18:59:30 +0100 Subject: [PATCH 04/33] chore(node-builder): make `DB` generic `Unpin` in node components (#6227) --- crates/node-core/src/cli/components.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/node-core/src/cli/components.rs b/crates/node-core/src/cli/components.rs index b8d5ba5728fe..04168dbc4403 100644 --- a/crates/node-core/src/cli/components.rs +++ b/crates/node-core/src/cli/components.rs @@ -48,7 +48,7 @@ impl FullProvider for T where /// The trait that is implemented for the Node command. pub trait RethNodeComponents: Clone + Send + Sync + 'static { /// Underlying database type. - type DB: Database + Clone + 'static; + type DB: Database + Clone + Unpin + 'static; /// The Provider type that is provided by the node itself type Provider: FullProvider; /// The transaction pool type @@ -147,7 +147,7 @@ impl impl RethNodeComponents for RethNodeComponentsImpl where - DB: Database + Clone + 'static, + DB: Database + Clone + Unpin + 'static, Provider: FullProvider + Clone + 'static, Tasks: TaskSpawner + Clone + Unpin + 'static, Pool: TransactionPool + Clone + Unpin + 'static, From 9c9be5796a0301de5b97c29cbc9ad9ea74d3f7e3 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:29:38 +0100 Subject: [PATCH 05/33] doc(eth-wire): add missing documentation for `message` mod (#6232) --- crates/net/eth-wire/src/types/message.rs | 55 ++++++++++++++++++++---- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/crates/net/eth-wire/src/types/message.rs b/crates/net/eth-wire/src/types/message.rs index 81f6035a995d..60bf098150f9 100644 --- a/crates/net/eth-wire/src/types/message.rs +++ b/crates/net/eth-wire/src/types/message.rs @@ -1,4 +1,10 @@ -#![allow(missing_docs)] +//! Implements Ethereum wire protocol for versions 66, 67, and 68. +//! Defines structs/enums for messages, request-response pairs, and broadcasts. +//! Handles compatibility with [`EthVersion`]. +//! +//! Examples include creating, encoding, and decoding protocol messages. +//! +//! Reference: [Ethereum Wire Protocol](https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol). use super::{ broadcast::NewBlockHashes, BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders, @@ -8,10 +14,9 @@ use super::{ use crate::{errors::EthStreamError, EthVersion, SharedTransactions}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use reth_primitives::bytes::{Buf, BufMut}; -use std::{fmt::Debug, sync::Arc}; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use std::{fmt::Debug, sync::Arc}; /// [`MAX_MESSAGE_SIZE`] is the maximum cap on the size of a protocol message. // https://github.com/ethereum/go-ethereum/blob/30602163d5d8321fbc68afdcbbaf2362b2641bde/eth/protocols/eth/protocol.go#L50 @@ -21,7 +26,9 @@ pub const MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ProtocolMessage { + /// The unique identifier representing the type of the Ethereum message. pub message_type: EthMessageID, + /// The content of the message, including specific data based on the message type. pub message: EthMessage, } @@ -126,7 +133,10 @@ impl From for ProtocolMessage { /// Represents messages that can be sent to multiple peers. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ProtocolBroadcastMessage { + /// The unique identifier representing the type of the Ethereum message. pub message_type: EthMessageID, + /// The content of the message to be broadcasted, including specific data based on the message + /// type. pub message: EthBroadcastMessage, } @@ -168,25 +178,38 @@ impl From for ProtocolBroadcastMessage { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum EthMessage { - /// Status is required for the protocol handshake + /// Represents a Status message required for the protocol handshake. Status(Status), - /// The following messages are broadcast to the network + /// Represents a NewBlockHashes message broadcast to the network. NewBlockHashes(NewBlockHashes), + /// Represents a NewBlock message broadcast to the network. NewBlock(Box), + /// Represents a Transactions message broadcast to the network. Transactions(Transactions), + /// Represents a NewPooledTransactionHashes message for eth/66 version. NewPooledTransactionHashes66(NewPooledTransactionHashes66), + /// Represents a NewPooledTransactionHashes message for eth/68 version. NewPooledTransactionHashes68(NewPooledTransactionHashes68), - // The following messages are request-response message pairs + /// Represents a GetBlockHeaders request-response pair. GetBlockHeaders(RequestPair), + /// Represents a BlockHeaders request-response pair. BlockHeaders(RequestPair), + /// Represents a GetBlockBodies request-response pair. GetBlockBodies(RequestPair), + /// Represents a BlockBodies request-response pair. BlockBodies(RequestPair), + /// Represents a GetPooledTransactions request-response pair. GetPooledTransactions(RequestPair), + /// Represents a PooledTransactions request-response pair. PooledTransactions(RequestPair), + /// Represents a GetNodeData request-response pair. GetNodeData(RequestPair), + /// Represents a NodeData request-response pair. NodeData(RequestPair), + /// Represents a GetReceipts request-response pair. GetReceipts(RequestPair), + /// Represents a Receipts request-response pair. Receipts(RequestPair), } @@ -266,7 +289,9 @@ impl Encodable for EthMessage { /// Note: This is only useful for outgoing messages. #[derive(Clone, Debug, PartialEq, Eq)] pub enum EthBroadcastMessage { + /// Represents a new block broadcast message. NewBlock(Arc), + /// Represents a transactions broadcast message. Transactions(SharedTransactions), } @@ -303,20 +328,35 @@ impl Encodable for EthBroadcastMessage { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum EthMessageID { + /// Status message. Status = 0x00, + /// New block hashes message. NewBlockHashes = 0x01, + /// Transactions message. Transactions = 0x02, + /// Get block headers message. GetBlockHeaders = 0x03, + /// Block headers message. BlockHeaders = 0x04, + /// Get block bodies message. GetBlockBodies = 0x05, + /// Block bodies message. BlockBodies = 0x06, + /// New block message. NewBlock = 0x07, + /// New pooled transaction hashes message. NewPooledTransactionHashes = 0x08, + /// Requests pooled transactions. GetPooledTransactions = 0x09, + /// Represents pooled transactions. PooledTransactions = 0x0a, + /// Requests node data. GetNodeData = 0x0d, + /// Represents node data. NodeData = 0x0e, + /// Requests receipts. GetReceipts = 0x0f, + /// Represents receipts. Receipts = 0x10, } @@ -338,8 +378,7 @@ impl Encodable for EthMessageID { impl Decodable for EthMessageID { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - let id = buf.first().ok_or(alloy_rlp::Error::InputTooShort)?; - let id = match id { + let id = match buf.first().ok_or(alloy_rlp::Error::InputTooShort)? { 0x00 => EthMessageID::Status, 0x01 => EthMessageID::NewBlockHashes, 0x02 => EthMessageID::Transactions, From abe3358b363a2b942d0d33248c259c52734be150 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:37:07 -0500 Subject: [PATCH 06/33] feat: move NodeBuilderWithDatabase to reth bin (#6231) --- Cargo.lock | 6 +- bin/reth/Cargo.toml | 1 + bin/reth/src/builder.rs | 843 ++++++++++++++++ .../commands/debug_cmd}/engine_api_store.rs | 0 bin/reth/src/commands/debug_cmd/mod.rs | 1 + .../src/commands/debug_cmd/replay_engine.rs | 2 +- .../reth/src/commands/node}/cl_events.rs | 0 .../reth/src/commands/node}/events.rs | 4 +- bin/reth/src/commands/node/mod.rs | 16 +- {crates/node-core => bin/reth}/src/init.rs | 0 bin/reth/src/lib.rs | 13 +- crates/node-core/Cargo.toml | 9 +- crates/node-core/src/lib.rs | 4 - crates/node-core/src/node_config.rs | 944 +----------------- 14 files changed, 884 insertions(+), 959 deletions(-) create mode 100644 bin/reth/src/builder.rs rename {crates/node-core/src => bin/reth/src/commands/debug_cmd}/engine_api_store.rs (100%) rename {crates/node-core/src => bin/reth/src/commands/node}/cl_events.rs (100%) rename {crates/node-core/src => bin/reth/src/commands/node}/events.rs (99%) rename {crates/node-core => bin/reth}/src/init.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 6e345574cca6..2fbae246078b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5786,6 +5786,7 @@ dependencies = [ "serde_json", "similar-asserts", "tempfile", + "thiserror", "tokio", "toml 0.8.8", "tracing", @@ -6395,16 +6396,13 @@ dependencies = [ "alloy-rlp", "assert_matches", "clap", - "confy", "const-str", "dirs-next", "eyre", - "fdlimit", "futures", "humantime", "hyper", "jemalloc-ctl", - "jemallocator", "jsonrpsee", "metrics", "metrics-exporter-prometheus", @@ -6424,14 +6422,12 @@ dependencies = [ "reth-db", "reth-discv4", "reth-downloaders", - "reth-ethereum-payload-builder", "reth-interfaces", "reth-metrics", "reth-net-nat", "reth-network", "reth-network-api", "reth-node-api", - "reth-node-builder", "reth-optimism-payload-builder", "reth-payload-builder", "reth-primitives", diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 71b3bdb035dd..a6c0a5a4357a 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -102,6 +102,7 @@ hyper = "0.14.25" # misc aquamarine.workspace = true eyre.workspace = true +thiserror.workspace = true clap = { workspace = true, features = ["derive"] } tempfile.workspace = true backon = "0.4" diff --git a/bin/reth/src/builder.rs b/bin/reth/src/builder.rs new file mode 100644 index 000000000000..669f73617b94 --- /dev/null +++ b/bin/reth/src/builder.rs @@ -0,0 +1,843 @@ +//! Contains types and methods that can be used to launch a node based off of a [NodeConfig]. + +use crate::{ + commands::{ + debug_cmd::engine_api_store::EngineApiStore, + node::{cl_events::ConsensusLayerHealthEvents, events}, + }, + init::init_genesis, +}; +use eyre::Context; +use fdlimit::raise_fd_limit; +use futures::{future::Either, stream, stream_select, StreamExt}; +use reth_auto_seal_consensus::AutoSealBuilder; +use reth_beacon_consensus::{ + hooks::{EngineHooks, PruneHook}, + BeaconConsensusEngine, BeaconConsensusEngineError, MIN_BLOCKS_FOR_PIPELINE_RUN, +}; +use reth_blockchain_tree::{config::BlockchainTreeConfig, ShareableBlockchainTree}; +use reth_config::Config; +use reth_db::{ + database::Database, + database_metrics::{DatabaseMetadata, DatabaseMetrics}, +}; +use reth_interfaces::p2p::either::EitherDownloader; +use reth_network::NetworkEvents; +use reth_network_api::{NetworkInfo, PeersInfo}; +#[cfg(not(feature = "optimism"))] +use reth_node_builder::EthEngineTypes; +#[cfg(feature = "optimism")] +use reth_node_builder::OptimismEngineTypes; +use reth_node_core::{ + cli::{ + components::{RethNodeComponentsImpl, RethRpcServerHandles}, + config::RethRpcConfig, + db_type::DatabaseInstance, + ext::{DefaultRethNodeCommandConfig, RethCliExt, RethNodeCommandConfig}, + }, + dirs::{ChainPath, DataDirPath}, + version::SHORT_VERSION, +}; +use reth_payload_builder::PayloadBuilderHandle; +use reth_primitives::DisplayHardforks; +use reth_provider::{providers::BlockchainProvider, ProviderFactory}; +use reth_prune::PrunerBuilder; +use reth_rpc_engine_api::EngineApi; +use reth_tasks::{TaskExecutor, TaskManager}; +use reth_transaction_pool::TransactionPool; +use std::{path::PathBuf, sync::Arc}; +use tokio::sync::{mpsc::unbounded_channel, oneshot}; +use tracing::*; + +/// Re-export `NodeConfig` from `reth_node_core`. +pub use reth_node_core::node_config::NodeConfig; + +/// Launches the node, also adding any RPC extensions passed. +/// +/// # Example +/// ```rust +/// # use reth_tasks::{TaskManager, TaskSpawner}; +/// # use reth_node_core::node_config::NodeConfig; +/// # use reth_node_core::cli::{ +/// # ext::DefaultRethNodeCommandConfig, +/// # }; +/// # use tokio::runtime::Handle; +/// # use reth::builder::launch_from_config; +/// +/// async fn t() { +/// let handle = Handle::current(); +/// let manager = TaskManager::new(handle); +/// let executor = manager.executor(); +/// let builder = NodeConfig::default(); +/// let ext = DefaultRethNodeCommandConfig::default(); +/// let handle = launch_from_config::<()>(builder, ext, executor).await.unwrap(); +/// } +/// ``` +pub async fn launch_from_config( + mut config: NodeConfig, + ext: E::Node, + executor: TaskExecutor, +) -> eyre::Result { + info!(target: "reth::cli", "reth {} starting", SHORT_VERSION); + + let database = std::mem::take(&mut config.database); + let db_instance = database.init_db(config.db.log_level, config.chain.chain)?; + + match db_instance { + DatabaseInstance::Real { db, data_dir } => { + let builder = NodeBuilderWithDatabase { config, db, data_dir }; + builder.launch::(ext, executor).await + } + DatabaseInstance::Test { db, data_dir } => { + let builder = NodeBuilderWithDatabase { config, db, data_dir }; + builder.launch::(ext, executor).await + } + } +} + +/// A version of the [NodeConfig] that has an installed database. This is used to construct the +/// [NodeHandle]. +/// +/// This also contains a path to a data dir that cannot be changed. +#[derive(Debug)] +pub struct NodeBuilderWithDatabase { + /// The node config + pub config: NodeConfig, + /// The database + pub db: Arc, + /// The data dir + pub data_dir: ChainPath, +} + +impl NodeBuilderWithDatabase { + /// Launch the node with the given extensions and executor + pub async fn launch( + mut self, + mut ext: E::Node, + executor: TaskExecutor, + ) -> eyre::Result { + // Raise the fd limit of the process. + // Does not do anything on windows. + raise_fd_limit()?; + + // get config + let config = self.load_config()?; + + let prometheus_handle = self.config.install_prometheus_recorder()?; + info!(target: "reth::cli", "Database opened"); + + let mut provider_factory = + ProviderFactory::new(Arc::clone(&self.db), Arc::clone(&self.config.chain)); + + // configure snapshotter + let snapshotter = reth_snapshot::Snapshotter::new( + provider_factory.clone(), + self.data_dir.snapshots_path(), + self.config.chain.snapshot_block_interval, + )?; + + provider_factory = provider_factory.with_snapshots( + self.data_dir.snapshots_path(), + snapshotter.highest_snapshot_receiver(), + )?; + + self.config.start_metrics_endpoint(prometheus_handle, Arc::clone(&self.db)).await?; + + debug!(target: "reth::cli", chain=%self.config.chain.chain, genesis=?self.config.chain.genesis_hash(), "Initializing genesis"); + + let genesis_hash = init_genesis(Arc::clone(&self.db), self.config.chain.clone())?; + + info!(target: "reth::cli", "{}", DisplayHardforks::new(self.config.chain.hardforks())); + + let consensus = self.config.consensus(); + + debug!(target: "reth::cli", "Spawning stages metrics listener task"); + let (sync_metrics_tx, sync_metrics_rx) = unbounded_channel(); + let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx); + executor.spawn_critical("stages metrics listener task", sync_metrics_listener); + + let prune_config = self + .config + .pruning + .prune_config(Arc::clone(&self.config.chain))? + .or(config.prune.clone()); + + // configure blockchain tree + let tree_config = BlockchainTreeConfig::default(); + let tree = self.config.build_blockchain_tree( + provider_factory.clone(), + consensus.clone(), + prune_config.clone(), + sync_metrics_tx.clone(), + tree_config, + )?; + let canon_state_notification_sender = tree.canon_state_notification_sender(); + let blockchain_tree = ShareableBlockchainTree::new(tree); + debug!(target: "reth::cli", "configured blockchain tree"); + + // fetch the head block from the database + let head = self + .config + .lookup_head(provider_factory.clone()) + .wrap_err("the head block is missing")?; + + // setup the blockchain provider + let blockchain_db = + BlockchainProvider::new(provider_factory.clone(), blockchain_tree.clone())?; + + // build transaction pool + let transaction_pool = + self.config.build_and_spawn_txpool(&blockchain_db, head, &executor, &self.data_dir)?; + + // build network + let (network_client, mut network_builder) = self + .config + .build_network( + &config, + provider_factory.clone(), + executor.clone(), + head, + &self.data_dir, + ) + .await?; + + let components = RethNodeComponentsImpl::new( + blockchain_db.clone(), + transaction_pool.clone(), + network_builder.handle(), + executor.clone(), + blockchain_db.clone(), + ); + + // allow network modifications + ext.configure_network(network_builder.network_mut(), &components)?; + + // launch network + let network = self.config.start_network( + network_builder, + &executor, + transaction_pool.clone(), + network_client, + &self.data_dir, + ); + + info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), enode = %network.local_node_record(), "Connected to P2P network"); + debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID"); + let network_client = network.fetch_client().await?; + + ext.on_components_initialized(&components)?; + + debug!(target: "reth::cli", "Spawning payload builder service"); + + // TODO: stateful node builder should handle this in with_payload_builder + // Optimism's payload builder is implemented on the OptimismPayloadBuilder type. + #[cfg(feature = "optimism")] + let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() + .set_compute_pending_block(self.config.builder.compute_pending_block); + + #[cfg(feature = "optimism")] + let payload_builder: PayloadBuilderHandle = + ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?; + + // The default payload builder is implemented on the unit type. + #[cfg(not(feature = "optimism"))] + let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default(); + + #[cfg(not(feature = "optimism"))] + let payload_builder: PayloadBuilderHandle = + ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?; + + let (consensus_engine_tx, mut consensus_engine_rx) = unbounded_channel(); + if let Some(store_path) = self.config.debug.engine_api_store.clone() { + let (engine_intercept_tx, engine_intercept_rx) = unbounded_channel(); + let engine_api_store = EngineApiStore::new(store_path); + executor.spawn_critical( + "engine api interceptor", + engine_api_store.intercept(consensus_engine_rx, engine_intercept_tx), + ); + consensus_engine_rx = engine_intercept_rx; + }; + let max_block = self.config.max_block(&network_client, provider_factory.clone()).await?; + + // Configure the pipeline + let (mut pipeline, client) = if self.config.dev.dev { + info!(target: "reth::cli", "Starting Reth in dev mode"); + let mining_mode = + self.config.mining_mode(transaction_pool.pending_transactions_listener()); + + let (_, client, mut task) = AutoSealBuilder::new( + Arc::clone(&self.config.chain), + blockchain_db.clone(), + transaction_pool.clone(), + consensus_engine_tx.clone(), + canon_state_notification_sender, + mining_mode, + ) + .build(); + + let mut pipeline = self + .config + .build_networked_pipeline( + &config.stages, + client.clone(), + Arc::clone(&consensus), + provider_factory.clone(), + &executor, + sync_metrics_tx, + prune_config.clone(), + max_block, + ) + .await?; + + let pipeline_events = pipeline.events(); + task.set_pipeline_events(pipeline_events); + debug!(target: "reth::cli", "Spawning auto mine task"); + executor.spawn(Box::pin(task)); + + (pipeline, EitherDownloader::Left(client)) + } else { + let pipeline = self + .config + .build_networked_pipeline( + &config.stages, + network_client.clone(), + Arc::clone(&consensus), + provider_factory.clone(), + &executor.clone(), + sync_metrics_tx, + prune_config.clone(), + max_block, + ) + .await?; + + (pipeline, EitherDownloader::Right(network_client)) + }; + + let pipeline_events = pipeline.events(); + + let initial_target = self.config.initial_pipeline_target(genesis_hash); + let mut hooks = EngineHooks::new(); + + let pruner_events = if let Some(prune_config) = prune_config { + let mut pruner = PrunerBuilder::new(prune_config.clone()) + .max_reorg_depth(tree_config.max_reorg_depth() as usize) + .prune_delete_limit(self.config.chain.prune_delete_limit) + .build(provider_factory, snapshotter.highest_snapshot_receiver()); + + let events = pruner.events(); + hooks.add(PruneHook::new(pruner, Box::new(executor.clone()))); + + info!(target: "reth::cli", ?prune_config, "Pruner initialized"); + Either::Left(events) + } else { + Either::Right(stream::empty()) + }; + + // Configure the consensus engine + let (beacon_consensus_engine, beacon_engine_handle) = BeaconConsensusEngine::with_channel( + client, + pipeline, + blockchain_db.clone(), + Box::new(executor.clone()), + Box::new(network.clone()), + max_block, + self.config.debug.continuous, + payload_builder.clone(), + initial_target, + MIN_BLOCKS_FOR_PIPELINE_RUN, + consensus_engine_tx, + consensus_engine_rx, + hooks, + )?; + info!(target: "reth::cli", "Consensus engine initialized"); + + let events = stream_select!( + network.event_listener().map(Into::into), + beacon_engine_handle.event_listener().map(Into::into), + pipeline_events.map(Into::into), + if self.config.debug.tip.is_none() { + Either::Left( + ConsensusLayerHealthEvents::new(Box::new(blockchain_db.clone())) + .map(Into::into), + ) + } else { + Either::Right(stream::empty()) + }, + pruner_events.map(Into::into) + ); + executor.spawn_critical( + "events task", + events::handle_events( + Some(network.clone()), + Some(head.number), + events, + self.db.clone(), + ), + ); + + let engine_api = EngineApi::new( + blockchain_db.clone(), + self.config.chain.clone(), + beacon_engine_handle, + payload_builder.into(), + Box::new(executor.clone()), + ); + info!(target: "reth::cli", "Engine API handler initialized"); + + // extract the jwt secret from the args if possible + let default_jwt_path = self.data_dir.jwt_path(); + let jwt_secret = self.config.rpc.auth_jwt_secret(default_jwt_path)?; + + // adjust rpc port numbers based on instance number + self.config.adjust_instance_ports(); + + // Start RPC servers + let rpc_server_handles = + self.config.rpc.start_servers(&components, engine_api, jwt_secret, &mut ext).await?; + + // Run consensus engine to completion + let (tx, rx) = oneshot::channel(); + info!(target: "reth::cli", "Starting consensus engine"); + executor.spawn_critical_blocking("consensus engine", async move { + let res = beacon_consensus_engine.await; + let _ = tx.send(res); + }); + + ext.on_node_started(&components)?; + + // If `enable_genesis_walkback` is set to true, the rollup client will need to + // perform the derivation pipeline from genesis, validating the data dir. + // When set to false, set the finalized, safe, and unsafe head block hashes + // on the rollup client using a fork choice update. This prevents the rollup + // client from performing the derivation pipeline from genesis, and instead + // starts syncing from the current tip in the DB. + #[cfg(feature = "optimism")] + if self.config.chain.is_optimism() && !self.config.rollup.enable_genesis_walkback { + let client = rpc_server_handles.auth.http_client(); + reth_rpc_api::EngineApiClient::::fork_choice_updated_v2( + &client, + reth_rpc_types::engine::ForkchoiceState { + head_block_hash: head.hash, + safe_block_hash: head.hash, + finalized_block_hash: head.hash, + }, + None, + ) + .await?; + } + + // construct node handle and return + let node_handle = NodeHandle { + rpc_server_handles, + consensus_engine_rx: rx, + terminate: self.config.debug.terminate, + }; + Ok(node_handle) + } + + /// Returns the path to the config file. + fn config_path(&self) -> PathBuf { + self.config.config.clone().unwrap_or_else(|| self.data_dir.config_path()) + } + + /// Loads the reth config with the given datadir root + fn load_config(&self) -> eyre::Result { + let config_path = self.config_path(); + + let mut config = confy::load_path::(&config_path) + .wrap_err_with(|| format!("Could not load config file {:?}", config_path))?; + + info!(target: "reth::cli", path = ?config_path, "Configuration loaded"); + + // Update the config with the command line arguments + config.peers.connect_trusted_nodes_only = self.config.network.trusted_only; + + if !self.config.network.trusted_peers.is_empty() { + info!(target: "reth::cli", "Adding trusted nodes"); + self.config.network.trusted_peers.iter().for_each(|peer| { + config.peers.trusted_nodes.insert(*peer); + }); + } + + Ok(config) + } +} + +/// The [NodeHandle] contains the [RethRpcServerHandles] returned by the reth initialization +/// process, as well as a method for waiting for the node exit. +#[derive(Debug)] +pub struct NodeHandle { + /// The handles to the RPC servers + rpc_server_handles: RethRpcServerHandles, + + /// The receiver half of the channel for the consensus engine. + /// This can be used to wait for the consensus engine to exit. + consensus_engine_rx: oneshot::Receiver>, + + /// Flag indicating whether the node should be terminated after the pipeline sync. + terminate: bool, +} + +impl NodeHandle { + /// Returns the [RethRpcServerHandles] for this node. + pub fn rpc_server_handles(&self) -> &RethRpcServerHandles { + &self.rpc_server_handles + } + + /// Waits for the node to exit, if it was configured to exit. + pub async fn wait_for_node_exit(self) -> eyre::Result<()> { + self.consensus_engine_rx.await??; + + if self.terminate { + Ok(()) + } else { + // The pipeline has finished downloading blocks up to `--debug.tip` or + // `--debug.max-block`. Keep other node components alive for further usage. + futures::future::pending().await + } + } +} + +/// A simple function to launch a node with the specified [NodeConfig], spawning tasks on the +/// [TaskExecutor] constructed from [TaskManager::current]. +/// +/// # Example +/// ``` +/// # use reth_node_core::{ +/// # node_config::NodeConfig, +/// # args::RpcServerArgs, +/// # }; +/// # use reth::builder::spawn_node; +/// async fn t() { +/// // Create a node builder with an http rpc server enabled +/// let rpc_args = RpcServerArgs::default().with_http(); +/// +/// let builder = NodeConfig::test().with_rpc(rpc_args); +/// +/// // Spawn the builder, returning a handle to the node +/// let (_handle, _manager) = spawn_node(builder).await.unwrap(); +/// } +/// ``` +pub async fn spawn_node(config: NodeConfig) -> eyre::Result<(NodeHandle, TaskManager)> { + let task_manager = TaskManager::current(); + let ext = DefaultRethNodeCommandConfig::default(); + Ok((launch_from_config::<()>(config, ext, task_manager.executor()).await?, task_manager)) +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_node_core::args::RpcServerArgs; + use reth_primitives::U256; + use reth_rpc_api::EthApiClient; + + #[tokio::test] + async fn block_number_node_config_test() { + // this launches a test node with http + let rpc_args = RpcServerArgs::default().with_http(); + + let (handle, _manager) = spawn_node(NodeConfig::test().with_rpc(rpc_args)).await.unwrap(); + + // call a function on the node + let client = handle.rpc_server_handles().rpc.http_client().unwrap(); + let block_number = client.block_number().await.unwrap(); + + // it should be zero, since this is an ephemeral test node + assert_eq!(block_number, U256::ZERO); + } + + #[tokio::test] + async fn rpc_handles_none_without_http() { + // this launches a test node _without_ http + let (handle, _manager) = spawn_node(NodeConfig::test()).await.unwrap(); + + // ensure that the `http_client` is none + let maybe_client = handle.rpc_server_handles().rpc.http_client(); + assert!(maybe_client.is_none()); + } + + #[tokio::test] + async fn launch_multiple_nodes() { + // spawn_test_node takes roughly 1 second per node, so this test takes ~4 seconds + let num_nodes = 4; + + // contains handles and managers + let mut handles = Vec::new(); + for _ in 0..num_nodes { + let handle = spawn_node(NodeConfig::test()).await.unwrap(); + handles.push(handle); + } + } + + #[cfg(feature = "optimism")] + #[tokio::test] + async fn optimism_pre_canyon_no_withdrawals_valid() { + reth_tracing::init_test_tracing(); + use alloy_chains::Chain; + use jsonrpsee::http_client::HttpClient; + use reth_primitives::{ChainSpec, Genesis}; + use reth_rpc_api::EngineApiClient; + use reth_rpc_types::engine::{ + ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, + }; + + // this launches a test node with http + let rpc_args = RpcServerArgs::default().with_http(); + + // create optimism genesis with canyon at block 2 + let spec = ChainSpec::builder() + .chain(Chain::optimism_mainnet()) + .genesis(Genesis::default()) + .regolith_activated() + .build(); + + let genesis_hash = spec.genesis_hash(); + + // create node config + let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); + + let (handle, _manager) = spawn_node(node_config).await.unwrap(); + + // call a function on the node + let client = handle.rpc_server_handles().auth.http_client(); + let block_number = client.block_number().await.unwrap(); + + // it should be zero, since this is an ephemeral test node + assert_eq!(block_number, U256::ZERO); + + // call the engine_forkchoiceUpdated function with payload attributes + let forkchoice_state = ForkchoiceState { + head_block_hash: genesis_hash, + safe_block_hash: genesis_hash, + finalized_block_hash: genesis_hash, + }; + + let payload_attributes = OptimismPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 1, + prev_randao: Default::default(), + suggested_fee_recipient: Default::default(), + // canyon is _not_ in the chain spec, so this should cause the engine call to fail + withdrawals: None, + parent_beacon_block_root: None, + }, + no_tx_pool: None, + gas_limit: Some(1), + transactions: None, + }; + + // call the engine_forkchoiceUpdated function with payload attributes + let res = >::fork_choice_updated_v2( + &client, + forkchoice_state, + Some(payload_attributes), + ) + .await; + res.expect("pre-canyon engine call without withdrawals should succeed"); + } + + #[cfg(feature = "optimism")] + #[tokio::test] + async fn optimism_pre_canyon_withdrawals_invalid() { + reth_tracing::init_test_tracing(); + use alloy_chains::Chain; + use assert_matches::assert_matches; + use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE}; + use reth_primitives::{ChainSpec, Genesis}; + use reth_rpc_api::EngineApiClient; + use reth_rpc_types::engine::{ + ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, + }; + + // this launches a test node with http + let rpc_args = RpcServerArgs::default().with_http(); + + // create optimism genesis with canyon at block 2 + let spec = ChainSpec::builder() + .chain(Chain::optimism_mainnet()) + .genesis(Genesis::default()) + .regolith_activated() + .build(); + + let genesis_hash = spec.genesis_hash(); + + // create node config + let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); + + let (handle, _manager) = spawn_node(node_config).await.unwrap(); + + // call a function on the node + let client = handle.rpc_server_handles().auth.http_client(); + let block_number = client.block_number().await.unwrap(); + + // it should be zero, since this is an ephemeral test node + assert_eq!(block_number, U256::ZERO); + + // call the engine_forkchoiceUpdated function with payload attributes + let forkchoice_state = ForkchoiceState { + head_block_hash: genesis_hash, + safe_block_hash: genesis_hash, + finalized_block_hash: genesis_hash, + }; + + let payload_attributes = OptimismPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 1, + prev_randao: Default::default(), + suggested_fee_recipient: Default::default(), + // canyon is _not_ in the chain spec, so this should cause the engine call to fail + withdrawals: Some(vec![]), + parent_beacon_block_root: None, + }, + no_tx_pool: None, + gas_limit: Some(1), + transactions: None, + }; + + // call the engine_forkchoiceUpdated function with payload attributes + let res = >::fork_choice_updated_v2( + &client, + forkchoice_state, + Some(payload_attributes), + ) + .await; + let err = res.expect_err("pre-canyon engine call with withdrawals should fail"); + assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE); + } + + #[cfg(feature = "optimism")] + #[tokio::test] + async fn optimism_post_canyon_no_withdrawals_invalid() { + reth_tracing::init_test_tracing(); + use alloy_chains::Chain; + use assert_matches::assert_matches; + use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE}; + use reth_primitives::{ChainSpec, Genesis}; + use reth_rpc_api::EngineApiClient; + use reth_rpc_types::engine::{ + ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, + }; + + // this launches a test node with http + let rpc_args = RpcServerArgs::default().with_http(); + + // create optimism genesis with canyon at block 2 + let spec = ChainSpec::builder() + .chain(Chain::optimism_mainnet()) + .genesis(Genesis::default()) + .canyon_activated() + .build(); + + let genesis_hash = spec.genesis_hash(); + + // create node config + let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); + + let (handle, _manager) = spawn_node(node_config).await.unwrap(); + + // call a function on the node + let client = handle.rpc_server_handles().auth.http_client(); + let block_number = client.block_number().await.unwrap(); + + // it should be zero, since this is an ephemeral test node + assert_eq!(block_number, U256::ZERO); + + // call the engine_forkchoiceUpdated function with payload attributes + let forkchoice_state = ForkchoiceState { + head_block_hash: genesis_hash, + safe_block_hash: genesis_hash, + finalized_block_hash: genesis_hash, + }; + + let payload_attributes = OptimismPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 1, + prev_randao: Default::default(), + suggested_fee_recipient: Default::default(), + // canyon is _not_ in the chain spec, so this should cause the engine call to fail + withdrawals: None, + parent_beacon_block_root: None, + }, + no_tx_pool: None, + gas_limit: Some(1), + transactions: None, + }; + + // call the engine_forkchoiceUpdated function with payload attributes + let res = >::fork_choice_updated_v2( + &client, + forkchoice_state, + Some(payload_attributes), + ) + .await; + let err = res.expect_err("post-canyon engine call with no withdrawals should fail"); + assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE); + } + + #[cfg(feature = "optimism")] + #[tokio::test] + async fn optimism_post_canyon_withdrawals_valid() { + reth_tracing::init_test_tracing(); + use alloy_chains::Chain; + use jsonrpsee::http_client::HttpClient; + use reth_primitives::{ChainSpec, Genesis}; + use reth_rpc_api::EngineApiClient; + use reth_rpc_types::engine::{ + ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, + }; + + // this launches a test node with http + let rpc_args = RpcServerArgs::default().with_http(); + + // create optimism genesis with canyon at block 2 + let spec = ChainSpec::builder() + .chain(Chain::optimism_mainnet()) + .genesis(Genesis::default()) + .canyon_activated() + .build(); + + let genesis_hash = spec.genesis_hash(); + + // create node config + let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); + + let (handle, _manager) = spawn_node(node_config).await.unwrap(); + + // call a function on the node + let client = handle.rpc_server_handles().auth.http_client(); + let block_number = client.block_number().await.unwrap(); + + // it should be zero, since this is an ephemeral test node + assert_eq!(block_number, U256::ZERO); + + // call the engine_forkchoiceUpdated function with payload attributes + let forkchoice_state = ForkchoiceState { + head_block_hash: genesis_hash, + safe_block_hash: genesis_hash, + finalized_block_hash: genesis_hash, + }; + + let payload_attributes = OptimismPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 1, + prev_randao: Default::default(), + suggested_fee_recipient: Default::default(), + // canyon is _not_ in the chain spec, so this should cause the engine call to fail + withdrawals: Some(vec![]), + parent_beacon_block_root: None, + }, + no_tx_pool: None, + gas_limit: Some(1), + transactions: None, + }; + + // call the engine_forkchoiceUpdated function with payload attributes + let res = >::fork_choice_updated_v2( + &client, + forkchoice_state, + Some(payload_attributes), + ) + .await; + res.expect("post-canyon engine call with withdrawals should succeed"); + } +} diff --git a/crates/node-core/src/engine_api_store.rs b/bin/reth/src/commands/debug_cmd/engine_api_store.rs similarity index 100% rename from crates/node-core/src/engine_api_store.rs rename to bin/reth/src/commands/debug_cmd/engine_api_store.rs diff --git a/bin/reth/src/commands/debug_cmd/mod.rs b/bin/reth/src/commands/debug_cmd/mod.rs index 688b4d693dc1..310b5fe55ea1 100644 --- a/bin/reth/src/commands/debug_cmd/mod.rs +++ b/bin/reth/src/commands/debug_cmd/mod.rs @@ -5,6 +5,7 @@ use clap::{Parser, Subcommand}; use crate::runner::CliContext; mod build_block; +pub mod engine_api_store; mod execution; mod in_memory_merkle; mod merkle; diff --git a/bin/reth/src/commands/debug_cmd/replay_engine.rs b/bin/reth/src/commands/debug_cmd/replay_engine.rs index 4a5d9978ba89..aaf522fef2fb 100644 --- a/bin/reth/src/commands/debug_cmd/replay_engine.rs +++ b/bin/reth/src/commands/debug_cmd/replay_engine.rs @@ -4,7 +4,7 @@ use crate::{ utils::{chain_help, genesis_value_parser, SUPPORTED_CHAINS}, DatabaseArgs, NetworkArgs, }, - core::engine_api_store::{EngineApiStore, StoredEngineApiMessage}, + commands::debug_cmd::engine_api_store::{EngineApiStore, StoredEngineApiMessage}, dirs::{DataDirPath, MaybePlatformPath}, runner::CliContext, }; diff --git a/crates/node-core/src/cl_events.rs b/bin/reth/src/commands/node/cl_events.rs similarity index 100% rename from crates/node-core/src/cl_events.rs rename to bin/reth/src/commands/node/cl_events.rs diff --git a/crates/node-core/src/events.rs b/bin/reth/src/commands/node/events.rs similarity index 99% rename from crates/node-core/src/events.rs rename to bin/reth/src/commands/node/events.rs index 4a8048700f89..4aaea44efe17 100644 --- a/crates/node-core/src/events.rs +++ b/bin/reth/src/commands/node/events.rs @@ -1,6 +1,6 @@ //! Support for handling events emitted by node components. -use crate::cl_events::ConsensusLayerHealthEvent; +use crate::commands::node::cl_events::ConsensusLayerHealthEvent; use futures::Stream; use reth_beacon_consensus::BeaconConsensusEngineEvent; use reth_db::{database::Database, database_metrics::DatabaseMetadata}; @@ -466,7 +466,7 @@ impl Display for Eta { #[cfg(test)] mod tests { - use crate::events::Eta; + use super::*; use std::time::{Duration, Instant}; #[test] diff --git a/bin/reth/src/commands/node/mod.rs b/bin/reth/src/commands/node/mod.rs index cb616090bf4c..5efa599df688 100644 --- a/bin/reth/src/commands/node/mod.rs +++ b/bin/reth/src/commands/node/mod.rs @@ -9,15 +9,8 @@ use reth_interfaces::consensus::Consensus; use reth_primitives::ChainSpec; use std::{net::SocketAddr, path::PathBuf, sync::Arc}; -/// Re-export from `reth_node_core` for backwards compatibility. -pub mod events { - pub use crate::core::events::*; -} - -/// Re-export from `reth_node_core` for backwards compatibility. -pub mod cl_events { - pub use crate::core::cl_events::*; -} +pub mod cl_events; +pub mod events; use crate::{ args::{ @@ -25,7 +18,7 @@ use crate::{ DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, }, - builder::NodeConfig, + builder::{launch_from_config, NodeConfig}, cli::{db_type::DatabaseBuilder, ext::RethCliExt}, dirs::{DataDirPath, MaybePlatformPath}, runner::CliContext, @@ -222,7 +215,8 @@ impl NodeCommand { let executor = ctx.task_executor; // launch the node - let handle = node_config.launch::(ext, executor).await?; + let handle = launch_from_config::(node_config, ext, executor).await?; + handle.wait_for_node_exit().await } diff --git a/crates/node-core/src/init.rs b/bin/reth/src/init.rs similarity index 100% rename from crates/node-core/src/init.rs rename to bin/reth/src/init.rs diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 3c2ea7f2e247..c13bd3970b3e 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -26,8 +26,10 @@ )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +pub mod builder; pub mod cli; pub mod commands; +pub mod init; pub mod runner; pub mod utils; @@ -42,11 +44,6 @@ pub mod core { pub use reth_node_core::*; } -/// Re-exported from `reth_node_core`. -pub mod builder { - pub use reth_node_core::node_config::*; -} - /// Re-exported from `reth_node_core`. pub mod prometheus_exporter { pub use reth_node_core::prometheus_exporter::*; @@ -67,12 +64,6 @@ pub mod version { pub use reth_node_core::version::*; } -/// Re-exported from `reth_node_core`, also to prevent a breaking change. See the comment on -/// the `reth_node_core::args` re-export for more details. -pub mod init { - pub use reth_node_core::init::*; -} - /// Re-exported from `reth_node_core`, also to prevent a breaking change. See the comment on /// the `reth_node_core::args` re-export for more details. pub mod dirs { diff --git a/crates/node-core/Cargo.toml b/crates/node-core/Cargo.toml index 00e34c59911d..e7479c58f881 100644 --- a/crates/node-core/Cargo.toml +++ b/crates/node-core/Cargo.toml @@ -39,14 +39,12 @@ reth-consensus-common.workspace = true reth-auto-seal-consensus.workspace = true reth-beacon-consensus.workspace = true reth-downloaders.workspace = true -reth-node-builder.workspace = true reth-revm.workspace = true reth-stages.workspace = true reth-prune.workspace = true reth-blockchain-tree.workspace = true revm-inspectors.workspace = true reth-snapshot.workspace = true -reth-ethereum-payload-builder.workspace = true reth-optimism-payload-builder = { workspace = true, optional = true } # async @@ -67,13 +65,11 @@ humantime = "2.1.0" thiserror.workspace = true const-str = "0.5.6" rand.workspace = true -fdlimit = "0.3.0" pin-project.workspace = true # io dirs-next = "2.0.0" shellexpand = "3.0.0" -confy.workspace = true serde.workspace = true serde_json.workspace = true @@ -92,7 +88,6 @@ secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recov futures.workspace = true [target.'cfg(not(windows))'.dependencies] -jemallocator = { version = "0.5.0", optional = true } jemalloc-ctl = { version = "0.5.0", optional = true } [target.'cfg(target_os = "linux")'.dependencies] @@ -116,7 +111,6 @@ optimism = [ "reth-network/optimism", "reth-network-api/optimism", "reth-payload-builder/optimism", - "reth-node-builder/optimism", "reth-rpc-types/optimism", "reth-rpc-types-compat/optimism", "reth-auto-seal-consensus/optimism", @@ -124,11 +118,10 @@ optimism = [ "reth-blockchain-tree/optimism", "reth-beacon-consensus/optimism", "reth-optimism-payload-builder/optimism", - "reth-ethereum-payload-builder/optimism", "reth-node-api/optimism", ] -jemalloc = ["dep:jemallocator", "dep:jemalloc-ctl"] +jemalloc = ["dep:jemalloc-ctl"] [build-dependencies] vergen = { version = "8.0.0", features = ["build", "cargo", "git", "git2"] } diff --git a/crates/node-core/src/lib.rs b/crates/node-core/src/lib.rs index 8d79b3411ea7..093a5b9934e2 100644 --- a/crates/node-core/src/lib.rs +++ b/crates/node-core/src/lib.rs @@ -8,12 +8,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod args; -pub mod cl_events; pub mod cli; pub mod dirs; -pub mod engine_api_store; -pub mod events; -pub mod init; pub mod metrics; pub mod node_config; pub mod utils; diff --git a/crates/node-core/src/node_config.rs b/crates/node-core/src/node_config.rs index 9956624b7642..d368cd531957 100644 --- a/crates/node-core/src/node_config.rs +++ b/crates/node-core/src/node_config.rs @@ -5,44 +5,23 @@ use crate::{ get_secret_key, DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, }, - cl_events::ConsensusLayerHealthEvents, - cli::{ - components::{RethNodeComponentsImpl, RethRpcServerHandles}, - config::{RethRpcConfig, RethTransactionPoolConfig}, - db_type::{DatabaseBuilder, DatabaseInstance}, - ext::{DefaultRethNodeCommandConfig, RethCliExt, RethNodeCommandConfig}, - }, + cli::{config::RethTransactionPoolConfig, db_type::DatabaseBuilder}, dirs::{ChainPath, DataDirPath, MaybePlatformPath}, - engine_api_store::EngineApiStore, - events, - init::init_genesis, metrics::prometheus_exporter, utils::{get_single_header, write_peers_to_file}, - version::SHORT_VERSION, }; -use core::future::Future; -use eyre::WrapErr; -use fdlimit::raise_fd_limit; -use futures::{future::Either, stream, stream_select, FutureExt, StreamExt}; use metrics_exporter_prometheus::PrometheusHandle; use once_cell::sync::Lazy; -use reth_auto_seal_consensus::{AutoSealBuilder, AutoSealConsensus, MiningMode}; -use reth_beacon_consensus::{ - hooks::{EngineHooks, PruneHook}, - BeaconConsensus, BeaconConsensusEngine, BeaconConsensusEngineError, - MIN_BLOCKS_FOR_PIPELINE_RUN, -}; +use reth_auto_seal_consensus::{AutoSealConsensus, MiningMode}; +use reth_beacon_consensus::BeaconConsensus; use reth_blockchain_tree::{ - config::BlockchainTreeConfig, externals::TreeExternals, BlockchainTree, ShareableBlockchainTree, + config::BlockchainTreeConfig, externals::TreeExternals, BlockchainTree, }; use reth_config::{ config::{PruneConfig, StageConfig}, Config, }; -use reth_db::{ - database::Database, - database_metrics::{DatabaseMetadata, DatabaseMetrics}, -}; +use reth_db::{database::Database, database_metrics::DatabaseMetrics}; use reth_downloaders::{ bodies::bodies::BodiesDownloaderBuilder, headers::reverse_headers::ReverseHeadersDownloaderBuilder, @@ -52,35 +31,23 @@ use reth_interfaces::{ consensus::Consensus, p2p::{ bodies::{client::BodiesClient, downloader::BodyDownloader}, - either::EitherDownloader, headers::{client::HeadersClient, downloader::HeaderDownloader}, }, RethResult, }; -use reth_network::{NetworkBuilder, NetworkConfig, NetworkEvents, NetworkHandle, NetworkManager}; -use reth_network_api::{NetworkInfo, PeersInfo}; -#[cfg(not(feature = "optimism"))] -use reth_node_builder::EthEngineTypes; -#[cfg(feature = "optimism")] -use reth_node_builder::OptimismEngineTypes; -use std::task::ready; - -use reth_payload_builder::PayloadBuilderHandle; +use reth_network::{NetworkBuilder, NetworkConfig, NetworkHandle, NetworkManager}; use reth_primitives::{ constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP}, kzg::KzgSettings, stage::StageId, - BlockHashOrNumber, BlockNumber, ChainSpec, DisplayHardforks, Head, SealedHeader, TxHash, B256, - MAINNET, + BlockHashOrNumber, BlockNumber, ChainSpec, Head, SealedHeader, TxHash, B256, MAINNET, }; use reth_provider::{ providers::BlockchainProvider, BlockHashReader, BlockReader, BlockchainTreePendingStateProvider, CanonStateSubscriptions, HeaderProvider, HeaderSyncMode, ProviderFactory, StageCheckpointReader, }; -use reth_prune::PrunerBuilder; use reth_revm::EvmProcessorFactory; -use reth_rpc_engine_api::EngineApi; use reth_stages::{ prelude::*, stages::{ @@ -90,7 +57,7 @@ use reth_stages::{ }, MetricEvent, }; -use reth_tasks::{TaskExecutor, TaskManager}; +use reth_tasks::TaskExecutor; use reth_transaction_pool::{ blobstore::DiskFileBlobStore, EthTransactionPool, TransactionPool, TransactionValidationTaskExecutor, @@ -100,13 +67,11 @@ use secp256k1::SecretKey; use std::{ net::{SocketAddr, SocketAddrV4}, path::PathBuf, - pin::Pin, sync::Arc, - task::{Context, Poll}, }; use tokio::sync::{ - mpsc::{unbounded_channel, Receiver, UnboundedSender}, - oneshot, watch, + mpsc::{Receiver, UnboundedSender}, + watch, }; use tracing::*; @@ -123,9 +88,6 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// # use reth_tasks::{TaskManager, TaskSpawner}; /// # use reth_node_core::{ /// # node_config::NodeConfig, -/// # cli::{ -/// # ext::DefaultRethNodeCommandConfig, -/// # }, /// # args::RpcServerArgs, /// # }; /// # use reth_rpc_builder::RpcModuleSelection; @@ -143,9 +105,6 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// let mut rpc = RpcServerArgs::default().with_http().with_ws(); /// rpc.http_api = Some(RpcModuleSelection::All); /// let builder = builder.with_rpc(rpc); -/// -/// let ext = DefaultRethNodeCommandConfig::default(); -/// let handle = builder.launch::<()>(ext, executor).await.unwrap(); /// } /// ``` /// @@ -157,9 +116,6 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// # use reth_tasks::{TaskManager, TaskSpawner}; /// # use reth_node_core::{ /// # node_config::NodeConfig, -/// # cli::{ -/// # ext::DefaultRethNodeCommandConfig, -/// # }, /// # args::RpcServerArgs, /// # }; /// # use reth_rpc_builder::RpcModuleSelection; @@ -177,9 +133,6 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy = /// let mut rpc = RpcServerArgs::default().with_http().with_ws(); /// rpc.http_api = Some(RpcModuleSelection::All); /// let builder = builder.with_rpc(rpc); -/// -/// let ext = DefaultRethNodeCommandConfig::default(); -/// let handle = builder.launch::<()>(ext, executor).await.unwrap(); /// } /// ``` #[derive(Debug)] @@ -365,48 +318,6 @@ impl NodeConfig { self } - /// Launches the node, also adding any RPC extensions passed. - /// - /// # Example - /// ```rust - /// # use reth_tasks::{TaskManager, TaskSpawner}; - /// # use reth_node_core::node_config::NodeConfig; - /// # use reth_node_core::cli::{ - /// # ext::DefaultRethNodeCommandConfig, - /// # }; - /// # use tokio::runtime::Handle; - /// - /// async fn t() { - /// let handle = Handle::current(); - /// let manager = TaskManager::new(handle); - /// let executor = manager.executor(); - /// let builder = NodeConfig::default(); - /// let ext = DefaultRethNodeCommandConfig::default(); - /// let handle = builder.launch::<()>(ext, executor).await.unwrap(); - /// } - /// ``` - pub async fn launch( - mut self, - ext: E::Node, - executor: TaskExecutor, - ) -> eyre::Result { - info!(target: "reth::cli", "reth {} starting", SHORT_VERSION); - - let database = std::mem::take(&mut self.database); - let db_instance = database.init_db(self.db.log_level, self.chain.chain)?; - - match db_instance { - DatabaseInstance::Real { db, data_dir } => { - let builder = NodeBuilderWithDatabase { config: self, db, data_dir }; - builder.launch::(ext, executor).await - } - DatabaseInstance::Test { db, data_dir } => { - let builder = NodeBuilderWithDatabase { config: self, db, data_dir }; - builder.launch::(ext, executor).await - } - } - } - /// Get the network secret from the given data dir pub fn network_secret(&self, data_dir: &ChainPath) -> eyre::Result { let network_secret_path = @@ -439,7 +350,7 @@ impl NodeConfig { /// Returns the max block that the node should run to, looking it up from the network if /// necessary - async fn max_block( + pub async fn max_block( &self, network_client: &Client, provider: Provider, @@ -606,7 +517,7 @@ impl NodeConfig { /// Constructs a [Pipeline] that's wired to the network #[allow(clippy::too_many_arguments)] - async fn build_networked_pipeline( + pub async fn build_networked_pipeline( &self, config: &StageConfig, client: Client, @@ -649,7 +560,7 @@ impl NodeConfig { /// Loads the trusted setup params from a given file path or falls back to /// `MAINNET_KZG_TRUSTED_SETUP`. - fn kzg_settings(&self) -> eyre::Result> { + pub fn kzg_settings(&self) -> eyre::Result> { if let Some(ref trusted_setup_file) = self.trusted_setup_file { let trusted_setup = KzgSettings::load_trusted_setup_file(trusted_setup_file) .map_err(LoadKzgSettingsError::KzgError)?; @@ -659,11 +570,13 @@ impl NodeConfig { } } - fn install_prometheus_recorder(&self) -> eyre::Result { + /// Installs the prometheus recorder. + pub fn install_prometheus_recorder(&self) -> eyre::Result { Ok(PROMETHEUS_RECORDER_HANDLE.clone()) } - async fn start_metrics_endpoint( + /// Serves the prometheus endpoint over HTTP with the given database and prometheus handle. + pub async fn start_metrics_endpoint( &self, prometheus_handle: PrometheusHandle, db: Metrics, @@ -687,7 +600,7 @@ impl NodeConfig { /// Spawns the configured network and associated tasks and returns the [NetworkHandle] connected /// to that network. - fn start_network( + pub fn start_network( &self, builder: NetworkBuilder, task_executor: &TaskExecutor, @@ -722,7 +635,7 @@ impl NodeConfig { /// Fetches the head block from the database. /// /// If the database is empty, returns the genesis block. - fn lookup_head(&self, factory: ProviderFactory) -> RethResult { + pub fn lookup_head(&self, factory: ProviderFactory) -> RethResult { let provider = factory.provider()?; let head = provider.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default().block_number; @@ -752,7 +665,7 @@ impl NodeConfig { /// If it doesn't exist, download the header and return the block number. /// /// NOTE: The download is attempted with infinite retries. - async fn lookup_or_fetch_tip( + pub async fn lookup_or_fetch_tip( &self, provider: Provider, client: Client, @@ -776,7 +689,7 @@ impl NodeConfig { /// Attempt to look up the block with the given number and return the header. /// /// NOTE: The download is attempted with infinite retries. - async fn fetch_tip_from_network( + pub async fn fetch_tip_from_network( &self, client: Client, tip: BlockHashOrNumber, @@ -798,7 +711,8 @@ impl NodeConfig { } } - fn load_network_config( + /// Builds the [NetworkConfig] with the given [ProviderFactory]. + pub fn load_network_config( &self, config: &Config, provider_factory: ProviderFactory, @@ -834,8 +748,9 @@ impl NodeConfig { cfg_builder.build(provider_factory) } + /// Builds the [Pipeline] with the given [ProviderFactory] and downloaders. #[allow(clippy::too_many_arguments)] - async fn build_pipeline( + pub async fn build_pipeline( &self, provider_factory: ProviderFactory, stage_config: &StageConfig, @@ -948,13 +863,13 @@ impl NodeConfig { /// Change rpc port numbers based on the instance number, using the inner /// [RpcServerArgs::adjust_instance_ports] method. - fn adjust_instance_ports(&mut self) { + pub fn adjust_instance_ports(&mut self) { self.rpc.adjust_instance_ports(self.instance); } /// Sets networking and RPC ports to zero, causing the OS to choose random unused ports when /// sockets are bound. - fn with_unused_ports(mut self) -> Self { + pub fn with_unused_ports(mut self) -> Self { self.rpc = self.rpc.with_unused_ports(); self.network = self.network.with_unused_ports(); self @@ -983,808 +898,3 @@ impl Default for NodeConfig { } } } - -/// A version of the [NodeConfig] that has an installed database. This is used to construct the -/// [NodeHandle]. -/// -/// This also contains a path to a data dir that cannot be changed. -#[derive(Debug)] -pub struct NodeBuilderWithDatabase { - /// The node config - pub config: NodeConfig, - /// The database - pub db: Arc, - /// The data dir - pub data_dir: ChainPath, -} - -impl NodeBuilderWithDatabase { - /// Launch the node with the given extensions and executor - pub async fn launch( - mut self, - mut ext: E::Node, - executor: TaskExecutor, - ) -> eyre::Result { - // Raise the fd limit of the process. - // Does not do anything on windows. - raise_fd_limit()?; - - // get config - let config = self.load_config()?; - - let prometheus_handle = self.config.install_prometheus_recorder()?; - info!(target: "reth::cli", "Database opened"); - - let mut provider_factory = - ProviderFactory::new(Arc::clone(&self.db), Arc::clone(&self.config.chain)); - - // configure snapshotter - let snapshotter = reth_snapshot::Snapshotter::new( - provider_factory.clone(), - self.data_dir.snapshots_path(), - self.config.chain.snapshot_block_interval, - )?; - - provider_factory = provider_factory.with_snapshots( - self.data_dir.snapshots_path(), - snapshotter.highest_snapshot_receiver(), - )?; - - self.config.start_metrics_endpoint(prometheus_handle, Arc::clone(&self.db)).await?; - - debug!(target: "reth::cli", chain=%self.config.chain.chain, genesis=?self.config.chain.genesis_hash(), "Initializing genesis"); - - let genesis_hash = init_genesis(Arc::clone(&self.db), self.config.chain.clone())?; - - info!(target: "reth::cli", "{}", DisplayHardforks::new(self.config.chain.hardforks())); - - let consensus = self.config.consensus(); - - debug!(target: "reth::cli", "Spawning stages metrics listener task"); - let (sync_metrics_tx, sync_metrics_rx) = unbounded_channel(); - let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx); - executor.spawn_critical("stages metrics listener task", sync_metrics_listener); - - let prune_config = self - .config - .pruning - .prune_config(Arc::clone(&self.config.chain))? - .or(config.prune.clone()); - - // configure blockchain tree - let tree_config = BlockchainTreeConfig::default(); - let tree = self.config.build_blockchain_tree( - provider_factory.clone(), - consensus.clone(), - prune_config.clone(), - sync_metrics_tx.clone(), - tree_config, - )?; - let canon_state_notification_sender = tree.canon_state_notification_sender(); - let blockchain_tree = ShareableBlockchainTree::new(tree); - debug!(target: "reth::cli", "configured blockchain tree"); - - // fetch the head block from the database - let head = self - .config - .lookup_head(provider_factory.clone()) - .wrap_err("the head block is missing")?; - - // setup the blockchain provider - let blockchain_db = - BlockchainProvider::new(provider_factory.clone(), blockchain_tree.clone())?; - - // build transaction pool - let transaction_pool = - self.config.build_and_spawn_txpool(&blockchain_db, head, &executor, &self.data_dir)?; - - // build network - let (network_client, mut network_builder) = self - .config - .build_network( - &config, - provider_factory.clone(), - executor.clone(), - head, - &self.data_dir, - ) - .await?; - - let components = RethNodeComponentsImpl::new( - blockchain_db.clone(), - transaction_pool.clone(), - network_builder.handle(), - executor.clone(), - blockchain_db.clone(), - ); - - // allow network modifications - ext.configure_network(network_builder.network_mut(), &components)?; - - // launch network - let network = self.config.start_network( - network_builder, - &executor, - transaction_pool.clone(), - network_client, - &self.data_dir, - ); - - info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), enode = %network.local_node_record(), "Connected to P2P network"); - debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID"); - let network_client = network.fetch_client().await?; - - ext.on_components_initialized(&components)?; - - debug!(target: "reth::cli", "Spawning payload builder service"); - - // TODO: stateful node builder should handle this in with_payload_builder - // Optimism's payload builder is implemented on the OptimismPayloadBuilder type. - #[cfg(feature = "optimism")] - let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() - .set_compute_pending_block(self.config.builder.compute_pending_block); - - #[cfg(feature = "optimism")] - let payload_builder: PayloadBuilderHandle = - ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?; - - // The default payload builder is implemented on the unit type. - #[cfg(not(feature = "optimism"))] - let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default(); - - #[cfg(not(feature = "optimism"))] - let payload_builder: PayloadBuilderHandle = - ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?; - - let (consensus_engine_tx, mut consensus_engine_rx) = unbounded_channel(); - if let Some(store_path) = self.config.debug.engine_api_store.clone() { - let (engine_intercept_tx, engine_intercept_rx) = unbounded_channel(); - let engine_api_store = EngineApiStore::new(store_path); - executor.spawn_critical( - "engine api interceptor", - engine_api_store.intercept(consensus_engine_rx, engine_intercept_tx), - ); - consensus_engine_rx = engine_intercept_rx; - }; - let max_block = self.config.max_block(&network_client, provider_factory.clone()).await?; - - // Configure the pipeline - let (mut pipeline, client) = if self.config.dev.dev { - info!(target: "reth::cli", "Starting Reth in dev mode"); - let mining_mode = - self.config.mining_mode(transaction_pool.pending_transactions_listener()); - - let (_, client, mut task) = AutoSealBuilder::new( - Arc::clone(&self.config.chain), - blockchain_db.clone(), - transaction_pool.clone(), - consensus_engine_tx.clone(), - canon_state_notification_sender, - mining_mode, - ) - .build(); - - let mut pipeline = self - .config - .build_networked_pipeline( - &config.stages, - client.clone(), - Arc::clone(&consensus), - provider_factory.clone(), - &executor, - sync_metrics_tx, - prune_config.clone(), - max_block, - ) - .await?; - - let pipeline_events = pipeline.events(); - task.set_pipeline_events(pipeline_events); - debug!(target: "reth::cli", "Spawning auto mine task"); - executor.spawn(Box::pin(task)); - - (pipeline, EitherDownloader::Left(client)) - } else { - let pipeline = self - .config - .build_networked_pipeline( - &config.stages, - network_client.clone(), - Arc::clone(&consensus), - provider_factory.clone(), - &executor.clone(), - sync_metrics_tx, - prune_config.clone(), - max_block, - ) - .await?; - - (pipeline, EitherDownloader::Right(network_client)) - }; - - let pipeline_events = pipeline.events(); - - let initial_target = self.config.initial_pipeline_target(genesis_hash); - let mut hooks = EngineHooks::new(); - - let pruner_events = if let Some(prune_config) = prune_config { - let mut pruner = PrunerBuilder::new(prune_config.clone()) - .max_reorg_depth(tree_config.max_reorg_depth() as usize) - .prune_delete_limit(self.config.chain.prune_delete_limit) - .build(provider_factory, snapshotter.highest_snapshot_receiver()); - - let events = pruner.events(); - hooks.add(PruneHook::new(pruner, Box::new(executor.clone()))); - - info!(target: "reth::cli", ?prune_config, "Pruner initialized"); - Either::Left(events) - } else { - Either::Right(stream::empty()) - }; - - // Configure the consensus engine - let (beacon_consensus_engine, beacon_engine_handle) = BeaconConsensusEngine::with_channel( - client, - pipeline, - blockchain_db.clone(), - Box::new(executor.clone()), - Box::new(network.clone()), - max_block, - self.config.debug.continuous, - payload_builder.clone(), - initial_target, - MIN_BLOCKS_FOR_PIPELINE_RUN, - consensus_engine_tx, - consensus_engine_rx, - hooks, - )?; - info!(target: "reth::cli", "Consensus engine initialized"); - - let events = stream_select!( - network.event_listener().map(Into::into), - beacon_engine_handle.event_listener().map(Into::into), - pipeline_events.map(Into::into), - if self.config.debug.tip.is_none() { - Either::Left( - ConsensusLayerHealthEvents::new(Box::new(blockchain_db.clone())) - .map(Into::into), - ) - } else { - Either::Right(stream::empty()) - }, - pruner_events.map(Into::into) - ); - executor.spawn_critical( - "events task", - events::handle_events( - Some(network.clone()), - Some(head.number), - events, - self.db.clone(), - ), - ); - - let engine_api = EngineApi::new( - blockchain_db.clone(), - self.config.chain.clone(), - beacon_engine_handle, - payload_builder.into(), - Box::new(executor.clone()), - ); - info!(target: "reth::cli", "Engine API handler initialized"); - - // extract the jwt secret from the args if possible - let default_jwt_path = self.data_dir.jwt_path(); - let jwt_secret = self.config.rpc.auth_jwt_secret(default_jwt_path)?; - - // adjust rpc port numbers based on instance number - self.config.adjust_instance_ports(); - - // Start RPC servers - let rpc_server_handles = - self.config.rpc.start_servers(&components, engine_api, jwt_secret, &mut ext).await?; - - // Run consensus engine to completion - let (tx, rx) = oneshot::channel(); - info!(target: "reth::cli", "Starting consensus engine"); - executor.spawn_critical_blocking("consensus engine", async move { - let res = beacon_consensus_engine.await; - let _ = tx.send(res); - }); - - ext.on_node_started(&components)?; - - // If `enable_genesis_walkback` is set to true, the rollup client will need to - // perform the derivation pipeline from genesis, validating the data dir. - // When set to false, set the finalized, safe, and unsafe head block hashes - // on the rollup client using a fork choice update. This prevents the rollup - // client from performing the derivation pipeline from genesis, and instead - // starts syncing from the current tip in the DB. - #[cfg(feature = "optimism")] - if self.config.chain.is_optimism() && !self.config.rollup.enable_genesis_walkback { - let client = rpc_server_handles.auth.http_client(); - reth_rpc_api::EngineApiClient::::fork_choice_updated_v2( - &client, - reth_rpc_types::engine::ForkchoiceState { - head_block_hash: head.hash, - safe_block_hash: head.hash, - finalized_block_hash: head.hash, - }, - None, - ) - .await?; - } - - // wait for node exit future - let node_exit_future = NodeExitFuture::new(rx, self.config.debug.terminate); - - // construct node handle and return - let node_handle = NodeHandle { rpc_server_handles, node_exit_future }; - Ok(node_handle) - } - - /// Returns the path to the config file. - fn config_path(&self) -> PathBuf { - self.config.config.clone().unwrap_or_else(|| self.data_dir.config_path()) - } - - /// Loads the reth config with the given datadir root - fn load_config(&self) -> eyre::Result { - let config_path = self.config_path(); - - let mut config = confy::load_path::(&config_path) - .wrap_err_with(|| format!("Could not load config file {:?}", config_path))?; - - info!(target: "reth::cli", path = ?config_path, "Configuration loaded"); - - // Update the config with the command line arguments - config.peers.connect_trusted_nodes_only = self.config.network.trusted_only; - - if !self.config.network.trusted_peers.is_empty() { - info!(target: "reth::cli", "Adding trusted nodes"); - self.config.network.trusted_peers.iter().for_each(|peer| { - config.peers.trusted_nodes.insert(*peer); - }); - } - - Ok(config) - } -} - -/// The [NodeHandle] contains the [RethRpcServerHandles] returned by the reth initialization -/// process, as well as a method for waiting for the node exit. -#[derive(Debug)] -pub struct NodeHandle { - /// The handles to the RPC servers - rpc_server_handles: RethRpcServerHandles, - - /// A Future which waits node exit - /// See [`NodeExitFuture`] - node_exit_future: NodeExitFuture, -} - -impl NodeHandle { - /// Returns the [RethRpcServerHandles] for this node. - pub fn rpc_server_handles(&self) -> &RethRpcServerHandles { - &self.rpc_server_handles - } - - /// Waits for the node to exit. Uses [`NodeExitFuture`] - pub async fn wait_for_node_exit(self) -> eyre::Result<()> { - self.node_exit_future.await - } -} - -/// A Future which resolves when the node exits -#[derive(Debug)] -pub struct NodeExitFuture { - /// The receiver half of the channel for the consensus engine. - /// This can be used to wait for the consensus engine to exit. - consensus_engine_rx: Option>>, - - /// Flag indicating whether the node should be terminated after the pipeline sync. - terminate: bool, -} - -impl NodeExitFuture { - fn new( - consensus_engine_rx: oneshot::Receiver>, - terminate: bool, - ) -> Self { - Self { consensus_engine_rx: Some(consensus_engine_rx), terminate } - } -} - -impl Future for NodeExitFuture { - type Output = eyre::Result<()>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - if let Some(rx) = this.consensus_engine_rx.as_mut() { - match ready!(rx.poll_unpin(cx)) { - Ok(res) => { - this.consensus_engine_rx.take(); - res?; - if this.terminate { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - Err(err) => Poll::Ready(Err(err.into())), - } - } else { - Poll::Pending - } - } -} - -/// A simple function to launch a node with the specified [NodeConfig], spawning tasks on the -/// [TaskExecutor] constructed from [TaskManager::current]. -/// -/// # Example -/// ``` -/// # use reth_node_core::{ -/// # node_config::{NodeConfig, spawn_node}, -/// # args::RpcServerArgs, -/// # }; -/// async fn t() { -/// // Create a node builder with an http rpc server enabled -/// let rpc_args = RpcServerArgs::default().with_http(); -/// -/// let builder = NodeConfig::test().with_rpc(rpc_args); -/// -/// // Spawn the builder, returning a handle to the node -/// let (_handle, _manager) = spawn_node(builder).await.unwrap(); -/// } -/// ``` -pub async fn spawn_node(config: NodeConfig) -> eyre::Result<(NodeHandle, TaskManager)> { - let task_manager = TaskManager::current(); - let ext = DefaultRethNodeCommandConfig::default(); - Ok((config.launch::<()>(ext, task_manager.executor()).await?, task_manager)) -} - -#[cfg(test)] -mod tests { - use super::*; - use futures::future::poll_fn; - use reth_primitives::U256; - use reth_rpc_api::EthApiClient; - - #[tokio::test] - async fn block_number_node_config_test() { - // this launches a test node with http - let rpc_args = RpcServerArgs::default().with_http(); - - let (handle, _manager) = spawn_node(NodeConfig::test().with_rpc(rpc_args)).await.unwrap(); - - // call a function on the node - let client = handle.rpc_server_handles().rpc.http_client().unwrap(); - let block_number = client.block_number().await.unwrap(); - - // it should be zero, since this is an ephemeral test node - assert_eq!(block_number, U256::ZERO); - } - - #[tokio::test] - async fn rpc_handles_none_without_http() { - // this launches a test node _without_ http - let (handle, _manager) = spawn_node(NodeConfig::test()).await.unwrap(); - - // ensure that the `http_client` is none - let maybe_client = handle.rpc_server_handles().rpc.http_client(); - assert!(maybe_client.is_none()); - } - - #[tokio::test] - async fn launch_multiple_nodes() { - // spawn_test_node takes roughly 1 second per node, so this test takes ~4 seconds - let num_nodes = 4; - - // contains handles and managers - let mut handles = Vec::new(); - for _ in 0..num_nodes { - let handle = spawn_node(NodeConfig::test()).await.unwrap(); - handles.push(handle); - } - } - - #[tokio::test] - async fn test_node_exit_future_terminate_true() { - let (tx, rx) = oneshot::channel::>(); - - let _ = tx.send(Ok(())); - - let node_exit_future = NodeExitFuture::new(rx, true); - - let res = node_exit_future.await; - - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_node_exit_future_terminate_false() { - let (tx, rx) = oneshot::channel::>(); - - let _ = tx.send(Ok(())); - - let mut node_exit_future = NodeExitFuture::new(rx, false); - poll_fn(|cx| { - assert!(node_exit_future.poll_unpin(cx).is_pending()); - Poll::Ready(()) - }) - .await; - } - - #[cfg(feature = "optimism")] - #[tokio::test] - async fn optimism_pre_canyon_no_withdrawals_valid() { - reth_tracing::init_test_tracing(); - use alloy_chains::Chain; - use jsonrpsee::http_client::HttpClient; - use reth_primitives::Genesis; - use reth_rpc_api::EngineApiClient; - use reth_rpc_types::engine::{ - ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, - }; - - // this launches a test node with http - let rpc_args = RpcServerArgs::default().with_http(); - - // create optimism genesis with canyon at block 2 - let spec = ChainSpec::builder() - .chain(Chain::optimism_mainnet()) - .genesis(Genesis::default()) - .regolith_activated() - .build(); - - let genesis_hash = spec.genesis_hash(); - - // create node config - let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); - - let (handle, _manager) = spawn_node(node_config).await.unwrap(); - - // call a function on the node - let client = handle.rpc_server_handles().auth.http_client(); - let block_number = client.block_number().await.unwrap(); - - // it should be zero, since this is an ephemeral test node - assert_eq!(block_number, U256::ZERO); - - // call the engine_forkchoiceUpdated function with payload attributes - let forkchoice_state = ForkchoiceState { - head_block_hash: genesis_hash, - safe_block_hash: genesis_hash, - finalized_block_hash: genesis_hash, - }; - - let payload_attributes = OptimismPayloadAttributes { - payload_attributes: PayloadAttributes { - timestamp: 1, - prev_randao: Default::default(), - suggested_fee_recipient: Default::default(), - // canyon is _not_ in the chain spec, so this should cause the engine call to fail - withdrawals: None, - parent_beacon_block_root: None, - }, - no_tx_pool: None, - gas_limit: Some(1), - transactions: None, - }; - - // call the engine_forkchoiceUpdated function with payload attributes - let res = >::fork_choice_updated_v2( - &client, - forkchoice_state, - Some(payload_attributes), - ) - .await; - res.expect("pre-canyon engine call without withdrawals should succeed"); - } - - #[cfg(feature = "optimism")] - #[tokio::test] - async fn optimism_pre_canyon_withdrawals_invalid() { - reth_tracing::init_test_tracing(); - use alloy_chains::Chain; - use assert_matches::assert_matches; - use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE}; - use reth_primitives::Genesis; - use reth_rpc_api::EngineApiClient; - use reth_rpc_types::engine::{ - ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, - }; - - // this launches a test node with http - let rpc_args = RpcServerArgs::default().with_http(); - - // create optimism genesis with canyon at block 2 - let spec = ChainSpec::builder() - .chain(Chain::optimism_mainnet()) - .genesis(Genesis::default()) - .regolith_activated() - .build(); - - let genesis_hash = spec.genesis_hash(); - - // create node config - let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); - - let (handle, _manager) = spawn_node(node_config).await.unwrap(); - - // call a function on the node - let client = handle.rpc_server_handles().auth.http_client(); - let block_number = client.block_number().await.unwrap(); - - // it should be zero, since this is an ephemeral test node - assert_eq!(block_number, U256::ZERO); - - // call the engine_forkchoiceUpdated function with payload attributes - let forkchoice_state = ForkchoiceState { - head_block_hash: genesis_hash, - safe_block_hash: genesis_hash, - finalized_block_hash: genesis_hash, - }; - - let payload_attributes = OptimismPayloadAttributes { - payload_attributes: PayloadAttributes { - timestamp: 1, - prev_randao: Default::default(), - suggested_fee_recipient: Default::default(), - // canyon is _not_ in the chain spec, so this should cause the engine call to fail - withdrawals: Some(vec![]), - parent_beacon_block_root: None, - }, - no_tx_pool: None, - gas_limit: Some(1), - transactions: None, - }; - - // call the engine_forkchoiceUpdated function with payload attributes - let res = >::fork_choice_updated_v2( - &client, - forkchoice_state, - Some(payload_attributes), - ) - .await; - let err = res.expect_err("pre-canyon engine call with withdrawals should fail"); - assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE); - } - - #[cfg(feature = "optimism")] - #[tokio::test] - async fn optimism_post_canyon_no_withdrawals_invalid() { - reth_tracing::init_test_tracing(); - use alloy_chains::Chain; - use assert_matches::assert_matches; - use jsonrpsee::{core::Error, http_client::HttpClient, types::error::INVALID_PARAMS_CODE}; - use reth_primitives::Genesis; - use reth_rpc_api::EngineApiClient; - use reth_rpc_types::engine::{ - ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, - }; - - // this launches a test node with http - let rpc_args = RpcServerArgs::default().with_http(); - - // create optimism genesis with canyon at block 2 - let spec = ChainSpec::builder() - .chain(Chain::optimism_mainnet()) - .genesis(Genesis::default()) - .canyon_activated() - .build(); - - let genesis_hash = spec.genesis_hash(); - - // create node config - let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); - - let (handle, _manager) = spawn_node(node_config).await.unwrap(); - - // call a function on the node - let client = handle.rpc_server_handles().auth.http_client(); - let block_number = client.block_number().await.unwrap(); - - // it should be zero, since this is an ephemeral test node - assert_eq!(block_number, U256::ZERO); - - // call the engine_forkchoiceUpdated function with payload attributes - let forkchoice_state = ForkchoiceState { - head_block_hash: genesis_hash, - safe_block_hash: genesis_hash, - finalized_block_hash: genesis_hash, - }; - - let payload_attributes = OptimismPayloadAttributes { - payload_attributes: PayloadAttributes { - timestamp: 1, - prev_randao: Default::default(), - suggested_fee_recipient: Default::default(), - // canyon is _not_ in the chain spec, so this should cause the engine call to fail - withdrawals: None, - parent_beacon_block_root: None, - }, - no_tx_pool: None, - gas_limit: Some(1), - transactions: None, - }; - - // call the engine_forkchoiceUpdated function with payload attributes - let res = >::fork_choice_updated_v2( - &client, - forkchoice_state, - Some(payload_attributes), - ) - .await; - let err = res.expect_err("post-canyon engine call with no withdrawals should fail"); - assert_matches!(err, Error::Call(ref object) if object.code() == INVALID_PARAMS_CODE); - } - - #[cfg(feature = "optimism")] - #[tokio::test] - async fn optimism_post_canyon_withdrawals_valid() { - reth_tracing::init_test_tracing(); - use alloy_chains::Chain; - use jsonrpsee::http_client::HttpClient; - use reth_primitives::Genesis; - use reth_rpc_api::EngineApiClient; - use reth_rpc_types::engine::{ - ForkchoiceState, OptimismPayloadAttributes, PayloadAttributes, - }; - - // this launches a test node with http - let rpc_args = RpcServerArgs::default().with_http(); - - // create optimism genesis with canyon at block 2 - let spec = ChainSpec::builder() - .chain(Chain::optimism_mainnet()) - .genesis(Genesis::default()) - .canyon_activated() - .build(); - - let genesis_hash = spec.genesis_hash(); - - // create node config - let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec); - - let (handle, _manager) = spawn_node(node_config).await.unwrap(); - - // call a function on the node - let client = handle.rpc_server_handles().auth.http_client(); - let block_number = client.block_number().await.unwrap(); - - // it should be zero, since this is an ephemeral test node - assert_eq!(block_number, U256::ZERO); - - // call the engine_forkchoiceUpdated function with payload attributes - let forkchoice_state = ForkchoiceState { - head_block_hash: genesis_hash, - safe_block_hash: genesis_hash, - finalized_block_hash: genesis_hash, - }; - - let payload_attributes = OptimismPayloadAttributes { - payload_attributes: PayloadAttributes { - timestamp: 1, - prev_randao: Default::default(), - suggested_fee_recipient: Default::default(), - // canyon is _not_ in the chain spec, so this should cause the engine call to fail - withdrawals: Some(vec![]), - parent_beacon_block_root: None, - }, - no_tx_pool: None, - gas_limit: Some(1), - transactions: None, - }; - - // call the engine_forkchoiceUpdated function with payload attributes - let res = >::fork_choice_updated_v2( - &client, - forkchoice_state, - Some(payload_attributes), - ) - .await; - res.expect("post-canyon engine call with withdrawals should succeed"); - } -} From acccf1441e1c48939f1d2691f07667740ce921b7 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:40:12 -0500 Subject: [PATCH 07/33] feat: EvmEnvConfig trait (#6051) --- Cargo.lock | 10 + bin/reth/src/builder.rs | 17 +- .../src/commands/debug_cmd/build_block.rs | 14 +- bin/reth/src/commands/debug_cmd/execution.rs | 5 +- .../commands/debug_cmd/in_memory_merkle.rs | 4 +- bin/reth/src/commands/debug_cmd/merkle.rs | 5 +- .../src/commands/debug_cmd/replay_engine.rs | 12 +- bin/reth/src/commands/import.rs | 4 +- bin/reth/src/commands/stage/dump/execution.rs | 12 +- bin/reth/src/commands/stage/dump/merkle.rs | 3 +- bin/reth/src/commands/stage/run.rs | 7 +- crates/blockchain-tree/Cargo.toml | 1 + crates/blockchain-tree/src/blockchain_tree.rs | 4 +- crates/consensus/auto-seal/Cargo.toml | 1 + crates/consensus/auto-seal/src/lib.rs | 45 +++- crates/consensus/auto-seal/src/task.rs | 28 ++- .../consensus/beacon/src/engine/test_utils.rs | 5 +- crates/node-api/Cargo.toml | 1 + crates/node-api/src/evm/mod.rs | 35 +++ crates/node-api/src/lib.rs | 4 + crates/node-builder/src/engine.rs | 28 --- crates/node-builder/src/evm.rs | 70 ++++++ crates/node-builder/src/lib.rs | 10 +- crates/node-builder/src/optimism.rs | 72 ++++++ crates/node-core/src/args/rpc_server_args.rs | 14 +- crates/node-core/src/cli/components.rs | 36 ++- crates/node-core/src/node_config.rs | 20 +- crates/primitives/src/revm/env.rs | 101 ++------- crates/primitives/src/revm/mod.rs | 26 +-- crates/revm/Cargo.toml | 2 + crates/revm/src/factory.rs | 22 +- crates/revm/src/optimism/processor.rs | 6 +- crates/revm/src/processor.rs | 69 ++++-- crates/rpc/rpc-builder/Cargo.toml | 1 + crates/rpc/rpc-builder/src/auth.rs | 20 +- crates/rpc/rpc-builder/src/eth.rs | 4 +- crates/rpc/rpc-builder/src/lib.rs | 210 ++++++++++++------ crates/rpc/rpc-builder/tests/it/utils.rs | 4 +- crates/rpc/rpc/Cargo.toml | 4 +- crates/rpc/rpc/src/eth/api/block.rs | 9 +- crates/rpc/rpc/src/eth/api/call.rs | 4 +- crates/rpc/rpc/src/eth/api/fees.rs | 4 +- crates/rpc/rpc/src/eth/api/mod.rs | 36 ++- crates/rpc/rpc/src/eth/api/server.rs | 22 +- crates/rpc/rpc/src/eth/api/sign.rs | 2 +- crates/rpc/rpc/src/eth/api/state.rs | 12 +- crates/rpc/rpc/src/eth/api/transactions.rs | 18 +- crates/rpc/rpc/src/eth/cache/mod.rs | 38 +++- crates/rpc/rpc/src/eth/revm_utils.rs | 15 +- crates/stages/Cargo.toml | 1 + crates/stages/src/lib.rs | 3 +- crates/stages/src/sets.rs | 6 +- crates/stages/src/stages/execution.rs | 12 +- crates/stages/src/stages/mod.rs | 8 +- crates/storage/provider/Cargo.toml | 1 + .../provider/src/providers/database/mod.rs | 45 +++- .../src/providers/database/provider.rs | 56 +++-- crates/storage/provider/src/providers/mod.rs | 45 +++- .../storage/provider/src/test_utils/mock.rs | 37 ++- .../storage/provider/src/test_utils/noop.rs | 37 ++- crates/storage/provider/src/traits/evm_env.rs | 44 +++- testing/ef-tests/Cargo.toml | 1 + testing/ef-tests/src/cases/blockchain_test.rs | 2 + 63 files changed, 1003 insertions(+), 391 deletions(-) create mode 100644 crates/node-api/src/evm/mod.rs create mode 100644 crates/node-builder/src/evm.rs create mode 100644 crates/node-builder/src/optimism.rs diff --git a/Cargo.lock b/Cargo.lock index 2fbae246078b..012cb0c9d977 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2284,6 +2284,7 @@ dependencies = [ "alloy-rlp", "reth-db", "reth-interfaces", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-revm", @@ -5804,6 +5805,7 @@ dependencies = [ "reth-beacon-consensus", "reth-interfaces", "reth-node-api", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-revm", @@ -5896,6 +5898,7 @@ dependencies = [ "reth-db", "reth-interfaces", "reth-metrics", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-revm", @@ -6373,6 +6376,7 @@ dependencies = [ "reth-payload-builder", "reth-primitives", "reth-rpc-types", + "revm-primitives", "serde", "thiserror", ] @@ -6579,6 +6583,7 @@ dependencies = [ "reth-interfaces", "reth-metrics", "reth-nippy-jar", + "reth-node-api", "reth-primitives", "reth-trie", "revm", @@ -6618,6 +6623,8 @@ version = "0.1.0-alpha.16" dependencies = [ "reth-consensus-common", "reth-interfaces", + "reth-node-api", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-trie", @@ -6655,6 +6662,8 @@ dependencies = [ "reth-metrics", "reth-network", "reth-network-api", + "reth-node-api", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-revm", @@ -6853,6 +6862,7 @@ dependencies = [ "reth-eth-wire", "reth-interfaces", "reth-metrics", + "reth-node-builder", "reth-primitives", "reth-provider", "reth-revm", diff --git a/bin/reth/src/builder.rs b/bin/reth/src/builder.rs index 669f73617b94..d55c27465e7e 100644 --- a/bin/reth/src/builder.rs +++ b/bin/reth/src/builder.rs @@ -25,9 +25,9 @@ use reth_interfaces::p2p::either::EitherDownloader; use reth_network::NetworkEvents; use reth_network_api::{NetworkInfo, PeersInfo}; #[cfg(not(feature = "optimism"))] -use reth_node_builder::EthEngineTypes; +use reth_node_builder::{EthEngineTypes, EthEvmConfig}; #[cfg(feature = "optimism")] -use reth_node_builder::OptimismEngineTypes; +use reth_node_builder::{OptimismEngineTypes, OptimismEvmConfig}; use reth_node_core::{ cli::{ components::{RethNodeComponentsImpl, RethRpcServerHandles}, @@ -162,6 +162,14 @@ impl NodeBuilderWit .prune_config(Arc::clone(&self.config.chain))? .or(config.prune.clone()); + // TODO: stateful node builder should be able to remove cfgs here + #[cfg(feature = "optimism")] + let evm_config = OptimismEvmConfig::default(); + + // The default payload builder is implemented on the unit type. + #[cfg(not(feature = "optimism"))] + let evm_config = EthEvmConfig::default(); + // configure blockchain tree let tree_config = BlockchainTreeConfig::default(); let tree = self.config.build_blockchain_tree( @@ -170,6 +178,7 @@ impl NodeBuilderWit prune_config.clone(), sync_metrics_tx.clone(), tree_config, + evm_config, )?; let canon_state_notification_sender = tree.canon_state_notification_sender(); let blockchain_tree = ShareableBlockchainTree::new(tree); @@ -207,6 +216,7 @@ impl NodeBuilderWit network_builder.handle(), executor.clone(), blockchain_db.clone(), + evm_config, ); // allow network modifications @@ -272,6 +282,7 @@ impl NodeBuilderWit consensus_engine_tx.clone(), canon_state_notification_sender, mining_mode, + evm_config, ) .build(); @@ -286,6 +297,7 @@ impl NodeBuilderWit sync_metrics_tx, prune_config.clone(), max_block, + evm_config, ) .await?; @@ -307,6 +319,7 @@ impl NodeBuilderWit sync_metrics_tx, prune_config.clone(), max_block, + evm_config, ) .await?; diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 0608c357072c..1da61834d9b5 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -14,6 +14,10 @@ use reth_blockchain_tree::{ use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv}; use reth_interfaces::{consensus::Consensus, RethResult}; use reth_node_api::PayloadBuilderAttributes; +#[cfg(not(feature = "optimism"))] +use reth_node_builder::EthEvmConfig; +#[cfg(feature = "optimism")] +use reth_node_builder::OptimismEvmConfig; use reth_payload_builder::database::CachedReads; #[cfg(feature = "optimism")] use reth_payload_builder::OptimismPayloadBuilderAttributes; @@ -156,11 +160,17 @@ impl Command { let consensus: Arc = Arc::new(BeaconConsensus::new(Arc::clone(&self.chain))); + #[cfg(feature = "optimism")] + let evm_config = OptimismEvmConfig::default(); + + #[cfg(not(feature = "optimism"))] + let evm_config = EthEvmConfig::default(); + // configure blockchain tree let tree_externals = TreeExternals::new( provider_factory.clone(), Arc::clone(&consensus), - EvmProcessorFactory::new(self.chain.clone()), + EvmProcessorFactory::new(self.chain.clone(), evm_config), ); let tree = BlockchainTree::new(tree_externals, BlockchainTreeConfig::default(), None)?; let blockchain_tree = ShareableBlockchainTree::new(tree); @@ -298,7 +308,7 @@ impl Command { let block_with_senders = SealedBlockWithSenders::new(block.clone(), senders).unwrap(); - let executor_factory = EvmProcessorFactory::new(self.chain.clone()); + let executor_factory = EvmProcessorFactory::new(self.chain.clone(), evm_config); let mut executor = executor_factory.with_state(blockchain_db.latest()?); executor .execute_and_verify_receipt(&block_with_senders.clone().unseal(), U256::MAX)?; diff --git a/bin/reth/src/commands/debug_cmd/execution.rs b/bin/reth/src/commands/debug_cmd/execution.rs index 6dd9e2d7ef27..4ecc6cba580e 100644 --- a/bin/reth/src/commands/debug_cmd/execution.rs +++ b/bin/reth/src/commands/debug_cmd/execution.rs @@ -27,7 +27,7 @@ use reth_interfaces::{ }; use reth_network::{NetworkEvents, NetworkHandle}; use reth_network_api::NetworkInfo; - +use reth_node_builder::EthEvmConfig; use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, B256}; use reth_provider::{BlockExecutionWriter, HeaderSyncMode, ProviderFactory, StageCheckpointReader}; use reth_stages::{ @@ -110,7 +110,8 @@ impl Command { let stage_conf = &config.stages; let (tip_tx, tip_rx) = watch::channel(B256::ZERO); - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let factory = + reth_revm::EvmProcessorFactory::new(self.chain.clone(), EthEvmConfig::default()); let header_mode = HeaderSyncMode::Tip(tip_rx); let pipeline = Pipeline::builder() diff --git a/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs b/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs index 50dc6bbe38b8..4d280b7e4773 100644 --- a/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/commands/debug_cmd/in_memory_merkle.rs @@ -17,6 +17,7 @@ use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv}; use reth_interfaces::executor::BlockValidationError; use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; +use reth_node_builder::EthEvmConfig; use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, ChainSpec}; use reth_provider::{ AccountExtReader, BlockWriter, ExecutorFactory, HashingWriter, HeaderProvider, @@ -162,7 +163,8 @@ impl Command { ) .await?; - let executor_factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let executor_factory = + reth_revm::EvmProcessorFactory::new(self.chain.clone(), EthEvmConfig::default()); let mut executor = executor_factory.with_state(LatestStateProviderRef::new(provider.tx_ref())); diff --git a/bin/reth/src/commands/debug_cmd/merkle.rs b/bin/reth/src/commands/debug_cmd/merkle.rs index 2abcf8fe810c..25c0960acc65 100644 --- a/bin/reth/src/commands/debug_cmd/merkle.rs +++ b/bin/reth/src/commands/debug_cmd/merkle.rs @@ -20,7 +20,7 @@ use reth_db::{ use reth_interfaces::{consensus::Consensus, p2p::full_block::FullBlockClient}; use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; - +use reth_node_builder::EthEvmConfig; use reth_primitives::{ fs, stage::{StageCheckpoint, StageId}, @@ -202,7 +202,8 @@ impl Command { checkpoint.stage_checkpoint.is_some() }); - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let factory = + reth_revm::EvmProcessorFactory::new(self.chain.clone(), EthEvmConfig::default()); let mut execution_stage = ExecutionStage::new( factory, ExecutionStageThresholds { diff --git a/bin/reth/src/commands/debug_cmd/replay_engine.rs b/bin/reth/src/commands/debug_cmd/replay_engine.rs index aaf522fef2fb..fd45630f4929 100644 --- a/bin/reth/src/commands/debug_cmd/replay_engine.rs +++ b/bin/reth/src/commands/debug_cmd/replay_engine.rs @@ -21,9 +21,9 @@ use reth_interfaces::consensus::Consensus; use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; #[cfg(not(feature = "optimism"))] -use reth_node_builder::EthEngineTypes; +use reth_node_builder::{EthEngineTypes, EthEvmConfig}; #[cfg(feature = "optimism")] -use reth_node_builder::OptimismEngineTypes; +use reth_node_builder::{OptimismEngineTypes, OptimismEvmConfig}; use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; use reth_primitives::{fs, ChainSpec}; use reth_provider::{providers::BlockchainProvider, CanonStateSubscriptions, ProviderFactory}; @@ -125,11 +125,17 @@ impl Command { let consensus: Arc = Arc::new(BeaconConsensus::new(Arc::clone(&self.chain))); + #[cfg(not(feature = "optimism"))] + let evm_config = EthEvmConfig::default(); + + #[cfg(feature = "optimism")] + let evm_config = OptimismEvmConfig::default(); + // Configure blockchain tree let tree_externals = TreeExternals::new( provider_factory.clone(), Arc::clone(&consensus), - EvmProcessorFactory::new(self.chain.clone()), + EvmProcessorFactory::new(self.chain.clone(), evm_config), ); let tree = BlockchainTree::new(tree_externals, BlockchainTreeConfig::default(), None)?; let blockchain_tree = ShareableBlockchainTree::new(tree); diff --git a/bin/reth/src/commands/import.rs b/bin/reth/src/commands/import.rs index 7c57e6c0a498..acd04e97df6b 100644 --- a/bin/reth/src/commands/import.rs +++ b/bin/reth/src/commands/import.rs @@ -16,6 +16,7 @@ use reth_downloaders::{ headers::reverse_headers::ReverseHeadersDownloaderBuilder, }; use reth_interfaces::consensus::Consensus; +use reth_node_builder::EthEvmConfig; use reth_primitives::{stage::StageId, ChainSpec, B256}; use reth_provider::{HeaderSyncMode, ProviderFactory, StageCheckpointReader}; use reth_stages::{ @@ -158,7 +159,8 @@ impl ImportCommand { .into_task(); let (tip_tx, tip_rx) = watch::channel(B256::ZERO); - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let factory = + reth_revm::EvmProcessorFactory::new(self.chain.clone(), EthEvmConfig::default()); let max_block = file_client.max_block().unwrap_or(0); let mut pipeline = Pipeline::builder() diff --git a/bin/reth/src/commands/stage/dump/execution.rs b/bin/reth/src/commands/stage/dump/execution.rs index d0ce96ce5dfc..48bfc67dfe0f 100644 --- a/bin/reth/src/commands/stage/dump/execution.rs +++ b/bin/reth/src/commands/stage/dump/execution.rs @@ -5,6 +5,7 @@ use reth_db::{ cursor::DbCursorRO, database::Database, table::TableImporter, tables, transaction::DbTx, DatabaseEnv, }; +use reth_node_builder::EthEvmConfig; use reth_primitives::{stage::StageCheckpoint, ChainSpec}; use reth_provider::ProviderFactory; use reth_revm::EvmProcessorFactory; @@ -98,8 +99,10 @@ async fn unwind_and_copy( let factory = ProviderFactory::new(db_tool.db, db_tool.chain.clone()); let provider = factory.provider_rw()?; - let mut exec_stage = - ExecutionStage::new_with_factory(EvmProcessorFactory::new(db_tool.chain.clone())); + let mut exec_stage = ExecutionStage::new_with_factory(EvmProcessorFactory::new( + db_tool.chain.clone(), + EthEvmConfig::default(), + )); exec_stage.unwind( &provider, @@ -130,7 +133,10 @@ async fn dry_run( info!(target: "reth::cli", "Executing stage. [dry-run]"); let factory = ProviderFactory::new(&output_db, chain.clone()); - let mut exec_stage = ExecutionStage::new_with_factory(EvmProcessorFactory::new(chain.clone())); + let mut exec_stage = ExecutionStage::new_with_factory(EvmProcessorFactory::new( + chain.clone(), + EthEvmConfig::default(), + )); let input = reth_stages::ExecInput { target: Some(to), checkpoint: Some(StageCheckpoint::new(from)) }; diff --git a/bin/reth/src/commands/stage/dump/merkle.rs b/bin/reth/src/commands/stage/dump/merkle.rs index cba4afb964c5..3f7481be1fe9 100644 --- a/bin/reth/src/commands/stage/dump/merkle.rs +++ b/bin/reth/src/commands/stage/dump/merkle.rs @@ -2,6 +2,7 @@ use super::setup; use crate::utils::DbTool; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, DatabaseEnv}; +use reth_node_builder::EthEvmConfig; use reth_primitives::{stage::StageCheckpoint, BlockNumber, ChainSpec, PruneModes}; use reth_provider::ProviderFactory; use reth_stages::{ @@ -68,7 +69,7 @@ async fn unwind_and_copy( // Bring Plainstate to TO (hashing stage execution requires it) let mut exec_stage = ExecutionStage::new( - reth_revm::EvmProcessorFactory::new(db_tool.chain.clone()), + reth_revm::EvmProcessorFactory::new(db_tool.chain.clone(), EthEvmConfig::default()), ExecutionStageThresholds { max_blocks: Some(u64::MAX), max_changes: None, diff --git a/bin/reth/src/commands/stage/run.rs b/bin/reth/src/commands/stage/run.rs index f4a49c1ad094..d32fe9ae7e86 100644 --- a/bin/reth/src/commands/stage/run.rs +++ b/bin/reth/src/commands/stage/run.rs @@ -17,7 +17,7 @@ use reth_beacon_consensus::BeaconConsensus; use reth_config::Config; use reth_db::{init_db, mdbx::DatabaseArguments}; use reth_downloaders::bodies::bodies::BodiesDownloaderBuilder; - +use reth_node_builder::EthEvmConfig; use reth_primitives::ChainSpec; use reth_provider::{ProviderFactory, StageCheckpointReader}; use reth_stages::{ @@ -198,7 +198,10 @@ impl Command { } StageEnum::Senders => (Box::new(SenderRecoveryStage::new(batch_size)), None), StageEnum::Execution => { - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let factory = reth_revm::EvmProcessorFactory::new( + self.chain.clone(), + EthEvmConfig::default(), + ); ( Box::new(ExecutionStage::new( factory, diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index 7565fe06d702..56c45ab9fcc7 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -45,6 +45,7 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true , features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-revm.workspace = true +reth-node-builder.workspace = true parking_lot.workspace = true assert_matches.workspace = true diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index d2e93c6a0d0b..9bb48e1f5b53 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1239,6 +1239,7 @@ mod tests { use linked_hash_set::LinkedHashSet; use reth_db::{tables, test_utils::TempDatabase, transaction::DbTxMut, DatabaseEnv}; use reth_interfaces::test_utils::TestConsensus; + use reth_node_builder::EthEvmConfig; use reth_primitives::{ constants::{EIP1559_INITIAL_BASE_FEE, EMPTY_ROOT_HASH, ETHEREUM_BLOCK_GAS_LIMIT}, keccak256, @@ -1402,7 +1403,8 @@ mod tests { ); let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); let consensus = Arc::new(TestConsensus::default()); - let executor_factory = EvmProcessorFactory::new(chain_spec.clone()); + let executor_factory = + EvmProcessorFactory::new(chain_spec.clone(), EthEvmConfig::default()); { let provider_rw = provider_factory.provider_rw().unwrap(); diff --git a/crates/consensus/auto-seal/Cargo.toml b/crates/consensus/auto-seal/Cargo.toml index 4fa7ddfc1e65..25b5db192ae7 100644 --- a/crates/consensus/auto-seal/Cargo.toml +++ b/crates/consensus/auto-seal/Cargo.toml @@ -21,6 +21,7 @@ reth-stages.workspace = true reth-revm.workspace = true reth-transaction-pool.workspace = true reth-node-api.workspace = true +reth-node-builder.workspace = true # async diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index cd70af97bdae..8e6fc27b1a7d 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -19,7 +19,7 @@ use reth_interfaces::{ consensus::{Consensus, ConsensusError}, executor::{BlockExecutionError, BlockValidationError}, }; -use reth_node_api::EngineTypes; +use reth_node_api::{EngineTypes, EvmEnvConfig}; use reth_primitives::{ constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT}, proofs, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, Bloom, @@ -94,7 +94,7 @@ impl Consensus for AutoSealConsensus { /// Builder type for configuring the setup #[derive(Debug)] -pub struct AutoSealBuilder { +pub struct AutoSealBuilder { client: Client, consensus: AutoSealConsensus, pool: Pool, @@ -102,11 +102,12 @@ pub struct AutoSealBuilder { storage: Storage, to_engine: UnboundedSender>, canon_state_notification: CanonStateNotificationSender, + evm_config: EvmConfig, } // === impl AutoSealBuilder === -impl AutoSealBuilder +impl AutoSealBuilder where Client: BlockReaderIdExt, Pool: TransactionPool, @@ -120,6 +121,7 @@ where to_engine: UnboundedSender>, canon_state_notification: CanonStateNotificationSender, mode: MiningMode, + evm_config: EvmConfig, ) -> Self { let latest_header = client .latest_header() @@ -135,6 +137,7 @@ where mode, to_engine, canon_state_notification, + evm_config, } } @@ -146,9 +149,19 @@ where /// Consumes the type and returns all components #[track_caller] - pub fn build(self) -> (AutoSealConsensus, AutoSealClient, MiningTask) { - let Self { client, consensus, pool, mode, storage, to_engine, canon_state_notification } = - self; + pub fn build( + self, + ) -> (AutoSealConsensus, AutoSealClient, MiningTask) { + let Self { + client, + consensus, + pool, + mode, + storage, + to_engine, + canon_state_notification, + evm_config, + } = self; let auto_client = AutoSealClient::new(storage.clone()); let task = MiningTask::new( Arc::clone(&consensus.chain_spec), @@ -158,6 +171,7 @@ where storage, client, pool, + evm_config, ); (consensus, auto_client, task) } @@ -298,11 +312,14 @@ impl StorageInner { /// Executes the block with the given block and senders, on the provided [EVMProcessor]. /// /// This returns the poststate from execution and post-block changes, as well as the gas used. - pub(crate) fn execute( + pub(crate) fn execute( &mut self, block: &BlockWithSenders, - executor: &mut EVMProcessor<'_>, - ) -> Result<(BundleStateWithReceipts, u64), BlockExecutionError> { + executor: &mut EVMProcessor<'_, EvmConfig>, + ) -> Result<(BundleStateWithReceipts, u64), BlockExecutionError> + where + EvmConfig: EvmEnvConfig, + { trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); // TODO: there isn't really a parent beacon block root here, so not sure whether or not to // call the 4788 beacon contract @@ -370,12 +387,16 @@ impl StorageInner { /// Builds and executes a new block with the given transactions, on the provided [EVMProcessor]. /// /// This returns the header of the executed block, as well as the poststate from execution. - pub(crate) fn build_and_execute( + pub(crate) fn build_and_execute( &mut self, transactions: Vec, client: &impl StateProviderFactory, chain_spec: Arc, - ) -> Result<(SealedHeader, BundleStateWithReceipts), BlockExecutionError> { + evm_config: EvmConfig, + ) -> Result<(SealedHeader, BundleStateWithReceipts), BlockExecutionError> + where + EvmConfig: EvmEnvConfig, + { let header = self.build_header_template(&transactions, chain_spec.clone()); let block = Block { header, body: transactions, ommers: vec![], withdrawals: None } @@ -389,7 +410,7 @@ impl StorageInner { .with_database_boxed(Box::new(StateProviderDatabase::new(client.latest().unwrap()))) .with_bundle_update() .build(); - let mut executor = EVMProcessor::new_with_state(chain_spec.clone(), db); + let mut executor = EVMProcessor::new_with_state(chain_spec.clone(), db, evm_config); let (bundle_state, gas_used) = self.execute(&block, &mut executor)?; diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 3e64e8bdbae9..5b1a48a33d09 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -2,7 +2,7 @@ use crate::{mode::MiningMode, Storage}; use futures_util::{future::BoxFuture, FutureExt}; use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus}; use reth_interfaces::consensus::ForkchoiceState; -use reth_node_api::EngineTypes; +use reth_node_api::{EngineTypes, EvmEnvConfig}; use reth_primitives::{Block, ChainSpec, IntoRecoveredTransaction, SealedBlockWithSenders}; use reth_provider::{CanonChainTracker, CanonStateNotificationSender, Chain, StateProviderFactory}; use reth_stages::PipelineEvent; @@ -19,7 +19,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use tracing::{debug, error, warn}; /// A Future that listens for new ready transactions and puts new blocks into storage -pub struct MiningTask { +pub struct MiningTask { /// The configured chain spec chain_spec: Arc, /// The client used to interact with the state @@ -40,12 +40,17 @@ pub struct MiningTask { canon_state_notification: CanonStateNotificationSender, /// The pipeline events to listen on pipe_line_events: Option>, + /// The type that defines how to configure the EVM. + evm_config: EvmConfig, } // === impl MiningTask === -impl MiningTask { +impl + MiningTask +{ /// Creates a new instance of the task + #[allow(clippy::too_many_arguments)] pub(crate) fn new( chain_spec: Arc, miner: MiningMode, @@ -54,6 +59,7 @@ impl MiningTask Self { Self { chain_spec, @@ -66,6 +72,7 @@ impl MiningTask MiningTask Future for MiningTask +impl Future for MiningTask where Client: StateProviderFactory + CanonChainTracker + Clone + Unpin + 'static, Pool: TransactionPool + Unpin + 'static, ::Transaction: IntoRecoveredTransaction, Engine: EngineTypes + 'static, + EvmConfig: EvmEnvConfig + Clone + Unpin + Send + Sync + 'static, { type Output = (); @@ -110,6 +118,7 @@ where let pool = this.pool.clone(); let events = this.pipe_line_events.take(); let canon_state_notification = this.canon_state_notification.clone(); + let evm_config = this.evm_config.clone(); // Create the mining future that creates a block, notifies the engine that drives // the pipeline @@ -125,7 +134,12 @@ where }) .unzip(); - match storage.build_and_execute(transactions.clone(), &client, chain_spec) { + match storage.build_and_execute( + transactions.clone(), + &client, + chain_spec, + evm_config, + ) { Ok((new_header, bundle_state)) => { // clear all transactions from pool pool.remove_transactions( @@ -230,8 +244,8 @@ where } } -impl std::fmt::Debug - for MiningTask +impl std::fmt::Debug + for MiningTask { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("MiningTask").finish_non_exhaustive() diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 35d34dfe0abe..bbe0f8492264 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -22,7 +22,7 @@ use reth_interfaces::{ sync::NoopSyncStateUpdater, test_utils::{NoopFullBlockClient, TestConsensus}, }; -use reth_node_builder::EthEngineTypes; +use reth_node_builder::{EthEngineTypes, EthEvmConfig}; use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_primitives::{BlockNumber, ChainSpec, PruneModes, Receipt, B256, U256}; use reth_provider::{ @@ -46,7 +46,7 @@ type TestBeaconConsensusEngine = BeaconConsensusEngine< Arc, ShareableBlockchainTree< Arc, - EitherExecutorFactory, + EitherExecutorFactory>, >, >, Arc>, @@ -474,6 +474,7 @@ where } TestExecutorConfig::Real => EitherExecutorFactory::Right(EvmProcessorFactory::new( self.base_config.chain_spec.clone(), + EthEvmConfig::default(), )), }; diff --git a/crates/node-api/Cargo.toml b/crates/node-api/Cargo.toml index c0d7b8c5017d..097dde333dbd 100644 --- a/crates/node-api/Cargo.toml +++ b/crates/node-api/Cargo.toml @@ -14,6 +14,7 @@ workspace = true # reth reth-primitives.workspace = true reth-rpc-types.workspace = true +revm-primitives.workspace = true thiserror.workspace = true # io diff --git a/crates/node-api/src/evm/mod.rs b/crates/node-api/src/evm/mod.rs new file mode 100644 index 000000000000..b66126ebe63f --- /dev/null +++ b/crates/node-api/src/evm/mod.rs @@ -0,0 +1,35 @@ +use reth_primitives::{revm::env::fill_block_env, Address, ChainSpec, Header, Transaction, U256}; +use revm_primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}; + +/// This represents the set of methods used to configure the EVM before execution. +pub trait EvmEnvConfig: Send + Sync + Unpin + Clone { + /// The type of the transaction metadata. + type TxMeta; + + /// Fill transaction environment from a [Transaction] and the given sender address. + fn fill_tx_env(tx_env: &mut TxEnv, transaction: T, sender: Address, meta: Self::TxMeta) + where + T: AsRef; + + /// Fill [CfgEnv] fields according to the chain spec and given header + fn fill_cfg_env( + cfg_env: &mut CfgEnv, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, + ); + + /// Convenience function to call both [fill_cfg_env](EvmEnvConfig::fill_cfg_env) and + /// [fill_block_env]. + fn fill_cfg_and_block_env( + cfg: &mut CfgEnv, + block_env: &mut BlockEnv, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, + ) { + Self::fill_cfg_env(cfg, chain_spec, header, total_difficulty); + let after_merge = cfg.spec_id >= SpecId::MERGE; + fill_block_env(block_env, chain_spec, header, after_merge); + } +} diff --git a/crates/node-api/src/lib.rs b/crates/node-api/src/lib.rs index 7f490698d282..cf2aa568b967 100644 --- a/crates/node-api/src/lib.rs +++ b/crates/node-api/src/lib.rs @@ -18,3 +18,7 @@ pub use engine::{ AttributesValidationError, BuiltPayload, EngineApiMessageVersion, EngineTypes, PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes, }; + +/// Traits and helper types used to abstract over EVM methods and types. +pub mod evm; +pub use evm::EvmEnvConfig; diff --git a/crates/node-builder/src/engine.rs b/crates/node-builder/src/engine.rs index 05e3b77191be..75a98dbd2dc9 100644 --- a/crates/node-builder/src/engine.rs +++ b/crates/node-builder/src/engine.rs @@ -1,15 +1,9 @@ -#[cfg(feature = "optimism")] -use reth_node_api::optimism_validate_version_specific_fields; use reth_node_api::{ validate_version_specific_fields, AttributesValidationError, EngineApiMessageVersion, EngineTypes, PayloadOrAttributes, }; -#[cfg(feature = "optimism")] -use reth_payload_builder::OptimismPayloadBuilderAttributes; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_primitives::ChainSpec; -#[cfg(feature = "optimism")] -use reth_rpc_types::engine::OptimismPayloadAttributes; use reth_rpc_types::engine::PayloadAttributes as EthPayloadAttributes; /// The types used in the default mainnet ethereum beacon consensus engine. @@ -30,25 +24,3 @@ impl EngineTypes for EthEngineTypes { validate_version_specific_fields(chain_spec, version, payload_or_attrs) } } - -#[cfg(feature = "optimism")] -/// The types used in the optimism beacon consensus engine. -#[derive(Debug, Default, Clone)] -#[non_exhaustive] -pub struct OptimismEngineTypes; - -// TODO: remove cfg once Hardfork::Canyon can be used without the flag -#[cfg(feature = "optimism")] -impl EngineTypes for OptimismEngineTypes { - type PayloadAttributes = OptimismPayloadAttributes; - type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes; - type BuiltPayload = EthBuiltPayload; - - fn validate_version_specific_fields( - chain_spec: &ChainSpec, - version: EngineApiMessageVersion, - payload_or_attrs: PayloadOrAttributes<'_, OptimismPayloadAttributes>, - ) -> Result<(), AttributesValidationError> { - optimism_validate_version_specific_fields(chain_spec, version, payload_or_attrs) - } -} diff --git a/crates/node-builder/src/evm.rs b/crates/node-builder/src/evm.rs new file mode 100644 index 000000000000..dd78674202fe --- /dev/null +++ b/crates/node-builder/src/evm.rs @@ -0,0 +1,70 @@ +use reth_node_api::EvmEnvConfig; +use reth_primitives::{ + revm::{config::revm_spec, env::fill_tx_env}, + revm_primitives::{AnalysisKind, CfgEnv, TxEnv}, + Address, ChainSpec, Head, Header, Transaction, U256, +}; + +/// Ethereum-related EVM configuration. +#[derive(Debug, Clone, Copy, Default)] +#[non_exhaustive] +pub struct EthEvmConfig; + +impl EvmEnvConfig for EthEvmConfig { + type TxMeta = (); + + fn fill_tx_env(tx_env: &mut TxEnv, transaction: T, sender: Address, _meta: ()) + where + T: AsRef, + { + fill_tx_env(tx_env, transaction, sender) + } + + fn fill_cfg_env( + cfg_env: &mut CfgEnv, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, + ) { + let spec_id = revm_spec( + chain_spec, + Head { + number: header.number, + timestamp: header.timestamp, + difficulty: header.difficulty, + total_difficulty, + hash: Default::default(), + }, + ); + + cfg_env.chain_id = chain_spec.chain().id(); + cfg_env.spec_id = spec_id; + cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_primitives::revm_primitives::BlockEnv; + + #[test] + #[ignore] + fn test_fill_cfg_and_block_env() { + let mut cfg_env = CfgEnv::default(); + let mut block_env = BlockEnv::default(); + let header = Header::default(); + let chain_spec = ChainSpec::default(); + let total_difficulty = U256::ZERO; + + EthEvmConfig::fill_cfg_and_block_env( + &mut cfg_env, + &mut block_env, + &chain_spec, + &header, + total_difficulty, + ); + + assert_eq!(cfg_env.chain_id, chain_spec.chain().id()); + } +} diff --git a/crates/node-builder/src/lib.rs b/crates/node-builder/src/lib.rs index c15e0132da5c..13050eb40614 100644 --- a/crates/node-builder/src/lib.rs +++ b/crates/node-builder/src/lib.rs @@ -12,5 +12,13 @@ pub mod engine; pub use engine::EthEngineTypes; +/// Exports commonly used concrete instances of the [EvmEnvConfig](reth_node_api::EvmEnvConfig) +/// trait. +pub mod evm; +pub use evm::EthEvmConfig; + +/// Exports optimism-specific types that implement traits in [reth_node_api]. +#[cfg(feature = "optimism")] +pub mod optimism; #[cfg(feature = "optimism")] -pub use engine::OptimismEngineTypes; +pub use optimism::{OptimismEngineTypes, OptimismEvmConfig}; diff --git a/crates/node-builder/src/optimism.rs b/crates/node-builder/src/optimism.rs new file mode 100644 index 000000000000..8385fb6f8bed --- /dev/null +++ b/crates/node-builder/src/optimism.rs @@ -0,0 +1,72 @@ +#![cfg(feature = "optimism")] +use reth_node_api::{ + optimism_validate_version_specific_fields, AttributesValidationError, EngineApiMessageVersion, + EngineTypes, EvmEnvConfig, PayloadOrAttributes, +}; +use reth_payload_builder::{EthBuiltPayload, OptimismPayloadBuilderAttributes}; +use reth_primitives::{ + revm::{config::revm_spec, env::fill_op_tx_env}, + revm_primitives::{AnalysisKind, CfgEnv, TxEnv}, + Address, Bytes, ChainSpec, Head, Header, Transaction, U256, +}; +use reth_rpc_types::engine::OptimismPayloadAttributes; + +/// The types used in the optimism beacon consensus engine. +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct OptimismEngineTypes; + +impl EngineTypes for OptimismEngineTypes { + type PayloadAttributes = OptimismPayloadAttributes; + type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes; + type BuiltPayload = EthBuiltPayload; + + fn validate_version_specific_fields( + chain_spec: &ChainSpec, + version: EngineApiMessageVersion, + payload_or_attrs: PayloadOrAttributes<'_, OptimismPayloadAttributes>, + ) -> Result<(), AttributesValidationError> { + optimism_validate_version_specific_fields(chain_spec, version, payload_or_attrs) + } +} + +/// Optimism-related EVM configuration. +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct OptimismEvmConfig; + +impl EvmEnvConfig for OptimismEvmConfig { + type TxMeta = Bytes; + + fn fill_tx_env(tx_env: &mut TxEnv, transaction: T, sender: Address, meta: Bytes) + where + T: AsRef, + { + fill_op_tx_env(tx_env, transaction, sender, meta); + } + + fn fill_cfg_env( + cfg_env: &mut CfgEnv, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, + ) { + let spec_id = revm_spec( + chain_spec, + Head { + number: header.number, + timestamp: header.timestamp, + difficulty: header.difficulty, + total_difficulty, + hash: Default::default(), + }, + ); + + cfg_env.chain_id = chain_spec.chain().id(); + cfg_env.spec_id = spec_id; + cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; + + // optimism-specific configuration + cfg_env.optimism = chain_spec.is_optimism(); + } +} diff --git a/crates/node-core/src/args/rpc_server_args.rs b/crates/node-core/src/args/rpc_server_args.rs index e0c6fcd8f807..0de47e9518a1 100644 --- a/crates/node-core/src/args/rpc_server_args.rs +++ b/crates/node-core/src/args/rpc_server_args.rs @@ -19,7 +19,7 @@ use clap::{ use futures::TryFutureExt; use rand::Rng; use reth_network_api::{NetworkInfo, Peers}; -use reth_node_api::EngineTypes; +use reth_node_api::{EngineTypes, EvmEnvConfig}; use reth_provider::{ AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, StateProviderFactory, @@ -292,6 +292,7 @@ impl RpcServerArgs { .with_network(components.network()) .with_events(components.events()) .with_executor(components.task_executor()) + .with_evm_config(components.evm_config()) .build_with_auth_server(module_config, engine_api); let rpc_components = RethRpcComponents { @@ -338,13 +339,14 @@ impl RpcServerArgs { } /// Convenience function for starting a rpc server with configs which extracted from cli args. - pub async fn start_rpc_server( + pub async fn start_rpc_server( &self, provider: Provider, pool: Pool, network: Network, executor: Tasks, events: Events, + evm_config: EvmConfig, ) -> Result where Provider: BlockReaderIdExt @@ -361,6 +363,7 @@ impl RpcServerArgs { Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { reth_rpc_builder::launch( provider, @@ -370,12 +373,14 @@ impl RpcServerArgs { self.rpc_server_config(), executor, events, + evm_config, ) .await } /// Create Engine API server. - pub async fn start_auth_server( + #[allow(clippy::too_many_arguments)] + pub async fn start_auth_server( &self, provider: Provider, pool: Pool, @@ -383,6 +388,7 @@ impl RpcServerArgs { executor: Tasks, engine_api: EngineApi, jwt_secret: JwtSecret, + evm_config: EvmConfig, ) -> Result where Provider: BlockReaderIdExt @@ -397,6 +403,7 @@ impl RpcServerArgs { Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, EngineT: EngineTypes + 'static, + EvmConfig: EvmEnvConfig + 'static, { let socket_address = SocketAddr::new(self.auth_addr, self.auth_port); @@ -408,6 +415,7 @@ impl RpcServerArgs { engine_api, socket_address, jwt_secret, + evm_config, ) .await } diff --git a/crates/node-core/src/cli/components.rs b/crates/node-core/src/cli/components.rs index 04168dbc4403..28580a38a1a6 100644 --- a/crates/node-core/src/cli/components.rs +++ b/crates/node-core/src/cli/components.rs @@ -3,6 +3,7 @@ use reth_db::database::Database; use reth_network::{NetworkEvents, NetworkProtocols}; use reth_network_api::{NetworkInfo, Peers}; +use reth_node_api::EvmEnvConfig; use reth_primitives::ChainSpec; use reth_provider::{ AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, @@ -59,6 +60,8 @@ pub trait RethNodeComponents: Clone + Send + Sync + 'static { type Events: CanonStateSubscriptions + Clone + 'static; /// The type that is used to spawn tasks. type Tasks: TaskSpawner + Clone + Unpin + 'static; + /// The type that defines how to configure the EVM before execution. + type EvmConfig: EvmEnvConfig + 'static; /// Returns the instance of the provider fn provider(&self) -> Self::Provider; @@ -75,6 +78,9 @@ pub trait RethNodeComponents: Clone + Send + Sync + 'static { /// Returns the instance of the events subscription handler. fn events(&self) -> Self::Events; + /// Returns the instance of the EVM config. + fn evm_config(&self) -> Self::EvmConfig; + /// Helper function to return the chain spec. fn chain_spec(&self) -> Arc { self.provider().chain_spec() @@ -98,6 +104,7 @@ pub struct RethRpcComponents<'a, Reth: RethNodeComponents> { Reth::Network, Reth::Tasks, Reth::Events, + Reth::EvmConfig, >, /// Holds installed modules per transport type. /// @@ -114,7 +121,7 @@ pub struct RethRpcComponents<'a, Reth: RethNodeComponents> { /// /// Represents components required for the Reth node. #[derive(Clone, Debug)] -pub struct RethNodeComponentsImpl { +pub struct RethNodeComponentsImpl { /// Represents underlying database type. __phantom: PhantomData, /// Represents the provider instance. @@ -127,10 +134,12 @@ pub struct RethNodeComponentsImpl { pub task_executor: Tasks, /// Represents the events subscription handler instance. pub events: Events, + /// Represents the type that is used to configure the EVM before execution. + pub evm_config: EvmConfig, } -impl - RethNodeComponentsImpl +impl + RethNodeComponentsImpl { /// Create new instance of the node components. pub fn new( @@ -139,13 +148,22 @@ impl network: Network, task_executor: Tasks, events: Events, + evm_config: EvmConfig, ) -> Self { - Self { provider, pool, network, task_executor, events, __phantom: std::marker::PhantomData } + Self { + provider, + pool, + network, + task_executor, + events, + evm_config, + __phantom: std::marker::PhantomData, + } } } -impl RethNodeComponents - for RethNodeComponentsImpl +impl RethNodeComponents + for RethNodeComponentsImpl where DB: Database + Clone + Unpin + 'static, Provider: FullProvider + Clone + 'static, @@ -153,6 +171,7 @@ where Pool: TransactionPool + Clone + Unpin + 'static, Network: NetworkInfo + Peers + NetworkProtocols + NetworkEvents + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { type DB = DB; type Provider = Provider; @@ -160,6 +179,7 @@ where type Network = Network; type Events = Events; type Tasks = Tasks; + type EvmConfig = EvmConfig; fn provider(&self) -> Self::Provider { self.provider.clone() @@ -180,6 +200,10 @@ where fn events(&self) -> Self::Events { self.events.clone() } + + fn evm_config(&self) -> Self::EvmConfig { + self.evm_config.clone() + } } /// Contains the handles to the spawned RPC servers. diff --git a/crates/node-core/src/node_config.rs b/crates/node-core/src/node_config.rs index d368cd531957..c58cc4b9f804 100644 --- a/crates/node-core/src/node_config.rs +++ b/crates/node-core/src/node_config.rs @@ -36,6 +36,7 @@ use reth_interfaces::{ RethResult, }; use reth_network::{NetworkBuilder, NetworkConfig, NetworkHandle, NetworkManager}; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP}, kzg::KzgSettings, @@ -412,22 +413,24 @@ impl NodeConfig { } /// Build the blockchain tree - pub fn build_blockchain_tree( + pub fn build_blockchain_tree( &self, provider_factory: ProviderFactory, consensus: Arc, prune_config: Option, sync_metrics_tx: UnboundedSender, tree_config: BlockchainTreeConfig, - ) -> eyre::Result> + evm_config: EvmConfig, + ) -> eyre::Result>> where DB: Database + Unpin + Clone + 'static, + EvmConfig: EvmEnvConfig + Clone + 'static, { // configure blockchain tree let tree_externals = TreeExternals::new( provider_factory.clone(), consensus.clone(), - EvmProcessorFactory::new(self.chain.clone()), + EvmProcessorFactory::new(self.chain.clone(), evm_config), ); let tree = BlockchainTree::new( tree_externals, @@ -517,7 +520,7 @@ impl NodeConfig { /// Constructs a [Pipeline] that's wired to the network #[allow(clippy::too_many_arguments)] - pub async fn build_networked_pipeline( + pub async fn build_networked_pipeline( &self, config: &StageConfig, client: Client, @@ -527,10 +530,12 @@ impl NodeConfig { metrics_tx: reth_stages::MetricEventsSender, prune_config: Option, max_block: Option, + evm_config: EvmConfig, ) -> eyre::Result> where DB: Database + Unpin + Clone + 'static, Client: HeadersClient + BodiesClient + Clone + 'static, + EvmConfig: EvmEnvConfig + Clone + 'static, { // building network downloaders using the fetch client let header_downloader = ReverseHeadersDownloaderBuilder::new(config.headers) @@ -552,6 +557,7 @@ impl NodeConfig { self.debug.continuous, metrics_tx, prune_config, + evm_config, ) .await?; @@ -750,7 +756,7 @@ impl NodeConfig { /// Builds the [Pipeline] with the given [ProviderFactory] and downloaders. #[allow(clippy::too_many_arguments)] - pub async fn build_pipeline( + pub async fn build_pipeline( &self, provider_factory: ProviderFactory, stage_config: &StageConfig, @@ -761,11 +767,13 @@ impl NodeConfig { continuous: bool, metrics_tx: reth_stages::MetricEventsSender, prune_config: Option, + evm_config: EvmConfig, ) -> eyre::Result> where DB: Database + Clone + 'static, H: HeaderDownloader + 'static, B: BodyDownloader + 'static, + EvmConfig: EvmEnvConfig + Clone + 'static, { let mut builder = Pipeline::builder(); @@ -776,7 +784,7 @@ impl NodeConfig { let (tip_tx, tip_rx) = watch::channel(B256::ZERO); use revm_inspectors::stack::InspectorStackConfig; - let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone()); + let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone(), evm_config); let stack_config = InspectorStackConfig { use_printer_tracer: self.debug.print_inspector, diff --git a/crates/primitives/src/revm/env.rs b/crates/primitives/src/revm/env.rs index 6741d191c0f8..bcc819a23a6e 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/primitives/src/revm/env.rs @@ -1,56 +1,14 @@ use crate::{ constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, recover_signer_unchecked, - revm::config::revm_spec, - revm_primitives::{AnalysisKind, BlockEnv, CfgEnv, Env, SpecId, TransactTo, TxEnv}, - Address, Bytes, Chain, ChainSpec, Head, Header, Transaction, TransactionKind, + revm_primitives::{BlockEnv, Env, TransactTo, TxEnv}, + Address, Bytes, Chain, ChainSpec, Header, Transaction, TransactionKind, TransactionSignedEcRecovered, B256, U256, }; #[cfg(feature = "optimism")] use revm_primitives::OptimismFields; -/// Convenience function to call both [fill_cfg_env] and [fill_block_env] -pub fn fill_cfg_and_block_env( - cfg: &mut CfgEnv, - block_env: &mut BlockEnv, - chain_spec: &ChainSpec, - header: &Header, - total_difficulty: U256, -) { - fill_cfg_env(cfg, chain_spec, header, total_difficulty); - let after_merge = cfg.spec_id >= SpecId::MERGE; - fill_block_env(block_env, chain_spec, header, after_merge); -} - -/// Fill [CfgEnv] fields according to the chain spec and given header -pub fn fill_cfg_env( - cfg_env: &mut CfgEnv, - chain_spec: &ChainSpec, - header: &Header, - total_difficulty: U256, -) { - let spec_id = revm_spec( - chain_spec, - Head { - number: header.number, - timestamp: header.timestamp, - difficulty: header.difficulty, - total_difficulty, - hash: Default::default(), - }, - ); - - cfg_env.chain_id = chain_spec.chain().id(); - cfg_env.spec_id = spec_id; - cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; - - #[cfg(feature = "optimism")] - { - cfg_env.optimism = chain_spec.is_optimism(); - } -} - /// Fill block environment from Block. pub fn fill_block_env( block_env: &mut BlockEnv, @@ -146,7 +104,12 @@ pub fn tx_env_with_recovered(transaction: &TransactionSignedEcRecovered) -> TxEn { let mut envelope_buf = Vec::with_capacity(transaction.length_without_header()); transaction.encode_enveloped(&mut envelope_buf); - fill_tx_env(&mut tx_env, transaction.as_ref(), transaction.signer(), envelope_buf.into()); + fill_op_tx_env( + &mut tx_env, + transaction.as_ref(), + transaction.signer(), + envelope_buf.into(), + ); } tx_env @@ -175,7 +138,7 @@ pub fn fill_tx_env_with_beacon_root_contract_call(env: &mut Env, parent_beacon_b nonce: None, gas_limit: 30_000_000, value: U256::ZERO, - data: parent_beacon_block_root.0.to_vec().into(), + data: parent_beacon_block_root.0.into(), // Setting the gas price to zero enforces that no value is transferred as part of the call, // and that the call will not count against the block's gas limit gas_price: U256::ZERO, @@ -217,16 +180,12 @@ pub fn fill_tx_env_with_recovered( transaction: &TransactionSignedEcRecovered, envelope: Bytes, ) { - fill_tx_env(tx_env, transaction.as_ref(), transaction.signer(), envelope); + fill_op_tx_env(tx_env, transaction.as_ref(), transaction.signer(), envelope); } /// Fill transaction environment from a [Transaction] and the given sender address. -pub fn fill_tx_env( - tx_env: &mut TxEnv, - transaction: T, - sender: Address, - #[cfg(feature = "optimism")] envelope: Bytes, -) where +pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: T, sender: Address) +where T: AsRef, { tx_env.caller = sender; @@ -332,14 +291,18 @@ pub fn fill_tx_env( tx_env.nonce = None; } } - - #[cfg(feature = "optimism")] - fill_op_tx_env(tx_env, transaction, envelope); } +/// Fill transaction environment from a [Transaction], envelope, and the given sender address. #[cfg(feature = "optimism")] #[inline(always)] -fn fill_op_tx_env>(tx_env: &mut TxEnv, transaction: T, envelope: Bytes) { +pub fn fill_op_tx_env>( + tx_env: &mut TxEnv, + transaction: T, + sender: Address, + envelope: Bytes, +) { + fill_tx_env(tx_env, &transaction, sender); match transaction.as_ref() { Transaction::Deposit(tx) => { tx_env.optimism = OptimismFields { @@ -359,27 +322,3 @@ fn fill_op_tx_env>(tx_env: &mut TxEnv, transaction: T, env } } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[ignore] - fn test_fill_cfg_and_block_env() { - let mut cfg_env = CfgEnv::default(); - let mut block_env = BlockEnv::default(); - let header = Header::default(); - let chain_spec = ChainSpec::default(); - let total_difficulty = U256::ZERO; - - fill_cfg_and_block_env( - &mut cfg_env, - &mut block_env, - &chain_spec, - &header, - total_difficulty, - ); - - assert_eq!(cfg_env.chain_id, chain_spec.chain().id()); - } -} diff --git a/crates/primitives/src/revm/mod.rs b/crates/primitives/src/revm/mod.rs index 14bafa18e75a..311c5e2b5007 100644 --- a/crates/primitives/src/revm/mod.rs +++ b/crates/primitives/src/revm/mod.rs @@ -1,20 +1,18 @@ -/// The `compat` module contains a set of utility functions that bridge the gap between Revm and -/// Reth Ethereum implementations. +/// The `compat` module contains utility functions that perform conversions between reth and revm, +/// compare analogous types from the two implementations, and calculate intrinsic gas usage. /// -/// These functions enable the conversion of data structures between the two implementations, such -/// as converting `Log` structures, `AccountInfo`, and `Account` objects. -/// -/// Additionally, it provides a function to calculate intrinsic gas usage for transactions beyond -/// the Merge hardfork, offering compatibility for both Shanghai and Merge Ethereum specifications. -/// -/// These utilities facilitate interoperability and data exchange between Revm and Reth -/// implementations. +/// The included conversion methods can be used to convert between: +/// * reth's [Log](crate::Log) type and revm's [Log](revm_primitives::Log) type. +/// * reth's [Account](crate::Account) type and revm's [AccountInfo](revm_primitives::AccountInfo) +/// type. pub mod compat; + /// Reth block execution/validation configuration and constants pub mod config; -/// The `env` module provides essential utilities for managing Ethereum transaction and block -/// environments. + +/// The `env` module provides utility methods for filling revm transaction and block environments. /// -/// It includes functions to fill transaction and block environments with relevant data, handle -/// system contract calls, and recover the signer of Ethereum headers. +/// It includes functions to fill transaction and block environments with relevant data, prepare +/// the block and transaction environments for system contract calls, and recover the signer from +/// Clique-formatted extra data in ethereum headers. pub mod env; diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index e717ee22ed3a..2ca18b165c44 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -17,6 +17,7 @@ reth-primitives.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true reth-consensus-common.workspace = true +reth-node-api.workspace = true # revm revm.workspace = true @@ -27,6 +28,7 @@ tracing.workspace = true [dev-dependencies] reth-trie.workspace = true +reth-node-builder.workspace = true [features] optimism = [ diff --git a/crates/revm/src/factory.rs b/crates/revm/src/factory.rs index c35b1ad11af6..a35e0dcd217e 100644 --- a/crates/revm/src/factory.rs +++ b/crates/revm/src/factory.rs @@ -3,21 +3,24 @@ use crate::{ processor::EVMProcessor, stack::{InspectorStack, InspectorStackConfig}, }; +use reth_node_api::EvmEnvConfig; use reth_primitives::ChainSpec; use reth_provider::{ExecutorFactory, PrunableBlockExecutor, StateProvider}; use std::sync::Arc; /// Factory for creating [EVMProcessor]. #[derive(Clone, Debug)] -pub struct EvmProcessorFactory { +pub struct EvmProcessorFactory { chain_spec: Arc, stack: Option, + /// Type that defines how the produced EVM should be configured. + evm_config: EvmConfig, } -impl EvmProcessorFactory { +impl EvmProcessorFactory { /// Create new factory - pub fn new(chain_spec: Arc) -> Self { - Self { chain_spec, stack: None } + pub fn new(chain_spec: Arc, evm_config: EvmConfig) -> Self { + Self { chain_spec, stack: None, evm_config } } /// Sets the inspector stack for all generated executors. @@ -33,13 +36,20 @@ impl EvmProcessorFactory { } } -impl ExecutorFactory for EvmProcessorFactory { +impl ExecutorFactory for EvmProcessorFactory +where + EvmConfig: EvmEnvConfig + Send + Sync + Clone + 'static, +{ fn with_state<'a, SP: StateProvider + 'a>( &'a self, sp: SP, ) -> Box { let database_state = StateProviderDatabase::new(sp); - let mut evm = Box::new(EVMProcessor::new_with_db(self.chain_spec.clone(), database_state)); + let mut evm = Box::new(EVMProcessor::new_with_db( + self.chain_spec.clone(), + database_state, + self.evm_config.clone(), + )); if let Some(ref stack) = self.stack { evm.set_stack(stack.clone()); } diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index 40ff62f667d7..164725f03f78 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -2,6 +2,7 @@ use crate::processor::{verify_receipt, EVMProcessor}; use reth_interfaces::executor::{ BlockExecutionError, BlockValidationError, OptimismBlockExecutionError, }; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ revm::compat::into_reth_log, revm_primitives::ResultAndState, BlockWithSenders, Hardfork, Receipt, U256, @@ -11,7 +12,10 @@ use revm::DatabaseCommit; use std::time::Instant; use tracing::{debug, trace}; -impl<'a> BlockExecutor for EVMProcessor<'a> { +impl<'a, EvmConfig> BlockExecutor for EVMProcessor<'a, EvmConfig> +where + EvmConfig: EvmEnvConfig, +{ fn execute( &mut self, block: &BlockWithSenders, diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index f9290d866bb4..c56b4ec84850 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -5,8 +5,8 @@ use crate::{ state_change::{apply_beacon_root_contract_call, post_block_balance_increments}, }; use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ - revm::env::{fill_cfg_and_block_env, fill_tx_env}, Address, Block, BlockNumber, BlockWithSenders, Bloom, ChainSpec, GotExpected, Hardfork, Header, PruneMode, PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, MINIMUM_PRUNING_DISTANCE, U256, @@ -21,6 +21,11 @@ use revm::{ }; use std::{sync::Arc, time::Instant}; +#[cfg(feature = "optimism")] +use reth_primitives::revm::env::fill_op_tx_env; +#[cfg(not(feature = "optimism"))] +use reth_primitives::revm::env::fill_tx_env; + #[cfg(not(feature = "optimism"))] use reth_primitives::revm::compat::into_reth_log; #[cfg(not(feature = "optimism"))] @@ -48,7 +53,7 @@ use tracing::{debug, trace}; // TODO: https://github.com/bluealloy/revm/pull/745 // #[derive(Debug)] #[allow(missing_debug_implementations)] -pub struct EVMProcessor<'a> { +pub struct EVMProcessor<'a, EvmConfig> { /// The configured chain-spec pub(crate) chain_spec: Arc, /// revm instance that contains database and env environment. @@ -74,16 +79,21 @@ pub struct EVMProcessor<'a> { pruning_address_filter: Option<(u64, Vec
)>, /// Execution stats pub(crate) stats: BlockExecutorStats, + /// The type that is able to configure the EVM environment. + _evm_config: EvmConfig, } -impl<'a> EVMProcessor<'a> { +impl<'a, EvmConfig> EVMProcessor<'a, EvmConfig> +where + EvmConfig: EvmEnvConfig, +{ /// Return chain spec. pub fn chain_spec(&self) -> &Arc { &self.chain_spec } /// Create a new pocessor with the given chain spec. - pub fn new(chain_spec: Arc) -> Self { + pub fn new(chain_spec: Arc, evm_config: EvmConfig) -> Self { let evm = EVM::new(); EVMProcessor { chain_spec, @@ -95,6 +105,7 @@ impl<'a> EVMProcessor<'a> { prune_modes: PruneModes::none(), pruning_address_filter: None, stats: BlockExecutorStats::default(), + _evm_config: evm_config, } } @@ -102,19 +113,21 @@ impl<'a> EVMProcessor<'a> { pub fn new_with_db( chain_spec: Arc, db: StateProviderDatabase, + evm_config: EvmConfig, ) -> Self { let state = State::builder() .with_database_boxed(Box::new(db)) .with_bundle_update() .without_state_clear() .build(); - EVMProcessor::new_with_state(chain_spec, state) + EVMProcessor::new_with_state(chain_spec, state, evm_config) } /// Create a new EVM processor with the given revm state. pub fn new_with_state( chain_spec: Arc, revm_state: StateDBBox<'a, ProviderError>, + evm_config: EvmConfig, ) -> Self { let mut evm = EVM::new(); evm.database(revm_state); @@ -128,6 +141,7 @@ impl<'a> EVMProcessor<'a> { prune_modes: PruneModes::none(), pruning_address_filter: None, stats: BlockExecutorStats::default(), + _evm_config: evm_config, } } @@ -157,7 +171,7 @@ impl<'a> EVMProcessor<'a> { self.db_mut().set_state_clear_flag(state_clear_flag); - fill_cfg_and_block_env( + EvmConfig::fill_cfg_and_block_env( &mut self.evm.env.cfg, &mut self.evm.env.block, &self.chain_spec, @@ -240,7 +254,7 @@ impl<'a> EVMProcessor<'a> { { let mut envelope_buf = Vec::with_capacity(transaction.length_without_header()); transaction.encode_enveloped(&mut envelope_buf); - fill_tx_env(&mut self.evm.env.tx, transaction, sender, envelope_buf.into()); + fill_op_tx_env(&mut self.evm.env.tx, transaction, sender, envelope_buf.into()); } let hash = transaction.hash(); @@ -374,7 +388,10 @@ impl<'a> EVMProcessor<'a> { /// Default Ethereum implementation of the [BlockExecutor] trait for the [EVMProcessor]. #[cfg(not(feature = "optimism"))] -impl<'a> BlockExecutor for EVMProcessor<'a> { +impl<'a, EvmConfig> BlockExecutor for EVMProcessor<'a, EvmConfig> +where + EvmConfig: EvmEnvConfig, +{ fn execute( &mut self, block: &BlockWithSenders, @@ -486,7 +503,10 @@ impl<'a> BlockExecutor for EVMProcessor<'a> { } } -impl<'a> PrunableBlockExecutor for EVMProcessor<'a> { +impl<'a, EvmConfig> PrunableBlockExecutor for EVMProcessor<'a, EvmConfig> +where + EvmConfig: EvmEnvConfig, +{ fn set_tip(&mut self, tip: BlockNumber) { self.tip = Some(tip); } @@ -536,6 +556,7 @@ pub fn verify_receipt<'a>( mod tests { use super::*; use reth_interfaces::provider::ProviderResult; + use reth_node_builder::EthEvmConfig; use reth_primitives::{ bytes, constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, @@ -664,7 +685,11 @@ mod tests { ); // execute invalid header (no parent beacon block root) - let mut executor = EVMProcessor::new_with_db(chain_spec, StateProviderDatabase::new(db)); + let mut executor = EVMProcessor::new_with_db( + chain_spec, + StateProviderDatabase::new(db), + EthEvmConfig::default(), + ); // attempt to execute a block without parent beacon block root, expect err let err = executor @@ -752,7 +777,11 @@ mod tests { .build(), ); - let mut executor = EVMProcessor::new_with_db(chain_spec, StateProviderDatabase::new(db)); + let mut executor = EVMProcessor::new_with_db( + chain_spec, + StateProviderDatabase::new(db), + EthEvmConfig::default(), + ); executor.init_env(&header, U256::ZERO); // get the env @@ -809,7 +838,11 @@ mod tests { .build(), ); - let mut executor = EVMProcessor::new_with_db(chain_spec, StateProviderDatabase::new(db)); + let mut executor = EVMProcessor::new_with_db( + chain_spec, + StateProviderDatabase::new(db), + EthEvmConfig::default(), + ); // construct the header for block one let header = Header { @@ -872,7 +905,11 @@ mod tests { let mut header = chain_spec.genesis_header(); - let mut executor = EVMProcessor::new_with_db(chain_spec, StateProviderDatabase::new(db)); + let mut executor = EVMProcessor::new_with_db( + chain_spec, + StateProviderDatabase::new(db), + EthEvmConfig::default(), + ); executor.init_env(&header, U256::ZERO); // attempt to execute the genesis block with non-zero parent beacon block root, expect err @@ -962,7 +999,11 @@ mod tests { ); // execute header - let mut executor = EVMProcessor::new_with_db(chain_spec, StateProviderDatabase::new(db)); + let mut executor = EVMProcessor::new_with_db( + chain_spec, + StateProviderDatabase::new(db), + EthEvmConfig::default(), + ); executor.init_env(&header, U256::ZERO); // ensure that the env is configured with a base fee diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 396dbe805a1c..afea11c3103d 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -26,6 +26,7 @@ reth-tasks.workspace = true reth-transaction-pool.workspace = true reth-rpc-types-compat.workspace = true reth-node-api.workspace = true +reth-node-builder.workspace = true # rpc/net jsonrpsee = { workspace = true, features = ["server"] } diff --git a/crates/rpc/rpc-builder/src/auth.rs b/crates/rpc/rpc-builder/src/auth.rs index 6bf01a883d93..5649784e3aa4 100644 --- a/crates/rpc/rpc-builder/src/auth.rs +++ b/crates/rpc/rpc-builder/src/auth.rs @@ -12,7 +12,7 @@ use jsonrpsee::{ Methods, }; use reth_network_api::{NetworkInfo, Peers}; -use reth_node_api::EngineTypes; +use reth_node_api::{EngineTypes, EvmEnvConfig}; use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt, StateProviderFactory, @@ -35,7 +35,7 @@ use std::{ /// Configure and launch a _standalone_ auth server with `engine` and a _new_ `eth` namespace. #[allow(clippy::too_many_arguments)] -pub async fn launch( +pub async fn launch( provider: Provider, pool: Pool, network: Network, @@ -43,6 +43,7 @@ pub async fn launch( engine_api: EngineApi, socket_addr: SocketAddr, secret: JwtSecret, + evm_config: EvmConfig, ) -> Result where Provider: BlockReaderIdExt @@ -59,10 +60,15 @@ where Tasks: TaskSpawner + Clone + 'static, EngineT: EngineTypes, EngineApi: EngineApiServer, + EvmConfig: EvmEnvConfig + 'static, { // spawn a new cache task - let eth_cache = - EthStateCache::spawn_with(provider.clone(), Default::default(), executor.clone()); + let eth_cache = EthStateCache::spawn_with( + provider.clone(), + Default::default(), + executor.clone(), + evm_config.clone(), + ); let gas_oracle = GasPriceOracle::new(provider.clone(), Default::default(), eth_cache.clone()); @@ -78,6 +84,7 @@ where Box::new(executor.clone()), BlockingTaskPool::build().expect("failed to build tracing pool"), fee_history_cache, + evm_config, ); let config = EthFilterConfig::default() .max_logs_per_response(DEFAULT_MAX_LOGS_PER_RESPONSE) @@ -88,8 +95,8 @@ where } /// Configure and launch a _standalone_ auth server with existing EthApi implementation. -pub async fn launch_with_eth_api( - eth_api: EthApi, +pub async fn launch_with_eth_api( + eth_api: EthApi, eth_filter: EthFilter, engine_api: EngineApi, socket_addr: SocketAddr, @@ -108,6 +115,7 @@ where Network: NetworkInfo + Peers + Clone + 'static, EngineT: EngineTypes, EngineApi: EngineApiServer, + EvmConfig: EvmEnvConfig + 'static, { // Configure the module and start the server. let mut module = RpcModule::new(()); diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index 8da3405368fd..d6fc9878501a 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -13,9 +13,9 @@ use serde::{Deserialize, Serialize}; /// All handlers for the `eth` namespace #[derive(Debug, Clone)] -pub struct EthHandlers { +pub struct EthHandlers { /// Main `eth_` request handler - pub api: EthApi, + pub api: EthApi, /// The async caching layer used by the eth handlers pub cache: EthStateCache, /// Polling based filter handler available on all transports diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index a85fce251d65..22c624c23c31 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -18,6 +18,7 @@ //! //! ``` //! use reth_network_api::{NetworkInfo, Peers}; +//! use reth_node_api::EvmEnvConfig; //! use reth_provider::{ //! AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, //! ChangeSetReader, EvmEnvProvider, StateProviderFactory, @@ -27,11 +28,12 @@ //! }; //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::TransactionPool; -//! pub async fn launch( +//! pub async fn launch( //! provider: Provider, //! pool: Pool, //! network: Network, //! events: Events, +//! evm_config: EvmConfig, //! ) where //! Provider: AccountReader //! + BlockReaderIdExt @@ -45,6 +47,7 @@ //! Pool: TransactionPool + Clone + 'static, //! Network: NetworkInfo + Peers + Clone + 'static, //! Events: CanonStateSubscriptions + Clone + 'static, +//! EvmConfig: EvmEnvConfig + 'static, //! { //! // configure the rpc module per transport //! let transports = TransportRpcModuleConfig::default().with_http(vec![ @@ -53,9 +56,15 @@ //! RethRpcModule::Eth, //! RethRpcModule::Web3, //! ]); -//! let transport_modules = -//! RpcModuleBuilder::new(provider, pool, network, TokioTaskExecutor::default(), events) -//! .build(transports); +//! let transport_modules = RpcModuleBuilder::new( +//! provider, +//! pool, +//! network, +//! TokioTaskExecutor::default(), +//! events, +//! evm_config, +//! ) +//! .build(transports); //! let handle = RpcServerConfig::default() //! .with_http(ServerBuilder::default()) //! .start(transport_modules) @@ -69,7 +78,7 @@ //! //! ``` //! use reth_network_api::{NetworkInfo, Peers}; -//! use reth_node_api::EngineTypes; +//! use reth_node_api::{EngineTypes, EvmEnvConfig}; //! use reth_provider::{ //! AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, //! ChangeSetReader, EvmEnvProvider, StateProviderFactory, @@ -83,12 +92,13 @@ //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::TransactionPool; //! use tokio::try_join; -//! pub async fn launch( +//! pub async fn launch( //! provider: Provider, //! pool: Pool, //! network: Network, //! events: Events, //! engine_api: EngineApi, +//! evm_config: EvmConfig, //! ) where //! Provider: AccountReader //! + BlockReaderIdExt @@ -104,6 +114,7 @@ //! Events: CanonStateSubscriptions + Clone + 'static, //! EngineApi: EngineApiServer, //! EngineT: EngineTypes, +//! EvmConfig: EvmEnvConfig + 'static, //! { //! // configure the rpc module per transport //! let transports = TransportRpcModuleConfig::default().with_http(vec![ @@ -112,8 +123,14 @@ //! RethRpcModule::Eth, //! RethRpcModule::Web3, //! ]); -//! let builder = -//! RpcModuleBuilder::new(provider, pool, network, TokioTaskExecutor::default(), events); +//! let builder = RpcModuleBuilder::new( +//! provider, +//! pool, +//! network, +//! TokioTaskExecutor::default(), +//! events, +//! evm_config, +//! ); //! //! // configure the server modules //! let (modules, auth_module, _registry) = @@ -150,7 +167,8 @@ use jsonrpsee::{ server::{IdProvider, Server, ServerHandle}, Methods, RpcModule, }; -use reth_node_api::EngineTypes; +use reth_node_api::{EngineTypes, EvmEnvConfig}; +use reth_node_builder::EthEvmConfig; use serde::{Deserialize, Serialize, Serializer}; use strum::{AsRefStr, EnumIter, EnumVariantNames, IntoStaticStr, ParseError, VariantNames}; use tower::layer::util::{Identity, Stack}; @@ -207,7 +225,8 @@ pub mod constants; mod metrics; /// Convenience function for starting a server in one step. -pub async fn launch( +#[allow(clippy::too_many_arguments)] +pub async fn launch( provider: Provider, pool: Pool, network: Network, @@ -215,6 +234,7 @@ pub async fn launch( server_config: impl Into, executor: Tasks, events: Events, + evm_config: EvmConfig, ) -> Result where Provider: BlockReaderIdExt @@ -230,10 +250,11 @@ where Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { let module_config = module_config.into(); let server_config = server_config.into(); - RpcModuleBuilder::new(provider, pool, network, executor, events) + RpcModuleBuilder::new(provider, pool, network, executor, events, evm_config) .build(module_config) .start_server(server_config) .await @@ -243,7 +264,7 @@ where /// /// This is the main entrypoint and the easiest way to configure an RPC server. #[derive(Debug, Clone)] -pub struct RpcModuleBuilder { +pub struct RpcModuleBuilder { /// The Provider type to when creating all rpc handlers provider: Provider, /// The Pool type to when creating all rpc handlers @@ -254,12 +275,14 @@ pub struct RpcModuleBuilder { executor: Tasks, /// Provides access to chain events, such as new blocks, required by pubsub. events: Events, + /// Defines how the EVM should be configured before execution. + evm_config: EvmConfig, } // === impl RpcBuilder === -impl - RpcModuleBuilder +impl + RpcModuleBuilder { /// Create a new instance of the builder pub fn new( @@ -268,26 +291,33 @@ impl network: Network, executor: Tasks, events: Events, + evm_config: EvmConfig, ) -> Self { - Self { provider, pool, network, executor, events } + Self { provider, pool, network, executor, events, evm_config } } /// Configure the provider instance. - pub fn with_provider

(self, provider: P) -> RpcModuleBuilder + pub fn with_provider

( + self, + provider: P, + ) -> RpcModuleBuilder where P: BlockReader + StateProviderFactory + EvmEnvProvider + 'static, { - let Self { pool, network, executor, events, .. } = self; - RpcModuleBuilder { provider, network, pool, executor, events } + let Self { pool, network, executor, events, evm_config, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } } /// Configure the transaction pool instance. - pub fn with_pool

(self, pool: P) -> RpcModuleBuilder + pub fn with_pool

( + self, + pool: P, + ) -> RpcModuleBuilder where P: TransactionPool + 'static, { - let Self { provider, network, executor, events, .. } = self; - RpcModuleBuilder { provider, network, pool, executor, events } + let Self { provider, network, executor, events, evm_config, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } } /// Configure a [NoopTransactionPool] instance. @@ -297,24 +327,28 @@ impl /// requires a [TransactionPool] implementation. pub fn with_noop_pool( self, - ) -> RpcModuleBuilder { - let Self { provider, executor, events, network, .. } = self; + ) -> RpcModuleBuilder { + let Self { provider, executor, events, network, evm_config, .. } = self; RpcModuleBuilder { provider, executor, events, network, + evm_config, pool: NoopTransactionPool::default(), } } /// Configure the network instance. - pub fn with_network(self, network: N) -> RpcModuleBuilder + pub fn with_network( + self, + network: N, + ) -> RpcModuleBuilder where N: NetworkInfo + Peers + 'static, { - let Self { provider, pool, executor, events, .. } = self; - RpcModuleBuilder { provider, network, pool, executor, events } + let Self { provider, pool, executor, events, evm_config, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } } /// Configure a [NoopNetwork] instance. @@ -322,21 +356,30 @@ impl /// Caution: This will configure a network API that does abosultely nothing. /// This is only intended for allow easier setup of namespaces that depend on the [EthApi] which /// requires a [NetworkInfo] implementation. - pub fn with_noop_network(self) -> RpcModuleBuilder { - let Self { provider, pool, executor, events, .. } = self; - RpcModuleBuilder { provider, pool, executor, events, network: NoopNetwork::default() } + pub fn with_noop_network( + self, + ) -> RpcModuleBuilder { + let Self { provider, pool, executor, events, evm_config, .. } = self; + RpcModuleBuilder { + provider, + pool, + executor, + events, + network: NoopNetwork::default(), + evm_config, + } } /// Configure the task executor to use for additional tasks. pub fn with_executor( self, executor: T, - ) -> RpcModuleBuilder + ) -> RpcModuleBuilder where T: TaskSpawner + 'static, { - let Self { pool, network, provider, events, .. } = self; - RpcModuleBuilder { provider, network, pool, executor, events } + let Self { pool, network, provider, events, evm_config, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } } /// Configure [TokioTaskExecutor] as the task executor to use for additional tasks. @@ -345,23 +388,45 @@ impl /// [TokioTaskExecutor]. pub fn with_tokio_executor( self, - ) -> RpcModuleBuilder { - let Self { pool, network, provider, events, .. } = self; - RpcModuleBuilder { provider, network, pool, events, executor: TokioTaskExecutor::default() } + ) -> RpcModuleBuilder { + let Self { pool, network, provider, events, evm_config, .. } = self; + RpcModuleBuilder { + provider, + network, + pool, + events, + executor: TokioTaskExecutor::default(), + evm_config, + } } /// Configure the event subscriber instance - pub fn with_events(self, events: E) -> RpcModuleBuilder + pub fn with_events( + self, + events: E, + ) -> RpcModuleBuilder where E: CanonStateSubscriptions + 'static, { - let Self { provider, pool, executor, network, .. } = self; - RpcModuleBuilder { provider, network, pool, executor, events } + let Self { provider, pool, executor, network, evm_config, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } + } + + /// Configure the evm configuration type + pub fn with_evm_config( + self, + evm_config: E, + ) -> RpcModuleBuilder + where + E: EvmEnvConfig + 'static, + { + let Self { provider, pool, executor, network, events, .. } = self; + RpcModuleBuilder { provider, network, pool, executor, events, evm_config } } } -impl - RpcModuleBuilder +impl + RpcModuleBuilder where Provider: BlockReaderIdExt + AccountReader @@ -376,6 +441,7 @@ where Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Configures all [RpcModule]s specific to the given [TransportRpcModuleConfig] which can be /// used to start the transport server(s). @@ -389,14 +455,14 @@ where ) -> ( TransportRpcModules, AuthRpcModule, - RethModuleRegistry, + RethModuleRegistry, ) where EngineApi: EngineApiServer, { let mut modules = TransportRpcModules::default(); - let Self { provider, pool, network, executor, events } = self; + let Self { provider, pool, network, executor, events, evm_config } = self; let TransportRpcModuleConfig { http, ws, ipc, config } = module_config.clone(); @@ -407,6 +473,7 @@ where executor, events, config.unwrap_or_default(), + evm_config, ); modules.config = module_config; @@ -445,9 +512,9 @@ where pub fn into_registry( self, config: RpcModuleConfig, - ) -> RethModuleRegistry { - let Self { provider, pool, network, executor, events } = self; - RethModuleRegistry::new(provider, pool, network, executor, events, config) + ) -> RethModuleRegistry { + let Self { provider, pool, network, executor, events, evm_config } = self; + RethModuleRegistry::new(provider, pool, network, executor, events, config, evm_config) } /// Configures all [RpcModule]s specific to the given [TransportRpcModuleConfig] which can be @@ -457,7 +524,7 @@ where pub fn build(self, module_config: TransportRpcModuleConfig) -> TransportRpcModules<()> { let mut modules = TransportRpcModules::default(); - let Self { provider, pool, network, executor, events } = self; + let Self { provider, pool, network, executor, events, evm_config } = self; if !module_config.is_empty() { let TransportRpcModuleConfig { http, ws, ipc, config } = module_config.clone(); @@ -469,6 +536,7 @@ where executor, events, config.unwrap_or_default(), + evm_config, ); modules.config = module_config; @@ -481,9 +549,9 @@ where } } -impl Default for RpcModuleBuilder<(), (), (), (), ()> { +impl Default for RpcModuleBuilder<(), (), (), (), (), EthEvmConfig> { fn default() -> Self { - RpcModuleBuilder::new((), (), (), (), ()) + RpcModuleBuilder::new((), (), (), (), (), EthEvmConfig::default()) } } @@ -639,7 +707,8 @@ impl RpcModuleSelection { /// Note: This will always create new instance of the module handlers and is therefor only /// recommended for launching standalone transports. If multiple transports need to be /// configured it's recommended to use the [RpcModuleBuilder]. - pub fn standalone_module( + #[allow(clippy::too_many_arguments)] + pub fn standalone_module( &self, provider: Provider, pool: Pool, @@ -647,6 +716,7 @@ impl RpcModuleSelection { executor: Tasks, events: Events, config: RpcModuleConfig, + evm_config: EvmConfig, ) -> RpcModule<()> where Provider: BlockReaderIdExt @@ -662,9 +732,10 @@ impl RpcModuleSelection { Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { let mut registry = - RethModuleRegistry::new(provider, pool, network, executor, events, config); + RethModuleRegistry::new(provider, pool, network, executor, events, config, evm_config); registry.module_for(self) } @@ -849,16 +920,18 @@ impl Serialize for RethRpcModule { /// A Helper type the holds instances of the configured modules. #[derive(Debug)] -pub struct RethModuleRegistry { +pub struct RethModuleRegistry { provider: Provider, pool: Pool, network: Network, executor: Tasks, events: Events, + /// Defines how to configure the EVM before execution. + evm_config: EvmConfig, /// Additional settings for handlers. config: RpcModuleConfig, /// Holds a clone of all the eth namespace handlers - eth: Option>, + eth: Option>, /// to put trace calls behind semaphore blocking_pool_guard: BlockingTaskGuard, /// Contains the [Methods] of a module @@ -867,8 +940,8 @@ pub struct RethModuleRegistry { // === impl RethModuleRegistry === -impl - RethModuleRegistry +impl + RethModuleRegistry { /// Creates a new, empty instance. pub fn new( @@ -878,11 +951,13 @@ impl executor: Tasks, events: Events, config: RpcModuleConfig, + evm_config: EvmConfig, ) -> Self { Self { provider, pool, network, + evm_config, eth: None, executor, modules: Default::default(), @@ -927,8 +1002,8 @@ impl } } -impl - RethModuleRegistry +impl + RethModuleRegistry where Network: NetworkInfo + Peers + Clone + 'static, { @@ -957,8 +1032,8 @@ where } } -impl - RethModuleRegistry +impl + RethModuleRegistry where Provider: BlockReaderIdExt + AccountReader @@ -973,6 +1048,7 @@ where Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, Events: CanonStateSubscriptions + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Register Eth Namespace /// @@ -1185,13 +1261,14 @@ where /// - [FeeHistoryCache] fn with_eth(&mut self, f: F) -> R where - F: FnOnce(&EthHandlers) -> R, + F: FnOnce(&EthHandlers) -> R, { if self.eth.is_none() { let cache = EthStateCache::spawn_with( self.provider.clone(), self.config.eth.cache.clone(), self.executor.clone(), + self.evm_config.clone(), ); let gas_oracle = GasPriceOracle::new( self.provider.clone(), @@ -1234,6 +1311,7 @@ where executor.clone(), blocking_task_pool.clone(), fee_history_cache, + self.evm_config.clone(), ); let filter = EthFilter::new( self.provider.clone(), @@ -1262,7 +1340,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn eth_handlers(&mut self) -> EthHandlers { + pub fn eth_handlers(&mut self) -> EthHandlers { self.with_eth(|handlers| handlers.clone()) } @@ -1273,7 +1351,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. - pub fn eth_api(&mut self) -> EthApi { + pub fn eth_api(&mut self) -> EthApi { self.with_eth(|handlers| handlers.api.clone()) } @@ -1282,7 +1360,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn trace_api(&mut self) -> TraceApi> { + pub fn trace_api(&mut self) -> TraceApi> { let eth = self.eth_handlers(); TraceApi::new(self.provider.clone(), eth.api, self.blocking_pool_guard.clone()) } @@ -1292,7 +1370,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn bundle_api(&mut self) -> EthBundle> { + pub fn bundle_api(&mut self) -> EthBundle> { let eth_api = self.eth_api(); EthBundle::new(eth_api, self.blocking_pool_guard.clone()) } @@ -1302,7 +1380,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn otterscan_api(&mut self) -> OtterscanApi> { + pub fn otterscan_api(&mut self) -> OtterscanApi> { let eth_api = self.eth_api(); OtterscanApi::new(eth_api) } @@ -1312,7 +1390,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn debug_api(&mut self) -> DebugApi> { + pub fn debug_api(&mut self) -> DebugApi> { let eth_api = self.eth_api(); DebugApi::new(self.provider.clone(), eth_api, self.blocking_pool_guard.clone()) } @@ -1322,7 +1400,7 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [Self::eth_api] - pub fn net_api(&mut self) -> NetApi> { + pub fn net_api(&mut self) -> NetApi> { let eth_api = self.eth_api(); NetApi::new(self.network.clone(), eth_api) } diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs index 239900fd3cc4..a16b8dbb13a7 100644 --- a/crates/rpc/rpc-builder/tests/it/utils.rs +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -1,6 +1,6 @@ use reth_beacon_consensus::BeaconConsensusEngineHandle; use reth_network_api::noop::NoopNetwork; -use reth_node_builder::EthEngineTypes; +use reth_node_builder::{EthEngineTypes, EthEvmConfig}; use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_primitives::MAINNET; use reth_provider::test_utils::{NoopProvider, TestCanonStateSubscriptions}; @@ -99,6 +99,7 @@ pub fn test_rpc_builder() -> RpcModuleBuilder< NoopNetwork, TokioTaskExecutor, TestCanonStateSubscriptions, + EthEvmConfig, > { RpcModuleBuilder::default() .with_provider(NoopProvider::default()) @@ -106,4 +107,5 @@ pub fn test_rpc_builder() -> RpcModuleBuilder< .with_network(NoopNetwork::default()) .with_executor(TokioTaskExecutor::default()) .with_events(TestCanonStateSubscriptions::default()) + .with_evm_config(EthEvmConfig::default()) } diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 9f95d7fa3469..532e0005d31c 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -26,8 +26,8 @@ reth-revm = { workspace = true, features = ["js-tracer"] } reth-tasks.workspace = true reth-consensus-common.workspace = true reth-rpc-types-compat.workspace = true -lazy_static = "*" revm-inspectors.workspace = true +reth-node-api.workspace = true # eth alloy-rlp.workspace = true @@ -76,12 +76,14 @@ tracing-futures = "0.2" schnellru.workspace = true futures.workspace = true derive_more = "0.99" +lazy_static = "*" [dev-dependencies] jsonrpsee = { workspace = true, features = ["client"] } assert_matches.workspace = true tempfile.workspace = true reth-interfaces = { workspace = true, features = ["test-utils"] } +reth-node-builder.workspace = true [features] optimism = [ diff --git a/crates/rpc/rpc/src/eth/api/block.rs b/crates/rpc/rpc/src/eth/api/block.rs index e619d210a449..e3c962c477bb 100644 --- a/crates/rpc/rpc/src/eth/api/block.rs +++ b/crates/rpc/rpc/src/eth/api/block.rs @@ -1,7 +1,5 @@ //! Contains RPC handler implementations specific to blocks. -use std::sync::Arc; - use crate::{ eth::{ api::transactions::build_transaction_receipt_with_block_receipts, @@ -10,20 +8,21 @@ use crate::{ EthApi, }; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{BlockId, TransactionMeta}; - use reth_provider::{BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderFactory}; use reth_rpc_types::{Index, RichBlock, TransactionReceipt}; - use reth_rpc_types_compat::block::{from_block, uncle_block_from_header}; use reth_transaction_pool::TransactionPool; +use std::sync::Arc; -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Pool: TransactionPool + Clone + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Returns the uncle headers of the given block /// diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index 661d14d4c690..c11b7d202114 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -13,6 +13,7 @@ use crate::{ EthApi, }; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{revm::env::tx_env_with_recovered, BlockId, BlockNumberOrTag, Bytes, U256}; use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProvider, StateProviderFactory, @@ -33,12 +34,13 @@ use tracing::trace; const MIN_TRANSACTION_GAS: u64 = 21_000u64; const MIN_CREATE_GAS: u64 = 53_000u64; -impl EthApi +impl EthApi where Pool: TransactionPool + Clone + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Estimate gas needed for execution of the `request` at the [BlockId]. pub async fn estimate_gas_at( diff --git a/crates/rpc/rpc/src/eth/api/fees.rs b/crates/rpc/rpc/src/eth/api/fees.rs index f8d435871226..de62a3d46d44 100644 --- a/crates/rpc/rpc/src/eth/api/fees.rs +++ b/crates/rpc/rpc/src/eth/api/fees.rs @@ -8,18 +8,20 @@ use crate::{ EthApi, }; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{basefee::calculate_next_block_base_fee, BlockNumberOrTag, U256}; use reth_provider::{BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderFactory}; use reth_rpc_types::FeeHistory; use reth_transaction_pool::TransactionPool; use tracing::debug; -impl EthApi +impl EthApi where Pool: TransactionPool + Clone + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Returns a suggestion for a gas price for legacy transactions. /// diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 7a64a1930cfa..124411da142a 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -15,6 +15,7 @@ use crate::eth::{ use async_trait::async_trait; use reth_interfaces::RethResult; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv}, Address, BlockId, BlockNumberOrTag, ChainInfo, SealedBlockWithSenders, B256, U256, U64, @@ -82,12 +83,12 @@ pub trait EthApiSpec: EthTransactions + Send + Sync { /// are implemented separately in submodules. The rpc handler implementation can then delegate to /// the main impls. This way [`EthApi`] is not limited to [`jsonrpsee`] and can be used standalone /// or in other network handlers (for example ipc). -pub struct EthApi { +pub struct EthApi { /// All nested fields bundled together. - inner: Arc>, + inner: Arc>, } -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider, { @@ -102,6 +103,7 @@ where gas_cap: impl Into, blocking_task_pool: BlockingTaskPool, fee_history_cache: FeeHistoryCache, + evm_config: EvmConfig, ) -> Self { Self::with_spawner( provider, @@ -113,6 +115,7 @@ where Box::::default(), blocking_task_pool, fee_history_cache, + evm_config, ) } @@ -128,6 +131,7 @@ where task_spawner: Box, blocking_task_pool: BlockingTaskPool, fee_history_cache: FeeHistoryCache, + evm_config: EvmConfig, ) -> Self { // get the block number of the latest block let latest_block = provider @@ -150,6 +154,7 @@ where pending_block: Default::default(), blocking_task_pool, fee_history_cache, + evm_config, #[cfg(feature = "optimism")] http_client: reqwest::Client::builder().use_rustls_tls().build().unwrap(), }; @@ -215,7 +220,7 @@ where // === State access helpers === -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, @@ -252,12 +257,13 @@ where } } -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Pool: TransactionPool + Clone + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + Clone + 'static, { /// Configures the [CfgEnv] and [BlockEnv] for the pending block /// @@ -293,7 +299,12 @@ where let mut block_env = BlockEnv::default(); // Note: for the PENDING block we assume it is past the known merge block and thus this will // not fail when looking up the total difficulty value for the blockenv. - self.provider().fill_env_with_header(&mut cfg, &mut block_env, origin.header())?; + self.provider().fill_env_with_header( + &mut cfg, + &mut block_env, + origin.header(), + self.inner.evm_config.clone(), + )?; Ok(PendingBlockEnv { cfg, block_env, origin }) } @@ -347,25 +358,28 @@ where } } -impl std::fmt::Debug for EthApi { +impl std::fmt::Debug + for EthApi +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("EthApi").finish_non_exhaustive() } } -impl Clone for EthApi { +impl Clone for EthApi { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner) } } } #[async_trait] -impl EthApiSpec for EthApi +impl EthApiSpec for EthApi where Pool: TransactionPool + Clone + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Returns the current ethereum protocol version. /// @@ -443,7 +457,7 @@ impl From for u64 { } /// Container type `EthApi` -struct EthApiInner { +struct EthApiInner { /// The transaction pool. pool: Pool, /// The provider that can interact with the chain. @@ -468,6 +482,8 @@ struct EthApiInner { blocking_task_pool: BlockingTaskPool, /// Cache for block fees history fee_history_cache: FeeHistoryCache, + /// The type that defines how to configure the EVM + evm_config: EvmConfig, /// An http client for communicating with sequencers. #[cfg(feature = "optimism")] http_client: reqwest::Client, diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index 6d20f79123ab..6e97eaa5714d 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -12,6 +12,7 @@ use crate::{ }; use jsonrpsee::core::RpcResult as Result; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ serde_helper::{num::U64HexOrNumber, JsonStorageKey}, Address, BlockId, BlockNumberOrTag, Bytes, B256, B64, U256, U64, @@ -31,7 +32,7 @@ use serde_json::Value; use tracing::trace; #[async_trait::async_trait] -impl EthApiServer for EthApi +impl EthApiServer for EthApi where Self: EthApiSpec + EthTransactions, Pool: TransactionPool + 'static, @@ -44,6 +45,7 @@ where + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Handler for: `eth_protocolVersion` async fn protocol_version(&self) -> Result { @@ -402,6 +404,7 @@ mod tests { use jsonrpsee::types::error::INVALID_PARAMS_CODE; use reth_interfaces::test_utils::{generators, generators::Rng}; use reth_network_api::noop::NoopNetwork; + use reth_node_builder::EthEvmConfig; use reth_primitives::{ basefee::calculate_next_block_base_fee, constants::ETHEREUM_BLOCK_GAS_LIMIT, BaseFeeParams, Block, BlockNumberOrTag, Header, TransactionSigned, B256, U256, @@ -425,9 +428,9 @@ mod tests { + 'static, >( provider: P, - ) -> EthApi { - let cache = EthStateCache::spawn(provider.clone(), Default::default()); - + ) -> EthApi { + let evm_config = EthEvmConfig::default(); + let cache = EthStateCache::spawn(provider.clone(), Default::default(), evm_config); let fee_history_cache = FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()); @@ -440,6 +443,7 @@ mod tests { ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), fee_history_cache, + evm_config, ) } @@ -449,7 +453,7 @@ mod tests { mut oldest_block: Option, block_count: u64, mock_provider: MockEthProvider, - ) -> (EthApi, Vec, Vec) { + ) -> (EthApi, Vec, Vec) { let mut rng = generators::rng(); // Build mock data @@ -534,7 +538,7 @@ mod tests { /// Invalid block range #[tokio::test] async fn test_fee_history_empty() { - let response = as EthApiServer>::fee_history( + let response = as EthApiServer>::fee_history( &build_test_eth_api(NoopProvider::default()), 1.into(), BlockNumberOrTag::Latest, @@ -556,7 +560,7 @@ mod tests { let (eth_api, _, _) = prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default()); - let response = as EthApiServer>::fee_history( + let response = as EthApiServer>::fee_history( ð_api, (newest_block + 1).into(), newest_block.into(), @@ -579,7 +583,7 @@ mod tests { let (eth_api, _, _) = prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default()); - let response = as EthApiServer>::fee_history( + let response = as EthApiServer>::fee_history( ð_api, (1).into(), (newest_block + 1000).into(), @@ -602,7 +606,7 @@ mod tests { let (eth_api, _, _) = prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default()); - let response = as EthApiServer>::fee_history( + let response = as EthApiServer>::fee_history( ð_api, (0).into(), (newest_block).into(), diff --git a/crates/rpc/rpc/src/eth/api/sign.rs b/crates/rpc/rpc/src/eth/api/sign.rs index 4d1e9bda058a..97d9255c4f48 100644 --- a/crates/rpc/rpc/src/eth/api/sign.rs +++ b/crates/rpc/rpc/src/eth/api/sign.rs @@ -12,7 +12,7 @@ use reth_primitives::{Address, Bytes}; use serde_json::Value; use std::ops::Deref; -impl EthApi { +impl EthApi { pub(crate) async fn sign(&self, account: Address, message: Bytes) -> EthResult { let signer = self.find_signer(&account)?; let signature = signer.sign(account, &message).await?; diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index a63e08126d52..9bd219b7bd96 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -4,6 +4,7 @@ use crate::{ eth::error::{EthApiError, EthResult, RpcInvalidTransactionError}, EthApi, }; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ serde_helper::JsonStorageKey, Address, BlockId, BlockNumberOrTag, Bytes, B256, U256, }; @@ -14,12 +15,13 @@ use reth_rpc_types::EIP1186AccountProofResponse; use reth_rpc_types_compat::proof::from_primitive_account_proof; use reth_transaction_pool::{PoolTransaction, TransactionPool}; -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Pool: TransactionPool + Clone + 'static, Network: Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { pub(crate) fn get_code(&self, address: Address, block_id: Option) -> EthResult { let state = self.state_at_block_id_or_latest(block_id)?; @@ -131,6 +133,7 @@ mod tests { }, BlockingTaskPool, }; + use reth_node_builder::EthEvmConfig; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, StorageKey, StorageValue}; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider}; use reth_transaction_pool::test_utils::testing_pool; @@ -140,8 +143,9 @@ mod tests { async fn test_storage() { // === Noop === let pool = testing_pool(); + let evm_config = EthEvmConfig::default(); - let cache = EthStateCache::spawn(NoopProvider::default(), Default::default()); + let cache = EthStateCache::spawn(NoopProvider::default(), Default::default(), evm_config); let eth_api = EthApi::new( NoopProvider::default(), pool.clone(), @@ -151,6 +155,7 @@ mod tests { ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()), + evm_config, ); let address = Address::random(); let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap(); @@ -164,7 +169,7 @@ mod tests { let account = ExtendedAccount::new(0, U256::ZERO).extend_storage(storage); mock_provider.add_account(address, account); - let cache = EthStateCache::spawn(mock_provider.clone(), Default::default()); + let cache = EthStateCache::spawn(mock_provider.clone(), Default::default(), evm_config); let eth_api = EthApi::new( mock_provider.clone(), pool, @@ -174,6 +179,7 @@ mod tests { ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()), + evm_config, ); let storage_key: U256 = storage_key.into(); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index db91ad806f58..659c7032831e 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -14,6 +14,7 @@ use crate::{ }; use async_trait::async_trait; use reth_network_api::NetworkInfo; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ eip4844::calc_blob_gasprice, revm::env::{fill_block_env_with_coinbase, tx_env_with_recovered}, @@ -307,12 +308,14 @@ pub trait EthTransactions: Send + Sync { } #[async_trait] -impl EthTransactions for EthApi +impl EthTransactions + for EthApi where Pool: TransactionPool + Clone + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { fn call_gas_limit(&self) -> u64 { self.inner.gas_cap @@ -869,12 +872,13 @@ where // === impl EthApi === -impl EthApi +impl EthApi where Pool: TransactionPool + Clone + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { /// Spawns the given closure on a new blocking tracing task async fn spawn_tracing_task_with(&self, f: F) -> EthResult @@ -891,7 +895,7 @@ where } } -impl EthApi +impl EthApi where Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, @@ -1017,12 +1021,13 @@ where } } -impl EthApi +impl EthApi where Pool: TransactionPool + 'static, Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, + EvmConfig: EvmEnvConfig + 'static, { pub(crate) fn sign_request( &self, @@ -1271,6 +1276,7 @@ mod tests { BlockingTaskPool, EthApi, }; use reth_network_api::noop::NoopNetwork; + use reth_node_builder::EthEvmConfig; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, hex_literal::hex, Bytes}; use reth_provider::test_utils::NoopProvider; use reth_transaction_pool::{test_utils::testing_pool, TransactionPool}; @@ -1282,7 +1288,8 @@ mod tests { let pool = testing_pool(); - let cache = EthStateCache::spawn(noop_provider, Default::default()); + let evm_config = EthEvmConfig::default(); + let cache = EthStateCache::spawn(noop_provider, Default::default(), evm_config); let fee_history_cache = FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default()); let eth_api = EthApi::new( @@ -1294,6 +1301,7 @@ mod tests { ETHEREUM_BLOCK_GAS_LIMIT, BlockingTaskPool::build().expect("failed to build tracing pool"), fee_history_cache, + evm_config, ); // https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d diff --git a/crates/rpc/rpc/src/eth/cache/mod.rs b/crates/rpc/rpc/src/eth/cache/mod.rs index 05c16868293c..c2e7be58be3c 100644 --- a/crates/rpc/rpc/src/eth/cache/mod.rs +++ b/crates/rpc/rpc/src/eth/cache/mod.rs @@ -2,6 +2,7 @@ use futures::{future::Either, Stream, StreamExt}; use reth_interfaces::provider::{ProviderError, ProviderResult}; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ Block, BlockHashOrNumber, BlockWithSenders, Receipt, SealedBlock, SealedBlockWithSenders, TransactionSigned, TransactionSignedEcRecovered, B256, @@ -68,14 +69,15 @@ pub struct EthStateCache { impl EthStateCache { /// Creates and returns both [EthStateCache] frontend and the memory bound service. - fn create( + fn create( provider: Provider, action_task_spawner: Tasks, + evm_config: EvmConfig, max_blocks: u32, max_receipts: u32, max_envs: u32, max_concurrent_db_operations: usize, - ) -> (Self, EthStateCacheService) { + ) -> (Self, EthStateCacheService) { let (to_service, rx) = unbounded_channel(); let service = EthStateCacheService { provider, @@ -86,6 +88,7 @@ impl EthStateCache { action_rx: UnboundedReceiverStream::new(rx), action_task_spawner, rate_limiter: Arc::new(Semaphore::new(max_concurrent_db_operations)), + evm_config, }; let cache = EthStateCache { to_service }; (cache, service) @@ -95,31 +98,39 @@ impl EthStateCache { /// [tokio::spawn]. /// /// See also [Self::spawn_with] - pub fn spawn(provider: Provider, config: EthStateCacheConfig) -> Self + pub fn spawn( + provider: Provider, + config: EthStateCacheConfig, + evm_config: EvmConfig, + ) -> Self where Provider: StateProviderFactory + BlockReader + EvmEnvProvider + Clone + Unpin + 'static, + EvmConfig: EvmEnvConfig + 'static, { - Self::spawn_with(provider, config, TokioTaskExecutor::default()) + Self::spawn_with(provider, config, TokioTaskExecutor::default(), evm_config) } /// Creates a new async LRU backed cache service task and spawns it to a new task via the given /// spawner. /// /// The cache is memory limited by the given max bytes values. - pub fn spawn_with( + pub fn spawn_with( provider: Provider, config: EthStateCacheConfig, executor: Tasks, + evm_config: EvmConfig, ) -> Self where Provider: StateProviderFactory + BlockReader + EvmEnvProvider + Clone + Unpin + 'static, Tasks: TaskSpawner + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { let EthStateCacheConfig { max_blocks, max_receipts, max_envs, max_concurrent_db_requests } = config; let (this, service) = Self::create( provider, executor.clone(), + evm_config, max_blocks, max_receipts, max_envs, @@ -267,6 +278,7 @@ impl EthStateCache { pub(crate) struct EthStateCacheService< Provider, Tasks, + EvmConfig, LimitBlocks = ByLength, LimitReceipts = ByLength, LimitEnvs = ByLength, @@ -291,12 +303,15 @@ pub(crate) struct EthStateCacheService< action_task_spawner: Tasks, /// Rate limiter rate_limiter: Arc, + /// The type that determines how to configure the EVM. + evm_config: EvmConfig, } -impl EthStateCacheService +impl EthStateCacheService where Provider: StateProviderFactory + BlockReader + EvmEnvProvider + Clone + Unpin + 'static, Tasks: TaskSpawner + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { fn on_new_block(&mut self, block_hash: B256, res: ProviderResult>) { if let Some(queued) = self.full_block_cache.remove(&block_hash) { @@ -347,10 +362,11 @@ where } } -impl Future for EthStateCacheService +impl Future for EthStateCacheService where Provider: StateProviderFactory + BlockReader + EvmEnvProvider + Clone + Unpin + 'static, Tasks: TaskSpawner + Clone + 'static, + EvmConfig: EvmEnvConfig + 'static, { type Output = (); @@ -456,13 +472,19 @@ where let provider = this.provider.clone(); let action_tx = this.action_tx.clone(); let rate_limiter = this.rate_limiter.clone(); + let evm_config = this.evm_config.clone(); this.action_task_spawner.spawn_blocking(Box::pin(async move { // Acquire permit let _permit = rate_limiter.acquire().await; let mut cfg = CfgEnv::default(); let mut block_env = BlockEnv::default(); let res = provider - .fill_env_at(&mut cfg, &mut block_env, block_hash.into()) + .fill_env_at( + &mut cfg, + &mut block_env, + block_hash.into(), + evm_config, + ) .map(|_| (cfg, block_env)); let _ = action_tx.send(CacheAction::EnvResult { block_hash, diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 7524f066205f..931240a3e360 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -1,14 +1,20 @@ //! utilities for working with revm use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError}; +#[cfg(feature = "optimism")] +use reth_primitives::revm::env::fill_op_tx_env; +#[cfg(not(feature = "optimism"))] +use reth_primitives::revm::env::fill_tx_env; use reth_primitives::{ - revm::env::{fill_tx_env, fill_tx_env_with_recovered}, - Address, TransactionSigned, TransactionSignedEcRecovered, TxHash, B256, U256, + revm::env::fill_tx_env_with_recovered, Address, TransactionSigned, + TransactionSignedEcRecovered, TxHash, B256, U256, }; use reth_rpc_types::{ state::{AccountOverride, StateOverride}, BlockOverrides, CallRequest, }; +#[cfg(feature = "optimism")] +use revm::primitives::{Bytes, OptimismFields}; use revm::{ db::CacheDB, precompile::{Precompiles, SpecId as PrecompilesSpecId}, @@ -21,9 +27,6 @@ use revm_primitives::{ }; use tracing::trace; -#[cfg(feature = "optimism")] -use revm::primitives::{Bytes, OptimismFields}; - /// Helper type that bundles various overrides for EVM Execution. /// /// By `Default`, no overrides are included. @@ -104,7 +107,7 @@ impl FillableTransaction for TransactionSigned { { let mut envelope_buf = Vec::with_capacity(self.length_without_header()); self.encode_enveloped(&mut envelope_buf); - fill_tx_env(tx_env, self, signer, envelope_buf.into()); + fill_op_tx_env(tx_env, self, signer, envelope_buf.into()); } Ok(()) } diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index d3ddca5be3ae..20a73e87c5aa 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -62,6 +62,7 @@ reth-db = { workspace = true, features = ["test-utils", "mdbx"] } reth-interfaces = { workspace = true, features = ["test-utils"] } reth-downloaders.workspace = true reth-eth-wire.workspace = true # TODO(onbjerg): We only need this for [BlockBody] +reth-node-builder.workspace = true reth-blockchain-tree.workspace = true reth-revm.workspace = true reth-trie = { workspace = true, features = ["test-utils"] } diff --git a/crates/stages/src/lib.rs b/crates/stages/src/lib.rs index 7c8ed234b219..23254e4abc23 100644 --- a/crates/stages/src/lib.rs +++ b/crates/stages/src/lib.rs @@ -22,6 +22,7 @@ //! # use reth_stages::Pipeline; //! # use reth_stages::sets::DefaultStages; //! # use tokio::sync::watch; +//! # use reth_node_builder::EthEvmConfig; //! # use reth_provider::ProviderFactory; //! # use reth_provider::HeaderSyncMode; //! # use reth_provider::test_utils::create_test_provider_factory; @@ -39,7 +40,7 @@ //! # provider_factory.clone() //! # ); //! # let (tip_tx, tip_rx) = watch::channel(B256::default()); -//! # let executor_factory = EvmProcessorFactory::new(chain_spec.clone()); +//! # let executor_factory = EvmProcessorFactory::new(chain_spec.clone(), EthEvmConfig::default()); //! // Create a pipeline that can fully sync //! # let pipeline = //! Pipeline::builder() diff --git a/crates/stages/src/sets.rs b/crates/stages/src/sets.rs index 1f3d49e390b2..d4b7a8c61759 100644 --- a/crates/stages/src/sets.rs +++ b/crates/stages/src/sets.rs @@ -14,9 +14,10 @@ //! # use reth_stages::sets::{OfflineStages}; //! # use reth_revm::EvmProcessorFactory; //! # use reth_primitives::MAINNET; +//! # use reth_node_builder::EthEvmConfig; //! # use reth_provider::test_utils::create_test_provider_factory; //! -//! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone()); +//! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone(), EthEvmConfig::default()); //! # let provider_factory = create_test_provider_factory(); //! // Build a pipeline with all offline stages. //! # let pipeline = Pipeline::builder().add_stages(OfflineStages::new(executor_factory)).build(provider_factory); @@ -26,9 +27,10 @@ //! # use reth_stages::Pipeline; //! # use reth_stages::{StageSet, sets::OfflineStages}; //! # use reth_revm::EvmProcessorFactory; +//! # use reth_node_builder::EthEvmConfig; //! # use reth_primitives::MAINNET; //! // Build a pipeline with all offline stages and a custom stage at the end. -//! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone()); +//! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone(), EthEvmConfig::default()); //! Pipeline::builder() //! .add_stages( //! OfflineStages::new(executor_factory).builder().add_stage(MyCustomStage) diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 42225b1a093a..84ecacb68562 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -502,6 +502,7 @@ mod tests { use assert_matches::assert_matches; use reth_db::{models::AccountBeforeTx, test_utils::create_test_rw_db}; use reth_interfaces::executor::BlockValidationError; + use reth_node_builder::EthEvmConfig; use reth_primitives::{ address, hex_literal::hex, keccak256, stage::StageUnitCheckpoint, Account, Bytecode, ChainSpecBuilder, PruneModes, SealedBlock, StorageEntry, B256, MAINNET, U256, @@ -510,10 +511,11 @@ mod tests { use reth_revm::EvmProcessorFactory; use std::sync::Arc; - fn stage() -> ExecutionStage { - let executor_factory = EvmProcessorFactory::new(Arc::new( - ChainSpecBuilder::mainnet().berlin_activated().build(), - )); + fn stage() -> ExecutionStage> { + let executor_factory = EvmProcessorFactory::new( + Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()), + EthEvmConfig::default(), + ); ExecutionStage::new( executor_factory, ExecutionStageThresholds { @@ -701,7 +703,7 @@ mod tests { provider.commit().unwrap(); let provider = factory.provider_rw().unwrap(); - let mut execution_stage: ExecutionStage = stage(); + let mut execution_stage: ExecutionStage> = stage(); let output = execution_stage.execute(&provider, input).unwrap(); provider.commit().unwrap(); assert_matches!(output, ExecOutput { diff --git a/crates/stages/src/stages/mod.rs b/crates/stages/src/stages/mod.rs index 29e6a1c63450..cf6f5480b38f 100644 --- a/crates/stages/src/stages/mod.rs +++ b/crates/stages/src/stages/mod.rs @@ -55,6 +55,7 @@ mod tests { AccountHistory, DatabaseEnv, }; use reth_interfaces::test_utils::generators::{self, random_block}; + use reth_node_builder::EthEvmConfig; use reth_primitives::{ address, hex_literal::hex, keccak256, Account, Bytecode, ChainSpecBuilder, PruneMode, PruneModes, SealedBlock, U256, @@ -128,9 +129,10 @@ mod tests { // Check execution and create receipts and changesets according to the pruning // configuration let mut execution_stage = ExecutionStage::new( - EvmProcessorFactory::new(Arc::new( - ChainSpecBuilder::mainnet().berlin_activated().build(), - )), + EvmProcessorFactory::new( + Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()), + EthEvmConfig::default(), + ), ExecutionStageThresholds { max_blocks: Some(100), max_changes: None, diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 64ff9742a4c9..7e87f7fa3499 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -18,6 +18,7 @@ reth-interfaces.workspace = true reth-db.workspace = true reth-trie.workspace = true reth-nippy-jar.workspace = true +reth-node-api.workspace = true revm.workspace = true diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index fbe9b0ebdfc0..4c5358e2e9c4 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -11,6 +11,7 @@ use crate::{ }; use reth_db::{database::Database, init_db, models::StoredBlockBodyIndices, DatabaseEnv}; use reth_interfaces::{provider::ProviderResult, RethError, RethResult}; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ snapshot::HighestSnapshots, stage::{StageCheckpoint, StageId}, @@ -437,22 +438,30 @@ impl StageCheckpointReader for ProviderFactory { } impl EvmEnvProvider for ProviderFactory { - fn fill_env_at( + fn fill_env_at( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, at: BlockHashOrNumber, - ) -> ProviderResult<()> { - self.provider()?.fill_env_at(cfg, block_env, at) + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.provider()?.fill_env_at(cfg, block_env, at, evm_config) } - fn fill_env_with_header( + fn fill_env_with_header( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, header: &Header, - ) -> ProviderResult<()> { - self.provider()?.fill_env_with_header(cfg, block_env, header) + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.provider()?.fill_env_with_header(cfg, block_env, header, evm_config) } fn fill_block_env_at( @@ -471,12 +480,28 @@ impl EvmEnvProvider for ProviderFactory { self.provider()?.fill_block_env_with_header(block_env, header) } - fn fill_cfg_env_at(&self, cfg: &mut CfgEnv, at: BlockHashOrNumber) -> ProviderResult<()> { - self.provider()?.fill_cfg_env_at(cfg, at) + fn fill_cfg_env_at( + &self, + cfg: &mut CfgEnv, + at: BlockHashOrNumber, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.provider()?.fill_cfg_env_at(cfg, at, evm_config) } - fn fill_cfg_env_with_header(&self, cfg: &mut CfgEnv, header: &Header) -> ProviderResult<()> { - self.provider()?.fill_cfg_env_with_header(cfg, header) + fn fill_cfg_env_with_header( + &self, + cfg: &mut CfgEnv, + header: &Header, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.provider()?.fill_cfg_env_with_header(cfg, header, evm_config) } } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 4ff3e48c680e..eef70b2b83cd 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -31,12 +31,10 @@ use reth_interfaces::{ provider::{ProviderResult, RootMismatch}, RethError, RethResult, }; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ keccak256, - revm::{ - config::revm_spec, - env::{fill_block_env, fill_cfg_and_block_env, fill_cfg_env}, - }, + revm::{config::revm_spec, env::fill_block_env}, stage::{StageCheckpoint, StageId}, trie::Nibbles, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, @@ -1729,27 +1727,41 @@ impl WithdrawalsProvider for DatabaseProvider { } impl EvmEnvProvider for DatabaseProvider { - fn fill_env_at( + fn fill_env_at( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, at: BlockHashOrNumber, - ) -> ProviderResult<()> { + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { let hash = self.convert_number(at)?.ok_or(ProviderError::HeaderNotFound(at))?; let header = self.header(&hash)?.ok_or(ProviderError::HeaderNotFound(at))?; - self.fill_env_with_header(cfg, block_env, &header) + self.fill_env_with_header(cfg, block_env, &header, evm_config) } - fn fill_env_with_header( + fn fill_env_with_header( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, header: &Header, - ) -> ProviderResult<()> { + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { let total_difficulty = self .header_td_by_number(header.number)? .ok_or_else(|| ProviderError::HeaderNotFound(header.number.into()))?; - fill_cfg_and_block_env(cfg, block_env, &self.chain_spec, header, total_difficulty); + EvmConfig::fill_cfg_and_block_env( + cfg, + block_env, + &self.chain_spec, + header, + total_difficulty, + ); Ok(()) } @@ -1788,17 +1800,33 @@ impl EvmEnvProvider for DatabaseProvider { Ok(()) } - fn fill_cfg_env_at(&self, cfg: &mut CfgEnv, at: BlockHashOrNumber) -> ProviderResult<()> { + fn fill_cfg_env_at( + &self, + cfg: &mut CfgEnv, + at: BlockHashOrNumber, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { let hash = self.convert_number(at)?.ok_or(ProviderError::HeaderNotFound(at))?; let header = self.header(&hash)?.ok_or(ProviderError::HeaderNotFound(at))?; - self.fill_cfg_env_with_header(cfg, &header) + self.fill_cfg_env_with_header(cfg, &header, evm_config) } - fn fill_cfg_env_with_header(&self, cfg: &mut CfgEnv, header: &Header) -> ProviderResult<()> { + fn fill_cfg_env_with_header( + &self, + cfg: &mut CfgEnv, + header: &Header, + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { let total_difficulty = self .header_td_by_number(header.number)? .ok_or_else(|| ProviderError::HeaderNotFound(header.number.into()))?; - fill_cfg_env(cfg, &self.chain_spec, header, total_difficulty); + EvmConfig::fill_cfg_env(cfg, &self.chain_spec, header, total_difficulty); Ok(()) } } diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 2d0aac23defa..a3696f37fe14 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -13,6 +13,7 @@ use reth_interfaces::{ provider::ProviderResult, RethError, RethResult, }; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ stage::{StageCheckpoint, StageId}, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumber, @@ -456,22 +457,30 @@ where DB: Database, Tree: Send + Sync, { - fn fill_env_at( + fn fill_env_at( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, at: BlockHashOrNumber, - ) -> ProviderResult<()> { - self.database.provider()?.fill_env_at(cfg, block_env, at) + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.database.provider()?.fill_env_at(cfg, block_env, at, evm_config) } - fn fill_env_with_header( + fn fill_env_with_header( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, header: &Header, - ) -> ProviderResult<()> { - self.database.provider()?.fill_env_with_header(cfg, block_env, header) + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.database.provider()?.fill_env_with_header(cfg, block_env, header, evm_config) } fn fill_block_env_at( @@ -490,12 +499,28 @@ where self.database.provider()?.fill_block_env_with_header(block_env, header) } - fn fill_cfg_env_at(&self, cfg: &mut CfgEnv, at: BlockHashOrNumber) -> ProviderResult<()> { - self.database.provider()?.fill_cfg_env_at(cfg, at) + fn fill_cfg_env_at( + &self, + cfg: &mut CfgEnv, + at: BlockHashOrNumber, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.database.provider()?.fill_cfg_env_at(cfg, at, evm_config) } - fn fill_cfg_env_with_header(&self, cfg: &mut CfgEnv, header: &Header) -> ProviderResult<()> { - self.database.provider()?.fill_cfg_env_with_header(cfg, header) + fn fill_cfg_env_with_header( + &self, + cfg: &mut CfgEnv, + header: &Header, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { + self.database.provider()?.fill_cfg_env_with_header(cfg, header, evm_config) } } diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 0c862a1dacaa..3f4a4b20ae27 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -9,6 +9,7 @@ use crate::{ use parking_lot::Mutex; use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_interfaces::provider::{ProviderError, ProviderResult}; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ keccak256, trie::AccountProof, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, BlockWithSenders, Bytecode, Bytes, ChainInfo, ChainSpec, Header, Receipt, @@ -554,21 +555,29 @@ impl StateProvider for MockEthProvider { } impl EvmEnvProvider for MockEthProvider { - fn fill_env_at( + fn fill_env_at( &self, _cfg: &mut CfgEnv, _block_env: &mut BlockEnv, _at: BlockHashOrNumber, - ) -> ProviderResult<()> { + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } - fn fill_env_with_header( + fn fill_env_with_header( &self, _cfg: &mut CfgEnv, _block_env: &mut BlockEnv, _header: &Header, - ) -> ProviderResult<()> { + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } @@ -588,11 +597,27 @@ impl EvmEnvProvider for MockEthProvider { Ok(()) } - fn fill_cfg_env_at(&self, _cfg: &mut CfgEnv, _at: BlockHashOrNumber) -> ProviderResult<()> { + fn fill_cfg_env_at( + &self, + _cfg: &mut CfgEnv, + _at: BlockHashOrNumber, + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } - fn fill_cfg_env_with_header(&self, _cfg: &mut CfgEnv, _header: &Header) -> ProviderResult<()> { + fn fill_cfg_env_with_header( + &self, + _cfg: &mut CfgEnv, + _header: &Header, + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } } diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index 4036ca99f460..cdd3e93b9660 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -9,6 +9,7 @@ use crate::{ }; use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_interfaces::provider::ProviderResult; +use reth_node_api::EvmEnvConfig; use reth_primitives::{ stage::{StageCheckpoint, StageId}, trie::AccountProof, @@ -314,21 +315,29 @@ impl StateProvider for NoopProvider { } impl EvmEnvProvider for NoopProvider { - fn fill_env_at( + fn fill_env_at( &self, _cfg: &mut CfgEnv, _block_env: &mut BlockEnv, _at: BlockHashOrNumber, - ) -> ProviderResult<()> { + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } - fn fill_env_with_header( + fn fill_env_with_header( &self, _cfg: &mut CfgEnv, _block_env: &mut BlockEnv, _header: &Header, - ) -> ProviderResult<()> { + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } @@ -348,11 +357,27 @@ impl EvmEnvProvider for NoopProvider { Ok(()) } - fn fill_cfg_env_at(&self, _cfg: &mut CfgEnv, _at: BlockHashOrNumber) -> ProviderResult<()> { + fn fill_cfg_env_at( + &self, + _cfg: &mut CfgEnv, + _at: BlockHashOrNumber, + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } - fn fill_cfg_env_with_header(&self, _cfg: &mut CfgEnv, _header: &Header) -> ProviderResult<()> { + fn fill_cfg_env_with_header( + &self, + _cfg: &mut CfgEnv, + _header: &Header, + _evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig, + { Ok(()) } } diff --git a/crates/storage/provider/src/traits/evm_env.rs b/crates/storage/provider/src/traits/evm_env.rs index 4aa540078942..1358c207c039 100644 --- a/crates/storage/provider/src/traits/evm_env.rs +++ b/crates/storage/provider/src/traits/evm_env.rs @@ -1,4 +1,5 @@ use reth_interfaces::provider::ProviderResult; +use reth_node_api::EvmEnvConfig; use reth_primitives::{BlockHashOrNumber, Header}; use revm::primitives::{BlockEnv, CfgEnv}; @@ -10,28 +11,41 @@ use revm::primitives::{BlockEnv, CfgEnv}; pub trait EvmEnvProvider: Send + Sync { /// Fills the [CfgEnv] and [BlockEnv] fields with values specific to the given /// [BlockHashOrNumber]. - fn fill_env_at( + fn fill_env_at( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, at: BlockHashOrNumber, - ) -> ProviderResult<()>; + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig; /// Fills the default [CfgEnv] and [BlockEnv] fields with values specific to the given [Header]. - fn env_with_header(&self, header: &Header) -> ProviderResult<(CfgEnv, BlockEnv)> { + fn env_with_header( + &self, + header: &Header, + evm_config: EvmConfig, + ) -> ProviderResult<(CfgEnv, BlockEnv)> + where + EvmConfig: EvmEnvConfig, + { let mut cfg = CfgEnv::default(); let mut block_env = BlockEnv::default(); - self.fill_env_with_header(&mut cfg, &mut block_env, header)?; + self.fill_env_with_header::(&mut cfg, &mut block_env, header, evm_config)?; Ok((cfg, block_env)) } /// Fills the [CfgEnv] and [BlockEnv] fields with values specific to the given [Header]. - fn fill_env_with_header( + fn fill_env_with_header( &self, cfg: &mut CfgEnv, block_env: &mut BlockEnv, header: &Header, - ) -> ProviderResult<()>; + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig; /// Fills the [BlockEnv] fields with values specific to the given [BlockHashOrNumber]. fn fill_block_env_at( @@ -48,8 +62,22 @@ pub trait EvmEnvProvider: Send + Sync { ) -> ProviderResult<()>; /// Fills the [CfgEnv] fields with values specific to the given [BlockHashOrNumber]. - fn fill_cfg_env_at(&self, cfg: &mut CfgEnv, at: BlockHashOrNumber) -> ProviderResult<()>; + fn fill_cfg_env_at( + &self, + cfg: &mut CfgEnv, + at: BlockHashOrNumber, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig; /// Fills the [CfgEnv] fields with values specific to the given [Header]. - fn fill_cfg_env_with_header(&self, cfg: &mut CfgEnv, header: &Header) -> ProviderResult<()>; + fn fill_cfg_env_with_header( + &self, + cfg: &mut CfgEnv, + header: &Header, + evm_config: EvmConfig, + ) -> ProviderResult<()> + where + EvmConfig: EvmEnvConfig; } diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 6f1d776d150d..c5eabaf17e4a 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -21,6 +21,7 @@ reth-provider.workspace = true reth-stages.workspace = true reth-interfaces.workspace = true reth-revm.workspace = true +reth-node-builder.workspace = true alloy-rlp.workspace = true tokio = "1.28.1" diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index 3ff4ebbabda1..dece7eb82c2d 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -6,6 +6,7 @@ use crate::{ }; use alloy_rlp::Decodable; use reth_db::test_utils::create_test_rw_db; +use reth_node_builder::EthEvmConfig; use reth_primitives::{BlockBody, SealedBlock}; use reth_provider::{BlockWriter, HashingWriter, ProviderFactory}; use reth_stages::{stages::ExecutionStage, ExecInput, Stage}; @@ -108,6 +109,7 @@ impl Case for BlockchainTestCase { // network. let _ = ExecutionStage::new_with_factory(reth_revm::EvmProcessorFactory::new( Arc::new(case.network.clone().into()), + EthEvmConfig::default(), )) .execute( &provider, From f2c45150a1abddbdfec526c61b22fc8458ae757d Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Thu, 25 Jan 2024 22:46:58 +0000 Subject: [PATCH 08/33] fix(book): op-reth specs links (#6235) --- book/run/optimism.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book/run/optimism.md b/book/run/optimism.md index 1e6a884af8fe..b4839cc83079 100644 --- a/book/run/optimism.md +++ b/book/run/optimism.md @@ -107,13 +107,13 @@ op-node \ ``` [l1-el-spec]: https://github.com/ethereum/execution-specs -[rollup-node-spec]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/rollup-node.md +[rollup-node-spec]: https://github.com/ethereum-optimism/specs/blob/main/specs/rollup-node.md [op-geth-forkdiff]: https://op-geth.optimism.io [sequencer]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/introduction.md#sequencers -[op-stack-spec]: https://github.com/ethereum-optimism/optimism/tree/develop/specs -[l2-el-spec]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/exec-engine.md -[deposit-spec]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md -[derivation-spec]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md +[op-stack-spec]: https://github.com/ethereum-optimism/specs/blob/main/specs +[l2-el-spec]: https://github.com/ethereum-optimism/specs/blob/main/specs/exec-engine.md +[deposit-spec]: https://github.com/ethereum-optimism/specs/blob/main/specs/deposits.md +[derivation-spec]: https://github.com/ethereum-optimism/specs/blob/main/specs/derivation.md [op-node-docker]: https://console.cloud.google.com/artifacts/docker/oplabs-tools-artifacts/us/images/op-node From b9d392e29d5f6b0f631cd01bf1b554484947f79e Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 26 Jan 2024 05:57:33 +0100 Subject: [PATCH 09/33] small refactoring in `ethereum-forks` crate (#6234) --- crates/ethereum-forks/src/forkid.rs | 36 ++++++++++++++++++----------- crates/ethereum-forks/src/head.rs | 6 +---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/ethereum-forks/src/forkid.rs b/crates/ethereum-forks/src/forkid.rs index 39fa8a0c725b..876d407db7e8 100644 --- a/crates/ethereum-forks/src/forkid.rs +++ b/crates/ethereum-forks/src/forkid.rs @@ -92,7 +92,7 @@ impl PartialOrd for ForkFilterKey { impl Ord for ForkFilterKey { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - (ForkFilterKey::Block(a), ForkFilterKey::Block(b)) => a.cmp(b), + (ForkFilterKey::Block(a), ForkFilterKey::Block(b)) | (ForkFilterKey::Time(a), ForkFilterKey::Time(b)) => a.cmp(b), (ForkFilterKey::Block(_), ForkFilterKey::Time(_)) => Ordering::Less, _ => Ordering::Greater, @@ -194,6 +194,8 @@ impl ForkFilter { ForkFilterKey::Block(_) => true, ForkFilterKey::Time(time) => *time > genesis_timestamp, }) + .collect::>() + .into_iter() .fold( (BTreeMap::from([(ForkFilterKey::Block(0), genesis_fork_hash)]), genesis_fork_hash), |(mut acc, base_hash), key| { @@ -204,8 +206,10 @@ impl ForkFilter { ) .0; + // Compute cache based on filtered forks and the current head. let cache = Cache::compute_cache(&forks, head); + // Create and return a new `ForkFilter`. Self { forks, head, cache } } @@ -224,16 +228,14 @@ impl ForkFilter { head_in_past || head_in_future }; - let mut transition = None; - // recompute the cache - if recompute_cache { + let transition = if recompute_cache { let past = self.current(); - self.cache = Cache::compute_cache(&self.forks, head); - - transition = Some(ForkTransition { current: self.current(), past }) - } + Some(ForkTransition { current: self.current(), past }) + } else { + None + }; self.head = head; @@ -347,19 +349,23 @@ struct Cache { impl Cache { /// Compute cache. fn compute_cache(forks: &BTreeMap, head: Head) -> Self { + // Prepare vectors to store past and future forks. let mut past = Vec::with_capacity(forks.len()); let mut future = Vec::with_capacity(forks.len()); + // Initialize variables to track the epoch range. let mut epoch_start = ForkFilterKey::Block(0); let mut epoch_end = None; + + // Iterate through forks and categorize them into past and future. for (key, hash) in forks { - let active = if let ForkFilterKey::Block(block) = key { - *block <= head.number - } else if let ForkFilterKey::Time(time) = key { - *time <= head.timestamp - } else { - unreachable!() + // Check if the fork is active based on its type (Block or Time). + let active = match key { + ForkFilterKey::Block(block) => *block <= head.number, + ForkFilterKey::Time(time) => *time <= head.timestamp, }; + + // Categorize forks into past or future based on activity. if active { epoch_start = *key; past.push((*key, *hash)); @@ -371,11 +377,13 @@ impl Cache { } } + // Create ForkId using the last past fork's hash and the next epoch start. let fork_id = ForkId { hash: past.last().expect("there is always at least one - genesis - fork hash; qed").1, next: epoch_end.unwrap_or(ForkFilterKey::Block(0)).into(), }; + // Return the computed cache. Self { epoch_start, epoch_end, past, future, fork_id } } } diff --git a/crates/ethereum-forks/src/head.rs b/crates/ethereum-forks/src/head.rs index 18bebed5faa5..411853e0d3f1 100644 --- a/crates/ethereum-forks/src/head.rs +++ b/crates/ethereum-forks/src/head.rs @@ -44,11 +44,7 @@ impl Head { total_difficulty: U256, timestamp: u64, ) { - self.number = number; - self.hash = hash; - self.difficulty = difficulty; - self.total_difficulty = total_difficulty; - self.timestamp = timestamp; + *self = Self { number, hash, difficulty, total_difficulty, timestamp }; } /// Checks if the head block is an empty block (i.e., has default values). From ed575db3d04b1a83423c526d87f1c2e0a76c55e8 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 26 Jan 2024 06:26:50 +0100 Subject: [PATCH 10/33] small refactoring for transaction field length (#6233) --- crates/primitives/src/transaction/eip1559.rs | 20 +++++++--------- crates/primitives/src/transaction/eip2930.rs | 18 +++++++------- crates/primitives/src/transaction/eip4844.rs | 24 +++++++++---------- crates/primitives/src/transaction/legacy.rs | 14 +++++------ crates/primitives/src/transaction/optimism.rs | 18 +++++++------- 5 files changed, 42 insertions(+), 52 deletions(-) diff --git a/crates/primitives/src/transaction/eip1559.rs b/crates/primitives/src/transaction/eip1559.rs index 89f78e72364c..5c299806b834 100644 --- a/crates/primitives/src/transaction/eip1559.rs +++ b/crates/primitives/src/transaction/eip1559.rs @@ -110,17 +110,15 @@ impl TxEip1559 { /// Encodes only the transaction's fields into the desired buffer, without a RLP header. pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.chain_id.length(); - len += self.nonce.length(); - len += self.max_priority_fee_per_gas.length(); - len += self.max_fee_per_gas.length(); - len += self.gas_limit.length(); - len += self.to.length(); - len += self.value.length(); - len += self.input.0.length(); - len += self.access_list.length(); - len + self.chain_id.length() + + self.nonce.length() + + self.max_priority_fee_per_gas.length() + + self.max_fee_per_gas.length() + + self.gas_limit.length() + + self.to.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() } /// Encodes only the transaction's fields into the desired buffer, without a RLP header. diff --git a/crates/primitives/src/transaction/eip2930.rs b/crates/primitives/src/transaction/eip2930.rs index 1e8c51f47b73..036af743f212 100644 --- a/crates/primitives/src/transaction/eip2930.rs +++ b/crates/primitives/src/transaction/eip2930.rs @@ -91,16 +91,14 @@ impl TxEip2930 { /// Outputs the length of the transaction's fields, without a RLP header. pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.chain_id.length(); - len += self.nonce.length(); - len += self.gas_price.length(); - len += self.gas_limit.length(); - len += self.to.length(); - len += self.value.length(); - len += self.input.0.length(); - len += self.access_list.length(); - len + self.chain_id.length() + + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.to.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() } /// Encodes only the transaction's fields into the desired buffer, without a RLP header. diff --git a/crates/primitives/src/transaction/eip4844.rs b/crates/primitives/src/transaction/eip4844.rs index bafe7c0196ea..73cb22bf3e12 100644 --- a/crates/primitives/src/transaction/eip4844.rs +++ b/crates/primitives/src/transaction/eip4844.rs @@ -207,19 +207,17 @@ impl TxEip4844 { /// Outputs the length of the transaction's fields, without a RLP header. pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.chain_id.length(); - len += self.nonce.length(); - len += self.gas_limit.length(); - len += self.max_fee_per_gas.length(); - len += self.max_priority_fee_per_gas.length(); - len += self.to.length(); - len += self.value.length(); - len += self.access_list.length(); - len += self.blob_versioned_hashes.length(); - len += self.max_fee_per_blob_gas.length(); - len += self.input.0.length(); - len + self.chain_id.length() + + self.nonce.length() + + self.gas_limit.length() + + self.max_fee_per_gas.length() + + self.max_priority_fee_per_gas.length() + + self.to.length() + + self.value.length() + + self.access_list.length() + + self.blob_versioned_hashes.length() + + self.max_fee_per_blob_gas.length() + + self.input.0.length() } /// Encodes only the transaction's fields into the desired buffer, without a RLP header. diff --git a/crates/primitives/src/transaction/legacy.rs b/crates/primitives/src/transaction/legacy.rs index 2a5b0b19a9ea..f717764dc2c8 100644 --- a/crates/primitives/src/transaction/legacy.rs +++ b/crates/primitives/src/transaction/legacy.rs @@ -58,14 +58,12 @@ impl TxLegacy { /// Outputs the length of the transaction's fields, without a RLP header or length of the /// eip155 fields. pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.nonce.length(); - len += self.gas_price.length(); - len += self.gas_limit.length(); - len += self.to.length(); - len += self.value.length(); - len += self.input.0.length(); - len + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.to.length() + + self.value.length() + + self.input.0.length() } /// Encodes only the transaction's fields into the desired buffer, without a RLP header or diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs index 498e9b6873ce..535a7cd8f0e5 100644 --- a/crates/primitives/src/transaction/optimism.rs +++ b/crates/primitives/src/transaction/optimism.rs @@ -78,16 +78,14 @@ impl TxDeposit { /// Outputs the length of the transaction's fields, without a RLP header or length of the /// eip155 fields. pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.source_hash.length(); - len += self.from.length(); - len += self.to.length(); - len += self.mint.map_or(1, |mint| mint.length()); - len += self.value.length(); - len += self.gas_limit.length(); - len += self.is_system_transaction.length(); - len += self.input.0.length(); - len + self.source_hash.length() + + self.from.length() + + self.to.length() + + self.mint.map_or(1, |mint| mint.length()) + + self.value.length() + + self.gas_limit.length() + + self.is_system_transaction.length() + + self.input.0.length() } /// Encodes only the transaction's fields into the desired buffer, without a RLP header. From 4ea45562092108f82ffa2de694dcc0a6d928091f Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 26 Jan 2024 09:13:06 +0100 Subject: [PATCH 11/33] fix(tree): disable state trie caching (#6239) --- crates/blockchain-tree/src/chain.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 578dae720a35..b90b05fa5a30 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -167,7 +167,7 @@ impl AppendableChain { parent_block: &SealedHeader, bundle_state_data_provider: BSDP, externals: &TreeExternals, - block_attachment: BlockAttachment, + _block_attachment: BlockAttachment, block_validation_kind: BlockValidationKind, ) -> RethResult<(BundleStateWithReceipts, Option)> where @@ -194,13 +194,16 @@ impl AppendableChain { // validation was requested. if block_validation_kind.is_exhaustive() { // check state root - let (state_root, trie_updates) = if block_attachment.is_canonical() { - provider - .state_root_with_updates(&bundle_state) - .map(|(root, updates)| (root, Some(updates)))? - } else { - (provider.state_root(&bundle_state)?, None) - }; + // TODO: state root caching is disabled until debugged properly + // let (state_root, trie_updates) = if block_attachment.is_canonical() { + // provider + // .state_root_with_updates(&bundle_state) + // .map(|(root, updates)| (root, Some(updates)))? + + // } else { + // (provider.state_root(&bundle_state)?, None) + // }; + let state_root = provider.state_root(&bundle_state)?; if block.state_root != state_root { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), @@ -208,7 +211,7 @@ impl AppendableChain { .into()) } - Ok((bundle_state, trie_updates)) + Ok((bundle_state, None)) } else { Ok((bundle_state, None)) } From b3ed3016cd28e4f3d17247b8d40e2121d8ba6315 Mon Sep 17 00:00:00 2001 From: Vitaly Drogan Date: Fri, 26 Jan 2024 11:24:10 +0100 Subject: [PATCH 12/33] Hashed post state cursor works on top of a trait instead of &Tx. (#6226) Co-authored-by: Roman Krasiuk --- crates/trie/src/hashed_cursor/post_state.rs | 59 +++++++++------------ crates/trie/src/state.rs | 2 +- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/crates/trie/src/hashed_cursor/post_state.rs b/crates/trie/src/hashed_cursor/post_state.rs index 6a6aebb80b24..2baf72a2de94 100644 --- a/crates/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/src/hashed_cursor/post_state.rs @@ -1,45 +1,32 @@ use super::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor}; use crate::state::HashedPostState; -use reth_db::{ - cursor::{DbCursorRO, DbDupCursorRO}, - tables, - transaction::DbTx, -}; use reth_primitives::{Account, StorageEntry, B256, U256}; /// The hashed cursor factory for the post state. -#[derive(Debug)] -pub struct HashedPostStateCursorFactory<'a, 'b, TX> { - tx: &'a TX, - post_state: &'b HashedPostState, -} - -impl<'a, 'b, TX> Clone for HashedPostStateCursorFactory<'a, 'b, TX> { - fn clone(&self) -> Self { - Self { tx: self.tx, post_state: self.post_state } - } +#[derive(Debug, Clone)] +pub struct HashedPostStateCursorFactory<'a, CF> { + cursor_factory: CF, + post_state: &'a HashedPostState, } -impl<'a, 'b, TX> HashedPostStateCursorFactory<'a, 'b, TX> { +impl<'a, CF> HashedPostStateCursorFactory<'a, CF> { /// Create a new factory. - pub fn new(tx: &'a TX, post_state: &'b HashedPostState) -> Self { - Self { tx, post_state } + pub fn new(cursor_factory: CF, post_state: &'a HashedPostState) -> Self { + Self { cursor_factory, post_state } } } -impl<'a, 'b, TX: DbTx> HashedCursorFactory for HashedPostStateCursorFactory<'a, 'b, TX> { - type AccountCursor = - HashedPostStateAccountCursor<'b, ::Cursor>; - type StorageCursor = - HashedPostStateStorageCursor<'b, ::DupCursor>; +impl<'a, CF: HashedCursorFactory> HashedCursorFactory for HashedPostStateCursorFactory<'a, CF> { + type AccountCursor = HashedPostStateAccountCursor<'a, CF::AccountCursor>; + type StorageCursor = HashedPostStateStorageCursor<'a, CF::StorageCursor>; fn hashed_account_cursor(&self) -> Result { - let cursor = self.tx.cursor_read::()?; + let cursor = self.cursor_factory.hashed_account_cursor()?; Ok(HashedPostStateAccountCursor::new(cursor, self.post_state)) } fn hashed_storage_cursor(&self) -> Result { - let cursor = self.tx.cursor_dup_read::()?; + let cursor = self.cursor_factory.hashed_storage_cursor()?; Ok(HashedPostStateStorageCursor::new(cursor, self.post_state)) } } @@ -54,7 +41,7 @@ pub struct HashedPostStateAccountCursor<'b, C> { post_state: &'b HashedPostState, /// The post state account index where the cursor is currently at. post_state_account_index: usize, - /// The last hashed account key that was returned by the cursor. + /// The last hashed account that was returned by the cursor. /// De facto, this is a current cursor position. last_account: Option, } @@ -100,7 +87,7 @@ impl<'b, C> HashedPostStateAccountCursor<'b, C> { impl<'b, C> HashedAccountCursor for HashedPostStateAccountCursor<'b, C> where - C: DbCursorRO, + C: HashedAccountCursor, { /// Seek the next entry for a given hashed account key. /// @@ -165,7 +152,7 @@ where }; // If post state was given precedence, move the cursor forward. - let mut db_entry = self.cursor.current()?; + let mut db_entry = self.cursor.seek(*last_account)?; while db_entry .as_ref() .map(|(address, _)| address <= last_account || self.is_account_cleared(address)) @@ -258,7 +245,7 @@ impl<'b, C> HashedPostStateStorageCursor<'b, C> { impl<'b, C> HashedStorageCursor for HashedPostStateStorageCursor<'b, C> where - C: DbCursorRO + DbDupCursorRO, + C: HashedStorageCursor, { /// Returns `true` if the account has no storage entries. /// @@ -272,7 +259,7 @@ where // and the current storage does not contain any non-zero values storage.non_zero_valued_slots.is_empty() } - None => self.cursor.seek_exact(key)?.is_none(), + None => self.cursor.is_storage_empty(key)?, }; Ok(is_empty) } @@ -315,14 +302,14 @@ where let db_entry = if self.is_db_storage_wiped(&account) { None } else { - let mut db_entry = self.cursor.seek_by_key_subkey(account, subkey)?; + let mut db_entry = self.cursor.seek(account, subkey)?; while db_entry .as_ref() .map(|entry| self.is_slot_zero_valued(&account, &entry.key)) .unwrap_or_default() { - db_entry = self.cursor.next_dup_val()?; + db_entry = self.cursor.next()?; } db_entry @@ -352,7 +339,7 @@ where None } else { // If post state was given precedence, move the cursor forward. - let mut db_entry = self.cursor.seek_by_key_subkey(account, *last_slot)?; + let mut db_entry = self.cursor.seek(account, *last_slot)?; // If the entry was already returned or is zero-values, move to the next. while db_entry @@ -362,7 +349,7 @@ where }) .unwrap_or_default() { - db_entry = self.cursor.next_dup_val()?; + db_entry = self.cursor.next()?; } db_entry @@ -393,7 +380,9 @@ mod tests { use super::*; use proptest::prelude::*; - use reth_db::{database::Database, test_utils::create_test_rw_db, transaction::DbTxMut}; + use reth_db::{ + database::Database, tables, test_utils::create_test_rw_db, transaction::DbTxMut, + }; use std::collections::BTreeMap; fn assert_account_cursor_order( diff --git a/crates/trie/src/state.rs b/crates/trie/src/state.rs index 620254edad51..6d195cc5f15b 100644 --- a/crates/trie/src/state.rs +++ b/crates/trie/src/state.rs @@ -256,7 +256,7 @@ impl HashedPostState { fn state_root_calculator<'a, TX: DbTx>( &self, tx: &'a TX, - ) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, '_, TX>> { + ) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'_, &'a TX>> { assert!(self.sorted, "Hashed post state must be sorted for state root calculation"); let (account_prefix_set, storage_prefix_set) = self.construct_prefix_sets(); let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, self); From 7db5edf46b700b2fea6fef284518d42c8780d5e7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:05:58 +0100 Subject: [PATCH 13/33] chore: remove clippy global ignore-internal-mutability config (#6243) --- clippy.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy.toml b/clippy.toml index 3d11f32f43d9..5647b20b9563 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,3 +1,2 @@ msrv = "1.75" -ignore-interior-mutability = ["bytes::Bytes", "reth_primitives::trie::nibbles::Nibbles"] too-large-for-stack = 128 From 556741abb0c7a3d2cc30e050316807d627880507 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:56:27 +0100 Subject: [PATCH 14/33] chore: use Display instead of Debug when printing internal errors (#6242) --- bin/reth/src/commands/stage/unwind.rs | 2 +- crates/blockchain-tree/src/blockchain_tree.rs | 6 +++--- crates/rpc/rpc-builder/tests/it/startup.rs | 4 ++-- crates/rpc/rpc-testing-util/tests/it/trace.rs | 2 +- crates/rpc/rpc/src/layers/auth_layer.rs | 2 +- crates/rpc/rpc/src/layers/jwt_secret.rs | 2 +- crates/rpc/rpc/src/result.rs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bin/reth/src/commands/stage/unwind.rs b/bin/reth/src/commands/stage/unwind.rs index 1522c167594c..a5b568e4f8a9 100644 --- a/bin/reth/src/commands/stage/unwind.rs +++ b/bin/reth/src/commands/stage/unwind.rs @@ -73,7 +73,7 @@ impl Command { let blocks_and_execution = provider .take_block_and_execution_range(&self.chain, range) - .map_err(|err| eyre::eyre!("Transaction error on unwind: {err:?}"))?; + .map_err(|err| eyre::eyre!("Transaction error on unwind: {err}"))?; provider.commit()?; diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 9bb48e1f5b53..f88a8d127709 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -643,18 +643,18 @@ impl BlockchainTree { { error!( ?block, - "Failed to validate total difficulty for block {}: {e:?}", block.header.hash + "Failed to validate total difficulty for block {}: {e}", block.header.hash ); return Err(e) } if let Err(e) = self.externals.consensus.validate_header(block) { - error!(?block, "Failed to validate header {}: {e:?}", block.header.hash); + error!(?block, "Failed to validate header {}: {e}", block.header.hash); return Err(e) } if let Err(e) = self.externals.consensus.validate_block(block) { - error!(?block, "Failed to validate block {}: {e:?}", block.header.hash); + error!(?block, "Failed to validate block {}: {e}", block.header.hash); return Err(e) } diff --git a/crates/rpc/rpc-builder/tests/it/startup.rs b/crates/rpc/rpc-builder/tests/it/startup.rs index 9aa4f70d9fc9..6237b164737e 100644 --- a/crates/rpc/rpc-builder/tests/it/startup.rs +++ b/crates/rpc/rpc-builder/tests/it/startup.rs @@ -28,7 +28,7 @@ async fn test_http_addr_in_use() { .start_server(RpcServerConfig::http(Default::default()).with_http_address(addr)) .await; let err = result.unwrap_err(); - assert!(is_addr_in_use_kind(&err, ServerKind::Http(addr)), "{err:?}"); + assert!(is_addr_in_use_kind(&err, ServerKind::Http(addr)), "{err}"); } #[tokio::test(flavor = "multi_thread")] @@ -40,7 +40,7 @@ async fn test_ws_addr_in_use() { let result = server.start_server(RpcServerConfig::ws(Default::default()).with_ws_address(addr)).await; let err = result.unwrap_err(); - assert!(is_addr_in_use_kind(&err, ServerKind::WS(addr)), "{err:?}"); + assert!(is_addr_in_use_kind(&err, ServerKind::WS(addr)), "{err}"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/rpc/rpc-testing-util/tests/it/trace.rs b/crates/rpc/rpc-testing-util/tests/it/trace.rs index c2192b0f1e63..050cfca0d7ae 100644 --- a/crates/rpc/rpc-testing-util/tests/it/trace.rs +++ b/crates/rpc/rpc-testing-util/tests/it/trace.rs @@ -20,7 +20,7 @@ async fn trace_many_blocks() { let mut stream = client.trace_block_buffered_unordered(15_000_000..=16_000_100, 20); let now = Instant::now(); while let Some((err, block)) = stream.next_err().await { - eprintln!("Error tracing block {block:?}: {err:?}"); + eprintln!("Error tracing block {block:?}: {err}"); } println!("Traced all blocks in {:?}", now.elapsed()); } diff --git a/crates/rpc/rpc/src/layers/auth_layer.rs b/crates/rpc/rpc/src/layers/auth_layer.rs index e012a9ed9a33..5a74ec72cd84 100644 --- a/crates/rpc/rpc/src/layers/auth_layer.rs +++ b/crates/rpc/rpc/src/layers/auth_layer.rs @@ -234,7 +234,7 @@ mod tests { let jwt = "this jwt has serious encoding problems".to_string(); let (status, body) = send_request(Some(jwt)).await; assert_eq!(status, StatusCode::UNAUTHORIZED); - assert_eq!(body, "JWT decoding error: Error(InvalidToken)".to_string()); + assert_eq!(body, "JWT decoding error: InvalidToken".to_string()); } async fn send_request(jwt: Option) -> (StatusCode, String) { diff --git a/crates/rpc/rpc/src/layers/jwt_secret.rs b/crates/rpc/rpc/src/layers/jwt_secret.rs index 3b19e02e6cab..3dab8d30ae03 100644 --- a/crates/rpc/rpc/src/layers/jwt_secret.rs +++ b/crates/rpc/rpc/src/layers/jwt_secret.rs @@ -142,7 +142,7 @@ impl JwtSecret { ErrorKind::InvalidSignature => Err(JwtError::InvalidSignature)?, ErrorKind::InvalidAlgorithm => Err(JwtError::UnsupportedSignatureAlgorithm)?, _ => { - let detail = format!("{err:?}"); + let detail = format!("{err}"); Err(JwtError::JwtDecodingError(detail))? } }, diff --git a/crates/rpc/rpc/src/result.rs b/crates/rpc/rpc/src/result.rs index c37ced80179e..9b526fa0d65c 100644 --- a/crates/rpc/rpc/src/result.rs +++ b/crates/rpc/rpc/src/result.rs @@ -92,7 +92,7 @@ macro_rules! impl_to_rpc_result { match self { Ok(t) => Ok(t), Err(err) => { - let msg = format!("{msg}: {err:?}"); + let msg = format!("{msg}: {err}"); Err($crate::result::internal_rpc_err(msg)) } } From 0aa22466d21fc37f1ca1be8ecdc0dd5ac05e6cd6 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:57:08 +0100 Subject: [PATCH 15/33] chore: use Display when formatting addresses and hashes (#6245) --- crates/ethereum-forks/src/head.rs | 2 +- crates/interfaces/src/blockchain_tree/error.rs | 2 +- crates/net/network/src/transactions/mod.rs | 2 +- crates/rpc/rpc/src/admin.rs | 4 ++-- crates/transaction-pool/src/blobstore/disk.rs | 2 +- crates/transaction-pool/src/validate/mod.rs | 13 ++++++------- examples/trace-transaction-cli/src/main.rs | 9 +++------ testing/ef-tests/src/models.rs | 2 +- 8 files changed, 16 insertions(+), 20 deletions(-) diff --git a/crates/ethereum-forks/src/head.rs b/crates/ethereum-forks/src/head.rs index 411853e0d3f1..7420860c3b07 100644 --- a/crates/ethereum-forks/src/head.rs +++ b/crates/ethereum-forks/src/head.rs @@ -57,7 +57,7 @@ impl fmt::Display for Head { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Head Block:\n Number: {}\n Hash: {:?}\n Difficulty: {:?}\n Total Difficulty: {:?}\n Timestamp: {}", + "Head Block:\n Number: {}\n Hash: {}\n Difficulty: {:?}\n Total Difficulty: {:?}\n Timestamp: {}", self.number, self.hash, self.difficulty, self.total_difficulty, self.timestamp ) } diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index 7d20b1831138..2c64954e2135 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -151,7 +151,7 @@ impl std::fmt::Display for InsertBlockErrorData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Failed to insert block (hash={:?}, number={}, parent_hash={:?}): {}", + "Failed to insert block (hash={}, number={}, parent_hash={}): {}", self.block.hash, self.block.number, self.block.parent_hash, self.kind ) } diff --git a/crates/net/network/src/transactions/mod.rs b/crates/net/network/src/transactions/mod.rs index a089faa32d17..12ebe6850ff2 100644 --- a/crates/net/network/src/transactions/mod.rs +++ b/crates/net/network/src/transactions/mod.rs @@ -683,7 +683,7 @@ where debug_assert!( self.peers.contains_key(&peer_id), "a dead peer has been returned as idle by `@pop_any_idle_peer`, broken invariant `@peers` and `@transaction_fetcher`, -`%peer_id`: {:?}, +`%peer_id`: {}, `@peers`: {:?}, `@transaction_fetcher`: {:?}", peer_id, self.peers, self.transaction_fetcher diff --git a/crates/rpc/rpc/src/admin.rs b/crates/rpc/rpc/src/admin.rs index 560379ba2156..dec990f36a0c 100644 --- a/crates/rpc/rpc/src/admin.rs +++ b/crates/rpc/rpc/src/admin.rs @@ -55,7 +55,7 @@ where let peers = peers .into_iter() .map(|peer| PeerInfo { - id: Some(format!("{:?}", peer.remote_id)), + id: Some(peer.remote_id.to_string()), name: peer.client_version.to_string(), caps: peer.capabilities.capabilities().iter().map(|cap| cap.to_string()).collect(), network: PeerNetworkInfo { @@ -68,7 +68,7 @@ where protocols: PeerProtocolsInfo { eth: Some(PeerEthProtocolInfo { difficulty: Some(peer.status.total_difficulty), - head: format!("{:?}", peer.status.blockhash), + head: peer.status.blockhash.to_string(), version: peer.status.version as u32, }), pip: None, diff --git a/crates/transaction-pool/src/blobstore/disk.rs b/crates/transaction-pool/src/blobstore/disk.rs index ba0ffc605b4b..13736da6576e 100644 --- a/crates/transaction-pool/src/blobstore/disk.rs +++ b/crates/transaction-pool/src/blobstore/disk.rs @@ -211,7 +211,7 @@ impl DiskFileBlobStoreInner { /// Returns the path to the blob file for the given transaction hash. #[inline] fn blob_disk_file(&self, tx: B256) -> PathBuf { - self.blob_dir.join(format!("{:x}", tx)) + self.blob_dir.join(format!("{tx:x}")) } /// Retries the blob data for the given transaction hash. diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index 54eac27875c0..3338aca60b18 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -355,13 +355,12 @@ impl Clone for ValidPoolTransaction { } impl fmt::Debug for ValidPoolTransaction { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "Transaction {{ ")?; - write!(fmt, "hash: {:?}, ", &self.transaction.hash())?; - write!(fmt, "provides: {:?}, ", &self.transaction_id)?; - write!(fmt, "raw tx: {:?}", &self.transaction)?; - write!(fmt, "}}")?; - Ok(()) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ValidPoolTransaction") + .field("hash", self.transaction.hash()) + .field("provides", &self.transaction_id) + .field("raw_tx", &self.transaction) + .finish() } } diff --git a/examples/trace-transaction-cli/src/main.rs b/examples/trace-transaction-cli/src/main.rs index 7d63235825ce..4dd61135e107 100644 --- a/examples/trace-transaction-cli/src/main.rs +++ b/examples/trace-transaction-cli/src/main.rs @@ -73,7 +73,7 @@ impl RethNodeCommandConfig for RethCliTxpoolExt { // Waiting for new transactions while let Some(event) = pending_transactions.next().await { let tx = event.transaction; - println!("Transaction received: {:?}", tx); + println!("Transaction received: {tx:?}"); if let Some(tx_recipient_address) = tx.to() { if recipients.is_empty() || recipients.contains(&tx_recipient_address) { @@ -83,11 +83,8 @@ impl RethNodeCommandConfig for RethCliTxpoolExt { let tracerequest = TraceCallRequest::new(callrequest).with_trace_type(TraceType::Trace); if let Ok(trace_result) = traceapi.trace_call(tracerequest).await { - println!( - "trace result for transaction : {:?} is {:?}", - tx.hash(), - trace_result - ); + let hash = tx.hash(); + println!("trace result for transaction {hash}: {trace_result:?}"); } } } diff --git a/testing/ef-tests/src/models.rs b/testing/ef-tests/src/models.rs index a8120df0f056..049facbe4466 100644 --- a/testing/ef-tests/src/models.rs +++ b/testing/ef-tests/src/models.rs @@ -210,7 +210,7 @@ impl Account { /// In case of a mismatch, `Err(Error::Assertion)` is returned. pub fn assert_db(&self, address: Address, tx: &impl DbTx) -> Result<(), Error> { let account = tx.get::(address)?.ok_or_else(|| { - Error::Assertion(format!("Expected account ({address:?}) is missing from DB: {self:?}")) + Error::Assertion(format!("Expected account ({address}) is missing from DB: {self:?}")) })?; assert_equal(self.balance.into(), account.balance, "Balance does not match")?; From 3043244db0df258cb8691eb9480b09396586b3a7 Mon Sep 17 00:00:00 2001 From: bsh98 <31482749+bsh98@users.noreply.github.com> Date: Fri, 26 Jan 2024 08:55:36 -0800 Subject: [PATCH 16/33] add `builder_pubkey` to relay queries (#6236) --- crates/rpc/rpc-types/src/relay/mod.rs | 36 +++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/crates/rpc/rpc-types/src/relay/mod.rs b/crates/rpc/rpc-types/src/relay/mod.rs index 8e8ada470ac2..8fed94b79bfe 100644 --- a/crates/rpc/rpc-types/src/relay/mod.rs +++ b/crates/rpc/rpc-types/src/relay/mod.rs @@ -70,11 +70,9 @@ pub struct BidTrace { /// The hash of the block. pub block_hash: B256, /// The public key of the builder. - #[serde(rename = "builder_pubkey")] - pub builder_public_key: BlsPublicKey, + pub builder_pubkey: BlsPublicKey, /// The public key of the proposer. - #[serde(rename = "proposer_pubkey")] - pub proposer_public_key: BlsPublicKey, + pub proposer_pubkey: BlsPublicKey, /// The recipient of the proposer's fee. pub proposer_fee_recipient: Address, /// The gas limit associated with the block. @@ -197,9 +195,12 @@ pub struct ProposerPayloadsDeliveredQuery { /// Search for a specific EL block number #[serde(skip_serializing_if = "Option::is_none")] pub block_number: Option, - /// filter results by a proposer public key + /// Filter results by a proposer public key #[serde(skip_serializing_if = "Option::is_none")] - pub proposer_key: Option, + pub proposer_pubkey: Option, + /// Filter results by a builder public key + #[serde(skip_serializing_if = "Option::is_none")] + pub builder_pubkey: Option, /// How to order results #[serde(skip_serializing_if = "Option::is_none")] pub order_by: Option, @@ -231,8 +232,14 @@ impl ProposerPayloadsDeliveredQuery { } /// Sets the proposer public key - pub fn proposer_key(mut self, proposer_key: BlsPublicKey) -> Self { - self.proposer_key = Some(proposer_key); + pub fn proposer_pubkey(mut self, proposer_pubkey: BlsPublicKey) -> Self { + self.proposer_pubkey = Some(proposer_pubkey); + self + } + + /// Sets the builder public key + pub fn builder_pubkey(mut self, builder_pubkey: BlsPublicKey) -> Self { + self.builder_pubkey = Some(builder_pubkey); self } @@ -271,8 +278,8 @@ pub enum OrderBy { } /// Query for the GET `/relay/v1/data/bidtraces/builder_blocks_received` endpoint. -/// This endpoint provides BidTraces for the builder block submission for a given slot (that were -/// verified successfully). +/// This endpoint provides BidTraces for builder block submissions that match the query and were +/// verified successfully. #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BuilderBlocksReceivedQuery { /// A specific slot @@ -287,6 +294,9 @@ pub struct BuilderBlocksReceivedQuery { /// Search for a specific EL block number #[serde(skip_serializing_if = "Option::is_none")] pub block_number: Option, + /// Search for a specific builder public key. + #[serde(skip_serializing_if = "Option::is_none")] + pub builder_pubkey: Option, } impl BuilderBlocksReceivedQuery { @@ -313,6 +323,12 @@ impl BuilderBlocksReceivedQuery { self.block_number = Some(block_number); self } + + /// Sets the specific builder public key + pub fn builder_pubkey(mut self, builder_pubkey: BlsPublicKey) -> Self { + self.builder_pubkey = Some(builder_pubkey); + self + } } #[cfg(test)] From 039a69696651cefdb527dcd653952b21c4df81aa Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:13:58 +0100 Subject: [PATCH 17/33] Solve some clippy stuffs (#6247) --- crates/net/ecies/src/mac.rs | 16 +++++++++++++++- crates/transaction-pool/src/pool/parked.rs | 4 ++-- crates/transaction-pool/src/pool/pending.rs | 4 ++-- crates/transaction-pool/src/test_utils/gen.rs | 2 ++ crates/transaction-pool/src/test_utils/mock.rs | 3 ++- crates/transaction-pool/src/test_utils/mod.rs | 2 -- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/net/ecies/src/mac.rs b/crates/net/ecies/src/mac.rs index 6c2fab327902..a3e655f33a97 100644 --- a/crates/net/ecies/src/mac.rs +++ b/crates/net/ecies/src/mac.rs @@ -1,4 +1,13 @@ -#![allow(missing_docs)] +//! # Ethereum MAC Module +//! +//! This module provides the implementation of the Ethereum MAC (Message Authentication Code) +//! construction, as specified in the Ethereum RLPx protocol. +//! +//! The Ethereum MAC is a nonstandard MAC construction that utilizes AES-256 (as a block cipher) +//! and Keccak-256. It is specifically designed for messages of 128 bits in length and is not +//! intended for general MAC use. +//! +//! For more information, refer to the [Ethereum MAC specification](https://github.com/ethereum/devp2p/blob/master/rlpx.md#mac). use aes::Aes256Enc; use block_padding::NoPadding; @@ -9,6 +18,11 @@ use reth_primitives::{B128, B256}; use sha3::{Digest, Keccak256}; use typenum::U16; +/// Type alias for a fixed-size array of 16 bytes used as headers. +/// +/// This type is defined as [`GenericArray`] and is commonly employed in Ethereum RLPx +/// protocol-related structures for headers. It represents 16 bytes of data used in various +/// cryptographic operations, such as MAC (Message Authentication Code) computation. pub type HeaderBytes = GenericArray; /// [`Ethereum MAC`](https://github.com/ethereum/devp2p/blob/master/rlpx.md#mac) state. diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index d34aa96a1783..2add34d397eb 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -19,8 +19,7 @@ use std::{ /// /// Note: This type is generic over [ParkedPool] which enforces that the underlying transaction type /// is [ValidPoolTransaction] wrapped in an [Arc]. -#[allow(missing_debug_implementations)] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ParkedPool { /// Keeps track of transactions inserted in the pool. /// @@ -296,6 +295,7 @@ impl Default for ParkedPool { } /// Represents a transaction in this pool. +#[derive(Debug)] struct ParkedPoolTransaction { /// Identifier that tags when transaction was submitted in the pool. submission_id: u64, diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 6a7b0409e727..4a213b05dace 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -22,8 +22,7 @@ use tokio::sync::broadcast; /// /// Once an `independent` transaction was executed it *unlocks* the next nonce, if this transaction /// is also pending, then this will be moved to the `independent` queue. -#[allow(missing_debug_implementations)] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct PendingPool { /// How to order transactions. ordering: T, @@ -516,6 +515,7 @@ impl PendingPool { } /// A transaction that is ready to be included in a block. +#[derive(Debug)] pub(crate) struct PendingTransaction { /// Identifier that tags when transaction was submitted in the pool. pub(crate) submission_id: u64, diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index 1d8ab5579835..d94719473bdc 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -7,6 +7,7 @@ use reth_primitives::{ }; /// A generator for transactions for testing purposes. +#[derive(Debug)] pub struct TransactionGenerator { /// The random number generator used for generating keys and selecting signers. pub rng: R, @@ -99,6 +100,7 @@ impl TransactionGenerator { } /// A Builder type to configure and create a transaction. +#[derive(Debug)] pub struct TransactionBuilder { /// The signer used to sign the transaction. pub signer: B256, diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 14f578b9e79a..dcdc1ef6a62a 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -1094,7 +1094,7 @@ impl proptest::arbitrary::Arbitrary for MockTransaction { } /// A factory for creating and managing various types of mock transactions. -#[derive(Default)] +#[derive(Debug, Default)] pub struct MockTransactionFactory { pub(crate) ids: SenderIdentifiers, } @@ -1169,6 +1169,7 @@ impl TransactionOrdering for MockOrdering { } /// A configured distribution that can generate transactions +#[derive(Debug)] pub struct MockTransactionDistribution { /// legacy to EIP-1559 ration legacy_ratio: WeightedIndex, diff --git a/crates/transaction-pool/src/test_utils/mod.rs b/crates/transaction-pool/src/test_utils/mod.rs index c55489aef169..e73cc541ca09 100644 --- a/crates/transaction-pool/src/test_utils/mod.rs +++ b/crates/transaction-pool/src/test_utils/mod.rs @@ -1,7 +1,5 @@ //! Internal helpers for testing. -#![allow(missing_debug_implementations)] - use crate::{blobstore::InMemoryBlobStore, noop::MockTransactionValidator, Pool}; mod gen; From 30ee74693fd331626920068db7336fc74738a9e4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 26 Jan 2024 19:10:33 +0100 Subject: [PATCH 18/33] docs: add some notes about tracing (#6246) --- crates/rpc/rpc/src/debug.rs | 3 +++ crates/rpc/rpc/src/eth/api/transactions.rs | 19 +++++++++++++++++++ crates/rpc/rpc/src/eth/revm_utils.rs | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index cba91bbcce05..533fc6ab22d7 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -252,6 +252,9 @@ where /// The debug_traceCall method lets you run an `eth_call` within the context of the given block /// execution using the final state of parent block as the base. + /// + /// Differences compare to `eth_call`: + /// - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: pub async fn debug_trace_call( &self, call: CallRequest, diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 659c7032831e..c93de8f2158b 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -63,6 +63,22 @@ pub(crate) type StateCacheDB = CacheDB>; /// /// Async functions that are spawned onto the /// [BlockingTaskPool](crate::blocking_pool::BlockingTaskPool) begin with `spawn_` +/// +/// +/// ## Calls +/// +/// There are subtle differences between when transacting [CallRequest]: +/// +/// The endpoints `eth_call` and `eth_estimateGas` and `eth_createAccessList` should always +/// __disable__ the base fee check in the [Env] [Cfg](revm_primitives::CfgEnv). +/// +/// The behaviour for tracing endpoints is not consistent across clients. +/// Geth also disables the basefee check for tracing: +/// Erigon does not: +/// +/// See also +/// +/// This implementation follows the behaviour of Geth and disables the basefee check for tracing. #[async_trait::async_trait] pub trait EthTransactions: Send + Sync { /// Returns default gas limit to use for `eth_call` and tracing RPC methods. @@ -160,6 +176,9 @@ pub trait EthTransactions: Send + Sync { /// Prepares the state and env for the given [CallRequest] at the given [BlockId] and executes /// the closure on a new task returning the result of the closure. + /// + /// This returns the configured [Env] for the given [CallRequest] at the given [BlockId] and + /// with configured call settings: `prepare_call_env`. async fn spawn_with_call_at( &self, request: CallRequest, diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 931240a3e360..f7bdfb17114e 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -207,6 +207,11 @@ where /// Prepares the [Env] for execution. /// /// Does not commit any changes to the underlying database. +/// +/// EVM settings: +/// - `disable_block_gas_limit` is set to `true` +/// - `disable_eip3607` is set to `true` +/// - `disable_base_fee` is set to `true` pub(crate) fn prepare_call_env( mut cfg: CfgEnv, block: BlockEnv, From 1bcd48e8d5b4779e112f0edc7f1f3e419b0aa12f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 26 Jan 2024 20:29:35 +0100 Subject: [PATCH 19/33] Fix bug, rename make docs to make rustdocs (#6251) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5961654cc65e..48e41f1656f7 100644 --- a/Makefile +++ b/Makefile @@ -261,7 +261,7 @@ lint: make lint-op-reth && \ make lint-other-targets -docs: +rustdocs: RUSTDOCFLAGS="--cfg docsrs --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options -D warnings" cargo +nightly docs --document-private-items test-reth: From 4cb5eb23b81072734518b17f8628d03774fa2eec Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 26 Jan 2024 22:39:51 +0100 Subject: [PATCH 20/33] Improve maintainability of Makefile (#6253) --- Makefile | 63 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 48e41f1656f7..0d23fc67edd1 100644 --- a/Makefile +++ b/Makefile @@ -248,30 +248,79 @@ fmt: cargo +nightly fmt lint-reth: - cargo +nightly clippy --workspace --bin "reth" --lib --examples --tests --benches --features "ethereum $(BIN_OTHER_FEATURES)" -- -D warnings + cargo +nightly clippy \ + --workspace \ + --bin "reth" \ + --lib \ + --examples \ + --tests \ + --benches \ + --features "ethereum $(BIN_OTHER_FEATURES)" \ + -- -D warnings lint-op-reth: - cargo +nightly clippy --workspace --bin "op-reth" --lib --examples --tests --benches --features "optimism $(BIN_OTHER_FEATURES)" -- -D warnings + cargo +nightly clippy \ + --workspace \ + --bin "op-reth" \ + --lib \ + --examples \ + --tests \ + --benches \ + --features "optimism $(BIN_OTHER_FEATURES)" \ + -- -D warnings lint-other-targets: - cargo +nightly clippy --workspace --lib --examples --tests --benches --all-features -- -D warnings + cargo +nightly clippy \ + --workspace \ + --lib \ + --examples \ + --tests \ + --benches \ + --all-features \ + -- -D warnings lint: + make fmt && \ make lint-reth && \ make lint-op-reth && \ make lint-other-targets rustdocs: - RUSTDOCFLAGS="--cfg docsrs --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options -D warnings" cargo +nightly docs --document-private-items + RUSTDOCFLAGS="\ + --cfg docsrs \ + --show-type-layout \ + --generate-link-to-definition \ + --enable-index-page -Zunstable-options -D warnings" \ + cargo +nightly docs \ + --document-private-items test-reth: - cargo test --workspace --bin "reth" --lib --examples --tests --benches --features "ethereum $(BIN_OTHER_FEATURES)" + cargo test \ + --workspace \ + --bin "reth" \ + --lib \ + --examples \ + --tests \ + --benches \ + --features "ethereum $(BIN_OTHER_FEATURES)" test-op-reth: - cargo test --workspace --bin "op-reth" --lib --examples --tests --benches --features "optimism $(BIN_OTHER_FEATURES)" + cargo test \ + --workspace \ + --bin "op-reth" \ + --lib --examples \ + --tests \ + --benches \ + --features "optimism $(BIN_OTHER_FEATURES)" test-other-targets: - cargo test --workspace --lib --examples --tests --benches --all-features + cargo test \ + --workspace \ + --lib \ + --examples \ + --tests \ + --benches \ + --all-features test: make test-reth && \ From 2d6dbbc595b6b8c0adf161550cf13dbdec2e036d Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 28 Jan 2024 14:15:47 +0100 Subject: [PATCH 21/33] add `mainnet_activation_timestamp` for `Hardfork` (#6257) --- crates/ethereum-forks/src/hardfork.rs | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/ethereum-forks/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs index fb6dd9040df5..dcc54243ba6d 100644 --- a/crates/ethereum-forks/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -88,6 +88,42 @@ impl Hardfork { } } + /// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet. + pub fn mainnet_activation_timestamp(&self, chain: Chain) -> Option { + if chain != Chain::mainnet() { + return None; + } + match self { + Hardfork::Frontier => Some(1438226773), + Hardfork::Homestead => Some(1457938193), + Hardfork::Dao => Some(1468977640), + Hardfork::Tangerine => Some(1476753571), + Hardfork::SpuriousDragon => Some(1479788144), + Hardfork::Byzantium => Some(1508131331), + Hardfork::Constantinople => Some(1551340324), + Hardfork::Petersburg => Some(1551340324), + Hardfork::Istanbul => Some(1575807909), + Hardfork::MuirGlacier => Some(1577953849), + Hardfork::Berlin => Some(1618481223), + Hardfork::London => Some(1628166822), + Hardfork::ArrowGlacier => Some(1639036523), + Hardfork::GrayGlacier => Some(1656586444), + Hardfork::Paris => Some(1663224162), + Hardfork::Shanghai => Some(1681338455), + + // upcoming hardforks + Hardfork::Cancun => None, + + // optimism hardforks + #[cfg(feature = "optimism")] + Hardfork::Bedrock => None, + #[cfg(feature = "optimism")] + Hardfork::Regolith => None, + #[cfg(feature = "optimism")] + Hardfork::Canyon => None, + } + } + /// Checks if the hardfork is post the Ethereum merge. pub fn is_post_merge(&self) -> bool { self >= &Hardfork::Paris From a2ac07db2390d69c1500f86a7e3a10cbf0a3cc94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 13:26:46 +0000 Subject: [PATCH 22/33] chore(deps): weekly `cargo update` (#6260) Co-authored-by: github-merge-queue --- Cargo.lock | 104 ++++++++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 012cb0c9d977..f3a3c096ed05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-chains" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a5f61137c31916542bb63cd70d0e0dd7a76f76b7f962f4337bc438612d45b2" +checksum = "206e825321d0ed5b6e3907e6ddf70c2139cfdc6274e83a1ca2f4784bd0abc0c9" dependencies = [ "alloy-rlp", "arbitrary", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf1d316f6375d7cff11b526e91938b199e021da118861f9d238438938e0ac7d" +checksum = "c7265ac54c88a78604cea8444addfa9dfdad08d3098f153484cb4ee66fc202cc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#53ef6c2dc9867f03320131ff0f25577502259138" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b07fb0de8c985deebc60b97fc8c45fd45c101822c3241c1f389e9f9a557dd7" +checksum = "a7c5aecfe87e06da0e760840974c6e3cc19d4247be17a3172825fbbe759c8e60" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -195,7 +195,7 @@ dependencies = [ [[package]] name = "alloy-node-bindings" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#53ef6c2dc9867f03320131ff0f25577502259138" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3729132072f369bc4e8e6e070f9cf4deb3490fc9b9eea6f71f75ec19406df811" +checksum = "f4b6fb2b432ff223d513db7f908937f63c252bee0af9b82bfd25b0a5dd1eb0d8" dependencies = [ "alloy-rlp", "arbitrary", @@ -256,7 +256,7 @@ dependencies = [ [[package]] name = "alloy-rpc-engine-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#53ef6c2dc9867f03320131ff0f25577502259138" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -271,7 +271,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#53ef6c2dc9867f03320131ff0f25577502259138" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -282,7 +282,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#53ef6c2dc9867f03320131ff0f25577502259138" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5531f0a16e36c547e68c73a1638bea1f26237ee8ae785527190c4e4f9fecd2c5" +checksum = "8b0b5ab0cb07c21adf9d72e988b34e8200ce648c2bba8d009183bb1c50fb1216" dependencies = [ "const-hex", "dunce", @@ -318,18 +318,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6958f72e4aa4acc979ea70bca59204336c0686e92b26d02d48244cf25b0dabb0" +checksum = "4dd124ec0a456ec5e9dcca5b6e8b011bc723cc410d4d9a66bf032770feaeef4b" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783eb720b73d38f9d4c1fb9890e4db6bc8c708f7aa77d3071a19e06091ecd1c9" +checksum = "6c08f62ded7ce03513bfb60ef5cad4fff5d4f67eac6feb4df80426b7b9ffb06e" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -708,14 +708,14 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "972d3215e2b5ab2408f98713bee04b8b8d2f915bfecfcb569e07a14edec1e1e1" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -1187,9 +1187,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" [[package]] name = "byteorder" @@ -1292,9 +1292,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", @@ -4173,9 +4173,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.14" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050" +checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" dependencies = [ "cc", "libc", @@ -4322,9 +4322,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -5041,18 +5041,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -5613,7 +5613,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick 1.1.2", "memchr", - "regex-automata 0.4.4", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -5628,9 +5628,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick 1.1.2", "memchr", @@ -6976,7 +6976,7 @@ dependencies = [ [[package]] name = "revm" version = "3.5.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#e13f6041e2b4c0fca1028632a17d5a736212e6ec" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#2caa13db91db770e7e015a6e52b5feedbb38ee8a" dependencies = [ "auto_impl", "revm-interpreter", @@ -7003,7 +7003,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#e13f6041e2b4c0fca1028632a17d5a736212e6ec" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#2caa13db91db770e7e015a6e52b5feedbb38ee8a" dependencies = [ "revm-primitives", ] @@ -7011,7 +7011,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.2.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#e13f6041e2b4c0fca1028632a17d5a736212e6ec" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#2caa13db91db770e7e015a6e52b5feedbb38ee8a" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -7027,7 +7027,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#e13f6041e2b4c0fca1028632a17d5a736212e6ec" +source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#2caa13db91db770e7e015a6e52b5feedbb38ee8a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7517,9 +7517,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] @@ -7535,9 +7535,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -7546,9 +7546,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "4d1bd37ce2324cf3bf85e5a25f96eb4baf0d5aa6eba43e7ae8958870c4ec48ed" dependencies = [ "itoa", "ryu", @@ -8011,7 +8011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" dependencies = [ "debugid", - "memmap2 0.9.3", + "memmap2 0.9.4", "stable_deref_trait", "uuid 1.7.0", ] @@ -8051,9 +8051,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cfbd642e1748fd9e47951973abfa78f825b11fbf68af9e6b9db4c983a770166" +checksum = "63bef2e2c735acbc06874eca3a8506f02a3c4700e6e748afc92cc2e4220e8a03" dependencies = [ "paste", "proc-macro2", @@ -9367,9 +9367,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" dependencies = [ "memchr", ] From d68a3dacadebdca93e5e5eceab2b8bbbd8bdf25d Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 28 Jan 2024 16:31:11 +0100 Subject: [PATCH 23/33] get queued and pending transactions by sender in tx pool (#6256) --- crates/transaction-pool/src/identifier.rs | 13 ++++++------- crates/transaction-pool/src/pool/parked.rs | 2 +- crates/transaction-pool/src/pool/pending.rs | 19 +++++++++++++++---- crates/transaction-pool/src/pool/txpool.rs | 17 +++++++++++------ 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/crates/transaction-pool/src/identifier.rs b/crates/transaction-pool/src/identifier.rs index feefd57aeaa8..f5915ec3f346 100644 --- a/crates/transaction-pool/src/identifier.rs +++ b/crates/transaction-pool/src/identifier.rs @@ -30,13 +30,12 @@ impl SenderIdentifiers { /// Returns the existing `SendId` or assigns a new one if it's missing pub(crate) fn sender_id_or_create(&mut self, addr: Address) -> SenderId { - if let Some(id) = self.sender_id(&addr) { - return id - } - let id = self.next_id(); - self.address_to_id.insert(addr, id); - self.sender_to_address.insert(id, addr); - id + self.sender_id(&addr).unwrap_or_else(|| { + let id = self.next_id(); + self.address_to_id.insert(addr, id); + self.sender_to_address.insert(id, addr); + id + }) } /// Returns a new address diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index 2add34d397eb..68ef86a3cfdc 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -91,7 +91,7 @@ impl ParkedPool { self.by_id .range((sender.start_bound(), Unbounded)) .take_while(move |(other, _)| sender == other.sender) - .map(|(_, tx)| *tx.transaction.id()) + .map(|(tx_id, _)| *tx_id) .collect() } diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 4a213b05dace..b7b291941ba9 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -1,13 +1,15 @@ use crate::{ - identifier::TransactionId, - pool::{best::BestTransactions, size::SizeTracker}, + identifier::{SenderId, TransactionId}, + pool::{ + best::{BestTransactions, BestTransactionsWithBasefee}, + size::SizeTracker, + }, Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction, }; - -use crate::pool::best::BestTransactionsWithBasefee; use std::{ cmp::Ordering, collections::{BTreeMap, BTreeSet}, + ops::Bound::Unbounded, sync::Arc, }; use tokio::sync::broadcast; @@ -489,6 +491,15 @@ impl PendingPool { self.by_id.contains_key(id) } + /// Get transactions by sender + pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec { + self.by_id + .range((sender.start_bound(), Unbounded)) + .take_while(move |(other, _)| sender == other.sender) + .map(|(tx_id, _)| *tx_id) + .collect() + } + /// Retrieves a transaction with the given ID from the pool, if it exists. fn get(&self, id: &TransactionId) -> Option<&PendingTransaction> { self.by_id.get(id) diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 880bc851c760..59b56de2ddb2 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -357,9 +357,15 @@ impl TxPool { /// Returns all transactions from parked pools pub(crate) fn queued_transactions(&self) -> Vec>> { - let mut queued = self.basefee_pool.all().collect::>(); - queued.extend(self.queued_pool.all()); - queued + self.basefee_pool.all().chain(self.queued_pool.all()).collect() + } + + /// Returns queued and pending transactions for the specified sender + pub fn queued_and_pending_txs_by_sender( + &self, + sender: SenderId, + ) -> (Vec, Vec) { + (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender)) } /// Returns `true` if the transaction with the given hash is already included in this pool. @@ -583,8 +589,7 @@ impl TxPool { /// This will move/discard the given transaction according to the `PoolUpdate` fn process_updates(&mut self, updates: Vec) -> UpdateOutcome { let mut outcome = UpdateOutcome::default(); - for update in updates { - let PoolUpdate { id, hash, current, destination } = update; + for PoolUpdate { id, hash, current, destination } in updates { match destination { Destination::Discard => { // remove the transaction from the pool and subpool @@ -594,7 +599,7 @@ impl TxPool { self.metrics.removed_transactions.increment(1); } Destination::Pool(move_to) => { - debug_assert!(!move_to.eq(¤t), "destination must be different"); + debug_assert_ne!(&move_to, ¤t, "destination must be different"); let moved = self.move_transaction(current, move_to, &id); if matches!(move_to, SubPool::Pending) { if let Some(tx) = moved { From ba4f536f93b2a21c2f57a1e728bdabd306afbe65 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 28 Jan 2024 16:55:13 +0100 Subject: [PATCH 24/33] Separate `activation_timestamp` and `mainnet_activation_timestamp` methods (#6261) --- crates/ethereum-forks/src/hardfork.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/ethereum-forks/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs index dcc54243ba6d..2b4a74243283 100644 --- a/crates/ethereum-forks/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -88,11 +88,16 @@ impl Hardfork { } } - /// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet. - pub fn mainnet_activation_timestamp(&self, chain: Chain) -> Option { + /// Retrieves the activation timestamp for the specified hardfork on the given chain. + pub fn activation_timestamp(&self, chain: Chain) -> Option { if chain != Chain::mainnet() { return None; } + self.mainnet_activation_timestamp() + } + + /// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet. + pub fn mainnet_activation_timestamp(&self) -> Option { match self { Hardfork::Frontier => Some(1438226773), Hardfork::Homestead => Some(1457938193), From 4e0bead8bda5de8a99a9582c0f4b958dd0d83f4e Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:53:27 +0100 Subject: [PATCH 25/33] add `ConsensusType` enum for `Hardfork` (#6259) --- crates/ethereum-forks/src/hardfork.rs | 101 ++++++++++++++++++-------- 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/crates/ethereum-forks/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs index 2b4a74243283..8608ed050a94 100644 --- a/crates/ethereum-forks/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -2,6 +2,18 @@ use alloy_chains::Chain; use serde::{Deserialize, Serialize}; use std::{fmt::Display, str::FromStr}; +/// Represents the consensus type of a blockchain fork. +/// +/// This enum defines two variants: `ProofOfWork` for hardforks that use a proof-of-work consensus +/// mechanism, and `ProofOfStake` for hardforks that use a proof-of-stake consensus mechanism. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ConsensusType { + /// Indicates a proof-of-work consensus mechanism. + ProofOfWork, + /// Indicates a proof-of-stake consensus mechanism. + ProofOfStake, +} + /// The name of an Ethereum hardfork. #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[non_exhaustive] @@ -88,6 +100,25 @@ impl Hardfork { } } + /// Retrieves the consensus type for the specified hardfork. + pub fn consensus_type(&self) -> ConsensusType { + if *self >= Hardfork::Paris { + ConsensusType::ProofOfStake + } else { + ConsensusType::ProofOfWork + } + } + + /// Checks if the hardfork uses Proof of Stake consensus. + pub fn is_proof_of_stake(&self) -> bool { + matches!(self.consensus_type(), ConsensusType::ProofOfStake) + } + + /// Checks if the hardfork uses Proof of Work consensus. + pub fn is_proof_of_work(&self) -> bool { + matches!(self.consensus_type(), ConsensusType::ProofOfWork) + } + /// Retrieves the activation timestamp for the specified hardfork on the given chain. pub fn activation_timestamp(&self, chain: Chain) -> Option { if chain != Chain::mainnet() { @@ -128,11 +159,6 @@ impl Hardfork { Hardfork::Canyon => None, } } - - /// Checks if the hardfork is post the Ethereum merge. - pub fn is_post_merge(&self) -> bool { - self >= &Hardfork::Paris - } } impl FromStr for Hardfork { @@ -243,31 +269,46 @@ mod tests { } #[test] - fn check_post_merge() { - assert!(!Hardfork::Frontier.is_post_merge()); - assert!(!Hardfork::Homestead.is_post_merge()); - assert!(!Hardfork::Dao.is_post_merge()); - assert!(!Hardfork::Tangerine.is_post_merge()); - assert!(!Hardfork::SpuriousDragon.is_post_merge()); - assert!(!Hardfork::Byzantium.is_post_merge()); - assert!(!Hardfork::Constantinople.is_post_merge()); - assert!(!Hardfork::Petersburg.is_post_merge()); - assert!(!Hardfork::Istanbul.is_post_merge()); - assert!(!Hardfork::MuirGlacier.is_post_merge()); - assert!(!Hardfork::Berlin.is_post_merge()); - assert!(!Hardfork::London.is_post_merge()); - assert!(!Hardfork::ArrowGlacier.is_post_merge()); - assert!(!Hardfork::GrayGlacier.is_post_merge()); - assert!(Hardfork::Paris.is_post_merge()); - assert!(Hardfork::Shanghai.is_post_merge()); - assert!(Hardfork::Cancun.is_post_merge()); - } + fn check_consensus_type() { + let pow_hardforks = [ + Hardfork::Frontier, + Hardfork::Homestead, + Hardfork::Dao, + Hardfork::Tangerine, + Hardfork::SpuriousDragon, + Hardfork::Byzantium, + Hardfork::Constantinople, + Hardfork::Petersburg, + Hardfork::Istanbul, + Hardfork::MuirGlacier, + Hardfork::Berlin, + Hardfork::London, + Hardfork::ArrowGlacier, + Hardfork::GrayGlacier, + ]; - #[test] - #[cfg(feature = "optimism")] - fn check_op_post_merge() { - assert!(Hardfork::Bedrock.is_post_merge()); - assert!(Hardfork::Regolith.is_post_merge()); - assert!(Hardfork::Canyon.is_post_merge()); + let pos_hardforks = [Hardfork::Paris, Hardfork::Shanghai, Hardfork::Cancun]; + + #[cfg(feature = "optimism")] + let op_hardforks = [Hardfork::Bedrock, Hardfork::Regolith, Hardfork::Canyon]; + + for hardfork in pow_hardforks.iter() { + assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfWork); + assert!(!hardfork.is_proof_of_stake()); + assert!(hardfork.is_proof_of_work()); + } + + for hardfork in pos_hardforks.iter() { + assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfStake); + assert!(hardfork.is_proof_of_stake()); + assert!(!hardfork.is_proof_of_work()); + } + + #[cfg(feature = "optimism")] + for hardfork in op_hardforks.iter() { + assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfStake); + assert!(hardfork.is_proof_of_stake()); + assert!(!hardfork.is_proof_of_work()); + } } } From f56ee798c0d73946e7aa1e48ddccff9b3c1a24fa Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sun, 28 Jan 2024 18:54:36 +0100 Subject: [PATCH 26/33] fix(evm): constantinople hardfork reward adjustment (#6263) --- crates/consensus/common/src/calc.rs | 2 +- crates/primitives/src/chain/spec.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/consensus/common/src/calc.rs b/crates/consensus/common/src/calc.rs index addd4001d095..87ead142f607 100644 --- a/crates/consensus/common/src/calc.rs +++ b/crates/consensus/common/src/calc.rs @@ -28,7 +28,7 @@ pub fn base_block_reward( chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, block_difficulty) { None - } else if chain_spec.fork(Hardfork::Petersburg).active_at_block(block_number) { + } else if chain_spec.fork(Hardfork::Constantinople).active_at_block(block_number) { Some(ETH_TO_WEI * 2) } else if chain_spec.fork(Hardfork::Byzantium).active_at_block(block_number) { Some(ETH_TO_WEI * 3) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index a3abff65db9b..461dc1b2ca44 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -1221,9 +1221,16 @@ impl ChainSpecBuilder { self } + /// Enable Constantinople at genesis. + pub fn constantinople_activated(mut self) -> Self { + self = self.byzantium_activated(); + self.hardforks.insert(Hardfork::Constantinople, ForkCondition::Block(0)); + self + } + /// Enable Petersburg at genesis. pub fn petersburg_activated(mut self) -> Self { - self = self.byzantium_activated(); + self = self.constantinople_activated(); self.hardforks.insert(Hardfork::Petersburg, ForkCondition::Block(0)); self } From 5b3ce6731658f763038b0e3b986b4e3ec8c8a356 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:54:42 +0100 Subject: [PATCH 27/33] small refac in rpc crate (#6264) --- crates/rpc/rpc/src/debug.rs | 35 +++++++++++++++++------------------ crates/rpc/rpc/src/txpool.rs | 29 +++++++++++++---------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 533fc6ab22d7..f393ecf6f770 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -21,11 +21,6 @@ use reth_primitives::{ use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StateProviderBox, TransactionVariant, }; -use revm_inspectors::tracing::{ - js::{JsInspector, TransactionContext}, - FourByteInspector, TracingInspector, TracingInspectorConfig, -}; - use reth_revm::database::{StateProviderDatabase, SubState}; use reth_rpc_api::DebugApiServer; use reth_rpc_types::{ @@ -36,7 +31,10 @@ use reth_rpc_types::{ BlockError, Bundle, CallRequest, RichBlock, StateContext, }; use revm::{db::CacheDB, primitives::Env}; - +use revm_inspectors::tracing::{ + js::{JsInspector, TransactionContext}, + FourByteInspector, TracingInspector, TracingInspectorConfig, +}; use std::sync::Arc; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; @@ -640,18 +638,19 @@ where /// Handler for `debug_getRawReceipts` async fn raw_receipts(&self, block_id: BlockId) -> RpcResult> { - let receipts = - self.inner.provider.receipts_by_block_id(block_id).to_rpc_result()?.unwrap_or_default(); - let mut all_receipts = Vec::with_capacity(receipts.len()); - - for receipt in receipts { - let mut buf = Vec::new(); - let receipt = receipt.with_bloom(); - receipt.encode(&mut buf); - all_receipts.push(buf.into()); - } - - Ok(all_receipts) + Ok(self + .inner + .provider + .receipts_by_block_id(block_id) + .to_rpc_result()? + .unwrap_or_default() + .into_iter() + .map(|receipt| { + let mut buf = Vec::new(); + receipt.with_bloom().encode(&mut buf); + Bytes::from(buf) + }) + .collect()) } /// Handler for `debug_getBadBlocks` diff --git a/crates/rpc/rpc/src/txpool.rs b/crates/rpc/rpc/src/txpool.rs index b3d4cf2672f1..c61793f2fdf4 100644 --- a/crates/rpc/rpc/src/txpool.rs +++ b/crates/rpc/rpc/src/txpool.rs @@ -36,11 +36,10 @@ where tx: &T, content: &mut BTreeMap>, ) { - let entry = content.entry(tx.sender()).or_default(); - let key = tx.nonce().to_string(); - let tx = tx.to_recovered_transaction(); - let tx = reth_rpc_types_compat::transaction::from_recovered(tx); - entry.insert(key, tx); + content.entry(tx.sender()).or_default().insert( + tx.nonce().to_string(), + reth_rpc_types_compat::transaction::from_recovered(tx.to_recovered_transaction()), + ); } let AllPoolTransactions { pending, queued } = self.pool.all_transactions(); @@ -93,17 +92,15 @@ where let entry = inspect.entry(tx.sender()).or_default(); let key = tx.nonce().to_string(); let tx = tx.to_recovered_transaction(); - let to = tx.to(); - let gas_price = tx.transaction.max_fee_per_gas(); - let value = tx.value(); - let gas = tx.gas_limit(); - let summary = TxpoolInspectSummary { - to, - value: value.into(), - gas: U256::from(gas), - gas_price: U256::from(gas_price), - }; - entry.insert(key, summary); + entry.insert( + key, + TxpoolInspectSummary { + to: tx.to(), + value: tx.value().into(), + gas: U256::from(tx.gas_limit()), + gas_price: U256::from(tx.transaction.max_fee_per_gas()), + }, + ); } let mut inspect = TxpoolInspect::default(); From 273a101ed608647ef0845cb66a3501cc95dc7c8e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 29 Jan 2024 10:53:51 +0100 Subject: [PATCH 28/33] chore: new max trace response size unlocked (#6271) --- crates/node-core/src/args/rpc_server_args.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/node-core/src/args/rpc_server_args.rs b/crates/node-core/src/args/rpc_server_args.rs index 0de47e9518a1..f9c2dbcea5d1 100644 --- a/crates/node-core/src/args/rpc_server_args.rs +++ b/crates/node-core/src/args/rpc_server_args.rs @@ -47,12 +47,15 @@ use tracing::{debug, info}; /// Default max number of subscriptions per connection. pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024; + /// Default max request size in MB. pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15; + /// Default max response size in MB. /// /// This is only relevant for very large trace responses. -pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 150; +pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160; + /// Default number of incoming connections. pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500; From c46ff698baafd4c313d51b585d25490eb33920be Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Mon, 29 Jan 2024 11:06:53 +0100 Subject: [PATCH 29/33] test(tree): clean up tree testing utils (#6270) --- crates/blockchain-tree/src/blockchain_tree.rs | 12 +- crates/storage/provider/src/chain.rs | 5 + .../storage/provider/src/test_utils/blocks.rs | 171 ++++++++++-------- 3 files changed, 105 insertions(+), 83 deletions(-) diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index f88a8d127709..fe90047f62d0 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1576,9 +1576,9 @@ mod tests { ); } - #[tokio::test] - async fn test_side_chain_fork() { - let data = BlockChainTestData::default_with_numbers(11, 12); + #[test] + fn test_side_chain_fork() { + let data = BlockChainTestData::default_from_number(11); let (block1, exec1) = data.blocks[0].clone(); let (block2, exec2) = data.blocks[1].clone(); let genesis = data.genesis; @@ -1664,9 +1664,9 @@ mod tests { assert_eq!(tree.state.chains.get(&1.into()).unwrap().state().state().reverts.len(), 1); } - #[tokio::test] - async fn sanity_path() { - let data = BlockChainTestData::default_with_numbers(11, 12); + #[test] + fn sanity_path() { + let data = BlockChainTestData::default_from_number(11); let (block1, exec1) = data.blocks[0].clone(); let (block2, exec2) = data.blocks[1].clone(); let genesis = data.genesis; diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index ae9e5a3f4b29..6db59fc76b00 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -68,6 +68,11 @@ impl Chain { self.blocks.values().map(|block| block.header.clone()) } + /// Get cached trie updates for this chain. + pub fn trie_updates(&self) -> Option<&TrieUpdates> { + self.trie_updates.as_ref() + } + /// Get post state of this chain pub fn state(&self) -> &BundleStateWithReceipts { &self.state diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 562483b5e1d8..10d170421c93 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -1,13 +1,19 @@ //! Dummy blocks and data for tests - use crate::{BundleStateWithReceipts, DatabaseProviderRW}; use alloy_rlp::Decodable; use reth_db::{database::Database, models::StoredBlockBodyIndices, tables}; use reth_primitives::{ - b256, hex_literal::hex, Account, Address, BlockNumber, Bytes, Header, Log, Receipt, Receipts, - SealedBlock, SealedBlockWithSenders, StorageEntry, TxType, Withdrawal, B256, U256, + b256, + hex_literal::hex, + proofs::{state_root_unhashed, storage_root_unhashed}, + revm::compat::into_reth_acc, + Address, BlockNumber, Bytes, Header, Log, Receipt, Receipts, SealedBlock, + SealedBlockWithSenders, TxType, Withdrawal, B256, U256, +}; +use revm::{ + db::BundleState, + primitives::{AccountInfo, HashMap}, }; -use std::collections::HashMap; /// Assert genesis block pub fn assert_genesis_block(provider: &DatabaseProviderRW, g: SealedBlock) { @@ -47,6 +53,8 @@ pub fn assert_genesis_block(provider: &DatabaseProviderRW, g: // SyncStage is not updated in tests } +const BLOCK_RLP: [u8; 610] = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0"); + /// Test chain with genesis, blocks, execution results /// that have valid changesets. #[derive(Debug)] @@ -59,18 +67,18 @@ pub struct BlockChainTestData { impl BlockChainTestData { /// Create test data with two blocks that are connected, specifying their block numbers. - pub fn default_with_numbers(one: BlockNumber, two: BlockNumber) -> Self { - let one = block1(one); - let hash = one.0.hash; - Self { genesis: genesis(), blocks: vec![one, block2(two, hash)] } + pub fn default_from_number(first: BlockNumber) -> Self { + let one = block1(first); + let two = block2(first + 1, one.0.hash, &one.1); + Self { genesis: genesis(), blocks: vec![one, two] } } } impl Default for BlockChainTestData { fn default() -> Self { let one = block1(1); - let hash = one.0.hash; - Self { genesis: genesis(), blocks: vec![one, block2(2, hash)] } + let two = block2(2, one.0.hash, &one.1); + Self { genesis: genesis(), blocks: vec![one, two] } } } @@ -85,49 +93,41 @@ pub fn genesis() -> SealedBlock { } } +fn bundle_state_root(state: &BundleStateWithReceipts) -> B256 { + state_root_unhashed(state.bundle_accounts_iter().filter_map(|(address, account)| { + account.info.as_ref().map(|info| { + ( + address, + ( + into_reth_acc(info.clone()), + storage_root_unhashed( + account + .storage + .iter() + .map(|(slot, value)| ((*slot).into(), value.present_value)), + ), + ), + ) + }) + })) +} + /// Block one that points to genesis fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceipts) { - let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); - let mut block = SealedBlock::decode(&mut block_rlp).unwrap(); - block.withdrawals = Some(vec![Withdrawal::default()]); - let mut header = block.header.clone().unseal(); - header.number = number; - header.state_root = b256!("5d035ccb3e75a9057452ff060b773b213ec1fc353426174068edfc3971a0b6bd"); - header.parent_hash = B256::ZERO; - block.header = header.seal_slow(); - // block changes let account1: Address = [0x60; 20].into(); let account2: Address = [0x61; 20].into(); - let slot: B256 = B256::with_last_byte(5); + let slot = U256::from(5); + let info = AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() }; - let bundle = BundleStateWithReceipts::new_init( - HashMap::from([ - ( - account1, - ( - None, - Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), - HashMap::from([(slot, (U256::from(0), U256::from(10)))]), - ), - ), - ( - account2, - ( - None, - Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), - HashMap::from([]), - ), - ), - ]), - HashMap::from([( - number, - HashMap::from([ - (account1, (Some(None), vec![StorageEntry::new(slot, U256::from(0))])), - (account2, (Some(None), vec![])), - ]), - )]), - vec![], + let bundle = BundleStateWithReceipts::new( + BundleState::builder(number..=number) + .state_present_account_info(account1, info.clone()) + .revert_account_info(number, account1, Some(None)) + .state_present_account_info(account2, info) + .revert_account_info(number, account2, Some(None)) + .state_storage(account1, HashMap::from([(slot, (U256::ZERO, U256::from(10)))])) + .build(), Receipts::from_vec(vec![vec![Some(Receipt { tx_type: TxType::EIP2930, success: true, @@ -145,6 +145,20 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceip number, ); + let state_root = bundle_state_root(&bundle); + assert_eq!( + state_root, + b256!("5d035ccb3e75a9057452ff060b773b213ec1fc353426174068edfc3971a0b6bd") + ); + + let mut block = SealedBlock::decode(&mut BLOCK_RLP.as_slice()).unwrap(); + block.withdrawals = Some(vec![Withdrawal::default()]); + let mut header = block.header.clone().unseal(); + header.number = number; + header.state_root = state_root; + header.parent_hash = B256::ZERO; + block.header = header.seal_slow(); + (SealedBlockWithSenders { block, senders: vec![Address::new([0x30; 20])] }, bundle) } @@ -152,41 +166,26 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceip fn block2( number: BlockNumber, parent_hash: B256, + prev_state: &BundleStateWithReceipts, ) -> (SealedBlockWithSenders, BundleStateWithReceipts) { - let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); - let mut block = SealedBlock::decode(&mut block_rlp).unwrap(); - block.withdrawals = Some(vec![Withdrawal::default()]); - let mut header = block.header.clone().unseal(); - header.number = number; - header.state_root = b256!("90101a13dd059fa5cca99ed93d1dc23657f63626c5b8f993a2ccbdf7446b64f8"); - // parent_hash points to block1 hash - header.parent_hash = parent_hash; - block.header = header.seal_slow(); - // block changes let account: Address = [0x60; 20].into(); - let slot: B256 = B256::with_last_byte(5); + let slot = U256::from(5); - let bundle = BundleStateWithReceipts::new_init( - HashMap::from([( - account, - ( - None, - Some(Account { nonce: 3, balance: U256::from(20), bytecode_hash: None }), - HashMap::from([(slot, (U256::from(0), U256::from(15)))]), - ), - )]), - HashMap::from([( - number, - HashMap::from([( + let bundle = BundleStateWithReceipts::new( + BundleState::builder(number..=number) + .state_present_account_info( account, - ( - Some(Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None })), - vec![StorageEntry::new(slot, U256::from(10))], - ), - )]), - )]), - vec![], + AccountInfo { nonce: 3, balance: U256::from(20), ..Default::default() }, + ) + .state_storage(account, HashMap::from([(slot, (U256::ZERO, U256::from(15)))])) + .revert_account_info( + number, + account, + Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() })), + ) + .revert_storage(number, account, Vec::from([(slot, U256::from(10))])) + .build(), Receipts::from_vec(vec![vec![Some(Receipt { tx_type: TxType::EIP1559, success: false, @@ -203,5 +202,23 @@ fn block2( })]]), number, ); + + let mut extended = prev_state.clone(); + extended.extend(bundle.clone()); + let state_root = bundle_state_root(&extended); + assert_eq!( + state_root, + b256!("90101a13dd059fa5cca99ed93d1dc23657f63626c5b8f993a2ccbdf7446b64f8") + ); + + let mut block = SealedBlock::decode(&mut BLOCK_RLP.as_slice()).unwrap(); + block.withdrawals = Some(vec![Withdrawal::default()]); + let mut header = block.header.clone().unseal(); + header.number = number; + header.state_root = state_root; + // parent_hash points to block1 hash + header.parent_hash = parent_hash; + block.header = header.seal_slow(); + (SealedBlockWithSenders { block, senders: vec![Address::new([0x31; 20])] }, bundle) } From abb7fe6fb68cf99eac9e205d22a982be237cfae4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 29 Jan 2024 14:14:49 +0100 Subject: [PATCH 30/33] fix: only adjust ipc path if multiple instances are run (#6275) --- crates/node-core/src/args/rpc_server_args.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/node-core/src/args/rpc_server_args.rs b/crates/node-core/src/args/rpc_server_args.rs index f9c2dbcea5d1..c5fc8a668ce7 100644 --- a/crates/node-core/src/args/rpc_server_args.rs +++ b/crates/node-core/src/args/rpc_server_args.rs @@ -219,9 +219,10 @@ impl RpcServerArgs { // ws port is scaled by a factor of instance * 2 self.ws_port += instance * 2 - 2; - // also adjust the ipc path by appending the instance number to the path used for the - // endpoint - self.ipcpath = format!("{}-{}", self.ipcpath, instance); + // if multiple instances are being run, append the instance number to the ipc path + if instance > 1 { + self.ipcpath = format!("{}-{}", self.ipcpath, instance); + } } /// Set the http port to zero, to allow the OS to assign a random unused port when the rpc From 398432028a8f0e611207b7bca7c674891f496de7 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 29 Jan 2024 14:21:27 +0100 Subject: [PATCH 31/33] Fix typo Co-authored-by: Bjerg --- crates/net/network-api/src/reputation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/network-api/src/reputation.rs b/crates/net/network-api/src/reputation.rs index 109ae2bb0c66..1056192ee9dd 100644 --- a/crates/net/network-api/src/reputation.rs +++ b/crates/net/network-api/src/reputation.rs @@ -12,7 +12,7 @@ pub enum ReputationChangeKind { BadBlock, /// Peer sent a bad transaction message. E.g. Transactions which weren't recoverable. BadTransactions, - /// Peer sent a bad announcement messages, e.g. invalid transaction type for the configured + /// Peer sent a bad announcement message, e.g. invalid transaction type for the configured /// network. BadAnnouncement, /// Peer sent a message that included a hash or transaction that we already received from the From d48663778e99c15338eb51ddee17f059e449cded Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 29 Jan 2024 14:21:43 +0100 Subject: [PATCH 32/33] Fix typo Co-authored-by: Bjerg --- crates/net/network/src/transactions/validation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/network/src/transactions/validation.rs b/crates/net/network/src/transactions/validation.rs index 50da66bb9336..7eff95ce8a86 100644 --- a/crates/net/network/src/transactions/validation.rs +++ b/crates/net/network/src/transactions/validation.rs @@ -29,7 +29,7 @@ pub enum ValidationOutcome { ReportPeer, } -/// Filters valid entires in [`NewPooledTransactionHashes68`] and [`NewPooledTransactionHashes66`] +/// Filters valid entries in [`NewPooledTransactionHashes68`] and [`NewPooledTransactionHashes66`] /// in place, and flags misbehaving peers. pub trait FilterAnnouncement { /// Removes invalid entries from a [`NewPooledTransactionHashes68`] announcement. Returns From add991bba8a33fe67855b9cf1cfbe1546fec0eb8 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 29 Jan 2024 14:22:01 +0100 Subject: [PATCH 33/33] Fix typo Co-authored-by: Bjerg --- crates/net/network/src/transactions/validation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/network/src/transactions/validation.rs b/crates/net/network/src/transactions/validation.rs index 7eff95ce8a86..dc96e94c7f13 100644 --- a/crates/net/network/src/transactions/validation.rs +++ b/crates/net/network/src/transactions/validation.rs @@ -45,7 +45,7 @@ pub trait FilterAnnouncement { fn filter_valid_entries_66(&self, msg: &mut NewPooledTransactionHashes66) -> FilterOutcome; } -/// Outcome from filtering [`NewPooledTransactionHashes68`]. Signals to caller wether to penalize +/// Outcome from filtering [`NewPooledTransactionHashes68`]. Signals to caller whether to penalize /// the sender of the announcement or not. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FilterOutcome {