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

Refactor system_call() #976

Merged
merged 2 commits into from
Sep 5, 2024
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_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "../state/mpt_hash.hpp"
#include "../state/rlp.hpp"
#include "../state/state.hpp"
#include "../state/system_contracts.hpp"
#include "../test/statetest/statetest.hpp"
#include "blockchaintest.hpp"
#include <gtest/gtest.h>
Expand Down
2 changes: 2 additions & 0 deletions test/state/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ target_sources(
rlp.hpp
state.hpp
state.cpp
system_contracts.hpp
system_contracts.cpp
test_state.hpp
test_state.cpp
)
Expand Down
34 changes: 0 additions & 34 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,40 +379,6 @@ void delete_empty_accounts(State& state)
}
} // namespace

void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm)
{
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,
Expand Down
6 changes: 0 additions & 6 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,6 @@ 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,
int64_t blob_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
74 changes: 74 additions & 0 deletions test/state/system_contracts.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Copyright 2023 The evmone Authors.
// Copyright 2024 The evmone Authors.

Copy link
Member Author

Choose a reason for hiding this comment

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

2023 is the year then this code was introduced.

// SPDX-License-Identifier: Apache-2.0

#include "system_contracts.hpp"
#include "host.hpp"
#include "state.hpp"

namespace evmone::state
{
namespace
{
/// Information about a registered system contract.
struct SystemContract
{
using GetInputFn = bytes_view(const BlockInfo&) noexcept;

evmc_revision since = EVMC_MAX_REVISION; ///< EVM revision in which added.
address addr; ///< Address of the system contract.
GetInputFn* get_input = nullptr; ///< How to get the input for the system call.
};

/// Registered system contracts.
constexpr std::array SYSTEM_CONTRACTS{
SystemContract{EVMC_CANCUN, BEACON_ROOTS_ADDRESS,
[](const BlockInfo& block) noexcept { return bytes_view{block.parent_beacon_block_root}; }},
};

static_assert(std::ranges::is_sorted(SYSTEM_CONTRACTS,
[](const auto& a, const auto& b) noexcept { return a.since < b.since; }),
"system contract entries must be ordered by revision");

} // namespace

void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm)
{
for (const auto& [since, addr, get_input] : SYSTEM_CONTRACTS)
{
if (rev < since)
return; // Because entries are ordered, there are no other contracts for this revision.

// Skip the call if the target account doesn't exist. This is by EIP-4788 spec.
// > if no code exists at [address], the call must fail silently.
const auto acc = state.find(addr);
if (acc == nullptr)
continue;

const auto input = get_input(block);

const evmc_message msg{
.kind = EVMC_CALL,
.gas = 30'000'000,
.recipient = addr,
.sender = SYSTEM_ADDRESS,
.input_data = input.data(),
.input_size = input.size(),
};

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;
}
}
}
} // namespace evmone::state
26 changes: 26 additions & 0 deletions test/state/system_contracts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <evmc/evmc.hpp>

namespace evmone::state
{
using namespace evmc::literals;

/// The address of the sender of the system calls (EIP-4788).
constexpr auto SYSTEM_ADDRESS = 0xfffffffffffffffffffffffffffffffffffffffe_address;

/// The address of the system contract storing the root hashes of beacon chain blocks (EIP-4788).
constexpr auto BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address;

struct BlockInfo;
class State;

/// Performs the system call: invokes system contracts.
///
/// Executes code of pre-defined accounts via pseudo-transaction 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);
} // namespace evmone::state
1 change: 1 addition & 0 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../state/ethash_difficulty.hpp"
#include "../state/mpt_hash.hpp"
#include "../state/rlp.hpp"
#include "../state/system_contracts.hpp"
#include "../statetest/statetest.hpp"
#include "../utils/utils.hpp"
#include <evmone/evmone.h>
Expand Down
37 changes: 21 additions & 16 deletions test/unittests/state_system_call_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,42 @@
#include <evmone/evmone.h>
#include <gtest/gtest.h>
#include <test/state/state.hpp>
#include <test/state/system_contracts.hpp>
#include <test/utils/bytecode.hpp>

using namespace evmc::literals;
using namespace evmone::state;
using namespace evmone::test;

TEST(state_system_call, non_existient)
class state_system_call : public testing::Test
{
evmc::VM vm;
protected:
evmc::VM vm{evmc_create_evmone()};
State state;
};

system_call(state, {}, EVMC_CANCUN, vm);
TEST_F(state_system_call, non_existient)
{
// Use MAX revision to invoke all activate system contracts.
system_call(state, {}, EVMC_MAX_REVISION, vm);

EXPECT_EQ(state.get_accounts().size(), 0);
EXPECT_EQ(state.get_accounts().size(), 0) << "State must remain unchanged";
}

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

evmc::VM vm{evmc_create_evmone()};
const BlockInfo block{.number = 1, .timestamp = 0x0404};
State state;
state.insert(BeaconRootsAddress, {.code = sstore(OP_NUMBER, OP_TIMESTAMP)});
const BlockInfo block{.number = 1, .parent_beacon_block_root = 0xbeac04004a54_bytes32};
state.insert(
BEACON_ROOTS_ADDRESS, {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)});

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, 0x0404_bytes32);
EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr);
EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).nonce, 0);
EXPECT_EQ(state.get(BEACON_ROOTS_ADDRESS).balance, 0);
const auto& storage = state.get(BEACON_ROOTS_ADDRESS).storage;
ASSERT_EQ(storage.size(), 2);
EXPECT_EQ(storage.at(0x01_bytes32).current, block.parent_beacon_block_root);
EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS));
}