From 95e8c9ceb6df67f5d527236e4cb9f574ff2f775c Mon Sep 17 00:00:00 2001 From: Bartosz Zawistowski Date: Tue, 1 Oct 2024 11:34:26 +0200 Subject: [PATCH] Handle validation & execution by processor --- silkworm/core/execution/evm.cpp | 39 ---- silkworm/core/execution/evm.hpp | 2 - silkworm/core/execution/execution.hpp | 3 +- silkworm/core/execution/execution_test.cpp | 3 +- silkworm/core/execution/processor.cpp | 70 +++++++- silkworm/core/execution/processor.hpp | 8 +- silkworm/core/execution/processor_test.cpp | 12 +- silkworm/core/protocol/blockchain.cpp | 3 +- silkworm/core/protocol/validation.cpp | 76 ++++++++ silkworm/core/protocol/validation.hpp | 7 + .../db/test_util/test_database_context.cpp | 3 +- .../node/execution/block/block_executor.cpp | 3 +- silkworm/rpc/commands/debug_api.cpp | 2 +- silkworm/rpc/core/call_many.cpp | 2 +- silkworm/rpc/core/estimate_gas_oracle.cpp | 4 +- silkworm/rpc/core/evm_debug.cpp | 6 +- silkworm/rpc/core/evm_debug_test.cpp | 8 + silkworm/rpc/core/evm_executor.cpp | 166 ++++++------------ silkworm/rpc/core/evm_executor.hpp | 13 +- silkworm/rpc/core/evm_executor_test.cpp | 20 +-- silkworm/rpc/core/evm_trace.cpp | 16 +- 21 files changed, 257 insertions(+), 209 deletions(-) diff --git a/silkworm/core/execution/evm.cpp b/silkworm/core/execution/evm.cpp index 19ecd11a50..a4394a597e 100644 --- a/silkworm/core/execution/evm.cpp +++ b/silkworm/core/execution/evm.cpp @@ -270,45 +270,6 @@ evmc::Result EVM::call(const evmc_message& message) noexcept { return res; } -CallResult EVM::deduct_entry_fees(const Transaction& txn) const { - if (!bailout_) { - const evmc_revision rev{revision()}; - const intx::uint256 base_fee_per_gas{block().header.base_fee_per_gas.value_or(0)}; - - // EIP-1559 normal gas cost - intx::uint256 required_funds; - if (txn.max_fee_per_gas > 0 || txn.max_priority_fee_per_gas > 0) { - // This method should be called after check (max_fee and base_fee) present in pre_check() method - const intx::uint256 effective_gas_price{txn.max_fee_per_gas >= base_fee_per_gas ? txn.effective_gas_price(base_fee_per_gas) - : txn.max_priority_fee_per_gas}; - required_funds = txn.gas_limit * effective_gas_price; - } else { - required_funds = 0; - } - - // EIP-4844 blob gas cost (calc_data_fee) - if (block().header.blob_gas_used && rev >= EVMC_CANCUN) { - // compute blob fee for eip-4844 data blobs if any - const intx::uint256 blob_gas_price{block().header.blob_gas_price().value_or(0)}; - required_funds += txn.total_blob_gas() * blob_gas_price; - } - - intx::uint512 maximum_cost = required_funds; - if (txn.type != TransactionType::kLegacy && txn.type != TransactionType::kAccessList) { - maximum_cost = txn.maximum_gas_cost(); - } - - const auto owned_funds = state_.get_balance(*txn.sender()); - if (owned_funds < maximum_cost + txn.value) { - std::string from = address_to_hex(*txn.sender()); - std::string msg = "insufficient funds for gas * price + value: address " + from + " have " + intx::to_string(owned_funds) + " want " + intx::to_string(maximum_cost + txn.value); - return {.status = EVMC_INSUFFICIENT_BALANCE, .error_message = msg}; - } - state_.subtract_from_balance(*txn.sender(), required_funds); - } - return {.status = EVMC_SUCCESS}; -} - evmc_result EVM::execute(const evmc_message& message, ByteView code, const evmc::bytes32* code_hash) noexcept { const evmc_revision rev{revision()}; if (exo_evm) { diff --git a/silkworm/core/execution/evm.hpp b/silkworm/core/execution/evm.hpp index 8ee304afe6..36f0448ada 100644 --- a/silkworm/core/execution/evm.hpp +++ b/silkworm/core/execution/evm.hpp @@ -123,8 +123,6 @@ class EVM { gsl::not_null transfer{standard_transfer}; - CallResult deduct_entry_fees(const Transaction& txn) const; - private: friend class EvmHost; diff --git a/silkworm/core/execution/execution.hpp b/silkworm/core/execution/execution.hpp index bcedd293c6..53fd2b59a8 100644 --- a/silkworm/core/execution/execution.hpp +++ b/silkworm/core/execution/execution.hpp @@ -44,8 +44,7 @@ namespace silkworm { if (!rule_set) { return ValidationResult::kUnknownProtocolRuleSet; } - constexpr auto kBailout = false; - ExecutionProcessor processor{block, *rule_set, state, chain_config, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, chain_config}; if (const ValidationResult res = processor.execute_block(receipts); res != ValidationResult::kOk) { return res; diff --git a/silkworm/core/execution/execution_test.cpp b/silkworm/core/execution/execution_test.cpp index b0bd7e03da..bdaf93d0df 100644 --- a/silkworm/core/execution/execution_test.cpp +++ b/silkworm/core/execution/execution_test.cpp @@ -33,7 +33,6 @@ namespace silkworm { static constexpr auto kMiner{0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c_address}; static constexpr auto kSender{0xb685342b8c54347aad148e1f22eff3eb3eb29391_address}; -static constexpr auto kBailout = false; TEST_CASE("Execute two blocks") { // --------------------------------------- @@ -166,7 +165,7 @@ TEST_CASE("Execute block with tracing") { std::vector receipts; const auto rule_set{protocol::rule_set_factory(chain_config)}; REQUIRE(rule_set); - ExecutionProcessor processor{block, *rule_set, state, chain_config, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, chain_config}; BlockTracer block_tracer{}; processor.evm().add_tracer(block_tracer); diff --git a/silkworm/core/execution/processor.cpp b/silkworm/core/execution/processor.cpp index 312952d924..aa7e73c6f9 100644 --- a/silkworm/core/execution/processor.cpp +++ b/silkworm/core/execution/processor.cpp @@ -25,8 +25,8 @@ namespace silkworm { ExecutionProcessor::ExecutionProcessor(const Block& block, protocol::RuleSet& rule_set, State& state, - const ChainConfig& config, bool bailout) - : state_{state}, rule_set_{rule_set}, evm_{block, state_, config, bailout} { + const ChainConfig& config) + : state_{state}, rule_set_{rule_set}, evm_{block, state_, config} { evm_.beneficiary = rule_set.get_beneficiary(block.header); evm_.transfer = rule_set.transfer_func(); } @@ -81,7 +81,7 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re const CallResult vm_res{evm_.execute(txn, txn.gas_limit - static_cast(g0))}; - const uint64_t gas_used{txn.gas_limit - refund_gas(txn, vm_res.gas_left, vm_res.gas_refund)}; + const uint64_t gas_used{txn.gas_limit - refund_gas(txn, effective_gas_price, vm_res.gas_left, vm_res.gas_refund)}; // award the fee recipient const intx::uint256 amount{txn.priority_fee_per_gas(base_fee_per_gas) * gas_used}; @@ -110,11 +110,70 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re std::swap(receipt.logs, state_.logs()); } +CallResult ExecutionProcessor::call_with_evm(const Transaction& txn, EVM& call_evm, bool bailout, bool refund) noexcept { + const std::optional sender{txn.sender()}; + + SILKWORM_ASSERT(protocol::validate_call_precheck(txn, call_evm) == ValidationResult::kOk); + SILKWORM_ASSERT(protocol::validate_call_funds(txn, call_evm, state_.get_balance(*txn.sender()), bailout) == ValidationResult::kOk); + + const BlockHeader& header{call_evm.block().header}; + const intx::uint256 base_fee_per_gas{header.base_fee_per_gas.value_or(0)}; + + const intx::uint256 effective_gas_price{txn.max_fee_per_gas >= base_fee_per_gas ? txn.effective_gas_price(base_fee_per_gas) + : txn.max_priority_fee_per_gas}; + state_.access_account(*sender); + + if (txn.to) { + state_.access_account(*txn.to); + // EVM itself increments the nonce for contract creation + state_.set_nonce(*sender, state_.get_nonce(*txn.sender()) + 1); + } + + for (const AccessListEntry& ae : txn.access_list) { + state_.access_account(ae.account); + for (const evmc::bytes32& key : ae.storage_keys) { + state_.access_storage(ae.account, key); + } + } + + if (!bailout) { + const intx::uint256 required_funds = protocol::compute_call_cost(txn, effective_gas_price, call_evm); + state_.subtract_from_balance(*txn.sender(), required_funds); + } + const intx::uint128 g0{protocol::intrinsic_gas(txn, call_evm.revision())}; + const auto result = call_evm.execute(txn, txn.gas_limit - static_cast(g0)); + + uint64_t gas_left{result.gas_left}; + uint64_t gas_used{txn.gas_limit - result.gas_left}; + + if (refund && !bailout) { + gas_used = txn.gas_limit - refund_gas(txn, effective_gas_price, result.gas_left, result.gas_refund); + gas_left = txn.gas_limit - gas_used; + } + + // Reward the fee recipient + const intx::uint256 priority_fee_per_gas{txn.max_fee_per_gas >= base_fee_per_gas ? txn.priority_fee_per_gas(base_fee_per_gas) + : txn.max_priority_fee_per_gas}; + + state_.add_to_balance(call_evm.beneficiary, priority_fee_per_gas * gas_used); + + for (auto& tracer : call_evm.tracers()) { + tracer.get().on_reward_granted(result, state_); + } + state_.finalize_transaction(call_evm.revision()); + + return {result.status, gas_left, gas_used, result.data, result.error_message}; +} + +void ExecutionProcessor::reset() { + state_.clear_journal_and_substate(); +} + uint64_t ExecutionProcessor::available_gas() const noexcept { return evm_.block().header.gas_limit - cumulative_gas_used_; } -uint64_t ExecutionProcessor::refund_gas(const Transaction& txn, uint64_t gas_left, uint64_t gas_refund) noexcept { +uint64_t ExecutionProcessor::refund_gas(const Transaction& txn, const intx::uint256& effective_gas_price, uint64_t gas_left, uint64_t gas_refund) noexcept { const evmc_revision rev{evm_.revision()}; const uint64_t max_refund_quotient{rev >= EVMC_LONDON ? protocol::kMaxRefundQuotientLondon @@ -123,8 +182,6 @@ uint64_t ExecutionProcessor::refund_gas(const Transaction& txn, uint64_t gas_lef uint64_t refund = std::min(gas_refund, max_refund); gas_left += refund; - const intx::uint256 base_fee_per_gas{evm_.block().header.base_fee_per_gas.value_or(0)}; - const intx::uint256 effective_gas_price{txn.effective_gas_price(base_fee_per_gas)}; state_.add_to_balance(*txn.sender(), gas_left * effective_gas_price); return gas_left; @@ -142,6 +199,7 @@ ValidationResult ExecutionProcessor::execute_block_no_post_validation(std::vecto receipts.resize(block.transactions.size()); auto receipt_it{receipts.begin()}; + for (const auto& txn : block.transactions) { const ValidationResult err{protocol::validate_transaction(txn, state_, available_gas())}; if (err != ValidationResult::kOk) { diff --git a/silkworm/core/execution/processor.hpp b/silkworm/core/execution/processor.hpp index 9f3f34f9ba..ce92c73b80 100644 --- a/silkworm/core/execution/processor.hpp +++ b/silkworm/core/execution/processor.hpp @@ -33,7 +33,7 @@ class ExecutionProcessor { ExecutionProcessor(const ExecutionProcessor&) = delete; ExecutionProcessor& operator=(const ExecutionProcessor&) = delete; - ExecutionProcessor(const Block& block, protocol::RuleSet& rule_set, State& state, const ChainConfig& config, bool bailout); + ExecutionProcessor(const Block& block, protocol::RuleSet& rule_set, State& state, const ChainConfig& config); /** * Execute a transaction, but do not write to the DB yet. @@ -41,6 +41,8 @@ class ExecutionProcessor { */ void execute_transaction(const Transaction& txn, Receipt& receipt) noexcept; + CallResult call_with_evm(const Transaction& txn, EVM& evm, bool bailout, bool refund) noexcept; + //! \brief Execute the block. //! \remarks Warning: This method does not verify state root; pre-Byzantium receipt root isn't validated either. //! \pre RuleSet's validate_block_header & pre_validate_block_body must return kOk. @@ -53,6 +55,8 @@ class ExecutionProcessor { EVM& evm() noexcept { return evm_; } const EVM& evm() const noexcept { return evm_; } + IntraBlockState& get_ibs_state() { return state_; } + void reset(); private: /** @@ -68,7 +72,7 @@ class ExecutionProcessor { //! \brief Notify the registered tracers at the end of block execution. void notify_block_execution_end(const Block& block); - uint64_t refund_gas(const Transaction& txn, uint64_t gas_left, uint64_t gas_refund) noexcept; + uint64_t refund_gas(const Transaction& txn, const intx::uint256& effective_gas_price, uint64_t gas_left, uint64_t gas_refund) noexcept; uint64_t cumulative_gas_used_{0}; IntraBlockState state_; diff --git a/silkworm/core/execution/processor_test.cpp b/silkworm/core/execution/processor_test.cpp index 92ff9362a5..5019040e1d 100644 --- a/silkworm/core/execution/processor_test.cpp +++ b/silkworm/core/execution/processor_test.cpp @@ -26,8 +26,6 @@ namespace silkworm { -static constexpr auto kBailout = false; - TEST_CASE("Zero gas price") { Block block{}; block.header.number = 2'687'232; @@ -47,7 +45,7 @@ TEST_CASE("Zero gas price") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig}; Receipt receipt; processor.execute_transaction(txn, receipt); @@ -87,7 +85,7 @@ TEST_CASE("No refund on error") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig}; Transaction txn{}; txn.nonce = nonce; @@ -181,7 +179,7 @@ TEST_CASE("Self-destruct") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig}; processor.evm().state().add_to_balance(originator, kEther); processor.evm().state().set_code(caller_address, caller_code); @@ -329,7 +327,7 @@ TEST_CASE("Out of Gas during account re-creation") { txn.set_sender(caller); auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; @@ -372,7 +370,7 @@ TEST_CASE("Empty suicide beneficiary") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, kBailout}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; diff --git a/silkworm/core/protocol/blockchain.cpp b/silkworm/core/protocol/blockchain.cpp index c92de854ad..9bd2fe0e8b 100644 --- a/silkworm/core/protocol/blockchain.cpp +++ b/silkworm/core/protocol/blockchain.cpp @@ -89,8 +89,7 @@ ValidationResult Blockchain::insert_block(Block& block, bool check_state_root) { } ValidationResult Blockchain::execute_block(const Block& block, bool check_state_root) { - constexpr auto kBailout = false; - ExecutionProcessor processor{block, *rule_set_, state_, config_, kBailout}; + ExecutionProcessor processor{block, *rule_set_, state_, config_}; processor.evm().state_pool = state_pool; processor.evm().exo_evm = exo_evm; diff --git a/silkworm/core/protocol/validation.cpp b/silkworm/core/protocol/validation.cpp index 62f74bdb11..f475ad2123 100644 --- a/silkworm/core/protocol/validation.cpp +++ b/silkworm/core/protocol/validation.cpp @@ -159,6 +159,82 @@ ValidationResult pre_validate_transactions(const Block& block, const ChainConfig return ValidationResult::kOk; } +ValidationResult validate_call_precheck(const Transaction& txn, const EVM& evm) noexcept { + const std::optional sender{txn.sender()}; + if (!sender) { + return ValidationResult::kInvalidSignature; + } + + if (evm.revision() >= EVMC_LONDON) { + if (txn.max_fee_per_gas > 0 || txn.max_priority_fee_per_gas > 0) { + if (txn.max_fee_per_gas < txn.max_priority_fee_per_gas) { + return ValidationResult::kMaxPriorityFeeGreaterThanMax; + } + + if (txn.max_fee_per_gas < evm.block().header.base_fee_per_gas) { + return ValidationResult::kMaxFeeLessThanBase; + } + } + } else { + if (txn.type != silkworm::TransactionType::kLegacy && txn.type != silkworm::TransactionType::kAccessList) { + return ValidationResult::kUnsupportedTransactionType; + } + } + + if (evm.revision() >= EVMC_CANCUN) { + if (!evm.block().header.excess_blob_gas) { + return ValidationResult::kWrongBlobGasUsed; + } + } + + const intx::uint128 g0{protocol::intrinsic_gas(txn, evm.revision())}; + assert(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) + + if (txn.gas_limit < g0) { + return ValidationResult::kIntrinsicGas; + } + + return ValidationResult::kOk; +} + +ValidationResult validate_call_funds(const Transaction& txn, const EVM& evm, const intx::uint256& owned_funds, bool bailout) noexcept { + if (!bailout) { + const intx::uint256 base_fee{evm.block().header.base_fee_per_gas.value_or(0)}; + const intx::uint256 effective_gas_price{txn.max_fee_per_gas >= evm.block().header.base_fee_per_gas ? txn.effective_gas_price(base_fee) + : txn.max_priority_fee_per_gas}; + + const auto required_funds = compute_call_cost(txn, effective_gas_price, evm); + intx::uint512 maximum_cost = required_funds; + if (txn.type != TransactionType::kLegacy && txn.type != TransactionType::kAccessList) { + maximum_cost = txn.maximum_gas_cost(); + } + if (owned_funds < maximum_cost + txn.value) { + return ValidationResult::kInsufficientFunds; + } + } + return ValidationResult::kOk; +} + +intx::uint256 compute_call_cost(const Transaction& txn, const intx::uint256& effective_gas_price, const EVM& evm) { + // EIP-1559 normal gas cost + intx::uint256 required_funds; + if (txn.max_fee_per_gas > 0 || txn.max_priority_fee_per_gas > 0) { + // This method should be called after check (max_fee and base_fee) present in pre_check() method + required_funds = txn.gas_limit * effective_gas_price; + } else { + required_funds = 0; + } + + // EIP-4844 blob gas cost (calc_data_fee) + if (evm.block().header.blob_gas_used && evm.revision() >= EVMC_CANCUN) { + // compute blob fee for eip-4844 data blobs if any + const intx::uint256 blob_gas_price{evm.block().header.blob_gas_price().value_or(0)}; + required_funds += txn.total_blob_gas() * blob_gas_price; + } + + return required_funds; +} + intx::uint256 expected_base_fee_per_gas(const BlockHeader& parent) { if (!parent.base_fee_per_gas) { return kInitialBaseFee; diff --git a/silkworm/core/protocol/validation.hpp b/silkworm/core/protocol/validation.hpp index 8111e21d5d..c2a033c53f 100644 --- a/silkworm/core/protocol/validation.hpp +++ b/silkworm/core/protocol/validation.hpp @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -130,6 +131,12 @@ namespace protocol { ValidationResult validate_transaction(const Transaction& txn, const IntraBlockState& state, uint64_t available_gas) noexcept; + ValidationResult validate_call_precheck(const Transaction& txn, const EVM& evm) noexcept; + + ValidationResult validate_call_funds(const Transaction& txn, const EVM& evm, const intx::uint256& owned_funds, bool bailout) noexcept; + + intx::uint256 compute_call_cost(const Transaction& txn, const intx::uint256& effective_gas_price, const EVM& evm); + //! \see EIP-1559: Fee market change for ETH 1.0 chain intx::uint256 expected_base_fee_per_gas(const BlockHeader& parent); diff --git a/silkworm/db/test_util/test_database_context.cpp b/silkworm/db/test_util/test_database_context.cpp index 9cb8bcb7cf..3458483061 100644 --- a/silkworm/db/test_util/test_database_context.cpp +++ b/silkworm/db/test_util/test_database_context.cpp @@ -126,8 +126,7 @@ void populate_blocks(db::RWTxn& txn, const std::filesystem::path& tests_dir, InM // FIX 4b: populate receipts and logs table std::vector receipts; - static constexpr auto kBailout = false; - ExecutionProcessor processor{block, *ruleSet, state_buffer, *chain_config, kBailout}; + ExecutionProcessor processor{block, *ruleSet, state_buffer, *chain_config}; db::Buffer db_buffer{txn}; for (auto& block_txn : block.transactions) { silkworm::Receipt receipt{}; diff --git a/silkworm/node/execution/block/block_executor.cpp b/silkworm/node/execution/block/block_executor.cpp index fd0f2945b2..5cb0a63d0f 100644 --- a/silkworm/node/execution/block/block_executor.cpp +++ b/silkworm/node/execution/block/block_executor.cpp @@ -32,8 +32,7 @@ BlockExecutor::BlockExecutor(const ChainConfig* chain_config, bool write_receipt write_change_sets_{write_change_sets} {} ValidationResult BlockExecutor::execute_single(const Block& block, db::Buffer& state_buffer, AnalysisCache& analysis_cache, ObjectPool& state_pool) { - constexpr auto kBailout = false; - ExecutionProcessor processor{block, *protocol_rule_set_, state_buffer, *chain_config_, kBailout}; + ExecutionProcessor processor{block, *protocol_rule_set_, state_buffer, *chain_config_}; processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; diff --git a/silkworm/rpc/commands/debug_api.cpp b/silkworm/rpc/commands/debug_api.cpp index 09a39c5f32..9f3f942aff 100644 --- a/silkworm/rpc/commands/debug_api.cpp +++ b/silkworm/rpc/commands/debug_api.cpp @@ -319,7 +319,7 @@ Task DebugRpcApi::handle_debug_account_at(const nlohmann::json& request, n auto account_opt = state->read_account(address); account_opt.value_or(silkworm::Account{}); - EVMExecutor executor{chain_config, workers_, state}; + EVMExecutor executor{block, chain_config, workers_, state}; uint64_t index = std::min(static_cast(transactions.size()), tx_index); for (uint64_t idx{0}; idx < index; idx++) { diff --git a/silkworm/rpc/core/call_many.cpp b/silkworm/rpc/core/call_many.cpp index 304acdfc6c..03c549bc2e 100644 --- a/silkworm/rpc/core/call_many.cpp +++ b/silkworm/rpc/core/call_many.cpp @@ -46,7 +46,7 @@ CallManyResult CallExecutor::executes_all_bundles(const silkworm::ChainConfig& c const auto& block = block_with_hash->block; const auto& block_transactions = block.transactions; auto state = transaction_.create_state(this_executor, storage, block.header.number); - EVMExecutor executor{config, workers_, std::make_shared(*state, accounts_overrides)}; + EVMExecutor executor{block, config, workers_, std::make_shared(*state, accounts_overrides)}; std::uint64_t timeout = opt_timeout.value_or(5000); const auto start_time = clock_time::now(); diff --git a/silkworm/rpc/core/estimate_gas_oracle.cpp b/silkworm/rpc/core/estimate_gas_oracle.cpp index acdbc33e25..c59815234b 100644 --- a/silkworm/rpc/core/estimate_gas_oracle.cpp +++ b/silkworm/rpc/core/estimate_gas_oracle.cpp @@ -85,7 +85,7 @@ Task EstimateGasOracle::estimate_gas(const Call& call, const silk ExecutionResult result{evmc_status_code::EVMC_SUCCESS}; silkworm::Transaction transaction{call.to_transaction()}; while (lo + 1 < hi) { - EVMExecutor executor{config_, workers_, state}; + EVMExecutor executor{block, config_, workers_, state}; auto mid = (hi + lo) / 2; transaction.gas_limit = mid; result = try_execution(executor, block, transaction); @@ -101,7 +101,7 @@ Task EstimateGasOracle::estimate_gas(const Call& call, const silk } if (hi == cap) { - EVMExecutor executor{config_, workers_, state}; + EVMExecutor executor{block, config_, workers_, state}; transaction.gas_limit = hi; result = try_execution(executor, block, transaction); SILK_DEBUG << "HI == cap tested again with " << (result.error_code == evmc_status_code::EVMC_SUCCESS ? "succeed" : "failed"); diff --git a/silkworm/rpc/core/evm_debug.cpp b/silkworm/rpc/core/evm_debug.cpp index 0267b5515d..1ce0696b57 100644 --- a/silkworm/rpc/core/evm_debug.cpp +++ b/silkworm/rpc/core/evm_debug.cpp @@ -422,7 +422,7 @@ Task DebugExecutor::execute(json::Stream& stream, const ChainStorage& stor auto current_executor = co_await boost::asio::this_coro::executor; co_await async_task(workers_.executor(), [&]() -> void { auto state = tx_.create_state(current_executor, storage, block_number - 1); - EVMExecutor executor{chain_config, workers_, state}; + EVMExecutor executor{block, chain_config, workers_, state}; for (std::uint64_t idx = 0; idx < transactions.size(); idx++) { rpc::Transaction txn{block.transactions[idx]}; @@ -480,7 +480,7 @@ Task DebugExecutor::execute( auto current_executor = co_await boost::asio::this_coro::executor; co_await async_task(workers_.executor(), [&]() { auto state = tx_.create_state(current_executor, storage, block_number); - EVMExecutor executor{chain_config, workers_, state}; + EVMExecutor executor{block, chain_config, workers_, state}; for (auto idx{0}; idx < index; idx++) { silkworm::Transaction txn{block.transactions[static_cast(idx)]}; @@ -531,7 +531,7 @@ Task DebugExecutor::execute( auto current_executor = co_await boost::asio::this_coro::executor; co_await async_task(workers_.executor(), [&]() { auto state = tx_.create_state(current_executor, storage, block.header.number); - EVMExecutor executor{chain_config, workers_, state}; + EVMExecutor executor{block, chain_config, workers_, state}; for (auto idx{0}; idx < transaction_index; idx++) { silkworm::Transaction txn{block_transactions[static_cast(idx)]}; diff --git a/silkworm/rpc/core/evm_debug_test.cpp b/silkworm/rpc/core/evm_debug_test.cpp index 2fe5bd8836..8500a67731 100644 --- a/silkworm/rpc/core/evm_debug_test.cpp +++ b/silkworm/rpc/core/evm_debug_test.cpp @@ -270,6 +270,10 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { .WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{kAccountHistoryKey1, kAccountHistoryValue1}; })); + EXPECT_CALL(transaction, get(db::table::kAccountHistoryName, silkworm::ByteView{kAccountHistoryKey3})) + .WillOnce(InvokeWithoutArgs([]() -> Task { + co_return KeyValue{kAccountHistoryKey3, kAccountHistoryValue3}; + })); EXPECT_CALL(transaction, get(db::table::kAccountHistoryName, silkworm::ByteView{kAccountHistoryKey2})) .WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{kAccountHistoryKey2, kAccountHistoryValue2}; @@ -278,6 +282,10 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { .WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); + EXPECT_CALL(transaction, get_both_range(db::table::kAccountChangeSetName, silkworm::ByteView{kAccountChangeSetKey1}, silkworm::ByteView{kAccountChangeSetSubkey1})) + .WillOnce(InvokeWithoutArgs([]() -> Task> { + co_return kAccountChangeSetValue1; + })); const auto block_number = 5'405'095; // 0x5279A7 Call call; diff --git a/silkworm/rpc/core/evm_executor.cpp b/silkworm/rpc/core/evm_executor.cpp index b60052fdc9..ae5d7f271d 100644 --- a/silkworm/rpc/core/evm_executor.cpp +++ b/silkworm/rpc/core/evm_executor.cpp @@ -33,6 +33,8 @@ #include #include +#include "silkworm/core/execution/processor.hpp" + namespace silkworm::rpc { std::string ExecutionResult::error_message(bool full_error) const { @@ -161,24 +163,6 @@ std::string EVMExecutor::get_error_message(int64_t error_code, const Bytes& erro return error_message; } -uint64_t EVMExecutor::refund_gas(const EVM& evm, const silkworm::Transaction& txn, uint64_t gas_left, uint64_t gas_refund) { - const evmc_revision rev{evm.revision()}; - const uint64_t max_refund_quotient{rev >= EVMC_LONDON ? protocol::kMaxRefundQuotientLondon - : protocol::kMaxRefundQuotientFrontier}; - const uint64_t max_refund{(txn.gas_limit - gas_left) / max_refund_quotient}; - const uint64_t refund = std::min(gas_refund, max_refund); - gas_left += refund; - - const intx::uint256 base_fee_per_gas{evm.block().header.base_fee_per_gas.value_or(0)}; - SILK_DEBUG << "EVMExecutor::refund_gas txn.max_fee_per_gas: " << txn.max_fee_per_gas << " base_fee_per_gas: " << base_fee_per_gas; - - const intx::uint256 effective_gas_price{txn.max_fee_per_gas >= base_fee_per_gas ? txn.effective_gas_price(base_fee_per_gas) - : txn.max_priority_fee_per_gas}; - SILK_DEBUG << "EVMExecutor::refund_gas effective_gas_price: " << effective_gas_price; - ibs_state_.add_to_balance(*txn.sender(), gas_left * effective_gas_price); - return gas_left; -} - void EVMExecutor::call_first_n(const silkworm::Block& block, const uint64_t n, const Tracers& tracers, bool refund, bool gas_bailout) { for (size_t idx = 0; idx < block.transactions.size() && idx < n; idx++) { const auto& txn = block.transactions.at(idx); @@ -187,47 +171,55 @@ void EVMExecutor::call_first_n(const silkworm::Block& block, const uint64_t n, c } void EVMExecutor::reset() { - ibs_state_.clear_journal_and_substate(); + execution_processor_.reset(); } -std::optional EVMExecutor::pre_check(const EVM& evm, const silkworm::Transaction& txn, - const intx::uint256& base_fee_per_gas, const intx::uint128& g0) { - const evmc_revision rev{evm.revision()}; - - if (rev >= EVMC_LONDON) { - if (txn.max_fee_per_gas > 0 || txn.max_priority_fee_per_gas > 0) { - if (txn.max_fee_per_gas < txn.max_priority_fee_per_gas) { - std::string from = address_to_hex(*txn.sender()); - std::string error = "tip higher than fee cap: address " + from + ", tip: " + intx::to_string(txn.max_priority_fee_per_gas) + " gasFeeCap: " + - intx::to_string(txn.max_fee_per_gas); - return PreCheckResult{error, PreCheckErrorCode::kTipHigherThanFeeCap}; - } - - if (txn.max_fee_per_gas < base_fee_per_gas) { - const std::string from = address_to_hex(*txn.sender()); - std::string error = "fee cap less than block base fee: address " + from + ", gasFeeCap: " + - intx::to_string(txn.max_fee_per_gas) + " baseFee: " + intx::to_string(base_fee_per_gas); - return PreCheckResult{error, PreCheckErrorCode::kFeeCapLessThanBlockFeePerGas}; - } +ExecutionResult convert_validated_precheck(const ValidationResult& result, const Block& block, const silkworm::Transaction& txn, const EVM& evm) { + std::string from = address_to_hex(*txn.sender()); + switch (result) { + case ValidationResult::kMaxPriorityFeeGreaterThanMax: { + std::string error = "tip higher than fee cap: address " + from + ", tip: " + intx::to_string(txn.max_priority_fee_per_gas) + " gasFeeCap: " + + intx::to_string(txn.max_fee_per_gas); + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kTipHigherThanFeeCap}; } - } else { - if (txn.type != silkworm::TransactionType::kLegacy && txn.type != silkworm::TransactionType::kAccessList) { - return PreCheckResult{"eip-1559 transactions require london", PreCheckErrorCode::kIsNotLondon}; + case ValidationResult::kMaxFeeLessThanBase: { + std::string error = "fee cap less than block base fee: address " + from + ", gasFeeCap: " + + intx::to_string(txn.max_fee_per_gas) + " baseFee: " + intx::to_string(*block.header.base_fee_per_gas); + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kFeeCapLessThanBlockFeePerGas}; } - } - - if (rev >= EVMC_CANCUN) { - if (!evm.block().header.excess_blob_gas) { + case ValidationResult::kIntrinsicGas: { + const intx::uint128 g0{protocol::intrinsic_gas(txn, evm.revision())}; + std::string error = "intrinsic gas too low: have " + std::to_string(txn.gas_limit) + ", want " + intx::to_string(g0); + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kIntrinsicGasTooLow}; + } + case ValidationResult::kWrongBlockGas: { std::string error = "internal failure: Cancun is active but ExcessBlobGas is nil"; - return PreCheckResult{error, PreCheckErrorCode::kInternalError}; + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kInternalError}; + } + case ValidationResult::kUnsupportedTransactionType: { + std::string error = "eip-1559 transactions require london"; + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kIsNotLondon}; + } + default: { + std::string error = "internal failure"; + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kInternalError}; } } +} + +ExecutionResult convert_validated_funds(const Block& block, const silkworm::Transaction& txn, const EVM& evm, const intx::uint256& owned_funds) { + std::string from = address_to_hex(*txn.sender()); + const intx::uint256 base_fee_per_gas{block.header.base_fee_per_gas.value_or(0)}; - if (txn.gas_limit < g0) { - std::string error = "intrinsic gas too low: have " + std::to_string(txn.gas_limit) + ", want " + intx::to_string(g0); - return PreCheckResult{error, PreCheckErrorCode::kIntrinsicGasTooLow}; + const intx::uint256 effective_gas_price{txn.max_fee_per_gas >= base_fee_per_gas ? txn.effective_gas_price(base_fee_per_gas) + : txn.max_priority_fee_per_gas}; + const auto required_funds = protocol::compute_call_cost(txn, effective_gas_price, evm); + intx::uint512 maximum_cost = required_funds; + if (txn.type != TransactionType::kLegacy && txn.type != TransactionType::kAccessList) { + maximum_cost = txn.maximum_gas_cost(); } - return std::nullopt; + std::string error = "insufficient funds for gas * price + value: address " + from + " have " + intx::to_string(owned_funds) + " want " + intx::to_string(maximum_cost + txn.value); + return {std::nullopt, txn.gas_limit, {}, error, PreCheckErrorCode::kInsufficientFunds}; } ExecutionResult EVMExecutor::call( @@ -235,12 +227,10 @@ ExecutionResult EVMExecutor::call( const silkworm::Transaction& txn, const Tracers& tracers, bool refund, - bool gas_bailout) { - SILK_DEBUG << "EVMExecutor::call: blockNumber: " << block.header.number << " gas_limit: " << txn.gas_limit << " refund: " << refund - << " gas_bailout: " << gas_bailout << " transaction: " << rpc::Transaction{txn}; - + bool bailout) { auto& svc = use_service(workers_); - EVM evm{block, ibs_state_, config_, gas_bailout}; + EVM evm{block, execution_processor_.get_ibs_state(), config_, bailout}; + evm.analysis_cache = svc.get_analysis_cache(); evm.state_pool = svc.get_object_pool(); evm.beneficiary = rule_set_->get_beneficiary(block.header); @@ -253,68 +243,22 @@ ExecutionResult EVMExecutor::call( if (!txn.sender()) { return {std::nullopt, txn.gas_limit, Bytes{}, "malformed transaction: cannot recover sender"}; } - ibs_state_.access_account(*txn.sender()); - - const evmc_revision rev{evm.revision()}; - const intx::uint256 base_fee_per_gas{evm.block().header.base_fee_per_gas.value_or(0)}; - const intx::uint128 g0{protocol::intrinsic_gas(txn, rev)}; - SILKWORM_ASSERT(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) - - if (const auto pre_check_result = pre_check(evm, txn, base_fee_per_gas, g0)) { - Bytes data{}; - return {std::nullopt, txn.gas_limit, data, pre_check_result->pre_check_error, pre_check_result->pre_check_error_code}; - } - if (const auto result = evm.deduct_entry_fees(txn); result.status != EVMC_SUCCESS) { - return {std::nullopt, txn.gas_limit, {}, result.error_message, PreCheckErrorCode::kInsufficientFunds}; - } - if (txn.to.has_value()) { - ibs_state_.access_account(*txn.to); - // EVM itself increments the nonce for contract creation - ibs_state_.set_nonce(*txn.sender(), ibs_state_.get_nonce(*txn.sender()) + 1); - } - for (const AccessListEntry& ae : txn.access_list) { - ibs_state_.access_account(ae.account); - for (const evmc::bytes32& key : ae.storage_keys) { - ibs_state_.access_storage(ae.account, key); - } + if (const auto result = protocol::validate_call_precheck(txn, evm); + result != ValidationResult::kOk) { + return convert_validated_precheck(result, block, txn, evm); } - CallResult result; - try { - SILK_DEBUG << "EVMExecutor::call execute on EVM txn: " << &txn << " g0: " << static_cast(g0) << " start"; - result = evm.execute(txn, txn.gas_limit - static_cast(g0)); - SILK_DEBUG << "EVMExecutor::call execute on EVM txn: " << &txn << " gas_left: " << result.gas_left << " end"; - } catch (const std::exception& e) { - SILK_ERROR << "exception: evm_execute: " << e.what() << "\n"; - std::string error_msg = "evm.execute: "; - error_msg.append(e.what()); - return {std::nullopt, txn.gas_limit, /* data */ {}, error_msg}; - } catch (...) { - SILK_ERROR << "exception: evm_execute: unexpected exception\n"; - return {std::nullopt, txn.gas_limit, /* data */ {}, "evm.execute: unknown exception"}; - } + const auto owned_funds = execution_processor_.get_ibs_state().get_balance(*txn.sender()); - uint64_t gas_left{result.gas_left}; - uint64_t gas_used{txn.gas_limit - result.gas_left}; - - if (refund && !gas_bailout) { - gas_used = txn.gas_limit - refund_gas(evm, txn, result.gas_left, result.gas_refund); - gas_left = txn.gas_limit - gas_used; + if (const auto result = protocol::validate_call_funds(txn, evm, owned_funds, bailout); + result != ValidationResult::kOk) { + return convert_validated_funds(block, txn, evm, owned_funds); } - // Reward the fee recipient - const intx::uint256 priority_fee_per_gas{txn.max_fee_per_gas >= base_fee_per_gas ? txn.priority_fee_per_gas(base_fee_per_gas) - : txn.max_priority_fee_per_gas}; - SILK_DEBUG << "EVMExecutor::call evm.beneficiary: " << evm.beneficiary << " balance: " << priority_fee_per_gas * gas_used; - ibs_state_.add_to_balance(evm.beneficiary, priority_fee_per_gas * gas_used); - - for (auto& tracer : evm.tracers()) { - tracer.get().on_reward_granted(result, ibs_state_); - } - ibs_state_.finalize_transaction(rev); + const auto result = execution_processor_.call_with_evm(txn, evm, bailout, refund); - ExecutionResult exec_result{result.status, gas_left, result.data}; + ExecutionResult exec_result{result.status, result.gas_left, result.data}; SILK_DEBUG << "EVMExecutor::call call_result: " << exec_result.error_message() << " #data: " << exec_result.data.size() << " end"; @@ -334,7 +278,7 @@ Task EVMExecutor::call( auto this_executor = co_await boost::asio::this_coro::executor; const auto execution_result = co_await async_task(workers.executor(), [&]() -> ExecutionResult { auto state = state_factory(this_executor, block.header.number, chain_storage); - EVMExecutor executor{config, workers, state}; + EVMExecutor executor{block, config, workers, state}; return executor.call(block, txn, tracers, refund, gas_bailout); }); co_return execution_result; diff --git a/silkworm/rpc/core/evm_executor.hpp b/silkworm/rpc/core/evm_executor.hpp index ba2ccb254a..f042be2ea2 100644 --- a/silkworm/rpc/core/evm_executor.hpp +++ b/silkworm/rpc/core/evm_executor.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -102,12 +103,12 @@ class EVMExecutor { bool gas_bailout = false); static std::string get_error_message(int64_t error_code, const Bytes& error_data, bool full_error = true); - EVMExecutor(const silkworm::ChainConfig& config, WorkerPool& workers, std::shared_ptr state) + EVMExecutor(const silkworm::Block& block, const silkworm::ChainConfig& config, WorkerPool& workers, std::shared_ptr state) : config_(config), workers_{workers}, state_{std::move(state)}, - ibs_state_{*state_}, - rule_set_{protocol::rule_set_factory(config)} { + rule_set_{protocol::rule_set_factory(config)}, + execution_processor_{block, *rule_set_, *state_, config} { SILKWORM_ASSERT(rule_set_); if (!has_service(workers_)) { make_service(workers_); @@ -129,7 +130,7 @@ class EVMExecutor { void call_first_n(const silkworm::Block& block, uint64_t n, const Tracers& tracers = {}, bool refund = true, bool gas_bailout = false); - const IntraBlockState& get_ibs_state() { return ibs_state_; } + const IntraBlockState& get_ibs_state() { return execution_processor_.get_ibs_state(); } private: struct PreCheckResult { @@ -138,13 +139,11 @@ class EVMExecutor { }; static std::optional pre_check(const EVM& evm, const silkworm::Transaction& txn, const intx::uint256& base_fee_per_gas, const intx::uint128& g0); - uint64_t refund_gas(const EVM& evm, const silkworm::Transaction& txn, uint64_t gas_left, uint64_t gas_refund); - const silkworm::ChainConfig& config_; WorkerPool& workers_; std::shared_ptr state_; - IntraBlockState ibs_state_; protocol::RuleSetPtr rule_set_; + ExecutionProcessor execution_processor_; }; } // namespace silkworm::rpc diff --git a/silkworm/rpc/core/evm_executor_test.cpp b/silkworm/rpc/core/evm_executor_test.cpp index 83fa0a39fe..df496ff1bc 100644 --- a/silkworm/rpc/core/evm_executor_test.cpp +++ b/silkworm/rpc/core/evm_executor_test.cpp @@ -74,7 +74,7 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { silkworm::Block block{}; block.header.number = block_number; - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}); CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "intrinsic gas too low: have 0, want 53000"); @@ -88,7 +88,7 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { txn.max_fee_per_gas = 0x2; txn.set_sender(0xa872626373628737383927236382161739290870_address); - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}); CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "fee cap less than block base fee: address 0xa872626373628737383927236382161739290870, gasFeeCap: 2 baseFee: 7"); @@ -103,7 +103,7 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { txn.set_sender(0xa872626373628737383927236382161739290870_address); txn.max_priority_fee_per_gas = 0x18; - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}); CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "tip higher than fee cap: address 0xa872626373628737383927236382161739290870, tip: 24 gasFeeCap: 2"); @@ -121,15 +121,15 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { txn.gas_limit = 60000; txn.set_sender(0xa872626373628737383927236382161739290870_address); - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}); CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "insufficient funds for gas * price + value: address 0xa872626373628737383927236382161739290870 have 0 want 60000"); } SECTION("doesn't fail if transaction cost greater user amount && gasBailout == true") { - EXPECT_CALL(transaction, get(_, _)).Times(7).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{}; })); - EXPECT_CALL(transaction, get_one(_, _)).Times(7).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); + EXPECT_CALL(transaction, get(_, _)).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{}; })); + EXPECT_CALL(transaction, get_one(_, _)).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); silkworm::Block block{}; block.header.base_fee_per_gas = 0x1; @@ -139,7 +139,7 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { txn.gas_limit = 60000; txn.set_sender(0xa872626373628737383927236382161739290870_address); - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}, false, /* gasBailout */ true); executor.reset(); CHECK(result.error_code == 0); @@ -155,8 +155,8 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { }; SECTION("call returns SUCCESS") { - EXPECT_CALL(transaction, get(_, _)).Times(7).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{}; })); - EXPECT_CALL(transaction, get_one(_, _)).Times(7).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); + EXPECT_CALL(transaction, get(_, _)).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return KeyValue{}; })); + EXPECT_CALL(transaction, get_one(_, _)).WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); silkworm::Block block{}; block.header.number = block_number; @@ -165,7 +165,7 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { txn.set_sender(0xa872626373628737383927236382161739290870_address); txn.access_list = access_list; - EVMExecutor executor{*chain_config_ptr, workers, state}; + EVMExecutor executor{block, *chain_config_ptr, workers, state}; const auto result = executor.call(block, txn, {}, true, /* gasBailout */ true); CHECK(result.error_code == 0); } diff --git a/silkworm/rpc/core/evm_trace.cpp b/silkworm/rpc/core/evm_trace.cpp index 6bc5ac2f10..a5e5861d6b 100644 --- a/silkworm/rpc/core/evm_trace.cpp +++ b/silkworm/rpc/core/evm_trace.cpp @@ -1468,7 +1468,7 @@ Task> TraceCallExecutor::trace_block_transactions(c std::shared_ptr ibs_tracer = std::make_shared(state_addresses); auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; std::vector trace_call_result(transactions.size()); for (size_t index = 0; index < transactions.size(); index++) { @@ -1531,7 +1531,7 @@ Task TraceCallExecutor::trace_calls(const silkworm::Block& StateAddresses state_addresses(initial_ibs); auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number); - EVMExecutor executor{chain_config, workers_, state}; + EVMExecutor executor{block, chain_config, workers_, state}; std::shared_ptr ibs_tracer = std::make_shared(state_addresses); @@ -1591,7 +1591,7 @@ Task TraceCallExecutor::trace_deploy_transaction(const silkwo silkworm::IntraBlockState initial_ibs{*state}; auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; TraceDeployResult result; @@ -1650,7 +1650,7 @@ Task TraceCallExecutor::trace_transaction_entries(const Tran auto state = tx_.create_state(current_executor, chain_storage_, block_number - 1); silkworm::IntraBlockState initial_ibs{*state}; auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; executor.call_first_n(block, transaction_with_block.transaction.transaction_index); @@ -1676,7 +1676,7 @@ Task TraceCallExecutor::trace_transaction_error(const TransactionWi silkworm::IntraBlockState initial_ibs{*state}; auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; executor.call_first_n(block, transaction_with_block.transaction.transaction_index); @@ -1704,7 +1704,7 @@ Task TraceCallExecutor::trace_operations(const Transactio silkworm::IntraBlockState initial_ibs{*state}; auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; executor.call_first_n(block, transaction_with_block.transaction.transaction_index); @@ -1734,7 +1734,7 @@ Task TraceCallExecutor::trace_touch_block(const silkworm::BlockWithHash& b silkworm::IntraBlockState initial_ibs{*state}; auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number - 1); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; for (size_t i = 0; i < block.transactions.size(); i++) { auto tracer = std::make_shared(address, initial_ibs); @@ -1842,7 +1842,7 @@ Task TraceCallExecutor::execute( tracers.push_back(tracer); auto curr_state = tx_.create_state(current_executor, chain_storage_, block_number); - EVMExecutor executor{chain_config, workers_, curr_state}; + EVMExecutor executor{block, chain_config, workers_, curr_state}; for (std::size_t idx{0}; idx < transaction.transaction_index; idx++) { silkworm::Transaction txn{block.transactions[idx]}; const auto execution_result = executor.call(block, txn, tracers, /*refund=*/true, gas_bailout);