reth_rpc_engine_api/
error.rs1use alloy_primitives::{B256, U256};
2use jsonrpsee_types::error::{
3 INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG,
4};
5use reth_beacon_consensus::BeaconForkChoiceUpdateError;
6use reth_engine_primitives::BeaconOnNewPayloadError;
7use reth_payload_builder_primitives::PayloadBuilderError;
8use reth_payload_primitives::EngineObjectValidationError;
9use thiserror::Error;
10
11pub type EngineApiResult<Ok> = Result<Ok, EngineApiError>;
13
14pub const INVALID_PAYLOAD_ATTRIBUTES: i32 = -38003;
16pub const UNSUPPORTED_FORK_CODE: i32 = -38005;
18pub const UNKNOWN_PAYLOAD_CODE: i32 = -38001;
20pub const REQUEST_TOO_LARGE_CODE: i32 = -38004;
22
23const REQUEST_TOO_LARGE_MESSAGE: &str = "Too large request";
25
26const INVALID_PAYLOAD_ATTRIBUTES_MSG: &str = "Invalid payload attributes";
28
29#[derive(Error, Debug)]
34pub enum EngineApiError {
35 #[error("Unknown payload")]
38 UnknownPayload,
39 #[error("requested count too large: {len}")]
41 PayloadRequestTooLarge {
42 len: u64,
44 },
45 #[error("requested blob count too large: {len}")]
47 BlobRequestTooLarge {
48 len: usize,
50 },
51 #[error("invalid start ({start}) or count ({count})")]
53 InvalidBodiesRange {
54 start: u64,
56 count: u64,
58 },
59 #[error(
61 "invalid transition terminal total difficulty: \
62 execution: {execution}, consensus: {consensus}"
63 )]
64 TerminalTD {
65 execution: U256,
67 consensus: U256,
69 },
70 #[error(
72 "invalid transition terminal block hash: \
73 execution: {execution:?}, consensus: {consensus}"
74 )]
75 TerminalBlockHash {
76 execution: Option<B256>,
78 consensus: B256,
80 },
81 #[error(transparent)]
83 ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError),
84 #[error(transparent)]
86 NewPayload(#[from] BeaconOnNewPayloadError),
87 #[error(transparent)]
89 Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
90 #[error(transparent)]
92 GetPayloadError(#[from] PayloadBuilderError),
93 #[error(transparent)]
95 EngineObjectValidationError(#[from] EngineObjectValidationError),
96 #[error("{0}")]
98 Other(jsonrpsee_types::ErrorObject<'static>),
99}
100
101impl EngineApiError {
102 pub const fn other(err: jsonrpsee_types::ErrorObject<'static>) -> Self {
104 Self::Other(err)
105 }
106}
107
108#[derive(serde::Serialize)]
111struct ErrorData {
112 err: String,
113}
114
115impl ErrorData {
116 #[inline]
117 fn new(err: impl std::fmt::Display) -> Self {
118 Self { err: err.to_string() }
119 }
120}
121
122impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
123 fn from(error: EngineApiError) -> Self {
124 match error {
125 EngineApiError::InvalidBodiesRange { .. } |
126 EngineApiError::EngineObjectValidationError(
127 EngineObjectValidationError::Payload(_) |
128 EngineObjectValidationError::InvalidParams(_),
129 ) => {
130 jsonrpsee_types::error::ErrorObject::owned(
133 INVALID_PARAMS_CODE,
134 INVALID_PARAMS_MSG,
135 Some(ErrorData::new(error)),
136 )
137 }
138 EngineApiError::EngineObjectValidationError(
139 EngineObjectValidationError::PayloadAttributes(_),
140 ) => {
141 jsonrpsee_types::error::ErrorObject::owned(
144 INVALID_PAYLOAD_ATTRIBUTES,
145 INVALID_PAYLOAD_ATTRIBUTES_MSG,
146 Some(ErrorData::new(error)),
147 )
148 }
149 EngineApiError::UnknownPayload => jsonrpsee_types::error::ErrorObject::owned(
150 UNKNOWN_PAYLOAD_CODE,
151 error.to_string(),
152 None::<()>,
153 ),
154 EngineApiError::PayloadRequestTooLarge { .. } |
155 EngineApiError::BlobRequestTooLarge { .. } => {
156 jsonrpsee_types::error::ErrorObject::owned(
157 REQUEST_TOO_LARGE_CODE,
158 REQUEST_TOO_LARGE_MESSAGE,
159 Some(ErrorData::new(error)),
160 )
161 }
162 EngineApiError::EngineObjectValidationError(
163 EngineObjectValidationError::UnsupportedFork,
164 ) => jsonrpsee_types::error::ErrorObject::owned(
165 UNSUPPORTED_FORK_CODE,
166 error.to_string(),
167 None::<()>,
168 ),
169 EngineApiError::ForkChoiceUpdate(ref err) => match err {
171 BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => (*err).into(),
172 BeaconForkChoiceUpdateError::EngineUnavailable |
173 BeaconForkChoiceUpdateError::Internal(_) => {
174 jsonrpsee_types::error::ErrorObject::owned(
175 INTERNAL_ERROR_CODE,
176 SERVER_ERROR_MSG,
177 Some(ErrorData::new(error)),
178 )
179 }
180 },
181 EngineApiError::TerminalTD { .. } |
183 EngineApiError::TerminalBlockHash { .. } |
184 EngineApiError::NewPayload(_) |
185 EngineApiError::Internal(_) |
186 EngineApiError::GetPayloadError(_) => jsonrpsee_types::error::ErrorObject::owned(
187 INTERNAL_ERROR_CODE,
188 SERVER_ERROR_MSG,
189 Some(ErrorData::new(error)),
190 ),
191 EngineApiError::Other(err) => err,
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use alloy_rpc_types_engine::ForkchoiceUpdateError;
200
201 #[track_caller]
202 fn ensure_engine_rpc_error(
203 code: i32,
204 message: &str,
205 err: impl Into<jsonrpsee_types::error::ErrorObject<'static>>,
206 ) {
207 let err = err.into();
208 assert_eq!(err.code(), code);
209 assert_eq!(err.message(), message);
210 }
211
212 #[test]
215 fn engine_error_rpc_error_test() {
216 ensure_engine_rpc_error(
217 UNSUPPORTED_FORK_CODE,
218 "Unsupported fork",
219 EngineApiError::EngineObjectValidationError(
220 EngineObjectValidationError::UnsupportedFork,
221 ),
222 );
223
224 ensure_engine_rpc_error(
225 REQUEST_TOO_LARGE_CODE,
226 "Too large request",
227 EngineApiError::PayloadRequestTooLarge { len: 0 },
228 );
229
230 ensure_engine_rpc_error(
231 -38002,
232 "Invalid forkchoice state",
233 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
234 ForkchoiceUpdateError::InvalidState,
235 )),
236 );
237
238 ensure_engine_rpc_error(
239 -38003,
240 "Invalid payload attributes",
241 EngineApiError::ForkChoiceUpdate(BeaconForkChoiceUpdateError::ForkchoiceUpdateError(
242 ForkchoiceUpdateError::UpdatedInvalidPayloadAttributes,
243 )),
244 );
245
246 ensure_engine_rpc_error(
247 UNKNOWN_PAYLOAD_CODE,
248 "Unknown payload",
249 EngineApiError::UnknownPayload,
250 );
251 }
252}