feat: make Consensus trait generic over block parts (#12451)

This commit is contained in:
Arsenii Kulikov
2024-11-11 20:35:01 +04:00
committed by GitHub
parent 11fd5aa45e
commit 24b3e63ab3
12 changed files with 219 additions and 114 deletions

View File

@ -4,7 +4,9 @@ use alloy_consensus::constants::MAXIMUM_EXTRA_DATA_SIZE;
use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK};
use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_consensus::ConsensusError; use reth_consensus::ConsensusError;
use reth_primitives::{EthereumHardfork, GotExpected, Header, SealedBlock, SealedHeader}; use reth_primitives::{
BlockBody, EthereumHardfork, GotExpected, Header, SealedBlock, SealedHeader,
};
use revm_primitives::calc_excess_blob_gas; use revm_primitives::calc_excess_blob_gas;
/// Gas used needs to be less than gas limit. Gas used is going to be checked after execution. /// Gas used needs to be less than gas limit. Gas used is going to be checked after execution.
@ -73,6 +75,49 @@ pub fn validate_cancun_gas(block: &SealedBlock) -> Result<(), ConsensusError> {
Ok(()) Ok(())
} }
/// Ensures the block response data matches the header.
///
/// This ensures the body response items match the header's hashes:
/// - ommer hash
/// - transaction root
/// - withdrawals root
pub fn validate_body_against_header(
body: &BlockBody,
header: &SealedHeader,
) -> Result<(), ConsensusError> {
let ommers_hash = body.calculate_ommers_root();
if header.ommers_hash != ommers_hash {
return Err(ConsensusError::BodyOmmersHashDiff(
GotExpected { got: ommers_hash, expected: header.ommers_hash }.into(),
))
}
let tx_root = body.calculate_tx_root();
if header.transactions_root != tx_root {
return Err(ConsensusError::BodyTransactionRootDiff(
GotExpected { got: tx_root, expected: header.transactions_root }.into(),
))
}
match (header.withdrawals_root, &body.withdrawals) {
(Some(header_withdrawals_root), Some(withdrawals)) => {
let withdrawals = withdrawals.as_slice();
let withdrawals_root = reth_primitives::proofs::calculate_withdrawals_root(withdrawals);
if withdrawals_root != header_withdrawals_root {
return Err(ConsensusError::BodyWithdrawalsRootDiff(
GotExpected { got: withdrawals_root, expected: header_withdrawals_root }.into(),
))
}
}
(None, None) => {
// this is ok because we assume the fork is not active in this case
}
_ => return Err(ConsensusError::WithdrawalsRootUnexpected),
}
Ok(())
}
/// Validate a block without regard for state: /// Validate a block without regard for state:
/// ///
/// - Compares the ommer hash in the block header to the block body /// - Compares the ommer hash in the block header to the block body

View File

