mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: abstract RPC error over HaltReason (#14104)
This commit is contained in:
@ -28,7 +28,7 @@ use reth_primitives_traits::transaction::execute::FillTxEnv;
|
||||
use reth_revm::{inspector_handle_register, EvmBuilder};
|
||||
use revm_primitives::{
|
||||
AnalysisKind, BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, CfgEnvWithHandlerCfg, EVMError,
|
||||
HandlerCfg, ResultAndState, SpecId, TxEnv, TxKind,
|
||||
HaltReason, HandlerCfg, ResultAndState, SpecId, TxEnv, TxKind,
|
||||
};
|
||||
|
||||
mod config;
|
||||
@ -53,6 +53,7 @@ impl<EXT, DB: Database> Evm for EthEvm<'_, EXT, DB> {
|
||||
type DB = DB;
|
||||
type Tx = TxEnv;
|
||||
type Error = EVMError<DB::Error>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn block(&self) -> &BlockEnv {
|
||||
self.0.block()
|
||||
@ -238,6 +239,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
|
||||
impl ConfigureEvm for EthEvmConfig {
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> {
|
||||
let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg {
|
||||
|
||||
@ -54,8 +54,13 @@ pub trait Evm {
|
||||
type DB;
|
||||
/// Transaction environment
|
||||
type Tx;
|
||||
/// Error type.
|
||||
/// Error type returned by EVM. Contains either errors related to invalid transactions or
|
||||
/// internal irrecoverable execution errors.
|
||||
type Error;
|
||||
/// Halt reason. Enum over all possible reasons for halting the execution. When execution halts,
|
||||
/// it means that transaction is valid, however, it's execution was interrupted (e.g because of
|
||||
/// running out of gas or overflowing stack).
|
||||
type HaltReason;
|
||||
|
||||
/// Reference to [`BlockEnv`].
|
||||
fn block(&self) -> &BlockEnv;
|
||||
@ -96,11 +101,15 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
|
||||
Tx = Self::TxEnv,
|
||||
DB = DB,
|
||||
Error = Self::EvmError<DB::Error>,
|
||||
HaltReason = Self::HaltReason,
|
||||
>;
|
||||
|
||||
/// The error type returned by the EVM.
|
||||
/// The error type returned by the EVM. See [`Evm::Error`].
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static>: EvmError;
|
||||
|
||||
/// Halt reason type returned by the EVM. See [`Evm::HaltReason`].
|
||||
type HaltReason;
|
||||
|
||||
/// Returns a new EVM with the given database configured with the given environment settings,
|
||||
/// including the spec id and transaction environment.
|
||||
///
|
||||
@ -147,6 +156,7 @@ where
|
||||
{
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = T::Evm<'a, DB, I>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = T::EvmError<DBError>;
|
||||
type HaltReason = T::HaltReason;
|
||||
|
||||
fn evm_for_block<DB: Database>(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> {
|
||||
(*self).evm_for_block(db, header)
|
||||
|
||||
@ -40,8 +40,8 @@ pub use receipts::*;
|
||||
mod error;
|
||||
pub use error::OpBlockExecutionError;
|
||||
use revm_primitives::{
|
||||
BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, EVMError, HandlerCfg, OptimismFields,
|
||||
ResultAndState, SpecId, TxKind,
|
||||
BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, EVMError, HaltReason, HandlerCfg,
|
||||
OptimismFields, ResultAndState, SpecId, TxKind,
|
||||
};
|
||||
|
||||
/// OP EVM implementation.
|
||||
@ -53,6 +53,7 @@ impl<EXT, DB: Database> Evm for OpEvm<'_, EXT, DB> {
|
||||
type DB = DB;
|
||||
type Tx = TxEnv;
|
||||
type Error = EVMError<DB::Error>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn block(&self) -> &BlockEnv {
|
||||
self.0.block()
|
||||
@ -225,6 +226,7 @@ impl ConfigureEvmEnv for OpEvmConfig {
|
||||
impl ConfigureEvm for OpEvmConfig {
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = OpEvm<'a, I, DB>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> {
|
||||
let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg {
|
||||
|
||||
@ -4,9 +4,9 @@ use alloy_rpc_types_eth::{error::EthRpcErrorCode, BlockError};
|
||||
use jsonrpsee_types::error::INTERNAL_ERROR_CODE;
|
||||
use reth_optimism_evm::OpBlockExecutionError;
|
||||
use reth_rpc_eth_api::AsEthApiError;
|
||||
use reth_rpc_eth_types::EthApiError;
|
||||
use reth_rpc_eth_types::{error::api::FromEvmHalt, EthApiError};
|
||||
use reth_rpc_server_types::result::{internal_rpc_err, rpc_err};
|
||||
use revm::primitives::{EVMError, InvalidTransaction, OptimismInvalidTransaction};
|
||||
use revm::primitives::{EVMError, HaltReason, InvalidTransaction, OptimismInvalidTransaction};
|
||||
|
||||
/// Optimism specific errors, that extend [`EthApiError`].
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -128,3 +128,9 @@ where
|
||||
Self::Eth(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromEvmHalt for OpEthApiError {
|
||||
fn from_evm_halt(halt: HaltReason, gas_limit: u64) -> Self {
|
||||
EthApiError::from_evm_halt(halt, gas_limit).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ use reth_revm::{
|
||||
};
|
||||
use reth_rpc_eth_types::{
|
||||
cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
|
||||
error::ensure_success,
|
||||
error::{api::FromEvmHalt, ensure_success},
|
||||
revm_utils::{
|
||||
apply_block_overrides, apply_state_overrides, caller_gas_allowance, get_precompiles,
|
||||
},
|
||||
@ -441,7 +441,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
match result.result {
|
||||
ExecutionResult::Halt { reason, gas_used } => {
|
||||
let error =
|
||||
Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit()).to_string());
|
||||
Some(Self::Error::from_evm_halt(reason, tx_env.gas_limit()).to_string());
|
||||
return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error })
|
||||
}
|
||||
ExecutionResult::Revert { output, gas_used } => {
|
||||
@ -456,7 +456,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
let res = match result.result {
|
||||
ExecutionResult::Halt { reason, gas_used } => {
|
||||
let error =
|
||||
Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit()).to_string());
|
||||
Some(Self::Error::from_evm_halt(reason, tx_env.gas_limit()).to_string());
|
||||
AccessListResult { access_list, gas_used: U256::from(gas_used), error }
|
||||
}
|
||||
ExecutionResult::Revert { output, gas_used } => {
|
||||
|
||||
@ -9,12 +9,9 @@ use reth_chainspec::MIN_TRANSACTION_GAS;
|
||||
use reth_errors::ProviderError;
|
||||
use reth_evm::{env::EvmEnv, ConfigureEvmEnv, Database, TransactionEnv};
|
||||
use reth_provider::StateProvider;
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
db::CacheDB,
|
||||
primitives::{ExecutionResult, HaltReason},
|
||||
};
|
||||
use reth_revm::{database::StateProviderDatabase, db::CacheDB, primitives::ExecutionResult};
|
||||
use reth_rpc_eth_types::{
|
||||
error::api::FromEvmHalt,
|
||||
revm_utils::{apply_state_overrides, caller_gas_allowance},
|
||||
EthApiError, RevertError, RpcInvalidTransactionError,
|
||||
};
|
||||
@ -148,10 +145,10 @@ pub trait EstimateCall: Call {
|
||||
|
||||
let gas_refund = match res.result {
|
||||
ExecutionResult::Success { gas_refunded, .. } => gas_refunded,
|
||||
ExecutionResult::Halt { reason, gas_used } => {
|
||||
ExecutionResult::Halt { reason, .. } => {
|
||||
// here we don't check for invalid opcode because already executed with highest gas
|
||||
// limit
|
||||
return Err(RpcInvalidTransactionError::halt(reason, gas_used).into_eth_err())
|
||||
return Err(Self::Error::from_evm_halt(reason, tx_env.gas_limit()))
|
||||
}
|
||||
ExecutionResult::Revert { output, .. } => {
|
||||
// if price or limit was included in the request then we can execute the request
|
||||
@ -330,32 +327,17 @@ pub fn update_estimated_gas_range(
|
||||
// Cap the highest gas limit with the succeeding gas limit.
|
||||
*highest_gas_limit = tx_gas_limit;
|
||||
}
|
||||
ExecutionResult::Revert { .. } => {
|
||||
// Increase the lowest gas limit.
|
||||
ExecutionResult::Revert { .. } | ExecutionResult::Halt { .. } => {
|
||||
// We know that transaction succeeded with a higher gas limit before, so any failure
|
||||
// means that we need to increase it.
|
||||
//
|
||||
// We are ignoring all halts here, and not just OOG errors because there are cases when
|
||||
// non-OOG halt might flag insufficient gas limit as well.
|
||||
//
|
||||
// Common usage of invalid opcode in OpenZeppelin:
|
||||
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/94697be8a3f0dfcd95dfb13ffbd39b5973f5c65d/contracts/metatx/ERC2771Forwarder.sol#L360-L367>
|
||||
*lowest_gas_limit = tx_gas_limit;
|
||||
}
|
||||
ExecutionResult::Halt { reason, .. } => {
|
||||
match reason {
|
||||
HaltReason::OutOfGas(_) | HaltReason::InvalidFEOpcode => {
|
||||
// Both `OutOfGas` and `InvalidEFOpcode` can occur dynamically if the gas
|
||||
// left is too low. Treat this as an out of gas
|
||||
// condition, knowing that the call succeeds with a
|
||||
// higher gas limit.
|
||||
//
|
||||
// Common usage of invalid opcode in OpenZeppelin:
|
||||
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/94697be8a3f0dfcd95dfb13ffbd39b5973f5c65d/contracts/metatx/ERC2771Forwarder.sol#L360-L367>
|
||||
|
||||
// Increase the lowest gas limit.
|
||||
*lowest_gas_limit = tx_gas_limit;
|
||||
}
|
||||
err => {
|
||||
// These cases should be unreachable because we know the transaction
|
||||
// succeeds, but if they occur, treat them as an
|
||||
// error.
|
||||
return Err(RpcInvalidTransactionError::EvmHalt(err).into_eth_err())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
//! Helper traits to wrap generic l1 errors, in network specific error type configured in
|
||||
//! `reth_rpc_eth_api::EthApiTypes`.
|
||||
|
||||
use crate::{EthApiError, RpcInvalidTransactionError};
|
||||
use reth_errors::ProviderError;
|
||||
use reth_evm::ConfigureEvm;
|
||||
|
||||
use crate::EthApiError;
|
||||
use revm_primitives::HaltReason;
|
||||
|
||||
/// Helper trait to wrap core [`EthApiError`].
|
||||
pub trait FromEthApiError: From<EthApiError> {
|
||||
@ -53,7 +53,7 @@ pub trait AsEthApiError {
|
||||
fn as_err(&self) -> Option<&EthApiError>;
|
||||
|
||||
/// Returns `true` if error is
|
||||
/// [`RpcInvalidTransactionError::GasTooHigh`](crate::RpcInvalidTransactionError::GasTooHigh).
|
||||
/// [`RpcInvalidTransactionError::GasTooHigh`].
|
||||
fn is_gas_too_high(&self) -> bool {
|
||||
if let Some(err) = self.as_err() {
|
||||
return err.is_gas_too_high()
|
||||
@ -63,7 +63,7 @@ pub trait AsEthApiError {
|
||||
}
|
||||
|
||||
/// Returns `true` if error is
|
||||
/// [`RpcInvalidTransactionError::GasTooLow`](crate::RpcInvalidTransactionError::GasTooLow).
|
||||
/// [`RpcInvalidTransactionError::GasTooLow`].
|
||||
fn is_gas_too_low(&self) -> bool {
|
||||
if let Some(err) = self.as_err() {
|
||||
return err.is_gas_too_low()
|
||||
@ -80,7 +80,9 @@ impl AsEthApiError for EthApiError {
|
||||
}
|
||||
|
||||
/// Helper trait to convert from revm errors.
|
||||
pub trait FromEvmError<Evm: ConfigureEvm>: From<Evm::EvmError<ProviderError>> {
|
||||
pub trait FromEvmError<Evm: ConfigureEvm>:
|
||||
From<Evm::EvmError<ProviderError>> + FromEvmHalt
|
||||
{
|
||||
/// Converts from EVM error to this type.
|
||||
fn from_evm_err(err: Evm::EvmError<ProviderError>) -> Self {
|
||||
err.into()
|
||||
@ -89,7 +91,19 @@ pub trait FromEvmError<Evm: ConfigureEvm>: From<Evm::EvmError<ProviderError>> {
|
||||
|
||||
impl<T, Evm> FromEvmError<Evm> for T
|
||||
where
|
||||
T: From<Evm::EvmError<ProviderError>>,
|
||||
T: From<Evm::EvmError<ProviderError>> + FromEvmHalt,
|
||||
Evm: ConfigureEvm,
|
||||
{
|
||||
}
|
||||
|
||||
/// Helper trait to convert from revm errors.
|
||||
pub trait FromEvmHalt<Halt = HaltReason> {
|
||||
/// Converts from EVM halt to this type.
|
||||
fn from_evm_halt(halt: Halt, gas_limit: u64) -> Self;
|
||||
}
|
||||
|
||||
impl FromEvmHalt for EthApiError {
|
||||
fn from_evm_halt(halt: HaltReason, gas_limit: u64) -> Self {
|
||||
RpcInvalidTransactionError::halt(halt, gas_limit).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,11 @@ use revm::Database;
|
||||
use revm_primitives::{Address, Bytes, ExecutionResult, TxKind};
|
||||
|
||||
use crate::{
|
||||
error::{api::FromEthApiError, ToRpcError},
|
||||
EthApiError, RevertError, RpcInvalidTransactionError,
|
||||
error::{
|
||||
api::{FromEthApiError, FromEvmHalt},
|
||||
ToRpcError,
|
||||
},
|
||||
EthApiError, RevertError,
|
||||
};
|
||||
|
||||
/// Errors which may occur during `eth_simulateV1` execution.
|
||||
@ -118,7 +121,7 @@ pub fn build_simulated_block<T, B>(
|
||||
block: B,
|
||||
) -> Result<SimulatedBlock<Block<T::Transaction, Header<B::Header>>>, T::Error>
|
||||
where
|
||||
T: TransactionCompat<BlockTx<B>, Error: FromEthApiError>,
|
||||
T: TransactionCompat<BlockTx<B>, Error: FromEthApiError + FromEvmHalt>,
|
||||
B: reth_primitives_traits::Block,
|
||||
{
|
||||
let mut calls: Vec<SimCallResult> = Vec::with_capacity(results.len());
|
||||
@ -127,12 +130,12 @@ where
|
||||
for (index, (result, tx)) in results.iter().zip(block.body().transactions()).enumerate() {
|
||||
let call = match result {
|
||||
ExecutionResult::Halt { reason, gas_used } => {
|
||||
let error = RpcInvalidTransactionError::halt(*reason, tx.gas_limit());
|
||||
let error = T::Error::from_evm_halt(*reason, tx.gas_limit());
|
||||
SimCallResult {
|
||||
return_data: Bytes::new(),
|
||||
error: Some(SimulateError {
|
||||
code: error.error_code(),
|
||||
message: error.to_string(),
|
||||
code: error.into().code(),
|
||||
}),
|
||||
gas_used: *gas_used,
|
||||
logs: Vec::new(),
|
||||
|
||||
@ -16,7 +16,8 @@ use reth::{
|
||||
inspector_handle_register,
|
||||
precompile::{Precompile, PrecompileOutput, PrecompileSpecId},
|
||||
primitives::{
|
||||
CfgEnvWithHandlerCfg, EVMError, Env, HandlerCfg, PrecompileResult, SpecId, TxEnv,
|
||||
CfgEnvWithHandlerCfg, EVMError, Env, HaltReason, HandlerCfg, PrecompileResult, SpecId,
|
||||
TxEnv,
|
||||
},
|
||||
ContextPrecompiles, EvmBuilder, GetInspector,
|
||||
},
|
||||
@ -112,6 +113,7 @@ impl ConfigureEvmEnv for MyEvmConfig {
|
||||
impl ConfigureEvm for MyEvmConfig {
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> {
|
||||
let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg {
|
||||
|
||||
@ -14,7 +14,7 @@ use reth::{
|
||||
inspector_handle_register,
|
||||
precompile::{Precompile, PrecompileSpecId},
|
||||
primitives::{
|
||||
CfgEnvWithHandlerCfg, EVMError, Env, HandlerCfg, PrecompileResult, SpecId,
|
||||
CfgEnvWithHandlerCfg, EVMError, Env, HaltReason, HandlerCfg, PrecompileResult, SpecId,
|
||||
StatefulPrecompileMut, TxEnv,
|
||||
},
|
||||
ContextPrecompile, ContextPrecompiles, EvmBuilder, GetInspector,
|
||||
@ -174,6 +174,7 @@ impl ConfigureEvmEnv for MyEvmConfig {
|
||||
impl ConfigureEvm for MyEvmConfig {
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> {
|
||||
let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg {
|
||||
|
||||
Reference in New Issue
Block a user