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