From b15fa3fd8a3a782a67e7a2d46881cf5f35103798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 23 Aug 2023 10:30:59 +0200 Subject: [PATCH 1/3] mpt: Convert mpt_hash() of list to template --- test/state/mpt_hash.cpp | 17 ++++++----------- test/state/mpt_hash.hpp | 16 ++++++++++------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/test/state/mpt_hash.cpp b/test/state/mpt_hash.cpp index 316bd4cccf..6ff2470bdf 100644 --- a/test/state/mpt_hash.cpp +++ b/test/state/mpt_hash.cpp @@ -35,22 +35,17 @@ hash256 mpt_hash(const std::unordered_map& accounts) return trie.hash(); } -hash256 mpt_hash(std::span transactions) +template +hash256 mpt_hash(std::span list) { MPT trie; - for (size_t i = 0; i < transactions.size(); ++i) - trie.insert(rlp::encode(i), rlp::encode(transactions[i])); + for (size_t i = 0; i < list.size(); ++i) + trie.insert(rlp::encode(i), rlp::encode(list[i])); return trie.hash(); } -hash256 mpt_hash(std::span receipts) -{ - MPT trie; - for (size_t i = 0; i < receipts.size(); ++i) - trie.insert(rlp::encode(i), rlp::encode(receipts[i])); - - return trie.hash(); -} +template hash256 mpt_hash(std::span); +template hash256 mpt_hash(std::span); } // namespace evmone::state diff --git a/test/state/mpt_hash.hpp b/test/state/mpt_hash.hpp index e0a25a9df6..58c7301d23 100644 --- a/test/state/mpt_hash.hpp +++ b/test/state/mpt_hash.hpp @@ -10,16 +10,20 @@ namespace evmone::state { struct Account; -struct Transaction; -struct TransactionReceipt; /// Computes Merkle Patricia Trie root hash for the given collection of state accounts. hash256 mpt_hash(const std::unordered_map& accounts); -/// Computes Merkle Patricia Trie root hash for the given collection of transactions. -hash256 mpt_hash(std::span transactions); +/// Computes Merkle Patricia Trie root hash for the given list of structures. +template +hash256 mpt_hash(std::span list); -/// Computes Merkle Patricia Trie root hash for the given collection of transactions receipts. -hash256 mpt_hash(std::span receipts); +/// A helper to automatically convert collections (e.g. vector, array) to span. +template +inline hash256 mpt_hash(const T& list) + requires std::is_convertible_v> +{ + return mpt_hash(std::span{list}); +} } // namespace evmone::state From a0e23fa3bf1ebbfb5fd462fa3c5cdb0598a5366a Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 23 Aug 2023 11:18:50 +0200 Subject: [PATCH 2/3] state: Add withdrawalsRoot calculation --- test/state/mpt_hash.cpp | 1 + test/state/state.cpp | 6 + test/state/state.hpp | 5 + test/statetest/statetest.hpp | 3 + test/statetest/statetest_loader.cpp | 12 +- test/t8n/t8n.cpp | 3 + test/unittests/CMakeLists.txt | 1 + test/unittests/state_mpt_hash_test.cpp | 26 +++++ .../unittests/state_transition_block_test.cpp | 2 +- test/unittests/statetest_withdrawals_test.cpp | 104 ++++++++++++++++++ 10 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 test/unittests/statetest_withdrawals_test.cpp diff --git a/test/state/mpt_hash.cpp b/test/state/mpt_hash.cpp index 6ff2470bdf..910f768481 100644 --- a/test/state/mpt_hash.cpp +++ b/test/state/mpt_hash.cpp @@ -47,5 +47,6 @@ hash256 mpt_hash(std::span list) template hash256 mpt_hash(std::span); template hash256 mpt_hash(std::span); +template hash256 mpt_hash(std::span); } // namespace evmone::state diff --git a/test/state/state.cpp b/test/state/state.cpp index 150ba9214c..b461e6bcaa 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -295,4 +295,10 @@ std::variant transition(State& state, const } } +[[nodiscard]] bytes rlp_encode(const Withdrawal& withdrawal) +{ + return rlp::encode_tuple(withdrawal.index, withdrawal.validator_index, withdrawal.recipient, + withdrawal.amount_in_gwei); +} + } // namespace evmone::state diff --git a/test/state/state.hpp b/test/state/state.hpp index 76e5a347f6..28e11778e8 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -68,6 +68,8 @@ class State struct Withdrawal { + uint64_t index = 0; + uint64_t validator_index = 0; address recipient; uint64_t amount_in_gwei = 0; ///< The amount is denominated in gwei. @@ -185,4 +187,7 @@ std::variant validate_transaction(const Account& sende /// Defines how to RLP-encode a Log. [[nodiscard]] bytes rlp_encode(const Log& log); +/// Defines how to RLP-encode a Withdrawal. +[[nodiscard]] bytes rlp_encode(const Withdrawal& withdrawal); + } // namespace evmone::state diff --git a/test/statetest/statetest.hpp b/test/statetest/statetest.hpp index a4cf8f54d4..da54feca6b 100644 --- a/test/statetest/statetest.hpp +++ b/test/statetest/statetest.hpp @@ -77,6 +77,9 @@ int64_t from_json(const json::json& j); template <> state::BlockInfo from_json(const json::json& j); +template <> +state::Withdrawal from_json(const json::json& j); + template <> state::State from_json(const json::json& j); diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index 18a7c779b3..b4307ddc4c 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -147,6 +147,13 @@ inline uint64_t calculate_current_base_fee_eip1559( return base_fee; } +template <> +state::Withdrawal from_json(const json::json& j) +{ + return {from_json(j.at("index")), from_json(j.at("validatorIndex")), + from_json(j.at("address")), from_json(j.at("amount"))}; +} + template <> state::BlockInfo from_json(const json::json& j) { @@ -175,10 +182,7 @@ state::BlockInfo from_json(const json::json& j) if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end()) { for (const auto& withdrawal : *withdrawals_it) - { - withdrawals.push_back({from_json(withdrawal.at("address")), - from_json(withdrawal.at("amount"))}); - } + withdrawals.push_back(from_json(withdrawal)); } std::unordered_map block_hashes; diff --git a/test/t8n/t8n.cpp b/test/t8n/t8n.cpp index 03e807a153..ebf946294a 100644 --- a/test/t8n/t8n.cpp +++ b/test/t8n/t8n.cpp @@ -197,6 +197,9 @@ int main(int argc, const char* argv[]) j_result["logsBloom"] = hex0x(compute_bloom_filter(receipts)); j_result["receiptsRoot"] = hex0x(state::mpt_hash(receipts)); + if (rev >= EVMC_SHANGHAI) + j_result["withdrawalsRoot"] = hex0x(state::mpt_hash(block.withdrawals)); + j_result["txRoot"] = hex0x(state::mpt_hash(transactions)); j_result["gasUsed"] = hex0x(cumulative_gas_used); diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index aabc640ae0..cfd0dfe56d 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -48,6 +48,7 @@ target_sources( statetest_loader_test.cpp statetest_loader_tx_test.cpp statetest_logs_hash_test.cpp + statetest_withdrawals_test.cpp tracing_test.cpp ) target_link_libraries(evmone-unittests PRIVATE evmone evmone::evmmax evmone::state evmone::statetestutils testutils evmc::instructions GTest::gtest GTest::gtest_main) diff --git a/test/unittests/state_mpt_hash_test.cpp b/test/unittests/state_mpt_hash_test.cpp index c44744b4b4..e9fa398ae2 100644 --- a/test/unittests/state_mpt_hash_test.cpp +++ b/test/unittests/state_mpt_hash_test.cpp @@ -279,3 +279,29 @@ TEST(state_mpt_hash, pre_byzantium_receipt) EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1, receipt2}), 0x8a4fa43a95939b06ad13ce8cd08e026ae6e79ea3c5fc80c732d252e2769ce778_bytes32); } + +TEST(state_mpt_hash, mpt_hashing_test) +{ + // The same data as in 'statetest_withdrawals.withdrawals_warmup_test_case' unit test. + MPT trie; + + trie.insert("80"_hex, "db808094c000000000000000000000000000000000000001830186a0"_hex); + EXPECT_EQ( + trie.hash(), 0x4ae14e53d6bf2a9c73891aef9d2e6373ff06d400b6e7727b17a5eceb5e8dec9d_bytes32); + + trie.insert("01"_hex, "db018094c000000000000000000000000000000000000002830186a0"_hex); + EXPECT_EQ( + trie.hash(), 0xc5128234c0282b256e2aa91ddc783ddb2c21556766f2e11e64159561b59f8ac7_bytes32); + + trie.insert("0x02"_hex, "0xdb028094c000000000000000000000000000000000000003830186a0"_hex); + trie.insert("0x03"_hex, "0xdb038094c000000000000000000000000000000000000004830186a0"_hex); + trie.insert("0x04"_hex, "0xdb048094c000000000000000000000000000000000000005830186a0"_hex); + trie.insert("0x05"_hex, "0xdb058094c000000000000000000000000000000000000006830186a0"_hex); + trie.insert("0x06"_hex, "0xdb068094c000000000000000000000000000000000000007830186a0"_hex); + trie.insert("0x07"_hex, "0xdb078094c000000000000000000000000000000000000008830186a0"_hex); + trie.insert("0x08"_hex, "0xdb088094c000000000000000000000000000000000000009830186a0"_hex); + trie.insert("0x09"_hex, "0xdb098094c000000000000000000000000000000000000010830186a0"_hex); + + EXPECT_EQ( + trie.hash(), 0xaa45c53e9f7d6a8362f80876029915da00b1441ef39eb9bbb74f98465ff433ad_bytes32); +} diff --git a/test/unittests/state_transition_block_test.cpp b/test/unittests/state_transition_block_test.cpp index 562bd9b70e..16fc817ba6 100644 --- a/test/unittests/state_transition_block_test.cpp +++ b/test/unittests/state_transition_block_test.cpp @@ -12,7 +12,7 @@ TEST_F(state_transition, block_apply_withdrawal) { static constexpr auto withdrawal_address = 0x8888_address; - block.withdrawals = {{withdrawal_address, 3}}; + block.withdrawals = {{0, 0, withdrawal_address, 3}}; tx.to = To; expect.post[withdrawal_address].balance = intx::uint256{3} * 1'000'000'000; } diff --git a/test/unittests/statetest_withdrawals_test.cpp b/test/unittests/statetest_withdrawals_test.cpp new file mode 100644 index 0000000000..58eb72f37a --- /dev/null +++ b/test/unittests/statetest_withdrawals_test.cpp @@ -0,0 +1,104 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace evmone; +using namespace evmone::state; +using namespace evmone::test; + +TEST(statetest_withdrawals, withdrawals_root_hash) +{ + // Input taken from https://etherscan.io/block/17826409 + constexpr std::string_view input = + R"([{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe162d9","index":"0xc13ad8","validatorIndex":"0xa2f00"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1c5c2","index":"0xc13ad9","validatorIndex":"0xa2f01"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe14f28","index":"0xc13ada","validatorIndex":"0xa2f02"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe190f2","index":"0xc13adb","validatorIndex":"0xa2f03"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1e59c","index":"0xc13adc","validatorIndex":"0xa2f04"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1bbfe","index":"0xc13add","validatorIndex":"0xa2f05"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe20974","index":"0xc13ade","validatorIndex":"0xa2f06"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe145b7","index":"0xc13adf","validatorIndex":"0xa2f07"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe11e5d","index":"0xc13ae0","validatorIndex":"0xa2f08"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe221e0","index":"0xc13ae1","validatorIndex":"0xa2f09"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe2061a","index":"0xc13ae2","validatorIndex":"0xa2f0a"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe23d22","index":"0xc13ae3","validatorIndex":"0xa2f0b"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1ab3a","index":"0xc13ae4","validatorIndex":"0xa2f0c"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe19535","index":"0xc13ae5","validatorIndex":"0xa2f0d"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0x317c537","index":"0xc13ae6","validatorIndex":"0xa2f0e"},{"address":"0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f","amount":"0xe1965f","index":"0xc13ae7","validatorIndex":"0xa2f0f"}])"; + + const auto j = json::json::parse(input); + + std::vector withdrawals; + for (const auto& withdrawal : j) + withdrawals.push_back(from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0x38cd9ae992a22b94a1582e7d0691dbef56a90cdb36bf7b11d98373f80b102c8f_bytes32); +} + +TEST(statetest_withdrawals, withdrawals_warmup_test_case) +{ + // Input taken from + // https://github.com/ethereum/tests/blob/develop/BlockchainTests/InvalidBlocks/bc4895-withdrawals/warmup.json + constexpr std::string_view input = + R"([ +{ + "index" : "0x0", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000001" +}, +{ + "index" : "0x1", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000002" +}, +{ + "index" : "0x2", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000003" +}, +{ + "index" : "0x3", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000004" +}, +{ + "index" : "0x4", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000005" +}, +{ + "index" : "0x5", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000006" +}, +{ + "index" : "0x6", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000007" +}, +{ + "index" : "0x7", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000008" +}, +{ + "index" : "0x8", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000009" +}, +{ + "index" : "0x9", + "validatorIndex" : "0x0", + "amount" : "0x186a0", + "address" : "0xc000000000000000000000000000000000000010" +} +])"; + const auto j = json::json::parse(input); + + std::vector withdrawals; + for (const auto& withdrawal : j) + withdrawals.push_back(from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0xaa45c53e9f7d6a8362f80876029915da00b1441ef39eb9bbb74f98465ff433ad_bytes32); +} From 1ffe20f24d75770882c25a7616ab764a1d28ef29 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 2 Aug 2023 18:58:57 +0200 Subject: [PATCH 3/3] statetest: Translate ArrowGlacier fork to EVM revision Set ArrowGlacier as alias of London EVM revision. --- test/statetest/statetest_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/statetest/statetest_loader.cpp b/test/statetest/statetest_loader.cpp index b4307ddc4c..5b1938ba4d 100644 --- a/test/statetest/statetest_loader.cpp +++ b/test/statetest/statetest_loader.cpp @@ -242,7 +242,7 @@ evmc_revision to_rev(std::string_view s) return EVMC_ISTANBUL; if (s == "Berlin") return EVMC_BERLIN; - if (s == "London") + if (s == "London" || s == "ArrowGlacier") return EVMC_LONDON; if (s == "Merge") return EVMC_PARIS;