From 25e18009b45324327f95f3ad457eb666a93ffc32 Mon Sep 17 00:00:00 2001 From: moricho Date: Tue, 3 Dec 2024 10:54:17 +0900 Subject: [PATCH 1/8] feat: Migrate trie account type and state root functions from alloy --- Cargo.toml | 2 ++ src/account.rs | 41 +++++++++++++++++++++++ src/lib.rs | 20 +++++++++--- src/root.rs | 54 +++++++++++++++++++++++++++++-- src/serde/mod.rs | 3 ++ src/serde/quantity.rs | 75 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 src/account.rs create mode 100644 src/serde/mod.rs create mode 100644 src/serde/quantity.rs diff --git a/Cargo.toml b/Cargo.toml index ebd9f2b..d806d0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ derive_more = { version = "1", default-features = false, features = [ "from", "not", ] } +itertools = { version = "0.13", default-features = false } nybbles = { version = "0.2", default-features = false } smallvec = { version = "1.0", default-features = false, features = [ "const_new", @@ -67,6 +68,7 @@ std = [ "alloy-rlp/std", "arrayvec/std", "derive_more/std", + "itertools/use_alloc", "nybbles/std", "tracing/std", "serde?/std", diff --git a/src/account.rs b/src/account.rs new file mode 100644 index 0000000..aef78d8 --- /dev/null +++ b/src/account.rs @@ -0,0 +1,41 @@ +use crate::{EMPTY_ROOT_HASH, KECCAK_EMPTY}; +use alloy_primitives::{keccak256, B256, U256}; +use alloy_rlp::{RlpDecodable, RlpEncodable}; + +#[cfg(feature = "serde")] +use crate::serde::quantity; + +/// Represents an Account in the account trie. +#[derive(Copy, Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable)] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct Account { + /// The account's nonce. + #[cfg_attr(feature = "serde", serde(with = "quantity"))] + pub nonce: u64, + /// The account's balance. + pub balance: U256, + /// The hash of the storage account data. + pub storage_root: B256, + /// The hash of the code of the account. + pub code_hash: B256, +} + +impl Default for Account { + fn default() -> Self { + Self { + nonce: 0, + balance: U256::ZERO, + storage_root: EMPTY_ROOT_HASH, + code_hash: KECCAK_EMPTY, + } + } +} + +impl Account { + /// Compute hash as committed to in the MPT trie without memorizing. + pub fn trie_hash_slow(&self) -> B256 { + keccak256(alloy_rlp::encode(self)) + } +} diff --git a/src/lib.rs b/src/lib.rs index cca2fcd..d07c6e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,12 @@ pub use hash_builder::HashBuilder; pub mod proof; +#[cfg(feature = "serde")] +pub mod serde; + +mod account; +pub use account::Account; + mod mask; pub use mask::TrieMask; @@ -40,12 +46,18 @@ pub use alloy_primitives::map::HashMap; #[doc(no_inline)] pub use nybbles::{self, Nibbles}; +use alloy_primitives::{b256, B256}; + /// Root hash of an empty trie. -pub const EMPTY_ROOT_HASH: alloy_primitives::B256 = - alloy_primitives::b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); +pub const EMPTY_ROOT_HASH: B256 = + b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); + +/// Keccak256 over empty array. +pub const KECCAK_EMPTY: B256 = + b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); #[cfg(test)] -pub(crate) fn triehash_trie_root(iter: I) -> alloy_primitives::B256 +pub(crate) fn triehash_trie_root(iter: I) -> B256 where I: IntoIterator, K: AsRef<[u8]> + Ord, @@ -53,7 +65,7 @@ where { struct Keccak256Hasher; impl hash_db::Hasher for Keccak256Hasher { - type Out = alloy_primitives::B256; + type Out = B256; type StdHasher = plain_hasher::PlainHasher; const LENGTH: usize = 32; diff --git a/src/root.rs b/src/root.rs index c1a52db..3b52b20 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,9 +1,12 @@ -use alloc::vec::Vec; -use alloy_primitives::B256; +use crate::{Account, HashBuilder, EMPTY_ROOT_HASH}; +use alloy_primitives::{keccak256, B256}; use alloy_rlp::Encodable; + +use alloc::vec::Vec; use nybbles::Nibbles; -use crate::{HashBuilder, EMPTY_ROOT_HASH}; +#[cfg(feature = "std")] +use {alloy_primitives::Address, itertools::Itertools}; /// Adjust the index of an item for rlp encoding. pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { @@ -47,3 +50,48 @@ where hb.root() } + +/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state +/// represented as MPT. +/// See [`state_root_unsorted`] for more info. +#[cfg(feature = "std")] +pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( + state: impl IntoIterator, +) -> B256 { + state_root_unsorted( + state.into_iter().map(|(address, account)| (keccak256(address), account.clone())), + ) +} + +/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state +/// represented as MPT. +/// See [`state_root_unsorted`] for more info. +#[cfg(feature = "std")] +pub fn state_root_unhashed>( + state: impl IntoIterator, +) -> B256 { + state_root_unsorted(state.into_iter().map(|(address, account)| (keccak256(address), account))) +} + +/// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. +/// See [`state_root`] for more info. +#[cfg(feature = "std")] +pub fn state_root_unsorted>(state: impl IntoIterator) -> B256 { + state_root(state.into_iter().sorted_unstable_by_key(|(key, _)| *key)) +} + +/// Calculates the root hash of the state represented as MPT. +/// +/// Corresponds to [geth's `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). +/// +/// # Panics +/// +/// If the items are not in sorted order. +pub fn state_root>(state: impl IntoIterator) -> B256 { + let mut hb = HashBuilder::default(); + for (hashed_key, account) in state { + let account_rlp_buf = alloy_rlp::encode(account.into()); + hb.add_leaf(Nibbles::unpack(hashed_key), &account_rlp_buf); + } + hb.root() +} diff --git a/src/serde/mod.rs b/src/serde/mod.rs new file mode 100644 index 0000000..d15d33b --- /dev/null +++ b/src/serde/mod.rs @@ -0,0 +1,3 @@ +//! Serde helpers. + +pub mod quantity; diff --git a/src/serde/quantity.rs b/src/serde/quantity.rs new file mode 100644 index 0000000..6bc0edf --- /dev/null +++ b/src/serde/quantity.rs @@ -0,0 +1,75 @@ +//! Serde functions for encoding primitive numbers using the Ethereum JSON-RPC "quantity" format. +//! +//! This is defined as a "hex encoded unsigned integer", with a special case of 0 being `0x0`. +//! +//! A regex for this format is: `^0x([1-9a-f]+[0-9a-f]*|0)$`. +//! +//! This is only valid for human-readable [`serde`] implementations. +//! For non-human-readable implementations, the format is unspecified. +//! Currently, it uses a fixed-width big-endian byte-array. + +use private::ConvertRuint; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Serializes a primitive number as a "quantity" hex string. +pub fn serialize(value: &T, serializer: S) -> Result +where + T: ConvertRuint, + S: Serializer, +{ + value.into_ruint().serialize(serializer) +} + +/// Deserializes a primitive number from a "quantity" hex string. +pub fn deserialize<'de, T, D>(deserializer: D) -> Result +where + T: ConvertRuint, + D: Deserializer<'de>, +{ + T::Ruint::deserialize(deserializer).map(T::from_ruint) +} + +/// Private implementation details of the [`quantity`](self) module. +#[allow(unnameable_types)] +mod private { + #[doc(hidden)] + pub trait ConvertRuint: Copy + Sized { + // We have to use `Try*` traits because `From` is not implemented by ruint types. + // They shouldn't ever error. + type Ruint: Copy + + serde::Serialize + + serde::de::DeserializeOwned + + TryFrom + + TryInto; + + #[inline] + fn into_ruint(self) -> Self::Ruint { + self.try_into().ok().unwrap() + } + + #[inline] + fn from_ruint(ruint: Self::Ruint) -> Self { + ruint.try_into().ok().unwrap() + } + } + + macro_rules! impl_from_ruint { + ($($primitive:ty = $ruint:ty),* $(,)?) => { + $( + impl ConvertRuint for $primitive { + type Ruint = $ruint; + } + )* + }; + } + + impl_from_ruint! { + bool = alloy_primitives::ruint::aliases::U1, + u8 = alloy_primitives::U8, + u16 = alloy_primitives::U16, + u32 = alloy_primitives::U32, + u64 = alloy_primitives::U64, + u128 = alloy_primitives::U128, + } +} From d2d6cb9f15aa79831e5d5c909b09f648f0116bb8 Mon Sep 17 00:00:00 2001 From: moricho Date: Tue, 3 Dec 2024 20:41:38 +0900 Subject: [PATCH 2/8] Rename to TrieAccount / Use simple u64 serialize/deserialize --- src/account.rs | 33 +++++++++++++++---- src/lib.rs | 5 +-- src/root.rs | 12 ++++--- src/serde/mod.rs | 3 -- src/serde/quantity.rs | 75 ------------------------------------------- 5 files changed, 34 insertions(+), 94 deletions(-) delete mode 100644 src/serde/mod.rs delete mode 100644 src/serde/quantity.rs diff --git a/src/account.rs b/src/account.rs index aef78d8..210d316 100644 --- a/src/account.rs +++ b/src/account.rs @@ -2,15 +2,12 @@ use crate::{EMPTY_ROOT_HASH, KECCAK_EMPTY}; use alloy_primitives::{keccak256, B256, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; -#[cfg(feature = "serde")] -use crate::serde::quantity; - -/// Represents an Account in the account trie. +/// Represents an TrieAccount in the account trie. #[derive(Copy, Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable)] #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -pub struct Account { +pub struct TrieAccount { /// The account's nonce. #[cfg_attr(feature = "serde", serde(with = "quantity"))] pub nonce: u64, @@ -22,7 +19,7 @@ pub struct Account { pub code_hash: B256, } -impl Default for Account { +impl Default for TrieAccount { fn default() -> Self { Self { nonce: 0, @@ -33,9 +30,31 @@ impl Default for Account { } } -impl Account { +impl TrieAccount { /// Compute hash as committed to in the MPT trie without memorizing. pub fn trie_hash_slow(&self) -> B256 { keccak256(alloy_rlp::encode(self)) } } + +#[cfg(feature = "serde")] +mod quantity { + use alloy_primitives::U64; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Serializes a primitive number as a "quantity" hex string. + pub(crate) fn serialize(value: &u64, serializer: S) -> Result + where + S: Serializer, + { + U64::from(*value).serialize(serializer) + } + + /// Deserializes a primitive number from a "quantity" hex string. + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + U64::deserialize(deserializer).map(|value| value.to()) + } +} diff --git a/src/lib.rs b/src/lib.rs index d07c6e9..da86629 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,11 +28,8 @@ pub use hash_builder::HashBuilder; pub mod proof; -#[cfg(feature = "serde")] -pub mod serde; - mod account; -pub use account::Account; +pub use account::TrieAccount; mod mask; pub use mask::TrieMask; diff --git a/src/root.rs b/src/root.rs index 3b52b20..f34a5e7 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,4 +1,4 @@ -use crate::{Account, HashBuilder, EMPTY_ROOT_HASH}; +use crate::{HashBuilder, TrieAccount, EMPTY_ROOT_HASH}; use alloy_primitives::{keccak256, B256}; use alloy_rlp::Encodable; @@ -55,7 +55,7 @@ where /// represented as MPT. /// See [`state_root_unsorted`] for more info. #[cfg(feature = "std")] -pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( +pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( state: impl IntoIterator, ) -> B256 { state_root_unsorted( @@ -67,7 +67,7 @@ pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( /// represented as MPT. /// See [`state_root_unsorted`] for more info. #[cfg(feature = "std")] -pub fn state_root_unhashed>( +pub fn state_root_unhashed>( state: impl IntoIterator, ) -> B256 { state_root_unsorted(state.into_iter().map(|(address, account)| (keccak256(address), account))) @@ -76,7 +76,9 @@ pub fn state_root_unhashed>( /// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. /// See [`state_root`] for more info. #[cfg(feature = "std")] -pub fn state_root_unsorted>(state: impl IntoIterator) -> B256 { +pub fn state_root_unsorted>( + state: impl IntoIterator, +) -> B256 { state_root(state.into_iter().sorted_unstable_by_key(|(key, _)| *key)) } @@ -87,7 +89,7 @@ pub fn state_root_unsorted>(state: impl IntoIterator>(state: impl IntoIterator) -> B256 { +pub fn state_root>(state: impl IntoIterator) -> B256 { let mut hb = HashBuilder::default(); for (hashed_key, account) in state { let account_rlp_buf = alloy_rlp::encode(account.into()); diff --git a/src/serde/mod.rs b/src/serde/mod.rs deleted file mode 100644 index d15d33b..0000000 --- a/src/serde/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Serde helpers. - -pub mod quantity; diff --git a/src/serde/quantity.rs b/src/serde/quantity.rs deleted file mode 100644 index 6bc0edf..0000000 --- a/src/serde/quantity.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Serde functions for encoding primitive numbers using the Ethereum JSON-RPC "quantity" format. -//! -//! This is defined as a "hex encoded unsigned integer", with a special case of 0 being `0x0`. -//! -//! A regex for this format is: `^0x([1-9a-f]+[0-9a-f]*|0)$`. -//! -//! This is only valid for human-readable [`serde`] implementations. -//! For non-human-readable implementations, the format is unspecified. -//! Currently, it uses a fixed-width big-endian byte-array. - -use private::ConvertRuint; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -/// Serializes a primitive number as a "quantity" hex string. -pub fn serialize(value: &T, serializer: S) -> Result -where - T: ConvertRuint, - S: Serializer, -{ - value.into_ruint().serialize(serializer) -} - -/// Deserializes a primitive number from a "quantity" hex string. -pub fn deserialize<'de, T, D>(deserializer: D) -> Result -where - T: ConvertRuint, - D: Deserializer<'de>, -{ - T::Ruint::deserialize(deserializer).map(T::from_ruint) -} - -/// Private implementation details of the [`quantity`](self) module. -#[allow(unnameable_types)] -mod private { - #[doc(hidden)] - pub trait ConvertRuint: Copy + Sized { - // We have to use `Try*` traits because `From` is not implemented by ruint types. - // They shouldn't ever error. - type Ruint: Copy - + serde::Serialize - + serde::de::DeserializeOwned - + TryFrom - + TryInto; - - #[inline] - fn into_ruint(self) -> Self::Ruint { - self.try_into().ok().unwrap() - } - - #[inline] - fn from_ruint(ruint: Self::Ruint) -> Self { - ruint.try_into().ok().unwrap() - } - } - - macro_rules! impl_from_ruint { - ($($primitive:ty = $ruint:ty),* $(,)?) => { - $( - impl ConvertRuint for $primitive { - type Ruint = $ruint; - } - )* - }; - } - - impl_from_ruint! { - bool = alloy_primitives::ruint::aliases::U1, - u8 = alloy_primitives::U8, - u16 = alloy_primitives::U16, - u32 = alloy_primitives::U32, - u64 = alloy_primitives::U64, - u128 = alloy_primitives::U128, - } -} From c45d86eda4175f3833e811137734de9ea11c42a0 Mon Sep 17 00:00:00 2001 From: moricho Date: Tue, 3 Dec 2024 20:45:07 +0900 Subject: [PATCH 3/8] fix cargo hack warnings --- Cargo.toml | 2 +- src/root.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d806d0f..5b0c8fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ derive_more = { version = "1", default-features = false, features = [ "from", "not", ] } -itertools = { version = "0.13", default-features = false } +itertools = { version = "0.13", default-features = false, optional = true } nybbles = { version = "0.2", default-features = false } smallvec = { version = "1.0", default-features = false, features = [ "const_new", diff --git a/src/root.rs b/src/root.rs index f34a5e7..e5a7d12 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,12 +1,15 @@ use crate::{HashBuilder, TrieAccount, EMPTY_ROOT_HASH}; -use alloy_primitives::{keccak256, B256}; +use alloy_primitives::B256; use alloy_rlp::Encodable; use alloc::vec::Vec; use nybbles::Nibbles; #[cfg(feature = "std")] -use {alloy_primitives::Address, itertools::Itertools}; +use { + alloy_primitives::{keccak256, Address}, + itertools::Itertools, +}; /// Adjust the index of an item for rlp encoding. pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { From b87c74c8ae07bd4f7c419555be0a16321fd7fd53 Mon Sep 17 00:00:00 2001 From: moricho Date: Tue, 3 Dec 2024 23:32:08 +0900 Subject: [PATCH 4/8] Remove itertools and handle sort internally --- Cargo.toml | 2 -- src/root.rs | 15 ++++----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b0c8fb..ebd9f2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ derive_more = { version = "1", default-features = false, features = [ "from", "not", ] } -itertools = { version = "0.13", default-features = false, optional = true } nybbles = { version = "0.2", default-features = false } smallvec = { version = "1.0", default-features = false, features = [ "const_new", @@ -68,7 +67,6 @@ std = [ "alloy-rlp/std", "arrayvec/std", "derive_more/std", - "itertools/use_alloc", "nybbles/std", "tracing/std", "serde?/std", diff --git a/src/root.rs b/src/root.rs index e5a7d12..cab4fd3 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,16 +1,10 @@ use crate::{HashBuilder, TrieAccount, EMPTY_ROOT_HASH}; -use alloy_primitives::B256; +use alloy_primitives::{keccak256, Address, B256}; use alloy_rlp::Encodable; use alloc::vec::Vec; use nybbles::Nibbles; -#[cfg(feature = "std")] -use { - alloy_primitives::{keccak256, Address}, - itertools::Itertools, -}; - /// Adjust the index of an item for rlp encoding. pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { if i > 0x7f { @@ -57,7 +51,6 @@ where /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state /// represented as MPT. /// See [`state_root_unsorted`] for more info. -#[cfg(feature = "std")] pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( state: impl IntoIterator, ) -> B256 { @@ -69,7 +62,6 @@ pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state /// represented as MPT. /// See [`state_root_unsorted`] for more info. -#[cfg(feature = "std")] pub fn state_root_unhashed>( state: impl IntoIterator, ) -> B256 { @@ -78,11 +70,12 @@ pub fn state_root_unhashed>( /// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. /// See [`state_root`] for more info. -#[cfg(feature = "std")] pub fn state_root_unsorted>( state: impl IntoIterator, ) -> B256 { - state_root(state.into_iter().sorted_unstable_by_key(|(key, _)| *key)) + let mut vec = Vec::from_iter(state); + vec.sort_unstable_by_key(|(key, _)| *key); + state_root(vec) } /// Calculates the root hash of the state represented as MPT. From bfbeb2284f0da2e0aa77bc8e6a85af016c290485 Mon Sep 17 00:00:00 2001 From: moricho Date: Wed, 4 Dec 2024 00:18:05 +0900 Subject: [PATCH 5/8] fix cfg_attr for TrieAccount --- src/account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.rs b/src/account.rs index 210d316..17fd66a 100644 --- a/src/account.rs +++ b/src/account.rs @@ -4,7 +4,7 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; /// Represents an TrieAccount in the account trie. #[derive(Copy, Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable)] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct TrieAccount { From 7f97f9ce6cdd1e92886ad1b50477a94d639e46fc Mon Sep 17 00:00:00 2001 From: moricho Date: Wed, 4 Dec 2024 23:22:38 +0900 Subject: [PATCH 6/8] Feature gate TrieAccount related code behind `ethereum` --- Cargo.toml | 1 + src/lib.rs | 2 ++ src/root.rs | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ebd9f2b..41855c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ arbitrary = [ "alloy-primitives/arbitrary", "nybbles/arbitrary", ] +ethereum = [] [[bench]] name = "bench" diff --git a/src/lib.rs b/src/lib.rs index da86629..da4ba04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,9 @@ pub use hash_builder::HashBuilder; pub mod proof; +#[cfg(feature = "ethereum")] mod account; +#[cfg(feature = "ethereum")] pub use account::TrieAccount; mod mask; diff --git a/src/root.rs b/src/root.rs index cab4fd3..bac1a55 100644 --- a/src/root.rs +++ b/src/root.rs @@ -51,6 +51,7 @@ where /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state /// represented as MPT. /// See [`state_root_unsorted`] for more info. +#[cfg(feature = "ethereum")] pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( state: impl IntoIterator, ) -> B256 { @@ -62,6 +63,7 @@ pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state /// represented as MPT. /// See [`state_root_unsorted`] for more info. +#[cfg(feature = "ethereum")] pub fn state_root_unhashed>( state: impl IntoIterator, ) -> B256 { @@ -70,6 +72,7 @@ pub fn state_root_unhashed>( /// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. /// See [`state_root`] for more info. +#[cfg(feature = "ethereum")] pub fn state_root_unsorted>( state: impl IntoIterator, ) -> B256 { @@ -85,6 +88,7 @@ pub fn state_root_unsorted>( /// # Panics /// /// If the items are not in sorted order. +#[cfg(feature = "ethereum")] pub fn state_root>(state: impl IntoIterator) -> B256 { let mut hb = HashBuilder::default(); for (hashed_key, account) in state { From d70e8433235e45ef79be1a6425459e70dbb1e6e7 Mon Sep 17 00:00:00 2001 From: moricho Date: Wed, 4 Dec 2024 23:26:09 +0900 Subject: [PATCH 7/8] fix import --- src/root.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/root.rs b/src/root.rs index bac1a55..585a12b 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,6 +1,11 @@ -use crate::{HashBuilder, TrieAccount, EMPTY_ROOT_HASH}; -use alloy_primitives::{keccak256, Address, B256}; +use crate::{HashBuilder, EMPTY_ROOT_HASH}; +use alloy_primitives::B256; use alloy_rlp::Encodable; +#[cfg(feature = "ethereum")] +use { + crate::TrieAccount, + alloy_primitives::{keccak256, Address}, +}; use alloc::vec::Vec; use nybbles::Nibbles; From a93080cd17112d34481c05fbb8e5db3944e8db5b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 4 Dec 2024 16:34:22 +0100 Subject: [PATCH 8/8] chore: move into ethereum crate --- src/root.rs | 102 ++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/root.rs b/src/root.rs index 585a12b..7703b71 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,13 +1,7 @@ use crate::{HashBuilder, EMPTY_ROOT_HASH}; +use alloc::vec::Vec; use alloy_primitives::B256; use alloy_rlp::Encodable; -#[cfg(feature = "ethereum")] -use { - crate::TrieAccount, - alloy_primitives::{keccak256, Address}, -}; - -use alloc::vec::Vec; use nybbles::Nibbles; /// Adjust the index of an item for rlp encoding. @@ -53,52 +47,60 @@ where hb.root() } -/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state -/// represented as MPT. -/// See [`state_root_unsorted`] for more info. +/// Ethereum specific trie root functions. #[cfg(feature = "ethereum")] -pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( - state: impl IntoIterator, -) -> B256 { - state_root_unsorted( - state.into_iter().map(|(address, account)| (keccak256(address), account.clone())), - ) -} - -/// Hashes and sorts account keys, then proceeds to calculating the root hash of the state -/// represented as MPT. -/// See [`state_root_unsorted`] for more info. +pub use ethereum::*; #[cfg(feature = "ethereum")] -pub fn state_root_unhashed>( - state: impl IntoIterator, -) -> B256 { - state_root_unsorted(state.into_iter().map(|(address, account)| (keccak256(address), account))) -} +mod ethereum { + use super::*; + use crate::TrieAccount; + use alloy_primitives::{keccak256, Address}; -/// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. -/// See [`state_root`] for more info. -#[cfg(feature = "ethereum")] -pub fn state_root_unsorted>( - state: impl IntoIterator, -) -> B256 { - let mut vec = Vec::from_iter(state); - vec.sort_unstable_by_key(|(key, _)| *key); - state_root(vec) -} + /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state + /// represented as MPT. + /// See [`state_root_unsorted`] for more info. + pub fn state_root_ref_unhashed<'a, A: Into + Clone + 'a>( + state: impl IntoIterator, + ) -> B256 { + state_root_unsorted( + state.into_iter().map(|(address, account)| (keccak256(address), account.clone())), + ) + } -/// Calculates the root hash of the state represented as MPT. -/// -/// Corresponds to [geth's `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). -/// -/// # Panics -/// -/// If the items are not in sorted order. -#[cfg(feature = "ethereum")] -pub fn state_root>(state: impl IntoIterator) -> B256 { - let mut hb = HashBuilder::default(); - for (hashed_key, account) in state { - let account_rlp_buf = alloy_rlp::encode(account.into()); - hb.add_leaf(Nibbles::unpack(hashed_key), &account_rlp_buf); + /// Hashes and sorts account keys, then proceeds to calculating the root hash of the state + /// represented as MPT. + /// See [`state_root_unsorted`] for more info. + pub fn state_root_unhashed>( + state: impl IntoIterator, + ) -> B256 { + state_root_unsorted( + state.into_iter().map(|(address, account)| (keccak256(address), account)), + ) + } + + /// Sorts the hashed account keys and calculates the root hash of the state represented as MPT. + /// See [`state_root`] for more info. + pub fn state_root_unsorted>( + state: impl IntoIterator, + ) -> B256 { + let mut vec = Vec::from_iter(state); + vec.sort_unstable_by_key(|(key, _)| *key); + state_root(vec) + } + + /// Calculates the root hash of the state represented as MPT. + /// + /// Corresponds to [geth's `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). + /// + /// # Panics + /// + /// If the items are not in sorted order. + pub fn state_root>(state: impl IntoIterator) -> B256 { + let mut hb = HashBuilder::default(); + for (hashed_key, account) in state { + let account_rlp_buf = alloy_rlp::encode(account.into()); + hb.add_leaf(Nibbles::unpack(hashed_key), &account_rlp_buf); + } + hb.root() } - hb.root() }