reth_optimism_rpc/
error.rs

1//! RPC errors specific to OP.
2
3use alloy_json_rpc::ErrorPayload;
4use alloy_rpc_types_eth::{error::EthRpcErrorCode, BlockError};
5use alloy_transport::{RpcError, TransportErrorKind};
6use jsonrpsee_types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE};
7use op_revm::{OpHaltReason, OpTransactionError};
8use reth_evm::execute::ProviderError;
9use reth_optimism_evm::OpBlockExecutionError;
10use reth_rpc_eth_api::{AsEthApiError, EthTxEnvError, TransactionConversionError};
11use reth_rpc_eth_types::{error::api::FromEvmHalt, EthApiError};
12use reth_rpc_server_types::result::{internal_rpc_err, rpc_err};
13use revm::context_interface::result::{EVMError, InvalidTransaction};
14use std::{convert::Infallible, fmt::Display};
15
16/// Optimism specific errors, that extend [`EthApiError`].
17#[derive(Debug, thiserror::Error)]
18pub enum OpEthApiError {
19    /// L1 ethereum error.
20    #[error(transparent)]
21    Eth(#[from] EthApiError),
22    /// EVM error originating from invalid optimism data.
23    #[error(transparent)]
24    Evm(#[from] OpBlockExecutionError),
25    /// Thrown when calculating L1 gas fee.
26    #[error("failed to calculate l1 gas fee")]
27    L1BlockFeeError,
28    /// Thrown when calculating L1 gas used
29    #[error("failed to calculate l1 gas used")]
30    L1BlockGasError,
31    /// Wrapper for [`revm_primitives::InvalidTransaction`](InvalidTransaction).
32    #[error(transparent)]
33    InvalidTransaction(#[from] OpInvalidTransactionError),
34    /// Sequencer client error.
35    #[error(transparent)]
36    Sequencer(#[from] SequencerClientError),
37}
38
39impl AsEthApiError for OpEthApiError {
40    fn as_err(&self) -> Option<&EthApiError> {
41        match self {
42            Self::Eth(err) => Some(err),
43            _ => None,
44        }
45    }
46}
47
48impl From<OpEthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
49    fn from(err: OpEthApiError) -> Self {
50        match err {
51            OpEthApiError::Eth(err) => err.into(),
52            OpEthApiError::InvalidTransaction(err) => err.into(),
53            OpEthApiError::Evm(_) |
54            OpEthApiError::L1BlockFeeError |
55            OpEthApiError::L1BlockGasError => internal_rpc_err(err.to_string()),
56            OpEthApiError::Sequencer(err) => err.into(),
57        }
58    }
59}
60
61/// Optimism specific invalid transaction errors
62#[derive(thiserror::Error, Debug)]
63pub enum OpInvalidTransactionError {
64    /// A deposit transaction was submitted as a system transaction post-regolith.
65    #[error("no system transactions allowed after regolith")]
66    DepositSystemTxPostRegolith,
67    /// A deposit transaction halted post-regolith
68    #[error("deposit transaction halted after regolith")]
69    HaltedDepositPostRegolith,
70    /// Transaction conditional errors.
71    #[error(transparent)]
72    TxConditionalErr(#[from] TxConditionalErr),
73}
74
75impl From<OpInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
76    fn from(err: OpInvalidTransactionError) -> Self {
77        match err {
78            OpInvalidTransactionError::DepositSystemTxPostRegolith |
79            OpInvalidTransactionError::HaltedDepositPostRegolith => {
80                rpc_err(EthRpcErrorCode::TransactionRejected.code(), err.to_string(), None)
81            }
82            OpInvalidTransactionError::TxConditionalErr(_) => err.into(),
83        }
84    }
85}
86
87impl TryFrom<OpTransactionError> for OpInvalidTransactionError {
88    type Error = InvalidTransaction;
89
90    fn try_from(err: OpTransactionError) -> Result<Self, Self::Error> {
91        match err {
92            OpTransactionError::DepositSystemTxPostRegolith => {
93                Ok(Self::DepositSystemTxPostRegolith)
94            }
95            OpTransactionError::HaltedDepositPostRegolith => Ok(Self::HaltedDepositPostRegolith),
96            OpTransactionError::Base(err) => Err(err),
97        }
98    }
99}
100
101/// Transaction conditional related errors.
102#[derive(Debug, thiserror::Error)]
103pub enum TxConditionalErr {
104    /// Transaction conditional cost exceeded maximum allowed
105    #[error("conditional cost exceeded maximum allowed")]
106    ConditionalCostExceeded,
107    /// Invalid conditional parameters
108    #[error("invalid conditional parameters")]
109    InvalidCondition,
110    /// Internal error
111    #[error("internal error: {0}")]
112    Internal(String),
113    /// Thrown if the conditional's storage value doesn't match the latest state's.
114    #[error("storage value mismatch")]
115    StorageValueMismatch,
116    /// Thrown when the conditional's storage root doesn't match the latest state's root.
117    #[error("storage root mismatch")]
118    StorageRootMismatch,
119}
120
121impl TxConditionalErr {
122    /// Creates an internal error variant
123    pub fn internal<E: Display>(err: E) -> Self {
124        Self::Internal(err.to_string())
125    }
126}
127
128impl From<TxConditionalErr> for jsonrpsee_types::error::ErrorObject<'static> {
129    fn from(err: TxConditionalErr) -> Self {
130        let code = match &err {
131            TxConditionalErr::Internal(_) => INTERNAL_ERROR_CODE,
132            _ => INVALID_PARAMS_CODE,
133        };
134
135        jsonrpsee_types::error::ErrorObject::owned(code, err.to_string(), None::<String>)
136    }
137}
138
139/// Error type when interacting with the Sequencer
140#[derive(Debug, thiserror::Error)]
141pub enum SequencerClientError {
142    /// Wrapper around an [`RpcError<TransportErrorKind>`].
143    #[error(transparent)]
144    HttpError(#[from] RpcError<TransportErrorKind>),
145}
146
147impl From<SequencerClientError> for jsonrpsee_types::error::ErrorObject<'static> {
148    fn from(err: SequencerClientError) -> Self {
149        match err {
150            SequencerClientError::HttpError(RpcError::ErrorResp(ErrorPayload {
151                code,
152                message,
153                data,
154            })) => jsonrpsee_types::error::ErrorObject::owned(code as i32, message, data),
155            err => jsonrpsee_types::error::ErrorObject::owned(
156                INTERNAL_ERROR_CODE,
157                err.to_string(),
158                None::<String>,
159            ),
160        }
161    }
162}
163
164impl<T> From<EVMError<T, OpTransactionError>> for OpEthApiError
165where
166    T: Into<EthApiError>,
167{
168    fn from(error: EVMError<T, OpTransactionError>) -> Self {
169        match error {
170            EVMError::Transaction(err) => match err.try_into() {
171                Ok(err) => Self::InvalidTransaction(err),
172                Err(err) => Self::Eth(EthApiError::InvalidTransaction(err.into())),
173            },
174            EVMError::Database(err) => Self::Eth(err.into()),
175            EVMError::Header(err) => Self::Eth(err.into()),
176            EVMError::Custom(err) => Self::Eth(EthApiError::EvmCustom(err)),
177        }
178    }
179}
180
181impl FromEvmHalt<OpHaltReason> for OpEthApiError {
182    fn from_evm_halt(halt: OpHaltReason, gas_limit: u64) -> Self {
183        match halt {
184            OpHaltReason::FailedDeposit => {
185                OpInvalidTransactionError::HaltedDepositPostRegolith.into()
186            }
187            OpHaltReason::Base(halt) => EthApiError::from_evm_halt(halt, gas_limit).into(),
188        }
189    }
190}
191
192impl From<TransactionConversionError> for OpEthApiError {
193    fn from(value: TransactionConversionError) -> Self {
194        Self::Eth(EthApiError::from(value))
195    }
196}
197
198impl From<EthTxEnvError> for OpEthApiError {
199    fn from(value: EthTxEnvError) -> Self {
200        Self::Eth(EthApiError::from(value))
201    }
202}
203
204impl From<ProviderError> for OpEthApiError {
205    fn from(value: ProviderError) -> Self {
206        Self::Eth(EthApiError::from(value))
207    }
208}
209
210impl From<BlockError> for OpEthApiError {
211    fn from(value: BlockError) -> Self {
212        Self::Eth(EthApiError::from(value))
213    }
214}
215
216impl From<Infallible> for OpEthApiError {
217    fn from(value: Infallible) -> Self {
218        match value {}
219    }
220}