mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
1 Commits
push
...
georgios/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6363897e30 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -8336,6 +8336,7 @@ dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rlp",
|
||||
"alloy-rpc-types-engine",
|
||||
"alloy-trie",
|
||||
"assert_matches",
|
||||
"codspeed-criterion-compat",
|
||||
"crossbeam-channel",
|
||||
|
||||
@@ -19,6 +19,7 @@ reth-consensus.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
alloy-primitives = { workspace = true, features = ["rand"] }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH};
|
||||
use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams};
|
||||
use alloy_primitives::B256;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
|
||||
use reth_consensus::ConsensusError;
|
||||
use reth_primitives_traits::{
|
||||
@@ -137,9 +138,13 @@ where
|
||||
/// - Compares the ommer hash in the block header to the block body
|
||||
/// - Compares the transactions root in the block header to the block body
|
||||
/// - Pre-execution transaction validation
|
||||
///
|
||||
/// If `transactions_root` is provided, the pre-computed transaction root is used instead of
|
||||
/// recomputing it from the block body.
|
||||
pub fn validate_block_pre_execution<B, ChainSpec>(
|
||||
block: &SealedBlock<B>,
|
||||
chain_spec: &ChainSpec,
|
||||
transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError>
|
||||
where
|
||||
B: Block,
|
||||
@@ -148,7 +153,13 @@ where
|
||||
post_merge_hardfork_fields(block, chain_spec)?;
|
||||
|
||||
// Check transaction root
|
||||
if let Err(error) = block.ensure_transaction_root_valid() {
|
||||
if let Some(root) = transactions_root {
|
||||
if block.header().transactions_root() != root {
|
||||
return Err(ConsensusError::BodyTransactionRootDiff(
|
||||
GotExpected { got: root, expected: block.header().transactions_root() }.into(),
|
||||
))
|
||||
}
|
||||
} else if let Err(error) = block.ensure_transaction_root_valid() {
|
||||
return Err(ConsensusError::BodyTransactionRootDiff(error.into()))
|
||||
}
|
||||
|
||||
@@ -485,7 +496,7 @@ mod tests {
|
||||
|
||||
// validate blob, it should fail blob gas used validation
|
||||
assert!(matches!(
|
||||
validate_block_pre_execution(&block, &chain_spec).unwrap_err(),
|
||||
validate_block_pre_execution(&block, &chain_spec, None).unwrap_err(),
|
||||
ConsensusError::BlobGasUsedDiff(diff)
|
||||
if diff.got == 1 && diff.expected == expected_blob_gas_used
|
||||
));
|
||||
|
||||
@@ -76,8 +76,15 @@ pub trait Consensus<B: Block>: HeaderValidator<B::Header> {
|
||||
///
|
||||
/// **This should not be called for the genesis block**.
|
||||
///
|
||||
/// If `transactions_root` is provided, the implementation should use the pre-computed
|
||||
/// transaction root instead of recomputing it from the block body.
|
||||
///
|
||||
/// Note: validating blocks does not include other validations of the Consensus
|
||||
fn validate_block_pre_execution(&self, block: &SealedBlock<B>) -> Result<(), ConsensusError>;
|
||||
fn validate_block_pre_execution(
|
||||
&self,
|
||||
block: &SealedBlock<B>,
|
||||
transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError>;
|
||||
}
|
||||
|
||||
/// `HeaderValidator` is a protocol that validates headers and their relationships.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
use crate::{Consensus, ConsensusError, FullConsensus, HeaderValidator, ReceiptRootBloom};
|
||||
use alloc::sync::Arc;
|
||||
use alloy_primitives::B256;
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives_traits::{Block, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader};
|
||||
|
||||
@@ -65,7 +66,11 @@ impl<B: Block> Consensus<B> for NoopConsensus {
|
||||
}
|
||||
|
||||
/// Validates block before execution (no-op implementation).
|
||||
fn validate_block_pre_execution(&self, _block: &SealedBlock<B>) -> Result<(), ConsensusError> {
|
||||
fn validate_block_pre_execution(
|
||||
&self,
|
||||
_block: &SealedBlock<B>,
|
||||
_transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{Consensus, ConsensusError, FullConsensus, HeaderValidator, ReceiptRootBloom};
|
||||
use alloy_primitives::B256;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives_traits::{Block, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader};
|
||||
@@ -74,7 +75,11 @@ impl<B: Block> Consensus<B> for TestConsensus {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_block_pre_execution(&self, _block: &SealedBlock<B>) -> Result<(), ConsensusError> {
|
||||
fn validate_block_pre_execution(
|
||||
&self,
|
||||
_block: &SealedBlock<B>,
|
||||
_transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
if self.fail_validation() {
|
||||
Err(ConsensusError::BaseFeeMissing)
|
||||
} else {
|
||||
|
||||
@@ -45,6 +45,7 @@ alloy-eip7928.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
alloy-rlp.workspace = true
|
||||
alloy-rpc-types-engine.workspace = true
|
||||
alloy-trie.workspace = true
|
||||
|
||||
revm.workspace = true
|
||||
revm-primitives.workspace = true
|
||||
|
||||
@@ -2145,7 +2145,7 @@ where
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
if let Err(e) = self.consensus.validate_block_pre_execution(block) {
|
||||
if let Err(e) = self.consensus.validate_block_pre_execution(block, None) {
|
||||
error!(target: "engine::tree", ?block, "Failed to validate block {}: {e}", block.hash());
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ where
|
||||
let block = self.convert_to_block(input)?;
|
||||
|
||||
// Validate block consensus rules which includes header validation
|
||||
if let Err(consensus_err) = self.validate_block_inner(&block) {
|
||||
if let Err(consensus_err) = self.validate_block_inner(&block, None) {
|
||||
// Header validation error takes precedence over execution error
|
||||
return Err(InsertBlockError::new(block, consensus_err.into()).into())
|
||||
}
|
||||
@@ -341,6 +341,23 @@ where
|
||||
V: PayloadValidator<T, Block = N::Block>,
|
||||
Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
|
||||
{
|
||||
// For payloads, spawn a background task to compute the transaction root from the
|
||||
// raw encoded transactions. This runs concurrently with setup + execution, avoiding
|
||||
// the cost of re-encoding all transactions into an MPT during validation.
|
||||
// Regular blocks skip this since they were already validated when downloaded.
|
||||
let tx_root_rx = match &input {
|
||||
BlockOrPayload::Payload(payload) => {
|
||||
let raw_txs = payload.raw_transactions();
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
self.payload_processor.executor().spawn_blocking(move || {
|
||||
let root = alloy_trie::root::ordered_trie_root_encoded(raw_txs.as_slice());
|
||||
let _ = tx.send(root);
|
||||
});
|
||||
Some(rx)
|
||||
}
|
||||
BlockOrPayload::Block(_) => None,
|
||||
};
|
||||
|
||||
/// A helper macro that returns the block in case there was an error
|
||||
/// This macro is used for early returns before block conversion
|
||||
macro_rules! ensure_ok {
|
||||
@@ -496,13 +513,17 @@ where
|
||||
})
|
||||
.ok();
|
||||
|
||||
// Await the pre-computed transaction root (for payloads only).
|
||||
let transactions_root = tx_root_rx.and_then(|rx| rx.blocking_recv().ok());
|
||||
|
||||
let hashed_state = ensure_ok_post_block!(
|
||||
self.validate_post_execution(
|
||||
&block,
|
||||
&parent_block,
|
||||
&output,
|
||||
&mut ctx,
|
||||
receipt_root_bloom
|
||||
receipt_root_bloom,
|
||||
transactions_root,
|
||||
),
|
||||
block
|
||||
);
|
||||
@@ -653,13 +674,17 @@ where
|
||||
/// Validate if block is correct and satisfies all the consensus rules that concern the header
|
||||
/// and block body itself.
|
||||
#[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)]
|
||||
fn validate_block_inner(&self, block: &SealedBlock<N::Block>) -> Result<(), ConsensusError> {
|
||||
fn validate_block_inner(
|
||||
&self,
|
||||
block: &SealedBlock<N::Block>,
|
||||
transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
if let Err(e) = self.consensus.validate_header(block.sealed_header()) {
|
||||
error!(target: "engine::tree::payload_validator", ?block, "Failed to validate header {}: {e}", block.hash());
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
if let Err(e) = self.consensus.validate_block_pre_execution(block) {
|
||||
if let Err(e) = self.consensus.validate_block_pre_execution(block, transactions_root) {
|
||||
error!(target: "engine::tree::payload_validator", ?block, "Failed to validate block {}: {e}", block.hash());
|
||||
return Err(e)
|
||||
}
|
||||
@@ -973,6 +998,7 @@ where
|
||||
output: &BlockExecutionOutput<N::Receipt>,
|
||||
ctx: &mut TreeCtx<'_, N>,
|
||||
receipt_root_bloom: Option<ReceiptRootBloom>,
|
||||
transactions_root: Option<B256>,
|
||||
) -> Result<HashedPostState, InsertBlockErrorKind>
|
||||
where
|
||||
V: PayloadValidator<T, Block = N::Block>,
|
||||
@@ -981,7 +1007,7 @@ where
|
||||
|
||||
trace!(target: "engine::tree::payload_validator", block=?block.num_hash(), "Validating block consensus");
|
||||
// validate block consensus rules
|
||||
if let Err(e) = self.validate_block_inner(block) {
|
||||
if let Err(e) = self.validate_block_inner(block, transactions_root) {
|
||||
return Err(e.into())
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ extern crate alloc;
|
||||
use alloc::{fmt::Debug, sync::Arc};
|
||||
use alloy_consensus::{constants::MAXIMUM_EXTRA_DATA_SIZE, EMPTY_OMMER_ROOT_HASH};
|
||||
use alloy_eips::eip7840::BlobParams;
|
||||
use alloy_primitives::B256;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator, ReceiptRootBloom};
|
||||
use reth_consensus_common::validation::{
|
||||
@@ -99,8 +100,12 @@ where
|
||||
validate_body_against_header(body, header.header())
|
||||
}
|
||||
|
||||
fn validate_block_pre_execution(&self, block: &SealedBlock<B>) -> Result<(), ConsensusError> {
|
||||
validate_block_pre_execution(block, &self.chain_spec)
|
||||
fn validate_block_pre_execution(
|
||||
&self,
|
||||
block: &SealedBlock<B>,
|
||||
transactions_root: Option<B256>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
validate_block_pre_execution(block, &self.chain_spec, transactions_root)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ where
|
||||
|
||||
let block = SealedBlock::from_sealed_parts(next_header, next_body);
|
||||
|
||||
if let Err(error) = self.consensus.validate_block_pre_execution(&block) {
|
||||
if let Err(error) = self.consensus.validate_block_pre_execution(&block, None) {
|
||||
// Body is invalid, put the header back and return an error
|
||||
let hash = block.hash();
|
||||
let number = block.number();
|
||||
|
||||
@@ -288,7 +288,7 @@ impl<B: FullBlock<Header: reth_primitives_traits::BlockHeader>> FromReader
|
||||
}
|
||||
|
||||
// Validate block against header
|
||||
self.consensus.validate_block_pre_execution(&block)?;
|
||||
self.consensus.validate_block_pre_execution(&block, None)?;
|
||||
|
||||
// add to the internal maps
|
||||
let block_hash = block.hash();
|
||||
|
||||
@@ -85,7 +85,7 @@ where
|
||||
};
|
||||
|
||||
let block = SealedBlock::from_sealed_parts(header, body);
|
||||
consensus.validate_block_pre_execution(&block)?;
|
||||
consensus.validate_block_pre_execution(&block, None)?;
|
||||
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ pub trait ExecutionPayload:
|
||||
|
||||
/// Returns the number of transactions in the payload.
|
||||
fn transaction_count(&self) -> usize;
|
||||
|
||||
/// Returns the raw RLP-encoded transactions from the payload.
|
||||
///
|
||||
/// These are the pre-encoded transaction bytes that can be used to compute the
|
||||
/// transaction root without decoding and re-encoding.
|
||||
fn raw_transactions(&self) -> Vec<Bytes>;
|
||||
}
|
||||
|
||||
impl ExecutionPayload for ExecutionData {
|
||||
@@ -96,6 +102,10 @@ impl ExecutionPayload for ExecutionData {
|
||||
fn transaction_count(&self) -> usize {
|
||||
self.payload.as_v1().transactions.len()
|
||||
}
|
||||
|
||||
fn raw_transactions(&self) -> Vec<Bytes> {
|
||||
self.payload.as_v1().transactions.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// A unified type for handling both execution payloads and payload attributes.
|
||||
@@ -207,6 +217,10 @@ impl ExecutionPayload for op_alloy_rpc_types_engine::OpExecutionData {
|
||||
fn transaction_count(&self) -> usize {
|
||||
self.payload.as_v1().transactions.len()
|
||||
}
|
||||
|
||||
fn raw_transactions(&self) -> Vec<Bytes> {
|
||||
self.payload.as_v1().transactions.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended functionality for Ethereum execution payloads
|
||||
|
||||
@@ -131,7 +131,7 @@ where
|
||||
self.validate_message_against_header(block.sealed_header(), &message)?;
|
||||
|
||||
self.consensus.validate_header(block.sealed_header())?;
|
||||
self.consensus.validate_block_pre_execution(block.sealed_block())?;
|
||||
self.consensus.validate_block_pre_execution(block.sealed_block(), None)?;
|
||||
|
||||
if !self.disallow.is_empty() {
|
||||
if self.disallow.contains(&block.beneficiary()) {
|
||||
|
||||
@@ -283,7 +283,7 @@ where
|
||||
consensus.validate_header(block.sealed_header())?;
|
||||
consensus.validate_header_against_parent(block.sealed_header(), parent)?;
|
||||
|
||||
consensus.validate_block_pre_execution(block)?;
|
||||
consensus.validate_block_pre_execution(block, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user