mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: generic TxEnv (#13957)
This commit is contained in:
@ -19,7 +19,7 @@ use reth_evm::{
|
||||
},
|
||||
state_change::post_block_balance_increments,
|
||||
system_calls::{OnStateHook, SystemCaller},
|
||||
ConfigureEvm, Evm, TxEnvOverrides,
|
||||
ConfigureEvm, Evm,
|
||||
};
|
||||
use reth_primitives::{EthPrimitives, Receipt, RecoveredBlock};
|
||||
use reth_primitives_traits::{BlockBody, SignedTransaction};
|
||||
@ -94,8 +94,6 @@ where
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
/// How to create an EVM.
|
||||
evm_config: EvmConfig,
|
||||
/// Optional overrides for the transactions environment.
|
||||
tx_env_overrides: Option<Box<dyn TxEnvOverrides>>,
|
||||
/// Current state for block execution.
|
||||
state: State<DB>,
|
||||
/// Utility to call system smart contracts.
|
||||
@ -109,7 +107,7 @@ where
|
||||
/// Creates a new [`EthExecutionStrategy`]
|
||||
pub fn new(state: State<DB>, chain_spec: Arc<ChainSpec>, evm_config: EvmConfig) -> Self {
|
||||
let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
|
||||
Self { state, chain_spec, evm_config, system_caller, tx_env_overrides: None }
|
||||
Self { state, chain_spec, evm_config, system_caller }
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,13 +121,8 @@ where
|
||||
{
|
||||
type DB = DB;
|
||||
type Error = BlockExecutionError;
|
||||
|
||||
type Primitives = EthPrimitives;
|
||||
|
||||
fn init(&mut self, tx_env_overrides: Box<dyn TxEnvOverrides>) {
|
||||
self.tx_env_overrides = Some(tx_env_overrides);
|
||||
}
|
||||
|
||||
fn apply_pre_execution_changes(
|
||||
&mut self,
|
||||
block: &RecoveredBlock<reth_primitives::Block>,
|
||||
@ -166,11 +159,7 @@ where
|
||||
.into())
|
||||
}
|
||||
|
||||
let mut tx_env = self.evm_config.tx_env(transaction, *sender);
|
||||
|
||||
if let Some(tx_env_overrides) = &mut self.tx_env_overrides {
|
||||
tx_env_overrides.apply(&mut tx_env);
|
||||
}
|
||||
let tx_env = self.evm_config.tx_env(transaction, *sender);
|
||||
|
||||
// Execute transaction.
|
||||
let result_and_state = evm.transact(tx_env).map_err(move |err| {
|
||||
|
||||
@ -150,6 +150,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
|
||||
type Header = Header;
|
||||
type Transaction = TransactionSigned;
|
||||
type Error = Infallible;
|
||||
type TxEnv = TxEnv;
|
||||
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
|
||||
transaction.fill_tx_env(tx_env, sender);
|
||||
|
||||
@ -6,7 +6,6 @@ use crate::{
|
||||
execute::{BatchExecutor, BlockExecutorProvider, Executor},
|
||||
system_calls::OnStateHook,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloy_primitives::BlockNumber;
|
||||
use reth_prune_types::PruneModes;
|
||||
use reth_storage_errors::provider::ProviderError;
|
||||
@ -60,13 +59,6 @@ where
|
||||
type Output = A::Output;
|
||||
type Error = A::Error;
|
||||
|
||||
fn init(&mut self, tx_env_overrides: Box<dyn crate::TxEnvOverrides>) {
|
||||
match self {
|
||||
Self::Left(a) => a.init(tx_env_overrides),
|
||||
Self::Right(b) => b.init(tx_env_overrides),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
|
||||
match self {
|
||||
Self::Left(a) => a.execute(input),
|
||||
|
||||
@ -8,7 +8,7 @@ pub use reth_execution_errors::{
|
||||
pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
|
||||
pub use reth_storage_errors::provider::ProviderError;
|
||||
|
||||
use crate::{system_calls::OnStateHook, TxEnvOverrides};
|
||||
use crate::system_calls::OnStateHook;
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use alloy_eips::eip7685::Requests;
|
||||
use alloy_primitives::{
|
||||
@ -38,9 +38,6 @@ pub trait Executor<DB> {
|
||||
/// The error type returned by the executor.
|
||||
type Error;
|
||||
|
||||
/// Initialize the executor with the given transaction environment overrides.
|
||||
fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}
|
||||
|
||||
/// Consumes the type and executes the block.
|
||||
///
|
||||
/// # Note
|
||||
@ -199,9 +196,6 @@ pub trait BlockExecutionStrategy {
|
||||
/// The error type returned by this strategy's methods.
|
||||
type Error: From<ProviderError> + core::error::Error;
|
||||
|
||||
/// Initialize the strategy with the given transaction environment overrides.
|
||||
fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}
|
||||
|
||||
/// Applies any necessary changes before executing the block's transactions.
|
||||
fn apply_pre_execution_changes(
|
||||
&mut self,
|
||||
@ -341,10 +335,6 @@ where
|
||||
type Output = BlockExecutionOutput<<S::Primitives as NodePrimitives>::Receipt>;
|
||||
type Error = S::Error;
|
||||
|
||||
fn init(&mut self, env_overrides: Box<dyn TxEnvOverrides>) {
|
||||
self.strategy.init(env_overrides);
|
||||
}
|
||||
|
||||
fn execute(mut self, block: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
|
||||
self.strategy.apply_pre_execution_changes(block)?;
|
||||
let ExecuteOutput { receipts, gas_used } = self.strategy.execute_transactions(block)?;
|
||||
@ -518,7 +508,7 @@ mod tests {
|
||||
use reth_chainspec::{ChainSpec, MAINNET};
|
||||
use reth_primitives::EthPrimitives;
|
||||
use revm::db::{CacheDB, EmptyDBTyped};
|
||||
use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY};
|
||||
use revm_primitives::{address, bytes, AccountInfo, KECCAK_EMPTY};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -734,29 +724,6 @@ mod tests {
|
||||
assert_eq!(block_execution_output.state, expected_finish_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tx_env_overrider() {
|
||||
let strategy_factory = TestExecutorStrategyFactory {
|
||||
execute_transactions_result: ExecuteOutput {
|
||||
receipts: vec![Receipt::default()],
|
||||
gas_used: 10,
|
||||
},
|
||||
apply_post_execution_changes_result: Requests::new(vec![bytes!("deadbeef")]),
|
||||
finish_result: BundleState::default(),
|
||||
};
|
||||
let provider = BasicBlockExecutorProvider::new(strategy_factory);
|
||||
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
|
||||
|
||||
// if we want to apply tx env overrides the executor must be mut.
|
||||
let mut executor = provider.executor(db);
|
||||
// execute consumes the executor, so we can only call it once.
|
||||
executor.init(Box::new(|tx_env: &mut TxEnv| {
|
||||
tx_env.nonce.take();
|
||||
}));
|
||||
let result = executor.execute(&Default::default());
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
fn setup_state_with_account(
|
||||
addr: Address,
|
||||
balance: u128,
|
||||
|
||||
@ -17,11 +17,16 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use alloy_consensus::BlockHeader as _;
|
||||
use alloy_eips::eip2930::AccessList;
|
||||
use alloy_primitives::{Address, Bytes, B256, U256};
|
||||
use reth_primitives_traits::{BlockHeader, SignedTransaction};
|
||||
use revm::{Database, DatabaseCommit, GetInspector};
|
||||
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EVMError, ResultAndState, SpecId, TxEnv};
|
||||
use revm_primitives::{
|
||||
BlockEnv, CfgEnvWithHandlerCfg, EVMError, ResultAndState, SpecId, TxEnv, TxKind,
|
||||
};
|
||||
|
||||
pub mod either;
|
||||
/// EVM environment configuration.
|
||||
@ -87,7 +92,11 @@ pub trait Evm {
|
||||
/// Trait for configuring the EVM for executing full blocks.
|
||||
pub trait ConfigureEvm: ConfigureEvmEnv {
|
||||
/// The EVM implementation.
|
||||
type Evm<'a, DB: Database + 'a, I: 'a>: Evm<Tx = TxEnv, DB = DB, Error = EVMError<DB::Error>>;
|
||||
type Evm<'a, DB: Database + 'a, I: 'a>: Evm<
|
||||
Tx = Self::TxEnv,
|
||||
DB = DB,
|
||||
Error = EVMError<DB::Error>,
|
||||
>;
|
||||
|
||||
/// Returns a new EVM with the given database configured with the given environment settings,
|
||||
/// including the spec id and transaction environment.
|
||||
@ -127,7 +136,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
|
||||
impl<'b, T> ConfigureEvm for &'b T
|
||||
where
|
||||
T: ConfigureEvm,
|
||||
&'b T: ConfigureEvmEnv<Header = T::Header>,
|
||||
&'b T: ConfigureEvmEnv<Header = T::Header, TxEnv = T::TxEnv>,
|
||||
{
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = T::Evm<'a, DB, I>;
|
||||
|
||||
@ -165,18 +174,26 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
|
||||
/// The transaction type.
|
||||
type Transaction: SignedTransaction;
|
||||
|
||||
/// Transaction environment used by EVM.
|
||||
type TxEnv: TransactionEnv;
|
||||
|
||||
/// The error type that is returned by [`Self::next_cfg_and_block_env`].
|
||||
type Error: core::error::Error + Send + Sync;
|
||||
|
||||
/// Returns a [`TxEnv`] from a transaction and [`Address`].
|
||||
fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> TxEnv {
|
||||
let mut tx_env = TxEnv::default();
|
||||
fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv {
|
||||
let mut tx_env = Default::default();
|
||||
self.fill_tx_env(&mut tx_env, transaction, signer);
|
||||
tx_env
|
||||
}
|
||||
|
||||
/// Fill transaction environment from a transaction and the given sender address.
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &Self::Transaction, sender: Address);
|
||||
fn fill_tx_env(
|
||||
&self,
|
||||
tx_env: &mut Self::TxEnv,
|
||||
transaction: &Self::Transaction,
|
||||
sender: Address,
|
||||
);
|
||||
|
||||
/// Returns a [`CfgEnvWithHandlerCfg`] for the given header.
|
||||
fn cfg_env(&self, header: &Self::Header) -> CfgEnvWithHandlerCfg {
|
||||
@ -262,17 +279,77 @@ pub struct NextBlockEnvAttributes {
|
||||
pub gas_limit: u64,
|
||||
}
|
||||
|
||||
/// Function hook that allows to modify a transaction environment.
|
||||
pub trait TxEnvOverrides {
|
||||
/// Apply the overrides by modifying the given `TxEnv`.
|
||||
fn apply(&mut self, env: &mut TxEnv);
|
||||
/// Abstraction over transaction environment.
|
||||
pub trait TransactionEnv:
|
||||
Into<revm_primitives::TxEnv> + Debug + Default + Clone + Send + Sync + 'static
|
||||
{
|
||||
/// Returns configured gas limit.
|
||||
fn gas_limit(&self) -> u64;
|
||||
|
||||
/// Set the gas limit.
|
||||
fn set_gas_limit(&mut self, gas_limit: u64);
|
||||
|
||||
/// Set the gas limit.
|
||||
fn with_gas_limit(mut self, gas_limit: u64) -> Self {
|
||||
self.set_gas_limit(gas_limit);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns configured gas price.
|
||||
fn gas_price(&self) -> U256;
|
||||
|
||||
/// Returns configured value.
|
||||
fn value(&self) -> U256;
|
||||
|
||||
/// Caller of the transaction.
|
||||
fn caller(&self) -> Address;
|
||||
|
||||
/// Set access list.
|
||||
fn set_access_list(&mut self, access_list: AccessList);
|
||||
|
||||
/// Set access list.
|
||||
fn with_access_list(mut self, access_list: AccessList) -> Self {
|
||||
self.set_access_list(access_list);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns calldata for the transaction.
|
||||
fn input(&self) -> &Bytes;
|
||||
|
||||
/// Returns [`TxKind`] of the transaction.
|
||||
fn kind(&self) -> TxKind;
|
||||
}
|
||||
|
||||
impl<F> TxEnvOverrides for F
|
||||
where
|
||||
F: FnMut(&mut TxEnv),
|
||||
{
|
||||
fn apply(&mut self, env: &mut TxEnv) {
|
||||
self(env)
|
||||
impl TransactionEnv for TxEnv {
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.gas_limit
|
||||
}
|
||||
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
self.gas_limit = gas_limit;
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> U256 {
|
||||
self.gas_price.to()
|
||||
}
|
||||
|
||||
fn value(&self) -> U256 {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn caller(&self) -> Address {
|
||||
self.caller
|
||||
}
|
||||
|
||||
fn set_access_list(&mut self, access_list: AccessList) {
|
||||
self.access_list = access_list.to_vec();
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
self.transact_to
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ use reth_evm::{
|
||||
},
|
||||
state_change::post_block_balance_increments,
|
||||
system_calls::{OnStateHook, SystemCaller},
|
||||
ConfigureEvmFor, Evm, TxEnvOverrides,
|
||||
ConfigureEvmFor, Evm,
|
||||
};
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_consensus::validate_block_post_execution;
|
||||
@ -104,8 +104,6 @@ where
|
||||
chain_spec: Arc<OpChainSpec>,
|
||||
/// How to create an EVM.
|
||||
evm_config: EvmConfig,
|
||||
/// Optional overrides for the transactions environment.
|
||||
tx_env_overrides: Option<Box<dyn TxEnvOverrides>>,
|
||||
/// Current state for block execution.
|
||||
state: State<DB>,
|
||||
/// Utility to call system smart contracts.
|
||||
@ -127,14 +125,7 @@ where
|
||||
receipt_builder: Arc<dyn OpReceiptBuilder<N::SignedTx, Receipt = N::Receipt>>,
|
||||
) -> Self {
|
||||
let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
|
||||
Self {
|
||||
state,
|
||||
chain_spec,
|
||||
evm_config,
|
||||
system_caller,
|
||||
tx_env_overrides: None,
|
||||
receipt_builder,
|
||||
}
|
||||
Self { state, chain_spec, evm_config, system_caller, receipt_builder }
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,10 +143,6 @@ where
|
||||
type Primitives = N;
|
||||
type Error = BlockExecutionError;
|
||||
|
||||
fn init(&mut self, tx_env_overrides: Box<dyn TxEnvOverrides>) {
|
||||
self.tx_env_overrides = Some(tx_env_overrides);
|
||||
}
|
||||
|
||||
fn apply_pre_execution_changes(
|
||||
&mut self,
|
||||
block: &RecoveredBlock<N::Block>,
|
||||
@ -223,11 +210,7 @@ where
|
||||
.transpose()
|
||||
.map_err(|_| OpBlockExecutionError::AccountLoadFailed(*sender))?;
|
||||
|
||||
let mut tx_env = self.evm_config.tx_env(transaction, *sender);
|
||||
|
||||
if let Some(tx_env_overrides) = &mut self.tx_env_overrides {
|
||||
tx_env_overrides.apply(&mut tx_env);
|
||||
}
|
||||
let tx_env = self.evm_config.tx_env(transaction, *sender);
|
||||
|
||||
// Execute transaction.
|
||||
let result_and_state = evm.transact(tx_env).map_err(move |err| {
|
||||
|
||||
@ -157,6 +157,7 @@ impl ConfigureEvmEnv for OpEvmConfig {
|
||||
type Header = Header;
|
||||
type Transaction = OpTransactionSigned;
|
||||
type Error = EIP1559ParamError;
|
||||
type TxEnv = TxEnv;
|
||||
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &OpTransactionSigned, sender: Address) {
|
||||
transaction.fill_tx_env(tx_env, sender);
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
use op_alloy_consensus::OpPooledTransaction;
|
||||
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
|
||||
use reth_chainspec::{EthChainSpec, Hardforks};
|
||||
use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvmFor};
|
||||
use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvmEnv, ConfigureEvmFor};
|
||||
use reth_network::{NetworkConfig, NetworkHandle, NetworkManager, NetworkPrimitives, PeersInfo};
|
||||
use reth_node_api::{AddOnsContext, FullNodeComponents, NodeAddOns, PrimitivesTy, TxTy};
|
||||
use reth_node_builder::{
|
||||
@ -43,6 +43,7 @@ use reth_transaction_pool::{
|
||||
TransactionValidationTaskExecutor,
|
||||
};
|
||||
use reth_trie_db::MerklePatriciaTrie;
|
||||
use revm::primitives::TxEnv;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Storage implementation for Optimism.
|
||||
@ -190,6 +191,7 @@ where
|
||||
Storage = OpStorage,
|
||||
Engine = OpEngineTypes,
|
||||
>,
|
||||
Evm: ConfigureEvmEnv<TxEnv = TxEnv>,
|
||||
>,
|
||||
{
|
||||
type Handle = RpcHandle<N, OpEthApi<N>>;
|
||||
@ -239,6 +241,7 @@ where
|
||||
Storage = OpStorage,
|
||||
Engine = OpEngineTypes,
|
||||
>,
|
||||
Evm: ConfigureEvmEnv<TxEnv = TxEnv>,
|
||||
>,
|
||||
{
|
||||
type EthApi = OpEthApi<N>;
|
||||
|
||||
@ -28,7 +28,8 @@ where
|
||||
|
||||
impl<N> Call for OpEthApi<N>
|
||||
where
|
||||
Self: LoadState<Evm: ConfigureEvm<Header = ProviderHeader<Self::Provider>>> + SpawnBlocking,
|
||||
Self: LoadState<Evm: ConfigureEvm<Header = ProviderHeader<Self::Provider>, TxEnv = TxEnv>>
|
||||
+ SpawnBlocking,
|
||||
Self::Error: From<OpEthApiError>,
|
||||
N: OpNodeCore,
|
||||
{
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
//! use reth_consensus::{ConsensusError, FullConsensus};
|
||||
//! use reth_engine_primitives::PayloadValidator;
|
||||
//! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm};
|
||||
//! use reth_evm_ethereum::EthEvmConfig;
|
||||
//! use reth_network_api::{NetworkInfo, Peers};
|
||||
//! use reth_primitives::{Header, PooledTransaction, TransactionSigned};
|
||||
//! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider};
|
||||
@ -30,21 +31,12 @@
|
||||
//! use reth_transaction_pool::{PoolTransaction, TransactionPool};
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! pub async fn launch<
|
||||
//! Provider,
|
||||
//! Pool,
|
||||
//! Network,
|
||||
//! Events,
|
||||
//! EvmConfig,
|
||||
//! BlockExecutor,
|
||||
//! Consensus,
|
||||
//! Validator,
|
||||
//! >(
|
||||
//! pub async fn launch<Provider, Pool, Network, Events, BlockExecutor, Consensus, Validator>(
|
||||
//! provider: Provider,
|
||||
//! pool: Pool,
|
||||
//! network: Network,
|
||||
//! events: Events,
|
||||
//! evm_config: EvmConfig,
|
||||
//! evm_config: EthEvmConfig,
|
||||
//! block_executor: BlockExecutor,
|
||||
//! consensus: Consensus,
|
||||
//! validator: Validator,
|
||||
@ -66,7 +58,6 @@
|
||||
//! Network: NetworkInfo + Peers + Clone + 'static,
|
||||
//! Events:
|
||||
//! CanonStateSubscriptions<Primitives = reth_primitives::EthPrimitives> + Clone + 'static,
|
||||
//! EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
|
||||
//! BlockExecutor: BlockExecutorProvider<Primitives = Events::Primitives>,
|
||||
//! Consensus: FullConsensus<Error = ConsensusError> + Clone + 'static,
|
||||
//! Validator: PayloadValidator<Block = reth_primitives::Block>,
|
||||
@ -103,6 +94,7 @@
|
||||
//! use reth_consensus::{ConsensusError, FullConsensus};
|
||||
//! use reth_engine_primitives::{EngineTypes, PayloadValidator};
|
||||
//! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm};
|
||||
//! use reth_evm_ethereum::EthEvmConfig;
|
||||
//! use reth_network_api::{NetworkInfo, Peers};
|
||||
//! use reth_primitives::{Header, PooledTransaction, TransactionSigned};
|
||||
//! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider};
|
||||
@ -125,7 +117,6 @@
|
||||
//! Events,
|
||||
//! EngineApi,
|
||||
//! EngineT,
|
||||
//! EvmConfig,
|
||||
//! BlockExecutor,
|
||||
//! Consensus,
|
||||
//! Validator,
|
||||
@ -135,7 +126,7 @@
|
||||
//! network: Network,
|
||||
//! events: Events,
|
||||
//! engine_api: EngineApi,
|
||||
//! evm_config: EvmConfig,
|
||||
//! evm_config: EthEvmConfig,
|
||||
//! block_executor: BlockExecutor,
|
||||
//! consensus: Consensus,
|
||||
//! validator: Validator,
|
||||
@ -159,7 +150,6 @@
|
||||
//! CanonStateSubscriptions<Primitives = reth_primitives::EthPrimitives> + Clone + 'static,
|
||||
//! EngineApi: EngineApiServer<EngineT>,
|
||||
//! EngineT: EngineTypes,
|
||||
//! EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
|
||||
//! BlockExecutor: BlockExecutorProvider<Primitives = Events::Primitives>,
|
||||
//! Consensus: FullConsensus<Error = ConsensusError> + Clone + 'static,
|
||||
//! Validator: PayloadValidator<Block = reth_primitives::Block>,
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
|
||||
use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
|
||||
use crate::{
|
||||
helpers::estimate::EstimateCall, FromEthApiError, FromEvmError, FullEthApiTypes,
|
||||
IntoEthApiError, RpcBlock, RpcNodeCore,
|
||||
helpers::estimate::EstimateCall, FromEthApiError, FromEvmError, FullEthApiTypes, RpcBlock,
|
||||
RpcNodeCore,
|
||||
};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::{eip1559::calc_next_block_base_fee, eip2930::AccessListResult};
|
||||
@ -17,14 +17,14 @@ use alloy_rpc_types_eth::{
|
||||
};
|
||||
use futures::Future;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm};
|
||||
use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, TransactionEnv};
|
||||
use reth_node_api::BlockBody;
|
||||
use reth_primitives_traits::SignedTransaction;
|
||||
use reth_provider::{BlockIdReader, ChainSpecProvider, ProviderHeader};
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
db::CacheDB,
|
||||
primitives::{BlockEnv, ExecutionResult, ResultAndState, TxEnv},
|
||||
primitives::{BlockEnv, ExecutionResult, ResultAndState},
|
||||
DatabaseRef,
|
||||
};
|
||||
use reth_rpc_eth_types::{
|
||||
@ -32,7 +32,6 @@ use reth_rpc_eth_types::{
|
||||
error::ensure_success,
|
||||
revm_utils::{
|
||||
apply_block_overrides, apply_state_overrides, caller_gas_allowance, get_precompiles,
|
||||
CallFees,
|
||||
},
|
||||
simulate::{self, EthSimulateError},
|
||||
EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb,
|
||||
@ -206,7 +205,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
}
|
||||
|
||||
transactions.push(tx);
|
||||
senders.push(tx_env.caller);
|
||||
senders.push(tx_env.caller());
|
||||
results.push(res.result);
|
||||
}
|
||||
|
||||
@ -410,10 +409,10 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
|
||||
let mut db = CacheDB::new(StateProviderDatabase::new(state));
|
||||
|
||||
if request.gas.is_none() && tx_env.gas_price > U256::ZERO {
|
||||
if request.gas.is_none() && tx_env.gas_price() > U256::ZERO {
|
||||
let cap = caller_gas_allowance(&mut db, &tx_env)?;
|
||||
// no gas limit was provided in the request, so we need to cap the request's gas limit
|
||||
tx_env.gas_limit = cap.min(evm_env.block_env.gas_limit).saturating_to();
|
||||
tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit).saturating_to());
|
||||
}
|
||||
|
||||
let from = request.from.unwrap_or_default();
|
||||
@ -434,11 +433,11 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
let (result, (evm_env, mut tx_env)) =
|
||||
self.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
|
||||
let access_list = inspector.into_access_list();
|
||||
tx_env.access_list = access_list.to_vec();
|
||||
tx_env.set_access_list(access_list.clone());
|
||||
match result.result {
|
||||
ExecutionResult::Halt { reason, gas_used } => {
|
||||
let error =
|
||||
Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit).to_string());
|
||||
Some(RpcInvalidTransactionError::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 } => {
|
||||
@ -453,7 +452,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(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit()).to_string());
|
||||
AccessListResult { access_list, gas_used: U256::from(gas_used), error }
|
||||
}
|
||||
ExecutionResult::Revert { output, gas_used } => {
|
||||
@ -490,14 +489,15 @@ pub trait Call:
|
||||
f(StateProviderTraitObjWrapper(&state))
|
||||
}
|
||||
|
||||
/// Executes the [`TxEnv`] against the given [Database] without committing state
|
||||
/// Executes the `TxEnv` against the given [Database] without committing state
|
||||
/// changes.
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn transact<DB>(
|
||||
&self,
|
||||
db: DB,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error>
|
||||
tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
) -> Result<(ResultAndState, (EvmEnv, <Self::Evm as ConfigureEvmEnv>::TxEnv)), Self::Error>
|
||||
where
|
||||
DB: Database,
|
||||
EthApiError: From<DB::Error>,
|
||||
@ -511,13 +511,14 @@ pub trait Call:
|
||||
|
||||
/// Executes the [`EvmEnv`] against the given [Database] without committing state
|
||||
/// changes.
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn transact_with_inspector<DB>(
|
||||
&self,
|
||||
db: DB,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
inspector: impl GetInspector<DB>,
|
||||
) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error>
|
||||
) -> Result<(ResultAndState, (EvmEnv, <Self::Evm as ConfigureEvmEnv>::TxEnv)), Self::Error>
|
||||
where
|
||||
DB: Database,
|
||||
EthApiError: From<DB::Error>,
|
||||
@ -530,12 +531,18 @@ pub trait Call:
|
||||
}
|
||||
|
||||
/// Executes the call request at the given [`BlockId`].
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn transact_call_at(
|
||||
&self,
|
||||
request: TransactionRequest,
|
||||
at: BlockId,
|
||||
overrides: EvmOverrides,
|
||||
) -> impl Future<Output = Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error>> + Send
|
||||
) -> impl Future<
|
||||
Output = Result<
|
||||
(ResultAndState, (EvmEnv, <Self::Evm as ConfigureEvmEnv>::TxEnv)),
|
||||
Self::Error,
|
||||
>,
|
||||
> + Send
|
||||
where
|
||||
Self: LoadPendingBlock,
|
||||
{
|
||||
@ -585,7 +592,11 @@ pub trait Call:
|
||||
) -> impl Future<Output = Result<R, Self::Error>> + Send
|
||||
where
|
||||
Self: LoadPendingBlock,
|
||||
F: FnOnce(StateCacheDbRefMutWrapper<'_, '_>, EvmEnv, TxEnv) -> Result<R, Self::Error>
|
||||
F: FnOnce(
|
||||
StateCacheDbRefMutWrapper<'_, '_>,
|
||||
EvmEnv,
|
||||
<Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
) -> Result<R, Self::Error>
|
||||
+ Send
|
||||
+ 'static,
|
||||
R: Send + 'static,
|
||||
@ -694,84 +705,15 @@ pub trait Call:
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
/// Configures a new [`TxEnv`] for the [`TransactionRequest`]
|
||||
/// Configures a new `TxEnv` for the [`TransactionRequest`]
|
||||
///
|
||||
/// All [`TxEnv`] fields are derived from the given [`TransactionRequest`], if fields are
|
||||
/// All `TxEnv` fields are derived from the given [`TransactionRequest`], if fields are
|
||||
/// `None`, they fall back to the [`BlockEnv`]'s settings.
|
||||
fn create_txn_env(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
request: TransactionRequest,
|
||||
) -> Result<TxEnv, Self::Error> {
|
||||
// Ensure that if versioned hashes are set, they're not empty
|
||||
if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) {
|
||||
return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err())
|
||||
}
|
||||
|
||||
let TransactionRequest {
|
||||
from,
|
||||
to,
|
||||
gas_price,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
gas,
|
||||
value,
|
||||
input,
|
||||
nonce,
|
||||
access_list,
|
||||
chain_id,
|
||||
blob_versioned_hashes,
|
||||
max_fee_per_blob_gas,
|
||||
authorization_list,
|
||||
transaction_type: _,
|
||||
sidecar: _,
|
||||
} = request;
|
||||
|
||||
let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
|
||||
CallFees::ensure_fees(
|
||||
gas_price.map(U256::from),
|
||||
max_fee_per_gas.map(U256::from),
|
||||
max_priority_fee_per_gas.map(U256::from),
|
||||
block_env.basefee,
|
||||
blob_versioned_hashes.as_deref(),
|
||||
max_fee_per_blob_gas.map(U256::from),
|
||||
block_env.get_blob_gasprice().map(U256::from),
|
||||
)?;
|
||||
|
||||
let gas_limit = gas.unwrap_or_else(|| {
|
||||
// Use maximum allowed gas limit. The reason for this
|
||||
// is that both Erigon and Geth use pre-configured gas cap even if
|
||||
// it's possible to derive the gas limit from the block:
|
||||
// <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956
|
||||
// https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94>
|
||||
block_env.gas_limit.saturating_to()
|
||||
});
|
||||
|
||||
#[allow(clippy::needless_update)]
|
||||
let env = TxEnv {
|
||||
gas_limit,
|
||||
nonce,
|
||||
caller: from.unwrap_or_default(),
|
||||
gas_price,
|
||||
gas_priority_fee: max_priority_fee_per_gas,
|
||||
transact_to: to.unwrap_or(TxKind::Create),
|
||||
value: value.unwrap_or_default(),
|
||||
data: input
|
||||
.try_into_unique_input()
|
||||
.map_err(Self::Error::from_eth_err)?
|
||||
.unwrap_or_default(),
|
||||
chain_id,
|
||||
access_list: access_list.unwrap_or_default().into(),
|
||||
// EIP-4844 fields
|
||||
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
|
||||
max_fee_per_blob_gas,
|
||||
// EIP-7702 fields
|
||||
authorization_list: authorization_list.map(Into::into),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(env)
|
||||
}
|
||||
) -> Result<<Self::Evm as ConfigureEvmEnv>::TxEnv, Self::Error>;
|
||||
|
||||
/// Prepares the [`EvmEnv`] for execution of calls.
|
||||
///
|
||||
@ -792,7 +734,7 @@ pub trait Call:
|
||||
mut request: TransactionRequest,
|
||||
db: &mut CacheDB<DB>,
|
||||
overrides: EvmOverrides,
|
||||
) -> Result<(EvmEnv, TxEnv), Self::Error>
|
||||
) -> Result<(EvmEnv, <Self::Evm as ConfigureEvmEnv>::TxEnv), Self::Error>
|
||||
where
|
||||
DB: DatabaseRef,
|
||||
EthApiError: From<<DB as DatabaseRef>::Error>,
|
||||
@ -831,12 +773,12 @@ pub trait Call:
|
||||
|
||||
if request_gas.is_none() {
|
||||
// No gas limit was provided in the request, so we need to cap the transaction gas limit
|
||||
if tx_env.gas_price > U256::ZERO {
|
||||
if tx_env.gas_price() > U256::ZERO {
|
||||
// If gas price is specified, cap transaction gas limit with caller allowance
|
||||
trace!(target: "rpc::eth::call", ?tx_env, "Applying gas limit cap with caller allowance");
|
||||
let cap = caller_gas_allowance(db, &tx_env)?;
|
||||
// ensure we cap gas_limit to the block's
|
||||
tx_env.gas_limit = cap.min(evm_env.block_env.gas_limit).saturating_to();
|
||||
tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit).saturating_to());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,19 +6,19 @@ use alloy_primitives::U256;
|
||||
use alloy_rpc_types_eth::{state::StateOverride, transaction::TransactionRequest, BlockId};
|
||||
use futures::Future;
|
||||
use reth_chainspec::MIN_TRANSACTION_GAS;
|
||||
use reth_evm::env::EvmEnv;
|
||||
use reth_evm::{env::EvmEnv, ConfigureEvmEnv, TransactionEnv};
|
||||
use reth_provider::StateProvider;
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
db::CacheDB,
|
||||
primitives::{ExecutionResult, HaltReason, TransactTo},
|
||||
primitives::{ExecutionResult, HaltReason},
|
||||
};
|
||||
use reth_rpc_eth_types::{
|
||||
revm_utils::{apply_state_overrides, caller_gas_allowance},
|
||||
EthApiError, RevertError, RpcInvalidTransactionError,
|
||||
};
|
||||
use reth_rpc_server_types::constants::gas_oracle::{CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO};
|
||||
use revm_primitives::{db::Database, TxEnv};
|
||||
use revm_primitives::{db::Database, TxKind};
|
||||
use tracing::trace;
|
||||
|
||||
/// Gas execution estimates
|
||||
@ -84,8 +84,8 @@ pub trait EstimateCall: Call {
|
||||
}
|
||||
|
||||
// Optimize for simple transfer transactions, potentially reducing the gas estimate.
|
||||
if tx_env.data.is_empty() {
|
||||
if let TransactTo::Call(to) = tx_env.transact_to {
|
||||
if tx_env.input().is_empty() {
|
||||
if let TxKind::Call(to) = tx_env.kind() {
|
||||
if let Ok(code) = db.db.account_code(&to) {
|
||||
let no_code_callee = code.map(|code| code.is_empty()).unwrap_or(true);
|
||||
if no_code_callee {
|
||||
@ -95,7 +95,7 @@ pub trait EstimateCall: Call {
|
||||
// field combos that bump the price up, so we try executing the function
|
||||
// with the minimum gas limit to make sure.
|
||||
let mut tx_env = tx_env.clone();
|
||||
tx_env.gas_limit = MIN_TRANSACTION_GAS;
|
||||
tx_env.set_gas_limit(MIN_TRANSACTION_GAS);
|
||||
if let Ok((res, _)) = self.transact(&mut db, evm_env.clone(), tx_env) {
|
||||
if res.result.is_success() {
|
||||
return Ok(U256::from(MIN_TRANSACTION_GAS))
|
||||
@ -109,7 +109,7 @@ pub trait EstimateCall: Call {
|
||||
// Check funds of the sender (only useful to check if transaction gas price is more than 0).
|
||||
//
|
||||
// The caller allowance is check by doing `(account.balance - tx.value) / tx.gas_price`
|
||||
if tx_env.gas_price > U256::ZERO {
|
||||
if tx_env.gas_price() > U256::ZERO {
|
||||
// cap the highest gas limit by max gas caller can afford with given gas price
|
||||
highest_gas_limit = highest_gas_limit
|
||||
.min(caller_gas_allowance(&mut db, &tx_env).map_err(Self::Error::from_eth_err)?);
|
||||
@ -119,7 +119,7 @@ pub trait EstimateCall: Call {
|
||||
let mut highest_gas_limit = highest_gas_limit.saturating_to::<u64>();
|
||||
|
||||
// If the provided gas limit is less than computed cap, use that
|
||||
tx_env.gas_limit = tx_env.gas_limit.min(highest_gas_limit);
|
||||
tx_env.set_gas_limit(tx_env.gas_limit().min(highest_gas_limit));
|
||||
|
||||
trace!(target: "rpc::eth::estimate", ?evm_env, ?tx_env, "Starting gas estimation");
|
||||
|
||||
@ -169,7 +169,7 @@ pub trait EstimateCall: Call {
|
||||
|
||||
// we know the tx succeeded with the configured gas limit, so we can use that as the
|
||||
// highest, in case we applied a gas cap due to caller allowance above
|
||||
highest_gas_limit = tx_env.gas_limit;
|
||||
highest_gas_limit = tx_env.gas_limit();
|
||||
|
||||
// NOTE: this is the gas the transaction used, which is less than the
|
||||
// transaction requires to succeed.
|
||||
@ -186,7 +186,7 @@ pub trait EstimateCall: Call {
|
||||
let optimistic_gas_limit = (gas_used + gas_refund + CALL_STIPEND_GAS) * 64 / 63;
|
||||
if optimistic_gas_limit < highest_gas_limit {
|
||||
// Set the transaction's gas limit to the calculated optimistic gas limit.
|
||||
tx_env.gas_limit = optimistic_gas_limit;
|
||||
tx_env.set_gas_limit(optimistic_gas_limit);
|
||||
// Re-execute the transaction with the new gas limit and update the result and
|
||||
// environment.
|
||||
(res, (evm_env, tx_env)) = self.transact(&mut db, evm_env, tx_env)?;
|
||||
@ -221,7 +221,7 @@ pub trait EstimateCall: Call {
|
||||
break
|
||||
};
|
||||
|
||||
tx_env.gas_limit = mid_gas_limit;
|
||||
tx_env.set_gas_limit(mid_gas_limit);
|
||||
|
||||
// Execute transaction and handle potential gas errors, adjusting limits accordingly.
|
||||
match self.transact(&mut db, evm_env.clone(), tx_env.clone()) {
|
||||
@ -282,15 +282,15 @@ pub trait EstimateCall: Call {
|
||||
&self,
|
||||
env_gas_limit: U256,
|
||||
evm_env: EvmEnv,
|
||||
mut tx_env: TxEnv,
|
||||
mut tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
db: &mut DB,
|
||||
) -> Self::Error
|
||||
where
|
||||
DB: Database,
|
||||
EthApiError: From<DB::Error>,
|
||||
{
|
||||
let req_gas_limit = tx_env.gas_limit;
|
||||
tx_env.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX);
|
||||
let req_gas_limit = tx_env.gas_limit();
|
||||
tx_env.set_gas_limit(env_gas_limit.try_into().unwrap_or(u64::MAX));
|
||||
let (res, _) = match self.transact(db, evm_env, tx_env) {
|
||||
Ok(res) => res,
|
||||
Err(err) => return err,
|
||||
|
||||
@ -18,7 +18,7 @@ use reth_rpc_eth_types::{
|
||||
};
|
||||
use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector};
|
||||
use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
|
||||
use revm_primitives::{EvmState, ExecutionResult, ResultAndState, TxEnv};
|
||||
use revm_primitives::{EvmState, ExecutionResult, ResultAndState};
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
|
||||
/// Executes CPU heavy tasks.
|
||||
@ -33,13 +33,14 @@ pub trait Trace:
|
||||
{
|
||||
/// Executes the [`EvmEnv`] against the given [Database] without committing state
|
||||
/// changes.
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn inspect<DB, I>(
|
||||
&self,
|
||||
db: DB,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
inspector: I,
|
||||
) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error>
|
||||
) -> Result<(ResultAndState, (EvmEnv, <Self::Evm as ConfigureEvmEnv>::TxEnv)), Self::Error>
|
||||
where
|
||||
DB: Database,
|
||||
EthApiError: From<DB::Error>,
|
||||
@ -61,7 +62,7 @@ pub trait Trace:
|
||||
fn trace_at<F, R>(
|
||||
&self,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
config: TracingInspectorConfig,
|
||||
at: BlockId,
|
||||
f: F,
|
||||
@ -88,7 +89,7 @@ pub trait Trace:
|
||||
fn spawn_trace_at_with_state<F, R>(
|
||||
&self,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
tx_env: <Self::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
config: TracingInspectorConfig,
|
||||
at: BlockId,
|
||||
f: F,
|
||||
|
||||
@ -5,10 +5,11 @@ use alloy_rpc_types_eth::{
|
||||
state::{AccountOverride, StateOverride},
|
||||
BlockOverrides,
|
||||
};
|
||||
use reth_evm::TransactionEnv;
|
||||
use revm::{
|
||||
db::CacheDB,
|
||||
precompile::{PrecompileSpecId, Precompiles},
|
||||
primitives::{db::DatabaseRef, Bytecode, SpecId, TxEnv},
|
||||
primitives::{db::DatabaseRef, Bytecode, SpecId},
|
||||
Database,
|
||||
};
|
||||
use revm_primitives::BlockEnv;
|
||||
@ -32,26 +33,26 @@ pub fn get_precompiles(spec_id: SpecId) -> impl IntoIterator<Item = Address> {
|
||||
///
|
||||
/// Note: this takes the mut [Database] trait because the loaded sender can be reused for the
|
||||
/// following operation like `eth_call`.
|
||||
pub fn caller_gas_allowance<DB>(db: &mut DB, env: &TxEnv) -> EthResult<U256>
|
||||
pub fn caller_gas_allowance<DB>(db: &mut DB, env: &impl TransactionEnv) -> EthResult<U256>
|
||||
where
|
||||
DB: Database,
|
||||
EthApiError: From<<DB as Database>::Error>,
|
||||
{
|
||||
// Get the caller account.
|
||||
let caller = db.basic(env.caller)?;
|
||||
let caller = db.basic(env.caller())?;
|
||||
// Get the caller balance.
|
||||
let balance = caller.map(|acc| acc.balance).unwrap_or_default();
|
||||
// Get transaction value.
|
||||
let value = env.value;
|
||||
let value = env.value();
|
||||
// Subtract transferred value from the caller balance. Return error if the caller has
|
||||
// insufficient funds.
|
||||
let balance = balance
|
||||
.checked_sub(env.value)
|
||||
.checked_sub(env.value())
|
||||
.ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds { cost: value, balance })?;
|
||||
|
||||
Ok(balance
|
||||
// Calculate the amount of gas the caller can afford with the specified gas price.
|
||||
.checked_div(env.gas_price)
|
||||
.checked_div(env.gas_price())
|
||||
// This will be 0 if gas price is 0. It is fine, because we check it before.
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ use reth_chainspec::EthereumHardforks;
|
||||
use reth_evm::{
|
||||
env::EvmEnv,
|
||||
execute::{BlockExecutorProvider, Executor},
|
||||
ConfigureEvmEnv,
|
||||
ConfigureEvmEnv, TransactionEnv,
|
||||
};
|
||||
use reth_primitives::{NodePrimitives, ReceiptWithBloom, RecoveredBlock};
|
||||
use reth_primitives_traits::{Block as _, BlockBody, SignedTransaction};
|
||||
@ -42,7 +42,6 @@ use revm::{
|
||||
use revm_inspectors::tracing::{
|
||||
FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext,
|
||||
};
|
||||
use revm_primitives::TxEnv;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{AcquireError, OwnedSemaphorePermit};
|
||||
|
||||
@ -311,7 +310,7 @@ where
|
||||
let (res, (_, tx_env)) =
|
||||
this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
let frame = inspector
|
||||
.with_transaction_gas_limit(tx_env.gas_limit)
|
||||
.with_transaction_gas_limit(tx_env.gas_limit())
|
||||
.into_geth_builder()
|
||||
.geth_call_traces(call_config, res.result.gas_used());
|
||||
Ok(frame.into())
|
||||
@ -341,7 +340,7 @@ where
|
||||
&mut inspector,
|
||||
)?;
|
||||
let frame = inspector
|
||||
.with_transaction_gas_limit(tx_env.gas_limit)
|
||||
.with_transaction_gas_limit(tx_env.gas_limit())
|
||||
.into_geth_builder()
|
||||
.geth_prestate_traces(&res, &prestate_config, db)
|
||||
.map_err(Eth::Error::from_eth_err)?;
|
||||
@ -410,7 +409,7 @@ where
|
||||
this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
let tx_info = TransactionInfo::default();
|
||||
let frame: FlatCallFrame = inspector
|
||||
.with_transaction_gas_limit(tx_env.gas_limit)
|
||||
.with_transaction_gas_limit(tx_env.gas_limit())
|
||||
.into_parity_builder()
|
||||
.into_localized_transaction_traces(tx_info);
|
||||
Ok(frame)
|
||||
@ -449,7 +448,7 @@ where
|
||||
let env = revm_primitives::Env::boxed(
|
||||
evm_env.cfg_env_with_handler_cfg.cfg_env,
|
||||
evm_env.block_env,
|
||||
tx_env,
|
||||
tx_env.into(),
|
||||
);
|
||||
inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err)
|
||||
})
|
||||
@ -470,7 +469,7 @@ where
|
||||
.spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
|
||||
let (res, (_, tx_env)) =
|
||||
this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
Ok((res, tx_env.gas_limit, inspector))
|
||||
Ok((res, tx_env.gas_limit(), inspector))
|
||||
})
|
||||
.await?;
|
||||
let gas_used = res.result.gas_used();
|
||||
@ -652,7 +651,7 @@ where
|
||||
&self,
|
||||
opts: &GethDebugTracingOptions,
|
||||
evm_env: EvmEnv,
|
||||
tx_env: TxEnv,
|
||||
tx_env: <Eth::Evm as ConfigureEvmEnv>::TxEnv,
|
||||
db: &mut StateCacheDb<'_>,
|
||||
transaction_context: Option<TransactionContext>,
|
||||
fused_inspector: &mut Option<TracingInspector>,
|
||||
@ -694,7 +693,7 @@ where
|
||||
let (res, (_, tx_env)) =
|
||||
self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit);
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit());
|
||||
|
||||
let frame = inspector
|
||||
.geth_builder()
|
||||
@ -716,7 +715,7 @@ where
|
||||
let (res, (_, tx_env)) =
|
||||
self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
|
||||
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit);
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit());
|
||||
let frame = inspector
|
||||
.geth_builder()
|
||||
.geth_prestate_traces(&res, &prestate_config, db)
|
||||
@ -756,7 +755,7 @@ where
|
||||
let (res, (_, tx_env)) =
|
||||
self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
let frame: FlatCallFrame = inspector
|
||||
.with_transaction_gas_limit(tx_env.gas_limit)
|
||||
.with_transaction_gas_limit(tx_env.gas_limit())
|
||||
.into_parity_builder()
|
||||
.into_localized_transaction_traces(tx_info);
|
||||
|
||||
@ -784,7 +783,7 @@ where
|
||||
let env = revm_primitives::Env::boxed(
|
||||
evm_env.cfg_env_with_handler_cfg.cfg_env,
|
||||
evm_env.block_env,
|
||||
tx_env,
|
||||
tx_env.into(),
|
||||
);
|
||||
let result =
|
||||
inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err)?;
|
||||
@ -801,7 +800,7 @@ where
|
||||
let (res, (_, tx_env)) = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
|
||||
let gas_used = res.result.gas_used();
|
||||
let return_value = res.result.into_output().unwrap_or_default();
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit);
|
||||
inspector.set_transaction_gas_limit(tx_env.gas_limit());
|
||||
let frame = inspector.geth_builder().geth_traces(gas_used, return_value, *config);
|
||||
|
||||
Ok((frame.into(), res.state))
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
|
||||
use crate::EthApi;
|
||||
use alloy_consensus::Header;
|
||||
use alloy_rpc_types::TransactionRequest;
|
||||
use reth_evm::ConfigureEvm;
|
||||
use reth_provider::{BlockReader, ProviderHeader};
|
||||
use reth_rpc_eth_api::{
|
||||
helpers::{estimate::EstimateCall, Call, EthCall, LoadPendingBlock, LoadState, SpawnBlocking},
|
||||
FullEthApiTypes,
|
||||
FromEthApiError, FullEthApiTypes, IntoEthApiError,
|
||||
};
|
||||
use reth_rpc_eth_types::{revm_utils::CallFees, RpcInvalidTransactionError};
|
||||
use revm_primitives::{BlockEnv, TxEnv, TxKind, U256};
|
||||
|
||||
impl<Provider, Pool, Network, EvmConfig> EthCall for EthApi<Provider, Pool, Network, EvmConfig>
|
||||
where
|
||||
@ -18,7 +21,8 @@ where
|
||||
|
||||
impl<Provider, Pool, Network, EvmConfig> Call for EthApi<Provider, Pool, Network, EvmConfig>
|
||||
where
|
||||
Self: LoadState<Evm: ConfigureEvm<Header = ProviderHeader<Self::Provider>>> + SpawnBlocking,
|
||||
Self: LoadState<Evm: ConfigureEvm<TxEnv = TxEnv, Header = ProviderHeader<Self::Provider>>>
|
||||
+ SpawnBlocking,
|
||||
EvmConfig: ConfigureEvm<Header = Header>,
|
||||
Provider: BlockReader,
|
||||
{
|
||||
@ -31,6 +35,81 @@ where
|
||||
fn max_simulate_blocks(&self) -> u64 {
|
||||
self.inner.max_simulate_blocks()
|
||||
}
|
||||
|
||||
fn create_txn_env(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
request: TransactionRequest,
|
||||
) -> Result<TxEnv, Self::Error> {
|
||||
// Ensure that if versioned hashes are set, they're not empty
|
||||
if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) {
|
||||
return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err())
|
||||
}
|
||||
|
||||
let TransactionRequest {
|
||||
from,
|
||||
to,
|
||||
gas_price,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
gas,
|
||||
value,
|
||||
input,
|
||||
nonce,
|
||||
access_list,
|
||||
chain_id,
|
||||
blob_versioned_hashes,
|
||||
max_fee_per_blob_gas,
|
||||
authorization_list,
|
||||
transaction_type: _,
|
||||
sidecar: _,
|
||||
} = request;
|
||||
|
||||
let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
|
||||
CallFees::ensure_fees(
|
||||
gas_price.map(U256::from),
|
||||
max_fee_per_gas.map(U256::from),
|
||||
max_priority_fee_per_gas.map(U256::from),
|
||||
block_env.basefee,
|
||||
blob_versioned_hashes.as_deref(),
|
||||
max_fee_per_blob_gas.map(U256::from),
|
||||
block_env.get_blob_gasprice().map(U256::from),
|
||||
)?;
|
||||
|
||||
let gas_limit = gas.unwrap_or_else(|| {
|
||||
// Use maximum allowed gas limit. The reason for this
|
||||
// is that both Erigon and Geth use pre-configured gas cap even if
|
||||
// it's possible to derive the gas limit from the block:
|
||||
// <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956
|
||||
// https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94>
|
||||
block_env.gas_limit.saturating_to()
|
||||
});
|
||||
|
||||
#[allow(clippy::needless_update)]
|
||||
let env = TxEnv {
|
||||
gas_limit,
|
||||
nonce,
|
||||
caller: from.unwrap_or_default(),
|
||||
gas_price,
|
||||
gas_priority_fee: max_priority_fee_per_gas,
|
||||
transact_to: to.unwrap_or(TxKind::Create),
|
||||
value: value.unwrap_or_default(),
|
||||
data: input
|
||||
.try_into_unique_input()
|
||||
.map_err(Self::Error::from_eth_err)?
|
||||
.unwrap_or_default(),
|
||||
chain_id,
|
||||
access_list: access_list.unwrap_or_default().into(),
|
||||
// EIP-4844 fields
|
||||
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
|
||||
max_fee_per_blob_gas,
|
||||
// EIP-7702 fields
|
||||
authorization_list: authorization_list.map(Into::into),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(env)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider, Pool, Network, EvmConfig> EstimateCall for EthApi<Provider, Pool, Network, EvmConfig>
|
||||
|
||||
@ -86,8 +86,8 @@ impl MyEvmConfig {
|
||||
impl ConfigureEvmEnv for MyEvmConfig {
|
||||
type Header = Header;
|
||||
type Transaction = TransactionSigned;
|
||||
|
||||
type Error = Infallible;
|
||||
type TxEnv = TxEnv;
|
||||
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
|
||||
self.inner.fill_tx_env(tx_env, transaction, sender);
|
||||
|
||||
@ -150,6 +150,7 @@ impl ConfigureEvmEnv for MyEvmConfig {
|
||||
type Header = Header;
|
||||
type Transaction = TransactionSigned;
|
||||
type Error = Infallible;
|
||||
type TxEnv = TxEnv;
|
||||
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
|
||||
self.inner.fill_tx_env(tx_env, transaction, sender)
|
||||
|
||||
Reference in New Issue
Block a user