reth_rpc_engine_api/
error.rs
1use alloy_primitives::{B256, U256};
2use jsonrpsee_types::error::{
3 INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG,
4};
5use reth_engine_primitives::{BeaconForkChoiceUpdateError, BeaconOnNewPayloadError};
6use reth_payload_builder_primitives::PayloadBuilderError;
7use reth_payload_primitives::EngineObjectValidationError;
8use thiserror::Error;
9
10pub type EngineApiResult<Ok> = Result<Ok, EngineApiError>;
12
13pub const INVALID_PAYLOAD_ATTRIBUTES: i32 = -38003;
15pub const UNSUPPORTED_FORK_CODE: i32 = -38005;
17pub const UNKNOWN_PAYLOAD_CODE: i32 = -38001;
19pub const REQUEST_TOO_LARGE_CODE: i32 = -38004;
21
22const REQUEST_TOO_LARGE_MESSAGE: &str = "Too large request";
24
25const INVALID_PAYLOAD_ATTRIBUTES_MSG: &str = "Invalid payload attributes";
27
28#[derive(Error, Debug)]
33pub enum EngineApiError {
34 #[error("Unknown payload")]
37 UnknownPayload,
38 #[error("requested count too large: {len}")]
40 PayloadRequestTooLarge {
41 len: u64,
43 },
44 #[error("requested blob count too large: {len}")]
46 BlobRequestTooLarge {
47 len: usize,
49 },
50 #[error("invalid start ({start}) or count ({count})")]
52 InvalidBodiesRange {
53 start: u64,
55 count: u64,
57 },
58 #[error(
60 "invalid transition terminal total difficulty: \
61 execution: {execution}, consensus: {consensus}"
62 )]
63 TerminalTD {
64 execution: U256,
66 consensus: U256,
68 },
69 #[error(
71 "invalid transition terminal block hash: \
72 execution: {execution:?}, consensus: {consensus}"
73 )]
74 TerminalBlockHash {
75 execution: Option<B256>,
77 consensus: B256,
79 },
80 #[error(transparent)]
82 ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError),
83 #[error(transparent)]
85 NewPayload(#[from] BeaconOnNewPayloadError),
86 #[error(transparent)]
88 Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
89 #[error(transparent)]
91 GetPayloadError(#[from] PayloadBuilderError),
92 #[error(transparent)]
94 EngineObjectValidationError(#[from] EngineObjectValidationError),
95 #[error("requests hash cannot be accepted by the API without `--engine.accept-execution-requests-hash` flag")]
97 UnexpectedRequestsHash,
98 #[error("{0}")]
100 Other(jsonrpsee_types::ErrorObject<'static>),
101}
102
103impl EngineApiError {
104 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
106 Self::Other(err)
107 }
108}
109
110#[derive(serde::Serialize)]
113struct ErrorData {
114 err: String,
115}
116
117impl ErrorData {
118 #[inline]
119 fn new(err: impl std::fmt::Display) -> Self {
120 Self { err: err.to_string() }
121 }
122}
123
124impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
125 fn from(error: EngineApiError) -> Self {
126 match error {
127 EngineApiError::InvalidBodiesRange { .. } |
128 EngineApiError::EngineObjectValidationError(
129 EngineObjectValidationError::Payload(_) |
130 EngineObjectValidationError::InvalidParams(_),
131 ) |
132 EngineApiError::UnexpectedRequestsHash => {
133 jsonrpsee_types::error::ErrorObject::owned(
136 INVALID_PARAMS_CODE,
137 INVALID_PARAMS_MSG,
138 Some(ErrorData::new(error)),
139 )
140 }
141 EngineApiError::EngineObjectValidationError(
142 EngineObjectValidationError::PayloadAttributes(_),
143 ) => {
144 jsonrpsee_types::error::ErrorObject::owned(
147 INVALID_PAYLOAD_ATTRIBUTES,
148 INVALID_PAYLOAD_ATTRIBUTES_MSG,
149 Some(ErrorData::new(error)),
150 )
151 }
152 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
153 UNKNOWN_PAYLOAD_CODE,
154 error.to_string(),
155 None::<()>,
156 ),
157 EngineApiError::PayloadRequestTooLarge { .. } |
158 EngineApiError::BlobRequestTooLarge { .. } => {
159 jsonrpsee_types::error::ErrorObject::owned(
160 REQUEST_TOO_LARGE_CODE,
161 REQUEST_TOO_LARGE_MESSAGE,
162 Some(ErrorData::new(error)),
163 )
164 }
165 EngineApiError::EngineObjectValidationError(
166 EngineObjectValidationError::UnsupportedFork,
167 ) => jsonrpsee_types::error::ErrorObject::owned(
168 UNSUPPORTED_FORK_CODE,
169 error.to_string(),
170 None::<()>,
171 ),
172 EngineApiError::ForkChoiceUpdate(ref err) => match err {
174 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => (*err).into(),
175 BeaconForkChoiceUpdateError::EngineUnavailable |
176 BeaconForkChoiceUpdateError::Internal(_) => {
177 jsonrpsee_types::error::ErrorObject::owned(
178 INTERNAL_ERROR_CODE,
179 SERVER_ERROR_MSG,
180 Some(ErrorData::new(error)),
181 )
182 }
183 },
184 EngineApiError::TerminalTD { .. } |
186 EngineApiError::TerminalBlockHash { .. } |
187 EngineApiError::NewPayload(_) |
188 EngineApiError::Internal(_) |
189 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
190 INTERNAL_ERROR_CODE,
191 SERVER_ERROR_MSG,
192 Some(ErrorData::new(error)),
193 ),
194 EngineApiError::Other(err) => err,
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use alloy_rpc_types_engine::ForkchoiceUpdateError;
203
204 #[track_caller]
205 fn ensure_engine_rpc_error(
206 code: i32,
207 message: &str,
208 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
209 ) {
210 let err = err.into();
211 assert_eq!(err.code(), code);
212 assert_eq!(err.message(), message);
213 }
214
215 #[test]
218 fn engine_error_rpc_error_test() {
219 ensure_engine_rpc_error(
220 UNSUPPORTED_FORK_CODE,
221 "Unsupported fork",
222 EngineApiError::EngineObjectValidationError(
223 EngineObjectValidationError::UnsupportedFork,
224 ),
225 );
226
227 ensure_engine_rpc_error(
228 REQUEST_TOO_LARGE_CODE,
229 "Too large request",
230 EngineApiError::PayloadRequestTooLarge { len: 0 },
231 );
232
233 ensure_engine_rpc_error(
234 -38002,
235 "Invalid forkchoice state",
236 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
237 ForkchoiceUpdateError::InvalidState,
238 )),
239 );
240
241 ensure_engine_rpc_error(
242 -38003,
243 "Invalid payload attributes",
244 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
245 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
246 )),
247 );
248
249 ensure_engine_rpc_error(
250 UNKNOWN_PAYLOAD_CODE,
251 "Unknown payload",
252 EngineApiError::UnknownPayload,
253 );
254 }
255}