reth_optimism_rpc/
error.rs

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