@ -15,8 +15,8 @@ use alloc::{fmt::Debug, vec::Vec};
use alloy_eips::eip7685::Requests; use alloy_eips::eip7685::Requests;
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256}; use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
use reth_primitives::{ use reth_primitives::{
constants::MINIMUM_GAS_LIMIT, BlockWithSenders, GotExpected, GotExpectedBoxed, Header, constants::MINIMUM_GAS_LIMIT, BlockBody, BlockWithSenders, GotExpected, GotExpectedBoxed,
InvalidTransactionError, Receipt, SealedBlock, SealedHeader, Header, InvalidTransactionError, Receipt, SealedBlock, SealedHeader,
}; };
/// A consensus implementation that does nothing. /// A consensus implementation that does nothing.
@ -44,11 +44,11 @@ impl<'a> PostExecutionInput<'a> {
/// Consensus is a protocol that chooses canonical chain. /// Consensus is a protocol that chooses canonical chain.
#[auto_impl::auto_impl(&, Arc)] #[auto_impl::auto_impl(&, Arc)]
pub trait Consensus: Debug + Send + Sync { pub trait Consensus<H = Header, B = BlockBody>: Debug + Send + Sync {
/// Validate if header is correct and follows consensus specification. /// Validate if header is correct and follows consensus specification.
/// ///
/// This is called on standalone header to check if all hashes are correct. /// This is called on standalone header to check if all hashes are correct.
fn validate_header(&self, header: &SealedHeader) -> Result<(), ConsensusError>; fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError>;
/// Validate that the header information regarding parent are correct. /// Validate that the header information regarding parent are correct.
/// This checks the block number, timestamp, basefee and gas limit increment. /// This checks the block number, timestamp, basefee and gas limit increment.
@ -61,8 +61,8 @@ pub trait Consensus: Debug + Send + Sync {
/// Note: Validating header against its parent does not include other Consensus validations. /// Note: Validating header against its parent does not include other Consensus validations.
fn validate_header_against_parent( fn validate_header_against_parent(
&self, &self,
header: &SealedHeader, header: &SealedHeader<H>,
parent: &SealedHeader, parent: &SealedHeader<H>,
) -> Result<(), ConsensusError>; ) -> Result<(), ConsensusError>;
/// Validates the given headers /// Validates the given headers
@ -71,7 +71,13 @@ pub trait Consensus: Debug + Send + Sync {
/// on its own and valid against its parent. /// on its own and valid against its parent.
/// ///
/// Note: this expects that the headers are in natural order (ascending block number) /// Note: this expects that the headers are in natural order (ascending block number)
fn validate_header_range(&self, headers: &[SealedHeader]) -> Result<(), HeaderConsensusError> { fn validate_header_range(
&self,
headers: &[SealedHeader<H>],
) -> Result<(), HeaderConsensusError<H>>
where
H: Clone,
{
if let Some((initial_header, remaining_headers)) = headers.split_first() { if let Some((initial_header, remaining_headers)) = headers.split_first() {
self.validate_header(initial_header) self.validate_header(initial_header)
.map_err(|e| HeaderConsensusError(e, initial_header.clone()))?; .map_err(|e| HeaderConsensusError(e, initial_header.clone()))?;
@ -94,10 +100,17 @@ pub trait Consensus: Debug + Send + Sync {
/// Note: validating headers with TD does not include other Consensus validation. /// Note: validating headers with TD does not include other Consensus validation.
fn validate_header_with_total_difficulty( fn validate_header_with_total_difficulty(
&self, &self,
header: &Header, header: &H,
total_difficulty: U256, total_difficulty: U256,
) -> Result<(), ConsensusError>; ) -> Result<(), ConsensusError>;
/// Ensures that body field values match the header.
fn validate_body_against_header(
&self,
body: &B,
header: &SealedHeader<H>,
) -> Result<(), ConsensusError>;
/// Validate a block disregarding world state, i.e. things that can be checked before sender /// Validate a block disregarding world state, i.e. things that can be checked before sender
/// recovery and execution. /// recovery and execution.
/// ///
@ -107,7 +120,8 @@ pub trait Consensus: Debug + Send + Sync {
/// **This should not be called for the genesis block**. /// **This should not be called for the genesis block**.
/// ///
/// Note: validating blocks does not include other validations of the Consensus /// Note: validating blocks does not include other validations of the Consensus
fn validate_block_pre_execution(&self, block: &SealedBlock) -> Result<(), ConsensusError>; fn validate_block_pre_execution(&self, block: &SealedBlock<H, B>)
-> Result<(), ConsensusError>;
/// Validate a block considering world state, i.e. things that can not be checked before /// Validate a block considering world state, i.e. things that can not be checked before
/// execution. /// execution.
@ -407,4 +421,4 @@ impl From<InvalidTransactionError> for ConsensusError {
/// `HeaderConsensusError` combines a `ConsensusError` with the `SealedHeader` it relates to. /// `HeaderConsensusError` combines a `ConsensusError` with the `SealedHeader` it relates to.
#[derive(derive_more::Display, derive_more::Error, Debug)] #[derive(derive_more::Display, derive_more::Error, Debug)]
#[display("Consensus error: {_0}, Invalid header: {_1:?}")] #[display("Consensus error: {_0}, Invalid header: {_1:?}")]
pub struct HeaderConsensusError(ConsensusError, SealedHeader); pub struct HeaderConsensusError<H>(ConsensusError, SealedHeader<H>);

View File

@ -1,34 +1,45 @@
use crate::{Consensus, ConsensusError, PostExecutionInput}; use crate::{Consensus, ConsensusError, PostExecutionInput};
use alloy_primitives::U256; use alloy_primitives::U256;
use reth_primitives::{BlockWithSenders, Header, SealedBlock, SealedHeader}; use reth_primitives::{BlockWithSenders, SealedBlock, SealedHeader};
/// A Consensus implementation that does nothing. /// A Consensus implementation that does nothing.
#[derive(Debug, Copy, Clone, Default)] #[derive(Debug, Copy, Clone, Default)]
#[non_exhaustive] #[non_exhaustive]
pub struct NoopConsensus; pub struct NoopConsensus;
impl Consensus for NoopConsensus { impl<H, B> Consensus<H, B> for NoopConsensus {
fn validate_header(&self, _header: &SealedHeader) -> Result<(), ConsensusError> { fn validate_header(&self, _header: &SealedHeader<H>) -> Result<(), ConsensusError> {
Ok(()) Ok(())
} }
fn validate_header_against_parent( fn validate_header_against_parent(
&self, &self,
_header: &SealedHeader, _header: &SealedHeader<H>,
_parent: &SealedHeader, _parent: &SealedHeader<H>,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
Ok(()) Ok(())
} }
fn validate_header_with_total_difficulty( fn validate_header_with_total_difficulty(
&self, &self,
_header: &Header, _header: &H,
_total_difficulty: U256, _total_difficulty: U256,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
Ok(()) Ok(())
} }
fn validate_block_pre_execution(&self, _block: &SealedBlock) -> Result<(), ConsensusError> { fn validate_body_against_header(
&self,
_body: &B,
_header: &SealedHeader<H>,
) -> Result<(), ConsensusError> {
Ok(())
}
fn validate_block_pre_execution(
&self,
_block: &SealedBlock<H, B>,
) -> Result<(), ConsensusError> {
Ok(()) Ok(())
} }

View File

@ -1,18 +1,25 @@
use crate::{Consensus, ConsensusError, PostExecutionInput}; use crate::{Consensus, ConsensusError, PostExecutionInput};
use alloy_primitives::U256; use alloy_primitives::U256;
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, Ordering};
use reth_primitives::{BlockWithSenders, Header, SealedBlock, SealedHeader}; use reth_primitives::{BlockWithSenders, SealedBlock, SealedHeader};
/// Consensus engine implementation for testing /// Consensus engine implementation for testing
#[derive(Debug)] #[derive(Debug)]
pub struct TestConsensus { pub struct TestConsensus {
/// Flag whether the header validation should purposefully fail /// Flag whether the header validation should purposefully fail
fail_validation: AtomicBool, fail_validation: AtomicBool,
/// Separate flag for setting whether `validate_body_against_header` should fail. It is needed
/// for testing networking logic for which the body failing this check is getting completely
/// rejected while more high-level failures are handled by the sync logic.
fail_body_against_header: AtomicBool,
} }
impl Default for TestConsensus { impl Default for TestConsensus {
fn default() -> Self { fn default() -> Self {
Self { fail_validation: AtomicBool::new(false) } Self {
fail_validation: AtomicBool::new(false),
fail_body_against_header: AtomicBool::new(false),
}
} }
} }
@ -24,12 +31,23 @@ impl TestConsensus {
/// Update the validation flag. /// Update the validation flag.
pub fn set_fail_validation(&self, val: bool) { pub fn set_fail_validation(&self, val: bool) {
self.fail_validation.store(val, Ordering::SeqCst) self.fail_validation.store(val, Ordering::SeqCst);
self.fail_body_against_header.store(val, Ordering::SeqCst);
}
/// Returns the body validation flag.
pub fn fail_body_against_header(&self) -> bool {
self.fail_body_against_header.load(Ordering::SeqCst)
}
/// Update the body validation flag.
pub fn set_fail_body_against_header(&self, val: bool) {
self.fail_body_against_header.store(val, Ordering::SeqCst);
} }
} }
impl Consensus for TestConsensus { impl<H, B> Consensus<H, B> for TestConsensus {
fn validate_header(&self, _header: &SealedHeader) -> Result<(), ConsensusError> { fn validate_header(&self, _header: &SealedHeader<H>) -> Result<(), ConsensusError> {
if self.fail_validation() { if self.fail_validation() {
Err(ConsensusError::BaseFeeMissing) Err(ConsensusError::BaseFeeMissing)
} else { } else {
@ -39,8 +57,8 @@ impl Consensus for TestConsensus {
fn validate_header_against_parent( fn validate_header_against_parent(
&self, &self,
_header: &SealedHeader, _header: &SealedHeader<H>,
_parent: &SealedHeader, _parent: &SealedHeader<H>,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
if self.fail_validation() { if self.fail_validation() {
Err(ConsensusError::BaseFeeMissing) Err(ConsensusError::BaseFeeMissing)
@ -51,7 +69,7 @@ impl Consensus for TestConsensus {
fn validate_header_with_total_difficulty( fn validate_header_with_total_difficulty(
&self, &self,
_header: &Header, _header: &H,
_total_difficulty: U256, _total_difficulty: U256,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
if self.fail_validation() { if self.fail_validation() {
@ -61,7 +79,22 @@ impl Consensus for TestConsensus {
} }
} }
fn validate_block_pre_execution(&self, _block: &SealedBlock) -> Result<(), ConsensusError> { fn validate_body_against_header(
&self,
_body: &B,
_header: &SealedHeader<H>,
) -> Result<(), ConsensusError> {
if self.fail_body_against_header() {
Err(ConsensusError::BaseFeeMissing)
} else {
Ok(())
}
}
fn validate_block_pre_execution(
&self,
_block: &SealedBlock<H, B>,
) -> Result<(), ConsensusError> {
if self.fail_validation() { if self.fail_validation() {
Err(ConsensusError::BaseFeeMissing) Err(ConsensusError::BaseFeeMissing)
} else { } else {

View File

@ -15,11 +15,11 @@ use reth_consensus::{Consensus, ConsensusError, PostExecutionInput};
use reth_consensus_common::validation::{ use reth_consensus_common::validation::{
validate_4844_header_standalone, validate_against_parent_4844, validate_4844_header_standalone, validate_against_parent_4844,
validate_against_parent_eip1559_base_fee, validate_against_parent_hash_number, validate_against_parent_eip1559_base_fee, validate_against_parent_hash_number,
validate_against_parent_timestamp, validate_block_pre_execution, validate_header_base_fee, validate_against_parent_timestamp, validate_block_pre_execution, validate_body_against_header,
validate_header_extradata, validate_header_gas, validate_header_base_fee, validate_header_extradata, validate_header_gas,
}; };
use reth_primitives::{ use reth_primitives::{
constants::MINIMUM_GAS_LIMIT, BlockWithSenders, Header, SealedBlock, SealedHeader, constants::MINIMUM_GAS_LIMIT, BlockBody, BlockWithSenders, Header, SealedBlock, SealedHeader,
}; };
use std::{fmt::Debug, sync::Arc, time::SystemTime}; use std::{fmt::Debug, sync::Arc, time::SystemTime};
@ -212,6 +212,14 @@ impl<ChainSpec: Send + Sync + EthChainSpec + EthereumHardforks + Debug> Consensu
Ok(()) Ok(())
} }
fn validate_body_against_header(
&self,
body: &BlockBody,
header: &SealedHeader,
) -> Result<(), ConsensusError> {
validate_body_against_header(body, header)
}
fn validate_block_pre_execution(&self, block: &SealedBlock) -> Result<(), ConsensusError> { fn validate_block_pre_execution(&self, block: &SealedBlock) -> Result<(), ConsensusError> {
validate_block_pre_execution(block, &self.chain_spec) validate_block_pre_execution(block, &self.chain_spec)
} }

View File

@ -1310,7 +1310,7 @@ mod tests {
fn test_head_update() { fn test_head_update() {
let client = Arc::new(TestHeadersClient::default()); let client = Arc::new(TestHeadersClient::default());
let header = SealedHeader::default(); let header: SealedHeader = SealedHeader::default();
let mut downloader = ReverseHeadersDownloaderBuilder::default() let mut downloader = ReverseHeadersDownloaderBuilder::default()
.build(Arc::clone(&client), Arc::new(TestConsensus::default())); .build(Arc::clone(&client), Arc::new(TestConsensus::default()));

View File

@ -473,7 +473,6 @@ mod tests {
use super::*; use super::*;
use crate::{peers::PeersManager, PeersConfig}; use crate::{peers::PeersManager, PeersConfig};
use alloy_primitives::B512; use alloy_primitives::B512;
use reth_primitives::SealedHeader;
use std::future::poll_fn; use std::future::poll_fn;
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
@ -590,8 +589,7 @@ mod tests {
}, },
response: tx, response: tx,
}; };
let mut header = SealedHeader::default().unseal(); let header = Header { number: 0, ..Default::default() };
header.number = 0u64;
(req, header) (req, header)
}; };

View File

@ -6,10 +6,10 @@ use crate::{
BlockClient, BlockClient,
}; };
use alloy_primitives::{Sealable, B256}; use alloy_primitives::{Sealable, B256};
use reth_consensus::{Consensus, ConsensusError}; use reth_consensus::Consensus;
use reth_eth_wire_types::HeadersDirection; use reth_eth_wire_types::HeadersDirection;
use reth_network_peers::WithPeerId; use reth_network_peers::WithPeerId;
use reth_primitives::{BlockBody, GotExpected, Header, SealedBlock, SealedHeader}; use reth_primitives::{BlockBody, Header, SealedBlock, SealedHeader};
use std::{ use std::{
cmp::Reverse, cmp::Reverse,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
@ -55,6 +55,7 @@ where
let client = self.client.clone(); let client = self.client.clone();
FetchFullBlockFuture { FetchFullBlockFuture {
hash, hash,
consensus: self.consensus.clone(),
request: FullBlockRequest { request: FullBlockRequest {
header: Some(client.get_header(hash.into())), header: Some(client.get_header(hash.into())),
body: Some(client.get_block_body(hash)), body: Some(client.get_block_body(hash)),
@ -110,6 +111,7 @@ where
Client: BlockClient, Client: BlockClient,
{ {
client: Client, client: Client,
consensus: Arc<dyn Consensus>,
hash: B256, hash: B256,
request: FullBlockRequest<Client>, request: FullBlockRequest<Client>,
header: Option<SealedHeader>, header: Option<SealedHeader>,
@ -142,7 +144,8 @@ where
BodyResponse::Validated(body) => Some(SealedBlock::new(header, body)), BodyResponse::Validated(body) => Some(SealedBlock::new(header, body)),
BodyResponse::PendingValidation(resp) => { BodyResponse::PendingValidation(resp) => {
// ensure the block is valid, else retry // ensure the block is valid, else retry
if let Err(err) = ensure_valid_body_response(&header, resp.data()) { if let Err(err) = self.consensus.validate_body_against_header(resp.data(), &header)
{
debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body"); debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
self.client.report_bad_message(resp.peer_id()); self.client.report_bad_message(resp.peer_id());
self.header = Some(header); self.header = Some(header);
@ -156,7 +159,7 @@ where
fn on_block_response(&mut self, resp: WithPeerId<BlockBody>) { fn on_block_response(&mut self, resp: WithPeerId<BlockBody>) {
if let Some(ref header) = self.header { if let Some(ref header) = self.header {
if let Err(err) = ensure_valid_body_response(header, resp.data()) { if let Err(err) = self.consensus.validate_body_against_header(resp.data(), header) {
debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body"); debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
self.client.report_bad_message(resp.peer_id()); self.client.report_bad_message(resp.peer_id());
return return
@ -306,50 +309,6 @@ enum BodyResponse {
/// Still needs to be validated against header /// Still needs to be validated against header
PendingValidation(WithPeerId<BlockBody>), PendingValidation(WithPeerId<BlockBody>),
} }
/// Ensures the block response data matches the header.
///
/// This ensures the body response items match the header's hashes:
/// - ommer hash
/// - transaction root
/// - withdrawals root
fn ensure_valid_body_response(
header: &SealedHeader,
block: &BlockBody,
) -> Result<(), ConsensusError> {
let ommers_hash = block.calculate_ommers_root();
if header.ommers_hash != ommers_hash {
return Err(ConsensusError::BodyOmmersHashDiff(
GotExpected { got: ommers_hash, expected: header.ommers_hash }.into(),
))
}
let tx_root = block.calculate_tx_root();
if header.transactions_root != tx_root {
return Err(ConsensusError::BodyTransactionRootDiff(
GotExpected { got: tx_root, expected: header.transactions_root }.into(),
))
}
match (header.withdrawals_root, &block.withdrawals) {
(Some(header_withdrawals_root), Some(withdrawals)) => {
let withdrawals = withdrawals.as_slice();
let withdrawals_root = reth_primitives::proofs::calculate_withdrawals_root(withdrawals);
if withdrawals_root != header_withdrawals_root {
return Err(ConsensusError::BodyWithdrawalsRootDiff(
GotExpected { got: withdrawals_root, expected: header_withdrawals_root }.into(),
))
}
}
(None, None) => {
// this is ok because we assume the fork is not active in this case
}
_ => return Err(ConsensusError::WithdrawalsRootUnexpected),
}
Ok(())
}
/// A future that downloads a range of full blocks from the network. /// A future that downloads a range of full blocks from the network.
/// ///
/// This first fetches the headers for the given range using the inner `Client`. Once the request /// This first fetches the headers for the given range using the inner `Client`. Once the request
@ -446,7 +405,9 @@ where
BodyResponse::Validated(body) => body, BodyResponse::Validated(body) => body,
BodyResponse::PendingValidation(resp) => { BodyResponse::PendingValidation(resp) => {
// ensure the block is valid, else retry // ensure the block is valid, else retry
if let Err(err) = ensure_valid_body_response(header, resp.data()) { if let Err(err) =
self.consensus.validate_body_against_header(resp.data(), header)
{
debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body in range response"); debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body in range response");
self.client.report_bad_message(resp.peer_id()); self.client.report_bad_message(resp.peer_id());
@ -695,7 +656,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn download_single_full_block() { async fn download_single_full_block() {
let client = TestFullBlockClient::default(); let client = TestFullBlockClient::default();
let header = SealedHeader::default(); let header: SealedHeader = SealedHeader::default();
let body = BlockBody::default(); let body = BlockBody::default();
client.insert(header.clone(), body.clone()); client.insert(header.clone(), body.clone());
let client = FullBlockClient::test_client(client); let client = FullBlockClient::test_client(client);
@ -707,7 +668,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn download_single_full_block_range() { async fn download_single_full_block_range() {
let client = TestFullBlockClient::default(); let client = TestFullBlockClient::default();
let header = SealedHeader::default(); let header: SealedHeader = SealedHeader::default();
let body = BlockBody::default(); let body = BlockBody::default();
client.insert(header.clone(), body.clone()); client.insert(header.clone(), body.clone());
let client = FullBlockClient::test_client(client); let client = FullBlockClient::test_client(client);
@ -722,7 +683,7 @@ mod tests {
client: &TestFullBlockClient, client: &TestFullBlockClient,
range: Range<usize>, range: Range<usize>,
) -> (SealedHeader, BlockBody) { ) -> (SealedHeader, BlockBody) {
let mut sealed_header = SealedHeader::default(); let mut sealed_header: SealedHeader = SealedHeader::default();
let body = BlockBody::default(); let body = BlockBody::default();
for _ in range { for _ in range {
let (mut header, hash) = sealed_header.split(); let (mut header, hash) = sealed_header.split();
@ -785,6 +746,7 @@ mod tests {
let test_consensus = reth_consensus::test_utils::TestConsensus::default(); let test_consensus = reth_consensus::test_utils::TestConsensus::default();
test_consensus.set_fail_validation(true); test_consensus.set_fail_validation(true);
test_consensus.set_fail_body_against_header(false);
let client = FullBlockClient::new(client, Arc::new(test_consensus)); let client = FullBlockClient::new(client, Arc::new(test_consensus));
let received = client.get_full_block_range(header.hash(), range_length as u64).await; let received = client.get_full_block_range(header.hash(), range_length as u64).await;

View File

@ -143,8 +143,10 @@ impl Stream for TestDownload {
return Poll::Ready(None) return Poll::Ready(None)
} }
let empty = SealedHeader::default(); let empty: SealedHeader = SealedHeader::default();
if let Err(error) = this.consensus.validate_header_against_parent(&empty, &empty) { if let Err(error) =
Consensus::<_>::validate_header_against_parent(&this.consensus, &empty, &empty)
{
this.done = true; this.done = true;
return Poll::Ready(Some(Err(DownloadError::HeaderValidation { return Poll::Ready(Some(Err(DownloadError::HeaderValidation {
hash: empty.hash(), hash: empty.hash(),

View File

@ -15,13 +15,15 @@ use reth_chainspec::EthereumHardforks;
use reth_consensus::{Consensus, ConsensusError, PostExecutionInput}; use reth_consensus::{Consensus, ConsensusError, PostExecutionInput};
use reth_consensus_common::validation::{ use reth_consensus_common::validation::{
validate_against_parent_4844, validate_against_parent_eip1559_base_fee, validate_against_parent_4844, validate_against_parent_eip1559_base_fee,
validate_against_parent_hash_number, validate_against_parent_timestamp, validate_cancun_gas, validate_against_parent_hash_number, validate_against_parent_timestamp,
validate_header_base_fee, validate_header_extradata, validate_header_gas, validate_body_against_header, validate_cancun_gas, validate_header_base_fee,
validate_shanghai_withdrawals, validate_header_extradata, validate_header_gas, validate_shanghai_withdrawals,
}; };
use reth_optimism_chainspec::OpChainSpec; use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_forks::OpHardforks; use reth_optimism_forks::OpHardforks;
use reth_primitives::{BlockWithSenders, GotExpected, Header, SealedBlock, SealedHeader}; use reth_primitives::{
BlockBody, BlockWithSenders, GotExpected, Header, SealedBlock, SealedHeader,
};
use std::{sync::Arc, time::SystemTime}; use std::{sync::Arc, time::SystemTime};
mod proof; mod proof;
@ -119,6 +121,14 @@ impl Consensus for OpBeaconConsensus {
Ok(()) Ok(())
} }
fn validate_body_against_header(
&self,
body: &BlockBody,
header: &SealedHeader,
) -> Result<(), ConsensusError> {
validate_body_against_header(body, header)
}
fn validate_block_pre_execution(&self, block: &SealedBlock) -> Result<(), ConsensusError> { fn validate_block_pre_execution(&self, block: &SealedBlock) -> Result<(), ConsensusError> {
// Check ommers hash // Check ommers hash
let ommers_hash = reth_primitives::proofs::calculate_ommers_root(&block.body.ommers); let ommers_hash = reth_primitives::proofs::calculate_ommers_root(&block.body.ommers);

View File

@ -30,12 +30,10 @@ impl<H> SealedHeader<H> {
pub const fn new(header: H, hash: BlockHash) -> Self { pub const fn new(header: H, hash: BlockHash) -> Self {
Self { header, hash } Self { header, hash }
} }
}
impl SealedHeader {
/// Returns the sealed Header fields. /// Returns the sealed Header fields.
#[inline] #[inline]
pub const fn header(&self) -> &Header { pub const fn header(&self) -> &H {
&self.header &self.header
} }
@ -46,15 +44,17 @@ impl SealedHeader {
} }
/// Extract raw header that can be modified. /// Extract raw header that can be modified.
pub fn unseal(self) -> Header { pub fn unseal(self) -> H {
self.header self.header
} }
/// This is the inverse of [`Header::seal_slow`] which returns the raw header and hash. /// This is the inverse of [`Header::seal_slow`] which returns the raw header and hash.
pub fn split(self) -> (Header, BlockHash) { pub fn split(self) -> (H, BlockHash) {
(self.header, self.hash) (self.header, self.hash)
} }
}
impl SealedHeader {
/// Return the number hash tuple. /// Return the number hash tuple.
pub fn num_hash(&self) -> BlockNumHash { pub fn num_hash(&self) -> BlockNumHash {
BlockNumHash::new(self.number, self.hash) BlockNumHash::new(self.number, self.hash)
@ -67,9 +67,9 @@ impl SealedHeader {
} }
} }
impl Default for SealedHeader { impl<H: Sealable + Default> Default for SealedHeader<H> {
fn default() -> Self { fn default() -> Self {
let sealed = Header::default().seal_slow(); let sealed = H::default().seal_slow();
let (header, hash) = sealed.into_parts(); let (header, hash) = sealed.into_parts();
Self { header, hash } Self { header, hash }
} }

View File

@ -256,22 +256,21 @@ impl BlockWithSenders {
/// Sealed Ethereum full block. /// Sealed Ethereum full block.
/// ///
/// Withdrawals can be optionally included at the end of the RLP encoded message. /// Withdrawals can be optionally included at the end of the RLP encoded message.
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp, 32))] #[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp, 32))]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, Deref, DerefMut)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Deref, DerefMut)]
pub struct SealedBlock { pub struct SealedBlock<H = Header, B = BlockBody> {
/// Locked block header. /// Locked block header.
#[deref] #[deref]
#[deref_mut] #[deref_mut]
pub header: SealedHeader, pub header: SealedHeader<H>,
/// Block body. /// Block body.
pub body: BlockBody, pub body: B,
} }
impl SealedBlock { impl<H, B> SealedBlock<H, B> {
/// Create a new sealed block instance using the sealed header and block body. /// Create a new sealed block instance using the sealed header and block body.
#[inline] #[inline]
pub const fn new(header: SealedHeader, body: BlockBody) -> Self { pub const fn new(header: SealedHeader<H>, body: B) -> Self {
Self { header, body } Self { header, body }
} }
@ -281,18 +280,20 @@ impl SealedBlock {
self.header.hash() self.header.hash()
} }
/// Splits the [`BlockBody`] and [`SealedHeader`] into separate components
#[inline]
pub fn split_header_body(self) -> (SealedHeader<H>, B) {
(self.header, self.body)
}
}
impl SealedBlock {
/// Splits the sealed block into underlying components /// Splits the sealed block into underlying components
#[inline] #[inline]
pub fn split(self) -> (SealedHeader, Vec<TransactionSigned>, Vec<Header>) { pub fn split(self) -> (SealedHeader, Vec<TransactionSigned>, Vec<Header>) {
(self.header, self.body.transactions, self.body.ommers) (self.header, self.body.transactions, self.body.ommers)
} }
/// Splits the [`BlockBody`] and [`SealedHeader`] into separate components
#[inline]
pub fn split_header_body(self) -> (SealedHeader, BlockBody) {
(self.header, self.body)
}
/// Returns an iterator over all blob transactions of the block /// Returns an iterator over all blob transactions of the block
#[inline] #[inline]
pub fn blob_transactions_iter(&self) -> impl Iterator<Item = &TransactionSigned> + '_ { pub fn blob_transactions_iter(&self) -> impl Iterator<Item = &TransactionSigned> + '_ {
@ -436,6 +437,27 @@ impl From<SealedBlock> for Block {
} }
} }
impl<H, B> Default for SealedBlock<H, B>
where
SealedHeader<H>: Default,
B: Default,
{
fn default() -> Self {
Self { header: Default::default(), body: Default::default() }
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a, H, B> arbitrary::Arbitrary<'a> for SealedBlock<H, B>
where
SealedHeader<H>: arbitrary::Arbitrary<'a>,
B: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
}
}
/// Sealed block with senders recovered from transactions. /// Sealed block with senders recovered from transactions.
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, Deref, DerefMut)] #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, Deref, DerefMut)]
pub struct SealedBlockWithSenders { pub struct SealedBlockWithSenders {
@ -503,7 +525,7 @@ impl SealedBlockWithSenders {
#[cfg(any(test, feature = "arbitrary"))] #[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for SealedBlockWithSenders { impl<'a> arbitrary::Arbitrary<'a> for SealedBlockWithSenders {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let block = SealedBlock::arbitrary(u)?; let block: SealedBlock = SealedBlock::arbitrary(u)?;
let senders = block let senders = block
.body .body
@ -1083,7 +1105,7 @@ mod tests {
#[test] #[test]
fn test_default_seal() { fn test_default_seal() {
let block = SealedBlock::default(); let block: SealedBlock = SealedBlock::default();
let sealed = block.hash(); let sealed = block.hash();
let block = block.unseal(); let block = block.unseal();
let block = block.seal_slow(); let block = block.seal_slow();