Skip to content

Commit

Permalink
state: Add withdrawalsRoot calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Aug 22, 2023
1 parent 6608cf3 commit 01446c7
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 5 deletions.
9 changes: 9 additions & 0 deletions test/state/mpt_hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@ hash256 mpt_hash(std::span<const TransactionReceipt> receipts)
return trie.hash();
}

hash256 mpt_hash(std::span<const Withdrawal> 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
4 changes: 4 additions & 0 deletions test/state/mpt_hash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<address, Account>& accounts);
Expand All @@ -22,4 +23,7 @@ hash256 mpt_hash(std::span<const Transaction> transactions);
/// Computes Merkle Patricia Trie root hash for the given collection of transactions receipts.
hash256 mpt_hash(std::span<const TransactionReceipt> receipts);

/// Computes Merkle Patricia Trie root hash for the given collection of block withdrawals.
hash256 mpt_hash(std::span<const Withdrawal> withdrawals);

} // namespace evmone::state
6 changes: 6 additions & 0 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,10 @@ std::variant<TransactionReceipt, std::error_code> 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
5 changes: 5 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -184,4 +186,7 @@ std::variant<int64_t, std::error_code> 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
3 changes: 3 additions & 0 deletions test/statetest/statetest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ int64_t from_json<int64_t>(const json::json& j);
template <>
state::BlockInfo from_json<state::BlockInfo>(const json::json& j);

template <>
state::Withdrawal from_json<state::Withdrawal>(const json::json& j);

template <>
state::State from_json<state::State>(const json::json& j);

Expand Down
12 changes: 8 additions & 4 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ inline uint64_t calculate_current_base_fee_eip1559(
return base_fee;
}

template <>
state::Withdrawal from_json<state::Withdrawal>(const json::json& j)
{
return {from_json<uint64_t>(j.at("index")), from_json<uint64_t>(j.at("validatorIndex")),
from_json<evmc::address>(j.at("address")), from_json<uint64_t>(j.at("amount"))};
}

template <>
state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
{
Expand Down Expand Up @@ -175,10 +182,7 @@ state::BlockInfo from_json<state::BlockInfo>(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<evmc::address>(withdrawal.at("address")),
from_json<uint64_t>(withdrawal.at("amount"))});
}
withdrawals.push_back(from_json<state::Withdrawal>(withdrawal));
}

return {from_json<int64_t>(j.at("currentNumber")), from_json<int64_t>(j.at("currentTimestamp")),
Expand Down
3 changes: 3 additions & 0 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
120 changes: 120 additions & 0 deletions test/unittests/state_mpt_hash_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <test/state/mpt_hash.hpp>
#include <test/state/rlp.hpp>
#include <test/state/state.hpp>
#include <test/statetest/statetest.hpp>
#include <array>

using namespace evmone;
Expand Down Expand Up @@ -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<state::Withdrawal> withdrawals;
for (const auto& withdrawal : j)
withdrawals.push_back(test::from_json<state::Withdrawal>(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<state::Withdrawal> withdrawals;
for (const auto& withdrawal : j)
withdrawals.push_back(test::from_json<state::Withdrawal>(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);
}
2 changes: 1 addition & 1 deletion test/unittests/state_transition_block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit 01446c7

Please sign in to comment.