From 79a8b5ef1f9f2a392cb6ae65b811842ac166bb6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 24 Sep 2023 15:05:02 +0200 Subject: [PATCH 1/4] test: JSON load "parentBeaconBlockRoot" --- test/blockchaintest/blockchaintest.hpp | 1 + test/blockchaintest/blockchaintest_loader.cpp | 2 ++ test/state/state.hpp | 1 + test/statetest/statetest_loader.cpp | 1 + 4 files changed, 5 insertions(+) diff --git a/test/blockchaintest/blockchaintest.hpp b/test/blockchaintest/blockchaintest.hpp index d139ec65b0..a4a290a92e 100644 --- a/test/blockchaintest/blockchaintest.hpp +++ b/test/blockchaintest/blockchaintest.hpp @@ -35,6 +35,7 @@ struct BlockHeader hash256 hash; hash256 transactions_root; hash256 withdrawal_root; + hash256 parent_beacon_block_root; }; struct TestBlock diff --git a/test/blockchaintest/blockchaintest_loader.cpp b/test/blockchaintest/blockchaintest_loader.cpp index 2cb9259777..4309fd23e3 100644 --- a/test/blockchaintest/blockchaintest_loader.cpp +++ b/test/blockchaintest/blockchaintest_loader.cpp @@ -40,6 +40,7 @@ BlockHeader from_json(const json::json& j) .hash = from_json(j.at("hash")), .transactions_root = from_json(j.at("transactionsTrie")), .withdrawal_root = load_if_exists(j, "withdrawalsRoot"), + .parent_beacon_block_root = load_if_exists(j, "parentBeaconBlockRoot"), }; } @@ -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) diff --git a/test/state/state.hpp b/test/state/state.hpp index 5502525fb6..ee15707205 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -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 ommers; std::vector withdrawals; diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 2451b8ff1c..67285dc277 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -231,6 +231,7 @@ state::BlockInfo from_json(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), From d623af96bab749259781a3ff9166fad50a650db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 24 Sep 2023 16:25:32 +0200 Subject: [PATCH 2/4] state: Implement system call (EIP-4788) Implement the system call from the "Block processing" of the EIP-4788. https://eips.ethereum.org/EIPS/eip-4788#block-processing At the beginning of each block a gas-free call is invoked from the _system_ account (0xff..fe) to the specific _system_ contract. --- test/state/host.cpp | 3 ++- test/state/state.cpp | 35 +++++++++++++++++++++++++++++++++++ test/state/state.hpp | 6 ++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/test/state/host.cpp b/test/state/host.cpp index 6e29eaa8d0..d09988a0c4 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -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; diff --git a/test/state/state.cpp b/test/state/state.cpp index 2b74a76b4f..65e254aabb 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -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) +{ + 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 block_reward, std::span ommers, std::span withdrawals) diff --git a/test/state/state.hpp b/test/state/state.hpp index ee15707205..8d35f3174f 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -191,6 +191,12 @@ std::variant 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); From bf3de2ff01ab0fd593ebbb4aac4804679259e27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 24 Nov 2023 17:51:41 +0100 Subject: [PATCH 3/4] test: Add unit tests for system call execution --- test/unittests/CMakeLists.txt | 1 + test/unittests/state_system_call_test.cpp | 40 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/unittests/state_system_call_test.cpp diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 4e74c518bd..2884002358 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -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 diff --git a/test/unittests/state_system_call_test.cpp b/test/unittests/state_system_call_test.cpp new file mode 100644 index 0000000000..51b8dd1c81 --- /dev/null +++ b/test/unittests/state_system_call_test.cpp @@ -0,0 +1,40 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +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); +} From 87e8d9b83898d21f33cc1484c110a8a00a37d6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 24 Sep 2023 16:26:02 +0200 Subject: [PATCH 4/4] blockchaintest: Execute system call --- test/blockchaintest/blockchaintest_runner.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/blockchaintest/blockchaintest_runner.cpp b/test/blockchaintest/blockchaintest_runner.cpp index 257ab987ab..1dfad75737 100644 --- a/test/blockchaintest/blockchaintest_runner.cpp +++ b/test/blockchaintest/blockchaintest_runner.cpp @@ -33,6 +33,8 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo const std::vector& txs, evmc_revision rev, std::optional block_reward) { + state::system_call(state, block, rev, vm); + std::vector txs_logs; int64_t block_gas_left = block.gas_limit;