diff --git a/crates/primitives/src/transaction/access_list.rs b/crates/primitives/src/transaction/access_list.rs index 636a910a2a..3053f09bab 100644 --- a/crates/primitives/src/transaction/access_list.rs +++ b/crates/primitives/src/transaction/access_list.rs @@ -3,6 +3,20 @@ /// Re-export from `alloy_eips`. #[doc(inline)] pub use alloy_eips::eip2930::{AccessList, AccessListItem}; +use revm_primitives::U256; +use serde::{Deserialize, Serialize}; + +/// `AccessListResult` for handling errors from `eth_createAccessList` +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct AccessListResult { + /// List with accounts accessed during transaction. + pub access_list: AccessList, + /// Estimated gas used with access list. + pub gas_used: U256, + /// Optional error message if the transaction failed. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, +} #[cfg(test)] mod tests { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 67322461f5..cf7ac10766 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -15,7 +15,7 @@ use once_cell::sync::Lazy; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; -pub use access_list::{AccessList, AccessListItem}; +pub use access_list::{AccessList, AccessListItem, AccessListResult}; pub use eip1559::TxEip1559; pub use eip2930::TxEip2930; pub use eip4844::TxEip4844; @@ -41,7 +41,7 @@ pub use tx_type::{ }; pub use variant::TransactionSignedVariant; -mod access_list; +pub(crate) mod access_list; mod compat; mod eip1559; mod eip2930; diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index 058e814b47..d77f15cf42 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -3,14 +3,17 @@ use alloy_dyn_abi::TypedData; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use reth_primitives::{Account, Address, BlockId, BlockNumberOrTag, Bytes, B256, B64, U256, U64}; +use reth_primitives::{ + transaction::AccessListResult, Account, Address, BlockId, BlockNumberOrTag, Bytes, B256, B64, + U256, U64, +}; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; use reth_rpc_types::{ serde_helpers::JsonStorageKey, state::{EvmOverrides, StateOverride}, - AccessListWithGasUsed, AnyTransactionReceipt, BlockOverrides, Bundle, - EIP1186AccountProofResponse, EthCallResponse, FeeHistory, Header, Index, RichBlock, - StateContext, SyncStatus, Transaction, TransactionRequest, Work, + AnyTransactionReceipt, BlockOverrides, Bundle, EIP1186AccountProofResponse, EthCallResponse, + FeeHistory, Header, Index, RichBlock, StateContext, SyncStatus, Transaction, + TransactionRequest, Work, }; use tracing::trace; @@ -229,7 +232,7 @@ pub trait EthApi { &self, request: TransactionRequest, block_number: Option, - ) -> RpcResult; + ) -> RpcResult; /// Generates and returns an estimate of how much gas is necessary to allow the transaction to /// complete. @@ -594,7 +597,7 @@ where &self, request: TransactionRequest, block_number: Option, - ) -> RpcResult { + ) -> RpcResult { trace!(target: "rpc::eth", ?request, ?block_number, "Serving eth_createAccessList"); Ok(EthCall::create_access_list_at(self, request, block_number).await?) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 4bf637f461..dee74fb6cf 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -8,6 +8,7 @@ use reth_primitives::{ BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HaltReason, ResultAndState, TransactTo, TxEnv, }, + transaction::AccessListResult, Bytes, TransactionSignedEcRecovered, TxKind, B256, U256, }; use reth_provider::{ChainSpecProvider, StateProvider}; @@ -26,8 +27,7 @@ use reth_rpc_server_types::constants::gas_oracle::{ }; use reth_rpc_types::{ state::{EvmOverrides, StateOverride}, - AccessListWithGasUsed, BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo, - TransactionRequest, + BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo, TransactionRequest, }; use revm::{Database, DatabaseCommit}; use revm_inspectors::access_list::AccessListInspector; @@ -179,13 +179,13 @@ pub trait EthCall: Call + LoadPendingBlock { } } - /// Creates [`AccessListWithGasUsed`] for the [`TransactionRequest`] at the given + /// Creates [`AccessListResult`] for the [`TransactionRequest`] at the given /// [`BlockId`], or latest block. fn create_access_list_at( &self, request: TransactionRequest, block_number: Option, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Trace, { @@ -200,7 +200,7 @@ pub trait EthCall: Call + LoadPendingBlock { } } - /// Creates [`AccessListWithGasUsed`] for the [`TransactionRequest`] at the given + /// Creates [`AccessListResult`] for the [`TransactionRequest`] at the given /// [`BlockId`]. fn create_access_list_with( &self, @@ -208,7 +208,7 @@ pub trait EthCall: Call + LoadPendingBlock { block: BlockEnv, at: BlockId, mut request: TransactionRequest, - ) -> Result + ) -> Result where Self: Trace, { @@ -246,21 +246,22 @@ pub trait EthCall: Call + LoadPendingBlock { let precompiles = get_precompiles(env.handler_cfg.spec_id); let mut inspector = AccessListInspector::new(initial, from, to, precompiles); + let (result, env) = self.inspect(&mut db, env, &mut inspector)?; + let access_list = inspector.into_access_list(); match result.result { - ExecutionResult::Halt { reason, .. } => Err(match reason { - HaltReason::NonceOverflow => RpcInvalidTransactionError::NonceMaxValue, - halt => RpcInvalidTransactionError::EvmHalt(halt), - }), - ExecutionResult::Revert { output, .. } => { - Err(RpcInvalidTransactionError::Revert(RevertError::new(output))) + ExecutionResult::Halt { reason, gas_used } => { + let error = + Some(RpcInvalidTransactionError::halt(reason, env.tx.gas_limit).to_string()); + return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error }) } - ExecutionResult::Success { .. } => Ok(()), - } - .map_err(Self::Error::from_eth_err)?; - - let access_list = inspector.into_access_list(); + ExecutionResult::Revert { output, gas_used } => { + let error = Some(RevertError::new(output).to_string()); + return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error }) + } + ExecutionResult::Success { .. } => {} + }; let cfg_with_spec_id = CfgEnvWithHandlerCfg { cfg_env: env.cfg.clone(), handler_cfg: env.handler_cfg }; @@ -270,7 +271,7 @@ pub trait EthCall: Call + LoadPendingBlock { let gas_used = self.estimate_gas_with(cfg_with_spec_id, env.block.clone(), request, &*db.db, None)?; - Ok(AccessListWithGasUsed { access_list, gas_used }) + Ok(AccessListResult { access_list, gas_used, error: None }) } }