Skip to content

Commit

Permalink
state: Define MPT root hash procedure for accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jun 17, 2022
1 parent 616cc5c commit 6b2a200
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 1 deletion.
2 changes: 2 additions & 0 deletions test/state/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ target_sources(
hash_utils.hpp
mpt.hpp
mpt.cpp
mpt_hash.hpp
mpt_hash.cpp
rlp.hpp
)
4 changes: 3 additions & 1 deletion test/state/hash_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@

namespace evmone
{
using evmc::address;
using evmc::bytes;
using evmc::bytes32;
using evmc::bytes_view;
using namespace evmc::literals;

/// Default type for 256-bit hash.
///
/// Better than ethash::hash256 because has some additional handy constructors.
using hash256 = evmc::bytes32;
using hash256 = bytes32;

/// Computes Keccak hash out of input bytes (wrapper of ethash::keccak256).
inline hash256 keccak256(bytes_view data) noexcept
Expand Down
36 changes: 36 additions & 0 deletions test/state/mpt_hash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "mpt_hash.hpp"
#include "account.hpp"
#include "mpt.hpp"
#include "rlp.hpp"

namespace evmone::state
{
namespace
{
hash256 mpt_hash(const std::unordered_map<hash256, StorageValue>& storage)
{
MPT trie;
for (const auto& [key, value] : storage)
{
if (!is_zero(value.current)) // Skip "deleted" values.
trie.insert(keccak256(key), rlp::encode(rlp::trim(value.current)));
}
return trie.hash();
}
} // namespace

hash256 mpt_hash(const std::unordered_map<address, Account>& accounts)
{
MPT trie;
for (const auto& [addr, acc] : accounts)
{
trie.insert(keccak256(addr),
rlp::encode_tuple(acc.nonce, acc.balance, mpt_hash(acc.storage), keccak256(acc.code)));
}
return trie.hash();
}
} // namespace evmone::state
15 changes: 15 additions & 0 deletions test/state/mpt_hash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include "hash_utils.hpp"
#include <unordered_map>

namespace evmone::state
{
struct Account;

/// Computes Merkle Patricia Trie root hash for the given collection of state accounts.
hash256 mpt_hash(const std::unordered_map<address, Account>& accounts);
} // namespace evmone::state
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_executable(evmone-unittests
evmone_test.cpp
execution_state_test.cpp
instructions_test.cpp
state_mpt_hash_test.cpp
state_mpt_test.cpp
state_rlp_test.cpp
tracing_test.cpp
Expand Down
60 changes: 60 additions & 0 deletions test/unittests/state_mpt_hash_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <test/state/account.hpp>
#include <test/state/mpt.hpp>
#include <test/state/mpt_hash.hpp>

using namespace evmone;
using namespace evmone::state;
using namespace intx;

TEST(state_mpt_hash, empty)
{
EXPECT_EQ(mpt_hash({}), emptyMPTHash);
}

TEST(state_mpt_hash, single_account_v1)
{
// Expected value computed in go-ethereum.
constexpr auto expected =
0x084f337237951e425716a04fb0aaa74111eda9d9c61767f2497697d0a201c92e_bytes32;

Account acc;
acc.balance = 1_u256;
const std::unordered_map<address, Account> accounts{{0x02_address, acc}};
EXPECT_EQ(mpt_hash(accounts), expected);
}

TEST(state_mpt_hash, two_accounts)
{
std::unordered_map<address, Account> accounts;
EXPECT_EQ(mpt_hash(accounts), emptyMPTHash);

accounts[0x00_address] = Account{};
EXPECT_EQ(mpt_hash(accounts),
0x0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785_bytes32);

Account acc2;
acc2.nonce = 1;
acc2.balance = -2_u256;
acc2.code = {0x00};
acc2.storage[0x01_bytes32] = {0xfe_bytes32};
acc2.storage[0x02_bytes32] = {0xfd_bytes32};
accounts[0x01_address] = acc2;
EXPECT_EQ(mpt_hash(accounts),
0xd3e845156fca75de99712281581304fbde104c0fc5a102b09288c07cdde0b666_bytes32);
}

TEST(state_mpt_hash, deleted_storage)
{
Account acc;
acc.storage[0x01_bytes32] = {};
acc.storage[0x02_bytes32] = {0xfd_bytes32};
acc.storage[0x03_bytes32] = {};
const std::unordered_map<address, Account> accounts{{0x07_address, acc}};
EXPECT_EQ(mpt_hash(accounts),
0x4e7338c16731491e0fb5d1623f5265c17699c970c816bab71d4d717f6071414d_bytes32);
}

0 comments on commit 6b2a200

Please sign in to comment.