reth_consensus/
lib.rs

1//! Consensus protocol functions
2
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6    issue_tracker_base_url = "https://github.com/SeismicSystems/seismic-reth/issues/"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14use alloc::{fmt::Debug, sync::Arc, vec::Vec};
15use alloy_consensus::Header;
16use alloy_eips::eip7685::Requests;
17use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
18use reth_primitives::{
19    BlockBody, BlockWithSenders, EthPrimitives, GotExpected, GotExpectedBoxed,
20    InvalidTransactionError, NodePrimitives, Receipt, SealedBlock, SealedHeader,
21};
22use reth_primitives_traits::constants::MINIMUM_GAS_LIMIT;
23
24/// A consensus implementation that does nothing.
25pub mod noop;
26
27#[cfg(any(test, feature = "test-utils"))]
28/// test helpers for mocking consensus
29pub mod test_utils;
30
31/// Post execution input passed to [`FullConsensus::validate_block_post_execution`].
32#[derive(Debug)]
33pub struct PostExecutionInput<'a, R = Receipt> {
34    /// Receipts of the block.
35    pub receipts: &'a [R],
36    /// EIP-7685 requests of the block.
37    pub requests: &'a Requests,
38}
39
40impl<'a, R> PostExecutionInput<'a, R> {
41    /// Creates a new instance of `PostExecutionInput`.
42    pub const fn new(receipts: &'a [R], requests: &'a Requests) -> Self {
43        Self { receipts, requests }
44    }
45}
46
47/// [`Consensus`] implementation which knows full node primitives and is able to validation block's
48/// execution outcome.
49#[auto_impl::auto_impl(&, Arc)]
50pub trait FullConsensus<N: NodePrimitives = EthPrimitives>:
51    AsConsensus<N::BlockHeader, N::BlockBody>
52{
53    /// Validate a block considering world state, i.e. things that can not be checked before
54    /// execution.
55    ///
56    /// See the Yellow Paper sections 4.3.2 "Holistic Validity".
57    ///
58    /// Note: validating blocks does not include other validations of the Consensus
59    fn validate_block_post_execution(
60        &self,
61        block: &BlockWithSenders<N::Block>,
62        input: PostExecutionInput<'_, N::Receipt>,
63    ) -> Result<(), ConsensusError>;
64}
65
66/// Consensus is a protocol that chooses canonical chain.
67#[auto_impl::auto_impl(&, Arc)]
68pub trait Consensus<H = Header, B = BlockBody>: AsHeaderValidator<H> {
69    /// Ensures that body field values match the header.
70    fn validate_body_against_header(
71        &self,
72        body: &B,
73        header: &SealedHeader<H>,
74    ) -> Result<(), ConsensusError>;
75
76    /// Validate a block disregarding world state, i.e. things that can be checked before sender
77    /// recovery and execution.
78    ///
79    /// See the Yellow Paper sections 4.3.2 "Holistic Validity", 4.3.4 "Block Header Validity", and
80    /// 11.1 "Ommer Validation".
81    ///
82    /// **This should not be called for the genesis block**.
83    ///
84    /// Note: validating blocks does not include other validations of the Consensus
85    fn validate_block_pre_execution(&self, block: &SealedBlock<H, B>)
86        -> Result<(), ConsensusError>;
87}
88
89/// HeaderValidator is a protocol that validates headers and their relationships.
90#[auto_impl::auto_impl(&, Arc)]
91pub trait HeaderValidator<H = Header>: Debug + Send + Sync {
92    /// Validate if header is correct and follows consensus specification.
93    ///
94    /// This is called on standalone header to check if all hashes are correct.
95    fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError>;
96
97    /// Validate that the header information regarding parent are correct.
98    /// This checks the block number, timestamp, basefee and gas limit increment.
99    ///
100    /// This is called before properties that are not in the header itself (like total difficulty)
101    /// have been computed.
102    ///
103    /// **This should not be called for the genesis block**.
104    ///
105    /// Note: Validating header against its parent does not include other HeaderValidator
106    /// validations.
107    fn validate_header_against_parent(
108        &self,
109        header: &SealedHeader<H>,
110        parent: &SealedHeader<H>,
111    ) -> Result<(), ConsensusError>;
112
113    /// Validates the given headers
114    ///
115    /// This ensures that the first header is valid on its own and all subsequent headers are valid
116    /// on its own and valid against its parent.
117    ///
118    /// Note: this expects that the headers are in natural order (ascending block number)
119    fn validate_header_range(
120        &self,
121        headers: &[SealedHeader<H>],
122    ) -> Result<(), HeaderConsensusError<H>>
123    where
124        H: Clone,
125    {
126        if let Some((initial_header, remaining_headers)) = headers.split_first() {
127            self.validate_header(initial_header)
128                .map_err(|e| HeaderConsensusError(e, initial_header.clone()))?;
129            let mut parent = initial_header;
130            for child in remaining_headers {
131                self.validate_header(child).map_err(|e| HeaderConsensusError(e, child.clone()))?;
132                self.validate_header_against_parent(child, parent)
133                    .map_err(|e| HeaderConsensusError(e, child.clone()))?;
134                parent = child;
135            }
136        }
137        Ok(())
138    }
139
140    /// Validate if the header is correct and follows the consensus specification, including
141    /// computed properties (like total difficulty).
142    ///
143    /// Some consensus engines may want to do additional checks here.
144    ///
145    /// Note: validating headers with TD does not include other HeaderValidator validation.
146    fn validate_header_with_total_difficulty(
147        &self,
148        header: &H,
149        total_difficulty: U256,
150    ) -> Result<(), ConsensusError>;
151}
152
153/// Helper trait to cast `Arc<dyn Consensus>` to `Arc<dyn HeaderValidator>`
154pub trait AsHeaderValidator<H>: HeaderValidator<H> {
155    /// Converts the [`Arc`] of self to [`Arc`] of [`HeaderValidator`]
156    fn as_header_validator<'a>(self: Arc<Self>) -> Arc<dyn HeaderValidator<H> + 'a>
157    where
158        Self: 'a;
159}
160
161impl<T: HeaderValidator<H>, H> AsHeaderValidator<H> for T {
162    fn as_header_validator<'a>(self: Arc<Self>) -> Arc<dyn HeaderValidator<H> + 'a>
163    where
164        Self: 'a,
165    {
166        self
167    }
168}
169
170/// Helper trait to cast `Arc<dyn FullConsensus>` to `Arc<dyn Consensus>`
171pub trait AsConsensus<H, B>: Consensus<H, B> {
172    /// Converts the [`Arc`] of self to [`Arc`] of [`HeaderValidator`]
173    fn as_consensus<'a>(self: Arc<Self>) -> Arc<dyn Consensus<H, B> + 'a>
174    where
175        Self: 'a;
176}
177
178impl<T: Consensus<H, B>, H, B> AsConsensus<H, B> for T {
179    fn as_consensus<'a>(self: Arc<Self>) -> Arc<dyn Consensus<H, B> + 'a>
180    where
181        Self: 'a,
182    {
183        self
184    }
185}
186
187/// Consensus Errors
188#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display, derive_more::Error)]
189pub enum ConsensusError {
190    /// Error when the gas used in the header exceeds the gas limit.
191    #[display("block used gas ({gas_used}) is greater than gas limit ({gas_limit})")]
192    HeaderGasUsedExceedsGasLimit {
193        /// The gas used in the block header.
194        gas_used: u64,
195        /// The gas limit in the block header.
196        gas_limit: u64,
197    },
198
199    /// Error when block gas used doesn't match expected value
200    #[display(
201        "block gas used mismatch: {gas}; gas spent by each transaction: {gas_spent_by_tx:?}"
202    )]
203    BlockGasUsed {
204        /// The gas diff.
205        gas: GotExpected<u64>,
206        /// Gas spent by each transaction
207        gas_spent_by_tx: Vec<(u64, u64)>,
208    },
209
210    /// Error when the hash of block ommer is different from the expected hash.
211    #[display("mismatched block ommer hash: {_0}")]
212    BodyOmmersHashDiff(GotExpectedBoxed<B256>),
213
214    /// Error when the state root in the block is different from the expected state root.
215    #[display("mismatched block state root: {_0}")]
216    BodyStateRootDiff(GotExpectedBoxed<B256>),
217
218    /// Error when the transaction root in the block is different from the expected transaction
219    /// root.
220    #[display("mismatched block transaction root: {_0}")]
221    BodyTransactionRootDiff(GotExpectedBoxed<B256>),
222
223    /// Error when the receipt root in the block is different from the expected receipt root.
224    #[display("receipt root mismatch: {_0}")]
225    BodyReceiptRootDiff(GotExpectedBoxed<B256>),
226
227    /// Error when header bloom filter is different from the expected bloom filter.
228    #[display("header bloom filter mismatch: {_0}")]
229    BodyBloomLogDiff(GotExpectedBoxed<Bloom>),
230
231    /// Error when the withdrawals root in the block is different from the expected withdrawals
232    /// root.
233    #[display("mismatched block withdrawals root: {_0}")]
234    BodyWithdrawalsRootDiff(GotExpectedBoxed<B256>),
235
236    /// Error when the requests hash in the block is different from the expected requests
237    /// hash.
238    #[display("mismatched block requests hash: {_0}")]
239    BodyRequestsHashDiff(GotExpectedBoxed<B256>),
240
241    /// Error when a block with a specific hash and number is already known.
242    #[display("block with [hash={hash}, number={number}] is already known")]
243    BlockKnown {
244        /// The hash of the known block.
245        hash: BlockHash,
246        /// The block number of the known block.
247        number: BlockNumber,
248    },
249
250    /// Error when the parent hash of a block is not known.
251    #[display("block parent [hash={hash}] is not known")]
252    ParentUnknown {
253        /// The hash of the unknown parent block.
254        hash: BlockHash,
255    },
256
257    /// Error when the block number does not match the parent block number.
258    #[display(
259        "block number {block_number} does not match parent block number {parent_block_number}"
260    )]
261    ParentBlockNumberMismatch {
262        /// The parent block number.
263        parent_block_number: BlockNumber,
264        /// The block number.
265        block_number: BlockNumber,
266    },
267
268    /// Error when the parent hash does not match the expected parent hash.
269    #[display("mismatched parent hash: {_0}")]
270    ParentHashMismatch(GotExpectedBoxed<B256>),
271
272    /// Error when the block timestamp is in the future compared to our clock time.
273    #[display(
274        "block timestamp {timestamp} is in the future compared to our clock time {present_timestamp}"
275    )]
276    TimestampIsInFuture {
277        /// The block's timestamp.
278        timestamp: u64,
279        /// The current timestamp.
280        present_timestamp: u64,
281    },
282
283    /// Error when the base fee is missing.
284    #[display("base fee missing")]
285    BaseFeeMissing,
286
287    /// Error when there is a transaction signer recovery error.
288    #[display("transaction signer recovery error")]
289    TransactionSignerRecoveryError,
290
291    /// Error when the extra data length exceeds the maximum allowed.
292    #[display("extra data {len} exceeds max length")]
293    ExtraDataExceedsMax {
294        /// The length of the extra data.
295        len: usize,
296    },
297
298    /// Error when the difficulty after a merge is not zero.
299    #[display("difficulty after merge is not zero")]
300    TheMergeDifficultyIsNotZero,
301
302    /// Error when the nonce after a merge is not zero.
303    #[display("nonce after merge is not zero")]
304    TheMergeNonceIsNotZero,
305
306    /// Error when the ommer root after a merge is not empty.
307    #[display("ommer root after merge is not empty")]
308    TheMergeOmmerRootIsNotEmpty,
309
310    /// Error when the withdrawals root is missing.
311    #[display("missing withdrawals root")]
312    WithdrawalsRootMissing,
313
314    /// Error when the requests hash is missing.
315    #[display("missing requests hash")]
316    RequestsHashMissing,
317
318    /// Error when an unexpected withdrawals root is encountered.
319    #[display("unexpected withdrawals root")]
320    WithdrawalsRootUnexpected,
321
322    /// Error when an unexpected requests hash is encountered.
323    #[display("unexpected requests hash")]
324    RequestsHashUnexpected,
325
326    /// Error when withdrawals are missing.
327    #[display("missing withdrawals")]
328    BodyWithdrawalsMissing,
329
330    /// Error when requests are missing.
331    #[display("missing requests")]
332    BodyRequestsMissing,
333
334    /// Error when blob gas used is missing.
335    #[display("missing blob gas used")]
336    BlobGasUsedMissing,
337
338    /// Error when unexpected blob gas used is encountered.
339    #[display("unexpected blob gas used")]
340    BlobGasUsedUnexpected,
341
342    /// Error when excess blob gas is missing.
343    #[display("missing excess blob gas")]
344    ExcessBlobGasMissing,
345
346    /// Error when unexpected excess blob gas is encountered.
347    #[display("unexpected excess blob gas")]
348    ExcessBlobGasUnexpected,
349
350    /// Error when the parent beacon block root is missing.
351    #[display("missing parent beacon block root")]
352    ParentBeaconBlockRootMissing,
353
354    /// Error when an unexpected parent beacon block root is encountered.
355    #[display("unexpected parent beacon block root")]
356    ParentBeaconBlockRootUnexpected,
357
358    /// Error when blob gas used exceeds the maximum allowed.
359    #[display("blob gas used {blob_gas_used} exceeds maximum allowance {max_blob_gas_per_block}")]
360    BlobGasUsedExceedsMaxBlobGasPerBlock {
361        /// The actual blob gas used.
362        blob_gas_used: u64,
363        /// The maximum allowed blob gas per block.
364        max_blob_gas_per_block: u64,
365    },
366
367    /// Error when blob gas used is not a multiple of blob gas per blob.
368    #[display(
369        "blob gas used {blob_gas_used} is not a multiple of blob gas per blob {blob_gas_per_blob}"
370    )]
371    BlobGasUsedNotMultipleOfBlobGasPerBlob {
372        /// The actual blob gas used.
373        blob_gas_used: u64,
374        /// The blob gas per blob.
375        blob_gas_per_blob: u64,
376    },
377
378    /// Error when excess blob gas is not a multiple of blob gas per blob.
379    #[display(
380        "excess blob gas {excess_blob_gas} is not a multiple of blob gas per blob {blob_gas_per_blob}"
381    )]
382    ExcessBlobGasNotMultipleOfBlobGasPerBlob {
383        /// The actual excess blob gas.
384        excess_blob_gas: u64,
385        /// The blob gas per blob.
386        blob_gas_per_blob: u64,
387    },
388
389    /// Error when the blob gas used in the header does not match the expected blob gas used.
390    #[display("blob gas used mismatch: {_0}")]
391    BlobGasUsedDiff(GotExpected<u64>),
392
393    /// Error for a transaction that violates consensus.
394    InvalidTransaction(InvalidTransactionError),
395
396    /// Error when the block's base fee is different from the expected base fee.
397    #[display("block base fee mismatch: {_0}")]
398    BaseFeeDiff(GotExpected<u64>),
399
400    /// Error when there is an invalid excess blob gas.
401    #[display(
402        "invalid excess blob gas: {diff}; \
403            parent excess blob gas: {parent_excess_blob_gas}, \
404            parent blob gas used: {parent_blob_gas_used}"
405    )]
406    ExcessBlobGasDiff {
407        /// The excess blob gas diff.
408        diff: GotExpected<u64>,
409        /// The parent excess blob gas.
410        parent_excess_blob_gas: u64,
411        /// The parent blob gas used.
412        parent_blob_gas_used: u64,
413    },
414
415    /// Error when the child gas limit exceeds the maximum allowed increase.
416    #[display("child gas_limit {child_gas_limit} max increase is {parent_gas_limit}/1024")]
417    GasLimitInvalidIncrease {
418        /// The parent gas limit.
419        parent_gas_limit: u64,
420        /// The child gas limit.
421        child_gas_limit: u64,
422    },
423
424    /// Error indicating that the child gas limit is below the minimum allowed limit.
425    ///
426    /// This error occurs when the child gas limit is less than the specified minimum gas limit.
427    #[display(
428        "child gas limit {child_gas_limit} is below the minimum allowed limit ({MINIMUM_GAS_LIMIT})"
429    )]
430    GasLimitInvalidMinimum {
431        /// The child gas limit.
432        child_gas_limit: u64,
433    },
434
435    /// Error when the child gas limit exceeds the maximum allowed decrease.
436    #[display("child gas_limit {child_gas_limit} max decrease is {parent_gas_limit}/1024")]
437    GasLimitInvalidDecrease {
438        /// The parent gas limit.
439        parent_gas_limit: u64,
440        /// The child gas limit.
441        child_gas_limit: u64,
442    },
443
444    /// Error when the block timestamp is in the past compared to the parent timestamp.
445    #[display(
446        "block timestamp {timestamp} is in the past compared to the parent timestamp {parent_timestamp}"
447    )]
448    TimestampIsInPast {
449        /// The parent block's timestamp.
450        parent_timestamp: u64,
451        /// The block's timestamp.
452        timestamp: u64,
453    },
454}
455
456impl ConsensusError {
457    /// Returns `true` if the error is a state root error.
458    pub const fn is_state_root_error(&self) -> bool {
459        matches!(self, Self::BodyStateRootDiff(_))
460    }
461}
462
463impl From<InvalidTransactionError> for ConsensusError {
464    fn from(value: InvalidTransactionError) -> Self {
465        Self::InvalidTransaction(value)
466    }
467}
468
469/// `HeaderConsensusError` combines a `ConsensusError` with the `SealedHeader` it relates to.
470#[derive(derive_more::Display, derive_more::Error, Debug)]
471#[display("Consensus error: {_0}, Invalid header: {_1:?}")]
472pub struct HeaderConsensusError<H>(ConsensusError, SealedHeader<H>);