From fca112824327c153c8cda27b7a2f0b5a9015df8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 14 Mar 2025 12:36:40 +0100 Subject: [PATCH] test: Move state test export procedure to testutils --- test/statetest/statetest.hpp | 5 ++ test/statetest/statetest_export.cpp | 127 ++++++++++++++++++++++++++++ test/unittests/state_transition.cpp | 115 +------------------------ 3 files changed, 134 insertions(+), 113 deletions(-) diff --git a/test/statetest/statetest.hpp b/test/statetest/statetest.hpp index 783e7de320..81ab47b919 100644 --- a/test/statetest/statetest.hpp +++ b/test/statetest/statetest.hpp @@ -100,6 +100,11 @@ state::Transaction from_json(const json::json& j); /// Exports the State (accounts) to JSON format (aka pre/post/alloc state). json::json to_json(const TestState& state); +/// Export the state test to JSON format. +json::json to_state_test(std::string_view test_name, const state::BlockInfo& block, + state::Transaction& tx, const TestState& pre, evmc_revision rev, + const std::variant& res, const TestState& post); + /// Returns the standardized error message for the transaction validation error. [[nodiscard]] std::string get_invalid_tx_message(state::ErrorCode errc) noexcept; diff --git a/test/statetest/statetest_export.cpp b/test/statetest/statetest_export.cpp index 0be56b2a02..2424f4382f 100644 --- a/test/statetest/statetest_export.cpp +++ b/test/statetest/statetest_export.cpp @@ -3,9 +3,27 @@ // SPDX-License-Identifier: Apache-2.0 #include "statetest.hpp" +#include namespace evmone::test { +namespace +{ +/// Converts EVM revision to the fork name commonly used in tests. +std::string_view to_test_fork_name(evmc_revision rev) noexcept +{ + switch (rev) + { + case EVMC_TANGERINE_WHISTLE: + return "EIP150"; + case EVMC_SPURIOUS_DRAGON: + return "EIP158"; + default: + return evmc::to_string(rev); + } +} +} // namespace + [[nodiscard]] std::string get_invalid_tx_message(state::ErrorCode errc) noexcept { using namespace state; @@ -71,4 +89,113 @@ json::json to_json(const TestState& state) } return j; } + +json::json to_state_test(std::string_view test_name, const state::BlockInfo& block, + state::Transaction& tx, const TestState& pre, evmc_revision rev, + const std::variant& res, const TestState& post) +{ + using state::Transaction; + + // FIXME: Move to common place. + static constexpr auto SenderSecretKey = + 0x00000000000000000000000000000000000000000000000000000002b1263d2b_bytes32; + + json::json j; + auto& jt = j[test_name]; + + auto& jenv = jt["env"]; + jenv["currentNumber"] = hex0x(block.number); + jenv["currentTimestamp"] = hex0x(block.timestamp); + jenv["currentGasLimit"] = hex0x(block.gas_limit); + jenv["currentCoinbase"] = hex0x(block.coinbase); + jenv["currentBaseFee"] = hex0x(block.base_fee); + jenv["currentRandom"] = hex0x(block.prev_randao); + + jt["pre"] = to_json(pre); + + auto& jtx = jt["transaction"]; + if (tx.to.has_value()) + jtx["to"] = hex0x(*tx.to); + jtx["sender"] = hex0x(tx.sender); + jtx["secretKey"] = hex0x(SenderSecretKey); + jtx["nonce"] = hex0x(tx.nonce); + if (tx.type >= Transaction::Type::eip1559) + { + jtx["maxFeePerGas"] = hex0x(tx.max_gas_price); + jtx["maxPriorityFeePerGas"] = hex0x(tx.max_priority_gas_price); + } + else + { + assert(tx.max_gas_price == tx.max_priority_gas_price); + jtx["gasPrice"] = hex0x(tx.max_gas_price); + } + + jtx["data"][0] = hex0x(tx.data); + jtx["gasLimit"][0] = hex0x(tx.gas_limit); + jtx["value"][0] = hex0x(tx.value); + + // Force `accessLists` output even if empty. + if (tx.type >= Transaction::Type::access_list) + jtx["accessLists"][0] = json::json::array(); + + if (!tx.access_list.empty()) + { + auto& ja = jtx["accessLists"][0]; + for (const auto& [addr, storage_keys] : tx.access_list) + { + json::json je; + je["address"] = hex0x(addr); + auto& jstorage_keys = je["storageKeys"] = json::json::array(); + for (const auto& k : storage_keys) + jstorage_keys.emplace_back(hex0x(k)); + ja.emplace_back(std::move(je)); + } + } + + if (tx.type == Transaction::Type::blob) + { + jtx["maxFeePerBlobGas"] = hex0x(tx.max_blob_gas_price); + jtx["blobVersionedHashes"] = json::json::array(); + for (const auto& blob_hash : tx.blob_hashes) + { + jtx["blobVersionedHashes"].emplace_back(hex0x(blob_hash)); + } + } + + if (!tx.authorization_list.empty()) + { + auto& ja = jtx["authorizationList"]; + for (const auto& [chain_id, addr, nonce, signer, r, s, y_parity] : tx.authorization_list) + { + json::json je; + je["chainId"] = hex0x(chain_id); + je["address"] = hex0x(addr); + je["nonce"] = hex0x(nonce); + je["v"] = hex0x(y_parity); + je["r"] = hex0x(r); + je["s"] = hex0x(s); + if (signer.has_value()) + je["signer"] = hex0x(*signer); + ja.emplace_back(std::move(je)); + } + } + + + auto& jpost = jt["post"][to_test_fork_name(rev)][0]; + jpost["indexes"] = {{"data", 0}, {"gas", 0}, {"value", 0}}; + jpost["hash"] = hex0x(mpt_hash(post)); + + if (holds_alternative(res)) + { + jpost["expectException"] = get_invalid_tx_message( + static_cast(std::get(res).value())); + jpost["logs"] = hex0x(logs_hash(std::vector())); + } + else + { + jpost["logs"] = hex0x(logs_hash(std::get(res).logs)); + } + + return j; +} } // namespace evmone::test diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index 3d2c42859e..7ca771b814 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -150,122 +150,11 @@ void state_transition::TearDown() export_state_test(res, post); } -namespace -{ -/// Converts EVM revision to the fork name commonly used in tests. -std::string_view to_test_fork_name(evmc_revision rev) noexcept -{ - switch (rev) - { - case EVMC_TANGERINE_WHISTLE: - return "EIP150"; - case EVMC_SPURIOUS_DRAGON: - return "EIP158"; - default: - return evmc::to_string(rev); - } -} -} // namespace - void state_transition::export_state_test( const std::variant& res, const TestState& post) { - json::json j; - auto& jt = j[export_test_name]; - - auto& jenv = jt["env"]; - jenv["currentNumber"] = hex0x(block.number); - jenv["currentTimestamp"] = hex0x(block.timestamp); - jenv["currentGasLimit"] = hex0x(block.gas_limit); - jenv["currentCoinbase"] = hex0x(block.coinbase); - jenv["currentBaseFee"] = hex0x(block.base_fee); - jenv["currentRandom"] = hex0x(block.prev_randao); - - jt["pre"] = to_json(pre); - - auto& jtx = jt["transaction"]; - if (tx.to.has_value()) - jtx["to"] = hex0x(*tx.to); - jtx["sender"] = hex0x(tx.sender); - jtx["secretKey"] = hex0x(SenderSecretKey); - jtx["nonce"] = hex0x(tx.nonce); - if (tx.type >= Transaction::Type::eip1559) - { - jtx["maxFeePerGas"] = hex0x(tx.max_gas_price); - jtx["maxPriorityFeePerGas"] = hex0x(tx.max_priority_gas_price); - } - else - { - assert(tx.max_gas_price == tx.max_priority_gas_price); - jtx["gasPrice"] = hex0x(tx.max_gas_price); - } - - jtx["data"][0] = hex0x(tx.data); - jtx["gasLimit"][0] = hex0x(tx.gas_limit); - jtx["value"][0] = hex0x(tx.value); - - // Force `accessLists` output even if empty. - if (tx.type >= Transaction::Type::access_list) - jtx["accessLists"][0] = json::json::array(); - - if (!tx.access_list.empty()) - { - auto& ja = jtx["accessLists"][0]; - for (const auto& [addr, storage_keys] : tx.access_list) - { - json::json je; - je["address"] = hex0x(addr); - auto& jstorage_keys = je["storageKeys"] = json::json::array(); - for (const auto& k : storage_keys) - jstorage_keys.emplace_back(hex0x(k)); - ja.emplace_back(std::move(je)); - } - } - - if (tx.type == Transaction::Type::blob) - { - jtx["maxFeePerBlobGas"] = hex0x(tx.max_blob_gas_price); - jtx["blobVersionedHashes"] = json::json::array(); - for (const auto& blob_hash : tx.blob_hashes) - { - jtx["blobVersionedHashes"].emplace_back(hex0x(blob_hash)); - } - } - - if (!tx.authorization_list.empty()) - { - auto& ja = jtx["authorizationList"]; - for (const auto& [chain_id, addr, nonce, signer, r, s, y_parity] : tx.authorization_list) - { - json::json je; - je["chainId"] = hex0x(chain_id); - je["address"] = hex0x(addr); - je["nonce"] = hex0x(nonce); - je["v"] = hex0x(y_parity); - je["r"] = hex0x(r); - je["s"] = hex0x(s); - if (signer.has_value()) - je["signer"] = hex0x(*signer); - ja.emplace_back(std::move(je)); - } - } - - - auto& jpost = jt["post"][to_test_fork_name(rev)][0]; - jpost["indexes"] = {{"data", 0}, {"gas", 0}, {"value", 0}}; - jpost["hash"] = hex0x(mpt_hash(post)); - - if (holds_alternative(res)) - { - jpost["expectException"] = - get_invalid_tx_message(static_cast(std::get(res).value())); - jpost["logs"] = hex0x(logs_hash(std::vector())); - } - else - { - jpost["logs"] = hex0x(logs_hash(std::get(res).logs)); - } - + const auto j = to_state_test( + export_test_name, block, tx, pre, rev, res, post); std::ofstream{export_file_path} << std::setw(2) << j; } } // namespace evmone::test