1use alloy_primitives::B256;
2use alloy_rpc_types_engine::{
3 ForkchoiceUpdateError, INVALID_FORK_CHOICE_STATE_ERROR, INVALID_FORK_CHOICE_STATE_ERROR_MSG,
4 INVALID_PAYLOAD_ATTRIBUTES_ERROR, INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
5};
6use jsonrpsee_types::error::{
7 INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG,
8};
9use reth_engine_primitives::{BeaconForkChoiceUpdateError, BeaconOnNewPayloadError};
10use reth_payload_builder_primitives::PayloadBuilderError;
11use reth_payload_primitives::{EngineObjectValidationError, VersionSpecificValidationError};
12use thiserror::Error;
13
14pub type EngineApiResult<Ok> = Result<Ok, EngineApiError>;
16
17pub const UNSUPPORTED_FORK_CODE: i32 = -38005;
19pub const UNKNOWN_PAYLOAD_CODE: i32 = -38001;
21pub const REQUEST_TOO_LARGE_CODE: i32 = -38004;
23
24const REQUEST_TOO_LARGE_MESSAGE: &str = "Too large request";
26
27#[derive(Error, Debug)]
32pub enum EngineApiError {
33 #[error("Unknown payload")]
36 UnknownPayload,
37 #[error("requested count too large: {len}")]
39 PayloadRequestTooLarge {
40 len: u64,
42 },
43 #[error("requested blob count too large: {len}")]
45 BlobRequestTooLarge {
46 len: usize,
48 },
49 #[error("invalid start ({start}) or count ({count})")]
51 InvalidBodiesRange {
52 start: u64,
54 count: u64,
56 },
57 #[error(
59 "invalid transition terminal block hash: \
60 execution: {execution:?}, consensus: {consensus}"
61 )]
62 TerminalBlockHash {
63 execution: Option<B256>,
65 consensus: B256,
67 },
68 #[error(transparent)]
70 ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError),
71 #[error(transparent)]
73 NewPayload(#[from] BeaconOnNewPayloadError),
74 #[error(transparent)]
76 Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
77 #[error(transparent)]
79 GetPayloadError(#[from] PayloadBuilderError),
80 #[error(transparent)]
82 EngineObjectValidationError(#[from] EngineObjectValidationError),
83 #[error("requests hash cannot be accepted by the API without `--engine.accept-execution-requests-hash` flag")]
85 UnexpectedRequestsHash,
86 #[error("{0}")]
88 Other(jsonrpsee_types::ErrorObject<'static>),
89}
90
91impl EngineApiError {
92 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
94 Self::Other(err)
95 }
96}
97
98#[derive(serde::Serialize)]
101struct ErrorData {
102 err: String,
103}
104
105impl ErrorData {
106 #[inline]
107 fn new(err: impl std::fmt::Display) -> Self {
108 Self { err: err.to_string() }
109 }
110}
111
112impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
113 fn from(error: EngineApiError) -> Self {
114 match error {
115 EngineApiError::InvalidBodiesRange { .. } |
116 EngineApiError::EngineObjectValidationError(
117 EngineObjectValidationError::Payload(_) |
118 EngineObjectValidationError::InvalidParams(_) |
119 EngineObjectValidationError::PayloadAttributes(
124 VersionSpecificValidationError::WithdrawalsNotSupportedInV1 |
125 VersionSpecificValidationError::NoWithdrawalsPostShanghai |
126 VersionSpecificValidationError::HasWithdrawalsPreShanghai,
127 ),
128 ) |
129 EngineApiError::UnexpectedRequestsHash => {
130 jsonrpsee_types::error::ErrorObject::owned(
133 INVALID_PARAMS_CODE,
134 INVALID_PARAMS_MSG,
135 Some(ErrorData::new(error)),
136 )
137 }
138 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
139 UNKNOWN_PAYLOAD_CODE,
140 error.to_string(),
141 None::<()>,
142 ),
143 EngineApiError::PayloadRequestTooLarge { .. } |
144 EngineApiError::BlobRequestTooLarge { .. } => {
145 jsonrpsee_types::error::ErrorObject::owned(
146 REQUEST_TOO_LARGE_CODE,
147 REQUEST_TOO_LARGE_MESSAGE,
148 Some(ErrorData::new(error)),
149 )
150 }
151 EngineApiError::EngineObjectValidationError(
152 EngineObjectValidationError::PayloadAttributes(
153 VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3 |
154 VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun,
155 ),
156 ) => jsonrpsee_types::error::ErrorObject::owned(
157 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
158 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
159 Some(ErrorData::new(error)),
160 ),
161 EngineApiError::EngineObjectValidationError(
162 EngineObjectValidationError::UnsupportedFork,
163 ) => jsonrpsee_types::error::ErrorObject::owned(
164 UNSUPPORTED_FORK_CODE,
165 error.to_string(),
166 None::<()>,
167 ),
168 EngineApiError::ForkChoiceUpdate(ref err) => match err {
170 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => match err {
171 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes => {
172 jsonrpsee_types::error::ErrorObject::owned(
173 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
174 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
175 None::<()>,
176 )
177 }
178 ForkchoiceUpdateError::InvalidState |
179 ForkchoiceUpdateError::UnknownFinalBlock => {
180 jsonrpsee_types::error::ErrorObject::owned(
181 INVALID_FORK_CHOICE_STATE_ERROR,
182 INVALID_FORK_CHOICE_STATE_ERROR_MSG,
183 None::<()>,
184 )
185 }
186 },
187 BeaconForkChoiceUpdateError::EngineUnavailable |
188 BeaconForkChoiceUpdateError::Internal(_) => {
189 jsonrpsee_types::error::ErrorObject::owned(
190 INTERNAL_ERROR_CODE,
191 SERVER_ERROR_MSG,
192 Some(ErrorData::new(error)),
193 )
194 }
195 },
196 EngineApiError::TerminalBlockHash { .. } |
198 EngineApiError::NewPayload(_) |
199 EngineApiError::Internal(_) |
200 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
201 INTERNAL_ERROR_CODE,
202 SERVER_ERROR_MSG,
203 Some(ErrorData::new(error)),
204 ),
205 EngineApiError::Other(err) => err,
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use alloy_rpc_types_engine::ForkchoiceUpdateError;
214 #[track_caller]
215 fn ensure_engine_rpc_error(
216 code: i32,
217 message: &str,
218 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
219 ) {
220 let err = err.into();
221 assert_eq!(err.code(), code);
222 assert_eq!(err.message(), message);
223 }
224
225 #[test]
228 fn engine_error_rpc_error_test() {
229 ensure_engine_rpc_error(
230 UNSUPPORTED_FORK_CODE,
231 "Unsupported fork",
232 EngineApiError::EngineObjectValidationError(
233 EngineObjectValidationError::UnsupportedFork,
234 ),
235 );
236
237 ensure_engine_rpc_error(
238 REQUEST_TOO_LARGE_CODE,
239 "Too large request",
240 EngineApiError::PayloadRequestTooLarge { len: 0 },
241 );
242
243 ensure_engine_rpc_error(
244 -38002,
245 "Invalid forkchoice state",
246 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
247 ForkchoiceUpdateError::InvalidState,
248 )),
249 );
250
251 ensure_engine_rpc_error(
254 -38003,
255 "Invalid payload attributes",
256 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
257 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
258 )),
259 );
260
261 ensure_engine_rpc_error(
262 UNKNOWN_PAYLOAD_CODE,
263 "Unknown payload",
264 EngineApiError::UnknownPayload,
265 );
266
267 ensure_engine_rpc_error(
271 INVALID_PARAMS_CODE,
272 INVALID_PARAMS_MSG,
273 EngineApiError::EngineObjectValidationError(
274 EngineObjectValidationError::PayloadAttributes(
275 VersionSpecificValidationError::NoWithdrawalsPostShanghai,
276 ),
277 ),
278 );
279
280 ensure_engine_rpc_error(
282 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
283 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
284 EngineApiError::EngineObjectValidationError(
285 EngineObjectValidationError::PayloadAttributes(
286 VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3,
287 ),
288 ),
289 );
290 }
291}