Skip to main content

reth_payload_primitives/
error.rs

1//! Error types for payload operations.
2
3use alloc::{boxed::Box, string::ToString};
4use alloy_primitives::B256;
5use alloy_rpc_types_engine::{ForkchoiceUpdateError, PayloadError, PayloadStatusEnum};
6use core::error;
7use reth_errors::{BlockExecutionError, ProviderError, RethError};
8use tokio::sync::{mpsc, oneshot};
9
10/// Possible error variants during payload building.
11#[derive(Debug, thiserror::Error)]
12pub enum PayloadBuilderError {
13    /// Thrown when the parent header cannot be found
14    #[error("missing parent header: {0}")]
15    MissingParentHeader(B256),
16    /// Thrown when the parent block is missing.
17    #[error("missing parent block {0}")]
18    MissingParentBlock(B256),
19    /// An oneshot channels has been closed.
20    #[error("sender has been dropped")]
21    ChannelClosed,
22    /// If there's no payload to resolve.
23    #[error("missing payload")]
24    MissingPayload,
25    /// Other internal error
26    #[error(transparent)]
27    Internal(#[from] RethError),
28    /// Unrecoverable error during evm execution.
29    #[error("evm execution error: {0}")]
30    EvmExecutionError(Box<dyn core::error::Error + Send + Sync>),
31    /// Any other payload building errors.
32    #[error(transparent)]
33    Other(Box<dyn core::error::Error + Send + Sync>),
34}
35
36impl PayloadBuilderError {
37    /// Create a new EVM error from a boxed error.
38    pub fn evm<E>(error: E) -> Self
39    where
40        E: core::error::Error + Send + Sync + 'static,
41    {
42        Self::EvmExecutionError(Box::new(error))
43    }
44
45    /// Create a new error from a boxed error.
46    pub fn other<E>(error: E) -> Self
47    where
48        E: core::error::Error + Send + Sync + 'static,
49    {
50        Self::Other(Box::new(error))
51    }
52}
53
54impl From<ProviderError> for PayloadBuilderError {
55    fn from(error: ProviderError) -> Self {
56        Self::Internal(RethError::Provider(error))
57    }
58}
59
60impl From<oneshot::error::RecvError> for PayloadBuilderError {
61    fn from(_: oneshot::error::RecvError) -> Self {
62        Self::ChannelClosed
63    }
64}
65
66impl From<BlockExecutionError> for PayloadBuilderError {
67    fn from(error: BlockExecutionError) -> Self {
68        Self::evm(error)
69    }
70}
71
72impl<T> From<mpsc::error::SendError<T>> for PayloadBuilderError {
73    fn from(_: mpsc::error::SendError<T>) -> Self {
74        Self::ChannelClosed
75    }
76}
77
78/// Thrown when the payload or attributes are known to be invalid __before__ processing.
79///
80/// This is used mainly for
81/// [`validate_version_specific_fields`](crate::validate_version_specific_fields), which validates
82/// both execution payloads and forkchoice update attributes with respect to a method version.
83#[derive(thiserror::Error, Debug)]
84pub enum EngineObjectValidationError {
85    /// Thrown when the underlying validation error occurred while validating an
86    /// `ExecutionPayload`.
87    #[error("Payload validation error: {0}")]
88    Payload(VersionSpecificValidationError),
89
90    /// Thrown when the underlying validation error occurred while validating a
91    /// `PayloadAttributes`.
92    #[error("Payload attributes validation error: {0}")]
93    PayloadAttributes(VersionSpecificValidationError),
94
95    /// Thrown if `PayloadAttributes` or `ExecutionPayload` were provided with a timestamp, but the
96    /// version of the engine method called is meant for a fork that occurs after the provided
97    /// timestamp.
98    #[error("Unsupported fork")]
99    UnsupportedFork,
100    /// Another type of error that is not covered by the above variants.
101    #[error("Invalid params: {0}")]
102    InvalidParams(#[from] Box<dyn core::error::Error + Send + Sync>),
103}
104
105/// Thrown when validating an execution payload OR payload attributes fails due to:
106/// * The existence of a new field that is not supported in the given engine method version, or
107/// * The absence of a field that is required in the given engine method version
108#[derive(thiserror::Error, Debug)]
109pub enum VersionSpecificValidationError {
110    /// Thrown if the pre-V3 `PayloadAttributes` or `ExecutionPayload` contains a parent beacon
111    /// block root
112    #[error("parent beacon block root not supported before V3")]
113    ParentBeaconBlockRootNotSupportedBeforeV3,
114    /// Thrown if `engine_forkchoiceUpdatedV1` or `engine_newPayloadV1` contains withdrawals
115    #[error("withdrawals not supported in V1")]
116    WithdrawalsNotSupportedInV1,
117    /// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains no withdrawals after
118    /// Shanghai
119    #[error("no withdrawals post-Shanghai")]
120    NoWithdrawalsPostShanghai,
121    /// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains withdrawals before
122    /// Shanghai
123    #[error("withdrawals pre-Shanghai")]
124    HasWithdrawalsPreShanghai,
125    /// Thrown if the `PayloadAttributes` or `ExecutionPayload` contains no parent beacon block
126    /// root after Cancun
127    #[error("no parent beacon block root post-cancun")]
128    NoParentBeaconBlockRootPostCancun,
129    /// Thrown if the pre-V6 `PayloadAttributes` or `ExecutionPayload` contains a block access list
130    #[error("block access list not before V6")]
131    BlockAccessListNotSupportedBeforeV6,
132    /// Thrown if  `engine_newPayload` contains no block access list
133    /// after Amsterdam
134    #[error("no block access list post-Amsterdam")]
135    NoBlockAccessListPostAmsterdam,
136    /// Thrown if  `engine_newPayload` contains block access list
137    /// before Amsterdam
138    #[error("block access list pre-Amsterdam")]
139    HasBlockAccessListPreAmsterdam,
140    /// Thrown if the pre-V6 `PayloadAttributes` or `ExecutionPayload` contains a slot number
141    #[error("slot number not before V6")]
142    SlotNumberNotSupportedBeforeV6,
143    /// Thrown if  `engine_newPayload` contains no slot number
144    /// after Amsterdam
145    #[error("no slot number post-Amsterdam")]
146    NoSlotNumberPostAmsterdam,
147    /// Thrown if  `engine_newPayload` contains slot number
148    /// before Amsterdam
149    #[error("slot number pre-Amsterdam")]
150    HasSlotNumberPreAmsterdam,
151}
152
153/// Error validating payload received over `newPayload` API.
154#[derive(thiserror::Error, Debug)]
155pub enum NewPayloadError {
156    /// Payload validation error.
157    #[error(transparent)]
158    Eth(#[from] PayloadError),
159    /// Custom payload validation error.
160    #[error(transparent)]
161    Other(Box<dyn error::Error + Send + Sync>),
162}
163
164impl NewPayloadError {
165    /// Creates instance of variant [`NewPayloadError::Other`].
166    #[inline]
167    pub fn other(err: impl error::Error + Send + Sync + 'static) -> Self {
168        Self::Other(Box::new(err))
169    }
170
171    /// Returns `true` if the error is caused by a block hash mismatch.
172    #[inline]
173    pub const fn is_block_hash_mismatch(&self) -> bool {
174        matches!(self, Self::Eth(PayloadError::BlockHash { .. }))
175    }
176
177    /// Returns `true` if the error is caused by invalid block hashes (Cancun).
178    #[inline]
179    pub const fn is_invalid_versioned_hashes(&self) -> bool {
180        matches!(self, Self::Eth(PayloadError::InvalidVersionedHashes))
181    }
182}
183
184impl From<NewPayloadError> for PayloadStatusEnum {
185    fn from(error: NewPayloadError) -> Self {
186        Self::Invalid { validation_error: error.to_string() }
187    }
188}
189
190impl EngineObjectValidationError {
191    /// Creates an instance of the `InvalidParams` variant with the given error.
192    pub fn invalid_params<E>(error: E) -> Self
193    where
194        E: core::error::Error + Send + Sync + 'static,
195    {
196        Self::InvalidParams(Box::new(error))
197    }
198}
199
200/// Thrown when validating the correctness of a payloadattributes object.
201#[derive(thiserror::Error, Debug)]
202pub enum InvalidPayloadAttributesError {
203    /// Thrown if the timestamp of the payload attributes is invalid according to the engine specs.
204    #[error("invalid timestamp")]
205    InvalidTimestamp,
206    /// Another type of error that is not covered by the above variants.
207    #[error("Invalid params: {0}")]
208    InvalidParams(#[from] Box<dyn core::error::Error + Send + Sync>),
209}
210
211impl From<InvalidPayloadAttributesError> for ForkchoiceUpdateError {
212    fn from(_: InvalidPayloadAttributesError) -> Self {
213        Self::UpdatedInvalidPayloadAttributes
214    }
215}