From 01446c74c89fecc43b9347dca21e5337e150af22 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Thu, 10 Aug 2023 11:24:44 +0200 Subject: [PATCH] state: Add withdrawalsRoot calculation --- test/state/mpt_hash.cpp | 9 ++ test/state/mpt_hash.hpp | 4 + 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/state_mpt_hash_test.cpp | 120 ++++++++++++++++++ .../unittests/state_transition_block_test.cpp | 2 +- 9 files changed, 159 insertions(+), 5 deletions(-) diff --git a/test/state/mpt_hash.cpp b/test/state/mpt_hash.cpp index 316bd4cccf..ef33d0b246 100644 --- a/test/state/mpt_hash.cpp +++ b/test/state/mpt_hash.cpp @@ -53,4 +53,13 @@ hash256 mpt_hash(std::span receipts) return trie.hash(); } +hash256 mpt_hash(std::span withdrawals) +{ + MPT trie; + for (size_t i = 0; i < withdrawals.size(); ++i) + trie.insert(rlp::encode(i), rlp::encode(withdrawals[i])); + + return trie.hash(); +} + } // namespace evmone::state diff --git a/test/state/mpt_hash.hpp b/test/state/mpt_hash.hpp index e0a25a9df6..38119ba63d 100644 --- a/test/state/mpt_hash.hpp +++ b/test/state/mpt_hash.hpp @@ -12,6 +12,7 @@ namespace evmone::state struct Account; struct Transaction; struct TransactionReceipt; +struct Withdrawal; /// Computes Merkle Patricia Trie root hash for the given collection of state accounts. hash256 mpt_hash(const std::unordered_map& accounts); @@ -22,4 +23,7 @@ hash256 mpt_hash(std::span transactions); /// Computes Merkle Patricia Trie root hash for the given collection of transactions receipts. hash256 mpt_hash(std::span receipts); +/// Computes Merkle Patricia Trie root hash for the given collection of block withdrawals. +hash256 mpt_hash(std::span withdrawals); + } // 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 8cb9571a7a..ce1e2f49b7 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. @@ -184,4 +186,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 f46f36e761..b275b1d096 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)); } return {from_json(j.at("currentNumber")), from_json(j.at("currentTimestamp")), 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/state_mpt_hash_test.cpp b/test/unittests/state_mpt_hash_test.cpp index c44744b4b4..7712ae9910 100644 --- a/test/unittests/state_mpt_hash_test.cpp +++ b/test/unittests/state_mpt_hash_test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include using namespace evmone; @@ -279,3 +280,122 @@ TEST(state_mpt_hash, pre_byzantium_receipt) EXPECT_EQ(mpt_hash(std::array{receipt0, receipt1, receipt2}), 0x8a4fa43a95939b06ad13ce8cd08e026ae6e79ea3c5fc80c732d252e2769ce778_bytes32); } + +TEST(state_mpt_hash, withdrawals) +{ + // 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(test::from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0x38cd9ae992a22b94a1582e7d0691dbef56a90cdb36bf7b11d98373f80b102c8f_bytes32); +} + +TEST(state_mpt_hash, withdrawals_bc_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(test::from_json(withdrawal)); + + EXPECT_EQ(mpt_hash(withdrawals), + 0xaa45c53e9f7d6a8362f80876029915da00b1441ef39eb9bbb74f98465ff433ad_bytes32); +} + +TEST(state_mpt_hash, mpt_hashing_test) +{ + // The same data as in 'withdrawals_bc_case' unit test above. + 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 bee2810c6b..d3aa1d24d3 100644 --- a/test/unittests/state_transition_block_test.cpp +++ b/test/unittests/state_transition_block_test.cpp @@ -11,7 +11,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; }