chore: reduce size of common types (#5304)

This commit is contained in:
DaniPopes
2023-11-06 13:45:20 +01:00
committed by GitHub
parent 2d315c2f3a
commit f8ceda9ea8
55 changed files with 580 additions and 532 deletions

4
Cargo.lock generated
View File

@ -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"

View File

@ -269,7 +269,7 @@ impl<Ext: RethCliExt> NodeCommand<Ext> {
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();

View File

@ -1,2 +1,3 @@
msrv = "1.70"
ignore-interior-mutability = ["bytes::Bytes", "reth_primitives::trie::nibbles::Nibbles"]
too-large-for-stack = 128

View File

@ -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())
}
}

View File

@ -11,7 +11,7 @@ pub enum BeaconConsensusEngineEvent {
/// A block was added to the canonical chain.
CanonicalBlockAdded(Arc<SealedBlock>),
/// A canonical chain was committed.
CanonicalChainCommitted(SealedHeader, Duration),
CanonicalChainCommitted(Box<SealedHeader>, Duration),
/// A block was added to the fork chain.
ForkBlockAdded(Arc<SealedBlock>),
}

View File

@ -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
}

View File

@ -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
}))
);
}
}

View File

@ -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<B256>),
/// 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<B256>),
/// 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<B256>),
/// 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<B256>),
/// 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<B256>),
/// 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<u64>),
/// 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<u64>),
/// 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<u64>,
/// The parent excess blob gas.
parent_excess_blob_gas: u64,
/// The parent blob gas used.

View File

@ -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<DatabaseWriteError>),
/// 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<DatabaseWriteError> 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<u8>,
}
/// 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,
}

View File

@ -1,7 +1,7 @@
/// Result alias for [`RethError`]
/// Result alias for [`RethError`].
pub type RethResult<T> = Result<T, RethError>;
/// 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<reth_nippy_jar::NippyJarError> 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::<RethError>()];
// Biggest variant.
let _: [(); 64] = [(); std::mem::size_of::<crate::provider::ProviderError>()];
// Other common types.
let _: [(); 16] = [(); std::mem::size_of::<crate::db::DatabaseError>()];
};

View File

@ -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<B256>,
/// The expected receipt root
expected: Box<B256>,
},
#[error("receipt root mismatch: {0}")]
ReceiptRootDiff(GotExpectedBoxed<B256>),
/// 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<Bloom>,
/// The expected bloom filter
expected: Box<Bloom>,
},
#[error("header bloom filter mismatch: {0}")]
BloomLogDiff(GotExpectedBoxed<Bloom>),
/// 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<u64>,
/// 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<BlockNumHash>,
/// The fork on the other chain
other_chain_fork: BlockNumHash,
other_chain_fork: Box<BlockNumHash>,
},
/// Only used for TestExecutor
///

View File

