diff --git a/include/evm_runtime/config_wrapper.hpp b/include/evm_runtime/config_wrapper.hpp index c109bafb..21cce268 100644 --- a/include/evm_runtime/config_wrapper.hpp +++ b/include/evm_runtime/config_wrapper.hpp @@ -28,7 +28,6 @@ struct config_wrapper { void set_ingress_bridge_fee(const eosio::asset& ingress_bridge_fee); uint64_t get_gas_price()const; - void set_gas_price(uint64_t gas_price); uint32_t get_miner_cut()const; void set_miner_cut(uint32_t miner_cut); @@ -43,6 +42,11 @@ struct config_wrapper { void set_fee_parameters(const fee_parameters& fee_params, bool allow_any_to_be_unspecified); + void update_consensus_parameters(eosio::asset ram_price_mb, uint64_t minimum_gas_price); + void update_consensus_parameters2(std::optional gas_txnewaccount, std::optional gas_newaccount, std::optional gas_txcreate, std::optional gas_codedeposit, std::optional gas_sset, std::optional minimum_gas_price); + + std::pair get_consensus_param_and_maybe_promote(); + private: bool is_dirty()const; void set_dirty(); diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index 6ef51f72..78005139 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -85,11 +85,20 @@ class [[eosio::contract]] evm_contract : public contract [[eosio::action]] void setversion(uint64_t version); + [[eosio::action]] void updtgasparam(eosio::asset ram_price_mb, uint64_t minimum_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); + // Events [[eosio::action]] void evmtx(eosio::ignore event){ eosio::check(get_sender() == get_self(), "forbidden to call"); }; + // Events + [[eosio::action]] void configchange(consensus_parameter_data_type consensus_parameter_data) { + eosio::check(get_sender() == get_self(), "forbidden to call"); + }; + using configchange_action = eosio::action_wrapper<"configchange"_n, &evm_contract::configchange>; + #ifdef WITH_ADMIN_ACTIONS [[eosio::action]] void rmgcstore(uint64_t id); [[eosio::action]] void setkvstore(uint64_t account_id, const bytes& key, const std::optional& value); diff --git a/include/evm_runtime/runtime_config.hpp b/include/evm_runtime/runtime_config.hpp index 404fec59..7e12985c 100644 --- a/include/evm_runtime/runtime_config.hpp +++ b/include/evm_runtime/runtime_config.hpp @@ -1,7 +1,6 @@ #pragma once - +#include namespace evm_runtime { - struct runtime_config { bool allow_special_signature = false; bool abort_on_failure = false; @@ -9,4 +8,17 @@ struct runtime_config { bool allow_non_self_miner = true; }; +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 { + uint64_t minimum_gas_price = 0; + gas_parameter_type gas_parameter; +}; +using consensus_parameter_data_type = std::variant; + } //namespace evm_runtime \ No newline at end of file diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index 1be9088c..2bb16407 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -273,6 +274,54 @@ struct evm_version_type { uint64_t cached_version=0; }; +struct pending_consensus_parameter_data_type { + consensus_parameter_data_type data; + time_point pending_time; +}; +struct consensus_parameter_type { + + consensus_parameter_data_type current; + std::optional pending; + + bool is_pending_active(time_point_sec genesis_time, time_point current_time)const { + if (!pending.has_value()) return false; + eosevm::block_mapping bm(genesis_time.sec_since_epoch()); + auto current_block_num = bm.timestamp_to_evm_block_num(current_time.time_since_epoch().count()); + auto pending_block_num = bm.timestamp_to_evm_block_num(pending->pending_time.time_since_epoch().count()); + return current_block_num > pending_block_num; + } + + // Reference invalidated by get_consensus_param_and_maybe_promote and update_consensus_param. + const consensus_parameter_data_type& get_consensus_param( + time_point_sec genesis_time, time_point current_time) const { + if (is_pending_active(genesis_time, current_time)) { + return pending->data; + } + return current; + } + + std::pair get_consensus_param_and_maybe_promote( + time_point_sec genesis_time, time_point current_time) { + if (is_pending_active(genesis_time, current_time)) { + current = pending->data; + pending.reset(); + // don't use make_pair as it create ref to temp objects + return std::pair(current, true); + } + return std::pair(current, false); + } + + template + void update_consensus_param(Visitor visitor_fn, time_point current_time) { + consensus_parameter_data_type new_pending = (pending.has_value() ? pending->data : current); + std::visit(visitor_fn, new_pending); + pending = pending_consensus_parameter_data_type{ + .data = new_pending, + .pending_time = current_time + }; + } +}; + struct [[eosio::table]] [[eosio::contract("evm_contract")]] config { unsigned_int version; // placeholder for future variant index @@ -283,8 +332,9 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config uint32_t miner_cut = 0; uint32_t status = 0; // <- bit mask values from status_flags binary_extension evm_version; + binary_extension consensus_parameter; - EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)); + EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)); }; } //namespace evm_runtime diff --git a/include/evm_runtime/types.hpp b/include/evm_runtime/types.hpp index cf52e68b..67bac0e9 100644 --- a/include/evm_runtime/types.hpp +++ b/include/evm_runtime/types.hpp @@ -12,7 +12,10 @@ namespace evm_runtime { using intx::operator""_u256; + static constexpr uint32_t ninety_percent = 90'000; 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; constexpr unsigned evm_precision = 18; constexpr eosio::name token_account(eosio::name(TOKEN_ACCOUNT_NAME)); @@ -20,6 +23,13 @@ namespace evm_runtime { constexpr intx::uint256 minimum_natively_representable = intx::exp(10_u256, intx::uint256(evm_precision - token_symbol.precision())); static_assert(evm_precision - token_symbol.precision() <= 14, "dust math may overflow a uint64_t"); + constexpr double pow10_const(int v) { + double r = 1.0; + while (v-- > 0) r *= 10.0; + return r; + } + constexpr double minimum_natively_representable_f = pow10_const(evm_precision - token_symbol.precision()); + typedef intx::uint<256> uint256; typedef intx::uint<512> uint512; typedef std::vector bytes; diff --git a/src/actions.cpp b/src/actions.cpp index 91872256..3470fd02 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -422,6 +422,12 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const auto current_version = _config->get_evm_version_and_maybe_promote(); + std::pair gas_param_pair = _config->get_consensus_param_and_maybe_promote(); + if (gas_param_pair.second) { + // should not happen + eosio::check(current_version >= 1, "gas param change requires evm_version >= 1"); + } + std::optional> found_chain_config = lookup_known_chain(_config->get_chainid()); check( found_chain_config.has_value(), "failed to find expected chain config" ); @@ -452,6 +458,12 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const engine.finalize(ep.state(), ep.evm().block()); ep.state().write_to_db(ep.evm().block().header.number); + + if (gas_param_pair.second) { + configchange_action act{get_self(), std::vector()}; + act.send(gas_param_pair.first); + } + if (current_version >= 1) { auto event = evmtx_type{evmtx_v0{current_version, txn.get_rlptx()}}; action(std::vector{}, get_self(), "evmtx"_n, event) @@ -783,4 +795,23 @@ void evm_contract::setversion(uint64_t version) { _config->set_evm_version(version); } +void evm_contract::updtgasparam(eosio::asset ram_price_mb, uint64_t minimum_gas_price) { + require_auth(get_self()); + _config->update_consensus_parameters(ram_price_mb, minimum_gas_price); +} + +void evm_contract::setgasparam(uint64_t gas_txnewaccount, + uint64_t gas_newaccount, + uint64_t gas_txcreate, + uint64_t gas_codedeposit, + uint64_t gas_sset) { + require_auth(get_self()); + _config->update_consensus_parameters2(gas_txnewaccount, + gas_newaccount, + gas_txcreate, + gas_codedeposit, + gas_sset, + std::optional() /* min_gas_price*/); +} + } //evm_runtime diff --git a/src/config_wrapper.cpp b/src/config_wrapper.cpp index c6656176..91401bf9 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -9,6 +9,13 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se if(_exists) { _cached_config = _config.get(); } + if (!_cached_config.consensus_parameter.has_value()) { + _cached_config.consensus_parameter = consensus_parameter_type(); + std::visit([&](auto &v) { + v.minimum_gas_price = _cached_config.gas_price; + }, _cached_config.consensus_parameter->current); + // Don't set dirty because action can be read-only. + } } config_wrapper::~config_wrapper() { @@ -65,12 +72,9 @@ void config_wrapper::set_ingress_bridge_fee(const eosio::asset& ingress_bridge_f } uint64_t config_wrapper::get_gas_price()const { - return _cached_config.gas_price; -} - -void config_wrapper::set_gas_price(uint64_t gas_price) { - _cached_config.gas_price = gas_price; - set_dirty(); + return std::visit([&](const auto& v) { + return v.minimum_gas_price; + }, _cached_config.consensus_parameter->get_consensus_param(_cached_config.genesis_time, get_current_time())); } uint32_t config_wrapper::get_miner_cut()const { @@ -78,6 +82,7 @@ uint32_t config_wrapper::get_miner_cut()const { } void config_wrapper::set_miner_cut(uint32_t miner_cut) { + eosio::check(miner_cut <= ninety_percent, "miner_cut must <= 90%"); _cached_config.miner_cut = miner_cut; set_dirty(); } @@ -121,14 +126,29 @@ void config_wrapper::set_fee_parameters(const fee_parameters& fee_params, bool allow_any_to_be_unspecified) { if (fee_params.gas_price.has_value()) { - _cached_config.gas_price = *fee_params.gas_price; + eosio::check(*fee_params.gas_price >= one_gwei, "gas_price must >= 1Gwei"); + if (get_evm_version() >= 1) { + // activate in the next evm block + this->update_consensus_parameters2(std::optional(), /* gas_txnewaccount */ + std::optional(), /* gas_newaccount */ + std::optional(), /* gas_txcreate */ + std::optional(), /* gas_codedeposit */ + std::optional(), /* gas_sset */ + *fee_params.gas_price /* minimum_gas_price */ + ); + } else { + _cached_config.gas_price = *fee_params.gas_price; + std::visit([&](auto &v) { + v.minimum_gas_price = _cached_config.gas_price; + }, + _cached_config.consensus_parameter->current); + } } else { eosio::check(allow_any_to_be_unspecified, "All required fee parameters not specified: missing gas_price"); } if (fee_params.miner_cut.has_value()) { - eosio::check(*fee_params.miner_cut <= hundred_percent, "miner_cut cannot exceed 100,000 (100%)"); - + eosio::check(*fee_params.miner_cut <= ninety_percent, "miner_cut must <= 90%"); _cached_config.miner_cut = *fee_params.miner_cut; } else { eosio::check(allow_any_to_be_unspecified, "All required fee parameters not specified: missing miner_cut"); @@ -144,6 +164,75 @@ void config_wrapper::set_fee_parameters(const fee_parameters& fee_params, set_dirty(); } +void config_wrapper::update_consensus_parameters(eosio::asset ram_price_mb, uint64_t minimum_gas_price) { + + eosio::check(ram_price_mb.symbol == token_symbol, "invalid price symbol"); + eosio::check(minimum_gas_price >= one_gwei, "gas_price must >= 1Gwei"); + + double gas_per_byte_f = (ram_price_mb.amount / (1024.0 * 1024.0) * minimum_natively_representable_f) / (minimum_gas_price * static_cast(hundred_percent - _cached_config.miner_cut) / hundred_percent); + + constexpr uint64_t account_bytes = 347; + constexpr uint64_t contract_fixed_bytes = 606; + constexpr uint64_t storage_slot_bytes = 346; + + constexpr uint64_t max_gas_per_byte = (1ull << 43) - 1; + + eosio::check(gas_per_byte_f >= 0.0, "gas_per_byte must >= 0"); + eosio::check(gas_per_byte_f * contract_fixed_bytes < (double)(max_gas_per_byte), "gas_per_byte too big"); + + uint64_t gas_per_byte = (uint64_t)(gas_per_byte_f + 1.0); + + this->update_consensus_parameters2(account_bytes * gas_per_byte, /* gas_txnewaccount */ + account_bytes * gas_per_byte, /* gas_newaccount */ + contract_fixed_bytes * gas_per_byte, /*gas_txcreate*/ + gas_per_byte,/*gas_codedeposit*/ + gas_sset_min + storage_slot_bytes * gas_per_byte,/*gas_sset*/ + minimum_gas_price /*minimum_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, std::optional minimum_gas_price) +{ + eosio::check(get_evm_version() >= 1, "evm_version must >= 1"); + + // should not happen + eosio::check(_cached_config.consensus_parameter.has_value(), "consensus_parameter not exist"); + + if (minimum_gas_price.has_value()) { + eosio::check(*minimum_gas_price >= one_gwei, "gas_price must >= 1Gwei"); + } + + _cached_config.consensus_parameter->update_consensus_param([&](auto & v) { + if (gas_txnewaccount.has_value()) v.gas_parameter.gas_txnewaccount = *gas_txnewaccount; + if (gas_newaccount.has_value()) v.gas_parameter.gas_newaccount = *gas_newaccount; + if (gas_txcreate.has_value()) v.gas_parameter.gas_txcreate = *gas_txcreate; + if (gas_codedeposit.has_value()) v.gas_parameter.gas_codedeposit = *gas_codedeposit; + if (gas_sset.has_value()) { + eosio::check(*gas_sset >= gas_sset_min, "gas_sset too small"); + v.gas_parameter.gas_sset = *gas_sset; + } + if (minimum_gas_price.has_value()) v.minimum_gas_price = *minimum_gas_price; + }, get_current_time()); + + set_dirty(); +} + +std::pair config_wrapper::get_consensus_param_and_maybe_promote() { + + // should not happen + eosio::check(_cached_config.consensus_parameter.has_value(), "consensus_parameter not exist"); + + auto pair = _cached_config.consensus_parameter->get_consensus_param_and_maybe_promote(_cached_config.genesis_time, get_current_time()); + if (pair.second) { + std::visit([&](const auto &v) { + _cached_config.gas_price = v.minimum_gas_price; + }, pair.first); + set_dirty(); + } + + return pair; +} + bool config_wrapper::is_dirty()const { return _dirty; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9307e09e..00fec591 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,7 @@ add_eosio_test_executable( unit_test ${CMAKE_SOURCE_DIR}/native_token_tests.cpp ${CMAKE_SOURCE_DIR}/mapping_tests.cpp ${CMAKE_SOURCE_DIR}/gas_fee_tests.cpp + ${CMAKE_SOURCE_DIR}/gas_param_tests.cpp ${CMAKE_SOURCE_DIR}/blockhash_tests.cpp ${CMAKE_SOURCE_DIR}/exec_tests.cpp ${CMAKE_SOURCE_DIR}/call_tests.cpp diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 858b5115..037e81b1 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -419,6 +419,24 @@ transaction_trace_ptr basic_evm_tester::setversion(uint64_t version, name actor) mvo()("version", version)); } +transaction_trace_ptr basic_evm_tester::updtgasparam(asset ram_price_mb, uint64_t minimum_gas_price, name actor) { + return basic_evm_tester::push_action(evm_account_name, "updtgasparam"_n, actor, + mvo()("ram_price_mb", ram_price_mb)("minimum_gas_price", minimum_gas_price)); +} + +transaction_trace_ptr basic_evm_tester::setgasparam(uint64_t gas_txnewaccount, + uint64_t gas_newaccount, + uint64_t gas_txcreate, + uint64_t gas_codedeposit, + uint64_t gas_sset, name actor) { + return basic_evm_tester::push_action(evm_account_name, "setgasparam"_n, actor, + mvo()("gas_txnewaccount", gas_txnewaccount) + ("gas_newaccount", gas_newaccount) + ("gas_txcreate", gas_txcreate) + ("gas_codedeposit", gas_codedeposit) + ("gas_sset", gas_sset)); +} + transaction_trace_ptr basic_evm_tester::rmgcstore(uint64_t id, name actor) { return basic_evm_tester::push_action(evm_account_name, "rmgcstore"_n, actor, mvo()("id", id)); diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index 85afe70f..49f718eb 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -403,6 +403,12 @@ class basic_evm_tester : public evm_validating_tester transaction_trace_ptr call(name from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor); transaction_trace_ptr admincall(const evmc::bytes& from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor); evmc::address deploy_contract(evm_eoa& eoa, evmc::bytes bytecode); + transaction_trace_ptr updtgasparam(asset ram_price_mb, uint64_t minimum_gas_price, name actor); + transaction_trace_ptr setgasparam(uint64_t gas_txnewaccount, + uint64_t gas_newaccount, + uint64_t gas_txcreate, + uint64_t gas_codedeposit, + uint64_t gas_sset, name actor); void addegress(const std::vector& accounts); void removeegress(const std::vector& accounts); diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index bab9980e..03d779a3 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -84,26 +84,36 @@ try { BOOST_CHECK_EQUAL(conf1.miner_cut, starting_miner_cut); BOOST_CHECK_EQUAL(conf1.ingress_bridge_fee, make_asset(starting_ingress_bridge_fee_amount)); - // Cannot set miner_cut to above 100%. - BOOST_REQUIRE_EXCEPTION(setfeeparams({.miner_cut = 100'001}), + // Cannot set miner_cut to above 90%. + BOOST_REQUIRE_EXCEPTION(setfeeparams({.miner_cut = 90'001}), eosio_assert_message_exception, - eosio_assert_message_is("miner_cut cannot exceed 100,000 (100%)")); + eosio_assert_message_is("miner_cut must <= 90%")); - // Change only miner_cut to 100%. - setfeeparams({.miner_cut = 100'000}); + // Change only miner_cut to 90%. + setfeeparams({.miner_cut = 90'000}); const auto& conf2 = get_config(); BOOST_CHECK_EQUAL(conf2.gas_price, conf1.gas_price); - BOOST_CHECK_EQUAL(conf2.miner_cut, 100'000); + BOOST_CHECK_EQUAL(conf2.miner_cut, 90'000); BOOST_CHECK_EQUAL(conf2.ingress_bridge_fee, conf1.ingress_bridge_fee); - // Change only gas_price to 0 - setfeeparams({.gas_price = 0}); + + + BOOST_REQUIRE_EXCEPTION(setfeeparams({.gas_price = 0}), + eosio_assert_message_exception, + eosio_assert_message_is("gas_price must >= 1Gwei")); + + BOOST_REQUIRE_EXCEPTION(setfeeparams({.gas_price = 999'999'999}), + eosio_assert_message_exception, + eosio_assert_message_is("gas_price must >= 1Gwei")); + + // Change only gas_price to 1Gwei + setfeeparams({.gas_price = 1'000'000'000}); const auto& conf3 = get_config(); - BOOST_CHECK_EQUAL(conf3.gas_price, 0); + BOOST_CHECK_EQUAL(conf3.gas_price, 1'000'000'000); BOOST_CHECK_EQUAL(conf3.miner_cut, conf2.miner_cut); BOOST_CHECK_EQUAL(conf3.ingress_bridge_fee, conf2.ingress_bridge_fee); @@ -205,10 +215,10 @@ try { }; std::vector gas_fee_trials = { - {0, 50'000, 0, 0 }, + {1'000'000'000, 50'000, 10'500'000'000'000, 10'500'000'000'000}, {1'000'000'000, 0, 0, 21'000'000'000'000}, {1'000'000'000, 10'000, 2'100'000'000'000, 18'900'000'000'000}, - {1'000'000'000, 100'000, 21'000'000'000'000, 0 }, + {1'000'000'000, 90'000, 18'900'000'000'000, 2'100'000'000'000}, }; // EVM contract account acts as the miner diff --git a/tests/gas_param_tests.cpp b/tests/gas_param_tests.cpp new file mode 100644 index 00000000..590d3adf --- /dev/null +++ b/tests/gas_param_tests.cpp @@ -0,0 +1,168 @@ +#include "basic_evm_tester.hpp" + +using namespace eosio::testing; +using namespace evm_test; + +BOOST_AUTO_TEST_SUITE(evm_gas_param_tests) + +struct gas_param_evm_tester : basic_evm_tester +{ + evm_eoa faucet_eoa; + + static constexpr name miner_account_name = "alice"_n; + + gas_param_evm_tester() : + faucet_eoa(evmc::from_hex("a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954").value()) + { + create_accounts({miner_account_name}); + transfer_token(faucet_account_name, miner_account_name, make_asset(100'0000)); + } + + void fund_evm_faucet() + { + transfer_token(faucet_account_name, evm_account_name, make_asset(100'0000), faucet_eoa.address_0x()); + } +}; + +BOOST_FIXTURE_TEST_CASE(basic, gas_param_evm_tester) try { + + uint64_t suggested_gas_price = 150'000'000'000ull; + init(15555, suggested_gas_price); + + produce_block(); + fund_evm_faucet(); + produce_block(); + + // wrong permission + BOOST_REQUIRE_EXCEPTION( + updtgasparam(asset(10'0000, native_symbol), 1'000'000'000, "alice"_n), + missing_auth_exception, [](const missing_auth_exception & e){ return true;}); + + // wrong permission + BOOST_REQUIRE_EXCEPTION( + setgasparam(1,2,3,4,5, "alice"_n), + missing_auth_exception, [](const missing_auth_exception & e){ return true;}); + + // require promoted evm_version larger or equal to 1 + BOOST_REQUIRE_EXCEPTION( + updtgasparam(asset(10'0000, native_symbol), 1'000'000'000, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("evm_version must >= 1")); + + // require promoted evm_version larger or equal to 1 + BOOST_REQUIRE_EXCEPTION( + setgasparam(1,2,3,4,5, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("evm_version must >= 1")); + + setversion(1, evm_account_name); + + produce_block(); + produce_block(); + + // kick of setverion, evmtx event generated + { + evm_eoa recipient; + silkworm::Transaction tx{ + .type = silkworm::Transaction::Type::kLegacy, + .max_priority_fee_per_gas = suggested_gas_price, + .max_fee_per_gas = suggested_gas_price, + .gas_limit = 21000, + .to = recipient.address, + .value = 1, + }; + faucet_eoa.sign(tx); + chain::transaction_trace_ptr trace = pushtx(tx); + BOOST_REQUIRE_EQUAL(trace->action_traces.size(), 2); + BOOST_ASSERT(trace->action_traces.size() >= 2 && trace->action_traces[0].act.name == "pushtx"_n); + BOOST_ASSERT(trace->action_traces.size() >= 2 && trace->action_traces[1].act.name == "evmtx"_n); + } + + // require miniumum gas at least 1 gwei + BOOST_REQUIRE_EXCEPTION( + updtgasparam(asset(10'0000, native_symbol), 999'999'999, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("gas_price must >= 1Gwei")); + + // invalid symbol in ram_price_mb paramerter + BOOST_REQUIRE_EXCEPTION( + updtgasparam(asset(10'0000, symbol::from_string("0,EOS")), 1'000'000'000, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("invalid price symbol")); + BOOST_REQUIRE_EXCEPTION( + updtgasparam(asset(10'0000, symbol::from_string("4,SYS")), 1'000'000'000, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("invalid price symbol")); + + // G_sset must >= 2900 + BOOST_REQUIRE_EXCEPTION( + setgasparam(21000,21000,21000,21000,2899, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("gas_sset too small")); + + updtgasparam(asset(10'0000, native_symbol), 1'000'000'000, evm_account_name); + + setgasparam(21000,21000,21000,21000,2900, evm_account_name); + + { + evm_eoa recipient; + silkworm::Transaction tx{ + .type = silkworm::Transaction::Type::kLegacy, + .max_priority_fee_per_gas = 1'000'000'000, + .max_fee_per_gas = 1'000'000'000, + .gas_limit = 21000, + .to = recipient.address, + .value = 1, + }; + uint64_t cur_nonce = faucet_eoa.next_nonce; + faucet_eoa.sign(tx); + BOOST_REQUIRE_EXCEPTION( + pushtx(tx), + eosio_assert_message_exception, + eosio_assert_message_is("gas price is too low")); + faucet_eoa.next_nonce = cur_nonce; + } + + produce_block(); + produce_block(); + + // new gas price take effect in the next evm block, configchange event before evmtx + { + evm_eoa recipient; + silkworm::Transaction tx{ + .type = silkworm::Transaction::Type::kLegacy, + .max_priority_fee_per_gas = 1'000'000'000, + .max_fee_per_gas = 1'000'000'000, + .gas_limit = 21000, + .to = recipient.address, + .value = 1, + }; + faucet_eoa.sign(tx); + chain::transaction_trace_ptr trace = pushtx(tx); + BOOST_REQUIRE_EQUAL(trace->action_traces.size(), 3); + BOOST_ASSERT(trace->action_traces.size() >= 3 && trace->action_traces[0].act.name == "pushtx"_n); + BOOST_ASSERT(trace->action_traces.size() >= 3 && trace->action_traces[1].act.name == "configchange"_n); + BOOST_ASSERT(trace->action_traces.size() >= 3 && trace->action_traces[2].act.name == "evmtx"_n); + } + + // subsequence trxs will have no more configchange event + { + evm_eoa recipient; + silkworm::Transaction tx{ + .type = silkworm::Transaction::Type::kLegacy, + .max_priority_fee_per_gas = 1'000'000'000, + .max_fee_per_gas = 1'000'000'000, + .gas_limit = 21000, + .to = recipient.address, + .value = 2, + }; + faucet_eoa.sign(tx); + chain::transaction_trace_ptr trace = pushtx(tx); + BOOST_REQUIRE_EQUAL(trace->action_traces.size(), 2); + BOOST_ASSERT(trace->action_traces.size() >= 2 && trace->action_traces[0].act.name == "pushtx"_n); + BOOST_ASSERT(trace->action_traces.size() >= 2 && trace->action_traces[1].act.name == "evmtx"_n); + } + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END()