reth_optimism_rpc/
error.rs

1//! RPC errors specific to OP.
2
3use alloy_rpc_types_eth::{error::EthRpcErrorCode, BlockError};
4use alloy_transport::{RpcError, TransportErrorKind};
5use jsonrpsee_types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE};
6use op_revm::{OpHaltReason, OpTransactionError};
7use reth_optimism_evm::OpBlockExecutionError;
8use reth_rpc_eth_api::AsEthApiError;
9use reth_rpc_eth_types::{error::api::FromEvmHalt, EthApiError};
10use reth_rpc_server_types::result::{internal_rpc_err, rpc_err};
11use revm::context_interface::result::{EVMError, InvalidTransaction};
12use std::fmt::Display;
13
14/// Optimism specific errors, that extend [`EthApiError`].
15#[derive(Debug, thiserror::Error)]
16pub enum OpEthApiError {
17    /// L1 ethereum error.
18    #[error(transparent)]
19    Eth(#[from] EthApiError),
20    /// EVM error originating from invalid optimism data.
21    #[error(transparent)]
22    Evm(#[from] OpBlockExecutionError),
23    /// Thrown when calculating L1 gas fee.
24    #[error("failed to calculate l1 gas fee")]
25    L1BlockFeeError,
26    /// Thrown when calculating L1 gas used
27    #[error("failed to calculate l1 gas used")]
28    L1BlockGasError,
29    /// Wrapper for [`revm_primitives::InvalidTransaction`](InvalidTransaction).
30    #[error(transparent)]
31    InvalidTransaction(#[from] OpInvalidTransactionError),
32    /// Sequencer client error.
33    #[error(transparent)]
34    Sequencer(#[from] SequencerClientError),
35}
36
37impl AsEthApiError for OpEthApiError {
38    fn as_err(&self) -> Option<&EthApiError> {
39        match self {
40            Self::Eth(err) => Some(err),
41            _ => None,
42        }
43    }
44}
45
46impl From<OpEthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
47    fn from(err: OpEthApiError) -> Self {
48        match err {
49            OpEthApiError::Eth(err) => err.into(),
50            OpEthApiError::InvalidTransaction(err) => err.into(),
51            OpEthApiError::Evm(_) |
52            OpEthApiError::L1BlockFeeError |
53            OpEthApiError::L1BlockGasError => internal_rpc_err(err.to_string()),
54            OpEthApiError::Sequencer(err) => err.into(),
55        }
56    }
57}
58
59/// Optimism specific invalid transaction errors
60#[derive(thiserror::Error, Debug)]
61pub enum OpInvalidTransactionError {
62    /// A deposit transaction was submitted as a system transaction post-regolith.
63    #[error("no system transactions allowed after regolith")]
64    DepositSystemTxPostRegolith,
65    /// A deposit transaction halted post-regolith
66    #[error("deposit transaction halted after regolith")]
67    HaltedDepositPostRegolith,
68    /// Transaction conditional errors.
69    #[error(transparent)]
70    TxConditionalErr(#[from] TxConditionalErr),
71}
72
73impl From<OpInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
74    fn from(err: OpInvalidTransactionError) -> Self {
75        match err {
76            OpInvalidTransactionError::DepositSystemTxPostRegolith |
77            OpInvalidTransactionError::HaltedDepositPostRegolith => {
78                rpc_err(EthRpcErrorCode::TransactionRejected.code(), err.to_string(), None)
79            }
80            OpInvalidTransactionError::TxConditionalErr(_) => err.into(),
81        }
82    }
83}
84
85impl TryFrom<OpTransactionError> for OpInvalidTransactionError {
86    type Error = InvalidTransaction;
87
88    fn try_from(err: OpTransactionError) -> Result<Self, Self::Error> {
89        match err {
90            OpTransactionError::DepositSystemTxPostRegolith => {
91                Ok(Self::DepositSystemTxPostRegolith)
92            }
93            OpTransactionError::HaltedDepositPostRegolith => Ok(Self::HaltedDepositPostRegolith),
94            OpTransactionError::Base(err) => Err(err),
95        }
96    }
97}
98
99/// Transaction conditional related errors.
100#[derive(Debug, thiserror::Error)]
101pub enum TxConditionalErr {
102    /// Transaction conditional cost exceeded maximum allowed
103    #[error("conditional cost exceeded maximum allowed")]
104    ConditionalCostExceeded,
105    /// Invalid conditional parameters
106    #[error("invalid conditional parameters")]
107    InvalidCondition,
108    /// Internal error
109    #[error("internal error: {0}")]
110    Internal(String),
111    /// Thrown if the conditional's storage value doesn't match the latest state's.
112    #[error("storage value mismatch")]
113    StorageValueMismatch,
114    /// Thrown when the conditional's storage root doesn't match the latest state's root.
115    #[error("storage root mismatch")]
116    StorageRootMismatch,
117}
118
119impl TxConditionalErr {
120    /// Creates an internal error variant
121    pub fn internal<E: Display>(err: E) -> Self {
122        Self::Internal(err.to_string())
123    }
124}
125
126impl From<TxConditionalErr> for jsonrpsee_types::error::ErrorObject<'static> {
127    fn from(err: TxConditionalErr) -> Self {
128        let code = match &err {
129            TxConditionalErr::Internal(_) => INTERNAL_ERROR_CODE,
130            _ => INVALID_PARAMS_CODE,
131        };
132
133        jsonrpsee_types::error::ErrorObject::owned(code, err.to_string(), None::<String>)
134    }
135}
136
137/// Error type when interacting with the Sequencer
138#[derive(Debug, thiserror::Error)]
139pub enum SequencerClientError {
140    /// Wrapper around an [`RpcError<TransportErrorKind>`].
141    #[error(transparent)]
142    HttpError(#[from] RpcError<TransportErrorKind>),
143    /// Thrown when serializing transaction to forward to sequencer
144    #[error("invalid sequencer transaction")]
145    InvalidSequencerTransaction,
146}
147
148impl From<SequencerClientError> for jsonrpsee_types::error::ErrorObject<'static> {
149    fn from(err: SequencerClientError) -> Self {
150        jsonrpsee_types::error::ErrorObject::owned(
151            INTERNAL_ERROR_CODE,
152            err.to_string(),
153            None::<String>,
154        )
155    }
156}
157
158impl From<BlockError> for OpEthApiError {
159    fn from(error: BlockError) -> Self {
160        Self::Eth(error.into())
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}