1use alloy_primitives::{B256, U256};
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 total difficulty: \
65 execution: {execution}, consensus: {consensus}"
66 )]
67 TerminalTD {
68 execution: U256,
70 consensus: U256,
72 },
73 #[error(
75 "invalid transition terminal block hash: \
76 execution: {execution:?}, consensus: {consensus}"
77 )]
78 TerminalBlockHash {
79 execution: Option<B256>,
81 consensus: B256,
83 },
84 #[error(transparent)]
86 ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError),
87 #[error(transparent)]
89 NewPayload(#[from] BeaconOnNewPayloadError),
90 #[error(transparent)]
92 Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
93 #[error(transparent)]
95 GetPayloadError(#[from] PayloadBuilderError),
96 #[error(transparent)]
98 EngineObjectValidationError(#[from] EngineObjectValidationError),
99 #[error("requests hash cannot be accepted by the API without `--engine.accept-execution-requests-hash` flag")]
101 UnexpectedRequestsHash,
102 #[error("{0}")]
104 Other(jsonrpsee_types::ErrorObject<'static>),
105}
106
107impl EngineApiError {
108 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
110 Self::Other(err)
111 }
112}
113
114#[derive(serde::Serialize)]
117struct ErrorData {
118 err: String,
119}
120
121impl ErrorData {
122 #[inline]
123 fn new(err: impl std::fmt::Display) -> Self {
124 Self { err: err.to_string() }
125 }
126}
127
128impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
129 fn from(error: EngineApiError) -> Self {
130 match error {
131 EngineApiError::InvalidBodiesRange { .. } |
132 EngineApiError::EngineObjectValidationError(
133 EngineObjectValidationError::Payload(_) |
134 EngineObjectValidationError::InvalidParams(_),
135 ) |
136 EngineApiError::UnexpectedRequestsHash => {
137 jsonrpsee_types::error::ErrorObject::owned(
140 INVALID_PARAMS_CODE,
141 INVALID_PARAMS_MSG,
142 Some(ErrorData::new(error)),
143 )
144 }
145 EngineApiError::EngineObjectValidationError(
146 EngineObjectValidationError::PayloadAttributes(_),
147 ) => {
148 jsonrpsee_types::error::ErrorObject::owned(
151 INVALID_PAYLOAD_ATTRIBUTES,
152 INVALID_PAYLOAD_ATTRIBUTES_MSG,
153 Some(ErrorData::new(error)),
154 )
155 }
156 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
157 UNKNOWN_PAYLOAD_CODE,
158 error.to_string(),
159 None::<()>,
160 ),
161 EngineApiError::PayloadRequestTooLarge { .. } |
162 EngineApiError::BlobRequestTooLarge { .. } => {
163 jsonrpsee_types::error::ErrorObject::owned(
164 REQUEST_TOO_LARGE_CODE,
165 REQUEST_TOO_LARGE_MESSAGE,
166 Some(ErrorData::new(error)),
167 )
168 }
169 EngineApiError::EngineObjectValidationError(
170 EngineObjectValidationError::UnsupportedFork,
171 ) => jsonrpsee_types::error::ErrorObject::owned(
172 UNSUPPORTED_FORK_CODE,
173 error.to_string(),
174 None::<()>,
175 ),
176 EngineApiError::ForkChoiceUpdate(ref err) => match err {
178 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => match err {
179 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes => {
180 jsonrpsee_types::error::ErrorObject::owned(
181 INVALID_PAYLOAD_ATTRIBUTES_ERROR,
182 INVALID_PAYLOAD_ATTRIBUTES_ERROR_MSG,
183 None::<()>,
184 )
185 }
186 ForkchoiceUpdateError::InvalidState |
187 ForkchoiceUpdateError::UnknownFinalBlock => {
188 jsonrpsee_types::error::ErrorObject::owned(
189 INVALID_FORK_CHOICE_STATE_ERROR,
190 INVALID_FORK_CHOICE_STATE_ERROR_MSG,
191 None::<()>,
192 )
193 }
194 },
195 BeaconForkChoiceUpdateError::EngineUnavailable |
196 BeaconForkChoiceUpdateError::Internal(_) => {
197 jsonrpsee_types::error::ErrorObject::owned(
198 INTERNAL_ERROR_CODE,
199 SERVER_ERROR_MSG,
200 Some(ErrorData::new(error)),
201 )
202 }
203 },
204 EngineApiError::TerminalTD { .. } |
206 EngineApiError::TerminalBlockHash { .. } |
207 EngineApiError::NewPayload(_) |
208 EngineApiError::Internal(_) |
209 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
210 INTERNAL_ERROR_CODE,
211 SERVER_ERROR_MSG,
212 Some(ErrorData::new(error)),
213 ),
214 EngineApiError::Other(err) => err,
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use alloy_rpc_types_engine::ForkchoiceUpdateError;
223
224 #[track_caller]
225 fn ensure_engine_rpc_error(
226 code: i32,
227 message: &str,
228 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
229 ) {
230 let err = err.into();
231 assert_eq!(err.code(), code);
232 assert_eq!(err.message(), message);
233 }
234
235 #[test]
238 fn engine_error_rpc_error_test() {
239 ensure_engine_rpc_error(
240 UNSUPPORTED_FORK_CODE,
241 "Unsupported fork",
242 EngineApiError::EngineObjectValidationError(
243 EngineObjectValidationError::UnsupportedFork,
244 ),
245 );
246
247 ensure_engine_rpc_error(
248 REQUEST_TOO_LARGE_CODE,
249 "Too large request",
250 EngineApiError::PayloadRequestTooLarge { len: 0 },
251 );
252
253 ensure_engine_rpc_error(
254 -38002,
255 "Invalid forkchoice state",
256 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
257 ForkchoiceUpdateError::InvalidState,
258 )),
259 );
260
261 ensure_engine_rpc_error(
262 -38003,
263 "Invalid payload attributes",
264 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
265 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
266 )),
267 );
268
269 ensure_engine_rpc_error(
270 UNKNOWN_PAYLOAD_CODE,
271 "Unknown payload",
272 EngineApiError::UnknownPayload,
273 );
274 }
275}