Skip to content

Commit

Permalink
Add gas parameters scaling tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elmato committed Nov 13, 2024
1 parent 9d0d421 commit eddafd3
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 7 deletions.
4 changes: 4 additions & 0 deletions include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] config2
struct gas_prices_type {
uint64_t overhead_price{0};
uint64_t storage_price{0};

uint64_t get_base_price()const {
return std::max(overhead_price, storage_price);
}
};

using evm_version_type = uint64_t;
Expand Down
2 changes: 1 addition & 1 deletion silkworm
Submodule silkworm updated 48 files
+1 −1 CMakeLists.txt
+102 −0 LICENSE
+1 −1 cmd/dev/check_changes.cpp
+1 −1 cmd/dev/scan_txs.cpp
+1 −1 cmd/state-transition/state_transition.cpp
+1 −1 cmd/test/ethereum.cpp
+6 −1 eosevm/block_extra_data.cpp
+1 −0 eosevm/block_extra_data.hpp
+38 −0 eosevm/gas_prices.cpp
+30 −0 eosevm/gas_prices.hpp
+80 −27 silkworm/core/execution/evm.cpp
+12 −1 silkworm/core/execution/evm.hpp
+151 −1 silkworm/core/execution/evm_test.cpp
+2 −2 silkworm/core/execution/execution.hpp
+2 −2 silkworm/core/execution/execution_test.cpp
+79 −11 silkworm/core/execution/processor.cpp
+5 −2 silkworm/core/execution/processor.hpp
+306 −86 silkworm/core/execution/processor_test.cpp
+3 −4 silkworm/core/protocol/blockchain.cpp
+3 −1 silkworm/core/protocol/blockchain.hpp
+14 −0 silkworm/core/types/block.hpp
+16 −0 silkworm/core/types/gas_prices.hpp
+27 −2 silkworm/node/db/access_layer.cpp
+6 −0 silkworm/node/db/access_layer.hpp
+46 −3 silkworm/node/db/access_layer_test.cpp
+5 −1 silkworm/node/db/tables.hpp
+3 −3 silkworm/node/stagedsync/stages/_test.cpp
+15 −0 silkworm/node/stagedsync/stages/stage.cpp
+6 −0 silkworm/node/stagedsync/stages/stage.hpp
+1 −1 silkworm/node/stagedsync/stages/stage_execution.cpp
+3 −3 silkworm/node/stagedsync/stages/stage_history_index_test.cpp
+6 −4 silkworm/node/stagedsync/stages/stage_senders.cpp
+9 −4 silkworm/silkrpc/commands/eth_api.cpp
+2 −1 silkworm/silkrpc/commands/trace_api.cpp
+8 −5 silkworm/silkrpc/core/call_many.cpp
+4 −1 silkworm/silkrpc/core/call_many.hpp
+1 −1 silkworm/silkrpc/core/estimate_gas_oracle.cpp
+1 −1 silkworm/silkrpc/core/estimate_gas_oracle.hpp
+1 −1 silkworm/silkrpc/core/estimate_gas_oracle_test.cpp
+9 −6 silkworm/silkrpc/core/evm_debug.cpp
+36 −19 silkworm/silkrpc/core/evm_executor.cpp
+4 −4 silkworm/silkrpc/core/evm_executor.hpp
+6 −6 silkworm/silkrpc/core/evm_executor_test.cpp
+16 −8 silkworm/silkrpc/core/evm_trace.cpp
+1 −0 silkworm/silkrpc/core/evm_trace.hpp
+1 −1 silkworm/silkrpc/core/gas_parameters.cpp
+3 −3 silkworm/silkrpc/core/gas_parameters.hpp
+1 −1 third_party/evmone
25 changes: 19 additions & 6 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,16 @@ Receipt evm_contract::execute_tx(const runtime_config& rc, eosio::name miner, Bl
check_result( r, tx, "validate_transaction error" );

Receipt receipt;
ep.execute_transaction(tx, receipt);
const auto res = ep.execute_transaction(tx, receipt);

// Calculate the miner portion of the actual gas fee (if necessary):
std::optional<intx::uint256> gas_fee_miner_portion;
if (miner) {
auto version = _config->get_evm_version();
uint64_t tx_gas_used = receipt.cumulative_gas_used; // Only transaction in the "block" so cumulative_gas_used is the tx gas_used.
if(_config->get_evm_version() >= 1) {
if(version >= 3) {
gas_fee_miner_portion.emplace(res.inclusion_fee);
} else if(version >= 1) {
eosio::check(ep.evm().block().header.base_fee_per_gas.has_value(), "no base fee");
intx::uint512 gas_fee = intx::uint256(tx_gas_used) * tx.priority_fee_per_gas(ep.evm().block().header.base_fee_per_gas.value());
check(gas_fee < std::numeric_limits<intx::uint256>::max(), "too much gas");
Expand Down Expand Up @@ -344,12 +347,18 @@ void evm_contract::exec(const exec_input& input, const std::optional<exec_callba
eosevm::block_mapping bm(_config->get_genesis_time().sec_since_epoch());

Block block;

auto evm_version = _config->get_evm_version();

std::optional<uint64_t> base_fee_per_gas;
auto gas_prices = _config->get_gas_prices();
if (evm_version >= 1) {
base_fee_per_gas = _config->get_gas_price();
if( evm_version >= 3) {
base_fee_per_gas = gas_prices.get_base_price();
} else {
base_fee_per_gas = _config->get_gas_price();
}
}

eosevm::prepare_block_header(block.header, bm, get_self().value,
bm.timestamp_to_evm_block_num(eosio::current_time_point().time_since_epoch().count()), evm_version, base_fee_per_gas);

Expand Down Expand Up @@ -475,7 +484,7 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const
auto gas_prices = _config->get_gas_prices();
if (current_version >= 1) {
if( current_version >= 3) {
//base_fee_per_gas = f(gas_prices, min_inclusion_price)
base_fee_per_gas = gas_prices.get_base_price();
} else {
base_fee_per_gas = _config->get_gas_price();
}
Expand All @@ -501,12 +510,16 @@ void evm_contract::process_tx(const runtime_config& rc, eosio::name miner, const
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");
if (current_version >= 3) {
gas_params = evmone::gas_parameters::apply_discount_factor(inclusion_price, *base_fee_per_gas, gas_prices.storage_price, gas_params);
}
} else { // old behavior
check(tx.max_priority_fee_per_gas == tx.max_fee_per_gas, "max_priority_fee_per_gas must be equal to max_fee_per_gas");
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};
auto gp = silkworm::gas_prices_t{gas_prices.overhead_price, gas_prices.storage_price};
silkworm::ExecutionProcessor ep{block, engine, state, *found_chain_config->second, gas_params, gp};

// Filter EVM messages (with data) that are sent to the reserved address
// corresponding to the EOS account holding the contract (self)
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_eosio_test_executable( unit_test
${CMAKE_SOURCE_DIR}/mapping_tests.cpp
${CMAKE_SOURCE_DIR}/gas_fee_tests.cpp
${CMAKE_SOURCE_DIR}/gas_param_tests.cpp
${CMAKE_SOURCE_DIR}/gas_prices_tests.cpp
${CMAKE_SOURCE_DIR}/blockhash_tests.cpp
${CMAKE_SOURCE_DIR}/exec_tests.cpp
${CMAKE_SOURCE_DIR}/call_tests.cpp
Expand Down
237 changes: 237 additions & 0 deletions tests/gas_prices_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#include "basic_evm_tester.hpp"

using namespace eosio::testing;
using namespace evm_test;

#include <eosevm/block_mapping.hpp>

BOOST_AUTO_TEST_SUITE(evm_gas_prices_tests)

struct gas_prices_evm_tester : basic_evm_tester
{
evm_eoa faucet_eoa;

static constexpr name miner_account_name = "alice"_n;

gas_prices_evm_tester() :
faucet_eoa(evmc::from_hex("a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954").value())
{
create_accounts({miner_account_name});
transfer_token(faucet_account_name, miner_account_name, make_asset(10000'0000));
}

void fund_evm_faucet()
{
transfer_token(faucet_account_name, evm_account_name, make_asset(1000'0000), faucet_eoa.address_0x());
}

auto send_tx(auto& eoa, silkworm::Transaction& txn, const silkworm::gas_prices_t& gas_prices, const evmone::gas_parameters& gas_params) -> auto {
auto pre = evm_balance(eoa);
eoa.sign(txn);
pushtx(txn);
auto post = evm_balance(eoa);
BOOST_REQUIRE(pre.has_value() && post.has_value());
auto inclusion_price = static_cast<uint64_t>(txn.priority_fee_per_gas(gas_prices.get_base_price()));
auto effective_gas_price = inclusion_price + static_cast<uint64_t>(gas_prices.get_base_price());
auto cost = *pre - *post - txn.value;
auto total_gas_used = cost/effective_gas_price;
auto scaled_gp = evmone::gas_parameters::apply_discount_factor(inclusion_price, gas_prices.get_base_price(), gas_prices.storage_price, gas_params);
return std::make_tuple(cost, inclusion_price, effective_gas_price, total_gas_used, scaled_gp);
}

auto get_code_len(uint64_t code_id) {
size_t len=0;
scan_account_code([&](const evm_test::account_code& ac) -> bool {
len = ac.code.size();
return ac.id == code_id;
});
return len;
}

auto pad(const std::string& s) {
const size_t l = 64;
if (s.length() >= l) return s;
size_t pl = l - s.length();
return std::string(pl, '0') + s;
}

auto validate_final_fee(const auto& res, uint64_t storage_gas, const silkworm::gas_prices_t& gas_prices) {

auto [cost, inclusion_price, effective_gas_price, total_gas_used, scaled_gp] = res;

intx::uint256 gas_refund = 0;
if( gas_prices.storage_price >= gas_prices.overhead_price ) {
gas_refund = intx::uint256(total_gas_used - storage_gas);
gas_refund *= intx::uint256(gas_prices.storage_price-gas_prices.overhead_price);
gas_refund /= intx::uint256(effective_gas_price);
}

auto cpu_gas = total_gas_used - storage_gas + gas_refund;
auto calculated_final_fee = (intx::uint256(cpu_gas + storage_gas)-gas_refund)*effective_gas_price;
BOOST_REQUIRE(cost == calculated_final_fee);
}

};

BOOST_FIXTURE_TEST_CASE(gas_param_scale, gas_prices_evm_tester) try {
using silkworm::kGiga;

intx::uint256 gas_refund{0};

uint64_t suggested_gas_price = 150*kGiga;
init(15555, suggested_gas_price);
produce_block();

/////////////////////////////////////
/// change EOS EVM VERSION => 3 ///
/////////////////////////////////////
setversion(3, evm_account_name);
fund_evm_faucet();
produce_block();
produce_block();

auto run_gasparams_scale_test = [&](const silkworm::gas_prices_t& gas_prices) {

fund_evm_faucet();
produce_block();

setgasprices({.overhead_price=gas_prices.overhead_price, .storage_price=gas_prices.storage_price});
produce_blocks(2*181);

// Test traces of `handle_evm_transfer` (EVM VERSION=3)
evm_eoa evm1;
const int64_t to_bridge = 1000000;
auto 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<evm_test::evmtx_v3>(trace->action_traces[3].act.data);
BOOST_REQUIRE(event_v3.eos_evm_version == 3);
BOOST_REQUIRE(event_v3.overhead_price == gas_prices.overhead_price);
BOOST_REQUIRE(event_v3.storage_price == gas_prices.storage_price);

evmone::gas_parameters gas_params{
/*G_txnewaccount*/ 10000,
/*G_newaccount*/ 25000,
/*G_txcreate*/ 32000,
/*G_codedeposit*/ 200,
/*G_sset*/ 20000
};

setgasparam(gas_params.G_txnewaccount, gas_params.G_newaccount, gas_params.G_txcreate, gas_params.G_codedeposit, gas_params.G_sset, evm_account_name);
produce_blocks(3);

// *****************************************************
// TEST G_codedeposit and G_txcreate
// *****************************************************

// pragma solidity >=0.8.2 <0.9.0;
// contract Storage {
// uint256 number;
// function store(uint256 num) public {
// number = num;
// }
// function retrieve() public view returns (uint256) {
// return number;
// }
// function transferRandomWei() public {
// address payable randomAddress = payable(address(uint160(uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty, block.number))))));
// randomAddress.transfer(1 wei);
// }
// receive() external payable {}
// }


auto deploy_contract_ex = [&](evm_eoa& eoa, const evmc::bytes& bytecode) {
auto pre = evm_balance(eoa);
const auto gas_price = get_config().gas_price;
const auto base_fee_per_gas = gas_prices.get_base_price();
auto contract_address = deploy_contract(evm1, bytecode);
auto post = evm_balance(eoa);
BOOST_REQUIRE(pre.has_value() && post.has_value());
auto inclusion_price = std::min(gas_price, gas_price - base_fee_per_gas);
auto effective_gas_price = inclusion_price + base_fee_per_gas;
auto cost = *pre - *post;
auto total_gas_used = cost/effective_gas_price;
auto scaled_gp = evmone::gas_parameters::apply_discount_factor(inclusion_price, gas_prices.get_base_price(), gas_prices.storage_price, gas_params);
return std::make_tuple(std::make_tuple(cost, inclusion_price, effective_gas_price, total_gas_used, scaled_gp), contract_address);
};

const std::string contract_bytecode = "608060405234801561001057600080fd5b50610265806100206000396000f3fe6080604052600436106100385760003560e01c80632e64cec11461004457806357756c591461006f5780636057361d146100865761003f565b3661003f57005b600080fd5b34801561005057600080fd5b506100596100af565b6040516100669190610158565b60405180910390f35b34801561007b57600080fd5b506100846100b8565b005b34801561009257600080fd5b506100ad60048036038101906100a891906101a4565b610135565b005b60008054905090565b60004244436040516020016100cf939291906101f2565b6040516020818303038152906040528051906020012060001c90508073ffffffffffffffffffffffffffffffffffffffff166108fc60019081150290604051600060405180830381858888f19350505050158015610131573d6000803e3d6000fd5b5050565b8060008190555050565b6000819050919050565b6101528161013f565b82525050565b600060208201905061016d6000830184610149565b92915050565b600080fd5b6101818161013f565b811461018c57600080fd5b50565b60008135905061019e81610178565b92915050565b6000602082840312156101ba576101b9610173565b5b60006101c88482850161018f565b91505092915050565b6000819050919050565b6101ec6101e78261013f565b6101d1565b82525050565b60006101fe82866101db565b60208201915061020e82856101db565b60208201915061021e82846101db565b60208201915081905094935050505056fea26469706673582212206c1cecefa543d1df1237047de88cf3e3fe2f7a6ed26ffd4ee4f844ef2987845964736f6c634300080d0033";
auto [res, contract_address] = deploy_contract_ex(evm1, evmc::from_hex(contract_bytecode).value());
auto contract_account = find_account_by_address(contract_address).value();
auto code_len = get_code_len(contract_account.code_id.value());
validate_final_fee(res, code_len*std::get<4>(res).G_codedeposit + std::get<4>(res).G_txcreate, gas_prices);

// *****************************************************
// TEST G_sset
// *****************************************************
auto set_value = [&](auto& eoa, const intx::uint256& v) -> auto {
auto txn = generate_tx(contract_address, 0, 1'000'000);
txn.data = evmc::from_hex("6057361d").value(); //sha3(store(uint256))
txn.data += evmc::from_hex(pad(intx::hex(v))).value();
return send_tx(eoa, txn, gas_prices, gas_params);
};
res = set_value(evm1, intx::uint256{77});
validate_final_fee(res, std::get<4>(res).G_sset - (2800 + 100), gas_prices); //sset - reset

// *****************************************************
// TEST G_txnewaccount
// *****************************************************
auto send_to_new_address = [&](auto& eoa, const intx::uint256& v) -> auto {
evm_eoa new_address;
auto txn = generate_tx(new_address.address, v, 1'000'000);
return send_tx(eoa, txn, gas_prices, gas_params);
};
res = send_to_new_address(evm1, intx::uint256{1});
validate_final_fee(res, std::get<4>(res).G_txnewaccount, gas_prices);

// *****************************************************
// TEST G_newaccount
// *****************************************************

// Fund contract with 100Wei
auto txn_fund = generate_tx(contract_address, intx::uint256{100}, 1'000'000);
evm1.sign(txn_fund);
pushtx(txn_fund);
produce_block();

auto transfer_random_wei = [&](auto& eoa) -> auto {
auto txn = generate_tx(contract_address, 0, 1'000'000);
txn.data = evmc::from_hex("57756c59").value(); //sha3(transferRandomWei())
return send_tx(eoa, txn, gas_prices, gas_params);
};

res = transfer_random_wei(evm1);
validate_final_fee(res, std::get<4>(res).G_newaccount, gas_prices);
};

silkworm::gas_prices_t gas_prices1{
.overhead_price = 80*kGiga,
.storage_price = 70*kGiga
};
run_gasparams_scale_test(gas_prices1);

silkworm::gas_prices_t gas_prices2{
.overhead_price = 30*kGiga,
.storage_price = 50*kGiga
};
run_gasparams_scale_test(gas_prices2);

silkworm::gas_prices_t gas_prices3{
.overhead_price = 100*kGiga,
.storage_price = 100*kGiga
};
run_gasparams_scale_test(gas_prices3);

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit eddafd3

Please sign in to comment.