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

Implement EIP-4788: Beacon block root in the EVM #709

Merged
merged 4 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions test/blockchaintest/blockchaintest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct BlockHeader
hash256 hash;
hash256 transactions_root;
hash256 withdrawal_root;
hash256 parent_beacon_block_root;
};

struct TestBlock
Expand Down
2 changes: 2 additions & 0 deletions test/blockchaintest/blockchaintest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ BlockHeader from_json<BlockHeader>(const json::json& j)
.hash = from_json<hash256>(j.at("hash")),
.transactions_root = from_json<hash256>(j.at("transactionsTrie")),
.withdrawal_root = load_if_exists<hash256>(j, "withdrawalsRoot"),
.parent_beacon_block_root = load_if_exists<hash256>(j, "parentBeaconBlockRoot"),
};
}

Expand All @@ -58,6 +59,7 @@ static TestBlock load_test_block(const json::json& j, evmc_revision rev)
tb.block_info.difficulty = tb.expected_block_header.difficulty;
tb.block_info.prev_randao = tb.expected_block_header.prev_randao;
tb.block_info.base_fee = tb.expected_block_header.base_fee_per_gas;
tb.block_info.parent_beacon_block_root = tb.expected_block_header.parent_beacon_block_root;

// Override prev_randao with difficulty pre-Merge
if (rev < EVMC_PARIS)
Expand Down
2 changes: 2 additions & 0 deletions test/blockchaintest/blockchaintest_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo
const std::vector<state::Transaction>& txs, evmc_revision rev,
std::optional<int64_t> block_reward)
{
state::system_call(state, block, rev, vm);

std::vector<state::Log> txs_logs;
int64_t block_gas_left = block.gas_limit;

Expand Down
3 changes: 2 additions & 1 deletion test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ evmc::Result Host::call(const evmc_message& orig_msg) noexcept
evmc_tx_context Host::get_tx_context() const noexcept
{
// TODO: The effective gas price is already computed in transaction validation.
assert(m_tx.max_gas_price >= m_block.base_fee);
// TODO: The effective gas price calculation is broken for system calls (gas price 0).
assert(m_tx.max_gas_price >= m_block.base_fee || m_tx.max_gas_price == 0);
const auto priority_gas_price =
std::min(m_tx.max_priority_gas_price, m_tx.max_gas_price - m_block.base_fee);
const auto effective_gas_price = m_block.base_fee + priority_gas_price;
Expand Down
35 changes: 35 additions & 0 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,41 @@ void delete_empty_accounts(State& state)
}
} // namespace

void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm)
chfast marked this conversation as resolved.
Show resolved Hide resolved
{
static constexpr auto SystemAddress = 0xfffffffffffffffffffffffffffffffffffffffe_address;
static constexpr auto BeaconRootsAddress = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address;

if (rev >= EVMC_CANCUN)
{
if (const auto acc = state.find(BeaconRootsAddress); acc != nullptr)
{
const evmc_message msg{
.kind = EVMC_CALL,
.gas = 30'000'000,
.recipient = BeaconRootsAddress,
.sender = SystemAddress,
.input_data = block.parent_beacon_block_root.bytes,
.input_size = sizeof(block.parent_beacon_block_root),
};

const Transaction empty_tx{};
Host host{rev, vm, state, block, empty_tx};
const auto& code = acc->code;
[[maybe_unused]] const auto res = vm.execute(host, rev, msg, code.data(), code.size());
assert(res.status_code == EVMC_SUCCESS);
assert(acc->access_status == EVMC_ACCESS_COLD);

// Reset storage status.
for (auto& [_, val] : acc->storage)
{
val.access_status = EVMC_ACCESS_COLD;
val.original = val.current;
}
}
}
}

void finalize(State& state, evmc_revision rev, const address& coinbase,
std::optional<uint64_t> block_reward, std::span<const Ommer> ommers,
std::span<const Withdrawal> withdrawals)
Expand Down
7 changes: 7 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ struct BlockInfo
int64_t parent_difficulty = 0;
hash256 parent_ommers_hash;
bytes32 prev_randao;
hash256 parent_beacon_block_root;
uint64_t base_fee = 0;
std::vector<Ommer> ommers;
std::vector<Withdrawal> withdrawals;
Expand Down Expand Up @@ -190,6 +191,12 @@ std::variant<int64_t, std::error_code> validate_transaction(const Account& sende
const BlockInfo& block, const Transaction& tx, evmc_revision rev,
int64_t block_gas_left) noexcept;

/// Performs the system call.
///
/// Executes code at pre-defined accounts from the system sender (0xff...fe).
/// The sender's nonce is not increased.
void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm);

/// Defines how to RLP-encode a Transaction.
[[nodiscard]] bytes rlp_encode(const Transaction& tx);

Expand Down
1 change: 1 addition & 0 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
.parent_difficulty = parent_difficulty,
.parent_ommers_hash = parent_uncle_hash,
.prev_randao = prev_randao,
.parent_beacon_block_root = {},
.base_fee = base_fee,
.ommers = std::move(ommers),
.withdrawals = std::move(withdrawals),
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ target_sources(
state_new_account_address_test.cpp
state_precompiles_test.cpp
state_rlp_test.cpp
state_system_call_test.cpp
state_transition.hpp
state_transition.cpp
state_transition_block_test.cpp
Expand Down
40 changes: 40 additions & 0 deletions test/unittests/state_system_call_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <evmone/evmone.h>
#include <gtest/gtest.h>
#include <test/state/state.hpp>
#include <test/utils/bytecode.hpp>

using namespace evmc;
using namespace evmone::state;

TEST(state_system_call, non_existient)
{
evmc::VM vm;
State state;

system_call(state, {}, EVMC_CANCUN, vm);

EXPECT_EQ(state.get_accounts().size(), 0);
}

TEST(state_system_call, sstore_timestamp)
{
static constexpr auto BeaconRootsAddress = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address;

evmc::VM vm{evmc_create_evmone()};
const BlockInfo block{.number = 1, .timestamp = 404};
State state;
state.insert(BeaconRootsAddress, {.code = sstore(OP_NUMBER, OP_TIMESTAMP)});

system_call(state, block, EVMC_CANCUN, vm);

ASSERT_EQ(state.get_accounts().size(), 1);
EXPECT_EQ(state.get(BeaconRootsAddress).nonce, 0);
EXPECT_EQ(state.get(BeaconRootsAddress).balance, 0);
const auto& storage = state.get(BeaconRootsAddress).storage;
ASSERT_EQ(storage.size(), 1);
EXPECT_EQ(storage.at(0x01_bytes32).current, 404);
}