From 33b16e76a6aa0f8bea58affa7e2ae2c03e6acfaa Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Mon, 28 Oct 2024 15:12:47 -0300 Subject: [PATCH 1/4] 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/4] 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/4] 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/4] 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); }