Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemente eos-evm gas-fee (G_codedeposit and G_txnewaccount) #127

Merged
merged 9 commits into from
Mar 5, 2024
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be better to simply panic if anywhere calling this function while not using kTrust?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wanted to preserve the old behavior in order to pass the original tests

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
29 changes: 25 additions & 4 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 @@ -251,6 +254,22 @@ evmc::Result EVM::call(const evmc_message& message) noexcept {
tracer.get().on_execution_end(res.raw(),state_);
}
} else {

if(eos_evm_version_ > 0 && message.depth == 0 && state_.is_dead(message.recipient)) {
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, message, {});
tracer.get().on_execution_end(res.raw(), state_);
}
return res;
}
}

const ByteView code{state_.get_code(message.code_address)};
if (code.empty() && tracers_.empty()) { // Do not skip execution if there are any tracers
return res;
Expand Down Expand Up @@ -320,7 +339,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
4 changes: 2 additions & 2 deletions silkworm/core/execution/execution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ namespace silkworm {
* @param state The Ethereum state at the beginning of the block.
*/
[[nodiscard]] inline ValidationResult execute_block(const Block& block, State& state,
const ChainConfig& chain_config) noexcept {
const ChainConfig& chain_config, const evmone::gas_parameters& gas_params) noexcept {
auto rule_set{protocol::rule_set_factory(chain_config)};
if (!rule_set) {
return ValidationResult::kUnknownProtocolRuleSet;
}
ExecutionProcessor processor{block, *rule_set, state, chain_config};
ExecutionProcessor processor{block, *rule_set, state, chain_config, gas_params};
std::vector<Receipt> receipts;
return processor.execute_and_write_block(receipts);
}
Expand Down
4 changes: 2 additions & 2 deletions silkworm/core/execution/execution_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ TEST_CASE("Execute two blocks") {
// Execute first block
// ---------------------------------------

REQUIRE(execute_block(block, state, test::kLondonConfig) == ValidationResult::kOk);
REQUIRE(execute_block(block, state, test::kLondonConfig, {}) == ValidationResult::kOk);

auto contract_address{create_address(sender, /*nonce=*/0)};
std::optional<Account> contract_account{state.read_account(contract_address)};
Expand Down Expand Up @@ -114,7 +114,7 @@ TEST_CASE("Execute two blocks") {
block.transactions[0].data = *from_hex(new_val);
block.transactions[0].max_priority_fee_per_gas = 20 * kGiga;

REQUIRE(execute_block(block, state, test::kLondonConfig) == ValidationResult::kOk);
REQUIRE(execute_block(block, state, test::kLondonConfig, {}) == ValidationResult::kOk);

storage0 = state.read_storage(contract_address, kDefaultIncarnation, storage_key0);
CHECK(to_hex(storage0) == new_val);
Expand Down
Loading
Loading