reth_engine_primitives/lib.rs
1//! Traits, validation methods, and helper types used to abstract over engine types.
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 alloy_consensus::BlockHeader;
15use reth_errors::ConsensusError;
16use reth_payload_primitives::{
17 EngineApiMessageVersion, EngineObjectValidationError, InvalidPayloadAttributesError,
18 NewPayloadError, PayloadAttributes, PayloadOrAttributes, PayloadTypes,
19};
20use reth_primitives_traits::{Block, RecoveredBlock, SealedBlock};
21use reth_trie_common::HashedPostState;
22use serde::{de::DeserializeOwned, Serialize};
23
24// Re-export [`ExecutionPayload`] moved to `reth_payload_primitives`
25#[cfg(feature = "std")]
26pub use reth_evm::{ConfigureEngineEvm, ConvertTx, ExecutableTxIterator, ExecutableTxTuple};
27pub use reth_payload_primitives::ExecutionPayload;
28
29mod error;
30pub use error::*;
31
32mod forkchoice;
33pub use forkchoice::{ForkchoiceStateHash, ForkchoiceStateTracker, ForkchoiceStatus};
34
35#[cfg(feature = "std")]
36mod message;
37#[cfg(feature = "std")]
38pub use message::*;
39
40mod event;
41pub use event::*;
42
43mod invalid_block_hook;
44pub use invalid_block_hook::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook};
45
46pub mod config;
47pub use config::*;
48
49/// This type defines the versioned types of the engine API based on the [ethereum engine API](https://github.com/ethereum/execution-apis/tree/main/src/engine).
50///
51/// This includes the execution payload types and payload attributes that are used to trigger a
52/// payload job. Hence this trait is also [`PayloadTypes`].
53///
54/// Implementations of this type are intended to be stateless and just define the types as
55/// associated types.
56/// This type is intended for non-ethereum chains that closely mirror the ethereum engine API spec,
57/// but may have different payload, for example opstack, but structurally equivalent otherwise (same
58/// engine API RPC endpoints for example).
59pub trait EngineTypes:
60 PayloadTypes<
61 BuiltPayload: TryInto<Self::ExecutionPayloadEnvelopeV1>
62 + TryInto<Self::ExecutionPayloadEnvelopeV2>
63 + TryInto<Self::ExecutionPayloadEnvelopeV3>
64 + TryInto<Self::ExecutionPayloadEnvelopeV4>
65 + TryInto<Self::ExecutionPayloadEnvelopeV5>
66 + TryInto<Self::ExecutionPayloadEnvelopeV6>,
67 > + DeserializeOwned
68 + Serialize
69{
70 /// Execution Payload V1 envelope type.
71 type ExecutionPayloadEnvelopeV1: DeserializeOwned
72 + Serialize
73 + Clone
74 + Unpin
75 + Send
76 + Sync
77 + 'static;
78 /// Execution Payload V2 envelope type.
79 type ExecutionPayloadEnvelopeV2: DeserializeOwned
80 + Serialize
81 + Clone
82 + Unpin
83 + Send
84 + Sync
85 + 'static;
86 /// Execution Payload V3 envelope type.
87 type ExecutionPayloadEnvelopeV3: DeserializeOwned
88 + Serialize
89 + Clone
90 + Unpin
91 + Send
92 + Sync
93 + 'static;
94 /// Execution Payload V4 envelope type.
95 type ExecutionPayloadEnvelopeV4: DeserializeOwned
96 + Serialize
97 + Clone
98 + Unpin
99 + Send
100 + Sync
101 + 'static;
102 /// Execution Payload V5 envelope type.
103 type ExecutionPayloadEnvelopeV5: DeserializeOwned
104 + Serialize
105 + Clone
106 + Unpin
107 + Send
108 + Sync
109 + 'static;
110 /// Execution Payload V6 envelope type.
111 type ExecutionPayloadEnvelopeV6: DeserializeOwned
112 + Serialize
113 + Clone
114 + Unpin
115 + Send
116 + Sync
117 + 'static;
118}
119
120/// Validates engine API requests at the RPC layer, before payloads and attributes
121/// are forwarded to the engine for processing.
122///
123/// - [`validate_version_specific_fields`](Self::validate_version_specific_fields): Enforced in each
124/// `engine_newPayloadVN` RPC handler to verify the payload contains the correct fields for the
125/// engine API version (e.g., blob fields in V3+, requests in V4+).
126///
127/// - [`ensure_well_formed_attributes`](Self::ensure_well_formed_attributes): Enforced in
128/// `engine_forkchoiceUpdatedVN` RPC handlers to validate payload attributes are well-formed for
129/// the given version before forwarding to the engine.
130///
131/// After this validation passes, the engine performs the full consensus validation
132/// pipeline (header, pre-execution, execution, post-execution).
133pub trait EngineApiValidator<Types: PayloadTypes>: Send + Sync + Unpin + 'static {
134 /// Validates the presence or exclusion of fork-specific fields based on the payload attributes
135 /// and the message version.
136 fn validate_version_specific_fields(
137 &self,
138 version: EngineApiMessageVersion,
139 payload_or_attrs: PayloadOrAttributes<'_, Types::ExecutionData, Types::PayloadAttributes>,
140 ) -> Result<(), EngineObjectValidationError>;
141
142 /// Ensures that the payload attributes are valid for the given [`EngineApiMessageVersion`].
143 fn ensure_well_formed_attributes(
144 &self,
145 version: EngineApiMessageVersion,
146 attributes: &Types::PayloadAttributes,
147 ) -> Result<(), EngineObjectValidationError>;
148}
149
150/// Type that validates an [`ExecutionPayload`].
151///
152/// This trait handles validation at the engine API boundary — converting payloads
153/// into blocks and validating payload attributes for block building.
154///
155/// # Methods and when they're used
156///
157/// - [`convert_payload_to_block`](Self::convert_payload_to_block): Used during `engine_newPayload`
158/// processing to decode the payload into a [`SealedBlock`]. Also used to validate payload
159/// structure during backfill buffering. In the engine tree, this runs concurrently with state
160/// setup on a background thread.
161///
162/// - [`ensure_well_formed_payload`](Self::ensure_well_formed_payload): Converts payload and
163/// recovers transaction signatures. Used when recovered senders are needed immediately.
164///
165/// - [`validate_payload_attributes_against_header`](Self::validate_payload_attributes_against_header):
166/// Enforced as part of the engine's `forkchoiceUpdated` handling when payload attributes
167/// are provided. Gates whether a payload build job is started.
168///
169/// - [`validate_block_post_execution_with_hashed_state`](Self::validate_block_post_execution_with_hashed_state):
170/// Called after block execution in the engine's payload validation pipeline.
171/// No-op on L1, used by L2s (e.g., OP Stack) for additional post-execution checks.
172///
173/// # Relationship to consensus traits
174///
175/// This trait does NOT replace the consensus traits (`Consensus`, `FullConsensus` from
176/// `reth-consensus`). Those handle the actual consensus rule
177/// validation (header checks, pre/post-execution). This trait handles engine API-specific
178/// concerns: payload encoding/decoding and attribute validation.
179#[auto_impl::auto_impl(&, Arc)]
180pub trait PayloadValidator<Types: PayloadTypes>: Send + Sync + Unpin + 'static {
181 /// The block type used by the engine.
182 type Block: Block;
183
184 /// Converts the given payload into a sealed block without recovering signatures.
185 ///
186 /// This function validates the payload and converts it into a [`SealedBlock`] which contains
187 /// the block hash but does not perform signature recovery on transactions.
188 ///
189 /// This is more efficient than [`Self::ensure_well_formed_payload`] when signature recovery
190 /// is not needed immediately or will be performed later.
191 ///
192 /// Implementers should ensure that the checks are done in the order that conforms with the
193 /// engine-API specification.
194 fn convert_payload_to_block(
195 &self,
196 payload: Types::ExecutionData,
197 ) -> Result<SealedBlock<Self::Block>, NewPayloadError>;
198
199 /// Ensures that the given payload does not violate any consensus rules that concern the block's
200 /// layout.
201 ///
202 /// This function must convert the payload into the executable block and pre-validate its
203 /// fields.
204 ///
205 /// Implementers should ensure that the checks are done in the order that conforms with the
206 /// engine-API specification.
207 fn ensure_well_formed_payload(
208 &self,
209 payload: Types::ExecutionData,
210 ) -> Result<RecoveredBlock<Self::Block>, NewPayloadError> {
211 let sealed_block = self.convert_payload_to_block(payload)?;
212 sealed_block.try_recover().map_err(|e| NewPayloadError::Other(e.into()))
213 }
214
215 /// Verifies payload post-execution w.r.t. hashed state updates.
216 fn validate_block_post_execution_with_hashed_state(
217 &self,
218 _state_updates: &HashedPostState,
219 _block: &RecoveredBlock<Self::Block>,
220 ) -> Result<(), ConsensusError> {
221 // method not used by l1
222 Ok(())
223 }
224
225 /// Validates the payload attributes with respect to the header.
226 ///
227 /// By default, this enforces that the payload attributes timestamp is greater than the
228 /// timestamp according to:
229 /// > 7. Client software MUST ensure that payloadAttributes.timestamp is greater than
230 /// > timestamp
231 /// > of a block referenced by forkchoiceState.headBlockHash.
232 ///
233 /// See also: <https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md#specification-1>
234 ///
235 /// Enforced as part of the engine's `forkchoiceUpdated` handling when the consensus layer
236 /// provides payload attributes. If this returns an error, the forkchoice state update itself
237 /// is NOT rolled back, but no payload build job is started — the response includes
238 /// `INVALID_PAYLOAD_ATTRIBUTES`.
239 fn validate_payload_attributes_against_header(
240 &self,
241 attr: &Types::PayloadAttributes,
242 header: &<Self::Block as Block>::Header,
243 ) -> Result<(), InvalidPayloadAttributesError> {
244 if attr.timestamp() <= header.timestamp() {
245 return Err(InvalidPayloadAttributesError::InvalidTimestamp);
246 }
247 Ok(())
248 }
249}