Skip to content

Commit

Permalink
Merge pull request #701 from eosnetworkfoundation/elmato/update-behav…
Browse files Browse the repository at this point in the history
…ior-set-gas-price

Update behavior when changing gas-price
  • Loading branch information
elmato committed Apr 23, 2024
2 parents a6829a5 + b9b7cc6 commit ae3a83f
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 8 deletions.
3 changes: 3 additions & 0 deletions include/evm_runtime/config_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
12 changes: 12 additions & 0 deletions include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions include/evm_runtime/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause)
if (WITH_LARGE_STACK)
target_link_options(evm_runtime PUBLIC --stack-size=50000000)
else()
target_link_options(evm_runtime PUBLIC --stack-size=35216)
target_link_options(evm_runtime PUBLIC --stack-size=34896)
endif()
4 changes: 4 additions & 0 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const
void evm_contract::pushtx(eosio::name miner, bytes rlptx) {
LOGTIME("EVM START0");
assert_unfrozen();
if(_config->get_evm_version() >= 1) _config->process_price_queue();

// Use default runtime configuration parameters.
runtime_config rc;
Expand Down Expand Up @@ -599,6 +600,7 @@ checksum256 evm_contract::get_code_hash(name account) const {
}

void evm_contract::handle_account_transfer(const eosio::asset& quantity, const std::string& memo) {
if(_config->get_evm_version() >= 1) _config->process_price_queue();
eosio::name receiver(memo);

balances balance_table(get_self(), get_self().value);
Expand All @@ -610,6 +612,7 @@ void evm_contract::handle_account_transfer(const eosio::asset& quantity, const s
}

void evm_contract::handle_evm_transfer(eosio::asset quantity, const std::string& memo) {
if(_config->get_evm_version() >= 1) _config->process_price_queue();
//move all incoming quantity in to the contract's balance. the evm bridge trx will "pull" from this balance
balances balance_table(get_self(), get_self().value);
balance_table.modify(balance_table.get(get_self().value), eosio::same_payer, [&](balance& b){
Expand Down Expand Up @@ -688,6 +691,7 @@ bool evm_contract::gc(uint32_t max) {
}

void evm_contract::call_(const runtime_config& rc, intx::uint256 s, const bytes& to, intx::uint256 value, const bytes& data, uint64_t gas_limit, uint64_t nonce) {
if(_config->get_evm_version() >= 1) _config->process_price_queue();

Transaction txn;
txn.type = TransactionType::kLegacy;
Expand Down
46 changes: 44 additions & 2 deletions src/config_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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<uint64_t> gas_txnewaccount, std::optional<uint64_t> gas_newaccount, std::optional<uint64_t> gas_txcreate, std::optional<uint64_t> gas_codedeposit, std::optional<uint64_t> gas_sset)
Expand Down
16 changes: 16 additions & 0 deletions tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
}}

Expand Down Expand Up @@ -742,6 +747,17 @@ bool basic_evm_tester::scan_account_code(std::function<bool(account_code)> visit
return true;
}

bool basic_evm_tester::scan_price_queue(std::function<bool(price_queue)> visitor) const
{
static constexpr eosio::chain::name price_queue_table_name = "pricequeue"_n;

scan_table<price_queue>(
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<char> 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<asset>(data);
Expand Down
43 changes: 39 additions & 4 deletions tests/basic_evm_tester.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#pragma once

#include <eosio/chain/abi_serializer.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/fixed_bytes.hpp>
Expand Down Expand Up @@ -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);
Expand All @@ -66,7 +71,25 @@ struct evm_version_type {
std::optional<pending> 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<consensus_parameter_data_v0>;
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_consensus_parameter_data_type> pending;
};
struct config_table_row
{
unsigned_int version;
Expand All @@ -77,6 +100,7 @@ struct config_table_row
uint32_t miner_cut;
uint32_t status;
std::optional<evm_version_type> evm_version;
std::optional<consensus_parameter_type> consensus_parameter;
};

struct config2_table_row
Expand Down Expand Up @@ -176,10 +200,14 @@ struct account_code {

using bridge_message = std::variant<bridge_message_v0>;

} // 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))
Expand All @@ -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
{
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -469,6 +503,7 @@ class basic_evm_tester : public evm_validating_tester
bool scan_gcstore(std::function<bool(gcstore)> visitor) const;
bool scan_account_code(std::function<bool(account_code)> visitor) const;
void scan_balances(std::function<bool(evm_test::vault_balance_row)> visitor) const;
bool scan_price_queue(std::function<bool(evm_test::price_queue)> visitor) const;

intx::uint128 tx_data_cost(const silkworm::Transaction& txn) const;
};
Expand Down
81 changes: 81 additions & 0 deletions tests/gas_fee_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<price_queue> {
std::vector<price_queue> 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()
10 changes: 9 additions & 1 deletion tests/gas_param_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

{
Expand Down

0 comments on commit ae3a83f

Please sign in to comment.