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("{0}")]
97 Other(jsonrpsee_types::ErrorObject<'static>),
98}
99
100impl EngineApiError {
101 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
103 Self::Other(err)
104 }
105}
106
107#[derive(serde::Serialize)]
110struct ErrorData {
111 err: String,
112}
113
114impl ErrorData {
115 #[inline]
116 fn new(err: impl std::fmt::Display) -> Self {
117 Self { err: err.to_string() }
118 }
119}
120
121impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
122 fn from(error: EngineApiError) -> Self {
123 match error {
124 EngineApiError::InvalidBodiesRange { .. } |
125 EngineApiError::EngineObjectValidationError(
126 EngineObjectValidationError::Payload(_) |
127 EngineObjectValidationError::InvalidParams(_),
128 ) => {
129 jsonrpsee_types::error::ErrorObject::owned(
132 INVALID_PARAMS_CODE,
133 INVALID_PARAMS_MSG,
134 Some(ErrorData::new(error)),
135 )
136 }
137 EngineApiError::EngineObjectValidationError(
138 EngineObjectValidationError::PayloadAttributes(_),
139 ) => {
140 jsonrpsee_types::error::ErrorObject::owned(
143 INVALID_PAYLOAD_ATTRIBUTES,
144 INVALID_PAYLOAD_ATTRIBUTES_MSG,
145 Some(ErrorData::new(error)),
146 )
147 }
148 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
149 UNKNOWN_PAYLOAD_CODE,
150 error.to_string(),
151 None::<()>,
152 ),
153 EngineApiError::PayloadRequestTooLarge { .. } |
154 EngineApiError::BlobRequestTooLarge { .. } => {
155 jsonrpsee_types::error::ErrorObject::owned(
156 REQUEST_TOO_LARGE_CODE,
157 REQUEST_TOO_LARGE_MESSAGE,
158 Some(ErrorData::new(error)),
159 )
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) => (*err).into(),
171 BeaconForkChoiceUpdateError::EngineUnavailable |
172 BeaconForkChoiceUpdateError::Internal(_) => {
173 jsonrpsee_types::error::ErrorObject::owned(
174 INTERNAL_ERROR_CODE,
175 SERVER_ERROR_MSG,
176 Some(ErrorData::new(error)),
177 )
178 }
179 },
180 EngineApiError::TerminalTD { .. } |
182 EngineApiError::TerminalBlockHash { .. } |
183 EngineApiError::NewPayload(_) |
184 EngineApiError::Internal(_) |
185 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
186 INTERNAL_ERROR_CODE,
187 SERVER_ERROR_MSG,
188 Some(ErrorData::new(error)),
189 ),
190 EngineApiError::Other(err) => err,
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use alloy_rpc_types_engine::ForkchoiceUpdateError;
199
200 #[track_caller]
201 fn ensure_engine_rpc_error(
202 code: i32,
203 message: &str,
204 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
205 ) {
206 let err = err.into();
207 assert_eq!(err.code(), code);
208 assert_eq!(err.message(), message);
209 }
210
211 #[test]
214 fn engine_error_rpc_error_test() {
215 ensure_engine_rpc_error(
216 UNSUPPORTED_FORK_CODE,
217 "Unsupported fork",
218 EngineApiError::EngineObjectValidationError(
219 EngineObjectValidationError::UnsupportedFork,
220 ),
221 );
222
223 ensure_engine_rpc_error(
224 REQUEST_TOO_LARGE_CODE,
225 "Too large request",
226 EngineApiError::PayloadRequestTooLarge { len: 0 },
227 );
228
229 ensure_engine_rpc_error(
230 -38002,
231 "Invalid forkchoice state",
232 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
233 ForkchoiceUpdateError::InvalidState,
234 )),
235 );
236
237 ensure_engine_rpc_error(
238 -38003,
239 "Invalid payload attributes",
240 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
241 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
242 )),
243 );
244
245 ensure_engine_rpc_error(
246 UNKNOWN_PAYLOAD_CODE,
247 "Unknown payload",
248 EngineApiError::UnknownPayload,
249 );
250 }
251}