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))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14use alloc::{boxed::Box, fmt::Debug, string::String, vec::Vec};
15use alloy_consensus::Header;
16use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256};
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>: Consensus<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>: HeaderValidator<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.4.2 "Holistic Validity", 4.4.4 "Block Header Validity".
66    /// Note: Ommer Validation (previously section 11.1) has been deprecated since the Paris hard
67    /// fork transition to proof of stake.
68    ///
69    /// **This should not be called for the genesis block**.
70    ///
71    /// Note: validating blocks does not include other validations of the Consensus
72    fn validate_block_pre_execution(&self, block: &SealedBlock<B>) -> Result<(), Self::Error>;
73}
74
75/// `HeaderValidator` is a protocol that validates headers and their relationships.
76#[auto_impl::auto_impl(&, Arc)]
77pub trait HeaderValidator<H = Header>: Debug + Send + Sync {
78    /// Validate if header is correct and follows consensus specification.
79    ///
80    /// This is called on standalone header to check if all hashes are correct.
81    fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError>;
82
83    /// Validate that the header information regarding parent are correct.
84    /// This checks the block number, timestamp, basefee and gas limit increment.
85    ///
86    /// This is called before properties that are not in the header itself (like total difficulty)
87    /// have been computed.
88    ///
89    /// **This should not be called for the genesis block**.
90    ///
91    /// Note: Validating header against its parent does not include other `HeaderValidator`
92    /// validations.
93    fn validate_header_against_parent(
94        &self,
95        header: &SealedHeader<H>,
96        parent: &SealedHeader<H>,
97    ) -> Result<(), ConsensusError>;
98
99    /// Validates the given headers
100    ///
101    /// This ensures that the first header is valid on its own and all subsequent headers are valid
102    /// on its own and valid against its parent.
103    ///
104    /// Note: this expects that the headers are in natural order (ascending block number)
105    fn validate_header_range(
106        &self,
107        headers: &[SealedHeader<H>],
108    ) -> Result<(), HeaderConsensusError<H>>
109    where
110        H: Clone,
111    {
112        if let Some((initial_header, remaining_headers)) = headers.split_first() {
113            self.validate_header(initial_header)
114                .map_err(|e| HeaderConsensusError(e, initial_header.clone()))?;
115            let mut parent = initial_header;
116            for child in remaining_headers {
117                self.validate_header(child).map_err(|e| HeaderConsensusError(e, child.clone()))?;
118                self.validate_header_against_parent(child, parent)
119                    .map_err(|e| HeaderConsensusError(e, child.clone()))?;
120                parent = child;
121            }
122        }
123        Ok(())
124    }
125}
126
127/// Consensus Errors
128#[derive(Debug, PartialEq, Eq, Clone, thiserror::Error)]
129pub enum ConsensusError {
130    /// Error when the gas used in the header exceeds the gas limit.
131    #[error("block used gas ({gas_used}) is greater than gas limit ({gas_limit})")]
132    HeaderGasUsedExceedsGasLimit {
133        /// The gas used in the block header.
134        gas_used: u64,
135        /// The gas limit in the block header.
136        gas_limit: u64,
137    },
138    /// Error when the gas the gas limit is more than the maximum allowed.
139    #[error(
140        "header gas limit ({gas_limit}) exceed the maximum allowed gas limit ({MAXIMUM_GAS_LIMIT_BLOCK})"
141    )]
142    HeaderGasLimitExceedsMax {
143        /// The gas limit in the block header.
144        gas_limit: u64,
145    },
146
147    /// Error when block gas used doesn't match expected value
148    #[error("block gas used mismatch: {gas}; gas spent by each transaction: {gas_spent_by_tx:?}")]
149    BlockGasUsed {
150        /// The gas diff.
151        gas: GotExpected<u64>,
152        /// Gas spent by each transaction
153        gas_spent_by_tx: Vec<(u64, u64)>,
154    },
155
156    /// Error when the hash of block ommer is different from the expected hash.
157    #[error("mismatched block ommer hash: {0}")]
158    BodyOmmersHashDiff(GotExpectedBoxed<B256>),
159
160    /// Error when the state root in the block is different from the expected state root.
161    #[error("mismatched block state root: {0}")]
162    BodyStateRootDiff(GotExpectedBoxed<B256>),
163
164    /// Error when the transaction root in the block is different from the expected transaction
165    /// root.
166    #[error("mismatched block transaction root: {0}")]
167    BodyTransactionRootDiff(GotExpectedBoxed<B256>),
168
169    /// Error when the receipt root in the block is different from the expected receipt root.
170    #[error("receipt root mismatch: {0}")]
171    BodyReceiptRootDiff(GotExpectedBoxed<B256>),
172
173    /// Error when header bloom filter is different from the expected bloom filter.
174    #[error("header bloom filter mismatch: {0}")]
175    BodyBloomLogDiff(GotExpectedBoxed<Bloom>),
176
177    /// Error when the withdrawals root in the block is different from the expected withdrawals
178    /// root.
179    #[error("mismatched block withdrawals root: {0}")]
180    BodyWithdrawalsRootDiff(GotExpectedBoxed<B256>),
181
182    /// Error when the requests hash in the block is different from the expected requests
183    /// hash.
184    #[error("mismatched block requests hash: {0}")]
185    BodyRequestsHashDiff(GotExpectedBoxed<B256>),
186
187    /// Error when a block with a specific hash and number is already known.
188    #[error("block with [hash={hash}, number={number}] is already known")]
189    BlockKnown {
190        /// The hash of the known block.
191        hash: BlockHash,
192        /// The block number of the known block.
193        number: BlockNumber,
194    },
195
196    /// Error when the parent hash of a block is not known.
197    #[error("block parent [hash={hash}] is not known")]
198    ParentUnknown {
199        /// The hash of the unknown parent block.
200        hash: BlockHash,
201    },
202
203    /// Error when the block number does not match the parent block number.
204    #[error(
205        "block number {block_number} does not match parent block number {parent_block_number}"
206    )]
207    ParentBlockNumberMismatch {
208        /// The parent block number.
209        parent_block_number: BlockNumber,
210        /// The block number.
211        block_number: BlockNumber,
212    },
213
214    /// Error when the parent hash does not match the expected parent hash.
215    #[error("mismatched parent hash: {0}")]
216    ParentHashMismatch(GotExpectedBoxed<B256>),
217
218    /// Error when the block timestamp is in the future compared to our clock time.
219    #[error(
220        "block timestamp {timestamp} is in the future compared to our clock time {present_timestamp}"
221    )]
222    TimestampIsInFuture {
223        /// The block's timestamp.
224        timestamp: u64,
225        /// The current timestamp.
226        present_timestamp: u64,
227    },
228
229    /// Error when the base fee is missing.
230    #[error("base fee missing")]
231    BaseFeeMissing,
232
233    /// Error when there is a transaction signer recovery error.
234    #[error("transaction signer recovery error")]
235    TransactionSignerRecoveryError,
236
237    /// Error when the extra data length exceeds the maximum allowed.
238    #[error("extra data {len} exceeds max length")]
239    ExtraDataExceedsMax {
240        /// The length of the extra data.
241        len: usize,
242    },
243
244    /// Error when the difficulty after a merge is not zero.
245    #[error("difficulty after merge is not zero")]
246    TheMergeDifficultyIsNotZero,
247
248    /// Error when the nonce after a merge is not zero.
249    #[error("nonce after merge is not zero")]
250    TheMergeNonceIsNotZero,
251
252    /// Error when the ommer root after a merge is not empty.
253    #[error("ommer root after merge is not empty")]
254    TheMergeOmmerRootIsNotEmpty,
255
256    /// Error when the withdrawals root is missing.
257    #[error("missing withdrawals root")]
258    WithdrawalsRootMissing,
259
260    /// Error when the requests hash is missing.
261    #[error("missing requests hash")]
262    RequestsHashMissing,
263
264    /// Error when an unexpected withdrawals root is encountered.
265    #[error("unexpected withdrawals root")]
266    WithdrawalsRootUnexpected,
267
268    /// Error when an unexpected requests hash is encountered.
269    #[error("unexpected requests hash")]
270    RequestsHashUnexpected,
271
272    /// Error when withdrawals are missing.
273    #[error("missing withdrawals")]
274    BodyWithdrawalsMissing,
275
276    /// Error when requests are missing.
277    #[error("missing requests")]
278    BodyRequestsMissing,
279
280    /// Error when blob gas used is missing.
281    #[error("missing blob gas used")]
282    BlobGasUsedMissing,
283
284    /// Error when unexpected blob gas used is encountered.
285    #[error("unexpected blob gas used")]
286    BlobGasUsedUnexpected,
287
288    /// Error when excess blob gas is missing.
289    #[error("missing excess blob gas")]
290    ExcessBlobGasMissing,
291
292    /// Error when unexpected excess blob gas is encountered.
293    #[error("unexpected excess blob gas")]
294    ExcessBlobGasUnexpected,
295
296    /// Error when the parent beacon block root is missing.
297    #[error("missing parent beacon block root")]
298    ParentBeaconBlockRootMissing,
299
300    /// Error when an unexpected parent beacon block root is encountered.
301    #[error("unexpected parent beacon block root")]
302    ParentBeaconBlockRootUnexpected,
303
304    /// Error when blob gas used exceeds the maximum allowed.
305    #[error("blob gas used {blob_gas_used} exceeds maximum allowance {max_blob_gas_per_block}")]
306    BlobGasUsedExceedsMaxBlobGasPerBlock {
307        /// The actual blob gas used.
308        blob_gas_used: u64,
309        /// The maximum allowed blob gas per block.
310        max_blob_gas_per_block: u64,
311    },
312
313    /// Error when blob gas used is not a multiple of blob gas per blob.
314    #[error(
315        "blob gas used {blob_gas_used} is not a multiple of blob gas per blob {blob_gas_per_blob}"
316    )]
317    BlobGasUsedNotMultipleOfBlobGasPerBlob {
318        /// The actual blob gas used.
319        blob_gas_used: u64,
320        /// The blob gas per blob.
321        blob_gas_per_blob: u64,
322    },
323
324    /// Error when the blob gas used in the header does not match the expected blob gas used.
325    #[error("blob gas used mismatch: {0}")]
326    BlobGasUsedDiff(GotExpected<u64>),
327
328    /// Error for a transaction that violates consensus.
329    #[error(transparent)]
330    InvalidTransaction(InvalidTransactionError),
331
332    /// Error when the block's base fee is different from the expected base fee.
333    #[error("block base fee mismatch: {0}")]
334    BaseFeeDiff(GotExpected<u64>),
335
336    /// Error when there is an invalid excess blob gas.
337    #[error(
338        "invalid excess blob gas: {diff}; \
339            parent excess blob gas: {parent_excess_blob_gas}, \
340            parent blob gas used: {parent_blob_gas_used}"
341    )]
342    ExcessBlobGasDiff {
343        /// The excess blob gas diff.
344        diff: GotExpected<u64>,
345        /// The parent excess blob gas.
346        parent_excess_blob_gas: u64,
347        /// The parent blob gas used.
348        parent_blob_gas_used: u64,
349    },
350
351    /// Error when the child gas limit exceeds the maximum allowed increase.
352    #[error("child gas_limit {child_gas_limit} max increase is {parent_gas_limit}/1024")]
353    GasLimitInvalidIncrease {
354        /// The parent gas limit.
355        parent_gas_limit: u64,
356        /// The child gas limit.
357        child_gas_limit: u64,
358    },
359
360    /// Error indicating that the child gas limit is below the minimum allowed limit.
361    ///
362    /// This error occurs when the child gas limit is less than the specified minimum gas limit.
363    #[error(
364        "child gas limit {child_gas_limit} is below the minimum allowed limit ({MINIMUM_GAS_LIMIT})"
365    )]
366    GasLimitInvalidMinimum {
367        /// The child gas limit.
368        child_gas_limit: u64,
369    },
370
371    /// Error indicating that the block gas limit is above the allowed maximum.
372    ///
373    /// This error occurs when the gas limit is more than the specified maximum gas limit.
374    #[error("child gas limit {block_gas_limit} is above the maximum allowed limit ({MAXIMUM_GAS_LIMIT_BLOCK})")]
375    GasLimitInvalidBlockMaximum {
376        /// block gas limit.
377        block_gas_limit: u64,
378    },
379
380    /// Error when the child gas limit exceeds the maximum allowed decrease.
381    #[error("child gas_limit {child_gas_limit} max decrease is {parent_gas_limit}/1024")]
382    GasLimitInvalidDecrease {
383        /// The parent gas limit.
384        parent_gas_limit: u64,
385        /// The child gas limit.
386        child_gas_limit: u64,
387    },
388
389    /// Error when the block timestamp is in the past compared to the parent timestamp.
390    #[error(
391        "block timestamp {timestamp} is in the past compared to the parent timestamp {parent_timestamp}"
392    )]
393    TimestampIsInPast {
394        /// The parent block's timestamp.
395        parent_timestamp: u64,
396        /// The block's timestamp.
397        timestamp: u64,
398    },
399    /// Error when the block is too large.
400    #[error("block is too large: {rlp_length} > {max_rlp_length}")]
401    BlockTooLarge {
402        /// The actual RLP length of the block.
403        rlp_length: usize,
404        /// The maximum allowed RLP length.
405        max_rlp_length: usize,
406    },
407    /// EIP-7825: Transaction gas limit exceeds maximum allowed
408    #[error(transparent)]
409    TransactionGasLimitTooHigh(Box<TxGasLimitTooHighErr>),
410    /// Other, likely an injected L2 error.
411    #[error("{0}")]
412    Other(String),
413}
414
415impl ConsensusError {
416    /// Returns `true` if the error is a state root error.
417    pub const fn is_state_root_error(&self) -> bool {
418        matches!(self, Self::BodyStateRootDiff(_))
419    }
420}
421
422impl From<InvalidTransactionError> for ConsensusError {
423    fn from(value: InvalidTransactionError) -> Self {
424        Self::InvalidTransaction(value)
425    }
426}
427
428impl From<TxGasLimitTooHighErr> for ConsensusError {
429    fn from(value: TxGasLimitTooHighErr) -> Self {
430        Self::TransactionGasLimitTooHigh(Box::new(value))
431    }
432}
433
434/// `HeaderConsensusError` combines a `ConsensusError` with the `SealedHeader` it relates to.
435#[derive(thiserror::Error, Debug)]
436#[error("Consensus error: {0}, Invalid header: {1:?}")]
437pub struct HeaderConsensusError<H>(ConsensusError, SealedHeader<H>);
438
439/// EIP-7825: Transaction gas limit exceeds maximum allowed
440#[derive(thiserror::Error, Debug, Eq, PartialEq, Clone)]
441#[error("transaction gas limit ({gas_limit}) is greater than the cap ({max_allowed})")]
442pub struct TxGasLimitTooHighErr {
443    /// Hash of the transaction that violates the rule
444    pub tx_hash: B256,
445    /// The gas limit of the transaction
446    pub gas_limit: u64,
447    /// The maximum allowed gas limit
448    pub max_allowed: u64,
449}