diff --git a/test/state/CMakeLists.txt b/test/state/CMakeLists.txt index 58a169a5c1..1f79ca0c53 100644 --- a/test/state/CMakeLists.txt +++ b/test/state/CMakeLists.txt @@ -7,8 +7,11 @@ add_library(evmone::state ALIAS evmone-state) target_link_libraries(evmone-state PRIVATE evmc::evmc_cpp ethash::keccak) target_sources( evmone-state PRIVATE + account.hpp hash_utils.hpp mpt.hpp mpt.cpp + mpt_hash.hpp + mpt_hash.cpp rlp.hpp ) diff --git a/test/state/account.hpp b/test/state/account.hpp new file mode 100644 index 0000000000..3f7c16d3e0 --- /dev/null +++ b/test/state/account.hpp @@ -0,0 +1,38 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2021 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +namespace evmone::state +{ +using evmc::address; +using evmc::bytes; +using evmc::bytes32; + +/// The representation of the account storage value. +struct StorageValue +{ + /// The current value. + bytes32 current{}; +}; + +/// The state account. +struct Account +{ + /// The account nonce. + uint64_t nonce = 0; + + /// The account balance. + intx::uint256 balance; + + /// The account storage map. + std::unordered_map storage; + + /// The account code. + bytes code; +}; +} // namespace evmone::state diff --git a/test/state/hash_utils.hpp b/test/state/hash_utils.hpp index 8ccebf0a6a..18f3de6b9c 100644 --- a/test/state/hash_utils.hpp +++ b/test/state/hash_utils.hpp @@ -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 diff --git a/test/state/mpt_hash.cpp b/test/state/mpt_hash.cpp new file mode 100644 index 0000000000..439a74f32a --- /dev/null +++ b/test/state/mpt_hash.cpp @@ -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& 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& 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 diff --git a/test/state/mpt_hash.hpp b/test/state/mpt_hash.hpp new file mode 100644 index 0000000000..717bc928a9 --- /dev/null +++ b/test/state/mpt_hash.hpp @@ -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 + +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& accounts); +} // namespace evmone::state diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index acd3b2d304..5700254866 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -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 diff --git a/test/unittests/state_mpt_hash_test.cpp b/test/unittests/state_mpt_hash_test.cpp new file mode 100644 index 0000000000..3684dd86a5 --- /dev/null +++ b/test/unittests/state_mpt_hash_test.cpp @@ -0,0 +1,60 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +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 accounts{{0x02_address, acc}}; + EXPECT_EQ(mpt_hash(accounts), expected); +} + +TEST(state_mpt_hash, two_accounts) +{ + std::unordered_map 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 accounts{{0x07_address, acc}}; + EXPECT_EQ(mpt_hash(accounts), + 0x4e7338c16731491e0fb5d1623f5265c17699c970c816bab71d4d717f6071414d_bytes32); +}