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::EngineObjectValidationError(
120 EngineObjectValidationError::PayloadAttributes(
121 VersionSpecificValidationError::WithdrawalsNotSupportedInV1 |
122 VersionSpecificValidationError::NoWithdrawalsPostShanghai |
123 VersionSpecificValidationError::HasWithdrawalsPreShanghai |
124 VersionSpecificValidationError::BlockAccessListNotSupportedBeforeV6 |
125 VersionSpecificValidationError::HasBlockAccessListPreAmsterdam |
126 VersionSpecificValidationError::NoBlockAccessListPostAmsterdam |
127 VersionSpecificValidationError::HasSlotNumberPreAmsterdam |
128 VersionSpecificValidationError::NoSlotNumberPostAmsterdam |
129 VersionSpecificValidationError::SlotNumberNotSupportedBeforeV6,
130 ),
131 ) |
132 EngineApiError::UnexpectedRequestsHash => {
133 jsonrpsee_types::error::ErrorObject::owned(
136 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
137 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
138 Some(ErrorData::new(error)),
139 )
140 }
141 EngineApiError::InvalidBodiesRange { .. } |
142 EngineApiError::EngineObjectValidationError(
143 EngineObjectValidationError::Payload(_) |
144 EngineObjectValidationError::InvalidParams(_),
145 ) => jsonrpsee_types::error::ErrorObject::owned(
146 INVALID_PARAMS_CODE,
147 INVALID_PARAMS_MSG,
148 Some(ErrorData::new(error)),
149 ),
150 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
151 UNKNOWN_PAYLOAD_CODE,
152 error.to_string(),
153 None::<()>,
154 ),
155 EngineApiError::PayloadRequestTooLarge { .. } |
156 EngineApiError::BlobRequestTooLarge { .. } => {
157 jsonrpsee_types::error::ErrorObject::owned(
158 REQUEST_TOO_LARGE_CODE,
159 REQUEST_TOO_LARGE_MESSAGE,
160 Some(ErrorData::new(error)),
161 )
162 }
163 EngineApiError::EngineObjectValidationError(
164 EngineObjectValidationError::PayloadAttributes(
165 VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3 |
166 VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun,
167 ),
168 ) => jsonrpsee_types::error::ErrorObject::owned(
169 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
170 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
171 Some(ErrorData::new(error)),
172 ),
173 EngineApiError::EngineObjectValidationError(
174 EngineObjectValidationError::UnsupportedFork,
175 ) => jsonrpsee_types::error::ErrorObject::owned(
176 UNSUPPORTED_FORK_CODE,
177 error.to_string(),
178 None::<()>,
179 ),
180 EngineApiError::ForkChoiceUpdate(ref err) => match err {
182 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => match err {
183 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes => {
184 jsonrpsee_types::error::ErrorObject::owned(
185 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
186 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
187 None::<()>,
188 )
189 }
190 ForkchoiceUpdateError::InvalidState |
191 ForkchoiceUpdateError::UnknownFinalBlock => {
192 jsonrpsee_types::error::ErrorObject::owned(
193 INVALID_FORK_CHOICE_STATE_ERROR,
194 INVALID_FORK_CHOICE_STATE_ERROR_MSG,
195 None::<()>,
196 )
197 }
198 },
199 BeaconForkChoiceUpdateError::EngineUnavailable |
200 BeaconForkChoiceUpdateError::Internal(_) => {
201 jsonrpsee_types::error::ErrorObject::owned(
202 INTERNAL_ERROR_CODE,
203 SERVER_ERROR_MSG,
204 Some(ErrorData::new(error)),
205 )
206 }
207 },
208 EngineApiError::TerminalBlockHash { .. } |
210 EngineApiError::NewPayload(_) |
211 EngineApiError::Internal(_) |
212 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
213 INTERNAL_ERROR_CODE,
214 SERVER_ERROR_MSG,
215 Some(ErrorData::new(error)),
216 ),
217 EngineApiError::Other(err) => err,
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use alloy_rpc_types_engine::ForkchoiceUpdateError;
226 #[track_caller]
227 fn ensure_engine_rpc_error(
228 code: i32,
229 message: &str,
230 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
231 ) {
232 let err = err.into();
233 assert_eq!(err.code(), code);
234 assert_eq!(err.message(), message);
235 }
236
237 #[test]
240 fn engine_error_rpc_error_test() {
241 ensure_engine_rpc_error(
242 UNSUPPORTED_FORK_CODE,
243 "Unsupported fork",
244 EngineApiError::EngineObjectValidationError(
245 EngineObjectValidationError::UnsupportedFork,
246 ),
247 );
248
249 ensure_engine_rpc_error(
250 REQUEST_TOO_LARGE_CODE,
251 "Too large request",
252 EngineApiError::PayloadRequestTooLarge { len: 0 },
253 );
254
255 ensure_engine_rpc_error(
256 -38002,
257 "Invalid forkchoice state",
258 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
259 ForkchoiceUpdateError::InvalidState,
260 )),
261 );
262
263 ensure_engine_rpc_error(
266 -38003,
267 "Invalid payload attributes",
268 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
269 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
270 )),
271 );
272
273 ensure_engine_rpc_error(
274 UNKNOWN_PAYLOAD_CODE,
275 "Unknown payload",
276 EngineApiError::UnknownPayload,
277 );
278
279 ensure_engine_rpc_error(
284 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
285 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
286 EngineApiError::EngineObjectValidationError(
287 EngineObjectValidationError::PayloadAttributes(
288 VersionSpecificValidationError::NoWithdrawalsPostShanghai,
289 ),
290 ),
291 );
292
293 ensure_engine_rpc_error(
294 INVALID_PARAMS_CODE,
295 INVALID_PARAMS_MSG,
296 EngineApiError::EngineObjectValidationError(EngineObjectValidationError::Payload(
297 VersionSpecificValidationError::NoWithdrawalsPostShanghai,
298 )),
299 );
300
301 ensure_engine_rpc_error(
302 INVALID_PARAMS_CODE,
303 INVALID_PARAMS_MSG,
304 EngineApiError::EngineObjectValidationError(EngineObjectValidationError::Payload(
305 VersionSpecificValidationError::HasWithdrawalsPreShanghai,
306 )),
307 );
308
309 ensure_engine_rpc_error(
310 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
311 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
312 EngineApiError::EngineObjectValidationError(
313 EngineObjectValidationError::PayloadAttributes(
314 VersionSpecificValidationError::HasWithdrawalsPreShanghai,
315 ),
316 ),
317 );
318
319 ensure_engine_rpc_error(
321 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
322 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
323 EngineApiError::EngineObjectValidationError(
324 EngineObjectValidationError::PayloadAttributes(
325 VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3,
326 ),
327 ),
328 );
329 }
330}