Skip to content

Commit

Permalink
Merge pull request #682 from eosnetworkfoundation/kayan_gas_param
Browse files Browse the repository at this point in the history
gas param config change event
  • Loading branch information
taokayan committed Feb 27, 2024
2 parents 2371df3 + 0953e8d commit c5541b3
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 24 deletions.
6 changes: 5 additions & 1 deletion include/evm_runtime/config_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<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, std::optional<uint64_t> minimum_gas_price);

std::pair<const consensus_parameter_data_type &, bool> get_consensus_param_and_maybe_promote();

private:
bool is_dirty()const;
void set_dirty();
Expand Down
9 changes: 9 additions & 0 deletions include/evm_runtime/evm_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<evm_runtime::evmtx_type> 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<bytes>& value);
Expand Down
16 changes: 14 additions & 2 deletions include/evm_runtime/runtime_config.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
#pragma once

#include <variant>
namespace evm_runtime {

struct runtime_config {
bool allow_special_signature = false;
bool abort_on_failure = false;
bool enforce_chain_id = true;
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<consensus_parameter_data_v0>;

} //namespace evm_runtime
52 changes: 51 additions & 1 deletion include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <eosio/binary_extension.hpp>

#include <evm_runtime/types.hpp>
#include <evm_runtime/runtime_config.hpp>
#include <eosevm/block_mapping.hpp>

#include <silkworm/core/common/base.hpp>
Expand Down Expand Up @@ -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_consensus_parameter_data_type> 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<const consensus_parameter_data_type &, bool> 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<const consensus_parameter_data_type &, bool>(current, true);
}
return std::pair<const consensus_parameter_data_type &, bool>(current, false);
}

template <typename Visitor>
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
Expand All @@ -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_type> evm_version;
binary_extension<consensus_parameter_type> 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
10 changes: 10 additions & 0 deletions include/evm_runtime/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@

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));
constexpr eosio::symbol token_symbol("EOS", 4u);
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<char> bytes;
Expand Down
31 changes: 31 additions & 0 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const consensus_parameter_data_type &, bool> 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<std::pair<const std::string, const ChainConfig*>> found_chain_config = lookup_known_chain(_config->get_chainid());
check( found_chain_config.has_value(), "failed to find expected chain config" );

Expand Down Expand Up @@ -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<eosio::permission_level>()};
act.send(gas_param_pair.first);
}

if (current_version >= 1) {
auto event = evmtx_type{evmtx_v0{current_version, txn.get_rlptx()}};
action(std::vector<permission_level>{}, get_self(), "evmtx"_n, event)
Expand Down Expand Up @@ -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<uint64_t>() /* min_gas_price*/);
}

} //evm_runtime
107 changes: 98 additions & 9 deletions src/config_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -65,19 +72,17 @@ 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 {
return _cached_config.miner_cut;
}

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();
}
Expand Down Expand Up @@ -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<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 */
*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");
Expand All @@ -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<double>(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<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, std::optional<uint64_t> 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<const consensus_parameter_data_type &, bool> 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;
}
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading

0 comments on commit c5541b3

Please sign in to comment.