@ -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<T> = Result<T, DownloadError>;
#[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<ConsensusError>,
},
/// Received an invalid tip.
#[error("received invalid tip: {0}")]
InvalidTip(GotExpectedBoxed<B256>),
/// Received a tip with an invalid tip number.
#[error("received invalid tip number: {0}")]
InvalidTipNumber(GotExpected<u64>),
/// 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<u64>),
/// 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<u64>),
/* ==================== 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<ConsensusError>,
},
/// 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<usize>),
/// Headers missing from the database.
#[error("header missing from the database: {block_number}")]
MissingHeader {

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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<SealedHeader>,
/// The header we attempted to attach.
header: SealedHeader,
header: Box<SealedHeader>,
/// The error that occurred when attempting to attach the header.
#[source]
error: Box<ConsensusError>,
},
}

View File

@ -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<RootMismatch>),
/// 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<RootMismatch>),
/// 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<B256>,
/// The target block number.
pub block_number: BlockNumber,
/// The target block hash.
pub block_hash: BlockHash,
}

View File

@ -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),
})))
}

View File

@ -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));

View File

@ -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<HeadersResponseError>),
}
impl From<HeadersResponseError> 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<HeadersResponseError>) {
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<PeerId>,
#[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)]

View File

@ -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<B256>),
#[error("mismatched protocol version in status message: {0}")]
MismatchedProtocolVersion(GotExpected<u8>),
#[error("mismatched chain in status message: {0}")]
MismatchedChain(GotExpected<Chain>),
#[error("total difficulty bitlen is too large: got {got}, maximum {maximum}")]
TotalDifficultyBitLenTooLarge { maximum: usize, got: usize },
TotalDifficultyBitLenTooLarge { got: usize, maximum: usize },
}

View File

@ -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<ProtocolVersion>),
#[error("started ping task before the handshake completed")]
PingBeforeHandshake,
#[error("too many messages buffered before sending")]

View File

@ -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 }
))
));

View File

@ -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:?}")

View File

@ -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<String>,
pub client_version: Arc<str>,
/// 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<Status>,
}
/// The direction of the connection.

View File

@ -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<String>,
client_version: Arc<str>,
/// Capabilities the peer announced
capabilities: Arc<Capabilities>,
/// A request channel to the session task.
messages: PeerRequestSender,
/// The status of the peer to which a session was established.
status: Status,
status: Arc<Status>,
/// negotiated eth version of the session
version: EthVersion,
},

View File

@ -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<EthStream<P2PStream<ECIESStream<MeteredStream<TcpStream>>>>>;
/// 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<P2PStream<ECIESStream<MeteredStream<TcpStream>>>>,
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<Capabilities>,
_remote_capabilities: Arc<Capabilities>,
active_session_tx: mpsc::Sender<ActiveSessionMessage>,
active_session_rx: ReceiverStream<ActiveSessionMessage>,
to_sessions: Vec<mpsc::Sender<SessionCommand>>,
@ -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![],

View File

@ -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<SessionCommand>,
/// The client's name and version
pub(crate) client_version: Arc<String>,
pub(crate) client_version: Arc<str>,
/// The address we're connected to
pub(crate) remote_addr: SocketAddr,
/// The local address of the connection.
pub(crate) local_addr: Option<SocketAddr>,
/// The Status message the peer sent for the `eth` handshake
pub(crate) status: Status,
pub(crate) status: Arc<Status>,
}
// === impl ActiveSessionHandle ===
@ -128,7 +126,7 @@ impl ActiveSessionHandle {
}
/// Returns the client's name and version.
pub fn client_version(&self) -> Arc<String> {
pub fn client_version(&self) -> Arc<str> {
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<Capabilities>,
/// The Status message the peer sent for the `eth` handshake
status: Status,
status: Arc<Status>,
/// The actual connection stream which can be used to send and receive `eth` protocol
/// messages
conn: EthStream<P2PStream<ECIESStream<MeteredStream<TcpStream>>>>,
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<EthStreamError>,
},
/// 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,

View File

@ -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<String>,
client_version: Arc<str>,
/// The capabilities the remote node has announced
capabilities: Arc<Capabilities>,
/// negotiated eth version
version: EthVersion,
/// The Status message the peer sent during the `eth` handshake
status: Status,
status: Arc<Status>,
/// 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,
}

View File

@ -130,7 +130,7 @@ where
&mut self,
peer: PeerId,
capabilities: Arc<Capabilities>,
status: Status,
status: Arc<Status>,
request_tx: PeerRequestSender,
timeout: Arc<AtomicU64>,
) {
@ -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)),
);

View File

@ -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<String>,
client_version: Arc<str>,
capabilities: Arc<Capabilities>,
/// negotiated eth version
version: EthVersion,
messages: PeerRequestSender,
status: Status,
status: Arc<Status>,
direction: Direction,
},
SessionClosed {

View File

@ -1043,7 +1043,7 @@ struct Peer {
version: EthVersion,
/// The peer's client version.
#[allow(unused)]
client_version: Arc<String>,
client_version: Arc<str>,
}
/// The type responsible for fetching missing transactions from peers.

View File

@ -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,

View File

@ -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<TransactionSigned>, Vec<Header>) {
(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,

View File

@ -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<I> From<I> for DisplayHardforks
where
I: IntoIterator<Item = (Hardfork, ForkCondition)>,
{
fn from(iter: I) -> Self {
impl DisplayHardforks {
/// Creates a new [`DisplayHardforks`] from an iterator of hardforks.
pub fn new(hardforks: &BTreeMap<Hardfork, ForkCondition>) -> Self {
Self::from_iter(hardforks.iter())
}
}
impl<'a, 'b> FromIterator<(&'a Hardfork, &'b ForkCondition)> for DisplayHardforks {
fn from_iter<T: IntoIterator<Item = (&'a Hardfork, &'b ForkCondition)>>(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
"

View File

@ -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)
}

View File

@ -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<T> {
/// The actual value.
pub got: T,
/// The expected value.
pub expected: T,
}
impl<T: fmt::Display> fmt::Display for GotExpected<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "got {}, expected {}", self.got, self.expected)
}
}
impl<T: fmt::Debug + fmt::Display> std::error::Error for GotExpected<T> {}
impl<T> From<(T, T)> for GotExpected<T> {
#[inline]
fn from((got, expected): (T, T)) -> Self {
Self::new(got, expected)
}
}
impl<T> GotExpected<T> {
/// 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<T>(pub Box<GotExpected<T>>);
impl<T: fmt::Debug> fmt::Debug for GotExpectedBoxed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for GotExpectedBoxed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Debug + fmt::Display> std::error::Error for GotExpectedBoxed<T> {}
impl<T> Deref for GotExpectedBoxed<T> {
type Target = GotExpected<T>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for GotExpectedBoxed<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<(T, T)> for GotExpectedBoxed<T> {
#[inline]
fn from(value: (T, T)) -> Self {
Self(Box::new(GotExpected::from(value)))
}
}
impl<T> From<GotExpected<T>> for GotExpectedBoxed<T> {
#[inline]
fn from(value: GotExpected<T>) -> Self {
Self(Box::new(value))
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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<U256>),
/// 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.

View File

@ -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::<Vec<ReceiptWithBloom>>();
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())
}

View File

@ -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<SealedHeader>,
/// 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<SealedHeader>,
/// The header we attempted to attach.
header: SealedHeader,
header: Box<SealedHeader>,
/// The error that occurred when attempting to attach the header.
#[source]
error: Box<consensus::ConsensusError>,

View File

@ -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<SealedHeader>,
},
/// The pipeline made progress.
Continue {

View File

@ -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,
),

View File

@ -162,7 +162,7 @@ impl<EF: ExecutorFactory> ExecutionStage<EF> {
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),
}
})?;

View File

@ -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<DB: Database>(
&self,
@ -271,7 +250,7 @@ impl<DB: Database> Stage<DB> 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<DB: Database> Stage<DB> 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<DB: Database> Stage<DB> 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::*;

View File

@ -146,7 +146,7 @@ impl<DB: Database> Stage<DB> 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,
),

View File

@ -72,7 +72,7 @@ impl<DB: Database> Stage<DB> 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<DB: Database> Stage<DB> 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),
}
})?;

View File

@ -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<Vec<u8>> + Send + Sync;
/// Encodes data going into the database.
fn encode(self) -> Self::Encoded;

View File

@ -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<T: Table> DbCursorRW<T> 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<T: Table> DbCursorRW<T> 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<T: Table> DbCursorRW<T> 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<T: DupSort> DbDupCursorRW<T> 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()
})
},
)

View File

@ -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::<CanonicalHeaders>().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(

View File

@ -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<E: EnvironmentKind> DbTxMut for Tx<'_, RW, E> {
Some(value.as_ref().len()),
|tx| {
tx.put(self.get_dbi::<T>()?, 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()
})
},
)

View File

@ -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())
}

View File

@ -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<TX: DbTxMut + DbTx> HashingWriter for DatabaseProvider<TX> {
.root_with_updates()
.map_err(Into::<reth_db::DatabaseError>::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<TX: DbTxMut + DbTx> BlockExecutionWriter for DatabaseProvider<TX> {
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)?;

View File

@ -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(),
)
}