From c885db3989a397cfeea549c78d59538bdd81ba88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 24 Sep 2024 13:59:13 +0200 Subject: [PATCH] test: Wrap usage of state transition API (#1013) Wrap the usage of the state transition API from the `evmone::state` for tests so that the new API in `evmone::test` only exposes `TestState` and hides `state::State`. This isolates usage of the `evmone::state` to lower the disturbance caused by API modifications, e.g. in https://github.com/ethereum/evmone/pull/802. --- test/blockchaintest/blockchaintest_runner.cpp | 22 +++++-------- test/state/test_state.cpp | 27 +++++++++++++++ test/state/test_state.hpp | 22 ++++++++++++- test/statetest/statetest_runner.cpp | 8 ++--- test/t8n/t8n.cpp | 18 +++++----- test/unittests/state_system_call_test.cpp | 33 ++++++++++--------- test/unittests/state_transition.cpp | 9 +++-- 7 files changed, 90 insertions(+), 49 deletions(-) diff --git a/test/blockchaintest/blockchaintest_runner.cpp b/test/blockchaintest/blockchaintest_runner.cpp index b80f83e3b4..b0f6f4aaaa 100644 --- a/test/blockchaintest/blockchaintest_runner.cpp +++ b/test/blockchaintest/blockchaintest_runner.cpp @@ -4,8 +4,6 @@ #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 @@ -30,11 +28,11 @@ struct TransitionResult namespace { -TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::BlockInfo& block, +TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockInfo& block, const std::vector& txs, evmc_revision rev, std::optional block_reward) { - state::system_call(state, block, rev, vm); + system_call(state, block, rev, vm); std::vector txs_logs; int64_t block_gas_left = block.gas_limit; @@ -50,7 +48,7 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo const auto& tx = txs[i]; const auto computed_tx_hash = keccak256(rlp::encode(tx)); - auto res = state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + auto res = test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); if (holds_alternative(res)) { @@ -67,7 +65,7 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo cumulative_gas_used += receipt.gas_used; receipt.cumulative_gas_used = cumulative_gas_used; if (rev < EVMC_BYZANTIUM) - receipt.post_state = state::mpt_hash(TestState{state}); + receipt.post_state = state::mpt_hash(state); block_gas_left -= receipt.gas_used; blob_gas_left -= tx.blob_gas_used(); @@ -75,7 +73,7 @@ TransitionResult apply_block(state::State& state, evmc::VM& vm, const state::Blo } } - state::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); + test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); const auto bloom = compute_bloom_filter(receipts); return {std::move(receipts), std::move(rejected_txs), cumulative_gas_used, bloom}; @@ -137,7 +135,7 @@ void run_blockchain_tests(std::span tests, evmc::VM& vm) bytes32{}); EXPECT_EQ(c.genesis_block_header.logs_bloom, bytes_view{state::BloomFilter{}}); - auto state = c.pre_state.to_intra_state(); + auto state = c.pre_state; std::unordered_map known_block_hashes{ {c.genesis_block_header.block_number, c.genesis_block_header.hash}}; @@ -158,8 +156,7 @@ void run_blockchain_tests(std::span tests, evmc::VM& vm) SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index) + '/' + c.name + '/' + std::to_string(test_block.block_info.number)); - EXPECT_EQ( - state::mpt_hash(TestState{state}), test_block.expected_block_header.state_root); + EXPECT_EQ(state::mpt_hash(state), test_block.expected_block_header.state_root); if (rev >= EVMC_SHANGHAI) { @@ -178,14 +175,13 @@ void run_blockchain_tests(std::span tests, evmc::VM& vm) // TODO: Add difficulty calculation verification. } - const TestState post{state}; const auto expected_post_hash = std::holds_alternative(c.expectation.post_state) ? state::mpt_hash(std::get(c.expectation.post_state)) : std::get(c.expectation.post_state); - EXPECT_TRUE(state::mpt_hash(post) == expected_post_hash) + EXPECT_TRUE(state::mpt_hash(state) == expected_post_hash) << "Result state:\n" - << print_state(post) + << print_state(state) << (std::holds_alternative(c.expectation.post_state) ? "\n\nExpected state:\n" + print_state(std::get(c.expectation.post_state)) : diff --git a/test/state/test_state.cpp b/test/state/test_state.cpp index c0073d05cb..50bac31d0e 100644 --- a/test/state/test_state.cpp +++ b/test/state/test_state.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "test_state.hpp" #include "state.hpp" +#include "system_contracts.hpp" namespace evmone::test { @@ -31,4 +32,30 @@ state::State TestState::to_intra_state() const } return intra_state; } + +[[nodiscard]] std::variant transition(TestState& state, + const state::BlockInfo& block, const state::Transaction& tx, evmc_revision rev, evmc::VM& vm, + int64_t block_gas_left, int64_t blob_gas_left) +{ + auto intra_state = state.to_intra_state(); + auto res = state::transition(intra_state, block, tx, rev, vm, block_gas_left, blob_gas_left); + state = TestState{intra_state}; + return res; +} + +void finalize(TestState& state, evmc_revision rev, const address& coinbase, + std::optional block_reward, std::span ommers, + std::span withdrawals) +{ + auto intra_state = state.to_intra_state(); + state::finalize(intra_state, rev, coinbase, block_reward, ommers, withdrawals); + state = TestState{intra_state}; +} + +void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm) +{ + auto intra_state = state.to_intra_state(); + state::system_call(intra_state, block, rev, vm); + state = TestState{intra_state}; +} } // namespace evmone::test diff --git a/test/state/test_state.hpp b/test/state/test_state.hpp index f937712418..3bac733c57 100644 --- a/test/state/test_state.hpp +++ b/test/state/test_state.hpp @@ -6,13 +6,20 @@ #include #include #include +#include +#include namespace evmone { namespace state { +struct BlockInfo; +struct Ommer; +struct Transaction; +struct TransactionReceipt; +struct Withdrawal; class State; -} +} // namespace state namespace test { @@ -63,5 +70,18 @@ class TestState : public std::map [[nodiscard]] state::State to_intra_state() const; }; +/// Wrapping of state::transition() which operates on TestState. +[[nodiscard]] std::variant transition(TestState& state, + const state::BlockInfo& block, const state::Transaction& tx, evmc_revision rev, evmc::VM& vm, + int64_t block_gas_left, int64_t blob_gas_left); + +/// Wrapping of state::finalize() which operates on TestState. +void finalize(TestState& state, evmc_revision rev, const address& coinbase, + std::optional block_reward, std::span ommers, + std::span withdrawals); + +/// Wrapping of state::system_call() which operates on TestState. +void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm); + } // namespace test } // namespace evmone diff --git a/test/statetest/statetest_runner.cpp b/test/statetest/statetest_runner.cpp index daac89f7c7..75ac2b509c 100644 --- a/test/statetest/statetest_runner.cpp +++ b/test/statetest/statetest_runner.cpp @@ -25,15 +25,15 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su const auto& expected = cases[case_index]; const auto tx = test.multi_tx.get(expected.indexes); - auto state = test.pre_state.to_intra_state(); + auto state = test.pre_state; - const auto res = state::transition(state, test.block, tx, rev, vm, test.block.gas_limit, + const auto res = test::transition(state, test.block, tx, rev, vm, test.block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); // Finalize block with reward 0. - state::finalize(state, rev, test.block.coinbase, 0, {}, {}); + test::finalize(state, rev, test.block.coinbase, 0, {}, {}); - const auto state_root = state::mpt_hash(TestState{state}); + const auto state_root = state::mpt_hash(state); if (trace_summary) { diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 6aca033129..912f61575f 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -6,7 +6,6 @@ #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 @@ -76,14 +75,13 @@ int main(int argc, const char* argv[]) } state::BlockInfo block; - state::State state; + TestState state; if (!alloc_file.empty()) { const auto j = json::json::parse(std::ifstream{alloc_file}, nullptr, false); - const auto test_state = test::from_json(j); - validate_state(test_state, rev); - state = test_state.to_intra_state(); + state = from_json(j); + validate_state(state, rev); } if (!env_file.empty()) { @@ -135,7 +133,7 @@ int main(int argc, const char* argv[]) j_result["receipts"] = json::json::array(); j_result["rejected"] = json::json::array(); - state::system_call(state, block, rev, vm); + test::system_call(state, block, rev, vm); for (size_t i = 0; i < j_txs.size(); ++i) { @@ -171,7 +169,7 @@ int main(int argc, const char* argv[]) } auto res = - state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); if (holds_alternative(res)) { @@ -196,7 +194,7 @@ int main(int argc, const char* argv[]) cumulative_gas_used += receipt.gas_used; receipt.cumulative_gas_used = cumulative_gas_used; if (rev < EVMC_BYZANTIUM) - receipt.post_state = state::mpt_hash(TestState{state}); + receipt.post_state = state::mpt_hash(state); j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used); j_receipt["blockHash"] = hex0x(bytes32{}); @@ -218,11 +216,11 @@ int main(int argc, const char* argv[]) } } - state::finalize( + test::finalize( state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); j_result["logsHash"] = hex0x(logs_hash(txs_logs)); - j_result["stateRoot"] = hex0x(state::mpt_hash(TestState{state})); + j_result["stateRoot"] = hex0x(state::mpt_hash(state)); } j_result["logsBloom"] = hex0x(compute_bloom_filter(receipts)); diff --git a/test/unittests/state_system_call_test.cpp b/test/unittests/state_system_call_test.cpp index 209234a06a..63683f5c86 100644 --- a/test/unittests/state_system_call_test.cpp +++ b/test/unittests/state_system_call_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include using namespace evmc::literals; @@ -16,7 +17,7 @@ class state_system_call : public testing::Test { protected: evmc::VM vm{evmc_create_evmone()}; - State state; + TestState state; }; TEST_F(state_system_call, non_existient) @@ -24,7 +25,7 @@ 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) << "State must remain unchanged"; + EXPECT_EQ(state.size(), 0) << "State must remain unchanged"; } TEST_F(state_system_call, beacon_roots) @@ -35,14 +36,14 @@ TEST_F(state_system_call, beacon_roots) system_call(state, block, EVMC_CANCUN, vm); - ASSERT_EQ(state.get_accounts().size(), 1); - 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(state.size(), 1); + EXPECT_FALSE(state.contains(SYSTEM_ADDRESS)); + EXPECT_EQ(state.at(BEACON_ROOTS_ADDRESS).nonce, 0); + EXPECT_EQ(state.at(BEACON_ROOTS_ADDRESS).balance, 0); + const auto& storage = state.at(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)); + EXPECT_EQ(storage.at(0x01_bytes32), block.parent_beacon_block_root); + EXPECT_EQ(storage.at(0x00_bytes32), to_bytes32(SYSTEM_ADDRESS)); } TEST_F(state_system_call, history_storage) @@ -55,12 +56,12 @@ TEST_F(state_system_call, history_storage) system_call(state, block, EVMC_PRAGUE, vm); - ASSERT_EQ(state.get_accounts().size(), 1); - EXPECT_EQ(state.find(SYSTEM_ADDRESS), nullptr); - EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).nonce, 0); - EXPECT_EQ(state.get(HISTORY_STORAGE_ADDRESS).balance, 0); - const auto& storage = state.get(HISTORY_STORAGE_ADDRESS).storage; + ASSERT_EQ(state.size(), 1); + EXPECT_FALSE(state.contains(SYSTEM_ADDRESS)); + EXPECT_EQ(state.at(HISTORY_STORAGE_ADDRESS).nonce, 0); + EXPECT_EQ(state.at(HISTORY_STORAGE_ADDRESS).balance, 0); + const auto& storage = state.at(HISTORY_STORAGE_ADDRESS).storage; ASSERT_EQ(storage.size(), 2); - EXPECT_EQ(storage.at(bytes32{NUMBER}).current, PREV_BLOCKHASH); - EXPECT_EQ(storage.at(0x00_bytes32).current, to_bytes32(SYSTEM_ADDRESS)); + EXPECT_EQ(storage.at(bytes32{NUMBER}), PREV_BLOCKHASH); + EXPECT_EQ(storage.at(0x00_bytes32), to_bytes32(SYSTEM_ADDRESS)); } diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index 804daa6d05..573e4f8fd6 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -48,6 +48,7 @@ void state_transition::TearDown() // Execution: + auto state = pre; const auto trace = !expect.trace.empty(); auto& selected_vm = trace ? tracing_vm : vm; @@ -56,12 +57,10 @@ void state_transition::TearDown() if (trace) trace_capture.emplace(); - auto intra_state = pre.to_intra_state(); - const auto res = state::transition(intra_state, block, tx, rev, selected_vm, block.gas_limit, + const auto res = test::transition(state, block, tx, rev, selected_vm, block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); - state::finalize( - intra_state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); - TestState post{intra_state}; + test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); + const auto& post = state; if (const auto expected_error = make_error_code(expect.tx_error)) {