diff --git a/test/blockchaintest/blockchaintest_runner.cpp b/test/blockchaintest/blockchaintest_runner.cpp index b0f6f4aaaa..59f4d687ef 100644 --- a/test/blockchaintest/blockchaintest_runner.cpp +++ b/test/blockchaintest/blockchaintest_runner.cpp @@ -29,10 +29,10 @@ struct TransitionResult namespace { TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockInfo& block, - const std::vector& txs, evmc_revision rev, - std::optional block_reward) + const state::BlockHashes& block_hashes, const std::vector& txs, + evmc_revision rev, std::optional block_reward) { - system_call(state, block, rev, vm); + system_call(state, block, block_hashes, rev, vm); std::vector txs_logs; int64_t block_gas_left = block.gas_limit; @@ -48,7 +48,8 @@ TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockI const auto& tx = txs[i]; const auto computed_tx_hash = keccak256(rlp::encode(tx)); - auto res = test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + auto res = + transition(state, block, block_hashes, tx, rev, vm, block_gas_left, blob_gas_left); if (holds_alternative(res)) { @@ -73,7 +74,7 @@ TransitionResult apply_block(TestState& state, evmc::VM& vm, const state::BlockI } } - test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); + 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,20 +138,19 @@ void run_blockchain_tests(std::span tests, evmc::VM& vm) auto state = c.pre_state; - std::unordered_map known_block_hashes{ + TestBlockHashes block_hashes{ {c.genesis_block_header.block_number, c.genesis_block_header.hash}}; for (const auto& test_block : c.test_blocks) { auto bi = test_block.block_info; - bi.known_block_hashes = known_block_hashes; const auto rev = c.rev.get_revision(bi.timestamp); - const auto res = - apply_block(state, vm, bi, test_block.transactions, rev, mining_reward(rev)); + const auto res = apply_block( + state, vm, bi, block_hashes, test_block.transactions, rev, mining_reward(rev)); - known_block_hashes[test_block.expected_block_header.block_number] = + block_hashes[test_block.expected_block_header.block_number] = test_block.expected_block_header.hash; SCOPED_TRACE(std::string{evmc::to_string(rev)} + '/' + std::to_string(case_index) + diff --git a/test/state/block.hpp b/test/state/block.hpp index 13daa47f62..91e12379d2 100644 --- a/test/state/block.hpp +++ b/test/state/block.hpp @@ -57,10 +57,6 @@ struct BlockInfo std::vector ommers; std::vector withdrawals; - - /// Collection of known block hashes by block number. Only use in tests. - /// TODO: This should be moved to evmone::test. - std::unordered_map known_block_hashes; }; /// Computes the current blob gas price based on the excess blob gas. diff --git a/test/state/test_state.cpp b/test/state/test_state.cpp index fbf79cc883..52c1981cb8 100644 --- a/test/state/test_state.cpp +++ b/test/state/test_state.cpp @@ -61,20 +61,20 @@ bytes32 TestState::get_storage(const address& addr, const bytes32& key) const no bytes32 TestBlockHashes::get_block_hash(int64_t block_number) const noexcept { - if (const auto& it = known_block_hashes_.find(block_number); it != known_block_hashes_.end()) + if (const auto& it = find(block_number); it != end()) return it->second; - // Convention for testing: if the block hash in unknown return the predefined "fake" value. + // Convention for testing: if the block hash is unknown return the predefined "fake" value. // https://github.com/ethereum/go-ethereum/blob/v1.12.2/tests/state_test_util.go#L432 const auto s = std::to_string(block_number); return keccak256({reinterpret_cast(s.data()), s.size()}); } [[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) + const state::BlockInfo& block, const state::BlockHashes& block_hashes, + const state::Transaction& tx, evmc_revision rev, evmc::VM& vm, int64_t block_gas_left, + int64_t blob_gas_left) { - const TestBlockHashes block_hashes{block.known_block_hashes}; const auto result_or_error = state::transition(state, block, block_hashes, tx, rev, vm, block_gas_left, blob_gas_left); if (const auto result = get_if(&result_or_error)) @@ -90,9 +90,9 @@ void finalize(TestState& state, evmc_revision rev, const address& coinbase, state.apply(diff); } -void system_call(TestState& state, const state::BlockInfo& block, evmc_revision rev, evmc::VM& vm) +void system_call(TestState& state, const state::BlockInfo& block, + const state::BlockHashes& block_hashes, evmc_revision rev, evmc::VM& vm) { - const TestBlockHashes block_hashes{block.known_block_hashes}; const auto diff = state::system_call(state, block, block_hashes, rev, vm); state.apply(diff); } diff --git a/test/state/test_state.hpp b/test/state/test_state.hpp index 2b598c8826..7afc37092f 100644 --- a/test/state/test_state.hpp +++ b/test/state/test_state.hpp @@ -72,23 +72,19 @@ class TestState : public state::StateView, public std::map void apply(const state::StateDiff& diff); }; -class TestBlockHashes : public state::BlockHashes +class TestBlockHashes : public state::BlockHashes, public std::unordered_map { - /// The map block_number => block hash of known blocks. - const std::unordered_map& known_block_hashes_; - public: - explicit TestBlockHashes(const std::unordered_map& known_block_hashes) - : known_block_hashes_{known_block_hashes} - {} + using std::unordered_map::unordered_map; bytes32 get_block_hash(int64_t block_number) const noexcept override; }; /// 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); + const state::BlockInfo& block, const state::BlockHashes& block_hashes, + 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, @@ -96,7 +92,8 @@ void finalize(TestState& state, evmc_revision rev, const address& coinbase, 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); +void system_call(TestState& state, const state::BlockInfo& block, + const state::BlockHashes& block_hashes, evmc_revision rev, evmc::VM& vm); } // namespace test } // namespace evmone diff --git a/test/statetest/statetest.hpp b/test/statetest/statetest.hpp index 760f78ba5b..b2112c13c3 100644 --- a/test/statetest/statetest.hpp +++ b/test/statetest/statetest.hpp @@ -59,6 +59,7 @@ struct StateTransitionTest std::string name; TestState pre_state; state::BlockInfo block; + TestBlockHashes block_hashes; TestMultiTransaction multi_tx; std::vector cases; std::unordered_map input_labels; @@ -85,6 +86,9 @@ bytes from_json(const json::json& j); template <> state::BlockInfo from_json(const json::json& j); +template <> +TestBlockHashes from_json(const json::json& j); + template <> state::Withdrawal from_json(const json::json& j); diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 4d60be0928..b81c0b7ffa 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -212,13 +212,6 @@ state::BlockInfo from_json(const json::json& j) withdrawals.push_back(from_json(withdrawal)); } - std::unordered_map block_hashes; - if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end()) - { - for (const auto& [j_num, j_hash] : block_hashes_it->items()) - block_hashes[from_json(j_num)] = from_json(j_hash); - } - std::vector ommers; if (const auto ommers_it = j.find("ommers"); ommers_it != j.end()) { @@ -265,10 +258,21 @@ state::BlockInfo from_json(const json::json& j) .blob_base_fee = state::compute_blob_gas_price(excess_blob_gas), .ommers = std::move(ommers), .withdrawals = std::move(withdrawals), - .known_block_hashes = std::move(block_hashes), }; } +template <> +TestBlockHashes from_json(const json::json& j) +{ + TestBlockHashes block_hashes; + if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end()) + { + for (const auto& [j_num, j_hash] : block_hashes_it->items()) + block_hashes[from_json(j_num)] = from_json(j_hash); + } + return block_hashes; +} + template <> TestState from_json(const json::json& j) { @@ -425,6 +429,7 @@ static void from_json(const json::json& j_t, StateTransitionTest& o) o.multi_tx = j_t.at("transaction").get(); o.block = from_json(j_t.at("env")); + o.block_hashes = from_json(j_t.at("env")); if (const auto info_it = j_t.find("_info"); info_it != j_t.end()) { diff --git a/test/statetest/statetest_runner.cpp b/test/statetest/statetest_runner.cpp index 75ac2b509c..bcde7a4c9a 100644 --- a/test/statetest/statetest_runner.cpp +++ b/test/statetest/statetest_runner.cpp @@ -27,8 +27,8 @@ void run_state_test(const StateTransitionTest& test, evmc::VM& vm, bool trace_su const auto tx = test.multi_tx.get(expected.indexes); auto state = test.pre_state; - const auto res = test::transition(state, test.block, tx, rev, vm, test.block.gas_limit, - state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); + const auto res = test::transition(state, test.block, test.block_hashes, tx, rev, vm, + test.block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); // Finalize block with reward 0. test::finalize(state, rev, test.block.coinbase, 0, {}, {}); diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 912f61575f..774beadc71 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -75,6 +75,7 @@ int main(int argc, const char* argv[]) } state::BlockInfo block; + TestBlockHashes block_hashes; TestState state; if (!alloc_file.empty()) @@ -86,7 +87,8 @@ int main(int argc, const char* argv[]) if (!env_file.empty()) { const auto j = json::json::parse(std::ifstream{env_file}); - block = test::from_json(j); + block = from_json(j); + block_hashes = from_json(j); } json::json j_result; @@ -133,7 +135,7 @@ int main(int argc, const char* argv[]) j_result["receipts"] = json::json::array(); j_result["rejected"] = json::json::array(); - test::system_call(state, block, rev, vm); + test::system_call(state, block, block_hashes, rev, vm); for (size_t i = 0; i < j_txs.size(); ++i) { @@ -168,8 +170,8 @@ int main(int argc, const char* argv[]) std::clog.rdbuf(trace_file_output.rdbuf()); } - auto res = - test::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left); + auto res = test::transition( + state, block, block_hashes, tx, rev, vm, block_gas_left, blob_gas_left); if (holds_alternative(res)) { diff --git a/test/unittests/state_system_call_test.cpp b/test/unittests/state_system_call_test.cpp index 63683f5c86..debd4004ac 100644 --- a/test/unittests/state_system_call_test.cpp +++ b/test/unittests/state_system_call_test.cpp @@ -18,12 +18,13 @@ class state_system_call : public testing::Test protected: evmc::VM vm{evmc_create_evmone()}; TestState state; + TestBlockHashes block_hashes; }; TEST_F(state_system_call, non_existient) { // Use MAX revision to invoke all activate system contracts. - system_call(state, {}, EVMC_MAX_REVISION, vm); + system_call(state, {}, block_hashes, EVMC_MAX_REVISION, vm); EXPECT_EQ(state.size(), 0) << "State must remain unchanged"; } @@ -34,7 +35,7 @@ TEST_F(state_system_call, beacon_roots) state.insert( BEACON_ROOTS_ADDRESS, {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)}); - system_call(state, block, EVMC_CANCUN, vm); + system_call(state, block, block_hashes, EVMC_CANCUN, vm); ASSERT_EQ(state.size(), 1); EXPECT_FALSE(state.contains(SYSTEM_ADDRESS)); @@ -50,11 +51,12 @@ TEST_F(state_system_call, history_storage) { static constexpr auto NUMBER = 123456789; static constexpr auto PREV_BLOCKHASH = 0xbbbb_bytes32; - const BlockInfo block{.number = NUMBER, .known_block_hashes = {{NUMBER - 1, PREV_BLOCKHASH}}}; + const BlockInfo block{.number = NUMBER}; + block_hashes = {{NUMBER - 1, PREV_BLOCKHASH}}; state.insert(HISTORY_STORAGE_ADDRESS, {.code = sstore(OP_NUMBER, calldataload(0)) + sstore(0, OP_CALLER)}); - system_call(state, block, EVMC_PRAGUE, vm); + system_call(state, block, block_hashes, EVMC_PRAGUE, vm); ASSERT_EQ(state.size(), 1); EXPECT_FALSE(state.contains(SYSTEM_ADDRESS)); diff --git a/test/unittests/state_transition.cpp b/test/unittests/state_transition.cpp index 66d205270e..98640c2a81 100644 --- a/test/unittests/state_transition.cpp +++ b/test/unittests/state_transition.cpp @@ -57,8 +57,8 @@ void state_transition::TearDown() if (trace) trace_capture.emplace(); - const auto res = test::transition(state, block, tx, rev, selected_vm, block.gas_limit, - state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); + const auto res = test::transition(state, block, block_hashes, tx, rev, selected_vm, + block.gas_limit, state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK); test::finalize(state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals); const auto& post = state; diff --git a/test/unittests/state_transition.hpp b/test/unittests/state_transition.hpp index 1e24f2b0f0..cf19b3a5e5 100644 --- a/test/unittests/state_transition.hpp +++ b/test/unittests/state_transition.hpp @@ -77,6 +77,7 @@ class state_transition : public ExportableFixture .coinbase = Coinbase, .base_fee = 999, }; + TestBlockHashes block_hashes; Transaction tx{ // The default type corresponds to the default `rev` and majority of tests. .type = Transaction::Type::eip1559, diff --git a/test/unittests/state_transition_block_test.cpp b/test/unittests/state_transition_block_test.cpp index 33d5194885..6aa0001a3b 100644 --- a/test/unittests/state_transition_block_test.cpp +++ b/test/unittests/state_transition_block_test.cpp @@ -19,9 +19,10 @@ TEST_F(state_transition, block_apply_withdrawal) TEST_F(state_transition, known_block_hash) { - block.known_block_hashes = { + block_hashes = { {1, 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32}, - {2, 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32}}; + {2, 0x0000000000000000000000000000000000000000000000000000000000000111_bytes32}, + }; block.number = 5; tx.to = To; diff --git a/test/unittests/statetest_loader_block_info_test.cpp b/test/unittests/statetest_loader_block_info_test.cpp index 6508b7f14b..9cc70bed3c 100644 --- a/test/unittests/statetest_loader_block_info_test.cpp +++ b/test/unittests/statetest_loader_block_info_test.cpp @@ -17,11 +17,7 @@ TEST(statetest_loader, block_info) "currentTimestamp": "0", "currentBaseFee": "7", "currentRandom": "0x00", - "withdrawals": [], - "blockHashes": { - "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", - "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" - } + "withdrawals": [] })"; const auto bi = test::from_json(json::json::parse(input)); @@ -31,10 +27,6 @@ TEST(statetest_loader, block_info) EXPECT_EQ(bi.base_fee, 7); EXPECT_EQ(bi.timestamp, 0); EXPECT_EQ(bi.number, 0); - EXPECT_EQ(bi.known_block_hashes.at(0), - 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); - EXPECT_EQ(bi.known_block_hashes.at(1), - 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); } TEST(statetest_loader, block_info_hex) @@ -51,9 +43,6 @@ TEST(statetest_loader, block_info_hex) "parentGasUsed": "0", "parentGasLimit": "0x16345785D8A0000", "parentTimstamp": "0", - "blockHashes": { - "0": "0xc305d826e3784046a7e9d31128ef98d3e96133fe454c16ef630574d967dfdb1a" - }, "ommers": [], "withdrawals": [], "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" @@ -82,9 +71,6 @@ TEST(statetest_loader, block_info_dec) "parentGasUsed": "0", "parentGasLimit": "100000000000000000", "parentTimstamp": "0", - "blockHashes": { - "0": "0xc305d826e3784046a7e9d31128ef98d3e96133fe454c16ef630574d967dfdb1a" - }, "ommers": [], "withdrawals": [], "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" @@ -111,9 +97,6 @@ TEST(statetest_loader, block_info_0_current_difficulty) "parentGasUsed": "0", "parentGasLimit": "100000000000000000", "parentTimstamp": "0", - "blockHashes": { - "0": "0xc305d826e3784046a7e9d31128ef98d3e96133fe454c16ef630574d967dfdb1a" - }, "ommers": [], "withdrawals": [], "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" @@ -140,9 +123,6 @@ TEST(statetest_loader, block_info_0_parent_difficulty) "parentGasUsed": "0", "parentGasLimit": "100000000000000000", "parentTimestamp": "253", - "blockHashes": { - "0": "0xc305d826e3784046a7e9d31128ef98d3e96133fe454c16ef630574d967dfdb1a" - }, "ommers": [], "withdrawals": [], "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" @@ -269,10 +249,6 @@ TEST(statetest_loader, block_info_parent_blob_gas) "currentBaseFee": "7", "currentRandom": "0x00", "withdrawals": [], - "blockHashes": { - "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", - "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" - }, "parentExcessBlobGas": "1", "parentBlobGasUsed": "0x60000" })"; @@ -284,10 +260,6 @@ TEST(statetest_loader, block_info_parent_blob_gas) EXPECT_EQ(bi.base_fee, 7); EXPECT_EQ(bi.timestamp, 0); EXPECT_EQ(bi.number, 0); - EXPECT_EQ(bi.known_block_hashes.at(0), - 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); - EXPECT_EQ(bi.known_block_hashes.at(1), - 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); EXPECT_EQ(bi.excess_blob_gas, 1); } @@ -302,10 +274,6 @@ TEST(statetest_loader, block_info_current_blob_gas) "currentBaseFee": "7", "currentRandom": "0x00", "withdrawals": [], - "blockHashes": { - "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", - "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" - }, "currentExcessBlobGas": "2" })"; @@ -316,10 +284,6 @@ TEST(statetest_loader, block_info_current_blob_gas) EXPECT_EQ(bi.base_fee, 7); EXPECT_EQ(bi.timestamp, 0); EXPECT_EQ(bi.number, 0); - EXPECT_EQ(bi.known_block_hashes.at(0), - 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); - EXPECT_EQ(bi.known_block_hashes.at(1), - 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); EXPECT_EQ(bi.excess_blob_gas, 2); } @@ -336,3 +300,16 @@ TEST(statetest_loader, block_info_parent_beacon_block_root) const auto bi = test::from_json(json::json::parse(input)); EXPECT_EQ(bi.parent_beacon_block_root, 0xbeac045007_bytes32); } + +TEST(statetest_loader, block_hashes) +{ + constexpr std::string_view input = R"({ + "blockHashes": { + "0" : "0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e", + "1" : "0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b" + }})"; + + const auto bh = test::from_json(json::json::parse(input)); + EXPECT_EQ(bh.at(0), 0xe729de3fec21e30bea3d56adb01ed14bc107273c2775f9355afb10f594a10d9e_bytes32); + EXPECT_EQ(bh.at(1), 0xb5eee60b45801179cbde3781b9a5dee9b3111554618c9cda3d6f7e351fd41e0b_bytes32); +}