reth_payload_primitives/
error.rs

1//! Error types emitted by types or implementations of this crate.
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::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
72/// Thrown when the payload or attributes are known to be invalid __before__ processing.
73///
74/// This is used mainly for
75/// [`validate_version_specific_fields`](crate::validate_version_specific_fields), which validates
76/// both execution payloads and forkchoice update attributes with respect to a method version.
77#[derive(thiserror::Error, Debug)]
78pub enum EngineObjectValidationError {
79    /// Thrown when the underlying validation error occurred while validating an
80    /// `ExecutionPayload`.
81    #[error("Payload validation error: {0}")]
82    Payload(VersionSpecificValidationError),
83
84    /// Thrown when the underlying validation error occurred while validating a
85    /// `PayloadAttributes`.
86    #[error("Payload attributes validation error: {0}")]
87    PayloadAttributes(VersionSpecificValidationError),
88
89    /// Thrown if `PayloadAttributes` or `ExecutionPayload` were provided with a timestamp, but the
90    /// version of the engine method called is meant for a fork that occurs after the provided
91    /// timestamp.
92    #[error("Unsupported fork")]
93    UnsupportedFork,
94    /// Another type of error that is not covered by the above variants.
95    #[error("Invalid params: {0}")]
96    InvalidParams(#[from] Box<dyn core::error::Error + Send + Sync>),
97}
98
99/// Thrown when validating an execution payload OR payload attributes fails due to:
100/// * The existence of a new field that is not supported in the given engine method version, or
101/// * The absence of a field that is required in the given engine method version
102#[derive(thiserror::Error, Debug)]
103pub enum VersionSpecificValidationError {
104    /// Thrown if the pre-V3 `PayloadAttributes` or `ExecutionPayload` contains a parent beacon
105    /// block root
106    #[error("parent beacon block root not supported before V3")]
107    ParentBeaconBlockRootNotSupportedBeforeV3,
108    /// Thrown if `engine_forkchoiceUpdatedV1` or `engine_newPayloadV1` contains withdrawals
109    #[error("withdrawals not supported in V1")]
110    WithdrawalsNotSupportedInV1,
111    /// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains no withdrawals after
112    /// Shanghai
113    #[error("no withdrawals post-Shanghai")]
114    NoWithdrawalsPostShanghai,
115    /// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains withdrawals before
116    /// Shanghai
117    #[error("withdrawals pre-Shanghai")]
118    HasWithdrawalsPreShanghai,
119    /// Thrown if the `PayloadAttributes` or `ExecutionPayload` contains no parent beacon block
120    /// root after Cancun
121    #[error("no parent beacon block root post-cancun")]
122    NoParentBeaconBlockRootPostCancun,
123}
124
125/// Error validating payload received over `newPayload` API.
126#[derive(thiserror::Error, Debug)]
127pub enum NewPayloadError {
128    /// Payload validation error.
129    #[error(transparent)]
130    Eth(#[from] PayloadError),
131    /// Custom payload validation error.
132    #[error(transparent)]
133    Other(Box<dyn error::Error + Send + Sync>),
134}
135
136impl NewPayloadError {
137    /// Creates instance of variant [`NewPayloadError::Other`].
138    #[inline]
139    pub fn other(err: impl error::Error + Send + Sync + 'static) -> Self {
140        Self::Other(Box::new(err))
141    }
142}
143
144impl NewPayloadError {
145    /// Returns `true` if the error is caused by a block hash mismatch.
146    #[inline]
147    pub const fn is_block_hash_mismatch(&self) -> bool {
148        matches!(self, Self::Eth(PayloadError::BlockHash { .. }))
149    }
150
151    /// Returns `true` if the error is caused by invalid block hashes (Cancun).
152    #[inline]
153    pub const fn is_invalid_versioned_hashes(&self) -> bool {
154        matches!(self, Self::Eth(PayloadError::InvalidVersionedHashes))
155    }
156}
157
158impl From<NewPayloadError> for PayloadStatusEnum {
159    fn from(error: NewPayloadError) -> Self {
160        Self::Invalid { validation_error: error.to_string() }
161    }
162}
163
164impl EngineObjectValidationError {
165    /// Creates an instance of the `InvalidParams` variant with the given error.
166    pub fn invalid_params<E>(error: E) -> Self
167    where
168        E: core::error::Error + Send + Sync + 'static,
169    {
170        Self::InvalidParams(Box::new(error))
171    }
172}
173
174/// Thrown when validating the correctness of a payloadattributes object.
175#[derive(thiserror::Error, Debug)]
176pub enum InvalidPayloadAttributesError {
177    /// Thrown if the timestamp of the payload attributes is invalid according to the engine specs.
178    #[error("parent beacon block root not supported before V3")]
179    InvalidTimestamp,
180    /// Another type of error that is not covered by the above variants.
181    #[error("Invalid params: {0}")]
182    InvalidParams(#[from] Box<dyn core::error::Error + Send + Sync>),
183}
184
185impl From<InvalidPayloadAttributesError> for ForkchoiceUpdateError {
186    fn from(_: InvalidPayloadAttributesError) -> Self {
187        Self::UpdatedInvalidPayloadAttributes
188    }
189}