diff --git a/Cargo.lock b/Cargo.lock index 6751c510b312..713dc86e68cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3965,9 +3965,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index bb576f783182..56508dc77447 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -269,7 +269,7 @@ impl NodeCommand { let genesis_hash = init_genesis(db.clone(), self.chain.clone())?; - info!(target: "reth::cli", "{}", DisplayHardforks::from(self.chain.hardforks().clone())); + info!(target: "reth::cli", "{}", DisplayHardforks::new(self.chain.hardforks())); let consensus = self.consensus(); diff --git a/clippy.toml b/clippy.toml index 1a6975cb01aa..e7b6d30ab98e 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,2 +1,3 @@ msrv = "1.70" ignore-interior-mutability = ["bytes::Bytes", "reth_primitives::trie::nibbles::Nibbles"] +too-large-for-stack = 128 diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 48f5974bf258..36227f003b72 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -14,7 +14,7 @@ use reth_interfaces::{ RethResult, }; use reth_primitives::{ - BlockHash, BlockNumber, ForkBlock, SealedBlockWithSenders, SealedHeader, U256, + BlockHash, BlockNumber, ForkBlock, GotExpected, SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{ providers::BundleStateProvider, BundleStateDataProvider, BundleStateWithReceipts, Chain, @@ -221,10 +221,9 @@ impl AppendableChain { // check state root let state_root = provider.state_root(&bundle_state)?; if block.state_root != state_root { - return Err(ConsensusError::BodyStateRootDiff { - got: state_root, - expected: block.state_root, - } + return Err(ConsensusError::BodyStateRootDiff( + GotExpected { got: state_root, expected: block.state_root }.into(), + ) .into()) } } diff --git a/crates/consensus/beacon/src/engine/event.rs b/crates/consensus/beacon/src/engine/event.rs index 27f737c1b21e..548d0797fcc6 100644 --- a/crates/consensus/beacon/src/engine/event.rs +++ b/crates/consensus/beacon/src/engine/event.rs @@ -11,7 +11,7 @@ pub enum BeaconConsensusEngineEvent { /// A block was added to the canonical chain. CanonicalBlockAdded(Arc), /// A canonical chain was committed. - CanonicalChainCommitted(SealedHeader, Duration), + CanonicalChainCommitted(Box, Duration), /// A block was added to the fork chain. ForkBlockAdded(Arc), } diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index e0c53c04a375..84dbaf2c8206 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -664,8 +664,8 @@ where let status = match make_canonical_result { Ok(outcome) => { - match outcome { - CanonicalOutcome::AlreadyCanonical { ref header } => { + match &outcome { + CanonicalOutcome::AlreadyCanonical { header } => { // On Optimism, the proposers are allowed to reorg their own chain at will. cfg_if::cfg_if! { if #[cfg(feature = "optimism")] { @@ -679,7 +679,7 @@ where let _ = self.update_head(header.clone()); self.listeners.notify( BeaconConsensusEngineEvent::CanonicalChainCommitted( - header.clone(), + Box::new(header.clone()), elapsed, ), ); @@ -701,7 +701,7 @@ where } } } - CanonicalOutcome::Committed { ref head } => { + CanonicalOutcome::Committed { head } => { debug!( target: "consensus::engine", hash=?state.head_block_hash, @@ -712,7 +712,7 @@ where // new VALID update that moved the canonical chain forward let _ = self.update_head(head.clone()); self.listeners.notify(BeaconConsensusEngineEvent::CanonicalChainCommitted( - head.clone(), + Box::new(head.clone()), elapsed, )); } @@ -873,7 +873,6 @@ where self.update_head(head)?; self.update_finalized_block(update.finalized_block_hash)?; self.update_safe_block(update.safe_block_hash)?; - Ok(()) } @@ -899,9 +898,7 @@ where head_block.total_difficulty = self.blockchain.header_td_by_number(head_block.number)?.ok_or_else(|| { - RethError::Provider(ProviderError::TotalDifficultyNotFound { - block_number: head_block.number, - }) + RethError::Provider(ProviderError::TotalDifficultyNotFound(head_block.number)) })?; self.sync_state_updater.update_status(head_block); @@ -1562,9 +1559,9 @@ where let elapsed = self.record_make_canonical_latency(start, &make_canonical_result); match make_canonical_result { Ok(outcome) => { - if let CanonicalOutcome::Committed { ref head } = outcome { + if let CanonicalOutcome::Committed { head } = &outcome { self.listeners.notify(BeaconConsensusEngineEvent::CanonicalChainCommitted( - head.clone(), + Box::new(head.clone()), elapsed, )); } @@ -1661,7 +1658,7 @@ where warn!(target: "consensus::engine", invalid_hash=?bad_block.hash, invalid_number=?bad_block.number, "Bad block detected in unwind"); // update the `invalid_headers` cache with the new invalid headers - self.invalid_headers.insert(bad_block); + self.invalid_headers.insert(*bad_block); return None } diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 04b1c1922c80..2fb0a68c54b4 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -6,8 +6,9 @@ use reth_primitives::{ eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}, }, eip4844::calculate_excess_blob_gas, - BlockNumber, ChainSpec, Hardfork, Header, InvalidTransactionError, SealedBlock, SealedHeader, - Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, TxLegacy, + BlockNumber, ChainSpec, GotExpected, Hardfork, Header, InvalidTransactionError, SealedBlock, + SealedHeader, Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, + TxLegacy, }; use reth_provider::{AccountReader, HeaderProvider, WithdrawalsProvider}; use std::collections::{hash_map::Entry, HashMap}; @@ -203,20 +204,18 @@ pub fn validate_block_standalone( // Check ommers hash let ommers_hash = reth_primitives::proofs::calculate_ommers_root(&block.ommers); if block.header.ommers_hash != ommers_hash { - return Err(ConsensusError::BodyOmmersHashDiff { - got: ommers_hash, - expected: block.header.ommers_hash, - }) + return Err(ConsensusError::BodyOmmersHashDiff( + GotExpected { got: ommers_hash, expected: block.header.ommers_hash }.into(), + )) } // Check transaction root // TODO(onbjerg): This should probably be accessible directly on [Block] let transaction_root = reth_primitives::proofs::calculate_transaction_root(&block.body); if block.header.transactions_root != transaction_root { - return Err(ConsensusError::BodyTransactionRootDiff { - got: transaction_root, - expected: block.header.transactions_root, - }) + return Err(ConsensusError::BodyTransactionRootDiff( + GotExpected { got: transaction_root, expected: block.header.transactions_root }.into(), + )) } // EIP-4895: Beacon chain push withdrawals as operations @@ -227,10 +226,9 @@ pub fn validate_block_standalone( let header_withdrawals_root = block.withdrawals_root.as_ref().ok_or(ConsensusError::WithdrawalsRootMissing)?; if withdrawals_root != *header_withdrawals_root { - return Err(ConsensusError::BodyWithdrawalsRootDiff { - got: withdrawals_root, - expected: *header_withdrawals_root, - }) + return Err(ConsensusError::BodyWithdrawalsRootDiff( + GotExpected { got: withdrawals_root, expected: *header_withdrawals_root }.into(), + )) } } @@ -241,10 +239,10 @@ pub fn validate_block_standalone( let header_blob_gas_used = block.blob_gas_used.ok_or(ConsensusError::BlobGasUsedMissing)?; let total_blob_gas = block.blob_gas_used(); if total_blob_gas != header_blob_gas_used { - return Err(ConsensusError::BlobGasUsedDiff { - header_blob_gas_used, - expected_blob_gas_used: total_blob_gas, - }) + return Err(ConsensusError::BlobGasUsedDiff(GotExpected { + got: header_blob_gas_used, + expected: total_blob_gas, + })) } } @@ -300,10 +298,9 @@ pub fn validate_header_regarding_parent( } if parent.hash != child.parent_hash { - return Err(ConsensusError::ParentHashMismatch { - expected_parent_hash: parent.hash, - got_parent_hash: child.parent_hash, - }) + return Err(ConsensusError::ParentHashMismatch( + GotExpected { got: child.parent_hash, expected: parent.hash }.into(), + )) } // timestamp in past check @@ -343,7 +340,10 @@ pub fn validate_header_regarding_parent( .ok_or(ConsensusError::BaseFeeMissing)? }; if expected_base_fee != base_fee { - return Err(ConsensusError::BaseFeeDiff { expected: expected_base_fee, got: base_fee }) + return Err(ConsensusError::BaseFeeDiff(GotExpected { + expected: expected_base_fee, + got: base_fee, + })) } } @@ -435,8 +435,7 @@ pub fn validate_4844_header_with_parent( calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used); if expected_excess_blob_gas != excess_blob_gas { return Err(ConsensusError::ExcessBlobGasDiff { - expected: expected_excess_blob_gas, - got: excess_blob_gas, + diff: GotExpected { got: excess_blob_gas, expected: expected_excess_blob_gas }, parent_excess_blob_gas, parent_blob_gas_used, }) @@ -843,10 +842,10 @@ mod tests { // validate blob, it should fail blob gas used validation assert_eq!( validate_block_standalone(&block, &chain_spec), - Err(ConsensusError::BlobGasUsedDiff { - header_blob_gas_used: 1, - expected_blob_gas_used - }) + Err(ConsensusError::BlobGasUsedDiff(GotExpected { + got: 1, + expected: expected_blob_gas_used + })) ); } } diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index 0a68d139ec14..339db8403ed3 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use reth_primitives::{ - BlockHash, BlockNumber, Header, InvalidTransactionError, SealedBlock, SealedHeader, B256, U256, + BlockHash, BlockNumber, GotExpected, GotExpectedBoxed, Header, InvalidTransactionError, + SealedBlock, SealedHeader, B256, U256, }; use std::fmt::Debug; @@ -78,7 +79,6 @@ pub trait Consensus: Debug + Send + Sync { } /// Consensus Errors -#[allow(missing_docs)] #[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] pub enum ConsensusError { /// Error when the gas used in the header exceeds the gas limit. @@ -91,42 +91,22 @@ pub enum ConsensusError { }, /// Error when the hash of block ommer is different from the expected hash. - #[error("block ommer hash ({got}) is different from expected ({expected})")] - BodyOmmersHashDiff { - /// The actual ommer hash. - got: B256, - /// The expected ommer hash. - expected: B256, - }, + #[error("mismatched block ommer hash: {0}")] + BodyOmmersHashDiff(GotExpectedBoxed), /// Error when the state root in the block is different from the expected state root. - #[error("block state root ({got}) is different from expected ({expected})")] - BodyStateRootDiff { - /// The actual state root. - got: B256, - /// The expected state root. - expected: B256, - }, + #[error("mismatched block state root: {0}")] + BodyStateRootDiff(GotExpectedBoxed), /// Error when the transaction root in the block is different from the expected transaction /// root. - #[error("block transaction root ({got}) is different from expected ({expected})")] - BodyTransactionRootDiff { - /// The actual transaction root. - got: B256, - /// The expected transaction root. - expected: B256, - }, + #[error("mismatched block transaction root: {0}")] + BodyTransactionRootDiff(GotExpectedBoxed), /// Error when the withdrawals root in the block is different from the expected withdrawals /// root. - #[error("block withdrawals root ({got}) is different from expected ({expected})")] - BodyWithdrawalsRootDiff { - /// The actual withdrawals root. - got: B256, - /// The expected withdrawals root. - expected: B256, - }, + #[error("mismatched block withdrawals root: {0}")] + BodyWithdrawalsRootDiff(GotExpectedBoxed), /// Error when a block with a specific hash and number is already known. #[error("block with [hash={hash}, number={number}] is already known")] @@ -156,13 +136,8 @@ pub enum ConsensusError { }, /// Error when the parent hash does not match the expected parent hash. - #[error("parent hash {got_parent_hash} does not match the expected {expected_parent_hash}")] - ParentHashMismatch { - /// The expected parent hash. - expected_parent_hash: B256, - /// The actual parent hash. - got_parent_hash: B256, - }, + #[error("mismatched parent hash: {0}")] + ParentHashMismatch(GotExpectedBoxed), /// Error when the block timestamp is in the past compared to the parent timestamp. #[error("block timestamp {timestamp} is in the past compared to the parent timestamp {parent_timestamp}")] @@ -205,13 +180,8 @@ pub enum ConsensusError { BaseFeeMissing, /// Error when the block's base fee is different from the expected base fee. - #[error("block base fee ({got}) is different than expected: ({expected})")] - BaseFeeDiff { - /// The expected base fee. - expected: u64, - /// The actual base fee. - got: u64, - }, + #[error("block base fee mismatch: {0}")] + BaseFeeDiff(GotExpected), /// Error when there is a transaction signer recovery error. #[error("transaction signer recovery error")] @@ -293,27 +263,18 @@ pub enum ConsensusError { }, /// Error when the blob gas used in the header does not match the expected blob gas used. - #[error( - "blob gas used in the header {header_blob_gas_used} \ - does not match the expected blob gas used {expected_blob_gas_used}" - )] - BlobGasUsedDiff { - /// The blob gas used in the header. - header_blob_gas_used: u64, - /// The expected blob gas used. - expected_blob_gas_used: u64, - }, + #[error("blob gas used mismatch: {0}")] + BlobGasUsedDiff(GotExpected), /// Error when there is an invalid excess blob gas. #[error( - "invalid excess blob gas. Expected: {expected}, got: {got}. \ - Parent excess blob gas: {parent_excess_blob_gas}, parent blob gas used: {parent_blob_gas_used}" + "invalid excess blob gas: {diff}; \ + parent excess blob gas: {parent_excess_blob_gas}, \ + parent blob gas used: {parent_blob_gas_used}" )] ExcessBlobGasDiff { - /// The expected excess blob gas. - expected: u64, - /// The actual excess blob gas. - got: u64, + /// The excess blob gas diff. + diff: GotExpected, /// The parent excess blob gas. parent_excess_blob_gas: u64, /// The parent blob gas used. diff --git a/crates/interfaces/src/db.rs b/crates/interfaces/src/db.rs index 014b92e2799a..b428f85d93e3 100644 --- a/crates/interfaces/src/db.rs +++ b/crates/interfaces/src/db.rs @@ -1,5 +1,7 @@ +use thiserror::Error; + /// Database error type. -#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] pub enum DatabaseError { /// Failed to open the database. #[error("failed to open the database ({0})")] @@ -8,20 +10,8 @@ pub enum DatabaseError { #[error("failed to create a table ({0})")] CreateTable(i32), /// Failed to write a value into a table. - #[error( - "write operation {operation:?} failed for key \"{key}\" in table {table_name:?} ({code})", - key = reth_primitives::hex::encode(key), - )] - Write { - /// Database error code - code: i32, - /// Database write operation type - operation: DatabaseWriteOperation, - /// Table name - table_name: &'static str, - /// Write key - key: Box<[u8]>, - }, + #[error(transparent)] + Write(#[from] Box), /// Failed to read a value from a table. #[error("failed to read a value from a database table ({0})")] Read(i32), @@ -48,14 +38,42 @@ pub enum DatabaseError { LogLevelUnavailable(LogLevel), } +impl From for DatabaseError { + #[inline] + fn from(value: DatabaseWriteError) -> Self { + Self::Write(Box::new(value)) + } +} + +/// Database write error. +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error( + "write operation {operation:?} failed for key \"{key}\" in table {table_name:?} ({code})", + key = reth_primitives::hex::encode(key), +)] +pub struct DatabaseWriteError { + /// The error code. + pub code: i32, + /// The write operation type. + pub operation: DatabaseWriteOperation, + /// The table name. + pub table_name: &'static str, + /// The write key. + pub key: Vec, +} + /// Database write operation type. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[allow(missing_docs)] pub enum DatabaseWriteOperation { + /// Append cursor. CursorAppend, + /// Upsert cursor. CursorUpsert, + /// Insert cursor. CursorInsert, + /// Append duplicate cursor. CursorAppendDup, + /// Put. Put, } diff --git a/crates/interfaces/src/error.rs b/crates/interfaces/src/error.rs index 47057a56ad7c..b972124fdba4 100644 --- a/crates/interfaces/src/error.rs +++ b/crates/interfaces/src/error.rs @@ -1,7 +1,7 @@ -/// Result alias for [`RethError`] +/// Result alias for [`RethError`]. pub type RethResult = Result; -/// Core error variants possible when interacting with the blockchain +/// Core error variants possible when interacting with the blockchain. #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum RethError { @@ -38,3 +38,15 @@ impl From for RethError { RethError::Custom(err.to_string()) } } + +// We don't want these types to be too large because they're used in a lot of places. +const _SIZE_ASSERTIONS: () = { + // Main error. + let _: [(); 64] = [(); std::mem::size_of::()]; + + // Biggest variant. + let _: [(); 64] = [(); std::mem::size_of::()]; + + // Other common types. + let _: [(); 16] = [(); std::mem::size_of::()]; +}; diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 316fcb507e35..3ca47013539d 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -1,5 +1,7 @@ use crate::RethError; -use reth_primitives::{BlockNumHash, Bloom, PruneSegmentError, B256}; +use reth_primitives::{ + BlockNumHash, Bloom, GotExpected, GotExpectedBoxed, PruneSegmentError, B256, +}; use revm_primitives::EVMError; use thiserror::Error; @@ -22,21 +24,11 @@ pub enum BlockValidationError { #[error("incrementing balance in post execution failed")] IncrementBalanceFailed, /// Error when receipt root doesn't match expected value - #[error("receipt root {got} is different than expected {expected}")] - ReceiptRootDiff { - /// The actual receipt root - got: Box, - /// The expected receipt root - expected: Box, - }, + #[error("receipt root mismatch: {0}")] + ReceiptRootDiff(GotExpectedBoxed), /// Error when header bloom filter doesn't match expected value - #[error("header bloom filter {got} is different than expected {expected}")] - BloomLogDiff { - /// The actual bloom filter - got: Box, - /// The expected bloom filter - expected: Box, - }, + #[error("header bloom filter mismatch: {0}")] + BloomLogDiff(GotExpectedBoxed), /// Error when transaction gas limit exceeds available block gas #[error("transaction gas limit {transaction_gas_limit} is more than blocks available gas {block_available_gas}")] TransactionGasLimitMoreThanAvailableBlockGas { @@ -46,15 +38,10 @@ pub enum BlockValidationError { block_available_gas: u64, }, /// Error when block gas used doesn't match expected value - #[error( - "block gas used {got} is different from expected gas used {expected}.\n\ - Gas spent by each transaction: {gas_spent_by_tx:?}" - )] + #[error("block gas used mismatch: {gas}; gas spent by each transaction: {gas_spent_by_tx:?}")] BlockGasUsed { - /// The actual gas used - got: u64, - /// The expected gas used - expected: u64, + /// The gas diff. + gas: GotExpected, /// Gas spent by each transaction gas_spent_by_tx: Vec<(u64, u64)>, }, @@ -119,9 +106,9 @@ pub enum BlockExecutionError { )] AppendChainDoesntConnect { /// The tip of the current chain - chain_tip: BlockNumHash, + chain_tip: Box, /// The fork on the other chain - other_chain_fork: BlockNumHash, + other_chain_fork: Box, }, /// Only used for TestExecutor /// diff --git a/crates/interfaces/src/p2p/error.rs b/crates/interfaces/src/p2p/error.rs index 06259f551288..53cf68e4b4de 100644 --- a/crates/interfaces/src/p2p/error.rs +++ b/crates/interfaces/src/p2p/error.rs @@ -1,7 +1,9 @@ use super::headers::client::HeadersRequest; -use crate::{consensus, db}; +use crate::{consensus::ConsensusError, db}; use reth_network_api::ReputationChangeKind; -use reth_primitives::{BlockHashOrNumber, BlockNumber, Header, WithPeerId, B256}; +use reth_primitives::{ + BlockHashOrNumber, BlockNumber, GotExpected, GotExpectedBoxed, Header, WithPeerId, B256, +}; use std::ops::RangeInclusive; use thiserror::Error; use tokio::sync::{mpsc, oneshot}; @@ -118,47 +120,28 @@ pub type DownloadResult = Result; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum DownloadError { /* ==================== HEADER ERRORS ==================== */ - /// Header validation failed + /// Header validation failed. #[error("failed to validate header {hash}: {error}")] HeaderValidation { /// Hash of header failing validation hash: B256, /// The details of validation failure #[source] - error: consensus::ConsensusError, - }, - /// Received an invalid tip - #[error("received invalid tip: {received}. Expected {expected}")] - InvalidTip { - /// The hash of the received tip - received: B256, - /// The hash of the expected tip - expected: B256, - }, - /// Received a tip with an invalid tip number - #[error("received invalid tip number: {received}. Expected {expected}")] - InvalidTipNumber { - /// The block number of the received tip - received: u64, - /// The block number of the expected tip - expected: u64, + error: Box, }, + /// Received an invalid tip. + #[error("received invalid tip: {0}")] + InvalidTip(GotExpectedBoxed), + /// Received a tip with an invalid tip number. + #[error("received invalid tip number: {0}")] + InvalidTipNumber(GotExpected), /// Received a response to a request with unexpected start block - #[error("headers response starts at unexpected block: {received}. Expected {expected}")] - HeadersResponseStartBlockMismatch { - /// The block number of the received tip - received: u64, - /// The hash of the expected tip - expected: u64, - }, + #[error("headers response starts at unexpected block: {0}")] + HeadersResponseStartBlockMismatch(GotExpected), /// Received headers with less than expected items. - #[error("received less headers than expected: {received}. Expected {expected}")] - HeadersResponseTooShort { - /// How many headers we received. - received: u64, - /// How many headers we expected. - expected: u64, - }, + #[error("received less headers than expected: {0}")] + HeadersResponseTooShort(GotExpected), + /* ==================== BODIES ERRORS ==================== */ /// Block validation failed #[error("failed to validate body for header {hash}: {error}")] @@ -167,16 +150,11 @@ pub enum DownloadError { hash: B256, /// The details of validation failure #[source] - error: consensus::ConsensusError, + error: Box, }, /// Received more bodies than requested. - #[error("received more bodies than requested. Expected: {expected}. Received: {received}")] - TooManyBodies { - /// How many bodies we received. - received: usize, - /// How many bodies we expected. - expected: usize, - }, + #[error("received more bodies than requested: {0}")] + TooManyBodies(GotExpected), /// Headers missing from the database. #[error("header missing from the database: {block_number}")] MissingHeader { diff --git a/crates/interfaces/src/p2p/full_block.rs b/crates/interfaces/src/p2p/full_block.rs index e50e3bbd6acc..64d63fb1b50f 100644 --- a/crates/interfaces/src/p2p/full_block.rs +++ b/crates/interfaces/src/p2p/full_block.rs @@ -9,7 +9,7 @@ use crate::{ }; use futures::Stream; use reth_primitives::{ - BlockBody, Header, HeadersDirection, SealedBlock, SealedHeader, WithPeerId, B256, + BlockBody, GotExpected, Header, HeadersDirection, SealedBlock, SealedHeader, WithPeerId, B256, }; use std::{ cmp::Reverse, @@ -309,27 +309,24 @@ fn ensure_valid_body_response( let body_roots = block.calculate_roots(); if header.ommers_hash != body_roots.ommers_hash { - return Err(ConsensusError::BodyOmmersHashDiff { - got: body_roots.ommers_hash, - expected: header.ommers_hash, - }) + return Err(ConsensusError::BodyOmmersHashDiff( + GotExpected { got: body_roots.ommers_hash, expected: header.ommers_hash }.into(), + )) } if header.transactions_root != body_roots.tx_root { - return Err(ConsensusError::BodyTransactionRootDiff { - got: body_roots.tx_root, - expected: header.transactions_root, - }) + return Err(ConsensusError::BodyTransactionRootDiff( + GotExpected { got: body_roots.tx_root, expected: header.transactions_root }.into(), + )) } let withdrawals = block.withdrawals.as_deref().unwrap_or(&[]); if let Some(header_withdrawals_root) = header.withdrawals_root { let withdrawals_root = reth_primitives::proofs::calculate_withdrawals_root(withdrawals); if withdrawals_root != header_withdrawals_root { - return Err(ConsensusError::BodyWithdrawalsRootDiff { - got: withdrawals_root, - expected: header_withdrawals_root, - }) + return Err(ConsensusError::BodyWithdrawalsRootDiff( + GotExpected { got: withdrawals_root, expected: header_withdrawals_root }.into(), + )) } return Ok(()) } diff --git a/crates/interfaces/src/p2p/headers/downloader.rs b/crates/interfaces/src/p2p/headers/downloader.rs index bdee5f060dd5..35dd41a3e8d8 100644 --- a/crates/interfaces/src/p2p/headers/downloader.rs +++ b/crates/interfaces/src/p2p/headers/downloader.rs @@ -80,12 +80,13 @@ pub fn validate_header_download( parent: &SealedHeader, ) -> DownloadResult<()> { // validate header against parent - consensus - .validate_header_against_parent(header, parent) - .map_err(|error| DownloadError::HeaderValidation { hash: parent.hash(), error })?; + consensus.validate_header_against_parent(header, parent).map_err(|error| { + DownloadError::HeaderValidation { hash: parent.hash(), error: Box::new(error) } + })?; // validate header standalone - consensus - .validate_header(header) - .map_err(|error| DownloadError::HeaderValidation { hash: parent.hash(), error })?; + consensus.validate_header(header).map_err(|error| DownloadError::HeaderValidation { + hash: parent.hash(), + error: Box::new(error), + })?; Ok(()) } diff --git a/crates/interfaces/src/p2p/headers/error.rs b/crates/interfaces/src/p2p/headers/error.rs index ce4f6c46b66d..12eab9548cc1 100644 --- a/crates/interfaces/src/p2p/headers/error.rs +++ b/crates/interfaces/src/p2p/headers/error.rs @@ -13,10 +13,11 @@ pub enum HeadersDownloaderError { #[error("valid downloaded header cannot be attached to the local head: {error}")] DetachedHead { /// The local head we attempted to attach to. - local_head: SealedHeader, + local_head: Box, /// The header we attempted to attach. - header: SealedHeader, + header: Box, /// The error that occurred when attempting to attach the header. + #[source] error: Box, }, } diff --git a/crates/interfaces/src/provider.rs b/crates/interfaces/src/provider.rs index b9d8bd43b4a6..da079fcdd1e2 100644 --- a/crates/interfaces/src/provider.rs +++ b/crates/interfaces/src/provider.rs @@ -1,22 +1,23 @@ use reth_primitives::{ - Address, BlockHash, BlockHashOrNumber, BlockNumber, TxHashOrNumber, TxNumber, B256, + Address, BlockHash, BlockHashOrNumber, BlockNumber, GotExpected, TxHashOrNumber, TxNumber, B256, }; +use thiserror::Error; /// Bundled errors variants thrown by various providers. -#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)] +#[derive(Clone, Debug, Error, PartialEq, Eq)] pub enum ProviderError { /// Database error. #[error(transparent)] Database(#[from] crate::db::DatabaseError), /// The header number was not found for the given block hash. - #[error("block hash {0:?} does not exist in Headers table")] + #[error("block hash {0} does not exist in Headers table")] BlockHashNotFound(BlockHash), /// A block body is missing. #[error("block meta not found for block #{0}")] BlockBodyIndicesNotFound(BlockNumber), /// The transition id was found for the given address and storage key, but the changeset was /// not found. - #[error("storage ChangeSet address: ({address:?} key: {storage_key:?}) for block:#{block_number} does not exist")] + #[error("storage ChangeSet address: ({address} key: {storage_key:?}) for block #{block_number} does not exist")] StorageChangesetNotFound { /// The block number found for the address and storage key. block_number: BlockNumber, @@ -26,7 +27,7 @@ pub enum ProviderError { storage_key: B256, }, /// The block number was found for the given address, but the changeset was not found. - #[error("account {address:?} ChangeSet for block #{block_number} does not exist")] + #[error("account {address} ChangeSet for block #{block_number} does not exist")] AccountChangesetNotFound { /// Block number found for the address. block_number: BlockNumber, @@ -34,11 +35,8 @@ pub enum ProviderError { address: Address, }, /// The total difficulty for a block is missing. - #[error("total difficulty not found for block #{block_number}")] - TotalDifficultyNotFound { - /// The block number. - block_number: BlockNumber, - }, + #[error("total difficulty not found for block #{0}")] + TotalDifficultyNotFound(BlockNumber), /// when required header related data was not found but was required. #[error("no header found for {0:?}")] HeaderNotFound(BlockHashOrNumber), @@ -85,29 +83,11 @@ pub enum ProviderError { #[error("unable to find the block number for a given transaction index")] BlockNumberForTransactionIndexNotFound, /// Root mismatch. - #[error("merkle trie root mismatch at #{block_number} ({block_hash}): got {got}, expected {expected}")] - StateRootMismatch { - /// The expected root. - expected: B256, - /// The calculated root. - got: B256, - /// The block number. - block_number: BlockNumber, - /// The block hash. - block_hash: BlockHash, - }, + #[error("merkle trie {0}")] + StateRootMismatch(Box), /// Root mismatch during unwind - #[error("unwind merkle trie root mismatch at #{block_number} ({block_hash}): got {got}, expected {expected}")] - UnwindStateRootMismatch { - /// Expected root - expected: B256, - /// Calculated root - got: B256, - /// Target block number - block_number: BlockNumber, - /// Block hash - block_hash: BlockHash, - }, + #[error("unwind merkle trie {0}")] + UnwindStateRootMismatch(Box), /// State is not available for the given block number because it is pruned. #[error("state at block #{0} is pruned")] StateAtBlockPruned(BlockNumber), @@ -115,3 +95,15 @@ pub enum ProviderError { #[error("this provider does not support this request")] UnsupportedProvider, } + +/// A root mismatch error at a given block height. +#[derive(Clone, Debug, Error, PartialEq, Eq)] +#[error("root mismatch at #{block_number} ({block_hash}): {root}")] +pub struct RootMismatch { + /// The target block root diff. + pub root: GotExpected, + /// The target block number. + pub block_number: BlockNumber, + /// The target block hash. + pub block_hash: BlockHash, +} diff --git a/crates/interfaces/src/test_utils/headers.rs b/crates/interfaces/src/test_utils/headers.rs index 469a91c62fe4..7fee42101dcc 100644 --- a/crates/interfaces/src/test_utils/headers.rs +++ b/crates/interfaces/src/test_utils/headers.rs @@ -161,7 +161,7 @@ impl Stream for TestDownload { this.done = true; return Poll::Ready(Some(Err(DownloadError::HeaderValidation { hash: empty.hash(), - error, + error: Box::new(error), }))) } diff --git a/crates/net/downloaders/src/bodies/request.rs b/crates/net/downloaders/src/bodies/request.rs index 4fd4fb7849e1..cc54acf3b800 100644 --- a/crates/net/downloaders/src/bodies/request.rs +++ b/crates/net/downloaders/src/bodies/request.rs @@ -8,7 +8,9 @@ use reth_interfaces::{ priority::Priority, }, }; -use reth_primitives::{BlockBody, PeerId, SealedBlock, SealedHeader, WithPeerId, B256}; +use reth_primitives::{ + BlockBody, GotExpected, PeerId, SealedBlock, SealedHeader, WithPeerId, B256, +}; use std::{ collections::VecDeque, mem, @@ -133,10 +135,10 @@ where } if response_len > request_len { - return Err(DownloadError::TooManyBodies { + return Err(DownloadError::TooManyBodies(GotExpected { + got: response_len, expected: request_len, - received: response_len, - }) + })) } // Buffer block responses @@ -185,7 +187,7 @@ where // Body is invalid, put the header back and return an error let hash = block.hash(); self.pending_headers.push_front(block.header); - return Err(DownloadError::BodyValidation { hash, error }) + return Err(DownloadError::BodyValidation { hash, error: Box::new(error) }) } self.buffer.push(BlockResponse::Full(block)); diff --git a/crates/net/downloaders/src/headers/reverse_headers.rs b/crates/net/downloaders/src/headers/reverse_headers.rs index 53fa2cf504bb..9b80aaa5f12a 100644 --- a/crates/net/downloaders/src/headers/reverse_headers.rs +++ b/crates/net/downloaders/src/headers/reverse_headers.rs @@ -18,7 +18,8 @@ use reth_interfaces::{ }, }; use reth_primitives::{ - BlockHashOrNumber, BlockNumber, Header, HeadersDirection, PeerId, SealedHeader, B256, + BlockHashOrNumber, BlockNumber, GotExpected, Header, HeadersDirection, PeerId, SealedHeader, + B256, }; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; use std::{ @@ -38,13 +39,18 @@ use tracing::{error, trace}; const REQUESTS_PER_PEER_MULTIPLIER: usize = 5; /// Wrapper for internal downloader errors. -#[allow(clippy::large_enum_variant)] #[derive(Error, Debug)] enum ReverseHeadersDownloaderError { #[error(transparent)] Downloader(#[from] HeadersDownloaderError), #[error(transparent)] - Response(#[from] HeadersResponseError), + Response(#[from] Box), +} + +impl From for ReverseHeadersDownloaderError { + fn from(value: HeadersResponseError) -> Self { + Self::Response(Box::new(value)) + } } /// Downloads headers concurrently. @@ -210,17 +216,19 @@ where Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::InvalidTip { received: header.hash(), expected: hash }, + error: DownloadError::InvalidTip( + GotExpected { got: header.hash(), expected: hash }.into(), + ), }) } SyncTargetBlock::Number(number) if header.number != number => { Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::InvalidTipNumber { - received: header.number, + error: DownloadError::InvalidTipNumber(GotExpected { + got: header.number, expected: number, - }, + }), }) } _ => Ok(()), @@ -274,7 +282,10 @@ where return Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::HeaderValidation { hash: head.hash(), error }, + error: DownloadError::HeaderValidation { + hash: head.hash(), + error: Box::new(error), + }, } .into()) } @@ -285,8 +296,8 @@ where // Replace the last header with a detached variant error!(target: "downloaders::headers", ?error, number = last_header.number, hash = ?last_header.hash, "Header cannot be attached to known canonical chain"); return Err(HeadersDownloaderError::DetachedHead { - local_head: head.clone(), - header: last_header.clone(), + local_head: Box::new(head.clone()), + header: Box::new(last_header.clone()), error: Box::new(error), } .into()) @@ -374,10 +385,9 @@ where return Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::InvalidTip { - received: target.hash(), - expected: hash, - }, + error: DownloadError::InvalidTip( + GotExpected { got: target.hash(), expected: hash }.into(), + ), } .into()) } @@ -387,10 +397,10 @@ where return Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::InvalidTipNumber { - received: target.number, + error: DownloadError::InvalidTipNumber(GotExpected { + got: target.number, expected: number, - }, + }), } .into()) } @@ -400,10 +410,9 @@ where return Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::InvalidTip { - received: target.hash(), - expected: hash, - }, + error: DownloadError::InvalidTip( + GotExpected { got: target.hash(), expected: hash }.into(), + ), } .into()) } @@ -461,10 +470,10 @@ where if (headers.len() as u64) != request.limit { return Err(HeadersResponseError { peer_id: Some(peer_id), - error: DownloadError::HeadersResponseTooShort { - received: headers.len() as u64, + error: DownloadError::HeadersResponseTooShort(GotExpected { + got: headers.len() as u64, expected: request.limit, - }, + }), request, } .into()) @@ -482,10 +491,10 @@ where return Err(HeadersResponseError { request, peer_id: Some(peer_id), - error: DownloadError::HeadersResponseStartBlockMismatch { - received: highest.number, + error: DownloadError::HeadersResponseStartBlockMismatch(GotExpected { + got: highest.number, expected: requested_block_number, - }, + }), } .into()) } @@ -530,8 +539,8 @@ where /// Handles the error of a bad response /// /// This will re-submit the request. - fn on_headers_error(&mut self, err: HeadersResponseError) { - let HeadersResponseError { request, peer_id, error } = err; + fn on_headers_error(&mut self, err: Box) { + let HeadersResponseError { request, peer_id, error } = *err; self.penalize_peer(peer_id, &error); @@ -955,10 +964,12 @@ impl Ord for OrderedHeadersResponse { } /// Type returned if a bad response was processed -#[derive(Debug)] +#[derive(Debug, Error)] +#[error("error requesting headers from peer {peer_id:?}: {error}; request: {request:?}")] struct HeadersResponseError { request: HeadersRequest, peer_id: Option, + #[source] error: DownloadError, } @@ -972,18 +983,6 @@ impl HeadersResponseError { } } -impl std::fmt::Display for HeadersResponseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Error requesting headers from peer {:?}. Error: {}. Request: {:?}", - self.peer_id, self.error, self.request, - ) - } -} - -impl std::error::Error for HeadersResponseError {} - /// The block to which we want to close the gap: (local head...sync target] /// This tracks the sync target block, so this could be either a block number or hash. #[derive(Clone, Debug)] diff --git a/crates/net/eth-wire/src/errors/eth.rs b/crates/net/eth-wire/src/errors/eth.rs index 49c6f494135b..6839bccac3e4 100644 --- a/crates/net/eth-wire/src/errors/eth.rs +++ b/crates/net/eth-wire/src/errors/eth.rs @@ -2,7 +2,7 @@ use crate::{ errors::P2PStreamError, version::ParseVersionError, DisconnectReason, EthMessageID, EthVersion, }; -use reth_primitives::{Chain, ValidationError, B256}; +use reth_primitives::{Chain, GotExpected, GotExpectedBoxed, ValidationError, B256}; use std::io; /// Errors when sending/receiving messages @@ -68,12 +68,12 @@ pub enum EthHandshakeError { NoResponse, #[error(transparent)] InvalidFork(#[from] ValidationError), - #[error("mismatched genesis in status message: got {got}, expected {expected}")] - MismatchedGenesis { expected: B256, got: B256 }, - #[error("mismatched protocol version in status message: got {got}, expected {expected}")] - MismatchedProtocolVersion { expected: u8, got: u8 }, - #[error("mismatched chain in status message: got {got}, expected {expected}")] - MismatchedChain { expected: Chain, got: Chain }, + #[error("mismatched genesis in status message: {0}")] + MismatchedGenesis(GotExpectedBoxed), + #[error("mismatched protocol version in status message: {0}")] + MismatchedProtocolVersion(GotExpected), + #[error("mismatched chain in status message: {0}")] + MismatchedChain(GotExpected), #[error("total difficulty bitlen is too large: got {got}, maximum {maximum}")] - TotalDifficultyBitLenTooLarge { maximum: usize, got: usize }, + TotalDifficultyBitLenTooLarge { got: usize, maximum: usize }, } diff --git a/crates/net/eth-wire/src/errors/p2p.rs b/crates/net/eth-wire/src/errors/p2p.rs index b10d3d347551..be51badf24ca 100644 --- a/crates/net/eth-wire/src/errors/p2p.rs +++ b/crates/net/eth-wire/src/errors/p2p.rs @@ -1,9 +1,11 @@ -//! Error handling for [`P2PStream`](crate::P2PStream) -use std::io; +//! Error handling for [`P2PStream`](crate::P2PStream). use crate::{ capability::SharedCapabilityError, disconnect::UnknownDisconnectReason, DisconnectReason, + ProtocolVersion, }; +use reth_primitives::GotExpected; +use std::io; /// Errors when sending/receiving p2p messages. These should result in kicking the peer. #[derive(thiserror::Error, Debug)] @@ -29,8 +31,8 @@ pub enum P2PStreamError { PingTimeout, #[error(transparent)] ParseVersionError(#[from] SharedCapabilityError), - #[error("mismatched protocol version in Hello message. expected: {expected:?}, got: {got:?}")] - MismatchedProtocolVersion { expected: u8, got: u8 }, + #[error("mismatched protocol version in Hello message: {0}")] + MismatchedProtocolVersion(GotExpected), #[error("started ping task before the handshake completed")] PingBeforeHandshake, #[error("too many messages buffered before sending")] diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index b09241e9d226..a72e9df66e77 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -9,7 +9,7 @@ use futures::{ready, Sink, SinkExt, StreamExt}; use pin_project::pin_project; use reth_primitives::{ bytes::{Bytes, BytesMut}, - ForkFilter, + ForkFilter, GotExpected, }; use std::{ pin::Pin, @@ -102,28 +102,27 @@ where ); if status.genesis != resp.genesis { self.inner.disconnect(DisconnectReason::ProtocolBreach).await?; - return Err(EthHandshakeError::MismatchedGenesis { - expected: status.genesis, - got: resp.genesis, - } + return Err(EthHandshakeError::MismatchedGenesis( + GotExpected { expected: status.genesis, got: resp.genesis }.into(), + ) .into()) } if status.version != resp.version { self.inner.disconnect(DisconnectReason::ProtocolBreach).await?; - return Err(EthHandshakeError::MismatchedProtocolVersion { - expected: status.version, + return Err(EthHandshakeError::MismatchedProtocolVersion(GotExpected { got: resp.version, - } + expected: status.version, + }) .into()) } if status.chain != resp.chain { self.inner.disconnect(DisconnectReason::ProtocolBreach).await?; - return Err(EthHandshakeError::MismatchedChain { - expected: status.chain, + return Err(EthHandshakeError::MismatchedChain(GotExpected { got: resp.chain, - } + expected: status.chain, + }) .into()) } @@ -132,8 +131,8 @@ where if status.total_difficulty.bit_len() > 100 { self.inner.disconnect(DisconnectReason::ProtocolBreach).await?; return Err(EthHandshakeError::TotalDifficultyBitLenTooLarge { - maximum: 100, got: status.total_difficulty.bit_len(), + maximum: 100, } .into()) } @@ -455,7 +454,7 @@ mod tests { assert!(matches!( handshake_res, Err(EthStreamError::EthHandshakeError( - EthHandshakeError::TotalDifficultyBitLenTooLarge { maximum: 100, got: 101 } + EthHandshakeError::TotalDifficultyBitLenTooLarge { got: 101, maximum: 100 } )) )); }); @@ -470,7 +469,7 @@ mod tests { assert!(matches!( handshake_res, Err(EthStreamError::EthHandshakeError( - EthHandshakeError::TotalDifficultyBitLenTooLarge { maximum: 100, got: 101 } + EthHandshakeError::TotalDifficultyBitLenTooLarge { got: 101, maximum: 100 } )) )); diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index c956f00f766f..7db22470c99d 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -13,11 +13,11 @@ use reth_codecs::derive_arbitrary; use reth_metrics::metrics::counter; use reth_primitives::{ bytes::{Buf, BufMut, Bytes, BytesMut}, - hex, + hex, GotExpected, }; use std::{ collections::{BTreeSet, HashMap, HashSet, VecDeque}, - io, + fmt, io, pin::Pin, task::{ready, Context, Poll}, time::Duration, @@ -151,10 +151,10 @@ where if (hello.protocol_version as u8) != their_hello.protocol_version as u8 { // send a disconnect message notifying the peer of the protocol version mismatch self.send_disconnect(DisconnectReason::IncompatibleP2PProtocolVersion).await?; - return Err(P2PStreamError::MismatchedProtocolVersion { - expected: hello.protocol_version as u8, - got: their_hello.protocol_version as u8, - }) + return Err(P2PStreamError::MismatchedProtocolVersion(GotExpected { + got: their_hello.protocol_version, + expected: hello.protocol_version, + })) } // determine shared capabilities (currently returns only one capability) @@ -823,6 +823,12 @@ pub enum ProtocolVersion { V5 = 5, } +impl fmt::Display for ProtocolVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "v{}", *self as u8) + } +} + impl Encodable for ProtocolVersion { fn encode(&self, out: &mut dyn BufMut) { (*self as u8).encode(out) @@ -970,9 +976,9 @@ mod tests { Ok((_, hello)) => { panic!("expected handshake to fail, instead got a successful Hello: {hello:?}") } - Err(P2PStreamError::MismatchedProtocolVersion { expected, got }) => { - assert_eq!(expected, server_hello.protocol_version as u8); + Err(P2PStreamError::MismatchedProtocolVersion(GotExpected { got, expected })) => { assert_ne!(expected, got); + assert_eq!(expected, server_hello.protocol_version); } Err(other_err) => { panic!("expected mismatched protocol version error, got {other_err:?}") @@ -993,9 +999,9 @@ mod tests { Ok((_, hello)) => { panic!("expected handshake to fail, instead got a successful Hello: {hello:?}") } - Err(P2PStreamError::MismatchedProtocolVersion { expected, got }) => { - assert_eq!(expected, client_hello.protocol_version as u8); + Err(P2PStreamError::MismatchedProtocolVersion(GotExpected { got, expected })) => { assert_ne!(expected, got); + assert_eq!(expected, client_hello.protocol_version); } Err(other_err) => { panic!("expected mismatched protocol version error, got {other_err:?}") diff --git a/crates/net/network-api/src/lib.rs b/crates/net/network-api/src/lib.rs index 6f5216b22792..10aacd0832c3 100644 --- a/crates/net/network-api/src/lib.rs +++ b/crates/net/network-api/src/lib.rs @@ -120,7 +120,7 @@ pub struct PeerInfo { /// The identifier of the remote peer pub remote_id: PeerId, /// The client's name and version - pub client_version: Arc, + pub client_version: Arc, /// The peer's address we're connected to pub remote_addr: SocketAddr, /// The local address of the connection @@ -130,7 +130,7 @@ pub struct PeerInfo { /// The negotiated eth version. pub eth_version: EthVersion, /// The Status message the peer sent for the `eth` handshake - pub status: Status, + pub status: Arc, } /// The direction of the connection. diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 523f3f5ce3a3..9015fc2e28c7 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -914,13 +914,13 @@ pub enum NetworkEvent { /// The remote addr of the peer to which a session was established. remote_addr: SocketAddr, /// The client version of the peer to which a session was established. - client_version: Arc, + client_version: Arc, /// Capabilities the peer announced capabilities: Arc, /// A request channel to the session task. messages: PeerRequestSender, /// The status of the peer to which a session was established. - status: Status, + status: Arc, /// negotiated eth version of the session version: EthVersion, }, diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index 8583adb33866..b750f674eec4 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -40,7 +40,7 @@ use tokio_stream::wrappers::ReceiverStream; use tokio_util::sync::PollSender; use tracing::{debug, trace}; -/// Constants for timeout updating +// Constants for timeout updating. /// Minimum timeout value const MINIMUM_TIMEOUT: Duration = Duration::from_secs(2); @@ -51,6 +51,11 @@ const SAMPLE_IMPACT: f64 = 0.1; /// Amount of RTTs before timeout const TIMEOUT_SCALING: u32 = 3; +/// The type of the underlying peer network connection. +// This type is boxed because the underlying stream is ~6KB, +// mostly coming from `P2PStream`'s `snap::Encoder` (2072), and `ECIESStream` (3600). +pub type PeerConnection = Box>>>>; + /// The type that advances an established session by listening for incoming messages (from local /// node or read from connection) and emitting events back to the /// [`SessionManager`](super::SessionManager). @@ -65,7 +70,7 @@ pub(crate) struct ActiveSession { /// Keeps track of request ids. pub(crate) next_id: u64, /// The underlying connection. - pub(crate) conn: EthStream>>>, + pub(crate) conn: PeerConnection, /// Identifier of the node we're connected to. pub(crate) remote_peer_id: PeerId, /// The address we're connected to. @@ -760,8 +765,6 @@ fn calculate_new_timeout(current_timeout: Duration, estimated_rtt: Duration) -> } #[cfg(test)] mod tests { - #![allow(dead_code)] - use super::*; use crate::session::{ config::{INITIAL_REQUEST_TIMEOUT, PROTOCOL_BREACH_REQUEST_TIMEOUT}, @@ -784,7 +787,7 @@ mod tests { } struct SessionBuilder { - remote_capabilities: Arc, + _remote_capabilities: Arc, active_session_tx: mpsc::Sender, active_session_rx: ReceiverStream, to_sessions: Vec>, @@ -914,7 +917,7 @@ mod tests { Self { next_id: 0, - remote_capabilities: Arc::new(Capabilities::from(vec![])), + _remote_capabilities: Arc::new(Capabilities::from(vec![])), active_session_tx, active_session_rx: ReceiverStream::new(active_session_rx), to_sessions: vec![], diff --git a/crates/net/network/src/session/handle.rs b/crates/net/network/src/session/handle.rs index 2cdc33d321f3..73664f73fedf 100644 --- a/crates/net/network/src/session/handle.rs +++ b/crates/net/network/src/session/handle.rs @@ -1,24 +1,22 @@ -//! Session handles +//! Session handles. + +use super::active::PeerConnection; use crate::{ message::PeerMessage, session::{Direction, SessionId}, }; -use reth_ecies::{stream::ECIESStream, ECIESError}; +use reth_ecies::ECIESError; use reth_eth_wire::{ capability::{Capabilities, CapabilityMessage}, errors::EthStreamError, - DisconnectReason, EthStream, EthVersion, P2PStream, Status, + DisconnectReason, EthVersion, Status, }; -use reth_net_common::bandwidth_meter::MeteredStream; use reth_network_api::PeerInfo; use reth_primitives::PeerId; use std::{io, net::SocketAddr, sync::Arc, time::Instant}; -use tokio::{ - net::TcpStream, - sync::{ - mpsc::{self, error::SendError}, - oneshot, - }, +use tokio::sync::{ + mpsc::{self, error::SendError}, + oneshot, }; /// A handler attached to a peer session that's not authenticated yet, pending Handshake and hello @@ -70,13 +68,13 @@ pub struct ActiveSessionHandle { /// Sender half of the command channel used send commands _to_ the spawned session pub(crate) commands_to_session: mpsc::Sender, /// The client's name and version - pub(crate) client_version: Arc, + pub(crate) client_version: Arc, /// The address we're connected to pub(crate) remote_addr: SocketAddr, /// The local address of the connection. pub(crate) local_addr: Option, /// The Status message the peer sent for the `eth` handshake - pub(crate) status: Status, + pub(crate) status: Arc, } // === impl ActiveSessionHandle === @@ -128,7 +126,7 @@ impl ActiveSessionHandle { } /// Returns the client's name and version. - pub fn client_version(&self) -> Arc { + pub fn client_version(&self) -> Arc { self.client_version.clone() } @@ -147,7 +145,7 @@ impl ActiveSessionHandle { capabilities: self.capabilities.clone(), client_version: self.client_version.clone(), eth_version: self.version, - status: self.status, + status: self.status.clone(), } } } @@ -172,10 +170,10 @@ pub enum PendingSessionEvent { /// All capabilities the peer announced capabilities: Arc, /// The Status message the peer sent for the `eth` handshake - status: Status, + status: Arc, /// The actual connection stream which can be used to send and receive `eth` protocol /// messages - conn: EthStream>>>, + conn: PeerConnection, /// The direction of the session, either `Inbound` or `Outgoing` direction: Direction, /// The remote node's user agent, usually containing the client name and version @@ -192,8 +190,7 @@ pub enum PendingSessionEvent { /// The error that caused the disconnect error: Option, }, - - /// Thrown when unable to establish a [`TcpStream`]. + /// Thrown when unable to establish a [`TcpStream`](tokio::net::TcpStream). OutgoingConnectionError { /// The remote node's socket address remote_addr: SocketAddr, diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 4b154a5cd1ce..61bf58e15993 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -465,9 +465,9 @@ impl SessionManager { self.spawn(session); - let client_version = Arc::new(client_id); + let client_version = client_id.into(); let handle = ActiveSessionHandle { - status, + status: status.clone(), direction, session_id, remote_id: peer_id, @@ -595,13 +595,13 @@ pub enum SessionEvent { /// The remote node's socket address remote_addr: SocketAddr, /// The user agent of the remote node, usually containing the client name and version - client_version: Arc, + client_version: Arc, /// The capabilities the remote node has announced capabilities: Arc, /// negotiated eth version version: EthVersion, /// The Status message the peer sent during the `eth` handshake - status: Status, + status: Arc, /// The channel for sending messages to the peer with the session messages: PeerRequestSender, /// The direction of the session, either `Inbound` or `Outgoing` @@ -917,8 +917,8 @@ async fn authenticate_stream( local_addr, peer_id: their_hello.id, capabilities: Arc::new(Capabilities::from(their_hello.capabilities)), - status: their_status, - conn: eth_stream, + status: Arc::new(their_status), + conn: Box::new(eth_stream), direction, client_id: their_hello.client_version, } diff --git a/crates/net/network/src/state.rs b/crates/net/network/src/state.rs index 389f6b57dff0..63351f5f9967 100644 --- a/crates/net/network/src/state.rs +++ b/crates/net/network/src/state.rs @@ -130,7 +130,7 @@ where &mut self, peer: PeerId, capabilities: Arc, - status: Status, + status: Arc, request_tx: PeerRequestSender, timeout: Arc, ) { @@ -527,7 +527,7 @@ mod tests { }; use reth_eth_wire::{ capability::{Capabilities, Capability}, - BlockBodies, EthVersion, Status, + BlockBodies, EthVersion, }; use reth_interfaces::p2p::{bodies::client::BodiesClient, error::RequestError}; use reth_primitives::{BlockBody, Header, PeerId, B256}; @@ -572,7 +572,7 @@ mod tests { state.on_session_activated( peer_id, capabilities(), - Status::default(), + Arc::default(), peer_tx, Arc::new(AtomicU64::new(1)), ); diff --git a/crates/net/network/src/swarm.rs b/crates/net/network/src/swarm.rs index 76e5dfc036fd..9f32efd16852 100644 --- a/crates/net/network/src/swarm.rs +++ b/crates/net/network/src/swarm.rs @@ -137,7 +137,7 @@ where self.state.on_session_activated( peer_id, capabilities.clone(), - status, + status.clone(), messages.clone(), timeout, ); @@ -394,12 +394,12 @@ pub(crate) enum SwarmEvent { SessionEstablished { peer_id: PeerId, remote_addr: SocketAddr, - client_version: Arc, + client_version: Arc, capabilities: Arc, /// negotiated eth version version: EthVersion, messages: PeerRequestSender, - status: Status, + status: Arc, direction: Direction, }, SessionClosed { diff --git a/crates/net/network/src/transactions.rs b/crates/net/network/src/transactions.rs index c4b40eb005c4..812e18a16f08 100644 --- a/crates/net/network/src/transactions.rs +++ b/crates/net/network/src/transactions.rs @@ -1043,7 +1043,7 @@ struct Peer { version: EthVersion, /// The peer's client version. #[allow(unused)] - client_version: Arc, + client_version: Arc, } /// The type responsible for fetching missing transactions from peers. diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index ae244653b988..4f5831de4900 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -20,11 +20,11 @@ use reth_payload_builder::{ }; use reth_primitives::{ bytes::BytesMut, - calculate_excess_blob_gas, constants::{ eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS, ETHEREUM_BLOCK_GAS_LIMIT, RETH_CLIENT_VERSION, SLOT_DURATION, }, + eip4844::calculate_excess_blob_gas, proofs, revm::{compat::into_reth_log, env::tx_env_with_recovered}, Block, BlockNumberOrTag, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, Receipts, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 9e7fb81bdf33..7f94a4d501f0 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -136,22 +136,26 @@ pub struct SealedBlock { impl SealedBlock { /// Create a new sealed block instance using the sealed header and block body. + #[inline] pub fn new(header: SealedHeader, body: BlockBody) -> Self { let BlockBody { transactions, ommers, withdrawals } = body; Self { header, body: transactions, ommers, withdrawals } } /// Header hash. + #[inline] pub fn hash(&self) -> B256 { self.header.hash() } /// Splits the sealed block into underlying components + #[inline] pub fn split(self) -> (SealedHeader, Vec, Vec
) { (self.header, self.body, self.ommers) } /// Splits the [BlockBody] and [SealedHeader] into separate components + #[inline] pub fn split_header_body(self) -> (SealedHeader, BlockBody) { ( self.header, diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 73f852fdb31b..3a545f3e23f6 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -1240,12 +1240,12 @@ impl Display for DisplayFork { /// A container for pretty-printing a list of hardforks. /// -/// # Example +/// # Examples /// /// ``` /// # use reth_primitives::MAINNET; /// # use reth_primitives::DisplayHardforks; -/// println!("{}", DisplayHardforks::from(MAINNET.hardforks().clone())); +/// println!("{}", DisplayHardforks::new(MAINNET.hardforks())); /// ``` /// /// An example of the output: @@ -1307,18 +1307,22 @@ impl Display for DisplayHardforks { } } -impl From for DisplayHardforks -where - I: IntoIterator, -{ - fn from(iter: I) -> Self { +impl DisplayHardforks { + /// Creates a new [`DisplayHardforks`] from an iterator of hardforks. + pub fn new(hardforks: &BTreeMap) -> Self { + Self::from_iter(hardforks.iter()) + } +} + +impl<'a, 'b> FromIterator<(&'a Hardfork, &'b ForkCondition)> for DisplayHardforks { + fn from_iter>(iter: T) -> Self { let mut pre_merge = Vec::new(); let mut with_merge = Vec::new(); let mut post_merge = Vec::new(); - for (fork, condition) in iter.into_iter() { + for (fork, condition) in iter { let display_fork = - DisplayFork { name: fork.to_string(), activated_at: condition, eip: None }; + DisplayFork { name: fork.to_string(), activated_at: *condition, eip: None }; match condition { ForkCondition::Block(_) => { @@ -1406,7 +1410,7 @@ mod tests { #[test] fn test_hardfork_list_display_mainnet() { assert_eq!( - DisplayHardforks::from(MAINNET.hardforks().clone()).to_string(), + DisplayHardforks::new(MAINNET.hardforks()).to_string(), "Pre-merge hard forks (block based): - Frontier @0 - Homestead @1150000 @@ -1440,7 +1444,7 @@ Post-merge hard forks (timestamp based): .with_fork(Hardfork::Shanghai, ForkCondition::Never) .build(); assert_eq!( - DisplayHardforks::from(spec.hardforks().clone()).to_string(), + DisplayHardforks::new(spec.hardforks()).to_string(), "Pre-merge hard forks (block based): - Frontier @0 " diff --git a/crates/primitives/src/eip4844.rs b/crates/primitives/src/eip4844.rs index 6d7668b59521..c93017e8dda1 100644 --- a/crates/primitives/src/eip4844.rs +++ b/crates/primitives/src/eip4844.rs @@ -1,28 +1,19 @@ -//! Helpers for working with EIP-4844 blob fee -use crate::constants::eip4844::TARGET_DATA_GAS_PER_BLOCK; +//! Helpers for working with EIP-4844 blob fee. + #[cfg(feature = "c-kzg")] -use crate::{constants::eip4844::VERSIONED_HASH_VERSION_KZG, kzg::KzgCommitment, B256}; +use crate::{constants::eip4844::VERSIONED_HASH_VERSION_KZG, B256}; #[cfg(feature = "c-kzg")] use sha2::{Digest, Sha256}; // re-exports from revm for calculating blob fee -pub use revm_primitives::calc_blob_gasprice; +pub use revm_primitives::{calc_blob_gasprice, calc_excess_blob_gas as calculate_excess_blob_gas}; /// Calculates the versioned hash for a KzgCommitment /// /// Specified in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension) #[cfg(feature = "c-kzg")] -pub fn kzg_to_versioned_hash(commitment: KzgCommitment) -> B256 { +pub fn kzg_to_versioned_hash(commitment: c_kzg::KzgCommitment) -> B256 { let mut res = Sha256::digest(commitment.as_slice()); res[0] = VERSIONED_HASH_VERSION_KZG; B256::new(res.into()) } - -/// Calculates the excess data gas for the next block, after applying the current set of blobs on -/// top of the excess data gas. -/// -/// Specified in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension) -pub fn calculate_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 { - let excess_blob_gas = parent_excess_blob_gas + parent_blob_gas_used; - excess_blob_gas.saturating_sub(TARGET_DATA_GAS_PER_BLOCK) -} diff --git a/crates/primitives/src/error.rs b/crates/primitives/src/error.rs new file mode 100644 index 000000000000..a6291cbbcee3 --- /dev/null +++ b/crates/primitives/src/error.rs @@ -0,0 +1,88 @@ +use std::{ + fmt, + ops::{Deref, DerefMut}, +}; + +/// A pair of values, one of which is expected and one of which is actual. +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GotExpected { + /// The actual value. + pub got: T, + /// The expected value. + pub expected: T, +} + +impl fmt::Display for GotExpected { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "got {}, expected {}", self.got, self.expected) + } +} + +impl std::error::Error for GotExpected {} + +impl From<(T, T)> for GotExpected { + #[inline] + fn from((got, expected): (T, T)) -> Self { + Self::new(got, expected) + } +} + +impl GotExpected { + /// Creates a new error from a pair of values. + #[inline] + pub fn new(got: T, expected: T) -> Self { + Self { got, expected } + } +} + +/// A pair of values, one of which is expected and one of which is actual. +/// +/// Same as [`GotExpected`], but [`Box`]ed for smaller size. +/// +/// Prefer instantiating using [`GotExpected`], and then using `.into()` to convert to this type. +#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GotExpectedBoxed(pub Box>); + +impl fmt::Debug for GotExpectedBoxed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Display for GotExpectedBoxed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for GotExpectedBoxed {} + +impl Deref for GotExpectedBoxed { + type Target = GotExpected; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for GotExpectedBoxed { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From<(T, T)> for GotExpectedBoxed { + #[inline] + fn from(value: (T, T)) -> Self { + Self(Box::new(GotExpected::from(value))) + } +} + +impl From> for GotExpectedBoxed { + #[inline] + fn from(value: GotExpected) -> Self { + Self(Box::new(value)) + } +} diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index 0080142a47fc..36b3322f3237 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -219,11 +219,13 @@ impl Header { /// Seal the header with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. + #[inline] pub fn seal(self, hash: B256) -> SealedHeader { SealedHeader { header: self, hash } } /// Calculate hash and seal the Header so that it can't be changed. + #[inline] pub fn seal_slow(self) -> SealedHeader { let hash = self.hash_slow(); self.seal(hash) diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 88273a918347..a5b3b73444f9 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -24,6 +24,7 @@ mod chain; mod compression; pub mod constants; pub mod eip4844; +mod error; mod forkid; pub mod fs; mod genesis; @@ -65,8 +66,7 @@ pub use constants::{ DEV_GENESIS_HASH, EMPTY_OMMER_ROOT_HASH, GOERLI_GENESIS_HASH, HOLESKY_GENESIS_HASH, KECCAK_EMPTY, MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH, }; -#[cfg(feature = "c-kzg")] -pub use eip4844::{calculate_excess_blob_gas, kzg_to_versioned_hash}; +pub use error::{GotExpected, GotExpectedBoxed}; pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError}; pub use genesis::{ChainConfig, Genesis, GenesisAccount}; pub use hardfork::Hardfork; @@ -115,27 +115,24 @@ pub use alloy_primitives::{ }; pub use revm_primitives::{self, JumpMap}; +#[doc(hidden)] +#[deprecated = "use B64 instead"] +pub type H64 = B64; #[doc(hidden)] #[deprecated = "use B128 instead"] pub type H128 = B128; - +#[doc(hidden)] +#[deprecated = "use Address instead"] +pub type H160 = Address; #[doc(hidden)] #[deprecated = "use B256 instead"] pub type H256 = B256; - #[doc(hidden)] #[deprecated = "use B512 instead"] pub type H512 = B512; -#[doc(hidden)] -#[deprecated = "use B64 instead"] -pub type H64 = B64; - #[cfg(any(test, feature = "arbitrary"))] pub use arbitrary; -/// EIP-4844 + KZG helpers #[cfg(feature = "c-kzg")] -pub mod kzg { - pub use c_kzg::*; -} +pub use c_kzg as kzg; diff --git a/crates/primitives/src/transaction/eip4844.rs b/crates/primitives/src/transaction/eip4844.rs index 1f074f6aa2c9..ce15de52af4f 100644 --- a/crates/primitives/src/transaction/eip4844.rs +++ b/crates/primitives/src/transaction/eip4844.rs @@ -3,19 +3,17 @@ use crate::{ constants::eip4844::DATA_GAS_PER_BLOB, keccak256, Bytes, ChainId, Signature, TransactionKind, TxType, TxValue, B256, }; - -#[cfg(feature = "c-kzg")] -use crate::transaction::sidecar::*; - -#[cfg(feature = "c-kzg")] -use crate::kzg_to_versioned_hash; - -#[cfg(feature = "c-kzg")] -use crate::kzg::{self, KzgCommitment, KzgProof, KzgSettings}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use bytes::BytesMut; use reth_codecs::{main_codec, Compact}; use std::mem; + +#[cfg(feature = "c-kzg")] +use crate::eip4844::kzg_to_versioned_hash; +#[cfg(feature = "c-kzg")] +use crate::kzg::{self, KzgCommitment, KzgProof, KzgSettings}; +#[cfg(feature = "c-kzg")] +use crate::transaction::sidecar::*; #[cfg(feature = "c-kzg")] use std::ops::Deref; diff --git a/crates/primitives/src/transaction/error.rs b/crates/primitives/src/transaction/error.rs index 7542e8f5c8f7..21fc575186be 100644 --- a/crates/primitives/src/transaction/error.rs +++ b/crates/primitives/src/transaction/error.rs @@ -1,21 +1,20 @@ -use crate::U256; +use crate::{GotExpectedBoxed, U256}; /// Represents error variants that can happen when trying to validate a /// [Transaction](crate::Transaction) -#[allow(missing_docs)] #[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)] pub enum InvalidTransactionError { /// The sender does not have enough funds to cover the transaction fees #[error( - "sender does not have enough funds ({available_funds}) to cover transaction fees: {cost}" + "sender does not have enough funds ({}) to cover transaction fees: {}", _0.got, _0.expected )] - InsufficientFunds { cost: U256, available_funds: U256 }, + InsufficientFunds(GotExpectedBoxed), /// The nonce is lower than the account's nonce, or there is a nonce gap present. /// /// This is a consensus error. #[error("transaction nonce is not consistent")] NonceNotConsistent, - /// The transaction is before Spurious Dragon and has a chain ID + /// The transaction is before Spurious Dragon and has a chain ID. #[error("transactions before Spurious Dragon should not have a chain ID")] OldLegacyChainId, /// The chain ID in the transaction does not match the current network configuration. @@ -36,8 +35,7 @@ pub enum InvalidTransactionError { /// The calculated gas of the transaction exceeds `u64::MAX`. #[error("gas overflow (maximum of u64)")] GasUintOverflow, - /// The transaction is specified to use less gas than required to start the - /// invocation. + /// The transaction is specified to use less gas than required to start the invocation. #[error("intrinsic gas too low")] GasTooLow, /// The transaction gas exceeds the limit @@ -47,7 +45,7 @@ pub enum InvalidTransactionError { /// fee cap. #[error("max priority fee per gas higher than max fee per gas")] TipAboveFeeCap, - /// Thrown post London if the transaction's fee is less than the base fee of the block + /// Thrown post London if the transaction's fee is less than the base fee of the block. #[error("max fee per gas less than block base fee")] FeeCapTooLow, /// Thrown if the sender of a transaction is a contract. diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index d08c818588fa..baf8b1595b82 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -10,8 +10,8 @@ use reth_interfaces::{ }; use reth_primitives::{ revm::env::{fill_cfg_and_block_env, fill_tx_env}, - Address, Block, BlockNumber, Bloom, ChainSpec, Hardfork, Header, PruneMode, PruneModes, - PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, + Address, Block, BlockNumber, Bloom, ChainSpec, GotExpected, Hardfork, Header, PruneMode, + PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, MINIMUM_PRUNING_DISTANCE, U256, }; use reth_provider::{BlockExecutor, BlockExecutorStats, PrunableBlockExecutor, StateProvider}; @@ -297,8 +297,7 @@ impl<'a> EVMProcessor<'a> { if block.gas_used != cumulative_gas_used { let receipts = Receipts::from_block_receipt(receipts); return Err(BlockValidationError::BlockGasUsed { - got: cumulative_gas_used, - expected: block.gas_used, + gas: GotExpected { got: cumulative_gas_used, expected: block.gas_used }, gas_spent_by_tx: receipts.gas_spent_by_tx()?, } .into()) @@ -537,20 +536,18 @@ pub fn verify_receipt<'a>( let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::>(); let receipts_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); if receipts_root != expected_receipts_root { - return Err(BlockValidationError::ReceiptRootDiff { - got: Box::new(receipts_root), - expected: Box::new(expected_receipts_root), - } + return Err(BlockValidationError::ReceiptRootDiff( + GotExpected { got: receipts_root, expected: expected_receipts_root }.into(), + ) .into()) } // Create header log bloom. let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom); if logs_bloom != expected_logs_bloom { - return Err(BlockValidationError::BloomLogDiff { - expected: Box::new(expected_logs_bloom), - got: Box::new(logs_bloom), - } + return Err(BlockValidationError::BloomLogDiff( + GotExpected { got: logs_bloom, expected: expected_logs_bloom }.into(), + ) .into()) } diff --git a/crates/stages/src/error.rs b/crates/stages/src/error.rs index b44a8a14f865..598e229a8040 100644 --- a/crates/stages/src/error.rs +++ b/crates/stages/src/error.rs @@ -25,7 +25,7 @@ pub enum StageError { #[error("stage encountered an error in block #{number}: {error}", number = block.number)] Block { /// The block that caused the error. - block: SealedHeader, + block: Box, /// The specific error type, either consensus or execution error. #[source] error: BlockErrorKind, @@ -43,9 +43,9 @@ pub enum StageError { )] DetachedHead { /// The local head we attempted to attach to. - local_head: SealedHeader, + local_head: Box, /// The header we attempted to attach. - header: SealedHeader, + header: Box, /// The error that occurred when attempting to attach the header. #[source] error: Box, diff --git a/crates/stages/src/pipeline/ctrl.rs b/crates/stages/src/pipeline/ctrl.rs index 820df423b631..eb40bc7d4bf5 100644 --- a/crates/stages/src/pipeline/ctrl.rs +++ b/crates/stages/src/pipeline/ctrl.rs @@ -10,7 +10,7 @@ pub enum ControlFlow { /// The block to unwind to. target: BlockNumber, /// The block that caused the unwind. - bad_block: SealedHeader, + bad_block: Box, }, /// The pipeline made progress. Continue { diff --git a/crates/stages/src/pipeline/mod.rs b/crates/stages/src/pipeline/mod.rs index ccf7f08efbac..4b58b69c87d2 100644 --- a/crates/stages/src/pipeline/mod.rs +++ b/crates/stages/src/pipeline/mod.rs @@ -838,7 +838,11 @@ mod tests { .add_stage( TestStage::new(StageId::Other("B")) .add_exec(Err(StageError::Block { - block: random_header(&mut generators::rng(), 5, Default::default()), + block: Box::new(random_header( + &mut generators::rng(), + 5, + Default::default(), + )), error: BlockErrorKind::Validation( consensus::ConsensusError::BaseFeeMissing, ), diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 6565ffabaabb..bd317726784d 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -162,7 +162,7 @@ impl ExecutionStage { let (block, senders) = block.into_components(); executor.execute_and_verify_receipt(&block, td, Some(senders)).map_err(|error| { StageError::Block { - block: block.header.clone().seal_slow(), + block: Box::new(block.header.clone().seal_slow()), error: BlockErrorKind::Execution(error), } })?; diff --git a/crates/stages/src/stages/merkle.rs b/crates/stages/src/stages/merkle.rs index 267c10ee9b07..cd02696cee15 100644 --- a/crates/stages/src/stages/merkle.rs +++ b/crates/stages/src/stages/merkle.rs @@ -10,7 +10,7 @@ use reth_primitives::{ hex, stage::{EntitiesCheckpoint, MerkleCheckpoint, StageCheckpoint, StageId}, trie::StoredSubNode, - BlockNumber, SealedHeader, B256, + BlockNumber, GotExpected, SealedHeader, B256, }; use reth_provider::{ DatabaseProviderRW, HeaderProvider, ProviderError, StageCheckpointReader, StageCheckpointWriter, @@ -77,27 +77,6 @@ impl MerkleStage { Self::Execution { clean_threshold } } - /// Check that the computed state root matches the root in the expected header. - fn validate_state_root( - &self, - got: B256, - expected: SealedHeader, - target_block: BlockNumber, - ) -> Result<(), StageError> { - if got == expected.state_root { - Ok(()) - } else { - warn!(target: "sync::stages::merkle", ?target_block, ?got, ?expected, "Failed to verify block state root"); - Err(StageError::Block { - block: expected.clone(), - error: BlockErrorKind::Validation(consensus::ConsensusError::BodyStateRootDiff { - got, - expected: expected.state_root, - }), - }) - } - } - /// Gets the hashing progress pub fn get_execution_checkpoint( &self, @@ -271,7 +250,7 @@ impl Stage for MerkleStage { // Reset the checkpoint self.save_execution_checkpoint(provider, None)?; - self.validate_state_root(trie_root, target_block.seal_slow(), to_block)?; + validate_state_root(trie_root, target_block.seal_slow(), to_block)?; Ok(ExecOutput { checkpoint: StageCheckpoint::new(to_block) @@ -321,7 +300,7 @@ impl Stage for MerkleStage { let target = provider .header_by_number(input.unwind_to)? .ok_or_else(|| ProviderError::HeaderNotFound(input.unwind_to.into()))?; - self.validate_state_root(block_root, target.seal_slow(), input.unwind_to)?; + validate_state_root(block_root, target.seal_slow(), input.unwind_to)?; // Validation passed, apply unwind changes to the database. updates.flush(provider.tx_ref())?; @@ -335,6 +314,26 @@ impl Stage for MerkleStage { } } +/// Check that the computed state root matches the root in the expected header. +#[inline] +fn validate_state_root( + got: B256, + expected: SealedHeader, + target_block: BlockNumber, +) -> Result<(), StageError> { + if got == expected.state_root { + Ok(()) + } else { + warn!(target: "sync::stages::merkle", ?target_block, ?got, ?expected, "Failed to verify block state root"); + Err(StageError::Block { + error: BlockErrorKind::Validation(consensus::ConsensusError::BodyStateRootDiff( + GotExpected { got, expected: expected.state_root }.into(), + )), + block: Box::new(expected), + }) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/stages/src/stages/sender_recovery.rs b/crates/stages/src/stages/sender_recovery.rs index 4105e810a276..80ffb040a057 100644 --- a/crates/stages/src/stages/sender_recovery.rs +++ b/crates/stages/src/stages/sender_recovery.rs @@ -146,7 +146,7 @@ impl Stage for SenderRecoveryStage { .sealed_header(block_number)? .ok_or(ProviderError::HeaderNotFound(block_number.into()))?; return Err(StageError::Block { - block: sealed_header, + block: Box::new(sealed_header), error: BlockErrorKind::Validation( consensus::ConsensusError::TransactionSignerRecoveryError, ), diff --git a/crates/stages/src/stages/total_difficulty.rs b/crates/stages/src/stages/total_difficulty.rs index 5a923e9d18f6..ea1e20630d4a 100644 --- a/crates/stages/src/stages/total_difficulty.rs +++ b/crates/stages/src/stages/total_difficulty.rs @@ -72,7 +72,7 @@ impl Stage for TotalDifficultyStage { let last_header_number = input.checkpoint().block_number; let last_entry = cursor_td .seek_exact(last_header_number)? - .ok_or(ProviderError::TotalDifficultyNotFound { block_number: last_header_number })?; + .ok_or(ProviderError::TotalDifficultyNotFound(last_header_number))?; let mut td: U256 = last_entry.1.into(); debug!(target: "sync::stages::total_difficulty", ?td, block_number = last_header_number, "Last total difficulty entry"); @@ -84,7 +84,7 @@ impl Stage for TotalDifficultyStage { self.consensus.validate_header_with_total_difficulty(&header, td).map_err(|error| { StageError::Block { - block: header.seal_slow(), + block: Box::new(header.seal_slow()), error: BlockErrorKind::Validation(error), } })?; diff --git a/crates/storage/db/src/abstraction/table.rs b/crates/storage/db/src/abstraction/table.rs index 1f4de1745669..253797c7b3cb 100644 --- a/crates/storage/db/src/abstraction/table.rs +++ b/crates/storage/db/src/abstraction/table.rs @@ -45,7 +45,7 @@ pub trait Decompress: Send + Sync + Sized + Debug { /// Trait that will transform the data to be saved in the DB. pub trait Encode: Send + Sync + Sized + Debug { /// Encoded type. - type Encoded: AsRef<[u8]> + Send + Sync; + type Encoded: AsRef<[u8]> + Into> + Send + Sync; /// Encodes data going into the database. fn encode(self) -> Self::Encoded; diff --git a/crates/storage/db/src/implementation/mdbx/cursor.rs b/crates/storage/db/src/implementation/mdbx/cursor.rs index e8a6f1e3399e..181e13e05421 100644 --- a/crates/storage/db/src/implementation/mdbx/cursor.rs +++ b/crates/storage/db/src/implementation/mdbx/cursor.rs @@ -1,6 +1,6 @@ //! Cursor wrapper for libmdbx-sys. -use reth_interfaces::db::DatabaseWriteOperation; +use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use std::{borrow::Cow, collections::Bound, marker::PhantomData, ops::RangeBounds}; use crate::{ @@ -262,11 +262,14 @@ impl DbCursorRW for Cursor<'_, RW, T> { |this| { this.inner .put(key.as_ref(), value.unwrap_or(&this.buf), WriteFlags::UPSERT) - .map_err(|e| DatabaseError::Write { - code: e.into(), - operation: DatabaseWriteOperation::CursorUpsert, - table_name: T::NAME, - key: Box::from(key.as_ref()), + .map_err(|e| { + DatabaseWriteError { + code: e.into(), + operation: DatabaseWriteOperation::CursorUpsert, + table_name: T::NAME, + key: key.into(), + } + .into() }) }, ) @@ -281,11 +284,14 @@ impl DbCursorRW for Cursor<'_, RW, T> { |this| { this.inner .put(key.as_ref(), value.unwrap_or(&this.buf), WriteFlags::NO_OVERWRITE) - .map_err(|e| DatabaseError::Write { - code: e.into(), - operation: DatabaseWriteOperation::CursorInsert, - table_name: T::NAME, - key: Box::from(key.as_ref()), + .map_err(|e| { + DatabaseWriteError { + code: e.into(), + operation: DatabaseWriteOperation::CursorInsert, + table_name: T::NAME, + key: key.into(), + } + .into() }) }, ) @@ -302,11 +308,14 @@ impl DbCursorRW for Cursor<'_, RW, T> { |this| { this.inner .put(key.as_ref(), value.unwrap_or(&this.buf), WriteFlags::APPEND) - .map_err(|e| DatabaseError::Write { - code: e.into(), - operation: DatabaseWriteOperation::CursorAppend, - table_name: T::NAME, - key: Box::from(key.as_ref()), + .map_err(|e| { + DatabaseWriteError { + code: e.into(), + operation: DatabaseWriteOperation::CursorAppend, + table_name: T::NAME, + key: key.into(), + } + .into() }) }, ) @@ -335,11 +344,14 @@ impl DbDupCursorRW for Cursor<'_, RW, T> { |this| { this.inner .put(key.as_ref(), value.unwrap_or(&this.buf), WriteFlags::APPEND_DUP) - .map_err(|e| DatabaseError::Write { - code: e.into(), - operation: DatabaseWriteOperation::CursorAppendDup, - table_name: T::NAME, - key: Box::from(key.as_ref()), + .map_err(|e| { + DatabaseWriteError { + code: e.into(), + operation: DatabaseWriteOperation::CursorAppendDup, + table_name: T::NAME, + key: key.into(), + } + .into() }) }, ) diff --git a/crates/storage/db/src/implementation/mdbx/mod.rs b/crates/storage/db/src/implementation/mdbx/mod.rs index 2fac746cf74f..19166aac126f 100644 --- a/crates/storage/db/src/implementation/mdbx/mod.rs +++ b/crates/storage/db/src/implementation/mdbx/mod.rs @@ -177,9 +177,9 @@ mod tests { tables::{AccountHistory, CanonicalHeaders, Headers, PlainAccountState, PlainStorageState}, test_utils::*, transaction::{DbTx, DbTxMut}, - AccountChangeSet, DatabaseError, + AccountChangeSet, }; - use reth_interfaces::db::DatabaseWriteOperation; + use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{NoWriteMap, WriteMap}; use reth_primitives::{Account, Address, Header, IntegerList, StorageEntry, B256, U256}; use std::{path::Path, str::FromStr, sync::Arc}; @@ -541,12 +541,13 @@ mod tests { // INSERT (failure) assert_eq!( cursor.insert(key_to_insert, B256::ZERO), - Err(DatabaseError::Write { + Err(DatabaseWriteError { code: -30799, operation: DatabaseWriteOperation::CursorInsert, table_name: CanonicalHeaders::NAME, - key: Box::from(key_to_insert.encode().as_ref()) - }) + key: key_to_insert.encode().into(), + } + .into()) ); assert_eq!(cursor.current(), Ok(Some((key_to_insert, B256::ZERO)))); @@ -684,12 +685,13 @@ mod tests { let mut cursor = tx.cursor_write::().unwrap(); assert_eq!( cursor.append(key_to_append, B256::ZERO), - Err(DatabaseError::Write { + Err(DatabaseWriteError { code: -30418, operation: DatabaseWriteOperation::CursorAppend, table_name: CanonicalHeaders::NAME, - key: Box::from(key_to_append.encode().as_ref()) - }) + key: key_to_append.encode().into(), + } + .into()) ); assert_eq!(cursor.current(), Ok(Some((5, B256::ZERO)))); // the end of table tx.commit().expect(ERROR_COMMIT); @@ -765,24 +767,26 @@ mod tests { transition_id, AccountBeforeTx { address: Address::with_last_byte(subkey_to_append), info: None } ), - Err(DatabaseError::Write { + Err(DatabaseWriteError { code: -30418, operation: DatabaseWriteOperation::CursorAppendDup, table_name: AccountChangeSet::NAME, - key: Box::from(transition_id.encode().as_ref()) - }) + key: transition_id.encode().into(), + } + .into()) ); assert_eq!( cursor.append( transition_id - 1, AccountBeforeTx { address: Address::with_last_byte(subkey_to_append), info: None } ), - Err(DatabaseError::Write { + Err(DatabaseWriteError { code: -30418, operation: DatabaseWriteOperation::CursorAppend, table_name: AccountChangeSet::NAME, - key: Box::from((transition_id - 1).encode().as_ref()) - }) + key: (transition_id - 1).encode().into(), + } + .into()) ); assert_eq!( cursor.append( diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index 276cd594d140..689a0cd76267 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -11,7 +11,7 @@ use crate::{ DatabaseError, }; use parking_lot::RwLock; -use reth_interfaces::db::DatabaseWriteOperation; +use reth_interfaces::db::{DatabaseWriteError, DatabaseWriteOperation}; use reth_libmdbx::{ffi::DBI, EnvironmentKind, Transaction, TransactionKind, WriteFlags, RW}; use std::{marker::PhantomData, str::FromStr, sync::Arc, time::Instant}; @@ -238,12 +238,13 @@ impl DbTxMut for Tx<'_, RW, E> { Some(value.as_ref().len()), |tx| { tx.put(self.get_dbi::()?, key.as_ref(), value, WriteFlags::UPSERT).map_err(|e| { - DatabaseError::Write { + DatabaseWriteError { code: e.into(), operation: DatabaseWriteOperation::Put, table_name: T::NAME, - key: Box::from(key.as_ref()), + key: key.into(), } + .into() }) }, ) diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index db68598061d7..3c9d40d81a81 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -167,8 +167,8 @@ impl Chain { let chain_tip = self.tip(); if chain_tip.hash != chain.fork_block_hash() { return Err(BlockExecutionError::AppendChainDoesntConnect { - chain_tip: chain_tip.num_hash(), - other_chain_fork: chain.fork_block(), + chain_tip: Box::new(chain_tip.num_hash()), + other_chain_fork: Box::new(chain.fork_block()), } .into()) } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 3fc623daf7c7..197aab86f06f 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -26,6 +26,7 @@ use reth_db::{ }; use reth_interfaces::{ executor::{BlockExecutionError, BlockValidationError}, + provider::RootMismatch, RethError, RethResult, }; use reth_primitives::{ @@ -37,10 +38,10 @@ use reth_primitives::{ stage::{StageCheckpoint, StageId}, trie::Nibbles, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, - ChainInfo, ChainSpec, Hardfork, Head, Header, PruneCheckpoint, PruneModes, PruneSegment, - Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StorageEntry, TransactionMeta, - TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxHash, TxNumber, - Withdrawal, B256, U256, + ChainInfo, ChainSpec, GotExpected, Hardfork, Head, Header, PruneCheckpoint, PruneModes, + PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StorageEntry, + TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, + TxHash, TxNumber, Withdrawal, B256, U256, }; use reth_trie::{prefix_set::PrefixSetMut, StateRoot}; use revm::primitives::{BlockEnv, CfgEnv, SpecId}; @@ -1649,12 +1650,11 @@ impl HashingWriter for DatabaseProvider { .root_with_updates() .map_err(Into::::into)?; if state_root != expected_state_root { - return Err(ProviderError::StateRootMismatch { - got: state_root, - expected: expected_state_root, + return Err(ProviderError::StateRootMismatch(Box::new(RootMismatch { + root: GotExpected { got: state_root, expected: expected_state_root }, block_number: *range.end(), block_hash: end_block_hash, - } + })) .into()) } trie_updates.flush(&self.tx)?; @@ -2033,12 +2033,11 @@ impl BlockExecutionWriter for DatabaseProvider { let parent_hash = self .block_hash(parent_number)? .ok_or_else(|| ProviderError::HeaderNotFound(parent_number.into()))?; - return Err(ProviderError::UnwindStateRootMismatch { - got: new_state_root, - expected: parent_state_root, + return Err(ProviderError::UnwindStateRootMismatch(Box::new(RootMismatch { + root: GotExpected { got: new_state_root, expected: parent_state_root }, block_number: parent_number, block_hash: parent_hash, - } + })) .into()) } trie_updates.flush(&self.tx)?; diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 4d04ee739ec9..51c6470bc10b 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -15,8 +15,8 @@ use reth_primitives::{ }, kzg::KzgSettings, revm::compat::calculate_intrinsic_gas_after_merge, - ChainSpec, InvalidTransactionError, SealedBlock, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, - EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, + ChainSpec, GotExpected, InvalidTransactionError, SealedBlock, EIP1559_TX_TYPE_ID, + EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; use reth_provider::{AccountReader, BlockReaderIdExt, StateProviderFactory}; use reth_tasks::TaskSpawner; @@ -420,10 +420,9 @@ where if cost > account.balance { return TransactionValidationOutcome::Invalid( transaction, - InvalidTransactionError::InsufficientFunds { - cost, - available_funds: account.balance, - } + InvalidTransactionError::InsufficientFunds( + GotExpected { got: account.balance, expected: cost }.into(), + ) .into(), ) }