Skip to content

Commit

Permalink
Merge pull request #127 from eosnetworkfoundation/elmato/implement-eo…
Browse files Browse the repository at this point in the history
…s-evm-gas-fee

Implemente eos-evm gas-fee (G_codedeposit and G_txnewaccount)
  • Loading branch information
elmato authored Mar 5, 2024
2 parents aaf7858 + 8de17ad commit f784e3d
Show file tree
Hide file tree
Showing 24 changed files with 185 additions and 61 deletions.
2 changes: 1 addition & 1 deletion cmd/dev/check_changes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ int main(int argc, char* argv[]) {

db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num};

ExecutionProcessor processor{block, *rule_set, buffer, *chain_config};
ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}};
processor.evm().analysis_cache = &analysis_cache;
processor.evm().state_pool = &state_pool;

Expand Down
2 changes: 1 addition & 1 deletion cmd/dev/scan_txs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ int main(int argc, char* argv[]) {

db::Buffer buffer{txn, /*prune_history_threshold=*/0, /*historical_block=*/block_num};

ExecutionProcessor processor{block, *rule_set, buffer, *chain_config};
ExecutionProcessor processor{block, *rule_set, buffer, *chain_config, {}};
processor.evm().analysis_cache = &analysis_cache;
processor.evm().state_pool = &state_pool;

Expand Down
2 changes: 1 addition & 1 deletion cmd/state-transition/state_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ void StateTransition::run() {
auto block = get_block(*state, config);
auto txn = get_transaction(expectedSubState);

ExecutionProcessor processor{block, *ruleSet, *state, config};
ExecutionProcessor processor{block, *ruleSet, *state, config, {}};
Receipt receipt;

const evmc_revision rev{config.revision(block.header)};
Expand Down
2 changes: 1 addition & 1 deletion cmd/test/ethereum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ RunResults blockchain_test(const nlohmann::json& json_test) {
InMemoryState state;
init_pre_state(json_test["pre"], state);

Blockchain blockchain{state, config, genesis_block};
Blockchain blockchain{state, config, genesis_block, {}};
blockchain.state_pool = &execution_state_pool;
blockchain.exo_evm = exo_evm;

Expand Down
17 changes: 12 additions & 5 deletions silkworm/core/chain/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,20 @@ evmc_revision ChainConfig::revision(const BlockHeader& header) const noexcept {
if(protocol_rule_set != protocol::RuleSetType::kTrust) {
return determine_revision_by_block(header.number, header.timestamp);
}
auto evm_version = eos_evm_version(header);
return eosevm::version_to_evmc_revision(evm_version);
}

uint64_t ChainConfig::eos_evm_version(const BlockHeader& header) const noexcept {
uint64_t evm_version = 0;
if(header.number == 0) {
evm_version = _version.has_value() ? *_version : 0;
} else {
evm_version = eosevm::nonce_to_version(header.nonce);
if(protocol_rule_set == protocol::RuleSetType::kTrust) {
if(header.number == 0) {
evm_version = _version.has_value() ? *_version : 0;
} else {
evm_version = eosevm::nonce_to_version(header.nonce);
}
}
return eosevm::version_to_evmc_revision(evm_version);
return evm_version;
}

std::vector<BlockNum> ChainConfig::distinct_fork_numbers() const {
Expand Down
1 change: 1 addition & 0 deletions silkworm/core/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ struct ChainConfig {
[[nodiscard]] evmc_revision determine_revision_by_block(uint64_t block_number, uint64_t block_time) const noexcept;

[[nodiscard]] evmc_revision revision(const BlockHeader& header) const noexcept;
[[nodiscard]] uint64_t eos_evm_version(const BlockHeader& header) const noexcept;

[[nodiscard]] std::vector<BlockNum> distinct_fork_numbers() const;
[[nodiscard]] std::vector<BlockTime> distinct_fork_times() const;
Expand Down
6 changes: 6 additions & 0 deletions silkworm/core/common/test_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ inline constexpr ChainConfig kShanghaiConfig{
._shanghai_time = 0,
};

inline constexpr ChainConfig kIstanbulTrustConfig{
.chain_id = 15555,
.protocol_rule_set = protocol::RuleSetType::kTrust,
._version = 1
};

static const std::map<std::string, ChainConfig> kNetworkConfig{
{"Frontier", test::kFrontierConfig},
{"Homestead",
Expand Down
44 changes: 35 additions & 9 deletions silkworm/core/execution/evm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ class DelegatingTracer : public evmone::Tracer {
IntraBlockState& intra_block_state_;
};

EVM::EVM(const Block& block, IntraBlockState& state, const ChainConfig& config) noexcept
EVM::EVM(const Block& block, IntraBlockState& state, const ChainConfig& config, const evmone::gas_parameters& gas_params) noexcept
: beneficiary{block.header.beneficiary},
block_{block},
state_{state},
config_{config},
evm1_{evmc_create_evmone()} {}
evm1_{evmc_create_evmone()},
gas_params_{gas_params},
eos_evm_version_{config.eos_evm_version(block.header)} { }

EVM::~EVM() { evm1_->destroy(evm1_); }

Expand All @@ -80,6 +82,7 @@ CallResult EVM::execute(const Transaction& txn, uint64_t gas) noexcept {

const evmc_message message{
.kind = contract_creation ? EVMC_CREATE : EVMC_CALL,
.depth = 0,
.gas = static_cast<int64_t>(gas),
.recipient = destination,
.sender = *txn.from,
Expand Down Expand Up @@ -157,7 +160,7 @@ evmc::Result EVM::create(const evmc_message& message) noexcept {

if (evm_res.status_code == EVMC_SUCCESS) {
const size_t code_len{evm_res.output_size};
const uint64_t code_deploy_gas{code_len * protocol::fee::kGCodeDeposit};
const uint64_t code_deploy_gas{code_len * (eos_evm_version_ > 0 ? gas_params_.G_codedeposit : protocol::fee::kGCodeDeposit)};

if (rev >= EVMC_SPURIOUS_DRAGON && code_len > protocol::kMaxCodeSize) {
// EIP-170: Contract code size limit
Expand Down Expand Up @@ -213,6 +216,11 @@ evmc::Result EVM::call(const evmc_message& message) noexcept {

const auto snapshot{state_.take_snapshot()};

const evmc_revision rev{revision()};
bool recipient_is_dead = !is_reserved_address(message.recipient) &&
!precompile::is_precompile(message.recipient, rev) &&
state_.is_dead(message.recipient);

if (message.kind == EVMC_CALL) {
if (message.flags & EVMC_STATIC) {
// Match geth logic
Expand All @@ -224,8 +232,6 @@ evmc::Result EVM::call(const evmc_message& message) noexcept {
}
}

const evmc_revision rev{revision()};

if (precompile::is_precompile(message.code_address, rev)) {
static_assert(std::size(precompile::kContracts) < 256);
const uint8_t num{message.code_address.bytes[kAddressLength - 1]};
Expand All @@ -251,13 +257,31 @@ evmc::Result EVM::call(const evmc_message& message) noexcept {
tracer.get().on_execution_end(res.raw(),state_);
}
} else {
const ByteView code{state_.get_code(message.code_address)};

evmc_message revisted_message{message};
if(eos_evm_version_ > 0 && revisted_message.depth == 0 && recipient_is_dead) {
if ((res.gas_left -= static_cast<int64_t>(gas_params_.G_txnewaccount)) < 0) {
// If we run out of gas lets do everything here
state_.revert_to_snapshot(snapshot);
res.status_code = EVMC_OUT_OF_GAS;
res.gas_refund = 0;
res.gas_left = 0;
for (auto tracer : tracers_) {
tracer.get().on_execution_start(rev, revisted_message, {});
tracer.get().on_execution_end(res.raw(), state_);
}
return res;
}
revisted_message.gas = res.gas_left;
}

const ByteView code{state_.get_code(revisted_message.code_address)};
if (code.empty() && tracers_.empty()) { // Do not skip execution if there are any tracers
return res;
}

const evmc::bytes32 code_hash{state_.get_code_hash(message.code_address)};
res = evmc::Result{execute(message, code, &code_hash)};
const evmc::bytes32 code_hash{state_.get_code_hash(revisted_message.code_address)};
res = evmc::Result{execute(revisted_message, code, &code_hash)};
}

if (res.status_code != EVMC_SUCCESS) {
Expand Down Expand Up @@ -320,7 +344,9 @@ evmc_result EVM::execute_with_baseline_interpreter(evmc_revision rev, const evmc

EvmHost host{*this};
gsl::owner<evmone::ExecutionState*> state{acquire_state()};
state->reset(msg, rev, host.get_interface(), host.to_context(), code);

state->reset(msg, rev, host.get_interface(), host.to_context(), code, gas_params_);
state->eos_evm_version = eos_evm_version_;

const auto vm{static_cast<evmone::VM*>(evm1_)};
evmc_result res{evmone::baseline::execute(*vm, msg.gas, *state, *analysis)};
Expand Down
9 changes: 8 additions & 1 deletion silkworm/core/execution/evm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class EVM {
EVM(const EVM&) = delete;
EVM& operator=(const EVM&) = delete;

EVM(const Block& block, IntraBlockState& state, const ChainConfig& config)
EVM(const Block& block, IntraBlockState& state, const ChainConfig& config, const evmone::gas_parameters& gas_params)
noexcept;

~EVM();
Expand Down Expand Up @@ -103,6 +103,10 @@ class EVM {
message_filter_ = message_filter;
}

void update_gas_params(const evmone::gas_parameters& gas_params) {
gas_params_ = gas_params;
}

private:
friend class EvmHost;

Expand All @@ -127,6 +131,9 @@ class EVM {

evmc_vm* evm1_{nullptr};
std::optional<FilterFunction> message_filter_;

evmone::gas_parameters gas_params_;
uint64_t eos_evm_version_=0;
};

class EvmHost : public evmc::Host {
Expand Down
89 changes: 77 additions & 12 deletions silkworm/core/execution/evm_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <silkworm/core/common/test_util.hpp>
#include <silkworm/core/common/util.hpp>
#include <silkworm/core/state/in_memory_state.hpp>
#include <eosevm/version.hpp>

#include "address.hpp"

Expand All @@ -40,7 +41,7 @@ TEST_CASE("Value transfer") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

CHECK(state.get_balance(from) == 0);
CHECK(state.get_balance(to) == 0);
Expand Down Expand Up @@ -96,7 +97,7 @@ TEST_CASE("Smart contract with storage") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, test::kShanghaiConfig};
EVM evm{block, state, test::kShanghaiConfig, {}};

Transaction txn{};
txn.from = caller;
Expand Down Expand Up @@ -168,7 +169,7 @@ TEST_CASE("Maximum call depth") {
IntraBlockState state{db};
state.set_code(contract, code);

EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

AnalysisCache analysis_cache{/*maxSize=*/16};
evm.analysis_cache = &analysis_cache;
Expand Down Expand Up @@ -227,7 +228,7 @@ TEST_CASE("DELEGATECALL") {
state.set_code(caller_address, caller_code);
state.set_code(callee_address, callee_code);

EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

Transaction txn{};
txn.from = caller_address;
Expand Down Expand Up @@ -288,7 +289,7 @@ TEST_CASE("CREATE should only return on failure") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

Transaction txn{};
txn.from = caller;
Expand Down Expand Up @@ -320,7 +321,7 @@ TEST_CASE("Contract overwrite") {
IntraBlockState state{db};
state.set_code(contract_address, old_code);

EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

Transaction txn{};
txn.from = caller;
Expand All @@ -343,7 +344,7 @@ TEST_CASE("EIP-3541: Reject new contracts starting with the 0xEF byte") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, config};
EVM evm{block, state, config, {}};

Transaction txn;
txn.from = 0x1000000000000000000000000000000000000000_address;
Expand Down Expand Up @@ -465,7 +466,7 @@ TEST_CASE("Tracing smart contract with storage") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

Transaction txn{};
txn.from = caller;
Expand Down Expand Up @@ -595,7 +596,7 @@ TEST_CASE("Tracing creation smart contract with CREATE2") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};

Transaction txn{};
txn.from = caller;
Expand All @@ -621,7 +622,7 @@ TEST_CASE("Tracing smart contract w/o code") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};
CHECK(evm.tracers().empty());

TestTracer tracer1;
Expand Down Expand Up @@ -682,7 +683,7 @@ TEST_CASE("Tracing precompiled contract failure") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, kMainnetConfig};
EVM evm{block, state, kMainnetConfig, {}};
CHECK(evm.tracers().empty());

TestTracer tracer1;
Expand Down Expand Up @@ -712,7 +713,7 @@ TEST_CASE("Smart contract creation w/ insufficient balance") {

InMemoryState db;
IntraBlockState state{db};
EVM evm{block, state, test::kShanghaiConfig};
EVM evm{block, state, test::kShanghaiConfig, {}};

Transaction txn{};
txn.from = caller;
Expand All @@ -724,4 +725,68 @@ TEST_CASE("Smart contract creation w/ insufficient balance") {
CHECK(res.status == EVMC_INSUFFICIENT_BALANCE);
}

TEST_CASE("EOS EVM codedeposit test") {
Block block{};
block.header.number = 1;
block.header.nonce = eosevm::version_to_nonce(1);

evmc::address caller{0x0a6bb546b9208cfab9e8fa2b9b2c042b18df7030_address};
Bytes code{*from_hex("608060405234801561001057600080fd5b50610173806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806313bdfacd14610030575b600080fd5b61003861004e565b604051610045919061011b565b60405180910390f35b60606040518060400160405280600c81526020017f48656c6c6f20576f726c64210000000000000000000000000000000000000000815250905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100c55780820151818401526020810190506100aa565b60008484015250505050565b6000601f19601f8301169050919050565b60006100ed8261008b565b6100f78185610096565b93506101078185602086016100a7565b610110816100d1565b840191505092915050565b6000602082019050818103600083015261013581846100e2565b90509291505056fea264697066735822122046344ce4c7e7c91dba98aef897cc7773af40fbff6b6da10885c36037b9d37a3764736f6c63430008110033")};

evmone::gas_parameters gas_params;
gas_params.G_codedeposit = 500;

InMemoryState db;
IntraBlockState state{db};
state.set_balance(caller, intx::uint256{1e18});
EVM evm{block, state, test::kIstanbulTrustConfig, gas_params};

Transaction txn{};
txn.from = caller;
txn.data = code;

uint64_t gas = 1'500'000;
CallResult res = evm.execute(txn, 1'500'000);
CHECK(res.status == EVMC_SUCCESS);
CHECK(gas-res.gas_left == 123 + 500*371); //G_codedeposit=500, codelen=371
}

TEST_CASE("EOS EVM G_txnewaccount") {
Block block{};
block.header.number = 1;
block.header.nonce = eosevm::version_to_nonce(1);

evmc::address sender{0x0a6bb546b9208cfab9e8fa2b9b2c042b18df7030_address};
evmc::address receiver{0x1a6bb546b9208cfab9e8fa2b9b2c042b18df7030_address};

evmone::gas_parameters gas_params;
gas_params.G_txnewaccount = 0;

InMemoryState db;
IntraBlockState state{db};
state.set_balance(sender, intx::uint256{1e18});
EVM evm{block, state, test::kIstanbulTrustConfig, gas_params};

Transaction txn{};
txn.from = sender;
txn.to = receiver;
txn.value = intx::uint256{0};

CallResult res = evm.execute(txn, 0);
CHECK(res.status == EVMC_SUCCESS);
CHECK(res.gas_left == 0);
CHECK(res.gas_refund == 0);

gas_params.G_txnewaccount = 1;
evm.update_gas_params(gas_params);

res = evm.execute(txn, 0);
CHECK(res.status == EVMC_OUT_OF_GAS);
CHECK(res.gas_refund == 0);
CHECK(res.gas_left == 0);

}



} // namespace silkworm
Loading

0 comments on commit f784e3d

Please sign in to comment.