1#![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, 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
25pub mod noop;
27
28#[cfg(any(test, feature = "test-utils"))]
29pub mod test_utils;
31
32#[auto_impl::auto_impl(&, Arc)]
35pub trait FullConsensus<N: NodePrimitives>: Consensus<N::Block> {
36 fn validate_block_post_execution(
43 &self,
44 block: &RecoveredBlock<N::Block>,
45 result: &BlockExecutionResult<N::Receipt>,
46 ) -> Result<(), ConsensusError>;
47}
48
49#[auto_impl::auto_impl(&, Arc)]
51pub trait Consensus<B: Block>: HeaderValidator<B::Header> {
52 type Error;
54
55 fn validate_body_against_header(
57 &self,
58 body: &B::Body,
59 header: &SealedHeader<B::Header>,
60 ) -> Result<(), Self::Error>;
61
62 fn validate_block_pre_execution(&self, block: &SealedBlock<B>) -> Result<(), Self::Error>;
72}
73
74#[auto_impl::auto_impl(&, Arc)]
76pub trait HeaderValidator<H = Header>: Debug + Send + Sync {
77 fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError>;
81
82 fn validate_header_against_parent(
93 &self,
94 header: &SealedHeader<H>,
95 parent: &SealedHeader<H>,
96 ) -> Result<(), ConsensusError>;
97
98 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
126#[derive(Debug, PartialEq, Eq, Clone, thiserror::Error)]
128pub enum ConsensusError {
129 #[error("block used gas ({gas_used}) is greater than gas limit ({gas_limit})")]
131 HeaderGasUsedExceedsGasLimit {
132 gas_used: u64,
134 gas_limit: u64,
136 },
137 #[error(
139 "header gas limit ({gas_limit}) exceed the maximum allowed gas limit ({MAXIMUM_GAS_LIMIT_BLOCK})"
140 )]
141 HeaderGasLimitExceedsMax {
142 gas_limit: u64,
144 },
145
146 #[error("block gas used mismatch: {gas}; gas spent by each transaction: {gas_spent_by_tx:?}")]
148 BlockGasUsed {
149 gas: GotExpected<u64>,
151 gas_spent_by_tx: Vec<(u64, u64)>,
153 },
154
155 #[error("mismatched block ommer hash: {0}")]
157 BodyOmmersHashDiff(GotExpectedBoxed<B256>),
158
159 #[error("mismatched block state root: {0}")]
161 BodyStateRootDiff(GotExpectedBoxed<B256>),
162
163 #[error("mismatched block transaction root: {0}")]
166 BodyTransactionRootDiff(GotExpectedBoxed<B256>),
167
168 #[error("receipt root mismatch: {0}")]
170 BodyReceiptRootDiff(GotExpectedBoxed<B256>),
171
172 #[error("header bloom filter mismatch: {0}")]
174 BodyBloomLogDiff(GotExpectedBoxed<Bloom>),
175
176 #[error("mismatched block withdrawals root: {0}")]
179 BodyWithdrawalsRootDiff(GotExpectedBoxed<B256>),
180
181 #[error("mismatched block requests hash: {0}")]
184 BodyRequestsHashDiff(GotExpectedBoxed<B256>),
185
186 #[error("block with [hash={hash}, number={number}] is already known")]
188 BlockKnown {
189 hash: BlockHash,
191 number: BlockNumber,
193 },
194
195 #[error("block parent [hash={hash}] is not known")]
197 ParentUnknown {
198 hash: BlockHash,
200 },
201
202 #[error(
204 "block number {block_number} does not match parent block number {parent_block_number}"
205 )]
206 ParentBlockNumberMismatch {
207 parent_block_number: BlockNumber,
209 block_number: BlockNumber,
211 },
212
213 #[error("mismatched parent hash: {0}")]
215 ParentHashMismatch(GotExpectedBoxed<B256>),
216
217 #[error(
219 "block timestamp {timestamp} is in the future compared to our clock time {present_timestamp}"
220 )]
221 TimestampIsInFuture {
222 timestamp: u64,
224 present_timestamp: u64,
226 },
227
228 #[error("base fee missing")]
230 BaseFeeMissing,
231
232 #[error("transaction signer recovery error")]
234 TransactionSignerRecoveryError,
235
236 #[error("extra data {len} exceeds max length")]
238 ExtraDataExceedsMax {
239 len: usize,
241 },
242
243 #[error("difficulty after merge is not zero")]
245 TheMergeDifficultyIsNotZero,
246
247 #[error("nonce after merge is not zero")]
249 TheMergeNonceIsNotZero,
250
251 #[error("ommer root after merge is not empty")]
253 TheMergeOmmerRootIsNotEmpty,
254
255 #[error("missing withdrawals root")]
257 WithdrawalsRootMissing,
258
259 #[error("missing requests hash")]
261 RequestsHashMissing,
262
263 #[error("unexpected withdrawals root")]
265 WithdrawalsRootUnexpected,
266
267 #[error("unexpected requests hash")]
269 RequestsHashUnexpected,
270
271 #[error("missing withdrawals")]
273 BodyWithdrawalsMissing,
274
275 #[error("missing requests")]
277 BodyRequestsMissing,
278
279 #[error("missing blob gas used")]
281 BlobGasUsedMissing,
282
283 #[error("unexpected blob gas used")]
285 BlobGasUsedUnexpected,
286
287 #[error("missing excess blob gas")]
289 ExcessBlobGasMissing,
290
291 #[error("unexpected excess blob gas")]
293 ExcessBlobGasUnexpected,
294
295 #[error("missing parent beacon block root")]
297 ParentBeaconBlockRootMissing,
298
299 #[error("unexpected parent beacon block root")]
301 ParentBeaconBlockRootUnexpected,
302
303 #[error("blob gas used {blob_gas_used} exceeds maximum allowance {max_blob_gas_per_block}")]
305 BlobGasUsedExceedsMaxBlobGasPerBlock {
306 blob_gas_used: u64,
308 max_blob_gas_per_block: u64,
310 },
311
312 #[error(
314 "blob gas used {blob_gas_used} is not a multiple of blob gas per blob {blob_gas_per_blob}"
315 )]
316 BlobGasUsedNotMultipleOfBlobGasPerBlob {
317 blob_gas_used: u64,
319 blob_gas_per_blob: u64,
321 },
322
323 #[error("blob gas used mismatch: {0}")]
325 BlobGasUsedDiff(GotExpected<u64>),
326
327 #[error(transparent)]
329 InvalidTransaction(InvalidTransactionError),
330
331 #[error("block base fee mismatch: {0}")]
333 BaseFeeDiff(GotExpected<u64>),
334
335 #[error(
337 "invalid excess blob gas: {diff}; \
338 parent excess blob gas: {parent_excess_blob_gas}, \
339 parent blob gas used: {parent_blob_gas_used}"
340 )]
341 ExcessBlobGasDiff {
342 diff: GotExpected<u64>,
344 parent_excess_blob_gas: u64,
346 parent_blob_gas_used: u64,
348 },
349
350 #[error("child gas_limit {child_gas_limit} max increase is {parent_gas_limit}/1024")]
352 GasLimitInvalidIncrease {
353 parent_gas_limit: u64,
355 child_gas_limit: u64,
357 },
358
359 #[error(
363 "child gas limit {child_gas_limit} is below the minimum allowed limit ({MINIMUM_GAS_LIMIT})"
364 )]
365 GasLimitInvalidMinimum {
366 child_gas_limit: u64,
368 },
369
370 #[error("child gas limit {block_gas_limit} is above the maximum allowed limit ({MAXIMUM_GAS_LIMIT_BLOCK})")]
374 GasLimitInvalidBlockMaximum {
375 block_gas_limit: u64,
377 },
378
379 #[error("child gas_limit {child_gas_limit} max decrease is {parent_gas_limit}/1024")]
381 GasLimitInvalidDecrease {
382 parent_gas_limit: u64,
384 child_gas_limit: u64,
386 },
387
388 #[error(
390 "block timestamp {timestamp} is in the past compared to the parent timestamp {parent_timestamp}"
391 )]
392 TimestampIsInPast {
393 parent_timestamp: u64,
395 timestamp: u64,
397 },
398 #[error("block is too large: {rlp_length} > {max_rlp_length}")]
400 BlockTooLarge {
401 rlp_length: usize,
403 max_rlp_length: usize,
405 },
406 #[error("{0}")]
408 Other(String),
409}
410
411impl ConsensusError {
412 pub const fn is_state_root_error(&self) -> bool {
414 matches!(self, Self::BodyStateRootDiff(_))
415 }
416}
417
418impl From<InvalidTransactionError> for ConsensusError {
419 fn from(value: InvalidTransactionError) -> Self {
420 Self::InvalidTransaction(value)
421 }
422}
423
424#[derive(thiserror::Error, Debug)]
426#[error("Consensus error: {0}, Invalid header: {1:?}")]
427pub struct HeaderConsensusError<H>(ConsensusError, SealedHeader<H>);