From 33b16e76a6aa0f8bea58affa7e2ae2c03e6acfaa Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Mon, 28 Oct 2024 15:12:47 -0300 Subject: [PATCH 1/6] Add final refund in processor --- cmd/dev/check_changes.cpp | 2 +- cmd/dev/scan_txs.cpp | 2 +- cmd/state-transition/state_transition.cpp | 2 +- cmd/test/ethereum.cpp | 2 +- silkworm/core/execution/evm.hpp | 10 +- silkworm/core/execution/execution.hpp | 4 +- silkworm/core/execution/execution_test.cpp | 4 +- silkworm/core/execution/processor.cpp | 62 ++++++-- silkworm/core/execution/processor.hpp | 7 +- silkworm/core/execution/processor_test.cpp | 149 ++++++++++++++++-- silkworm/core/protocol/blockchain.cpp | 7 +- silkworm/core/protocol/blockchain.hpp | 4 +- silkworm/core/types/gas_prices.hpp | 16 ++ silkworm/node/db/access_layer_test.cpp | 6 +- silkworm/node/stagedsync/stages/_test.cpp | 6 +- silkworm/node/stagedsync/stages/stage.cpp | 15 ++ silkworm/node/stagedsync/stages/stage.hpp | 6 + .../stagedsync/stages/stage_execution.cpp | 2 +- .../stages/stage_history_index_test.cpp | 6 +- third_party/evmone | 2 +- 20 files changed, 264 insertions(+), 50 deletions(-) create mode 100644 silkworm/core/types/gas_prices.hpp diff --git a/cmd/dev/check_changes.cpp b/cmd/dev/check_changes.cpp index 37d86cf9..d8d88d3b 100644 --- a/cmd/dev/check_changes.cpp +++ b/cmd/dev/check_changes.cpp @@ -94,7 +94,7 @@ int main(int argc, char* argv[]) { db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num}; - ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}}; + ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}, {}}; processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; diff --git a/cmd/dev/scan_txs.cpp b/cmd/dev/scan_txs.cpp index f3945902..9aac55d6 100644 --- a/cmd/dev/scan_txs.cpp +++ b/cmd/dev/scan_txs.cpp @@ -91,7 +91,7 @@ int main(int argc, char* argv[]) { db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num}; - ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}}; + ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}, {}}; processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; diff --git a/cmd/state-transition/state_transition.cpp b/cmd/state-transition/state_transition.cpp index 2a61692a..26987853 100644 --- a/cmd/state-transition/state_transition.cpp +++ b/cmd/state-transition/state_transition.cpp @@ -327,7 +327,7 @@ void StateTransition::run() { auto block = get_block(*state, config); auto txn = get_transaction(expectedSubState); - ExecutionProcessor processor{block, *ruleSet, *state, config, {}}; + ExecutionProcessor processor{block, *ruleSet, *state, config, {}, {}}; Receipt receipt; const evmc_revision rev{config.revision(block.header)}; diff --git a/cmd/test/ethereum.cpp b/cmd/test/ethereum.cpp index 7d160748..67737b30 100644 --- a/cmd/test/ethereum.cpp +++ b/cmd/test/ethereum.cpp @@ -254,7 +254,7 @@ RunResults blockchain_test(const nlohmann::json& json_test) { InMemoryState state; init_pre_state(json_test["pre"], state); - Blockchain blockchain{state, config, genesis_block, {}}; + Blockchain blockchain{state, config, genesis_block, {}, {}}; blockchain.state_pool = &execution_state_pool; blockchain.exo_evm = exo_evm; diff --git a/silkworm/core/execution/evm.hpp b/silkworm/core/execution/evm.hpp index 905a67ae..d8d54228 100644 --- a/silkworm/core/execution/evm.hpp +++ b/silkworm/core/execution/evm.hpp @@ -32,6 +32,14 @@ #include namespace silkworm { +struct ExecutionResult { + // inclusion_fee+overhead_fee+storage_fee == receipt.cumulative_gas_used*effective_price + uint64_t discounted_storage_gas_consumed{0}; + uint64_t cpu_gas_consumed{0}; + intx::uint256 overhead_fee; //approx. => cpu_gas_consumed * overhead_price + intx::uint256 inclusion_fee; //approx. => cpu_gas_consumed * inclusion_price + intx::uint256 storage_fee; //exactly => discounted_storage_gas_consumed * effective_price +}; struct CallResult { evmc_status_code status{EVMC_SUCCESS}; @@ -112,7 +120,7 @@ class EVM { gas_params_ = gas_params; } - const evmone::gas_parameters& get_gas_params() { + const evmone::gas_parameters get_gas_params() { return gas_params_; } diff --git a/silkworm/core/execution/execution.hpp b/silkworm/core/execution/execution.hpp index 667844bb..5c08da5f 100644 --- a/silkworm/core/execution/execution.hpp +++ b/silkworm/core/execution/execution.hpp @@ -41,12 +41,12 @@ namespace silkworm { * @param state The Ethereum state at the beginning of the block. */ [[nodiscard]] inline ValidationResult execute_block(const Block& block, State& state, - const ChainConfig& chain_config, const evmone::gas_parameters& gas_params) noexcept { + const ChainConfig& chain_config, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices) noexcept { auto rule_set{protocol::rule_set_factory(chain_config)}; if (!rule_set) { return ValidationResult::kUnknownProtocolRuleSet; } - ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, gas_prices}; std::vector receipts; return processor.execute_and_write_block(receipts); } diff --git a/silkworm/core/execution/execution_test.cpp b/silkworm/core/execution/execution_test.cpp index 05f4514e..053aa0ce 100644 --- a/silkworm/core/execution/execution_test.cpp +++ b/silkworm/core/execution/execution_test.cpp @@ -76,7 +76,7 @@ TEST_CASE("Execute two blocks") { // Execute first block // --------------------------------------- - REQUIRE(execute_block(block, state, test::kLondonConfig, {}) == ValidationResult::kOk); + REQUIRE(execute_block(block, state, test::kLondonConfig, {}, {}) == ValidationResult::kOk); auto contract_address{create_address(sender, /*nonce=*/0)}; std::optional contract_account{state.read_account(contract_address)}; @@ -114,7 +114,7 @@ TEST_CASE("Execute two blocks") { block.transactions[0].data = *from_hex(new_val); block.transactions[0].max_priority_fee_per_gas = 20 * kGiga; - REQUIRE(execute_block(block, state, test::kLondonConfig, {}) == ValidationResult::kOk); + REQUIRE(execute_block(block, state, test::kLondonConfig, {}, {}) == ValidationResult::kOk); storage0 = state.read_storage(contract_address, kDefaultIncarnation, storage_key0); CHECK(to_hex(storage0) == new_val); diff --git a/silkworm/core/execution/processor.cpp b/silkworm/core/execution/processor.cpp index 6faff987..b91c36ff 100644 --- a/silkworm/core/execution/processor.cpp +++ b/silkworm/core/execution/processor.cpp @@ -26,14 +26,16 @@ namespace silkworm { ExecutionProcessor::ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, - const ChainConfig& config, const evmone::gas_parameters& gas_params) - : state_{state}, rule_set_{rule_set}, evm_{block, state_, config, gas_params} { + const ChainConfig& config, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices) + : state_{state}, rule_set_{rule_set}, evm_{block, state_, config, gas_params}, gas_params_{gas_params}, gas_prices_{gas_prices} { evm_.beneficiary = rule_set.get_beneficiary(block.header); } -void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& receipt) noexcept { +ExecutionResult ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& receipt) noexcept { assert(protocol::validate_transaction(txn, state_, available_gas()) == ValidationResult::kOk); + ExecutionResult res; + // Optimization: since receipt.logs might have some capacity, let's reuse it. std::swap(receipt.logs, state_.logs()); @@ -71,9 +73,10 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re state_.subtract_from_balance(*txn.from, txn.total_data_gas() * data_gas_price); const auto eos_evm_version = evm_.get_eos_evm_version(); - const auto& gas_params = evm_.get_gas_params(); + intx::uint256 inclusion_price; + if( eos_evm_version >= 3 ) inclusion_price = std::min(txn.max_priority_fee_per_gas, txn.max_fee_per_gas - base_fee_per_gas); - const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, gas_params)}; + const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, gas_params_)}; assert(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) const CallResult vm_res{evm_.execute(txn, txn.gas_limit - static_cast(g0))}; @@ -90,9 +93,9 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re auto gas_left = vm_res.gas_left; if(contract_creation) { if( vm_res.status == EVMC_SUCCESS ) { - storage_gas_consumed += gas_params.G_txcreate; //correct storage gas consumed to account for initial G_txcreate storage gas + storage_gas_consumed += gas_params_.G_txcreate; //correct storage gas consumed to account for initial G_txcreate storage gas } else { - gas_left += gas_params.G_txcreate; + gas_left += gas_params_.G_txcreate; } } @@ -102,14 +105,46 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re gas_left += static_cast(vm_res_gas_state.collapse()); gas_used = txn.gas_limit - gas_left; assert(vm_res_gas_state.cpu_gas_refund() == 0); - const auto total_storage_gas_consumed = vm_res_gas_state.storage_gas_consumed(); - assert(gas_used > static_cast(total_storage_gas_consumed)); - const auto total_cpu_gas_consumed = gas_used - static_cast(total_storage_gas_consumed); - (void)total_cpu_gas_consumed; + const auto total_storage_gas_consumed = static_cast(vm_res_gas_state.storage_gas_consumed()); + assert(gas_used > total_storage_gas_consumed); + const auto total_cpu_gas_consumed = gas_used - total_storage_gas_consumed; - // award the fee recipient const intx::uint256 price{evm_.config().protocol_rule_set == protocol::RuleSetType::kTrust ? effective_gas_price : txn.priority_fee_per_gas(base_fee_per_gas)}; - state_.add_to_balance(evm_.beneficiary, price * gas_used); + + res.cpu_gas_consumed = total_cpu_gas_consumed; + res.discounted_storage_gas_consumed = static_cast(total_storage_gas_consumed); + res.inclusion_fee = intx::uint256(total_cpu_gas_consumed)*inclusion_price; + res.storage_fee = intx::uint256(total_storage_gas_consumed)*price; + + auto final_fee = price * gas_used; + if(gas_prices_.storage_price >= gas_prices_.overhead_price) { + intx::uint256 gas_refund = intx::uint256(total_cpu_gas_consumed); + gas_refund *= intx::uint256(gas_prices_.storage_price-gas_prices_.overhead_price); + gas_refund /= price; + + SILKWORM_ASSERT(gas_refund <= gas_used); + gas_left += static_cast(gas_refund); + assert(txn.gas_limit >= gas_left); + gas_used = txn.gas_limit - gas_left; + SILKWORM_ASSERT(gas_used >= total_storage_gas_consumed); + final_fee = price * gas_used; + + assert(final_fee >= res.storage_fee); + const auto overhead_and_inclusion_fee = final_fee - res.storage_fee; + if( overhead_and_inclusion_fee >= res.inclusion_fee ) { + res.overhead_fee = overhead_and_inclusion_fee - res.inclusion_fee; + } else { + res.inclusion_fee = overhead_and_inclusion_fee; + res.overhead_fee = 0; + } + } else { + res.overhead_fee = final_fee - res.inclusion_fee - res.storage_fee; + } + + assert(final_fee == res.inclusion_fee + res.storage_fee + res.overhead_fee); + + // award the fee recipient + state_.add_to_balance(evm_.beneficiary, final_fee); state_.add_to_balance(*txn.from, price * gas_left); } @@ -127,6 +162,7 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re receipt.cumulative_gas_used = cumulative_gas_used_; receipt.bloom = logs_bloom(state_.logs()); std::swap(receipt.logs, state_.logs()); + return res; } uint64_t ExecutionProcessor::available_gas() const noexcept { diff --git a/silkworm/core/execution/processor.hpp b/silkworm/core/execution/processor.hpp index 071dcbfc..2a004c41 100644 --- a/silkworm/core/execution/processor.hpp +++ b/silkworm/core/execution/processor.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace silkworm { @@ -33,13 +34,13 @@ class ExecutionProcessor { ExecutionProcessor(const ExecutionProcessor&) = delete; ExecutionProcessor& operator=(const ExecutionProcessor&) = delete; - ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, const ChainConfig& config, const evmone::gas_parameters& gas_params); + ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, const ChainConfig& config, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices); /** * Execute a transaction, but do not write to the DB yet. * Precondition: transaction must be valid. */ - void execute_transaction(const Transaction& txn, Receipt& receipt) noexcept; + ExecutionResult execute_transaction(const Transaction& txn, Receipt& receipt) noexcept; //! \brief Execute the block and write the result to the DB. //! \remarks Warning: This method does not verify state root; pre-Byzantium receipt root isn't validated either. @@ -71,6 +72,8 @@ class ExecutionProcessor { IntraBlockState state_; protocol::IRuleSet& rule_set_; EVM evm_; + evmone::gas_parameters gas_params_; + gas_prices_t gas_prices_; }; } // namespace silkworm diff --git a/silkworm/core/execution/processor_test.cpp b/silkworm/core/execution/processor_test.cpp index bdfe3e63..e5ba593b 100644 --- a/silkworm/core/execution/processor_test.cpp +++ b/silkworm/core/execution/processor_test.cpp @@ -47,7 +47,7 @@ TEST_CASE("Zero gas price") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; Receipt receipt; processor.execute_transaction(txn, receipt); @@ -87,7 +87,7 @@ TEST_CASE("No refund on error") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; Transaction txn{ {.nonce = nonce, @@ -182,7 +182,7 @@ TEST_CASE("Self-destruct") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; processor.evm().state().add_to_balance(originator, kEther); processor.evm().state().set_code(caller_address, caller_code); @@ -332,7 +332,7 @@ TEST_CASE("Out of Gas during account re-creation") { }; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; @@ -375,7 +375,7 @@ TEST_CASE("Empty suicide beneficiary") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; @@ -416,7 +416,7 @@ TEST_CASE("EOS EVM refund v2") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; Transaction txn{ {.nonce = nonce, @@ -506,7 +506,7 @@ TEST_CASE("EOS EVM message filter") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; Transaction txn{ {.nonce = nonce, @@ -610,7 +610,7 @@ TEST_CASE("EOS EVM message filter revert") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; Transaction txn{ {.nonce = nonce, @@ -739,7 +739,7 @@ TEST_CASE("EOS EVM No fee burn when chain uses trust ruleset") { InMemoryState state; auto rule_set{protocol::rule_set_factory(chain_config)}; - ExecutionProcessor processor{block, *rule_set, state, chain_config, {}}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, {}, {}}; Transaction txn{{ .type = TransactionType::kDynamicFee, @@ -822,7 +822,7 @@ TEST_CASE("EOS EVM v3 contract creation") { evmone::gas_parameters gas_params; gas_params.G_txcreate = 50000; - ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, {}}; Transaction txn{{ .type = TransactionType::kDynamicFee, @@ -872,4 +872,133 @@ TEST_CASE("EOS EVM v3 contract creation") { CHECK(receipt4.cumulative_gas_used == 79092+222156+88400); } +TEST_CASE("EOS EVM v3 final refund") { + + intx::uint256 max_priority_fee_per_gas = 5 * kGiga; + intx::uint256 max_fee_per_gas = 105 * kGiga; + + auto deploy_contract = [&](const ChainConfig& chain_config, uint64_t gas_limit, const gas_prices_t& gas_prices) -> auto { + + Block block{}; + block.header.number = 9'069'000; + block.header.gas_limit = 0x7fffffff; + block.header.beneficiary = 0xbbbbbbbbbbbbbbbbbbbbbbbb0000000000000000_address; + block.header.nonce = eosevm::version_to_nonce(3); + block.header.base_fee_per_gas = gas_prices.get_base_price(); + + evmc::address caller{0x834e9b529ac9fa63b39a06f8d8c9b0d6791fa5df_address}; + uint64_t nonce{3}; + + /* + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + contract HeavyInit { + // Example of 10 storage slots + uint256[10] public storageSlots; + constructor() { + for (uint256 i = 0; i < 10; i++) { + storageSlots[i] = 1; + } + } + function retrieve(uint256 index) public view returns (uint256){ + return storageSlots[index]; + } + } + */ + Bytes code{*from_hex("6080604052348015600e575f80fd5b505f5b600a811015603c5760015f82600a8110602b57602a6041565b5b018190555080806001019150506011565b50606e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6101ba8061007b5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80635387694b146100385780638f88708b14610068575b5f80fd5b610052600480360381019061004d9190610104565b610098565b60405161005f919061013e565b60405180910390f35b610082600480360381019061007d9190610104565b6100b0565b60405161008f919061013e565b60405180910390f35b5f81600a81106100a6575f80fd5b015f915090505481565b5f8082600a81106100c4576100c3610157565b5b01549050919050565b5f80fd5b5f819050919050565b6100e3816100d1565b81146100ed575f80fd5b50565b5f813590506100fe816100da565b92915050565b5f60208284031215610119576101186100cd565b5b5f610126848285016100f0565b91505092915050565b610138816100d1565b82525050565b5f6020820190506101515f83018461012f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea2646970667358221220702d8bb14041667c80c0f9380e5ddc6b1b1e6fdcf1e572f9af923407673c305e64736f6c634300081a0033")}; + + InMemoryState state; + auto rule_set{protocol::rule_set_factory(chain_config)}; + + evmone::gas_parameters gas_params; + gas_params.G_txcreate = 50000; + + ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, gas_prices}; + + Transaction txn{{ + .type = TransactionType::kDynamicFee, + .nonce = nonce, + .max_priority_fee_per_gas = max_priority_fee_per_gas, + .max_fee_per_gas = max_fee_per_gas, + .gas_limit = gas_limit, + .data = code + }, + false, // odd_y_parity + 1, // r + 1, // s + }; + + processor.evm().state().add_to_balance(caller, kEther*100); + processor.evm().state().set_nonce(caller, nonce); + txn.from = caller; + + Receipt receipt1; + auto res = processor.execute_transaction(txn, receipt1); + + return std::make_tuple( + res, + receipt1, + processor.evm().state().get_balance(block.header.beneficiary), + txn.effective_gas_price(*block.header.base_fee_per_gas) + ); + }; + + // g_txcreate = 50000 + // g0 = 29092 (cpu real) + g_txcreate (storage spec) = 79092 + // init = 23156 (cpu real) + 171000 (storage spec) + 28000 (cpu spec) = 222156 + // code_deposit = 442 * 200 = 88400 + + // cpu_gas_consumed = 23156 + 28000 + 29092 = 80248 + // storage_gas_consumed = 50000 + 171000 + 88400 = 309400 + + CHECK(79092+222156+88400 == 80248+309400); + + gas_prices_t gp; + gp.overhead_price = 70 * kGiga; + gp.storage_price = 80 * kGiga; + auto base_fee_per_gas = gp.get_base_price(); + auto inclusion_price = std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); + + // storage_price >= overhead_price + uint64_t expected_refund = 9440; + + auto [res, receipt, balance, effective_gas_price] = deploy_contract(kEOSEVMMainnetConfig, 79092+222156+88400, gp); + CHECK(receipt.success == true); + CHECK(receipt.cumulative_gas_used == 79092+222156+88400-expected_refund); + + auto inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); + CHECK(res.inclusion_fee == inclusion_fee); + CHECK(res.cpu_gas_consumed == 80248); + + auto storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; + CHECK(res.storage_fee == storage_fee); + CHECK(res.discounted_storage_gas_consumed == 309400); + + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+309400); + CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); + + // storage_price < overhead_price + gp.overhead_price = 80 * kGiga; + gp.storage_price = 70 * kGiga; + base_fee_per_gas = gp.get_base_price(); + inclusion_price = std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); + expected_refund = 0; + + std::tie(res, receipt, balance, effective_gas_price) = deploy_contract(kEOSEVMMainnetConfig, 79092+222156+88400, gp); + CHECK(receipt.success == true); + CHECK(receipt.cumulative_gas_used == 79092+222156+88400-expected_refund); + + inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); + CHECK(res.inclusion_fee == inclusion_fee); + CHECK(res.cpu_gas_consumed == 80248); + + storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; + CHECK(res.storage_fee == storage_fee); + CHECK(res.discounted_storage_gas_consumed == 309400); + + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+309400); + CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); +} + + } // namespace silkworm diff --git a/silkworm/core/protocol/blockchain.cpp b/silkworm/core/protocol/blockchain.cpp index 6f45edca..02cf7eb1 100644 --- a/silkworm/core/protocol/blockchain.cpp +++ b/silkworm/core/protocol/blockchain.cpp @@ -17,12 +17,11 @@ #include "blockchain.hpp" #include -#include namespace silkworm::protocol { -Blockchain::Blockchain(State& state, const ChainConfig& config, const Block& genesis_block, const evmone::gas_parameters& gas_params) - : state_{state}, config_{config}, rule_set_{rule_set_factory(config)}, gas_params_{gas_params} { +Blockchain::Blockchain(State& state, const ChainConfig& config, const Block& genesis_block, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices ) + : state_{state}, config_{config}, rule_set_{rule_set_factory(config)}, gas_params_{gas_params}, gas_prices_{gas_prices} { prime_state_with_genesis(genesis_block); } @@ -91,7 +90,7 @@ ValidationResult Blockchain::insert_block(Block& block, bool check_state_root) { } ValidationResult Blockchain::execute_block(const Block& block, bool check_state_root) { - ExecutionProcessor processor{block, *rule_set_, state_, config_, gas_params_}; + ExecutionProcessor processor{block, *rule_set_, state_, config_, gas_params_, gas_prices_}; processor.evm().state_pool = state_pool; processor.evm().exo_evm = exo_evm; diff --git a/silkworm/core/protocol/blockchain.hpp b/silkworm/core/protocol/blockchain.hpp index 27e3a4dc..55d31313 100644 --- a/silkworm/core/protocol/blockchain.hpp +++ b/silkworm/core/protocol/blockchain.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace silkworm::protocol { @@ -40,7 +41,7 @@ class Blockchain { * In the beginning the state must have the genesis allocation. * Later on the state may only be modified by the created instance of Blockchain. */ - explicit Blockchain(State& state, const ChainConfig& config, const Block& genesis_block, const evmone::gas_parameters& gas_params); + explicit Blockchain(State& state, const ChainConfig& config, const Block& genesis_block, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices); // Not copyable nor movable Blockchain(const Blockchain&) = delete; @@ -72,6 +73,7 @@ class Blockchain { std::unordered_map bad_blocks_; std::vector receipts_; evmone::gas_parameters gas_params_; + gas_prices_t gas_prices_; }; } // namespace silkworm::protocol diff --git a/silkworm/core/types/gas_prices.hpp b/silkworm/core/types/gas_prices.hpp new file mode 100644 index 00000000..780e7d1b --- /dev/null +++ b/silkworm/core/types/gas_prices.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include + +namespace silkworm { + +struct gas_prices_t { + uint64_t overhead_price{0}; + uint64_t storage_price{0}; + + uint64_t get_base_price()const { + return std::max(overhead_price, storage_price); + } +}; + +} // namespace silkworm diff --git a/silkworm/node/db/access_layer_test.cpp b/silkworm/node/db/access_layer_test.cpp index c693e72d..48197f4b 100644 --- a/silkworm/node/db/access_layer_test.cpp +++ b/silkworm/node/db/access_layer_test.cpp @@ -606,19 +606,19 @@ TEST_CASE("Account") { block1.header.number = 1; block1.header.beneficiary = miner_a; // miner_a gets one block reward - REQUIRE(execute_block(block1, buffer, test::kFrontierConfig, {}) == ValidationResult::kOk); + REQUIRE(execute_block(block1, buffer, test::kFrontierConfig, {}, {}) == ValidationResult::kOk); Block block2; block2.header.number = 2; block2.header.beneficiary = miner_b; // miner_a gets nothing - REQUIRE(execute_block(block2, buffer, test::kFrontierConfig, {}) == ValidationResult::kOk); + REQUIRE(execute_block(block2, buffer, test::kFrontierConfig, {}, {}) == ValidationResult::kOk); Block block3; block3.header.number = 3; block3.header.beneficiary = miner_a; // miner_a gets another block reward - REQUIRE(execute_block(block3, buffer, test::kFrontierConfig, {}) == ValidationResult::kOk); + REQUIRE(execute_block(block3, buffer, test::kFrontierConfig, {}, {}) == ValidationResult::kOk); buffer.write_to_db(); db::stages::write_stage_progress(txn, db::stages::kExecutionKey, 3); diff --git a/silkworm/node/stagedsync/stages/_test.cpp b/silkworm/node/stagedsync/stages/_test.cpp index b7bac313..69c0dd67 100644 --- a/silkworm/node/stagedsync/stages/_test.cpp +++ b/silkworm/node/stagedsync/stages/_test.cpp @@ -304,7 +304,7 @@ TEST_CASE("Sync Stages") { // --------------------------------------- auto expected_validation_result{magic_enum::enum_name(ValidationResult::kOk)}; auto actual_validation_result{ - magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {}))}; + magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {}, {}))}; REQUIRE(expected_validation_result == actual_validation_result); auto contract_address{create_address(sender, /*nonce=*/0)}; @@ -325,7 +325,7 @@ TEST_CASE("Sync Stages") { block.transactions[0].data = ByteView(new_val); actual_validation_result = - magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {})); + magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {}, {})); REQUIRE(expected_validation_result == actual_validation_result); // --------------------------------------- @@ -342,7 +342,7 @@ TEST_CASE("Sync Stages") { block.transactions[0].data = ByteView{new_val}; actual_validation_result = - magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {})); + magic_enum::enum_name(execute_block(block, buffer, node_settings.chain_config.value(), {}, {})); REQUIRE(expected_validation_result == actual_validation_result); REQUIRE_NOTHROW(buffer.write_to_db()); REQUIRE_NOTHROW(db::stages::write_stage_progress(txn, db::stages::kExecutionKey, 3)); diff --git a/silkworm/node/stagedsync/stages/stage.cpp b/silkworm/node/stagedsync/stages/stage.cpp index d157d179..dbb48a35 100644 --- a/silkworm/node/stagedsync/stages/stage.cpp +++ b/silkworm/node/stagedsync/stages/stage.cpp @@ -66,6 +66,21 @@ const evmone::gas_parameters& Stage::get_gas_params(db::ROTxn& txn, const Block& return last_gas_params; } +const gas_prices_t& Stage::get_gas_prices(db::ROTxn& txn, const Block& block) { + auto curr_gas_prices_index = block.get_gas_prices_index(); + if(curr_gas_prices_index != last_gas_prices_index) { + auto gas_prices = silkworm::db::read_gas_prices(txn, block); + if(gas_prices.has_value()) { + const auto& v = gas_prices.value(); + last_gas_prices = gas_prices_t(v.overhead_price, v.storage_price); + } else { + last_gas_prices=gas_prices_t{}; + } + last_gas_prices_index = curr_gas_prices_index; + } + return last_gas_prices; +} + StageError::StageError(Stage::Result err) : err_{magic_enum::enum_integer(err)}, message_{std::string(magic_enum::enum_name(err))} {} diff --git a/silkworm/node/stagedsync/stages/stage.hpp b/silkworm/node/stagedsync/stages/stage.hpp index 0059c2fb..6fd855dd 100644 --- a/silkworm/node/stagedsync/stages/stage.hpp +++ b/silkworm/node/stagedsync/stages/stage.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include namespace silkworm::stagedsync { @@ -128,6 +130,10 @@ class Stage : public Stoppable { std::optional last_consensus_parameter_index{std::nullopt}; const evmone::gas_parameters& get_gas_params(db::ROTxn& txn, const Block& block); + gas_prices_t last_gas_prices; + std::optional last_gas_prices_index{std::nullopt}; + const gas_prices_t& get_gas_prices(db::ROTxn& txn, const Block& block); + SyncContext* sync_context_; // Shared context across stages const char* stage_name_; // Human friendly identifier of the stage NodeSettings* node_settings_; // Pointer to shared node configuration settings diff --git a/silkworm/node/stagedsync/stages/stage_execution.cpp b/silkworm/node/stagedsync/stages/stage_execution.cpp index 6a6a99de..a25357dd 100644 --- a/silkworm/node/stagedsync/stages/stage_execution.cpp +++ b/silkworm/node/stagedsync/stages/stage_execution.cpp @@ -238,7 +238,7 @@ Stage::Result Execution::execute_batch(db::RWTxn& txn, BlockNum max_block_num, A log_time = now + 5s; } - ExecutionProcessor processor(block, *rule_set_, buffer, node_settings_->chain_config.value(), get_gas_params(txn, block)); + ExecutionProcessor processor(block, *rule_set_, buffer, node_settings_->chain_config.value(), get_gas_params(txn, block), get_gas_prices(txn, block)); processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; diff --git a/silkworm/node/stagedsync/stages/stage_history_index_test.cpp b/silkworm/node/stagedsync/stages/stage_history_index_test.cpp index a8cba21e..b61f9f3d 100644 --- a/silkworm/node/stagedsync/stages/stage_history_index_test.cpp +++ b/silkworm/node/stagedsync/stages/stage_history_index_test.cpp @@ -83,7 +83,7 @@ TEST_CASE("Stage History Index") { // --------------------------------------- // Execute first block // --------------------------------------- - CHECK(execute_block(block, buffer, kMainnetConfig, {}) == ValidationResult::kOk); + CHECK(execute_block(block, buffer, kMainnetConfig, {}, {}) == ValidationResult::kOk); auto contract_address{create_address(sender, /*nonce=*/0)}; // --------------------------------------- @@ -102,7 +102,7 @@ TEST_CASE("Stage History Index") { block.transactions[0].to = contract_address; block.transactions[0].data = *from_hex(new_val); - CHECK(execute_block(block, buffer, kMainnetConfig, {}) == ValidationResult::kOk); + CHECK(execute_block(block, buffer, kMainnetConfig, {}, {}) == ValidationResult::kOk); // --------------------------------------- // Execute third block @@ -120,7 +120,7 @@ TEST_CASE("Stage History Index") { block.transactions[0].to = contract_address; block.transactions[0].data = *from_hex(new_val); - CHECK(execute_block(block, buffer, kMainnetConfig, {}) == ValidationResult::kOk); + CHECK(execute_block(block, buffer, kMainnetConfig, {}, {}) == ValidationResult::kOk); buffer.write_to_db(); db::stages::write_stage_progress(txn, db::stages::kExecutionKey, 3); diff --git a/third_party/evmone b/third_party/evmone index bfac9812..2090a0d3 160000 --- a/third_party/evmone +++ b/third_party/evmone @@ -1 +1 @@ -Subproject commit bfac9812d225ba049336b63bfeac57f00f9f6506 +Subproject commit 2090a0d338769bc5ce6454747b81ad6a240010da From 9121e0a79de5d2487d2a53bd56491c568f9df0e4 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Wed, 13 Nov 2024 14:05:44 -0300 Subject: [PATCH 2/6] Update evmone with fix in G_sset scaling --- third_party/evmone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/evmone b/third_party/evmone index 2090a0d3..97081557 160000 --- a/third_party/evmone +++ b/third_party/evmone @@ -1 +1 @@ -Subproject commit 2090a0d338769bc5ce6454747b81ad6a240010da +Subproject commit 97081557a080948a1b9151256241dad449d91083 From 0c2fd600464637d40ad3a54d8325c3c73ff75171 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Thu, 14 Nov 2024 17:32:00 -0300 Subject: [PATCH 3/6] Update evmone with updated storage gas cost for v3 --- third_party/evmone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/evmone b/third_party/evmone index 97081557..72c15e00 160000 --- a/third_party/evmone +++ b/third_party/evmone @@ -1 +1 @@ -Subproject commit 97081557a080948a1b9151256241dad449d91083 +Subproject commit 72c15e004f074372d0aae59bcafe9857d893a9c9 From 24cd034eff8ca96e736470c457bec9306e47b844 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Thu, 14 Nov 2024 23:49:50 -0300 Subject: [PATCH 4/6] Fix processor_tests --- silkworm/core/execution/processor_test.cpp | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/silkworm/core/execution/processor_test.cpp b/silkworm/core/execution/processor_test.cpp index e5ba593b..a270ceeb 100644 --- a/silkworm/core/execution/processor_test.cpp +++ b/silkworm/core/execution/processor_test.cpp @@ -852,24 +852,24 @@ TEST_CASE("EOS EVM v3 contract creation") { // g_txcreate = 50000 // g0 = 29092+g_txcreate = 79092 - // init = 23156 (real) + 171000 (storage spec) + 28000 (cpu spec) = 222156 + // init = 23156 (real) + 200000 (storage spec) + 28000 (cpu spec) = 251156 // code_deposit = 442 * 200 = 88400 auto [receipt1, _unused1] = deploy_contract(kEOSEVMMainnetConfig, 79092); CHECK(receipt1.success == false); CHECK(receipt1.cumulative_gas_used == 29092); // Only the real intrinsic gas (g_txcreate is refunded) - auto [receipt2, _unused2] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 222156 - 1); + auto [receipt2, _unused2] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 251156 - 1); CHECK(receipt2.success == false); CHECK(receipt2.cumulative_gas_used == 29092+23156-1); // Only the real intrinsic+constructor - auto [receipt3, _unused3] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 222156 + 88400 - 1); + auto [receipt3, _unused3] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 251156 + 88400 - 1); CHECK(receipt3.success == false); CHECK(receipt3.cumulative_gas_used == 29092+23156); // Only the real intrinsic+constructor (full) - auto [receipt4, _unused4] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 222156 + 88400); + auto [receipt4, _unused4] = deploy_contract(kEOSEVMMainnetConfig, 79092 + 251156 + 88400); CHECK(receipt4.success == true); - CHECK(receipt4.cumulative_gas_used == 79092+222156+88400); + CHECK(receipt4.cumulative_gas_used == 79092+251156+88400); } TEST_CASE("EOS EVM v3 final refund") { @@ -945,13 +945,13 @@ TEST_CASE("EOS EVM v3 final refund") { // g_txcreate = 50000 // g0 = 29092 (cpu real) + g_txcreate (storage spec) = 79092 - // init = 23156 (cpu real) + 171000 (storage spec) + 28000 (cpu spec) = 222156 + // init = 23156 (cpu real) + 200000 (storage spec) + 28000 (cpu spec) = 251156 // code_deposit = 442 * 200 = 88400 // cpu_gas_consumed = 23156 + 28000 + 29092 = 80248 - // storage_gas_consumed = 50000 + 171000 + 88400 = 309400 + // storage_gas_consumed = 50000 + 200000 + 88400 = 338400 - CHECK(79092+222156+88400 == 80248+309400); + CHECK(79092+251156+88400 == 80248+338400); gas_prices_t gp; gp.overhead_price = 70 * kGiga; @@ -962,9 +962,9 @@ TEST_CASE("EOS EVM v3 final refund") { // storage_price >= overhead_price uint64_t expected_refund = 9440; - auto [res, receipt, balance, effective_gas_price] = deploy_contract(kEOSEVMMainnetConfig, 79092+222156+88400, gp); + auto [res, receipt, balance, effective_gas_price] = deploy_contract(kEOSEVMMainnetConfig, 79092+251156+88400, gp); CHECK(receipt.success == true); - CHECK(receipt.cumulative_gas_used == 79092+222156+88400-expected_refund); + CHECK(receipt.cumulative_gas_used == 79092+251156+88400-expected_refund); auto inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); CHECK(res.inclusion_fee == inclusion_fee); @@ -972,9 +972,9 @@ TEST_CASE("EOS EVM v3 final refund") { auto storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; CHECK(res.storage_fee == storage_fee); - CHECK(res.discounted_storage_gas_consumed == 309400); + CHECK(res.discounted_storage_gas_consumed == 338400); - CHECK(receipt.cumulative_gas_used+expected_refund == 80248+309400); + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+338400); CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); // storage_price < overhead_price @@ -984,9 +984,9 @@ TEST_CASE("EOS EVM v3 final refund") { inclusion_price = std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); expected_refund = 0; - std::tie(res, receipt, balance, effective_gas_price) = deploy_contract(kEOSEVMMainnetConfig, 79092+222156+88400, gp); + std::tie(res, receipt, balance, effective_gas_price) = deploy_contract(kEOSEVMMainnetConfig, 79092+251156+88400, gp); CHECK(receipt.success == true); - CHECK(receipt.cumulative_gas_used == 79092+222156+88400-expected_refund); + CHECK(receipt.cumulative_gas_used == 79092+251156+88400-expected_refund); inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); CHECK(res.inclusion_fee == inclusion_fee); @@ -994,9 +994,9 @@ TEST_CASE("EOS EVM v3 final refund") { storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; CHECK(res.storage_fee == storage_fee); - CHECK(res.discounted_storage_gas_consumed == 309400); + CHECK(res.discounted_storage_gas_consumed == 338400); - CHECK(receipt.cumulative_gas_used+expected_refund == 80248+309400); + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+338400); CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); } From 836777c7442333f2b17d21dfdf833ea4de13a558 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Mon, 2 Dec 2024 20:59:31 -0300 Subject: [PATCH 5/6] Refactor gas_params scaling --- cmd/dev/check_changes.cpp | 4 +- cmd/dev/scan_txs.cpp | 4 +- cmd/state-transition/state_transition.cpp | 4 +- eosevm/CMakeLists.txt | 2 + eosevm/refund_v3.cpp | 64 +++++++++++ eosevm/refund_v3.hpp | 11 ++ silkworm/core/CMakeLists.txt | 1 + silkworm/core/execution/evm.cpp | 6 +- silkworm/core/execution/evm.hpp | 12 +- silkworm/core/execution/evm_test.cpp | 107 +++++++++--------- silkworm/core/execution/execution.hpp | 4 +- silkworm/core/execution/processor.cpp | 95 +++++----------- silkworm/core/execution/processor.hpp | 9 +- silkworm/core/execution/processor_test.cpp | 102 +++++++++-------- silkworm/core/protocol/blockchain.cpp | 4 +- silkworm/core/types/gas_prices.hpp | 4 + .../stagedsync/stages/stage_execution.cpp | 7 +- silkworm/silkrpc/commands/eth_api.cpp | 12 +- silkworm/silkrpc/core/call_many.cpp | 9 +- silkworm/silkrpc/core/call_many.hpp | 2 + silkworm/silkrpc/core/estimate_gas_oracle.cpp | 10 +- silkworm/silkrpc/core/estimate_gas_oracle.hpp | 2 +- .../silkrpc/core/estimate_gas_oracle_test.cpp | 30 ++--- silkworm/silkrpc/core/evm_debug.cpp | 16 +-- silkworm/silkrpc/core/evm_executor.cpp | 55 ++++++--- silkworm/silkrpc/core/evm_executor.hpp | 5 +- silkworm/silkrpc/core/evm_executor_test.cpp | 12 +- silkworm/silkrpc/core/evm_trace.cpp | 30 ++--- silkworm/silkrpc/core/gas_parameters.cpp | 18 ++- silkworm/silkrpc/core/gas_parameters.hpp | 3 +- silkworm/silkrpc/core/rawdb/chain.cpp | 11 +- silkworm/silkrpc/core/rawdb/chain.hpp | 3 + .../silkrpc/test/mock_estimate_gas_oracle.hpp | 2 +- 33 files changed, 375 insertions(+), 285 deletions(-) create mode 100644 eosevm/refund_v3.cpp create mode 100644 eosevm/refund_v3.hpp diff --git a/cmd/dev/check_changes.cpp b/cmd/dev/check_changes.cpp index d8d88d3b..dd1cdc13 100644 --- a/cmd/dev/check_changes.cpp +++ b/cmd/dev/check_changes.cpp @@ -94,11 +94,11 @@ int main(int argc, char* argv[]) { db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num}; - ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}, {}}; + ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}}; processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; - if (const auto res{processor.execute_and_write_block(receipts)}; res != ValidationResult::kOk) { + if (const auto res{processor.execute_and_write_block(receipts, {})}; res != ValidationResult::kOk) { log::Error() << "Failed to execute block " << block_num; continue; } diff --git a/cmd/dev/scan_txs.cpp b/cmd/dev/scan_txs.cpp index 9aac55d6..b138132c 100644 --- a/cmd/dev/scan_txs.cpp +++ b/cmd/dev/scan_txs.cpp @@ -91,12 +91,12 @@ int main(int argc, char* argv[]) { db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num}; - ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}, {}}; + ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}}; processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; // Execute the block and retrieve the receipts - if (const auto res{processor.execute_and_write_block(receipts)}; res != ValidationResult::kOk) { + if (const auto res{processor.execute_and_write_block(receipts, {})}; res != ValidationResult::kOk) { std::cerr << "Validation error " << static_cast(res) << " at block " << block_num << "\n"; } diff --git a/cmd/state-transition/state_transition.cpp b/cmd/state-transition/state_transition.cpp index 26987853..04480f6c 100644 --- a/cmd/state-transition/state_transition.cpp +++ b/cmd/state-transition/state_transition.cpp @@ -327,7 +327,7 @@ void StateTransition::run() { auto block = get_block(*state, config); auto txn = get_transaction(expectedSubState); - ExecutionProcessor processor{block, *ruleSet, *state, config, {}, {}}; + ExecutionProcessor processor{block, *ruleSet, *state, config, {}}; Receipt receipt; const evmc_revision rev{config.revision(block.header)}; @@ -344,7 +344,7 @@ void StateTransition::run() { block_validation == ValidationResult::kOk && pre_txn_validation == ValidationResult::kOk && txn_validation == ValidationResult::kOk) { - processor.execute_transaction(txn, receipt); + processor.execute_transaction(txn, receipt, {}); processor.evm().state().write_to_db(block.header.number); } else { cleanup_error_block(block, processor, rev); diff --git a/eosevm/CMakeLists.txt b/eosevm/CMakeLists.txt index 5b94fd8c..8b017053 100644 --- a/eosevm/CMakeLists.txt +++ b/eosevm/CMakeLists.txt @@ -30,6 +30,8 @@ set(EOS_EVM_PUBLIC_LIBS evmc tl::expected nlohmann_json::nlohmann_json + evmone + Microsoft.GSL::GSL ) target_link_libraries( diff --git a/eosevm/refund_v3.cpp b/eosevm/refund_v3.cpp new file mode 100644 index 00000000..433eecb9 --- /dev/null +++ b/eosevm/refund_v3.cpp @@ -0,0 +1,64 @@ +#include "refund_v3.hpp" + +namespace eosevm { + +std::tuple gas_refund_v3(uint64_t eos_evm_version, const silkworm::CallResult& vm_res, const silkworm::Transaction& txn, + const evmone::gas_parameters scaled_gas_params, const intx::uint256& price, const silkworm::gas_prices_t& gas_prices, const intx::uint256& inclusion_price) { + + uint64_t storage_gas_consumed{vm_res.storage_gas_consumed}; + const bool contract_creation{!txn.to}; + auto gas_left = vm_res.gas_left; + if(contract_creation) { + if( vm_res.status == EVMC_SUCCESS ) { + storage_gas_consumed += scaled_gas_params.G_txcreate; //correct storage gas consumed to account for initial G_txcreate storage gas + } else { + gas_left += scaled_gas_params.G_txcreate; + } + } + + evmone::gas_state_t vm_res_gas_state(eos_evm_version, static_cast(vm_res.gas_refund), + static_cast(storage_gas_consumed), static_cast(vm_res.storage_gas_refund), static_cast(vm_res.speculative_cpu_gas_consumed)); + + gas_left += static_cast(vm_res_gas_state.collapse()); + auto gas_used = txn.gas_limit - gas_left; + assert(vm_res_gas_state.cpu_gas_refund() == 0); + const auto total_storage_gas_consumed = static_cast(vm_res_gas_state.storage_gas_consumed()); + assert(gas_used > total_storage_gas_consumed); + const auto total_cpu_gas_consumed = gas_used - total_storage_gas_consumed; + + silkworm::ExecutionResult res; + res.cpu_gas_consumed = total_cpu_gas_consumed; + res.discounted_storage_gas_consumed = static_cast(total_storage_gas_consumed); + res.inclusion_fee = intx::uint256(total_cpu_gas_consumed)*inclusion_price; + res.storage_fee = intx::uint256(total_storage_gas_consumed)*price; + + auto final_fee = price * gas_used; + if(gas_prices.storage_price >= gas_prices.overhead_price) { + intx::uint256 gas_refund = intx::uint256(total_cpu_gas_consumed); + gas_refund *= intx::uint256(gas_prices.storage_price-gas_prices.overhead_price); + gas_refund /= price; + + SILKWORM_ASSERT(gas_refund <= gas_used); + gas_left += static_cast(gas_refund); + assert(txn.gas_limit >= gas_left); + gas_used = txn.gas_limit - gas_left; + SILKWORM_ASSERT(gas_used >= total_storage_gas_consumed); + final_fee = price * gas_used; + + assert(final_fee >= res.storage_fee); + const auto overhead_and_inclusion_fee = final_fee - res.storage_fee; + if( overhead_and_inclusion_fee >= res.inclusion_fee ) { + res.overhead_fee = overhead_and_inclusion_fee - res.inclusion_fee; + } else { + res.inclusion_fee = overhead_and_inclusion_fee; + res.overhead_fee = 0; + } + } else { + res.overhead_fee = final_fee - res.inclusion_fee - res.storage_fee; + } + + assert(final_fee == res.inclusion_fee + res.storage_fee + res.overhead_fee); + return std::make_tuple(res, final_fee, gas_used, gas_left); +} + +} // namespace eosevm diff --git a/eosevm/refund_v3.hpp b/eosevm/refund_v3.hpp new file mode 100644 index 00000000..3f862089 --- /dev/null +++ b/eosevm/refund_v3.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace eosevm { + +std::tuple gas_refund_v3(uint64_t eos_evm_version, const silkworm::CallResult& vm_res, const silkworm::Transaction& txn, + const evmone::gas_parameters scaled_gas_params, const intx::uint256& price, const silkworm::gas_prices_t& gas_prices, const intx::uint256& inclusion_price); + +} // namespace eosevm diff --git a/silkworm/core/CMakeLists.txt b/silkworm/core/CMakeLists.txt index 10d71f00..1a97ea4a 100644 --- a/silkworm/core/CMakeLists.txt +++ b/silkworm/core/CMakeLists.txt @@ -48,6 +48,7 @@ set(SILKWORM_CORE_PUBLIC_LIBS nlohmann_json::nlohmann_json secp256k1 tl::expected + eos_evm ) set(SILKWORM_CORE_PRIVATE_LIBS ff blst) diff --git a/silkworm/core/execution/evm.cpp b/silkworm/core/execution/evm.cpp index b216d713..70af3967 100644 --- a/silkworm/core/execution/evm.cpp +++ b/silkworm/core/execution/evm.cpp @@ -79,19 +79,19 @@ class DelegatingTracer : public evmone::Tracer { IntraBlockState& intra_block_state_; }; -EVM::EVM(const Block& block, IntraBlockState& state, const ChainConfig& config, const evmone::gas_parameters& gas_params) noexcept +EVM::EVM(const Block& block, IntraBlockState& state, const ChainConfig& config) noexcept : beneficiary{block.header.beneficiary}, block_{block}, state_{state}, config_{config}, evm1_{evmc_create_evmone()}, - gas_params_{gas_params}, eos_evm_version_{config.eos_evm_version(block.header)} { } EVM::~EVM() { evm1_->destroy(evm1_); } -CallResult EVM::execute(const Transaction& txn, uint64_t gas) noexcept { +CallResult EVM::execute(const Transaction& txn, uint64_t gas, const evmone::gas_parameters& gas_params) noexcept { assert(txn.from.has_value()); // sender must be recovered + gas_params_ = gas_params; txn_ = &txn; diff --git a/silkworm/core/execution/evm.hpp b/silkworm/core/execution/evm.hpp index d8d54228..2ac20f62 100644 --- a/silkworm/core/execution/evm.hpp +++ b/silkworm/core/execution/evm.hpp @@ -83,7 +83,7 @@ class EVM { EVM(const EVM&) = delete; EVM& operator=(const EVM&) = delete; - EVM(const Block& block, IntraBlockState& state, const ChainConfig& config, const evmone::gas_parameters& gas_params) + EVM(const Block& block, IntraBlockState& state, const ChainConfig& config) noexcept; ~EVM(); @@ -96,7 +96,7 @@ class EVM { [[nodiscard]] const IntraBlockState& state() const noexcept { return state_; } // Precondition: txn.from must be recovered - CallResult execute(const Transaction& txn, uint64_t gas) noexcept; + CallResult execute(const Transaction& txn, uint64_t gas, const evmone::gas_parameters& gas_params) noexcept; [[nodiscard]] evmc_revision revision() const noexcept; @@ -116,14 +116,6 @@ class EVM { message_filter_ = message_filter; } - void update_gas_params(const evmone::gas_parameters& gas_params) { - gas_params_ = gas_params; - } - - const evmone::gas_parameters get_gas_params() { - return gas_params_; - } - uint64_t get_eos_evm_version()const { return eos_evm_version_; } diff --git a/silkworm/core/execution/evm_test.cpp b/silkworm/core/execution/evm_test.cpp index 5869bc30..a4bdb8b3 100644 --- a/silkworm/core/execution/evm_test.cpp +++ b/silkworm/core/execution/evm_test.cpp @@ -41,7 +41,7 @@ TEST_CASE("Value transfer") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; CHECK(state.get_balance(from) == 0); CHECK(state.get_balance(to) == 0); @@ -51,13 +51,13 @@ TEST_CASE("Value transfer") { txn.to = to; txn.value = value; - CallResult res{evm.execute(txn, 0)}; + CallResult res{evm.execute(txn, 0, {})}; CHECK(res.status == EVMC_INSUFFICIENT_BALANCE); CHECK(res.data.empty()); state.add_to_balance(from, kEther); - res = evm.execute(txn, 0); + res = evm.execute(txn, 0, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); @@ -97,19 +97,19 @@ TEST_CASE("Smart contract with storage") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, test::kShanghaiConfig, {}}; + EVM evm{block, state, test::kShanghaiConfig}; Transaction txn{}; txn.from = caller; txn.data = code; uint64_t gas{0}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_OUT_OF_GAS); CHECK(res.data.empty()); gas = 50'000; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(to_hex(res.data) == "5f355f55"); @@ -121,7 +121,7 @@ TEST_CASE("Smart contract with storage") { txn.to = contract_address; txn.data = ByteView{new_val}; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); CHECK(state.get_current_storage(contract_address, key0) == new_val); @@ -169,7 +169,7 @@ TEST_CASE("Maximum call depth") { IntraBlockState state{db}; state.set_code(contract, code); - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; AnalysisCache analysis_cache{/*maxSize=*/16}; evm.analysis_cache = &analysis_cache; @@ -179,19 +179,19 @@ TEST_CASE("Maximum call depth") { txn.to = contract; uint64_t gas{1'000'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); evmc::bytes32 num_of_recursions{to_bytes32(*from_hex("0400"))}; txn.data = ByteView{num_of_recursions}; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); num_of_recursions = to_bytes32(*from_hex("0401")); txn.data = ByteView{num_of_recursions}; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_INVALID_INSTRUCTION); CHECK(res.data.empty()); } @@ -228,7 +228,7 @@ TEST_CASE("DELEGATECALL") { state.set_code(caller_address, caller_code); state.set_code(callee_address, callee_code); - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; Transaction txn{}; txn.from = caller_address; @@ -236,7 +236,7 @@ TEST_CASE("DELEGATECALL") { txn.data = ByteView{to_bytes32(callee_address)}; uint64_t gas{1'000'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); @@ -289,14 +289,14 @@ TEST_CASE("CREATE should only return on failure") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; Transaction txn{}; txn.from = caller; txn.data = code; uint64_t gas{150'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); @@ -321,14 +321,14 @@ TEST_CASE("Contract overwrite") { IntraBlockState state{db}; state.set_code(contract_address, old_code); - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; Transaction txn{}; txn.from = caller; txn.data = new_code; uint64_t gas{100'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_INVALID_INSTRUCTION); CHECK(res.gas_left == 0); @@ -344,7 +344,7 @@ TEST_CASE("EIP-3541: Reject new contracts starting with the 0xEF byte") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, config, {}}; + EVM evm{block, state, config}; Transaction txn; txn.from = 0x1000000000000000000000000000000000000000_address; @@ -352,19 +352,19 @@ TEST_CASE("EIP-3541: Reject new contracts starting with the 0xEF byte") { // https://eips.ethereum.org/EIPS/eip-3541#test-cases txn.data = *from_hex("0x60ef60005360016000f3"); - CHECK(evm.execute(txn, gas).status == EVMC_CONTRACT_VALIDATION_FAILURE); + CHECK(evm.execute(txn, gas, {}).status == EVMC_CONTRACT_VALIDATION_FAILURE); txn.data = *from_hex("0x60ef60005360026000f3"); - CHECK(evm.execute(txn, gas).status == EVMC_CONTRACT_VALIDATION_FAILURE); + CHECK(evm.execute(txn, gas, {}).status == EVMC_CONTRACT_VALIDATION_FAILURE); txn.data = *from_hex("0x60ef60005360036000f3"); - CHECK(evm.execute(txn, gas).status == EVMC_CONTRACT_VALIDATION_FAILURE); + CHECK(evm.execute(txn, gas, {}).status == EVMC_CONTRACT_VALIDATION_FAILURE); txn.data = *from_hex("0x60ef60005360206000f3"); - CHECK(evm.execute(txn, gas).status == EVMC_CONTRACT_VALIDATION_FAILURE); + CHECK(evm.execute(txn, gas, {}).status == EVMC_CONTRACT_VALIDATION_FAILURE); txn.data = *from_hex("0x60fe60005360016000f3"); - CHECK(evm.execute(txn, gas).status == EVMC_SUCCESS); + CHECK(evm.execute(txn, gas, {}).status == EVMC_SUCCESS); } class TestTracer : public EvmTracer { @@ -470,7 +470,7 @@ TEST_CASE("Tracing smart contract with storage") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; Transaction txn{}; txn.from = caller; @@ -484,7 +484,7 @@ TEST_CASE("Tracing smart contract with storage") { CHECK(evm.tracers().size() == 1); uint64_t gas{0}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_OUT_OF_GAS); CHECK(res.data.empty()); @@ -507,7 +507,7 @@ TEST_CASE("Tracing smart contract with storage") { CHECK(evm.tracers().size() == 2); gas = 50'000; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data == from_hex("600035600055")); @@ -549,7 +549,7 @@ TEST_CASE("Tracing smart contract with storage") { txn.to = contract_address; txn.data = ByteView{new_val}; gas = 50'000; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); CHECK(state.get_current_storage(contract_address, key0) == new_val); @@ -600,7 +600,7 @@ TEST_CASE("Tracing creation smart contract with CREATE2") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; Transaction txn{}; txn.from = caller; @@ -611,7 +611,7 @@ TEST_CASE("Tracing creation smart contract with CREATE2") { CHECK(evm.tracers().size() == 1); uint64_t gas = {100'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(tracer.msg_stack().at(0).depth == 0); CHECK(tracer.msg_stack().at(1).depth == 1); @@ -626,7 +626,7 @@ TEST_CASE("Tracing smart contract w/o code") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; CHECK(evm.tracers().empty()); TestTracer tracer1; @@ -642,7 +642,7 @@ TEST_CASE("Tracing smart contract w/o code") { txn.data = code; uint64_t gas{50'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); @@ -666,7 +666,7 @@ TEST_CASE("Tracing smart contract w/o code") { txn.to = contract_address; txn.data = ByteView{to_bytes32(*from_hex("f5"))}; - res = evm.execute(txn, gas); + res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_SUCCESS); CHECK(res.data.empty()); @@ -687,7 +687,7 @@ TEST_CASE("Tracing precompiled contract failure") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, kMainnetConfig, {}}; + EVM evm{block, state, kMainnetConfig}; CHECK(evm.tracers().empty()); TestTracer tracer1; @@ -704,7 +704,7 @@ TEST_CASE("Tracing precompiled contract failure") { txn.to = blake2f_precompile; uint64_t gas{50'000}; - CallResult res{evm.execute(txn, gas)}; + CallResult res{evm.execute(txn, gas, {})}; CHECK(res.status == EVMC_PRECOMPILE_FAILURE); } @@ -717,7 +717,7 @@ TEST_CASE("Smart contract creation w/ insufficient balance") { InMemoryState db; IntraBlockState state{db}; - EVM evm{block, state, test::kShanghaiConfig, {}}; + EVM evm{block, state, test::kShanghaiConfig}; Transaction txn{}; txn.from = caller; @@ -725,7 +725,7 @@ TEST_CASE("Smart contract creation w/ insufficient balance") { txn.value = intx::uint256{1}; uint64_t gas = 50'000; - CallResult res = evm.execute(txn, gas); + CallResult res = evm.execute(txn, gas, {}); CHECK(res.status == EVMC_INSUFFICIENT_BALANCE); } @@ -743,14 +743,14 @@ TEST_CASE("EOS EVM codedeposit test") { InMemoryState db; IntraBlockState state{db}; state.set_balance(caller, intx::uint256{1e18}); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = caller; txn.data = code; uint64_t gas = 1'500'000; - CallResult res = evm.execute(txn, 1'500'000); + CallResult res = evm.execute(txn, 1'500'000, gas_params); CHECK(res.status == EVMC_SUCCESS); CHECK(gas-res.gas_left == 123 + 500*371); //G_codedeposit=500, codelen=371 CHECK(res.storage_gas_consumed == 0); @@ -773,14 +773,14 @@ TEST_CASE("EOS EVM codedeposit v3 test") { IntraBlockState state{db}; state.set_balance(caller, intx::uint256{1e18}); state.set_nonce(caller, 10); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = caller; txn.data = code; uint64_t gas = 1'500'000; - CallResult res = evm.execute(txn, gas); + CallResult res = evm.execute(txn, gas, gas_params); CHECK(res.status == EVMC_SUCCESS); CHECK(gas-res.gas_left == 123 + 500*371); //G_codedeposit=500, codelen=371 CHECK(res.storage_gas_consumed == 500*371); @@ -807,14 +807,14 @@ TEST_CASE("EOS EVM codedeposit v3 test oog") { InMemoryState db; IntraBlockState state{db}; state.set_balance(caller, intx::uint256{1e18}); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = caller; txn.data = code; uint64_t gas = 123 + 500*370; // enough to run initialization (real), but not enough to store code (speculative) - CallResult res = evm.execute(txn, gas); + CallResult res = evm.execute(txn, gas, gas_params); CHECK(res.status == EVMC_OUT_OF_GAS); CHECK(res.gas_left == 500*370); CHECK(res.storage_gas_consumed == 0); @@ -824,14 +824,14 @@ TEST_CASE("EOS EVM codedeposit v3 test oog") { InMemoryState db2; IntraBlockState state2{db2}; state2.set_balance(caller, intx::uint256{1e18}); - EVM evm2{block, state2, test::kIstanbulTrustConfig, gas_params}; + EVM evm2{block, state2, test::kIstanbulTrustConfig}; Transaction txn2{}; txn2.from = caller; txn2.data = code; uint64_t gas2 = 122; // not-enough to run initialization (real) - CallResult res2 = evm2.execute(txn, gas2); + CallResult res2 = evm2.execute(txn, gas2, gas_params); CHECK(res2.status == EVMC_OUT_OF_GAS); CHECK(res2.gas_left == 0); CHECK(res2.storage_gas_consumed == 0); @@ -854,14 +854,14 @@ TEST_CASE("EOS EVM G_txnewaccount") { InMemoryState db; IntraBlockState state{db}; state.set_balance(sender, intx::uint256{1e18}); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = sender; txn.to = receiver1; txn.value = intx::uint256{1}; - CallResult res = evm.execute(txn, 0); + CallResult res = evm.execute(txn, 0, gas_params); CHECK(res.status == EVMC_SUCCESS); CHECK(res.gas_left == 0); CHECK(res.gas_refund == 0); @@ -871,9 +871,8 @@ TEST_CASE("EOS EVM G_txnewaccount") { txn.to = receiver2; gas_params.G_txnewaccount = 1; - evm.update_gas_params(gas_params); - res = evm.execute(txn, 0); + res = evm.execute(txn, 0, gas_params); CHECK(res.status == EVMC_OUT_OF_GAS); CHECK(res.gas_refund == 0); CHECK(res.gas_left == 0); @@ -897,14 +896,14 @@ TEST_CASE("EOS EVM G_txnewaccount v3") { InMemoryState db; IntraBlockState state{db}; state.set_balance(sender, intx::uint256{1e18}); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = sender; txn.to = receiver1; txn.value = intx::uint256{1}; - CallResult res = evm.execute(txn, 1000); + CallResult res = evm.execute(txn, 1000, gas_params); CHECK(res.status == EVMC_SUCCESS); CHECK(res.gas_left == 0); CHECK(res.gas_refund == 0); @@ -915,14 +914,14 @@ TEST_CASE("EOS EVM G_txnewaccount v3") { InMemoryState db2; IntraBlockState state2{db2}; state2.set_balance(sender, intx::uint256{1e18}); - EVM evm2{block, state2, test::kIstanbulTrustConfig, gas_params}; + EVM evm2{block, state2, test::kIstanbulTrustConfig}; Transaction txn2{}; txn2.from = sender; txn2.to = receiver1; txn2.value = intx::uint256{1}; - CallResult res2 = evm2.execute(txn2, 999); + CallResult res2 = evm2.execute(txn2, 999, gas_params); CHECK(res2.status == EVMC_OUT_OF_GAS); CHECK(res2.gas_left == 999); CHECK(res2.gas_refund == 0); @@ -946,14 +945,14 @@ TEST_CASE("EOS EVM send value to reserved address (tx)") { InMemoryState db; IntraBlockState state{db}; state.set_balance(sender, intx::uint256{1e18}); - EVM evm{block, state, test::kIstanbulTrustConfig, gas_params}; + EVM evm{block, state, test::kIstanbulTrustConfig}; Transaction txn{}; txn.from = sender; txn.to = receiver1; txn.value = intx::uint256{1}; - CallResult res = evm.execute(txn, gas_limit); + CallResult res = evm.execute(txn, gas_limit, gas_params); return res; }; diff --git a/silkworm/core/execution/execution.hpp b/silkworm/core/execution/execution.hpp index 5c08da5f..4ae116a2 100644 --- a/silkworm/core/execution/execution.hpp +++ b/silkworm/core/execution/execution.hpp @@ -46,9 +46,9 @@ namespace silkworm { if (!rule_set) { return ValidationResult::kUnknownProtocolRuleSet; } - ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, gas_prices}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_prices}; std::vector receipts; - return processor.execute_and_write_block(receipts); + return processor.execute_and_write_block(receipts, gas_params); } } // namespace silkworm diff --git a/silkworm/core/execution/processor.cpp b/silkworm/core/execution/processor.cpp index b91c36ff..7404c69e 100644 --- a/silkworm/core/execution/processor.cpp +++ b/silkworm/core/execution/processor.cpp @@ -22,16 +22,16 @@ #include #include #include - +#include namespace silkworm { ExecutionProcessor::ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, - const ChainConfig& config, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices) - : state_{state}, rule_set_{rule_set}, evm_{block, state_, config, gas_params}, gas_params_{gas_params}, gas_prices_{gas_prices} { + const ChainConfig& config, const gas_prices_t& gas_prices) + : state_{state}, rule_set_{rule_set}, evm_{block, state_, config}, gas_prices_{gas_prices} { evm_.beneficiary = rule_set.get_beneficiary(block.header); } -ExecutionResult ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& receipt) noexcept { +ExecutionResult ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& receipt, const evmone::gas_parameters& gas_params) noexcept { assert(protocol::validate_transaction(txn, state_, available_gas()) == ValidationResult::kOk); ExecutionResult res; @@ -74,76 +74,35 @@ ExecutionResult ExecutionProcessor::execute_transaction(const Transaction& txn, const auto eos_evm_version = evm_.get_eos_evm_version(); intx::uint256 inclusion_price; - if( eos_evm_version >= 3 ) inclusion_price = std::min(txn.max_priority_fee_per_gas, txn.max_fee_per_gas - base_fee_per_gas); + evmone::gas_parameters scaled_gas_params; + if( eos_evm_version >= 3 ) { + inclusion_price = std::min(txn.max_priority_fee_per_gas, txn.max_fee_per_gas - base_fee_per_gas); + if(!gas_prices_.is_zero()){ + scaled_gas_params = evmone::gas_parameters::apply_discount_factor(inclusion_price, static_cast(base_fee_per_gas), gas_prices_.storage_price, gas_params); + } else { + scaled_gas_params = gas_params; + } + } else { + scaled_gas_params = gas_params; + } - const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, gas_params_)}; + const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, scaled_gas_params)}; assert(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) - const CallResult vm_res{evm_.execute(txn, txn.gas_limit - static_cast(g0))}; + const CallResult vm_res{evm_.execute(txn, txn.gas_limit - static_cast(g0), scaled_gas_params)}; + const intx::uint256 price{evm_.config().protocol_rule_set == protocol::RuleSetType::kTrust ? effective_gas_price : txn.priority_fee_per_gas(base_fee_per_gas)}; + uint64_t gas_used{0}; if(eos_evm_version < 3) { gas_used = txn.gas_limit - refund_gas(txn, vm_res.gas_left, vm_res.gas_refund); - // award the fee recipient - const intx::uint256 price{evm_.config().protocol_rule_set == protocol::RuleSetType::kTrust ? effective_gas_price : txn.priority_fee_per_gas(base_fee_per_gas)}; state_.add_to_balance(evm_.beneficiary, price * gas_used); } else { - uint64_t storage_gas_consumed{vm_res.storage_gas_consumed}; - const bool contract_creation{!txn.to}; - auto gas_left = vm_res.gas_left; - if(contract_creation) { - if( vm_res.status == EVMC_SUCCESS ) { - storage_gas_consumed += gas_params_.G_txcreate; //correct storage gas consumed to account for initial G_txcreate storage gas - } else { - gas_left += gas_params_.G_txcreate; - } - } - - evmone::gas_state_t vm_res_gas_state(eos_evm_version, static_cast(vm_res.gas_refund), - static_cast(storage_gas_consumed), static_cast(vm_res.storage_gas_refund), static_cast(vm_res.speculative_cpu_gas_consumed)); - - gas_left += static_cast(vm_res_gas_state.collapse()); - gas_used = txn.gas_limit - gas_left; - assert(vm_res_gas_state.cpu_gas_refund() == 0); - const auto total_storage_gas_consumed = static_cast(vm_res_gas_state.storage_gas_consumed()); - assert(gas_used > total_storage_gas_consumed); - const auto total_cpu_gas_consumed = gas_used - total_storage_gas_consumed; - - const intx::uint256 price{evm_.config().protocol_rule_set == protocol::RuleSetType::kTrust ? effective_gas_price : txn.priority_fee_per_gas(base_fee_per_gas)}; - - res.cpu_gas_consumed = total_cpu_gas_consumed; - res.discounted_storage_gas_consumed = static_cast(total_storage_gas_consumed); - res.inclusion_fee = intx::uint256(total_cpu_gas_consumed)*inclusion_price; - res.storage_fee = intx::uint256(total_storage_gas_consumed)*price; - - auto final_fee = price * gas_used; - if(gas_prices_.storage_price >= gas_prices_.overhead_price) { - intx::uint256 gas_refund = intx::uint256(total_cpu_gas_consumed); - gas_refund *= intx::uint256(gas_prices_.storage_price-gas_prices_.overhead_price); - gas_refund /= price; - - SILKWORM_ASSERT(gas_refund <= gas_used); - gas_left += static_cast(gas_refund); - assert(txn.gas_limit >= gas_left); - gas_used = txn.gas_limit - gas_left; - SILKWORM_ASSERT(gas_used >= total_storage_gas_consumed); - final_fee = price * gas_used; - - assert(final_fee >= res.storage_fee); - const auto overhead_and_inclusion_fee = final_fee - res.storage_fee; - if( overhead_and_inclusion_fee >= res.inclusion_fee ) { - res.overhead_fee = overhead_and_inclusion_fee - res.inclusion_fee; - } else { - res.inclusion_fee = overhead_and_inclusion_fee; - res.overhead_fee = 0; - } - } else { - res.overhead_fee = final_fee - res.inclusion_fee - res.storage_fee; - } - - assert(final_fee == res.inclusion_fee + res.storage_fee + res.overhead_fee); - + intx::uint256 final_fee{0}; + uint64_t gas_left{0}; + std::tie(res, final_fee, gas_used, gas_left) = eosevm::gas_refund_v3(eos_evm_version, vm_res, txn, scaled_gas_params, price, gas_prices_, inclusion_price); // award the fee recipient + state_.add_to_balance(evm_.beneficiary, final_fee); state_.add_to_balance(*txn.from, price * gas_left); } @@ -193,7 +152,7 @@ uint64_t ExecutionProcessor::refund_gas(const Transaction& txn, uint64_t gas_lef return gas_left; } -ValidationResult ExecutionProcessor::execute_block_no_post_validation(std::vector& receipts) noexcept { +ValidationResult ExecutionProcessor::execute_block_no_post_validation(std::vector& receipts, const evmone::gas_parameters& gas_params) noexcept { const Block& block{evm_.block()}; // Avoid calling dao_block() when the ruleset is kTrust to prevent triggering an assertion in the dao_block function @@ -215,7 +174,7 @@ ValidationResult ExecutionProcessor::execute_block_no_post_validation(std::vecto if (err != ValidationResult::kOk) { return err; } - execute_transaction(txn, *receipt_it); + execute_transaction(txn, *receipt_it, gas_params); state_.reset_reserved_objects(); ++receipt_it; } @@ -229,8 +188,8 @@ ValidationResult ExecutionProcessor::execute_block_no_post_validation(std::vecto return ValidationResult::kOk; } -ValidationResult ExecutionProcessor::execute_and_write_block(std::vector& receipts) noexcept { - if (const ValidationResult res{execute_block_no_post_validation(receipts)}; res != ValidationResult::kOk) { +ValidationResult ExecutionProcessor::execute_and_write_block(std::vector& receipts, const evmone::gas_parameters& gas_params) noexcept { + if (const ValidationResult res{execute_block_no_post_validation(receipts, gas_params)}; res != ValidationResult::kOk) { return res; } diff --git a/silkworm/core/execution/processor.hpp b/silkworm/core/execution/processor.hpp index 2a004c41..9727eeae 100644 --- a/silkworm/core/execution/processor.hpp +++ b/silkworm/core/execution/processor.hpp @@ -34,18 +34,18 @@ class ExecutionProcessor { ExecutionProcessor(const ExecutionProcessor&) = delete; ExecutionProcessor& operator=(const ExecutionProcessor&) = delete; - ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, const ChainConfig& config, const evmone::gas_parameters& gas_params, const gas_prices_t& gas_prices); + ExecutionProcessor(const Block& block, protocol::IRuleSet& rule_set, State& state, const ChainConfig& config, const gas_prices_t& gas_prices); /** * Execute a transaction, but do not write to the DB yet. * Precondition: transaction must be valid. */ - ExecutionResult execute_transaction(const Transaction& txn, Receipt& receipt) noexcept; + ExecutionResult execute_transaction(const Transaction& txn, Receipt& receipt, const evmone::gas_parameters& gas_params) noexcept; //! \brief Execute the block and write the result to the DB. //! \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. - [[nodiscard]] ValidationResult execute_and_write_block(std::vector& receipts) noexcept; + [[nodiscard]] ValidationResult execute_and_write_block(std::vector& receipts, const evmone::gas_parameters& gas_params) noexcept; EVM& evm() noexcept { return evm_; } const EVM& evm() const noexcept { return evm_; } @@ -64,7 +64,7 @@ class ExecutionProcessor { * Does not perform any post-execution validation (for example, receipt root is not checked). * Precondition: validate_block_header & pre_validate_block_body must return kOk. */ - [[nodiscard]] ValidationResult execute_block_no_post_validation(std::vector& receipts) noexcept; + [[nodiscard]] ValidationResult execute_block_no_post_validation(std::vector& receipts, const evmone::gas_parameters& gas_params) noexcept; uint64_t refund_gas(const Transaction& txn, uint64_t gas_left, uint64_t refund_gas) noexcept; @@ -72,7 +72,6 @@ class ExecutionProcessor { IntraBlockState state_; protocol::IRuleSet& rule_set_; EVM evm_; - evmone::gas_parameters gas_params_; gas_prices_t gas_prices_; }; diff --git a/silkworm/core/execution/processor_test.cpp b/silkworm/core/execution/processor_test.cpp index a270ceeb..c6e86816 100644 --- a/silkworm/core/execution/processor_test.cpp +++ b/silkworm/core/execution/processor_test.cpp @@ -47,10 +47,10 @@ TEST_CASE("Zero gas price") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; Receipt receipt; - processor.execute_transaction(txn, receipt); + processor.execute_transaction(txn, receipt, {}); CHECK(receipt.success); } @@ -87,7 +87,7 @@ TEST_CASE("No refund on error") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; Transaction txn{ {.nonce = nonce, @@ -105,7 +105,7 @@ TEST_CASE("No refund on error") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); // Call the newly created contract @@ -119,7 +119,7 @@ TEST_CASE("No refund on error") { txn.gas_limit = protocol::fee::kGTransaction + 5'020; Receipt receipt2; - processor.execute_transaction(txn, receipt2); + processor.execute_transaction(txn, receipt2, {}); CHECK(!receipt2.success); CHECK(receipt2.cumulative_gas_used - receipt1.cumulative_gas_used == txn.gas_limit); } @@ -182,7 +182,7 @@ TEST_CASE("Self-destruct") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; processor.evm().state().add_to_balance(originator, kEther); processor.evm().state().set_code(caller_address, caller_code); @@ -203,7 +203,7 @@ TEST_CASE("Self-destruct") { txn.data = ByteView{address_as_hash}; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); CHECK(!processor.evm().state().exists(suicidal_address)); @@ -214,7 +214,7 @@ TEST_CASE("Self-destruct") { txn.data.clear(); Receipt receipt2; - processor.execute_transaction(txn, receipt2); + processor.execute_transaction(txn, receipt2, {}); CHECK(receipt2.success); CHECK(processor.evm().state().exists(suicidal_address)); @@ -332,11 +332,11 @@ TEST_CASE("Out of Gas during account re-creation") { }; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; - processor.execute_transaction(txn, receipt); + processor.execute_transaction(txn, receipt, {}); // out of gas CHECK(!receipt.success); @@ -375,11 +375,11 @@ TEST_CASE("Empty suicide beneficiary") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kMainnetConfig, {}}; processor.evm().state().add_to_balance(caller, kEther); Receipt receipt; - processor.execute_transaction(txn, receipt); + processor.execute_transaction(txn, receipt, {}); CHECK(receipt.success); processor.evm().state().write_to_db(block_number); @@ -416,7 +416,7 @@ TEST_CASE("EOS EVM refund v2") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; Transaction txn{ {.nonce = nonce, @@ -434,7 +434,7 @@ TEST_CASE("EOS EVM refund v2") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); // Call run(10) on the newly created contract //a444f5e9 = run, 00..0a = 10 @@ -444,7 +444,7 @@ TEST_CASE("EOS EVM refund v2") { txn.gas_limit = 800'000; Receipt receipt2; - processor.execute_transaction(txn, receipt2); + processor.execute_transaction(txn, receipt2, {}); CHECK(receipt2.success); return receipt2.cumulative_gas_used - receipt1.cumulative_gas_used; }; @@ -506,7 +506,7 @@ TEST_CASE("EOS EVM message filter") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; Transaction txn{ {.nonce = nonce, @@ -525,7 +525,7 @@ TEST_CASE("EOS EVM message filter") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); // Call the newly created contract @@ -539,7 +539,7 @@ TEST_CASE("EOS EVM message filter") { return true; }); - processor.execute_transaction(txn, receipt2); + processor.execute_transaction(txn, receipt2, {}); CHECK(receipt2.success); const auto& filtered_messages = processor.state().filtered_messages(); @@ -557,7 +557,7 @@ TEST_CASE("EOS EVM message filter") { Receipt receipt3; - processor.execute_transaction(txn, receipt3); + processor.execute_transaction(txn, receipt3, {}); const auto& fm2 = processor.state().filtered_messages(); CHECK(receipt3.success); CHECK(fm2.size()==1); @@ -610,7 +610,7 @@ TEST_CASE("EOS EVM message filter revert") { InMemoryState state; auto rule_set{protocol::rule_set_factory(kEOSEVMMainnetConfig)}; - ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, kEOSEVMMainnetConfig, {}}; Transaction txn{ {.nonce = nonce, @@ -629,7 +629,7 @@ TEST_CASE("EOS EVM message filter revert") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); //Call failsucceed(no,false,0x00,yes,true,0x01) @@ -643,7 +643,7 @@ TEST_CASE("EOS EVM message filter revert") { return message.recipient == 0xbbbbbbbbbbbbbbbbbbbbbbbb56e4000000000000_address && message.input_size > 0; }); - processor.execute_transaction(txn, receipt2); + processor.execute_transaction(txn, receipt2, {}); CHECK(receipt2.success); const auto& filtered_messages = processor.state().filtered_messages(); @@ -669,7 +669,7 @@ TEST_CASE("EOS EVM message filter revert") { Receipt receipt3; processor.state().filtered_messages().clear(); - processor.execute_transaction(txn, receipt3); + processor.execute_transaction(txn, receipt3, {}); CHECK(receipt3.success); const auto& filtered_messages2 = processor.state().filtered_messages(); @@ -739,7 +739,7 @@ TEST_CASE("EOS EVM No fee burn when chain uses trust ruleset") { InMemoryState state; auto rule_set{protocol::rule_set_factory(chain_config)}; - ExecutionProcessor processor{block, *rule_set, state, chain_config, {}, {}}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, {}}; Transaction txn{{ .type = TransactionType::kDynamicFee, @@ -759,7 +759,7 @@ TEST_CASE("EOS EVM No fee burn when chain uses trust ruleset") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, {}); CHECK(receipt1.success); return std::make_tuple( @@ -822,7 +822,7 @@ TEST_CASE("EOS EVM v3 contract creation") { evmone::gas_parameters gas_params; gas_params.G_txcreate = 50000; - ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, {}}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, {}}; Transaction txn{{ .type = TransactionType::kDynamicFee, @@ -842,7 +842,7 @@ TEST_CASE("EOS EVM v3 contract creation") { txn.from = caller; Receipt receipt1; - processor.execute_transaction(txn, receipt1); + processor.execute_transaction(txn, receipt1, gas_params); return std::make_tuple( receipt1, @@ -913,7 +913,7 @@ TEST_CASE("EOS EVM v3 final refund") { evmone::gas_parameters gas_params; gas_params.G_txcreate = 50000; - ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params, gas_prices}; + ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_prices}; Transaction txn{{ .type = TransactionType::kDynamicFee, @@ -933,7 +933,7 @@ TEST_CASE("EOS EVM v3 final refund") { txn.from = caller; Receipt receipt1; - auto res = processor.execute_transaction(txn, receipt1); + auto res = processor.execute_transaction(txn, receipt1, gas_params); return std::make_tuple( res, @@ -943,28 +943,26 @@ TEST_CASE("EOS EVM v3 final refund") { ); }; - // g_txcreate = 50000 - // g0 = 29092 (cpu real) + g_txcreate (storage spec) = 79092 - // init = 23156 (cpu real) + 200000 (storage spec) + 28000 (cpu spec) = 251156 - // code_deposit = 442 * 200 = 88400 - - // cpu_gas_consumed = 23156 + 28000 + 29092 = 80248 - // storage_gas_consumed = 50000 + 200000 + 88400 = 338400 - - CHECK(79092+251156+88400 == 80248+338400); - gas_prices_t gp; gp.overhead_price = 70 * kGiga; gp.storage_price = 80 * kGiga; auto base_fee_per_gas = gp.get_base_price(); auto inclusion_price = std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); - // storage_price >= overhead_price + // ***** storage_price >= overhead_price ***** + // g_txcreate = 47058 (50000 scaled) + // g0 = 29092 (cpu real) + g_txcreate (storage spec) = 76150 + // init = 23156 (cpu real) + 188230 (200000 scaled) (storage spec) + 28000 (cpu spec) = 239386 + // code_deposit = 442 * 188 (200 scaled) = 83096 + // cpu_gas_consumed = 23156 + 28000 + 29092 = 80248 + // storage_gas_consumed = 47058 + 188230 + 83096 = 318384 + CHECK(76150+239386+83096 == 80248+318384); + uint64_t expected_refund = 9440; - auto [res, receipt, balance, effective_gas_price] = deploy_contract(kEOSEVMMainnetConfig, 79092+251156+88400, gp); + auto [res, receipt, balance, effective_gas_price] = deploy_contract(kEOSEVMMainnetConfig, 76150+239386+83096, gp); CHECK(receipt.success == true); - CHECK(receipt.cumulative_gas_used == 79092+251156+88400-expected_refund); + CHECK(receipt.cumulative_gas_used == 76150+239386+83096-expected_refund); auto inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); CHECK(res.inclusion_fee == inclusion_fee); @@ -972,21 +970,29 @@ TEST_CASE("EOS EVM v3 final refund") { auto storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; CHECK(res.storage_fee == storage_fee); - CHECK(res.discounted_storage_gas_consumed == 338400); + CHECK(res.discounted_storage_gas_consumed == 318384); - CHECK(receipt.cumulative_gas_used+expected_refund == 80248+338400); + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+318384); CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); // storage_price < overhead_price + // g_txcreate = 41176 (50000 scaled) + // g0 = 29092 (cpu real) + g_txcreate (storage spec) = 70268 + // init = 23156 (cpu real) + 164700 (200000 scaled) (storage spec) + 28000 (cpu spec) = 215856 + // code_deposit = 442 * 164 (200 scaled) = 72488 + // cpu_gas_consumed = 23156 + 28000 + 29092 = 80248 + // storage_gas_consumed = 41176 + 164700 + 72488 = 278364 + CHECK(70268+215856+72488 == 80248+278364); + gp.overhead_price = 80 * kGiga; gp.storage_price = 70 * kGiga; base_fee_per_gas = gp.get_base_price(); inclusion_price = std::min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas); expected_refund = 0; - std::tie(res, receipt, balance, effective_gas_price) = deploy_contract(kEOSEVMMainnetConfig, 79092+251156+88400, gp); + std::tie(res, receipt, balance, effective_gas_price) = deploy_contract(kEOSEVMMainnetConfig, 70268+215856+72488, gp); CHECK(receipt.success == true); - CHECK(receipt.cumulative_gas_used == 79092+251156+88400-expected_refund); + CHECK(receipt.cumulative_gas_used == 70268+215856+72488-expected_refund); inclusion_fee = inclusion_price * intx::uint256(res.cpu_gas_consumed); CHECK(res.inclusion_fee == inclusion_fee); @@ -994,9 +1000,9 @@ TEST_CASE("EOS EVM v3 final refund") { storage_fee = res.discounted_storage_gas_consumed*effective_gas_price; CHECK(res.storage_fee == storage_fee); - CHECK(res.discounted_storage_gas_consumed == 338400); + CHECK(res.discounted_storage_gas_consumed == 278364); - CHECK(receipt.cumulative_gas_used+expected_refund == 80248+338400); + CHECK(receipt.cumulative_gas_used+expected_refund == 80248+278364); CHECK(balance == storage_fee + inclusion_fee + res.overhead_fee); } diff --git a/silkworm/core/protocol/blockchain.cpp b/silkworm/core/protocol/blockchain.cpp index 02cf7eb1..8031c0a4 100644 --- a/silkworm/core/protocol/blockchain.cpp +++ b/silkworm/core/protocol/blockchain.cpp @@ -90,11 +90,11 @@ ValidationResult Blockchain::insert_block(Block& block, bool check_state_root) { } ValidationResult Blockchain::execute_block(const Block& block, bool check_state_root) { - ExecutionProcessor processor{block, *rule_set_, state_, config_, gas_params_, gas_prices_}; + ExecutionProcessor processor{block, *rule_set_, state_, config_, gas_prices_}; processor.evm().state_pool = state_pool; processor.evm().exo_evm = exo_evm; - if (const auto res{processor.execute_and_write_block(receipts_)}; res != ValidationResult::kOk) { + if (const auto res{processor.execute_and_write_block(receipts_, gas_params_)}; res != ValidationResult::kOk) { return res; } diff --git a/silkworm/core/types/gas_prices.hpp b/silkworm/core/types/gas_prices.hpp index 780e7d1b..96c4cbcc 100644 --- a/silkworm/core/types/gas_prices.hpp +++ b/silkworm/core/types/gas_prices.hpp @@ -11,6 +11,10 @@ struct gas_prices_t { uint64_t get_base_price()const { return std::max(overhead_price, storage_price); } + + bool is_zero()const { + return overhead_price == 0 && storage_price == 0; + } }; } // namespace silkworm diff --git a/silkworm/node/stagedsync/stages/stage_execution.cpp b/silkworm/node/stagedsync/stages/stage_execution.cpp index a25357dd..eb0504fc 100644 --- a/silkworm/node/stagedsync/stages/stage_execution.cpp +++ b/silkworm/node/stagedsync/stages/stage_execution.cpp @@ -238,13 +238,14 @@ Stage::Result Execution::execute_batch(db::RWTxn& txn, BlockNum max_block_num, A log_time = now + 5s; } - ExecutionProcessor processor(block, *rule_set_, buffer, node_settings_->chain_config.value(), get_gas_params(txn, block), get_gas_prices(txn, block)); + auto gas_prices = get_gas_prices(txn, block); + ExecutionProcessor processor(block, *rule_set_, buffer, node_settings_->chain_config.value(), gas_prices); processor.evm().analysis_cache = &analysis_cache; processor.evm().state_pool = &state_pool; // TODO Add Tracer and collect call traces - - if (const auto res{processor.execute_and_write_block(receipts)}; res != ValidationResult::kOk) { + auto gas_params = get_gas_params(txn, block); + if (const auto res{processor.execute_and_write_block(receipts, gas_params)}; res != ValidationResult::kOk) { // Persist work done so far if (block_num_ >= prune_receipts_threshold) { buffer.insert_receipts(block_num_, receipts); diff --git a/silkworm/silkrpc/commands/eth_api.cpp b/silkworm/silkrpc/commands/eth_api.cpp index 6a424eea..5d8b06a1 100644 --- a/silkworm/silkrpc/commands/eth_api.cpp +++ b/silkworm/silkrpc/commands/eth_api.cpp @@ -1153,14 +1153,14 @@ awaitable EthereumRpcApi::handle_eth_call(const nlohmann::json& request, s txn.max_fee_per_gas = base_fee_per_gas; } - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); const core::rawdb::DatabaseReader& db_reader = is_latest_block ? static_cast(cached_database) : static_cast(tx_database); const auto execution_result = co_await EVMExecutor::call( *chain_config_ptr, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); - }, gas_params, eos_evm_version, {}, true, txn.from == evmc::address{0}); + }, gas_params, gas_prices, eos_evm_version, {}, true, txn.from == evmc::address{0}); if (execution_result.success()) { make_glaze_json_content(reply, request["id"], execution_result.data); @@ -1302,7 +1302,7 @@ awaitable EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, tx_database, block_number_or_hash); const auto chain_id = co_await core::rawdb::read_chain_id(tx_database); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number; const core::rawdb::DatabaseReader& db_reader = @@ -1343,7 +1343,7 @@ awaitable EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js const auto execution_result = co_await EVMExecutor::call( *chain_config_ptr, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); - }, gas_params, eos_evm_version, + }, gas_params, gas_prices, eos_evm_version, std::move(tracers), /* refund */ true, /* gasBailout */ false); if (execution_result.pre_check_error) { @@ -1408,7 +1408,7 @@ awaitable EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, tx_database, block_number_or_hash); const auto chain_id = co_await core::rawdb::read_chain_id(tx_database); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number; const core::rawdb::DatabaseReader& db_reader = @@ -1434,7 +1434,7 @@ awaitable EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req const auto execution_result = co_await EVMExecutor::call( *chain_config_ptr, workers_, block_with_hash->block, tx_with_block->transaction, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); - }, gas_params, eos_evm_version, {}, true, false); + }, gas_params, gas_prices, eos_evm_version, {}, true, false); if (execution_result.pre_check_error) { reply = make_json_error(request["id"], -32000, execution_result.pre_check_error.value()); error = true; diff --git a/silkworm/silkrpc/core/call_many.cpp b/silkworm/silkrpc/core/call_many.cpp index f2299fbc..1921a02a 100644 --- a/silkworm/silkrpc/core/call_many.cpp +++ b/silkworm/silkrpc/core/call_many.cpp @@ -55,6 +55,7 @@ CallManyResult CallExecutor::executes_all_bundles(const silkworm::ChainConfig* c int32_t transaction_index, boost::asio::any_io_executor& this_executor, const evmone::gas_parameters& gas_params, + const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version) { CallManyResult result; const auto& block = block_with_hash.block; @@ -72,7 +73,7 @@ CallManyResult CallExecutor::executes_all_bundles(const silkworm::ChainConfig* c txn.recover_sender(); } - auto exec_result = executor.call(block, txn, gas_params, eos_evm_version); + auto exec_result = executor.call(block, txn, gas_params, gas_prices, eos_evm_version); if ((clock_time::since(start_time) / 1000000) > timeout) { std::ostringstream oss; @@ -112,7 +113,7 @@ CallManyResult CallExecutor::executes_all_bundles(const silkworm::ChainConfig* c for (const auto& call : bundle.transactions) { silkworm::Transaction txn{call.to_transaction()}; - auto call_execution_result = executor.call(blockContext.block, txn, gas_params, eos_evm_version); + auto call_execution_result = executor.call(blockContext.block, txn, gas_params, gas_prices, eos_evm_version); if (call_execution_result.pre_check_error) { result.error = call_execution_result.pre_check_error; @@ -173,13 +174,13 @@ boost::asio::awaitable CallExecutor::execute(const Bundles& bund if (transaction_index == -1) { transaction_index = static_cast(block_with_hash->block.transactions.size()); } - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block); auto this_executor = co_await boost::asio::this_coro::executor; result = co_await boost::asio::async_compose( [&](auto&& self) { boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - result = executes_all_bundles(chain_config_ptr, *block_with_hash, tx_database, bundles, opt_timeout, accounts_overrides, transaction_index, this_executor, gas_params, eos_evm_version); + result = executes_all_bundles(chain_config_ptr, *block_with_hash, tx_database, bundles, opt_timeout, accounts_overrides, transaction_index, this_executor, gas_params, gas_prices, eos_evm_version); boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { self.complete(result); }); diff --git a/silkworm/silkrpc/core/call_many.hpp b/silkworm/silkrpc/core/call_many.hpp index da581d98..0843a2bc 100644 --- a/silkworm/silkrpc/core/call_many.hpp +++ b/silkworm/silkrpc/core/call_many.hpp @@ -40,6 +40,7 @@ #include #include #include +#include namespace silkworm::rpc::call { @@ -71,6 +72,7 @@ class CallExecutor { int32_t transaction_index, boost::asio::any_io_executor& executor, const evmone::gas_parameters& gas_params, + const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version); private: diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.cpp b/silkworm/silkrpc/core/estimate_gas_oracle.cpp index 28d5fec3..c3e4a865 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.cpp @@ -78,7 +78,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call SILK_DEBUG << "hi: " << hi << ", lo: " << lo << ", cap: " << cap; - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database_, &config_, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(tx_database_, &config_, block); auto this_executor = co_await boost::asio::this_coro::executor; auto exec_result = co_await boost::asio::async_compose( @@ -94,7 +94,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call auto mid = (hi + lo) / 2; transaction.gas_limit = mid; - result = try_execution(executor, block, transaction, eos_evm_version, gas_params); + result = try_execution(executor, block, transaction, eos_evm_version, gas_params, gas_prices); if(result.pre_check_error && !result.pre_check_error.value().starts_with("intrinsic gas too low")) { boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { @@ -112,7 +112,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call if (hi == cap) { transaction.gas_limit = hi; - result = try_execution(executor, block, transaction, eos_evm_version, gas_params); + result = try_execution(executor, block, transaction, eos_evm_version, gas_params, gas_prices); SILK_DEBUG << "HI == cap tested again with " << (result.success() ? "succeed" : "failed"); } else { result.pre_check_error = std::nullopt; @@ -134,9 +134,9 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call co_return hi; } -ExecutionResult EstimateGasOracle::try_execution(EVMExecutor& executor, const silkworm::Block& block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params) { +ExecutionResult EstimateGasOracle::try_execution(EVMExecutor& executor, const silkworm::Block& block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params, const silkworm::gas_prices_t& gas_prices) { executor.reset_all(); - return executor.call(block, transaction, gas_params, eos_evm_version, {}, true, false); + return executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, {}, true, false); } void EstimateGasOracle::throw_exception(ExecutionResult& result, uint64_t cap) { diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.hpp b/silkworm/silkrpc/core/estimate_gas_oracle.hpp index d107bc9d..8e4ab90e 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.hpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.hpp @@ -89,7 +89,7 @@ class EstimateGasOracle { boost::asio::awaitable estimate_gas(const Call& call, const silkworm::Block& latest_block); protected: - virtual ExecutionResult try_execution(EVMExecutor& executor, const silkworm::Block& _block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params); + virtual ExecutionResult try_execution(EVMExecutor& executor, const silkworm::Block& _block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params, const silkworm::gas_prices_t& gas_prices); private: void throw_exception(ExecutionResult& result, uint64_t cap); diff --git a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp index 9083af49..c081e1f6 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp @@ -103,7 +103,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, always fails but success in last step") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -129,7 +129,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, always succeeds") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); const intx::uint256& estimate_gas = result.get(); CHECK(estimate_gas == kTxGas); @@ -138,7 +138,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, alternatively fails and succeeds") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(14) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_ok)) @@ -163,7 +163,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, alternatively succeeds and fails") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(14) .WillOnce(Return(expect_result_ok)) .WillOnce(Return(expect_result_fail)) @@ -189,7 +189,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 4; ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(17) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -217,7 +217,7 @@ TEST_CASE("estimate gas") { SECTION("Call with gas, always succeeds") { call.gas = kTxGas * 4; ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(15) .WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); @@ -232,7 +232,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 2; call.gas_price = intx::uint256{10'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -262,7 +262,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 2; call.gas_price = intx::uint256{40'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(13) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -290,7 +290,7 @@ TEST_CASE("estimate gas") { call.gas_price = intx::uint256{10'000}; call.value = intx::uint256{500'000'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -321,7 +321,7 @@ TEST_CASE("estimate gas") { call.gas_price = intx::uint256{20'000}; call.value = intx::uint256{500'000'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(13) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -345,7 +345,7 @@ TEST_CASE("estimate gas") { SECTION("Call gas above allowance, always succeeds, gas capped") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; call.gas = kGasCap * 2; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)).Times(26).WillRepeatedly(Return(expect_result_ok)); + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)).Times(26).WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); const intx::uint256& estimate_gas = result.get(); @@ -356,7 +356,7 @@ TEST_CASE("estimate gas") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; call.gas = kTxGas / 2; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); const intx::uint256& estimate_gas = result.get(); @@ -368,7 +368,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{2'000'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)).Times(16).WillRepeatedly(Return(expect_result_fail)); + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)).Times(16).WillRepeatedly(Return(expect_result_fail)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); result.get(); CHECK(false); @@ -389,7 +389,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{500'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(expect_result_fail_pre_check)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); @@ -413,7 +413,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{500'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(expect_result_fail_pre_check)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); diff --git a/silkworm/silkrpc/core/evm_debug.cpp b/silkworm/silkrpc/core/evm_debug.cpp index 7a423bc9..aa918700 100644 --- a/silkworm/silkrpc/core/evm_debug.cpp +++ b/silkworm/silkrpc/core/evm_debug.cpp @@ -347,7 +347,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, const silkworm::Blo const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; co_await boost::asio::async_compose( @@ -372,7 +372,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, const silkworm::Blo stream.open_array(); Tracers tracers{debug_tracer}; - const auto execution_result = executor.call(block, txn, gas_params, eos_evm_version, std::move(tracers), /* refund */ false, /* gasBailout */ false); + const auto execution_result = executor.call(block, txn, gas_params, gas_prices, eos_evm_version, std::move(tracers), /* refund */ false, /* gasBailout */ false); debug_tracer->flush_logs(); stream.close_array(); @@ -412,7 +412,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, uint64_t block_numb const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; co_await boost::asio::async_compose( @@ -427,7 +427,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, uint64_t block_numb if (!txn.from) { txn.recover_sender(); } - executor.call(block, txn, gas_params, eos_evm_version); + executor.call(block, txn, gas_params, gas_prices, eos_evm_version); } executor.reset(); @@ -437,7 +437,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, uint64_t block_numb stream.open_array(); Tracers tracers{debug_tracer}; - const auto execution_result = executor.call(block, transaction, gas_params, eos_evm_version, std::move(tracers)); + const auto execution_result = executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers)); debug_tracer->flush_logs(); stream.close_array(); @@ -475,7 +475,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; co_await boost::asio::async_compose( @@ -491,7 +491,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, txn.recover_sender(); } - executor.call(block, txn, gas_params, eos_evm_version); + executor.call(block, txn, gas_params, gas_prices, eos_evm_version); } executor.reset(); @@ -530,7 +530,7 @@ awaitable DebugExecutor::execute(json::Stream& stream, auto debug_tracer = std::make_shared(stream, config_); Tracers tracers{debug_tracer}; - const auto execution_result = executor.call(blockContext.block, txn, gas_params, eos_evm_version, std::move(tracers), /* refund */ false, /* gasBailout */ false); + const auto execution_result = executor.call(blockContext.block, txn, gas_params, gas_prices, eos_evm_version, std::move(tracers), /* refund */ false, /* gasBailout */ false); debug_tracer->flush_logs(); stream.close_array(); diff --git a/silkworm/silkrpc/core/evm_executor.cpp b/silkworm/silkrpc/core/evm_executor.cpp index d0a3ba45..18d1655d 100644 --- a/silkworm/silkrpc/core/evm_executor.cpp +++ b/silkworm/silkrpc/core/evm_executor.cpp @@ -33,7 +33,7 @@ #include #include #include - +#include namespace silkworm::rpc { std::string ExecutionResult::error_message(bool full_error) const { @@ -227,6 +227,7 @@ ExecutionResult EVMExecutor::call( const silkworm::Block& block, const silkworm::Transaction& txn, const evmone::gas_parameters& gas_params, + const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version, Tracers tracers, bool refund, @@ -236,7 +237,7 @@ ExecutionResult EVMExecutor::call( auto& svc = use_service(workers_); //TODO: get gas parameters - EVM evm{block, ibs_state_, config_, gas_params}; + EVM evm{block, ibs_state_, config_}; evm.analysis_cache = svc.get_analysis_cache(); evm.state_pool = svc.get_object_pool(); evm.beneficiary = rule_set_->get_beneficiary(block.header); @@ -256,7 +257,21 @@ ExecutionResult EVMExecutor::call( 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, eos_evm_version, gas_params)}; + + intx::uint256 inclusion_price; + evmone::gas_parameters scaled_gas_params; + if( eos_evm_version >= 3 ) { + inclusion_price = std::min(txn.max_priority_fee_per_gas, txn.max_fee_per_gas - base_fee_per_gas); + if(!gas_prices.is_zero()) { + scaled_gas_params = evmone::gas_parameters::apply_discount_factor(inclusion_price, static_cast(base_fee_per_gas), gas_prices.storage_price, gas_params); + } else { + scaled_gas_params = gas_params; + } + } else { + scaled_gas_params = gas_params; + } + + const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, scaled_gas_params)}; SILKWORM_ASSERT(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) const auto error = pre_check(evm, txn, base_fee_per_gas, g0); @@ -311,7 +326,7 @@ ExecutionResult EVMExecutor::call( silkworm::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)); + result = evm.execute(txn, txn.gas_limit - static_cast(g0), scaled_gas_params); 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"; @@ -323,16 +338,27 @@ ExecutionResult EVMExecutor::call( return {std::nullopt, txn.gas_limit, /* data */ {}, "evm.execute: unknown exception"}; } - uint64_t gas_left = result.gas_left; - const uint64_t gas_used{txn.gas_limit - refund_gas(evm, txn, result.gas_left, result.gas_refund)}; - if (refund) { - gas_left = txn.gas_limit - gas_used; - } - - // Reward the fee recipient const intx::uint256 price{evm.config().protocol_rule_set == protocol::RuleSetType::kTrust ? effective_gas_price : txn.priority_fee_per_gas(base_fee_per_gas)}; - ibs_state_.add_to_balance(evm.beneficiary, price * gas_used); - SILK_DEBUG << "EVMExecutor::call evm.beneficiary: " << evm.beneficiary << " balance: " << price * gas_used; + uint64_t gas_left{0}; + uint64_t gas_used{0}; + if(eos_evm_version < 3) { + gas_left = result.gas_left; + gas_used = txn.gas_limit - refund_gas(evm, txn, result.gas_left, result.gas_refund); + if (refund) { + gas_left = txn.gas_limit - gas_used; + } + // Reward the fee recipient + ibs_state_.add_to_balance(evm.beneficiary, price * gas_used); + SILK_DEBUG << "EVMExecutor::call evm.beneficiary: " << evm.beneficiary << " balance: " << price * gas_used; + } else { + intx::uint256 final_fee{0}; + silkworm::ExecutionResult res; + std::tie(res, final_fee, gas_used, gas_left) = eosevm::gas_refund_v3(eos_evm_version, result, txn, scaled_gas_params, price, gas_prices, inclusion_price); + + // award the fee recipient + ibs_state_.add_to_balance(evm.beneficiary, final_fee); + ibs_state_.add_to_balance(*txn.from, price * gas_left); + } for (auto tracer : evm.tracers()) { tracer.get().on_reward_granted(result, evm.state()); @@ -352,6 +378,7 @@ awaitable EVMExecutor::call(const silkworm::ChainConfig& config const silkworm::Transaction& txn, StateFactory state_factory, const evmone::gas_parameters& gas_params, + const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version, Tracers tracers, bool refund, @@ -362,7 +389,7 @@ awaitable EVMExecutor::call(const silkworm::ChainConfig& config boost::asio::post(workers, [&, self = std::move(self)]() mutable { auto state = state_factory(this_executor, block.header.number); EVMExecutor executor{config, workers, state}; - auto exec_result = executor.call(block, txn, gas_params, eos_evm_version, tracers, refund, gas_bailout); + auto exec_result = executor.call(block, txn, gas_params, gas_prices, eos_evm_version, tracers, refund, gas_bailout); boost::asio::post(this_executor, [exec_result, self = std::move(self)]() mutable { self.complete(exec_result); }); diff --git a/silkworm/silkrpc/core/evm_executor.hpp b/silkworm/silkrpc/core/evm_executor.hpp index ae1e16bf..b37a63d5 100644 --- a/silkworm/silkrpc/core/evm_executor.hpp +++ b/silkworm/silkrpc/core/evm_executor.hpp @@ -39,7 +39,7 @@ #include #include #include - +#include namespace silkworm::rpc { struct ExecutionResult { @@ -85,6 +85,7 @@ class EVMExecutor { const silkworm::Transaction& txn, StateFactory state_factory, const evmone::gas_parameters& gas_params, + const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version, Tracers tracers = {}, bool refund = true, @@ -107,7 +108,7 @@ class EVMExecutor { EVMExecutor(const EVMExecutor&) = delete; EVMExecutor& operator=(const EVMExecutor&) = delete; - ExecutionResult call(const silkworm::Block& block, const silkworm::Transaction& txn, const evmone::gas_parameters& gas_params, uint64_t eos_evm_version, Tracers tracers = {}, bool refund = true, bool gas_bailout = false); + ExecutionResult call(const silkworm::Block& block, const silkworm::Transaction& txn, const evmone::gas_parameters& gas_params, const silkworm::gas_prices_t& gas_prices, uint64_t eos_evm_version, Tracers tracers = {}, bool refund = true, bool gas_bailout = false); void reset(); void reset_all(); diff --git a/silkworm/silkrpc/core/evm_executor_test.cpp b/silkworm/silkrpc/core/evm_executor_test.cpp index 8015c2fb..c80e638a 100644 --- a/silkworm/silkrpc/core/evm_executor_test.cpp +++ b/silkworm/silkrpc/core/evm_executor_test.cpp @@ -81,7 +81,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}); + auto result = executor.call(block, txn, {}, {}, 0, {}); my_pool.stop(); my_pool.join(); CHECK(result.error_code == std::nullopt); @@ -110,7 +110,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}); + auto result = executor.call(block, txn, {}, {}, 0, {}); my_pool.stop(); my_pool.join(); CHECK(result.error_code == std::nullopt); @@ -140,7 +140,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}); + auto result = executor.call(block, txn, {}, {}, 0, {}); my_pool.stop(); my_pool.join(); CHECK(result.error_code == std::nullopt); @@ -170,7 +170,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}); + auto result = executor.call(block, txn, {}, {}, 0, {}); my_pool.stop(); my_pool.join(); CHECK(result.error_code == std::nullopt); @@ -200,7 +200,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}, false, /* gasBailout */ true); + auto result = executor.call(block, txn, {}, {}, 0, {}, false, /* gasBailout */ true); executor.reset(); my_pool.stop(); my_pool.join(); @@ -238,7 +238,7 @@ TEST_CASE("EVMExecutor") { test::DummyTransaction tx{0, mock_cursor}; auto state = tx.create_state(current_executor, tx_database, block_number); EVMExecutor executor{*chain_config_ptr, workers, state}; - auto result = executor.call(block, txn, {}, 0, {}, true, /* gasBailout */ true); + auto result = executor.call(block, txn, {}, {}, 0, {}, true, /* gasBailout */ true); my_pool.stop(); my_pool.join(); CHECK(result.error_code == 0); diff --git a/silkworm/silkrpc/core/evm_trace.cpp b/silkworm/silkrpc/core/evm_trace.cpp index 9b18591c..77b5b599 100644 --- a/silkworm/silkrpc/core/evm_trace.cpp +++ b/silkworm/silkrpc/core/evm_trace.cpp @@ -1238,7 +1238,7 @@ awaitable> TraceCallExecutor::trace_block_transacti const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; @@ -1285,7 +1285,7 @@ awaitable> TraceCallExecutor::trace_block_transacti tracers.push_back(ibs_tracer); - auto execution_result = executor.call(block, transaction, gas_params, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); + auto execution_result = executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); if (execution_result.pre_check_error) { result.pre_check_error = execution_result.pre_check_error.value(); } else { @@ -1317,7 +1317,7 @@ awaitable TraceCallExecutor::trace_calls(const silkworm::Bl const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; const auto ret_result = co_await boost::asio::async_compose( @@ -1357,7 +1357,7 @@ awaitable TraceCallExecutor::trace_calls(const silkworm::Bl } tracers.push_back(ibs_tracer); - auto execution_result = executor.call(block, transaction, gas_params, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); + auto execution_result = executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); if (execution_result.pre_check_error) { result.pre_check_error = "first run for txIndex " + std::to_string(index) + " error: " + execution_result.pre_check_error.value(); @@ -1387,7 +1387,7 @@ boost::asio::awaitable TraceCallExecutor::trace_deploy_transa const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; @@ -1412,7 +1412,7 @@ boost::asio::awaitable TraceCallExecutor::trace_deploy_transa transaction.recover_sender(); } - executor.call(block, transaction, gas_params, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); + executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); executor.reset(); if (create_tracer->found()) { @@ -1461,7 +1461,7 @@ boost::asio::awaitable TraceCallExecutor::trace_transaction_ const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); auto current_executor = co_await boost::asio::this_coro::executor; @@ -1478,7 +1478,7 @@ boost::asio::awaitable TraceCallExecutor::trace_transaction_ Tracers tracers{entry_tracer}; - executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); + executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); boost::asio::post(current_executor, [entry_tracer, self = std::move(self)]() mutable { self.complete(entry_tracer); @@ -1495,7 +1495,7 @@ boost::asio::awaitable TraceCallExecutor::trace_transaction_error(c const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); auto current_executor = co_await boost::asio::this_coro::executor; @@ -1509,7 +1509,7 @@ boost::asio::awaitable TraceCallExecutor::trace_transaction_error(c EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; Tracers tracers{}; - auto execution_result = executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); + auto execution_result = executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, gas_prices, eos_evm_version, std::move(tracers), /*refund=*/true, /*gas_bailout=*/true); std::string result = "0x"; if (execution_result.error_code != evmc_status_code::EVMC_SUCCESS) { @@ -1530,7 +1530,7 @@ boost::asio::awaitable TraceCallExecutor::trace_operation const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, transaction_with_block.block_with_hash.block); auto current_executor = co_await boost::asio::this_coro::executor; auto state = tx_.create_state(current_executor, database_reader_, block_number - 1); @@ -1541,7 +1541,7 @@ boost::asio::awaitable TraceCallExecutor::trace_operation auto tracer = std::make_shared(initial_ibs); Tracers tracers{tracer}; - auto execution_result = executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); + auto execution_result = executor.call(transaction_with_block.block_with_hash.block, transaction_with_block.transaction, gas_params, gas_prices, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); co_return tracer->result(); } @@ -1605,7 +1605,7 @@ awaitable TraceCallExecutor::execute(std::uint64_t block_number const auto chain_id = co_await core::rawdb::read_chain_id(database_reader_); const auto chain_config_ptr = lookup_chain_config(chain_id); - const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); + const auto [eos_evm_version, gas_params, gas_prices] = co_await load_gas_parameters(database_reader_, chain_config_ptr, block); auto current_executor = co_await boost::asio::this_coro::executor; @@ -1628,7 +1628,7 @@ awaitable TraceCallExecutor::execute(std::uint64_t block_number if (!txn.from) { txn.recover_sender(); } - const auto execution_result = executor.call(block, txn, gas_params, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); + const auto execution_result = executor.call(block, txn, gas_params, gas_prices, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); if (execution_result.pre_check_error) { SILK_ERROR << "execution failed for tx " << idx << " due to pre-check error: " << *execution_result.pre_check_error; } @@ -1650,7 +1650,7 @@ awaitable TraceCallExecutor::execute(std::uint64_t block_number tracers.push_back(std::make_shared(traces.state_diff.value(), state_addresses)); } - const auto execution_result = executor.call(block, transaction, gas_params, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); + const auto execution_result = executor.call(block, transaction, gas_params, gas_prices, eos_evm_version, tracers, /*refund=*/true, /*gas_bailout=*/true); if (execution_result.pre_check_error) { result.pre_check_error = execution_result.pre_check_error.value(); diff --git a/silkworm/silkrpc/core/gas_parameters.cpp b/silkworm/silkrpc/core/gas_parameters.cpp index 73b72dea..92ad4660 100644 --- a/silkworm/silkrpc/core/gas_parameters.cpp +++ b/silkworm/silkrpc/core/gas_parameters.cpp @@ -1,14 +1,15 @@ -#include #include - +#include namespace silkworm { -awaitable> load_gas_parameters(const DatabaseReader& tx_database, const silkworm::ChainConfig* chain_config_ptr, const silkworm::Block& block) { +awaitable> load_gas_parameters(const DatabaseReader& tx_database, const silkworm::ChainConfig* chain_config_ptr, const silkworm::Block& block) { auto eos_evm_version = chain_config_ptr->eos_evm_version(block.header); evmone::gas_parameters gas_params; + silkworm::gas_prices_t gas_prices; if(eos_evm_version > 0) { - auto consensus_params = co_await silkworm::rpc::core::rawdb::read_consensus_parameters(tx_database, block.get_consensus_parameter_index()); + auto block_index = block.get_consensus_parameter_index(); + auto consensus_params = co_await silkworm::rpc::core::rawdb::read_consensus_parameters(tx_database, block_index); if(consensus_params.has_value() && consensus_params->gas_fee_parameters.has_value()) { gas_params=evmone::gas_parameters( consensus_params->gas_fee_parameters->gas_txnewaccount, @@ -18,9 +19,16 @@ awaitable> load_gas_parameters(cons consensus_params->gas_fee_parameters->gas_sset ); } + if(eos_evm_version >= 3) { + auto gp = co_await silkworm::rpc::core::rawdb::read_gas_prices(tx_database, block_index); + if(gp.has_value()) { + gas_prices.overhead_price = gp->overhead_price; + gas_prices.storage_price = gp->storage_price; + } + } } - co_return std::make_tuple(eos_evm_version, gas_params); + co_return std::make_tuple(eos_evm_version, gas_params, gas_prices); } } diff --git a/silkworm/silkrpc/core/gas_parameters.hpp b/silkworm/silkrpc/core/gas_parameters.hpp index 729f7227..f51e442b 100644 --- a/silkworm/silkrpc/core/gas_parameters.hpp +++ b/silkworm/silkrpc/core/gas_parameters.hpp @@ -7,12 +7,13 @@ #include #include #include +#include using silkworm::rpc::core::rawdb::DatabaseReader; using boost::asio::awaitable; namespace silkworm { - awaitable> load_gas_parameters(const DatabaseReader& tx_database, const silkworm::ChainConfig* chain_config_ptr, const silkworm::Block& header); + awaitable> load_gas_parameters(const DatabaseReader& tx_database, const silkworm::ChainConfig* chain_config_ptr, const silkworm::Block& header); } diff --git a/silkworm/silkrpc/core/rawdb/chain.cpp b/silkworm/silkrpc/core/rawdb/chain.cpp index 3659ad7e..4a79768a 100644 --- a/silkworm/silkrpc/core/rawdb/chain.cpp +++ b/silkworm/silkrpc/core/rawdb/chain.cpp @@ -33,7 +33,6 @@ #include #include #include -#include namespace silkworm::rpc::core::rawdb { @@ -448,6 +447,16 @@ boost::asio::awaitable> read_consensu co_return eosevm::ConsensusParameters::decode(value); } +boost::asio::awaitable> read_gas_prices(const core::rawdb::DatabaseReader& reader, const std::optional& index) { + if(!index.has_value()) co_return std::nullopt; + const auto block_key = silkworm::db::block_key(index.value().bytes); + const auto value = co_await reader.get_one(db::table::kGasPricesName, block_key); + if (value.empty()) { + co_return std::nullopt; + } + co_return eosevm::gas_prices::decode(value); +} + boost::asio::awaitable> read_extra_block_data(const core::rawdb::DatabaseReader& reader, const evmc::bytes32& block_hash, uint64_t block_number) { const auto block_key = silkworm::db::block_key(block_number, block_hash.bytes); diff --git a/silkworm/silkrpc/core/rawdb/chain.hpp b/silkworm/silkrpc/core/rawdb/chain.hpp index e63e574d..2b41fc2e 100644 --- a/silkworm/silkrpc/core/rawdb/chain.hpp +++ b/silkworm/silkrpc/core/rawdb/chain.hpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace silkworm::rpc::core::rawdb { @@ -92,6 +93,8 @@ boost::asio::awaitable read_cumulative_gas_used(const core::rawdb boost::asio::awaitable> read_consensus_parameters(const core::rawdb::DatabaseReader& reader, const std::optional& index); +boost::asio::awaitable> read_gas_prices(const core::rawdb::DatabaseReader& reader, const std::optional& index); + boost::asio::awaitable> read_extra_block_data(const core::rawdb::DatabaseReader& reader, const evmc::bytes32& block_hash, uint64_t block_number); } // namespace silkworm::rpc::core::rawdb diff --git a/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp b/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp index 6d4d1ddc..667cc33b 100644 --- a/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp +++ b/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp @@ -34,7 +34,7 @@ class MockEstimateGasOracle : public EstimateGasOracle { const silkworm::ChainConfig& config, boost::asio::thread_pool& workers, ethdb::Transaction& tx, ethdb::TransactionDatabase& tx_database) : EstimateGasOracle(block_header_provider, account_reader, config, workers, tx, tx_database) {} - MOCK_METHOD((ExecutionResult), try_execution, (EVMExecutor&, const silkworm::Block&, const silkworm::Transaction&, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params), (override)); + MOCK_METHOD((ExecutionResult), try_execution, (EVMExecutor&, const silkworm::Block&, const silkworm::Transaction&, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params, const silkworm::gas_prices_t& gas_prices), (override)); }; } // namespace silkworm::rpc From 17694f037ae034f2331b6ae1145afbc5b36b4b04 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Wed, 22 Jan 2025 00:33:49 -0300 Subject: [PATCH 6/6] Update evmone --- third_party/evmone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/evmone b/third_party/evmone index 72c15e00..e5315665 160000 --- a/third_party/evmone +++ b/third_party/evmone @@ -1 +1 @@ -Subproject commit 72c15e004f074372d0aae59bcafe9857d893a9c9 +Subproject commit e53156653cbbf4c0e547144f6ac15f9d10ba1981