diff --git a/.github/workflows/contract.yml b/.github/workflows/contract.yml index 41490cb7..b3b55016 100644 --- a/.github/workflows/contract.yml +++ b/.github/workflows/contract.yml @@ -43,12 +43,14 @@ jobs: with: owner: AntelopeIO repo: cdt - target: 'v3.1.0' + target: 'v4.0.1' prereleases: false file: 'cdt_.*amd64.deb' - name: Install CDT - run: sudo apt-get install -y ./cdt*.deb + run: | + sudo apt-get update && sudo apt-get upgrade -y + sudo apt-get install -y ./cdt*.deb - name: Build EOS EVM Contract run: .github/workflows/build-contract.sh @@ -74,7 +76,9 @@ jobs: artifact-name: leap-dev-ubuntu22-amd64 - name: Install Leap - run: sudo apt-get install -y ./leap*.deb + run: | + sudo apt-get update && sudo apt-get upgrade -y + sudo apt-get install -y ./leap*.deb - name: Build EOS EVM Contract Tests run: .github/workflows/build-contract-test.sh diff --git a/include/evm_runtime/config_wrapper.hpp b/include/evm_runtime/config_wrapper.hpp index 14993d25..d0ab67a6 100644 --- a/include/evm_runtime/config_wrapper.hpp +++ b/include/evm_runtime/config_wrapper.hpp @@ -1,9 +1,9 @@ #pragma once -#include + +#include #include #include #include -#include namespace evm_runtime { struct fee_parameters; @@ -27,10 +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_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 4a761a97..b01b979b 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -18,6 +17,8 @@ using namespace eosio; namespace evm_runtime { +struct gas_prices_type; + class [[eosio::contract]] evm_contract : public contract { public: @@ -88,6 +89,8 @@ 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_type& prices); + // Events [[eosio::action]] void evmtx(eosio::ignore event){ eosio::check(get_sender() == get_self(), "forbidden to call"); diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index 94ed03e4..8bd30290 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -8,9 +9,11 @@ #include #include + #include #include + namespace evm_runtime { using namespace eosio; @@ -243,95 +246,15 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config2 EOSLIB_SERIALIZE(config2, (next_account_id)); }; -struct evm_version_type { - struct pending { - uint64_t version; - time_point time; - - bool is_active(time_point_sec genesis_time, time_point current_time)const { - 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(time.time_since_epoch().count()); - return current_block_num > pending_block_num; - } - }; - - uint64_t get_version(time_point_sec genesis_time, time_point current_time)const { - uint64_t current_version = cached_version; - if(pending_version.has_value() && pending_version->is_active(genesis_time, current_time)) { - current_version = pending_version->version; - } - return current_version; - } - - std::pair get_version_and_maybe_promote(time_point_sec genesis_time, time_point current_time) { - uint64_t current_version = cached_version; - bool promoted = false; - if(pending_version.has_value() && pending_version->is_active(genesis_time, current_time)) { - current_version = pending_version->version; - promote_pending(); - promoted = true; - } - return std::make_pair(current_version, promoted); - } - - void promote_pending() { - eosio::check(pending_version.has_value(), "no pending version"); - cached_version = pending_version.value().version; - pending_version.reset(); - } - - std::optional pending_version; - uint64_t cached_version=0; -}; - -struct pending_consensus_parameter_data_type { - consensus_parameter_data_type data; - time_point pending_time; +struct gas_prices_type { + uint64_t overhead_price{0}; + uint64_t storage_price{0}; }; -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; - } +using evm_version_type = uint64_t; - // 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 - }; - } -}; +VALUE_PROMOTER(evm_version_type); +VALUE_PROMOTER_REV(consensus_parameter_data_type); struct [[eosio::table]] [[eosio::contract("evm_contract")]] config { @@ -342,12 +265,13 @@ 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 consensus_parameter; + 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; - EOSLIB_SERIALIZE(config, (version)(chainid)(genesis_time)(ingress_bridge_fee)(gas_price)(miner_cut)(status)(evm_version)(consensus_parameter)(token_contract)(queue_front_block)); + 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)); }; struct [[eosio::table]] [[eosio::contract("evm_contract")]] price_queue @@ -359,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/include/evm_runtime/types.hpp b/include/evm_runtime/types.hpp index 410204d4..b0e83a7c 100644 --- a/include/evm_runtime/types.hpp +++ b/include/evm_runtime/types.hpp @@ -78,15 +78,24 @@ namespace evm_runtime { using bridge_message = std::variant; - struct evmtx_v0 { + struct evmtx_base { uint64_t eos_evm_version; bytes rlptx; + EOSLIB_SERIALIZE(evmtx_base, (eos_evm_version)(rlptx)); + }; + + struct evmtx_v1 : evmtx_base { uint64_t base_fee_per_gas; - - EOSLIB_SERIALIZE(evmtx_v0, (eos_evm_version)(rlptx)(base_fee_per_gas)); + EOSLIB_SERIALIZE_DERIVED(evmtx_v1, evmtx_base, (base_fee_per_gas)); + }; + + struct evmtx_v3 : evmtx_base { + uint64_t overhead_price; + uint64_t storage_price; + EOSLIB_SERIALIZE_DERIVED(evmtx_v3, evmtx_base, (overhead_price)(storage_price)); }; - using evmtx_type = std::variant; + using evmtx_type = std::variant; struct fee_parameters { diff --git a/include/evm_runtime/value_promoter.hpp b/include/evm_runtime/value_promoter.hpp new file mode 100644 index 00000000..e7d58f13 --- /dev/null +++ b/include/evm_runtime/value_promoter.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include + +#define VALUE_PROMOTER_PENDING(T)\ + struct T##_pending {\ + T value;\ + time_point time;\ + bool is_active(time_point_sec genesis_time, time_point current_time)const {\ + 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(time.time_since_epoch().count());\ + return current_block_num > pending_block_num;\ + }\ + }; + +#define VALUE_PROMOTER_IMPL(T)\ + T get_value(time_point_sec genesis_time, time_point current_time)const {\ + T current_value = cached_value;\ + if(pending_value.has_value() && pending_value->is_active(genesis_time, current_time)) {\ + current_value = pending_value->value;\ + }\ + return current_value;\ + }\ + std::pair get_value_and_maybe_promote(time_point_sec genesis_time, time_point current_time) {\ + T current_value = cached_value;\ + bool promoted = false;\ + if(pending_value.has_value() && pending_value->is_active(genesis_time, current_time)) {\ + current_value = pending_value->value;\ + promote_pending();\ + promoted = true;\ + }\ + return std::pair(current_value, promoted);\ + }\ + template \ + void update(Visitor&& visitor_fn, time_point_sec genesis_time, time_point current_time) {\ + auto value = get_value_and_maybe_promote(genesis_time, current_time);\ + visitor_fn(value.first);\ + pending_value.emplace(T##_pending{\ + .value = value.first,\ + .time = current_time\ + });\ + }\ + void promote_pending() {\ + eosio::check(pending_value.has_value(), "no pending value");\ + cached_value = pending_value.value().value;\ + pending_value.reset();\ + } + +#define VALUE_PROMOTER(T)\ +VALUE_PROMOTER_PENDING(T);\ +struct value_promoter_##T {\ + std::optional pending_value;\ + T cached_value = T{};\ + VALUE_PROMOTER_IMPL(T)\ +}; + +#define VALUE_PROMOTER_REV(T)\ +VALUE_PROMOTER_PENDING(T);\ +struct value_promoter_##T {\ + T cached_value = T{};\ + std::optional pending_value;\ + VALUE_PROMOTER_IMPL(T)\ +}; diff --git a/silkworm b/silkworm index c4da9c73..c21967a6 160000 --- a/silkworm +++ b/silkworm @@ -1 +1 @@ -Subproject commit c4da9c735207b8d2a2a018ebfc6bb9f3bfd3add4 +Subproject commit c21967a63932f5e3dd09ce5b68c9d5c7267d45f3 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a00c1a0..4a2a445e 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=37328) + target_link_options(evm_runtime PUBLIC --stack-size=35984) endif() diff --git a/src/actions.cpp b/src/actions.cpp index a2997856..fd67e941 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -472,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, @@ -493,8 +498,6 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const ); }, gas_param_pair.first); - silkworm::ExecutionProcessor ep{block, engine, state, *found_chain_config->second, gas_params}; - if (current_version >= 1) { auto inclusion_price = std::min(tx.max_priority_fee_per_gas, tx.max_fee_per_gas - *base_fee_per_gas); eosio::check(inclusion_price >= (min_inclusion_price.has_value() ? *min_inclusion_price : 0), "inclusion price must >= min_inclusion_price"); @@ -503,6 +506,8 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const check(tx.max_fee_per_gas >= _config->get_gas_price(), "gas price is too low"); } + silkworm::ExecutionProcessor ep{block, engine, state, *found_chain_config->second, gas_params}; + // Filter EVM messages (with data) that are sent to the reserved address // corresponding to the EOS account holding the contract (self) ep.set_evm_message_filter([&](const evmc_message& message) -> bool { @@ -522,10 +527,12 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const act.send(gas_param_pair.first); } - if (current_version >= 1) { - auto event = evmtx_type{evmtx_v0{current_version, txn.get_rlptx(), *base_fee_per_gas}}; - action(std::vector{}, get_self(), "evmtx"_n, event) - .send(); + if(current_version >= 3) { + auto event = evmtx_type{evmtx_v3{current_version, txn.get_rlptx(), gas_prices.overhead_price, gas_prices.storage_price}}; + action(std::vector{}, get_self(), "evmtx"_n, event).send(); + } else if (current_version >= 1) { + auto event = evmtx_type{evmtx_v1{current_version, txn.get_rlptx(), *base_fee_per_gas}}; + action(std::vector{}, get_self(), "evmtx"_n, event).send(); } LOGTIME("EVM END"); } @@ -899,4 +906,14 @@ void evm_contract::setgasparam(uint64_t gas_txnewaccount, gas_sset); } +void evm_contract::setgasprices(const gas_prices_type& prices) { + require_auth(get_self()); + 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 9d1b3647..274c6203 100644 --- a/src/config_wrapper.cpp +++ b/src/config_wrapper.cpp @@ -1,6 +1,5 @@ -#pragma once -#include #include +#include namespace evm_runtime { @@ -9,8 +8,12 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se if(_exists) { _cached_config = _config.get(); } + if (!_cached_config.evm_version.has_value()) { + _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()) { - _cached_config.consensus_parameter = consensus_parameter_type(); + _cached_config.consensus_parameter = value_promoter_consensus_parameter_data_type{}; // Don't set dirty because action can be read-only. } if (!_cached_config.token_contract.has_value()) { @@ -20,6 +23,9 @@ config_wrapper::config_wrapper(eosio::name self) : _self(self), _config(self, se if (!_cached_config.queue_front_block.has_value()) { _cached_config.queue_front_block = 0; } + if (!_cached_config.gas_prices.has_value()) { + _cached_config.gas_prices = gas_prices_type{}; + } } config_wrapper::~config_wrapper() { @@ -85,8 +91,18 @@ 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); +gas_prices_type config_wrapper::get_gas_prices()const { + return *_cached_config.gas_prices; +} + +void config_wrapper::set_gas_prices(const gas_prices_type& prices) { + _cached_config.gas_prices = prices; + set_dirty(); +} + +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()); @@ -98,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; } @@ -106,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 ) { @@ -114,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()); @@ -128,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); } @@ -157,18 +199,16 @@ void config_wrapper::set_status(uint32_t status) { } uint64_t config_wrapper::get_evm_version()const { - uint64_t current_version = 0; - if(_cached_config.evm_version.has_value()) { - current_version = _cached_config.evm_version->get_version(_cached_config.genesis_time, get_current_time()); - } - return current_version; + // should not happen + eosio::check(_cached_config.evm_version.has_value(), "evm_version not exist"); + return _cached_config.evm_version->get_value(_cached_config.genesis_time, get_current_time()); } uint64_t config_wrapper::get_evm_version_and_maybe_promote() { uint64_t current_version = 0; bool promoted = false; if(_cached_config.evm_version.has_value()) { - std::tie(current_version, promoted) = _cached_config.evm_version->get_version_and_maybe_promote(_cached_config.genesis_time, get_current_time()); + std::tie(current_version, promoted) = _cached_config.evm_version->get_value_and_maybe_promote(_cached_config.genesis_time, get_current_time()); } if(promoted) { if(current_version >=1 && _cached_config.miner_cut != 0) _cached_config.miner_cut = 0; @@ -179,9 +219,12 @@ uint64_t config_wrapper::get_evm_version_and_maybe_promote() { void config_wrapper::set_evm_version(uint64_t new_version) { eosio::check(new_version <= eosevm::max_eos_evm_version, "Unsupported version"); + eosio::check(new_version != 3 || _cached_config.queue_front_block.value() == 0, "price queue must be empty"); auto current_version = get_evm_version_and_maybe_promote(); eosio::check(new_version > current_version, "new version must be greater than the active one"); - _cached_config.evm_version.emplace(evm_version_type{evm_version_type::pending{new_version, get_current_time()}, current_version}); + _cached_config.evm_version->update([&](auto& v) { + v = new_version; + }, _cached_config.genesis_time, get_current_time()); set_dirty(); } @@ -190,7 +233,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); @@ -222,7 +267,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"); @@ -261,16 +308,18 @@ void config_wrapper::update_consensus_parameters2(std::optional gas_tx // should not happen eosio::check(_cached_config.consensus_parameter.has_value(), "consensus_parameter not exist"); - _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; - } - }, get_current_time()); + _cached_config.consensus_parameter->update([&](auto& p) { + std::visit([&](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; + } + }, p); + }, _cached_config.genesis_time, get_current_time()); set_dirty(); } @@ -278,7 +327,7 @@ void config_wrapper::update_consensus_parameters2(std::optional gas_tx const consensus_parameter_data_type& config_wrapper::get_consensus_param() { // should not happen eosio::check(_cached_config.consensus_parameter.has_value(), "consensus_parameter not exist"); - return _cached_config.consensus_parameter->get_consensus_param(_cached_config.genesis_time, get_current_time()); + return _cached_config.consensus_parameter->get_value(_cached_config.genesis_time, get_current_time()); } std::pair config_wrapper::get_consensus_param_and_maybe_promote() { @@ -286,7 +335,7 @@ std::pair config_wrapper::get_conse // 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()); + auto pair = _cached_config.consensus_parameter->get_value_and_maybe_promote(_cached_config.genesis_time, get_current_time()); if (pair.second) { set_dirty(); } diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 2fcd6844..e05a0c75 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -103,6 +103,12 @@ namespace fc { namespace raw { fc::raw::unpack(ds, queue_front_block); tmp.queue_front_block.emplace(queue_front_block); } + if(ds.remaining()) { + evm_test::gas_prices_type prices; + fc::raw::unpack(ds, prices); + tmp.gas_prices.emplace(prices); + } + } FC_RETHROW_EXCEPTIONS(warn, "error unpacking partial_account_table_row") } }} @@ -317,6 +323,7 @@ config_table_row basic_evm_tester::get_config() const static constexpr eosio::chain::name config_singleton_name = "config"_n; const vector d = get_row_by_account(evm_account_name, evm_account_name, config_singleton_name, config_singleton_name); + return fc::raw::unpack(d); } @@ -508,6 +515,11 @@ 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_type& prices, name actor) { + return basic_evm_tester::push_action(evm_account_name, "setgasprices"_n, actor, + mvo()("prices", prices)); +} + evmc::address basic_evm_tester::deploy_contract(evm_eoa& eoa, evmc::bytes bytecode) { uint64_t nonce = eoa.next_nonce; @@ -778,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); @@ -810,11 +833,7 @@ void basic_evm_tester::check_balances() { } silkworm::Transaction basic_evm_tester::get_tx_from_trace(const bytes& v) { - auto evmtx_v = fc::raw::unpack(v.data(), v.size()); - - BOOST_REQUIRE(std::holds_alternative(evmtx_v)); - - const auto& evmtx = std::get(evmtx_v); + auto evmtx = get_event_from_trace(v); BOOST_REQUIRE(evmtx.eos_evm_version == 1); silkworm::Transaction tx; diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index 88de6f1e..9db0f0f3 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -54,13 +54,22 @@ void to_variant(const evmc::address& o, fc::variant& v); namespace evm_test { -struct evmtx_v0 { +struct evmtx_base { uint64_t eos_evm_version; bytes rlptx; - uint64_t base_fee_per_gas; }; -using evmtx_type = std::variant; +struct evmtx_v1 : evmtx_base { + uint64_t base_fee_per_gas; +}; + +struct evmtx_v3 : evmtx_base { + uint64_t overhead_price; + uint64_t storage_price; +}; + + +using evmtx_type = std::variant; struct evm_version_type { struct pending { @@ -87,9 +96,15 @@ struct pending_consensus_parameter_data_type { fc::time_point pending_time; }; struct consensus_parameter_type { - consensus_parameter_data_type current; std::optional pending; + consensus_parameter_data_type current; +}; + +struct gas_prices_type { + uint64_t overhead_price{0}; + uint64_t storage_price{0}; }; + struct config_table_row { unsigned_int version; @@ -103,6 +118,7 @@ struct config_table_row std::optional consensus_parameter; std::optional token_contract; std::optional queue_front_block; + std::optional gas_prices; }; struct config2_table_row @@ -207,9 +223,16 @@ 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::config2_table_row,(next_account_id)) @@ -226,7 +249,9 @@ FC_REFLECT(evm_test::message_receiver, (account)(handler)(min_fee)(flags)); FC_REFLECT(evm_test::bridge_message_v0, (receiver)(sender)(timestamp)(value)(data)); 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::evmtx_base, (eos_evm_version)(rlptx)); +FC_REFLECT_DERIVED(evm_test::evmtx_v1, (evm_test::evmtx_base), (base_fee_per_gas)); +FC_REFLECT_DERIVED(evm_test::evmtx_v3, (evm_test::evmtx_base), (overhead_price)(storage_price)); FC_REFLECT(evm_test::consensus_parameter_type, (current)(pending)); FC_REFLECT(evm_test::pending_consensus_parameter_data_type, (data)(pending_time)); @@ -389,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; @@ -459,6 +485,8 @@ 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_type& prices, name actor=evm_account_name); + void open(name owner); void close(name owner); void withdraw(name owner, asset quantity); @@ -507,9 +535,18 @@ 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; + silkworm::Transaction get_tx_from_trace(const bytes& v); + + template + T get_event_from_trace(const bytes& v) { + auto evmtx_v = fc::raw::unpack(v.data(), v.size()); + BOOST_REQUIRE(std::holds_alternative(evmtx_v)); + return std::get(evmtx_v); + } }; inline constexpr intx::uint256 operator"" _wei(const char* s) { return intx::from_string(s); } diff --git a/tests/gas_fee_tests.cpp b/tests/gas_fee_tests.cpp index cbe5cebd..3572d910 100644 --- a/tests/gas_fee_tests.cpp +++ b/tests/gas_fee_tests.cpp @@ -406,6 +406,128 @@ 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); + + 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()); + }; + + eosevm::block_mapping bm(cfg.genesis_time.sec_since_epoch()); + + setversion(2, evm_account_name); + produce_blocks(2); + + // Price queue must be empty before switching to v3 + setfeeparams({.gas_price = ten_gwei}); + BOOST_REQUIRE_EXCEPTION(setversion(3, evm_account_name), + eosio_assert_message_exception, + eosio_assert_message_is("price queue must be empty")); + produce_blocks(400); + trigger_prices_queue_processing(); + cfg = get_config(); + BOOST_CHECK_EQUAL(cfg.queue_front_block.value(), 0); + + setversion(3, evm_account_name); + produce_blocks(2); + + // 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 1e8a8180..c052b1e9 100644 --- a/tests/version_tests.cpp +++ b/tests/version_tests.cpp @@ -94,7 +94,7 @@ BOOST_FIXTURE_TEST_CASE(set_version, version_tester) try { eosio_assert_message_exception, eosio_assert_message_is("new version must be greater than the active one")); - BOOST_REQUIRE_EXCEPTION(setversion(3, evm_account_name), + BOOST_REQUIRE_EXCEPTION(setversion(4, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("Unsupported version")); @@ -155,9 +155,9 @@ BOOST_FIXTURE_TEST_CASE(set_version, version_tester) try { auto evmtx_v = fc::raw::unpack( trace->action_traces[3].act.data.data(), trace->action_traces[3].act.data.size()); - BOOST_REQUIRE(std::holds_alternative(evmtx_v)); + BOOST_REQUIRE(std::holds_alternative(evmtx_v)); - const auto &evmtx = std::get(evmtx_v); + const auto &evmtx = std::get(evmtx_v); BOOST_CHECK_EQUAL(evmtx.eos_evm_version, 1); BOOST_CHECK_EQUAL(evmtx.base_fee_per_gas, config.gas_price); @@ -297,6 +297,37 @@ BOOST_FIXTURE_TEST_CASE(traces_in_different_eosevm_version, version_tester) try // Check 4 times call to `increment` from alice_addr BOOST_REQUIRE(retrieve(contract_address, alice_addr) == intx::uint256(4)); + ///////////////////////////////////// + /// change EOS EVM VERSION => 3 /// + ///////////////////////////////////// + + setgasprices({.overhead_price=5, .storage_price=6}); + setversion(3, evm_account_name); + 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); + BOOST_REQUIRE(trace->action_traces[1].act.account == token_account_name); + BOOST_REQUIRE(trace->action_traces[1].act.name == "transfer"_n); + BOOST_REQUIRE(trace->action_traces[2].act.account == token_account_name); + BOOST_REQUIRE(trace->action_traces[2].act.name == "transfer"_n); + BOOST_REQUIRE(trace->action_traces[3].act.account == evm_account_name); + BOOST_REQUIRE(trace->action_traces[3].act.name == "evmtx"_n); + + auto event_v3 = get_event_from_trace(trace->action_traces[3].act.data); + BOOST_REQUIRE(event_v3.eos_evm_version == 3); + BOOST_REQUIRE(event_v3.overhead_price == 5); + BOOST_REQUIRE(event_v3.storage_price == 6); + + config = get_config(); + BOOST_REQUIRE(config.gas_prices.has_value()); + BOOST_REQUIRE(config.gas_prices->overhead_price == 5); + BOOST_REQUIRE(config.gas_prices->storage_price == 6); + } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE(exec_does_not_update_version, version_tester) try {