mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: reduce size of common types (#5304)
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
msrv = "1.70"
|
||||
ignore-interior-mutability = ["bytes::Bytes", "reth_primitives::trie::nibbles::Nibbles"]
|
||||
too-large-for-stack = 128
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>),
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>()];
|
||||
};
|
||||
|
||||
@ -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
|
||||
///
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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),
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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 },
|
||||
}
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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 }
|
||||
))
|
||||
));
|
||||
|
||||
|
||||
@ -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:?}")
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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![],
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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)),
|
||||
);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
"
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
88
crates/primitives/src/error.rs
Normal file
88
crates/primitives/src/error.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
),
|
||||
|
||||
@ -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),
|
||||
}
|
||||
})?;
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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,
|
||||
),
|
||||
|
||||
@ -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),
|
||||
}
|
||||
})?;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user