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..b1394d86 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -337,4 +337,16 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config 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/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 ab9ee82f..71bffa7e 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=34880) endif() diff --git a/src/actions.cpp b/src/actions.cpp index 56729279..bf9edfcb 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -517,6 +517,7 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const void evm_contract::pushtx(eosio::name miner, bytes rlptx, eosio::binary_extension min_inclusion_price) { LOGTIME("EVM START0"); assert_unfrozen(); + if(_config->get_evm_version() >= 1) _config->process_price_queue(); // Use default runtime configuration parameters. runtime_config rc; @@ -609,6 +610,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); @@ -620,6 +622,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){ @@ -698,6 +701,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..97a9e8f5 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -77,6 +77,40 @@ void config_wrapper::set_gas_price(uint64_t gas_price) { set_dirty(); } +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_us; + el.price = gas_price; + }); + +} + +void config_wrapper::process_price_queue() { + 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); + } +} + uint32_t config_wrapper::get_miner_cut()const { return _cached_config.miner_cut; } @@ -127,7 +161,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 +212,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) diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index e6ad5a76..9c9346be 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -88,6 +88,11 @@ 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); + } } FC_RETHROW_EXCEPTIONS(warn, "error unpacking partial_account_table_row") } }} @@ -742,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 29cd9976..68dadd32 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -1,5 +1,4 @@ #pragma once - #include #include #include @@ -40,6 +39,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); @@ -66,7 +71,25 @@ 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 config_table_row { unsigned_int version; @@ -77,6 +100,7 @@ struct config_table_row uint32_t miner_cut; uint32_t status; std::optional evm_version; + std::optional consensus_parameter; }; struct config2_table_row @@ -176,10 +200,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::config_table_row, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)) +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)) @@ -198,6 +226,11 @@ 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::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 +386,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; @@ -469,6 +503,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 e51776d2..5a442db1 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -300,4 +300,85 @@ try { } FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(set_gas_price_queue, gas_fee_evm_tester) +try { + init(); + + setversion(1, evm_account_name); + produce_blocks(2); + + const auto ten_gwei = 10'000'000'000ull; + + auto get_price_queue = [&]() -> std::vector { + std::vector queue; + scan_price_queue([&](price_queue&& row) -> bool { + queue.push_back(row); + return false; + }); + return queue; + }; + + auto trigger_price_queue_processing = [&](){ + transfer_token("alice"_n, evm_account_name, make_asset(1), evm_account_name.to_string()); + }; + + // Queue change of gas_price to 10Gwei + setfeeparams({.gas_price = ten_gwei}); + 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[0].time, t1.time_since_epoch().count()); + BOOST_CHECK_EQUAL(q[0].price, ten_gwei); + + produce_blocks(100); + + // 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()); + 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); + + while(control->pending_block_time() != t1) { + produce_blocks(1); + } + trigger_price_queue_processing(); + + 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(); + + 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() + 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); {