reth_optimism_rpc/
error.rs

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