diff --git a/include/evm_runtime/config_wrapper.hpp b/include/evm_runtime/config_wrapper.hpp index 1299b428..d0ab67a6 100644 --- a/include/evm_runtime/config_wrapper.hpp +++ b/include/evm_runtime/config_wrapper.hpp @@ -27,14 +27,19 @@ struct config_wrapper { const eosio::asset& get_ingress_bridge_fee()const; void set_ingress_bridge_fee(const eosio::asset& ingress_bridge_fee); - gas_prices get_gas_prices()const; - gas_prices get_gas_prices_and_maybe_promote(); - void set_gas_prices(const gas_prices& price); + gas_prices_type get_gas_prices()const; + void set_gas_prices(const gas_prices_type& price); uint64_t get_gas_price()const; void set_gas_price(uint64_t gas_price); + template + void enqueue(Func&& update_fnc); void enqueue_gas_price(uint64_t gas_price); + void enqueue_gas_prices(const gas_prices_type& prices); + + template + void process_queue(Func&& update_func); void process_price_queue(); uint32_t get_miner_cut()const; diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index dfaa1d11..b01b979b 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -17,7 +17,7 @@ using namespace eosio; namespace evm_runtime { -struct gas_prices; +struct gas_prices_type; class [[eosio::contract]] evm_contract : public contract { @@ -89,7 +89,7 @@ class [[eosio::contract]] evm_contract : public contract [[eosio::action]] void updtgasparam(eosio::asset ram_price_mb, uint64_t gas_price); [[eosio::action]] void setgasparam(uint64_t gas_txnewaccount, uint64_t gas_newaccount, uint64_t gas_txcreate, uint64_t gas_codedeposit, uint64_t gas_sset); - [[eosio::action]] void setgasprices(const gas_prices& prices); + [[eosio::action]] void setgasprices(const gas_prices_type& prices); // Events [[eosio::action]] void evmtx(eosio::ignore event){ diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index e0ed0e5c..8bd30290 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -246,14 +246,15 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config2 EOSLIB_SERIALIZE(config2, (next_account_id)); }; -struct gas_prices { +struct gas_prices_type { uint64_t overhead_price{0}; uint64_t storage_price{0}; }; -VALUE_PROMOTER(uint64_t); +using evm_version_type = uint64_t; + +VALUE_PROMOTER(evm_version_type); VALUE_PROMOTER_REV(consensus_parameter_data_type); -VALUE_PROMOTER(gas_prices); struct [[eosio::table]] [[eosio::contract("evm_contract")]] config { @@ -264,11 +265,11 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config uint64_t gas_price = 0; uint32_t miner_cut = 0; uint32_t status = 0; // <- bit mask values from status_flags - binary_extension evm_version; + binary_extension evm_version; binary_extension consensus_parameter; binary_extension token_contract; // <- default(unset) means eosio.token binary_extension queue_front_block; - binary_extension gas_prices; + binary_extension gas_prices; EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)(token_contract)(queue_front_block)(gas_prices)); }; @@ -282,7 +283,17 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] price_queue EOSLIB_SERIALIZE(price_queue, (block)(price)); }; - typedef eosio::multi_index<"pricequeue"_n, price_queue> price_queue_table; +struct [[eosio::table]] [[eosio::contract("evm_contract")]] prices_queue +{ + uint64_t block; + gas_prices_type prices; + + uint64_t primary_key()const { return block; } + + EOSLIB_SERIALIZE(prices_queue, (block)(prices)); +}; +typedef eosio::multi_index<"pricesqueue"_n, prices_queue> prices_queue_table; + } //namespace evm_runtime diff --git a/src/actions.cpp b/src/actions.cpp index 8f61efc2..fd67e941 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -457,7 +457,6 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const "unexpected error: EVM contract generated inline pushtx without setting itself as the miner"); auto current_version = _config->get_evm_version_and_maybe_promote(); - auto gas_prices = _config->get_gas_prices_and_maybe_promote(); std::pair gas_param_pair = _config->get_consensus_param_and_maybe_promote(); if (gas_param_pair.second) { @@ -473,8 +472,13 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const Block block; std::optional base_fee_per_gas; + auto gas_prices = _config->get_gas_prices(); if (current_version >= 1) { - base_fee_per_gas = _config->get_gas_price(); + if( current_version >= 3) { + //base_fee_per_gas = f(gas_prices, min_inclusion_price) + } else { + base_fee_per_gas = _config->get_gas_price(); + } } eosevm::prepare_block_header(block.header, bm, get_self().value, @@ -902,9 +906,14 @@ void evm_contract::setgasparam(uint64_t gas_txnewaccount, gas_sset); } -void evm_contract::setgasprices(const gas_prices& prices) { +void evm_contract::setgasprices(const gas_prices_type& prices) { require_auth(get_self()); - _config->set_gas_prices(prices); + auto current_version = _config->get_evm_version_and_maybe_promote(); + if(current_version >= 3) { + _config->enqueue_gas_prices(prices); + } else { + _config->set_gas_prices(prices); + } } } //evm_runtime diff --git a/src/config_wrapper.cpp b/src/config_wrapper.cpp index c0c06749..66da7bec 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -9,7 +9,7 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se _cached_config = _config.get(); } if (!_cached_config.evm_version.has_value()) { - _cached_config.evm_version = value_promoter_uint64_t{}; + _cached_config.evm_version = value_promoter_evm_version_type{}; // Don't set dirty because action can be read-only. } if (!_cached_config.consensus_parameter.has_value()) { @@ -24,7 +24,7 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se _cached_config.queue_front_block = 0; } if (!_cached_config.gas_prices.has_value()) { - _cached_config.gas_prices = value_promoter_gas_prices{}; + _cached_config.gas_prices = gas_prices_type{}; } } @@ -91,31 +91,18 @@ void config_wrapper::set_gas_price(uint64_t gas_price) { set_dirty(); } -gas_prices config_wrapper::get_gas_prices()const { - // should not happen - eosio::check(_cached_config.gas_prices.has_value(), "gas_prices not exist"); - return _cached_config.gas_prices->get_value(_cached_config.genesis_time, get_current_time()); -} - -gas_prices config_wrapper::get_gas_prices_and_maybe_promote() { - // should not happen - eosio::check(_cached_config.gas_prices.has_value(), "gas_prices not exist"); - auto pair = _cached_config.gas_prices->get_value_and_maybe_promote(_cached_config.genesis_time, get_current_time()); - if (pair.second) { - set_dirty(); - } - return pair.first; +gas_prices_type config_wrapper::get_gas_prices()const { + return *_cached_config.gas_prices; } -void config_wrapper::set_gas_prices(const gas_prices& prices) { - _cached_config.gas_prices->update([&](gas_prices& v) { - v = prices; - }, _cached_config.genesis_time, get_current_time()); +void config_wrapper::set_gas_prices(const gas_prices_type& prices) { + _cached_config.gas_prices = prices; set_dirty(); } -void config_wrapper::enqueue_gas_price(uint64_t gas_price) { - price_queue_table queue(_self, _self.value); +template +void config_wrapper::enqueue(Func&& update_fnc) { + Q queue(_self, _self.value); auto activation_time = get_current_time() + eosio::seconds(grace_period_seconds); eosevm::block_mapping bm(get_genesis_time().sec_since_epoch()); @@ -127,7 +114,7 @@ void config_wrapper::enqueue_gas_price(uint64_t gas_price) { eosio::check(activation_block_num >= it->block, "internal error"); if(activation_block_num == it->block) { queue.modify(*it, eosio::same_payer, [&](auto& el) { - el.price = gas_price; + update_fnc(el); }); return; } @@ -135,7 +122,7 @@ void config_wrapper::enqueue_gas_price(uint64_t gas_price) { queue.emplace(_self, [&](auto& el) { el.block = activation_block_num; - el.price = gas_price; + update_fnc(el); }); if( _cached_config.queue_front_block.value() == 0 ) { @@ -143,12 +130,38 @@ void config_wrapper::enqueue_gas_price(uint64_t gas_price) { } } +void config_wrapper::enqueue_gas_price(uint64_t gas_price) { + enqueue([&](auto& el){ + el.price = gas_price; + }); +} + +void config_wrapper::enqueue_gas_prices(const gas_prices_type& prices) { + enqueue([&](auto& el){ + el.prices = prices; + }); +} + void config_wrapper::set_queue_front_block(uint32_t block_num) { _cached_config.queue_front_block = block_num; set_dirty(); } + void config_wrapper::process_price_queue() { + if( get_evm_version() >= 3) { + process_queue([&](const auto& row){ + set_gas_prices(row.prices); + }); + } else { + process_queue([&](const auto& row){ + set_gas_price(row.price); + }); + } +} + +template +void config_wrapper::process_queue(Func&& update_func) { eosevm::block_mapping bm(get_genesis_time().sec_since_epoch()); auto current_block_num = bm.timestamp_to_evm_block_num(get_current_time().time_since_epoch().count()); @@ -157,10 +170,10 @@ void config_wrapper::process_price_queue() { return; } - price_queue_table queue(_self, _self.value); + Q queue(_self, _self.value); auto it = queue.begin(); while( it != queue.end() && current_block_num >= it->block ) { - set_gas_price(it->price); + update_func(*it); it = queue.erase(it); set_queue_front_block(it != queue.end() ? it->block : 0); } @@ -219,7 +232,9 @@ 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"); - if(get_evm_version() >= 1) { + auto current_version = get_evm_version_and_maybe_promote(); + if( current_version >= 1 ) { + eosio::check(current_version < 3, "can't set gas_price"); enqueue_gas_price(*fee_params.gas_price); } else { set_gas_price(*fee_params.gas_price); @@ -251,7 +266,9 @@ void config_wrapper::set_fee_parameters(const fee_parameters& fee_params, } void config_wrapper::update_consensus_parameters(eosio::asset ram_price_mb, uint64_t gas_price) { + eosio::check(get_evm_version() < 3, "unable to set params"); + //TODO: should we allow to call this when version>=3 eosio::check(ram_price_mb.symbol == get_token_symbol(), "invalid price symbol"); eosio::check(gas_price >= one_gwei, "gas_price must >= 1Gwei"); diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 239c8fec..e05a0c75 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -515,7 +515,7 @@ transaction_trace_ptr basic_evm_tester::addopenbal(name account, const intx::uin mvo()("account", account)("delta",d)("subtract",subtract)); } -transaction_trace_ptr basic_evm_tester::setgasprices(const gas_prices_t& prices, name actor) { +transaction_trace_ptr basic_evm_tester::setgasprices(const gas_prices_type& prices, name actor) { return basic_evm_tester::push_action(evm_account_name, "setgasprices"_n, actor, mvo()("prices", prices)); } @@ -790,6 +790,17 @@ bool basic_evm_tester::scan_price_queue(std::function visitor return true; } +bool basic_evm_tester::scan_prices_queue(std::function visitor) const +{ + static constexpr eosio::chain::name prices_queue_table_name = "pricesqueue"_n; + + scan_table( + prices_queue_table_name, evm_account_name, [&visitor](prices_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 a01015f6..9db0f0f3 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -100,21 +100,11 @@ struct consensus_parameter_type { consensus_parameter_data_type current; }; -struct gas_prices_t { +struct gas_prices_type { uint64_t overhead_price{0}; uint64_t storage_price{0}; }; -struct gas_prices_type { - struct pending { - gas_prices_t value; - fc::time_point time; - }; - - std::optional pending_value; - gas_prices_t cached_value{}; -}; - struct config_table_row { unsigned_int version; @@ -233,14 +223,18 @@ struct price_queue { uint64_t price; }; +struct prices_queue { + uint64_t block; + gas_prices_type prices; +}; + } // namespace evm_test FC_REFLECT(evm_test::price_queue, (block)(price)) +FC_REFLECT(evm_test::prices_queue, (block)(prices)) +FC_REFLECT(evm_test::gas_prices_type, (overhead_price)(storage_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::gas_prices_t, (overhead_price)(storage_price)) -FC_REFLECT(evm_test::gas_prices_type, (pending_value)(cached_value)) -FC_REFLECT(evm_test::gas_prices_type::pending, (value)(time)) FC_REFLECT(evm_test::config2_table_row,(next_account_id)) FC_REFLECT(evm_test::balance_and_dust, (balance)(dust)); FC_REFLECT(evm_test::account_object, (id)(address)(nonce)(balance)) @@ -420,6 +414,7 @@ class basic_evm_tester : public evm_validating_tester 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 + static constexpr uint64_t prices_queue_grace_period = 180; // 180 seconds const symbol native_symbol; @@ -490,7 +485,7 @@ class basic_evm_tester : public evm_validating_tester transaction_trace_ptr addevmbal(uint64_t id, const intx::uint256& delta, bool subtract, name actor=evm_account_name); transaction_trace_ptr addopenbal(name account, const intx::uint256& delta, bool subtract, name actor=evm_account_name); - transaction_trace_ptr setgasprices(const gas_prices_t& prices, name actor=evm_account_name); + transaction_trace_ptr setgasprices(const gas_prices_type& prices, name actor=evm_account_name); void open(name owner); void close(name owner); @@ -540,6 +535,7 @@ class basic_evm_tester : public evm_validating_tester bool scan_account_code(std::function visitor) const; void scan_balances(std::function visitor) const; bool scan_price_queue(std::function visitor) const; + bool scan_prices_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 cbe5cebd..6698c4a6 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -406,6 +406,115 @@ try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(set_gas_prices_queue, gas_fee_evm_tester) +try { + init(); + + auto cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), 0); + + eosevm::block_mapping bm(cfg.genesis_time.sec_since_epoch()); + + setversion(3, evm_account_name); + produce_blocks(2); + + const auto one_gwei = 1'000'000'000ull; + const auto ten_gwei = 10'000'000'000ull; + + auto get_prices_queue = [&]() -> std::vector { + std::vector queue; + scan_prices_queue([&](prices_queue&& row) -> bool { + queue.push_back(row); + return false; + }); + return queue; + }; + + auto trigger_prices_queue_processing = [&](){ + transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); + }; + + // Queue change of gas_prices to overhead_price=10Gwei storage_price=1Gwei + setgasprices({.overhead_price = ten_gwei, .storage_price = one_gwei}); + auto t1 = (control->pending_block_time()+fc::seconds(prices_queue_grace_period)).time_since_epoch().count(); + auto b1 = bm.timestamp_to_evm_block_num(t1)+1; + + auto q = get_prices_queue(); + BOOST_CHECK_EQUAL(q.size(), 1); + BOOST_CHECK_EQUAL(q[0].block, b1); + BOOST_CHECK_EQUAL(q[0].prices.overhead_price, ten_gwei); + BOOST_CHECK_EQUAL(q[0].prices.storage_price, one_gwei); + + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), b1); + + produce_blocks(100); + + // Queue change of gas_price to overhead_price=30Gwei storage_price=10Gwei + setgasprices({.overhead_price = 3*ten_gwei, .storage_price=ten_gwei}); + auto t2 = (control->pending_block_time()+fc::seconds(prices_queue_grace_period)).time_since_epoch().count(); + auto b2 = bm.timestamp_to_evm_block_num(t2)+1; + + q = get_prices_queue(); + BOOST_CHECK_EQUAL(q.size(), 2); + BOOST_CHECK_EQUAL(q[0].block, b1); + BOOST_CHECK_EQUAL(q[0].prices.overhead_price, ten_gwei); + BOOST_CHECK_EQUAL(q[0].prices.storage_price, one_gwei); + BOOST_CHECK_EQUAL(q[1].block, b2); + BOOST_CHECK_EQUAL(q[1].prices.overhead_price, 3*ten_gwei); + BOOST_CHECK_EQUAL(q[1].prices.storage_price, ten_gwei); + + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), b1); + + // Overwrite queue change (same block) overhead_price=20Gwei, storage_price=5Gwei + setgasprices({.overhead_price = 2*ten_gwei, .storage_price=5*one_gwei}); + + q = get_prices_queue(); + BOOST_CHECK_EQUAL(q.size(), 2); + BOOST_CHECK_EQUAL(q[0].block, b1); + BOOST_CHECK_EQUAL(q[0].prices.overhead_price, ten_gwei); + BOOST_CHECK_EQUAL(q[0].prices.storage_price, one_gwei); + BOOST_CHECK_EQUAL(q[1].block, b2); + BOOST_CHECK_EQUAL(q[1].prices.overhead_price, 2*ten_gwei); + BOOST_CHECK_EQUAL(q[1].prices.storage_price, 5*one_gwei); + + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), b1); + + while(bm.timestamp_to_evm_block_num(control->pending_block_time().time_since_epoch().count()) != b1) { + produce_blocks(1); + } + trigger_prices_queue_processing(); + + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.gas_prices->overhead_price, ten_gwei); + BOOST_CHECK_EQUAL(cfg.gas_prices->storage_price, one_gwei); + + q = get_prices_queue(); + BOOST_CHECK_EQUAL(q.size(), 1); + BOOST_CHECK_EQUAL(q[0].block, b2); + BOOST_CHECK_EQUAL(q[0].prices.overhead_price, 2*ten_gwei); + BOOST_CHECK_EQUAL(q[0].prices.storage_price, 5*one_gwei); + + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), b2); + + while(bm.timestamp_to_evm_block_num(control->pending_block_time().time_since_epoch().count()) != b2) { + produce_blocks(1); + } + trigger_prices_queue_processing(); + + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.gas_prices->overhead_price, 2*ten_gwei); + BOOST_CHECK_EQUAL(cfg.gas_prices->storage_price, 5*one_gwei); + + q = get_prices_queue(); + BOOST_CHECK_EQUAL(q.size(), 0); + + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), 0); +} +FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(miner_cut_calculation_v1, gas_fee_evm_tester) try { static constexpr uint64_t base_gas_price = 300'000'000'000; // 300 gwei diff --git a/tests/version_tests.cpp b/tests/version_tests.cpp index 6be98773..c052b1e9 100644 --- a/tests/version_tests.cpp +++ b/tests/version_tests.cpp @@ -301,12 +301,13 @@ BOOST_FIXTURE_TEST_CASE(traces_in_different_eosevm_version, version_tester) try /// change EOS EVM VERSION => 3 /// ///////////////////////////////////// - setgasprices(gas_prices_t{.overhead_price=5, .storage_price=6}); + setgasprices({.overhead_price=5, .storage_price=6}); setversion(3, evm_account_name); - produce_blocks(2); + produce_blocks(2*180); // Test traces of `handle_evm_transfer` (EVM VERSION=3) trace = transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x()); + BOOST_REQUIRE(trace->action_traces.size() == 4); BOOST_REQUIRE(trace->action_traces[0].act.account == token_account_name); BOOST_REQUIRE(trace->action_traces[0].act.name == "transfer"_n); @@ -324,9 +325,8 @@ BOOST_FIXTURE_TEST_CASE(traces_in_different_eosevm_version, version_tester) try config = get_config(); BOOST_REQUIRE(config.gas_prices.has_value()); - BOOST_REQUIRE(!config.gas_prices->pending_value.has_value()); - BOOST_REQUIRE(config.gas_prices->cached_value.overhead_price == 5); - BOOST_REQUIRE(config.gas_prices->cached_value.storage_price == 6); + BOOST_REQUIRE(config.gas_prices->overhead_price == 5); + BOOST_REQUIRE(config.gas_prices->storage_price == 6); } FC_LOG_AND_RETHROW()