From 9dd689cf7b3eabe6243c959d69b7f1c5136be16e Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Thu, 18 Apr 2024 16:50:59 -0300 Subject: [PATCH 1/4] Implement gas price queue and processing --- include/evm_runtime/config_wrapper.hpp | 3 ++ include/evm_runtime/tables.hpp | 8 ++++- include/evm_runtime/types.hpp | 1 + src/CMakeLists.txt | 2 +- src/actions.cpp | 4 +++ src/config_wrapper.cpp | 45 ++++++++++++++++++++++++-- 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/include/evm_runtime/config_wrapper.hpp b/include/evm_runtime/config_wrapper.hpp index 7451070c..9a04b81b 100644 --- a/include/evm_runtime/config_wrapper.hpp +++ b/include/evm_runtime/config_wrapper.hpp @@ -30,6 +30,9 @@ struct config_wrapper { uint64_t get_gas_price()const; void set_gas_price(uint64_t gas_price); + void enqueue_gas_price(uint64_t gas_price); + void process_price_queue(); + uint32_t get_miner_cut()const; void set_miner_cut(uint32_t miner_cut); diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index 2bb16407..e12e85b0 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -322,6 +322,11 @@ struct consensus_parameter_type { } }; +struct price_time { + uint64_t price; + time_point time; +}; + struct [[eosio::table]] [[eosio::contract("evm_contract")]] config { unsigned_int version; // placeholder for future variant index @@ -333,8 +338,9 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config uint32_t status = 0; // <- bit mask values from status_flags binary_extension evm_version; binary_extension consensus_parameter; + binary_extension> base_price_queue; - EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)); + EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)(base_price_queue)); }; } //namespace evm_runtime diff --git a/include/evm_runtime/types.hpp b/include/evm_runtime/types.hpp index 686bcaef..decaea84 100644 --- a/include/evm_runtime/types.hpp +++ b/include/evm_runtime/types.hpp @@ -16,6 +16,7 @@ namespace evm_runtime { static constexpr uint32_t hundred_percent = 100'000; static constexpr uint64_t one_gwei = 1'000'000'000ull; static constexpr uint64_t gas_sset_min = 2900; + static constexpr uint64_t grace_period_seconds = 180; constexpr unsigned evm_precision = 18; constexpr eosio::name token_account(eosio::name(TOKEN_ACCOUNT_NAME)); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c473de02..ab9ee82f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,5 +92,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause) if (WITH_LARGE_STACK) target_link_options(evm_runtime PUBLIC --stack-size=50000000) else() - target_link_options(evm_runtime PUBLIC --stack-size=35216) + target_link_options(evm_runtime PUBLIC --stack-size=35168) endif() diff --git a/src/actions.cpp b/src/actions.cpp index d55508fc..fc31c35c 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -512,6 +512,7 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const void evm_contract::pushtx(eosio::name miner, bytes rlptx) { LOGTIME("EVM START0"); assert_unfrozen(); + if(_config->get_evm_version() >= 1) _config->process_price_queue(); // Use default runtime configuration parameters. runtime_config rc; @@ -599,6 +600,7 @@ checksum256 evm_contract::get_code_hash(name account) const { } void evm_contract::handle_account_transfer(const eosio::asset& quantity, const std::string& memo) { + if(_config->get_evm_version() >= 1) _config->process_price_queue(); eosio::name receiver(memo); balances balance_table(get_self(), get_self().value); @@ -610,6 +612,7 @@ void evm_contract::handle_account_transfer(const eosio::asset& quantity, const s } void evm_contract::handle_evm_transfer(eosio::asset quantity, const std::string& memo) { + if(_config->get_evm_version() >= 1) _config->process_price_queue(); //move all incoming quantity in to the contract's balance. the evm bridge trx will "pull" from this balance balances balance_table(get_self(), get_self().value); balance_table.modify(balance_table.get(get_self().value), eosio::same_payer, [&](balance& b){ @@ -688,6 +691,7 @@ bool evm_contract::gc(uint32_t max) { } void evm_contract::call_(const runtime_config& rc, intx::uint256 s, const bytes& to, intx::uint256 value, const bytes& data, uint64_t gas_limit, uint64_t nonce) { + if(_config->get_evm_version() >= 1) _config->process_price_queue(); Transaction txn; txn.type = TransactionType::kLegacy; diff --git a/src/config_wrapper.cpp b/src/config_wrapper.cpp index f65e3ae6..1649ebcc 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -13,6 +13,9 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se _cached_config.consensus_parameter = consensus_parameter_type(); // Don't set dirty because action can be read-only. } + if (!_cached_config.base_price_queue.has_value()) { + _cached_config.base_price_queue = std::deque(); + } } config_wrapper::~config_wrapper() { @@ -77,6 +80,36 @@ void config_wrapper::set_gas_price(uint64_t gas_price) { set_dirty(); } +void config_wrapper::enqueue_gas_price(uint64_t gas_price) { + auto pt = price_time{gas_price, eosio::current_time_point() + eosio::seconds(grace_period_seconds)}; + // should not happen + check(_cached_config.base_price_queue.has_value(), "no queue"); + auto& q = _cached_config.base_price_queue.value(); + if(!q.size()) { + q.push_back(pt); + } else { + if(q.back().price == gas_price) { + return; + } + if(q.back().time == pt.time) { + q.back() = pt; + } else { + q.push_back(pt); + } + } + set_dirty(); +} + +void config_wrapper::process_price_queue() { + if(!_cached_config.base_price_queue.has_value()) return; + auto now = eosio::current_time_point(); + auto& q = _cached_config.base_price_queue.value(); + while( q.size() && now >= q.front().time ) { + set_gas_price(q.front().price); + q.pop_front(); + } +} + uint32_t config_wrapper::get_miner_cut()const { return _cached_config.miner_cut; } @@ -127,7 +160,11 @@ void config_wrapper::set_fee_parameters(const fee_parameters& fee_params, { if (fee_params.gas_price.has_value()) { eosio::check(*fee_params.gas_price >= one_gwei, "gas_price must >= 1Gwei"); - _cached_config.gas_price = *fee_params.gas_price; + if(get_evm_version() >= 1) { + enqueue_gas_price(*fee_params.gas_price); + } else { + set_gas_price(*fee_params.gas_price); + } } else { eosio::check(allow_any_to_be_unspecified, "All required fee parameters not specified: missing gas_price"); } @@ -174,7 +211,11 @@ void config_wrapper::update_consensus_parameters(eosio::asset ram_price_mb, uint gas_sset_min + storage_slot_bytes * gas_per_byte /*gas_sset*/ ); - set_gas_price(gas_price); + if(get_evm_version() >= 1) { + enqueue_gas_price(gas_price); + } else { + set_gas_price(gas_price); + } } void config_wrapper::update_consensus_parameters2(std::optional gas_txnewaccount, std::optional gas_newaccount, std::optional gas_txcreate, std::optional gas_codedeposit, std::optional gas_sset) From 51f6cdf6cfdb75e7d463aa8754f78cf810fb8ad8 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Thu, 18 Apr 2024 16:52:14 -0300 Subject: [PATCH 2/4] Add tests for gas price queue processing --- tests/basic_evm_tester.cpp | 10 +++++ tests/basic_evm_tester.hpp | 41 +++++++++++++++++++- tests/gas_fee_tests.cpp | 77 ++++++++++++++++++++++++++++++++++++++ tests/gas_param_tests.cpp | 10 ++++- 4 files changed, 135 insertions(+), 3 deletions(-) diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 4c89b12c..0ed5d8de 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -88,6 +88,16 @@ namespace fc { namespace raw { fc::raw::unpack(ds, version); tmp.evm_version.emplace(version); } + if(ds.remaining()) { + evm_test::consensus_parameter_type consensus_parameter; + fc::raw::unpack(ds, consensus_parameter); + tmp.consensus_parameter.emplace(consensus_parameter); + } + if(ds.remaining()) { + std::deque base_price_queue; + fc::raw::unpack(ds, base_price_queue); + tmp.base_price_queue.emplace(base_price_queue); + } } FC_RETHROW_EXCEPTIONS(warn, "error unpacking partial_account_table_row") } }} diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index a4d60a56..11b72910 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -1,5 +1,5 @@ #pragma once - +#include #include #include #include @@ -66,7 +66,36 @@ struct evm_version_type { std::optional pending_version; uint64_t cached_version=0; }; +struct gas_parameter_type { + uint64_t gas_txnewaccount = 0; + uint64_t gas_newaccount = 25000; + uint64_t gas_txcreate = 32000; + uint64_t gas_codedeposit = 200; + uint64_t gas_sset = 20000; +}; +struct consensus_parameter_data_v0 { + gas_parameter_type gas_parameter; +}; +using consensus_parameter_data_type = std::variant; +struct pending_consensus_parameter_data_type { + consensus_parameter_data_type data; + fc::time_point pending_time; +}; +struct consensus_parameter_type { + consensus_parameter_data_type current; + std::optional pending; +}; +struct price_time { + uint64_t price; + fc::time_point time; + bool operator==(const price_time& o) const { return price == o.price && time == o.time; } +}; +inline std::ostream& operator<<(std::ostream& ds, const price_time& pt) +{ + ds << "{" << pt.price << "," << pt.time.to_iso_string() << "}"; + return ds; +} struct config_table_row { unsigned_int version; @@ -77,6 +106,8 @@ struct config_table_row uint32_t miner_cut; uint32_t status; std::optional evm_version; + std::optional consensus_parameter; + std::optional> base_price_queue; }; struct config2_table_row @@ -179,7 +210,6 @@ using bridge_message = std::variant; } // namespace evm_test -FC_REFLECT(evm_test::config_table_row, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)) FC_REFLECT(evm_test::evm_version_type, (pending_version)(cached_version)) FC_REFLECT(evm_test::evm_version_type::pending, (version)(time)) FC_REFLECT(evm_test::config2_table_row,(next_account_id)) @@ -198,6 +228,12 @@ FC_REFLECT(evm_test::gcstore, (id)(storage_id)); FC_REFLECT(evm_test::account_code, (id)(ref_count)(code)(code_hash)); FC_REFLECT(evm_test::evmtx_v0, (eos_evm_version)(rlptx)(base_fee_per_gas)); +FC_REFLECT(evm_test::price_time, (price)(time)); +FC_REFLECT(evm_test::consensus_parameter_type, (current)(pending)); +FC_REFLECT(evm_test::pending_consensus_parameter_data_type, (data)(pending_time)); +FC_REFLECT(evm_test::consensus_parameter_data_v0, (gas_parameter)); +FC_REFLECT(evm_test::gas_parameter_type, (gas_txnewaccount)(gas_newaccount)(gas_txcreate)(gas_codedeposit)(gas_sset)); + namespace evm_test { class evm_eoa { @@ -353,6 +389,7 @@ class basic_evm_tester : public evm_validating_tester static constexpr uint64_t suggested_gas_price = 150'000'000'000; // 150 gwei static constexpr uint32_t suggested_miner_cut = 10'000; // 10% static constexpr uint64_t suggested_ingress_bridge_fee_amount = 70; // 0.0070 EOS + static constexpr uint64_t price_queue_grace_period = 180; // 180 seconds const symbol native_symbol; diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index e51776d2..cb020582 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -300,4 +300,81 @@ try { } FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(set_gas_price_v1, gas_fee_evm_tester) +try { + init(); + + setversion(1, evm_account_name); + produce_blocks(2); + + const auto ten_gwei = 10'000'000'000ull; + + // Queue change of gas_price to 10Gwei + setfeeparams({.gas_price = ten_gwei}); + + auto conf1 = get_config(); + BOOST_REQUIRE(conf1.base_price_queue.has_value()); + + auto& q = conf1.base_price_queue.value(); + + auto gp1 = price_time{ + .price=ten_gwei, + .time=control->pending_block_time()+fc::seconds(price_queue_grace_period) + }; + + BOOST_CHECK_EQUAL(q.size(), 1); + BOOST_CHECK_EQUAL(q.front(), gp1); + + produce_blocks(2); + + // different time, same price => no effect + setfeeparams({.gas_price = ten_gwei}); + conf1 = get_config();; + q = conf1.base_price_queue.value(); + BOOST_CHECK_EQUAL(q.size(), 1); + BOOST_CHECK_EQUAL(q.front(), gp1); + + produce_blocks(2); + + // different time, different price => [ok] + setfeeparams({.gas_price = ten_gwei+1}); + conf1 = get_config();; + q = conf1.base_price_queue.value(); + auto gp2 = price_time{ + .price=ten_gwei+1, + .time=control->pending_block_time()+fc::seconds(price_queue_grace_period) + }; + + BOOST_CHECK_EQUAL(q.size(), 2); + BOOST_CHECK_EQUAL(q.front(), gp1); + BOOST_CHECK_EQUAL(q.back(), gp2); + + // Process price queue + conf1 = get_config(); + BOOST_CHECK_EQUAL(conf1.gas_price, suggested_gas_price); + + auto trigger_price_queue_processing = [&](){ + transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); + }; + + while(control->pending_block_time() != gp1.time) { + produce_blocks(1); + } + trigger_price_queue_processing(); + conf1 = get_config(); + BOOST_CHECK_EQUAL(conf1.gas_price, gp1.price); + + while(control->pending_block_time() != gp2.time) { + produce_blocks(1); + } + trigger_price_queue_processing(); + conf1 = get_config(); + BOOST_CHECK_EQUAL(conf1.gas_price, gp2.price); + + q = conf1.base_price_queue.value(); + BOOST_CHECK_EQUAL(q.size(), 0); +} +FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/gas_param_tests.cpp b/tests/gas_param_tests.cpp index e04cbfd7..92cc53a2 100644 --- a/tests/gas_param_tests.cpp +++ b/tests/gas_param_tests.cpp @@ -108,9 +108,17 @@ BOOST_FIXTURE_TEST_CASE(basic, gas_param_evm_tester) try { eosio_assert_message_exception, eosio_assert_message_is("gas_sset too small")); - // This change in the gas price now takes effect immediately + // Change in the gas parameters now takes effect immediately, but not gas price updtgasparam(asset(10'0000, native_symbol), 1'000'000'000, evm_account_name); + auto t0 = control->pending_block_time() + fc::seconds(price_queue_grace_period); + while(control->pending_block_time() != t0) { + produce_blocks(1); + } + + // Process price queue + transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); + setgasparam(21000,21000,21000,21000,2900, evm_account_name); { From 3fd8004c0aef24654565d92551956103c55c4f0e Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Sat, 20 Apr 2024 02:50:41 -0300 Subject: [PATCH 3/4] Change price queue implementation. Use a regular table (multi_index) instead of std::deque --- include/evm_runtime/tables.hpp | 20 +++++--- src/CMakeLists.txt | 2 +- src/config_wrapper.cpp | 38 +++++---------- tests/basic_evm_tester.cpp | 16 +++++-- tests/basic_evm_tester.hpp | 27 +++++------ tests/gas_fee_tests.cpp | 86 ++++++++++++++++------------------ 6 files changed, 90 insertions(+), 99 deletions(-) diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index e12e85b0..b1394d86 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -322,11 +322,6 @@ struct consensus_parameter_type { } }; -struct price_time { - uint64_t price; - time_point time; -}; - struct [[eosio::table]] [[eosio::contract("evm_contract")]] config { unsigned_int version; // placeholder for future variant index @@ -338,9 +333,20 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config uint32_t status = 0; // <- bit mask values from status_flags binary_extension evm_version; binary_extension consensus_parameter; - binary_extension> base_price_queue; - EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)(base_price_queue)); + EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)); +}; + +struct [[eosio::table]] [[eosio::contract("evm_contract")]] price_queue +{ + uint64_t time; + uint64_t price; + + uint64_t primary_key()const { return time; } + + EOSLIB_SERIALIZE(price_queue, (time)(price)); }; +typedef eosio::multi_index<"pricequeue"_n, price_queue> price_queue_table; + } //namespace evm_runtime diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ab9ee82f..e1b18fde 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,5 +92,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause) if (WITH_LARGE_STACK) target_link_options(evm_runtime PUBLIC --stack-size=50000000) else() - target_link_options(evm_runtime PUBLIC --stack-size=35168) + target_link_options(evm_runtime PUBLIC --stack-size=35088) endif() diff --git a/src/config_wrapper.cpp b/src/config_wrapper.cpp index 1649ebcc..58a86ea0 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -13,9 +13,6 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se _cached_config.consensus_parameter = consensus_parameter_type(); // Don't set dirty because action can be read-only. } - if (!_cached_config.base_price_queue.has_value()) { - _cached_config.base_price_queue = std::deque(); - } } config_wrapper::~config_wrapper() { @@ -81,32 +78,21 @@ void config_wrapper::set_gas_price(uint64_t gas_price) { } void config_wrapper::enqueue_gas_price(uint64_t gas_price) { - auto pt = price_time{gas_price, eosio::current_time_point() + eosio::seconds(grace_period_seconds)}; - // should not happen - check(_cached_config.base_price_queue.has_value(), "no queue"); - auto& q = _cached_config.base_price_queue.value(); - if(!q.size()) { - q.push_back(pt); - } else { - if(q.back().price == gas_price) { - return; - } - if(q.back().time == pt.time) { - q.back() = pt; - } else { - q.push_back(pt); - } - } - set_dirty(); + price_queue_table queue(_self, _self.value); + auto time = eosio::current_time_point() + eosio::seconds(grace_period_seconds); + queue.emplace(_self, [&](auto& el) { + el.time = time.elapsed.count(); + el.price = gas_price; + }); } void config_wrapper::process_price_queue() { - if(!_cached_config.base_price_queue.has_value()) return; - auto now = eosio::current_time_point(); - auto& q = _cached_config.base_price_queue.value(); - while( q.size() && now >= q.front().time ) { - set_gas_price(q.front().price); - q.pop_front(); + auto now = eosio::current_time_point().elapsed.count(); + price_queue_table queue(_self, _self.value); + auto it = queue.begin(); + while( it != queue.end() && now >= it->time ) { + set_gas_price(it->price); + it = queue.erase(it); } } diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 0ed5d8de..df3bd66a 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -93,11 +93,6 @@ namespace fc { namespace raw { fc::raw::unpack(ds, consensus_parameter); tmp.consensus_parameter.emplace(consensus_parameter); } - if(ds.remaining()) { - std::deque base_price_queue; - fc::raw::unpack(ds, base_price_queue); - tmp.base_price_queue.emplace(base_price_queue); - } } FC_RETHROW_EXCEPTIONS(warn, "error unpacking partial_account_table_row") } }} @@ -752,6 +747,17 @@ bool basic_evm_tester::scan_account_code(std::function visit return true; } +bool basic_evm_tester::scan_price_queue(std::function visitor) const +{ + static constexpr eosio::chain::name price_queue_table_name = "pricequeue"_n; + + scan_table( + price_queue_table_name, evm_account_name, [&visitor](price_queue&& row) { return visitor(row); } + ); + + return true; +} + asset basic_evm_tester::get_eos_balance( const account_name& act ) { vector data = get_row_by_account( "eosio.token"_n, act, "accounts"_n, name(native_symbol.to_symbol_code().value) ); return data.empty() ? asset(0, native_symbol) : fc::raw::unpack(data); diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index 11b72910..f6e783f3 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -40,6 +40,12 @@ inline std::ostream& operator<<(std::ostream& ds, const intx::uint256& num) } // namespace intx +inline std::ostream& operator<<(std::ostream& ds, const fc::time_point& tp) +{ + ds << tp.to_iso_string(); + return ds; +} + namespace fc { void to_variant(const intx::uint256& o, fc::variant& v); @@ -85,17 +91,6 @@ struct consensus_parameter_type { consensus_parameter_data_type current; std::optional pending; }; -struct price_time { - uint64_t price; - fc::time_point time; - bool operator==(const price_time& o) const { return price == o.price && time == o.time; } -}; - -inline std::ostream& operator<<(std::ostream& ds, const price_time& pt) -{ - ds << "{" << pt.price << "," << pt.time.to_iso_string() << "}"; - return ds; -} struct config_table_row { unsigned_int version; @@ -107,7 +102,6 @@ struct config_table_row uint32_t status; std::optional evm_version; std::optional consensus_parameter; - std::optional> base_price_queue; }; struct config2_table_row @@ -207,9 +201,14 @@ struct account_code { using bridge_message = std::variant; -} // namespace evm_test +struct price_queue { + uint64_t time; + uint64_t price; +}; +} // namespace evm_test +FC_REFLECT(evm_test::price_queue, (time)(price)) FC_REFLECT(evm_test::evm_version_type, (pending_version)(cached_version)) FC_REFLECT(evm_test::evm_version_type::pending, (version)(time)) FC_REFLECT(evm_test::config2_table_row,(next_account_id)) @@ -228,7 +227,6 @@ FC_REFLECT(evm_test::gcstore, (id)(storage_id)); FC_REFLECT(evm_test::account_code, (id)(ref_count)(code)(code_hash)); FC_REFLECT(evm_test::evmtx_v0, (eos_evm_version)(rlptx)(base_fee_per_gas)); -FC_REFLECT(evm_test::price_time, (price)(time)); FC_REFLECT(evm_test::consensus_parameter_type, (current)(pending)); FC_REFLECT(evm_test::pending_consensus_parameter_data_type, (data)(pending_time)); FC_REFLECT(evm_test::consensus_parameter_data_v0, (gas_parameter)); @@ -506,6 +504,7 @@ class basic_evm_tester : public evm_validating_tester bool scan_gcstore(std::function visitor) const; bool scan_account_code(std::function visitor) const; void scan_balances(std::function visitor) const; + bool scan_price_queue(std::function visitor) const; intx::uint128 tx_data_cost(const silkworm::Transaction& txn) const; }; diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index cb020582..29c58b9e 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -301,7 +301,7 @@ try { FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE(set_gas_price_v1, gas_fee_evm_tester) +BOOST_FIXTURE_TEST_CASE(set_gas_price_queue, gas_fee_evm_tester) try { init(); @@ -310,69 +310,63 @@ try { const auto ten_gwei = 10'000'000'000ull; - // Queue change of gas_price to 10Gwei - setfeeparams({.gas_price = ten_gwei}); - - auto conf1 = get_config(); - BOOST_REQUIRE(conf1.base_price_queue.has_value()); - - auto& q = conf1.base_price_queue.value(); - - auto gp1 = price_time{ - .price=ten_gwei, - .time=control->pending_block_time()+fc::seconds(price_queue_grace_period) + auto get_price_queue = [&]() -> std::vector { + std::vector queue; + scan_price_queue([&](price_queue&& row) -> bool { + queue.push_back(row); + return false; + }); + return queue; }; - BOOST_CHECK_EQUAL(q.size(), 1); - BOOST_CHECK_EQUAL(q.front(), gp1); - - produce_blocks(2); + auto trigger_price_queue_processing = [&](){ + transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); + }; - // different time, same price => no effect + // Queue change of gas_price to 10Gwei setfeeparams({.gas_price = ten_gwei}); - conf1 = get_config();; - q = conf1.base_price_queue.value(); + auto t1 = control->pending_block_time()+fc::seconds(price_queue_grace_period); + + auto q = get_price_queue(); BOOST_CHECK_EQUAL(q.size(), 1); - BOOST_CHECK_EQUAL(q.front(), gp1); + BOOST_CHECK_EQUAL(q[0].time, t1.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[0].price, ten_gwei); - produce_blocks(2); + produce_blocks(100); - // different time, different price => [ok] - setfeeparams({.gas_price = ten_gwei+1}); - conf1 = get_config();; - q = conf1.base_price_queue.value(); - auto gp2 = price_time{ - .price=ten_gwei+1, - .time=control->pending_block_time()+fc::seconds(price_queue_grace_period) - }; + // Queue change of gas_price to 20Gwei + setfeeparams({.gas_price = 2*ten_gwei}); + auto t2 = control->pending_block_time()+fc::seconds(price_queue_grace_period); + q = get_price_queue(); BOOST_CHECK_EQUAL(q.size(), 2); - BOOST_CHECK_EQUAL(q.front(), gp1); - BOOST_CHECK_EQUAL(q.back(), gp2); + BOOST_CHECK_EQUAL(q[0].time, t1.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[0].price, ten_gwei); + BOOST_CHECK_EQUAL(q[1].time, t2.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[1].price, 2*ten_gwei); - // Process price queue - conf1 = get_config(); - BOOST_CHECK_EQUAL(conf1.gas_price, suggested_gas_price); - - auto trigger_price_queue_processing = [&](){ - transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); - }; - - while(control->pending_block_time() != gp1.time) { + while(control->pending_block_time() != t1) { produce_blocks(1); } trigger_price_queue_processing(); - conf1 = get_config(); - BOOST_CHECK_EQUAL(conf1.gas_price, gp1.price); - while(control->pending_block_time() != gp2.time) { + auto cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.gas_price, ten_gwei); + + q = get_price_queue(); + BOOST_CHECK_EQUAL(q.size(), 1); + BOOST_CHECK_EQUAL(q[0].time, t2.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[0].price, 2*ten_gwei); + + while(control->pending_block_time() != t2) { produce_blocks(1); } trigger_price_queue_processing(); - conf1 = get_config(); - BOOST_CHECK_EQUAL(conf1.gas_price, gp2.price); - q = conf1.base_price_queue.value(); + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.gas_price, 2*ten_gwei); + + q = get_price_queue(); BOOST_CHECK_EQUAL(q.size(), 0); } FC_LOG_AND_RETHROW() From b9b7cc67cf1d7bb07ba4bf23a21621e0c791c974 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Mon, 22 Apr 2024 13:36:09 -0300 Subject: [PATCH 4/4] Allow enqueue_gas_price to be called multiple times on the same block --- src/CMakeLists.txt | 2 +- src/config_wrapper.cpp | 17 ++++++++++++++++- tests/basic_evm_tester.hpp | 1 - tests/gas_fee_tests.cpp | 14 ++++++++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e1b18fde..295d97cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,5 +92,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause) if (WITH_LARGE_STACK) target_link_options(evm_runtime PUBLIC --stack-size=50000000) else() - target_link_options(evm_runtime PUBLIC --stack-size=35088) + target_link_options(evm_runtime PUBLIC --stack-size=34896) endif() diff --git a/src/config_wrapper.cpp b/src/config_wrapper.cpp index 58a86ea0..97a9e8f5 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -80,10 +80,25 @@ void config_wrapper::set_gas_price(uint64_t gas_price) { void config_wrapper::enqueue_gas_price(uint64_t gas_price) { price_queue_table queue(_self, _self.value); auto time = eosio::current_time_point() + eosio::seconds(grace_period_seconds); + auto time_us = time.elapsed.count(); + + auto it = queue.end(); + if( it != queue.begin()) { + --it; + eosio::check(time_us >= it->time, "internal error"); + if(it->time == time_us) { + queue.modify(*it, eosio::same_payer, [&](auto& el) { + el.price = gas_price; + }); + return; + } + } + queue.emplace(_self, [&](auto& el) { - el.time = time.elapsed.count(); + el.time = time_us; el.price = gas_price; }); + } void config_wrapper::process_price_queue() { diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index f6e783f3..91c658bc 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index 29c58b9e..5a442db1 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -334,10 +334,20 @@ try { produce_blocks(100); - // Queue change of gas_price to 20Gwei - setfeeparams({.gas_price = 2*ten_gwei}); + // Queue change of gas_price to 30Gwei + setfeeparams({.gas_price = 3*ten_gwei}); auto t2 = control->pending_block_time()+fc::seconds(price_queue_grace_period); + q = get_price_queue(); + BOOST_CHECK_EQUAL(q.size(), 2); + BOOST_CHECK_EQUAL(q[0].time, t1.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[0].price, ten_gwei); + BOOST_CHECK_EQUAL(q[1].time, t2.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[1].price, 3*ten_gwei); + + // Overwrite queue change (same block) 20Gwei + setfeeparams({.gas_price = 2*ten_gwei}); + q = get_price_queue(); BOOST_CHECK_EQUAL(q.size(), 2); BOOST_CHECK_EQUAL(q[0].time, t1.time_since_epoch().count());