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;
12use thiserror::Error;
13
14pub type EngineApiResult<Ok> = Result<Ok, EngineApiError>;
16
17pub const INVALID_PAYLOAD_ATTRIBUTES: i32 = -38003;
19pub const UNSUPPORTED_FORK_CODE: i32 = -38005;
21pub const UNKNOWN_PAYLOAD_CODE: i32 = -38001;
23pub const REQUEST_TOO_LARGE_CODE: i32 = -38004;
25
26const REQUEST_TOO_LARGE_MESSAGE: &str = "Too large request";
28
29const INVALID_PAYLOAD_ATTRIBUTES_MSG: &str = "Invalid payload attributes";
31
32#[derive(Error, Debug)]
37pub enum EngineApiError {
38 #[error("Unknown payload")]
41 UnknownPayload,
42 #[error("requested count too large: {len}")]
44 PayloadRequestTooLarge {
45 len: u64,
47 },
48 #[error("requested blob count too large: {len}")]
50 BlobRequestTooLarge {
51 len: usize,
53 },
54 #[error("invalid start ({start}) or count ({count})")]
56 InvalidBodiesRange {
57 start: u64,
59 count: u64,
61 },
62 #[error(
64 "invalid transition terminal block hash: \
65 execution: {execution:?}, consensus: {consensus}"
66 )]
67 TerminalBlockHash {
68 execution: Option<B256>,
70 consensus: B256,
72 },
73 #[error(transparent)]
75 ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError),
76 #[error(transparent)]
78 NewPayload(#[from] BeaconOnNewPayloadError),
79 #[error(transparent)]
81 Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
82 #[error(transparent)]
84 GetPayloadError(#[from] PayloadBuilderError),
85 #[error(transparent)]
87 EngineObjectValidationError(#[from] EngineObjectValidationError),
88 #[error("requests hash cannot be accepted by the API without `--engine.accept-execution-requests-hash` flag")]
90 UnexpectedRequestsHash,
91 #[error("{0}")]
93 Other(jsonrpsee_types::ErrorObject<'static>),
94}
95
96impl EngineApiError {
97 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
99 Self::Other(err)
100 }
101}
102
103#[derive(serde::Serialize)]
106struct ErrorData {
107 err: String,
108}
109
110impl ErrorData {
111 #[inline]
112 fn new(err: impl std::fmt::Display) -> Self {
113 Self { err: err.to_string() }
114 }
115}
116
117impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
118 fn from(error: EngineApiError) -> Self {
119 match error {
120 EngineApiError::InvalidBodiesRange { .. } |
121 EngineApiError::EngineObjectValidationError(
122 EngineObjectValidationError::Payload(_) |
123 EngineObjectValidationError::InvalidParams(_),
124 ) |
125 EngineApiError::UnexpectedRequestsHash => {
126 jsonrpsee_types::error::ErrorObject::owned(
129 INVALID_PARAMS_CODE,
130 INVALID_PARAMS_MSG,
131 Some(ErrorData::new(error)),
132 )
133 }
134 EngineApiError::EngineObjectValidationError(
135 EngineObjectValidationError::PayloadAttributes(_),
136 ) => {
137 jsonrpsee_types::error::ErrorObject::owned(
140 INVALID_PAYLOAD_ATTRIBUTES,
141 INVALID_PAYLOAD_ATTRIBUTES_MSG,
142 Some(ErrorData::new(error)),
143 )
144 }
145 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
146 UNKNOWN_PAYLOAD_CODE,
147 error.to_string(),
148 None::<()>,
149 ),
150 EngineApiError::PayloadRequestTooLarge { .. } |
151 EngineApiError::BlobRequestTooLarge { .. } => {
152 jsonrpsee_types::error::ErrorObject::owned(
153 REQUEST_TOO_LARGE_CODE,
154 REQUEST_TOO_LARGE_MESSAGE,
155 Some(ErrorData::new(error)),
156 )
157 }
158 EngineApiError::EngineObjectValidationError(
159 EngineObjectValidationError::UnsupportedFork,
160 ) => jsonrpsee_types::error::ErrorObject::owned(
161 UNSUPPORTED_FORK_CODE,
162 error.to_string(),
163 None::<()>,
164 ),
165 EngineApiError::ForkChoiceUpdate(ref err) => match err {
167 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => match err {
168 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes => {
169 jsonrpsee_types::error::ErrorObject::owned(
170 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
171 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
172 None::<()>,
173 )
174 }
175 ForkchoiceUpdateError::InvalidState |
176 ForkchoiceUpdateError::UnknownFinalBlock => {
177 jsonrpsee_types::error::ErrorObject::owned(
178 INVALID_FORK_CHOICE_STATE_ERROR,
179 INVALID_FORK_CHOICE_STATE_ERROR_MSG,
180 None::<()>,
181 )
182 }
183 },
184 BeaconForkChoiceUpdateError::EngineUnavailable |
185 BeaconForkChoiceUpdateError::Internal(_) => {
186 jsonrpsee_types::error::ErrorObject::owned(
187 INTERNAL_ERROR_CODE,
188 SERVER_ERROR_MSG,
189 Some(ErrorData::new(error)),
190 )
191 }
192 },
193 EngineApiError::TerminalBlockHash { .. } |
195 EngineApiError::NewPayload(_) |
196 EngineApiError::Internal(_) |
197 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
198 INTERNAL_ERROR_CODE,
199 SERVER_ERROR_MSG,
200 Some(ErrorData::new(error)),
201 ),
202 EngineApiError::Other(err) => err,
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use alloy_rpc_types_engine::ForkchoiceUpdateError;
211
212 #[track_caller]
213 fn ensure_engine_rpc_error(
214 code: i32,
215 message: &str,
216 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
217 ) {
218 let err = err.into();
219 assert_eq!(err.code(), code);
220 assert_eq!(err.message(), message);
221 }
222
223 #[test]
226 fn engine_error_rpc_error_test() {
227 ensure_engine_rpc_error(
228 UNSUPPORTED_FORK_CODE,
229 "Unsupported fork",
230 EngineApiError::EngineObjectValidationError(
231 EngineObjectValidationError::UnsupportedFork,
232 ),
233 );
234
235 ensure_engine_rpc_error(
236 REQUEST_TOO_LARGE_CODE,
237 "Too large request",
238 EngineApiError::PayloadRequestTooLarge { len: 0 },
239 );
240
241 ensure_engine_rpc_error(
242 -38002,
243 "Invalid forkchoice state",
244 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
245 ForkchoiceUpdateError::InvalidState,
246 )),
247 );
248
249 ensure_engine_rpc_error(
250 -38003,
251 "Invalid payload attributes",
252 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
253 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
254 )),
255 );
256
257 ensure_engine_rpc_error(
258 UNKNOWN_PAYLOAD_CODE,
259 "Unknown payload",
260 EngineApiError::UnknownPayload,
261 );
262 }
263}