chore: move primitives/trie to reth-trie-types (#8717)

This commit is contained in:
joshieDo
2024-06-10 17:00:14 +02:00
committed by GitHub
parent 76a1a3d005
commit b16a6ec029
59 changed files with 283 additions and 234 deletions

View File

@ -3,10 +3,7 @@ use alloy_rlp::{BufMut, Encodable};
use itertools::Itertools;
use reth_db_api::database::Database;
use reth_execution_errors::StorageRootError;
use reth_primitives::{
trie::{HashBuilder, Nibbles, TrieAccount},
B256,
};
use reth_primitives::{proofs::IntoTrieAccount, B256};
use reth_provider::{providers::ConsistentDbView, DatabaseProviderFactory, ProviderError};
use reth_tasks::pool::BlockingTaskPool;
use reth_trie::{
@ -15,7 +12,7 @@ use reth_trie::{
trie_cursor::TrieCursorFactory,
updates::TrieUpdates,
walker::TrieWalker,
HashedPostState, StorageRoot,
HashBuilder, HashedPostState, Nibbles, StorageRoot,
};
use std::{collections::HashMap, sync::Arc};
use thiserror::Error;
@ -173,7 +170,7 @@ where
}
account_rlp.clear();
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
account.encode(&mut account_rlp as &mut dyn BufMut);
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
}

View File

@ -3,10 +3,7 @@ use alloy_rlp::{BufMut, Encodable};
use rayon::prelude::*;
use reth_db_api::database::Database;
use reth_execution_errors::StorageRootError;
use reth_primitives::{
trie::{HashBuilder, Nibbles, TrieAccount},
B256,
};
use reth_primitives::{proofs::IntoTrieAccount, B256};
use reth_provider::{providers::ConsistentDbView, DatabaseProviderFactory, ProviderError};
use reth_trie::{
hashed_cursor::{HashedCursorFactory, HashedPostStateCursorFactory},
@ -14,7 +11,7 @@ use reth_trie::{
trie_cursor::TrieCursorFactory,
updates::TrieUpdates,
walker::TrieWalker,
HashedPostState, StorageRoot,
HashBuilder, HashedPostState, Nibbles, StorageRoot,
};
use std::collections::HashMap;
use thiserror::Error;
@ -155,7 +152,7 @@ where
}
account_rlp.clear();
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
account.encode(&mut account_rlp as &mut dyn BufMut);
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
}

View File

@ -17,6 +17,7 @@ reth-primitives.workspace = true
reth-execution-errors.workspace = true
reth-db.workspace = true
reth-db-api.workspace = true
reth-trie-types.workspace = true
revm.workspace = true

View File

@ -7,8 +7,7 @@ use proptest::{
strategy::ValueTree,
test_runner::{basic_result_cache, TestRunner},
};
use reth_primitives::trie::Nibbles;
use reth_trie::prefix_set::PrefixSetMut;
use reth_trie::{prefix_set::PrefixSetMut, Nibbles};
use std::collections::BTreeSet;
/// Abstractions used for benching

View File

@ -50,6 +50,9 @@ pub use progress::{IntermediateStateRootState, StateRootProgress};
/// Trie calculation stats.
pub mod stats;
// re-export for convenience
pub use reth_trie_types::*;
/// Trie calculation metrics.
#[cfg(feature = "metrics")]
pub mod metrics;

View File

@ -1,6 +1,6 @@
use crate::{hashed_cursor::HashedCursor, trie_cursor::TrieCursor, walker::TrieWalker};
use crate::{hashed_cursor::HashedCursor, trie_cursor::TrieCursor, walker::TrieWalker, Nibbles};
use reth_db::DatabaseError;
use reth_primitives::{trie::Nibbles, B256};
use reth_primitives::B256;
/// Represents a branch node in the trie.
#[derive(Debug)]

View File

@ -1,4 +1,5 @@
use super::{PrefixSetMut, TriePrefixSets};
use crate::Nibbles;
use derive_more::Deref;
use reth_db::tables;
use reth_db_api::{
@ -7,12 +8,11 @@ use reth_db_api::{
transaction::DbTx,
DatabaseError,
};
use reth_primitives::{keccak256, trie::Nibbles, BlockNumber, StorageEntry, B256};
use reth_primitives::{keccak256, BlockNumber, StorageEntry, B256};
use std::{
collections::{HashMap, HashSet},
ops::RangeInclusive,
};
/// A wrapper around a database transaction that loads prefix sets within a given block range.
#[derive(Deref, Debug)]
pub struct PrefixSetLoader<'a, TX>(&'a TX);

View File

@ -1,4 +1,5 @@
use reth_primitives::{trie::Nibbles, B256};
use crate::Nibbles;
use reth_primitives::B256;
use std::{
collections::{HashMap, HashSet},
sync::Arc,
@ -37,8 +38,7 @@ pub struct TriePrefixSets {
/// # Examples
///
/// ```
/// use reth_primitives::trie::Nibbles;
/// use reth_trie::prefix_set::PrefixSetMut;
/// use reth_trie::{prefix_set::PrefixSetMut, Nibbles};
///
/// let mut prefix_set = PrefixSetMut::default();
/// prefix_set.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb]));

View File

@ -1,5 +1,5 @@
use crate::{trie_cursor::CursorSubNode, updates::TrieUpdates};
use reth_primitives::{stage::MerkleCheckpoint, trie::hash_builder::HashBuilder, B256};
use crate::{hash_builder::HashBuilder, trie_cursor::CursorSubNode, updates::TrieUpdates};
use reth_primitives::{stage::MerkleCheckpoint, B256};
/// The progress of the state root computation.
#[derive(Debug)]

View File

@ -4,6 +4,7 @@ use crate::{
prefix_set::PrefixSetMut,
trie_cursor::{DatabaseAccountTrieCursor, DatabaseStorageTrieCursor},
walker::TrieWalker,
HashBuilder, Nibbles,
};
use alloy_rlp::{BufMut, Encodable};
use reth_db::tables;
@ -12,10 +13,10 @@ use reth_execution_errors::{StateRootError, StorageRootError};
use reth_primitives::{
constants::EMPTY_ROOT_HASH,
keccak256,
trie::{proof::ProofRetainer, AccountProof, HashBuilder, Nibbles, StorageProof, TrieAccount},
proofs::{AccountProof, IntoTrieAccount, StorageProof},
Address, B256,
};
use reth_trie_types::proof::ProofRetainer;
/// A struct for generating merkle proofs.
///
/// Proof generator adds the target address and slots to the prefix set, enables the proof retainer
@ -82,7 +83,7 @@ where
};
account_rlp.clear();
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
account.encode(&mut account_rlp as &mut dyn BufMut);
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);

View File

@ -2,7 +2,7 @@ use crate::{
hashed_cursor::HashedPostStateCursorFactory,
prefix_set::{PrefixSetMut, TriePrefixSets},
updates::TrieUpdates,
StateRoot,
Nibbles, StateRoot,
};
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use reth_db::{tables, DatabaseError};
@ -13,8 +13,7 @@ use reth_db_api::{
};
use reth_execution_errors::StateRootError;
use reth_primitives::{
keccak256, revm::compat::into_reth_acc, trie::Nibbles, Account, Address, BlockNumber, B256,
U256,
keccak256, revm::compat::into_reth_acc, Account, Address, BlockNumber, B256, U256,
};
use revm::db::BundleAccount;
use std::{

View File

@ -1,6 +1,7 @@
use alloy_rlp::encode_fixed_size;
use reth_primitives::{
proofs::triehash::KeccakHasher, trie::TrieAccount, Account, Address, B256, U256,
proofs::{triehash::KeccakHasher, IntoTrieAccount},
Account, Address, B256, U256,
};
/// Re-export of [triehash].
@ -14,7 +15,7 @@ where
{
let encoded_accounts = accounts.into_iter().map(|(address, (account, storage))| {
let storage_root = storage_root(storage);
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
(address, alloy_rlp::encode(account))
});
triehash::sec_trie_root::<KeccakHasher, _, _, _>(encoded_accounts)
@ -35,7 +36,7 @@ where
{
let encoded_accounts = accounts.into_iter().map(|(address, (account, storage))| {
let storage_root = storage_root_prehashed(storage);
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
(address, alloy_rlp::encode(account))
});

View File

@ -7,15 +7,13 @@ use crate::{
trie_cursor::TrieCursorFactory,
updates::{TrieKey, TrieOp, TrieUpdates},
walker::TrieWalker,
HashBuilder, Nibbles,
};
use alloy_rlp::{BufMut, Encodable};
use reth_db_api::transaction::DbTx;
use reth_execution_errors::{StateRootError, StorageRootError};
use reth_primitives::{
constants::EMPTY_ROOT_HASH,
keccak256,
trie::{HashBuilder, Nibbles, TrieAccount},
Address, BlockNumber, B256,
constants::EMPTY_ROOT_HASH, keccak256, proofs::IntoTrieAccount, Address, BlockNumber, B256,
};
use std::ops::RangeInclusive;
use tracing::{debug, trace};
@ -284,7 +282,7 @@ where
};
account_rlp.clear();
let account = TrieAccount::from((account, storage_root));
let account = IntoTrieAccount::to_trie_account((account, storage_root));
account.encode(&mut account_rlp as &mut dyn BufMut);
hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
@ -552,6 +550,7 @@ mod tests {
use crate::{
prefix_set::PrefixSetMut,
test_utils::{state_root, state_root_prehashed, storage_root, storage_root_prehashed},
BranchNodeCompact, TrieMask,
};
use proptest::{prelude::ProptestConfig, proptest};
use reth_db::{tables, test_utils::TempDatabase, DatabaseEnv};
@ -560,10 +559,7 @@ mod tests {
transaction::DbTxMut,
};
use reth_primitives::{
hex_literal::hex,
proofs::triehash::KeccakHasher,
trie::{BranchNodeCompact, TrieMask},
Account, StorageEntry, U256,
hex_literal::hex, proofs::triehash::KeccakHasher, Account, StorageEntry, U256,
};
use reth_provider::{test_utils::create_test_provider_factory, DatabaseProviderRW};
use std::{
@ -832,7 +828,8 @@ mod tests {
}
fn encode_account(account: Account, storage_root: Option<B256>) -> Vec<u8> {
let account = TrieAccount::from((account, storage_root.unwrap_or(EMPTY_ROOT_HASH)));
let account =
IntoTrieAccount::to_trie_account((account, storage_root.unwrap_or(EMPTY_ROOT_HASH)));
let mut account_rlp = Vec::with_capacity(account.length());
account.encode(&mut account_rlp);
account_rlp

View File

@ -1,14 +1,11 @@
use super::{TrieCursor, TrieCursorFactory};
use crate::updates::TrieKey;
use crate::{updates::TrieKey, BranchNodeCompact, Nibbles, StoredNibbles, StoredNibblesSubKey};
use reth_db::{tables, DatabaseError};
use reth_db_api::{
cursor::{DbCursorRO, DbDupCursorRO},
transaction::DbTx,
};
use reth_primitives::{
trie::{BranchNodeCompact, Nibbles, StoredNibbles, StoredNibblesSubKey},
B256,
};
use reth_primitives::B256;
/// Implementation of the trie cursor factory for a database transaction.
impl<'a, TX: DbTx> TrieCursorFactory for &'a TX {
@ -116,11 +113,9 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{StorageTrieEntry, StoredBranchNode};
use reth_db_api::{cursor::DbCursorRW, transaction::DbTxMut};
use reth_primitives::{
hex_literal::hex,
trie::{StorageTrieEntry, StoredBranchNode},
};
use reth_primitives::hex_literal::hex;
use reth_provider::test_utils::create_test_provider_factory;
#[test]

View File

@ -1,10 +1,6 @@
use crate::updates::TrieKey;
use crate::{updates::TrieKey, BranchNodeCompact, Nibbles};
use reth_db::DatabaseError;
use reth_primitives::{
trie::{BranchNodeCompact, Nibbles},
B256,
};
use reth_primitives::B256;
mod database_cursors;
mod subnode;

View File

@ -1,7 +1,6 @@
use super::{TrieCursor, TrieCursorFactory};
use crate::updates::TrieKey;
use crate::{updates::TrieKey, BranchNodeCompact, Nibbles};
use reth_db::DatabaseError;
use reth_primitives::trie::{BranchNodeCompact, Nibbles};
/// Noop trie cursor factory.
#[derive(Default, Debug)]

View File

@ -1,7 +1,5 @@
use reth_primitives::{
trie::{nodes::CHILD_INDEX_RANGE, BranchNodeCompact, Nibbles, StoredSubNode},
B256,
};
use crate::{nodes::CHILD_INDEX_RANGE, BranchNodeCompact, Nibbles, StoredSubNode};
use reth_primitives::B256;
/// Cursor for iterating over a subtrie.
#[derive(Clone)]

View File

@ -1,17 +1,14 @@
use crate::walker::TrieWalker;
use crate::{
walker::TrieWalker, BranchNodeCompact, HashBuilder, Nibbles, StorageTrieEntry,
StoredBranchNode, StoredNibbles, StoredNibblesSubKey,
};
use derive_more::Deref;
use reth_db::tables;
use reth_db_api::{
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
transaction::{DbTx, DbTxMut},
};
use reth_primitives::{
trie::{
BranchNodeCompact, HashBuilder, Nibbles, StorageTrieEntry, StoredBranchNode, StoredNibbles,
StoredNibblesSubKey,
},
B256,
};
use reth_primitives::B256;
use std::collections::{hash_map::IntoIter, HashMap, HashSet};
/// The key of a trie node.

View File

@ -2,12 +2,10 @@ use crate::{
prefix_set::PrefixSet,
trie_cursor::{CursorSubNode, TrieCursor},
updates::TrieUpdates,
BranchNodeCompact, Nibbles,
};
use reth_db::DatabaseError;
use reth_primitives::{
trie::{BranchNodeCompact, Nibbles},
B256,
};
use reth_primitives::B256;
/// `TrieWalker` is a structure that enables traversal of a Merkle trie.
/// It allows moving through the trie in a depth-first manner, skipping certain branches
@ -249,10 +247,10 @@ mod tests {
use crate::{
prefix_set::PrefixSetMut,
trie_cursor::{DatabaseAccountTrieCursor, DatabaseStorageTrieCursor},
StorageTrieEntry, StoredBranchNode,
};
use reth_db::tables;
use reth_db_api::{cursor::DbCursorRW, transaction::DbTxMut};
use reth_primitives::trie::{StorageTrieEntry, StoredBranchNode};
use reth_provider::test_utils::create_test_provider_factory;
#[test]

View File

@ -0,0 +1,33 @@
[package]
name = "reth-trie-types"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "Commonly used types for trie usage in reth."
[lints]
workspace = true
[dependencies]
reth-codecs.workspace = true
alloy-primitives.workspace = true
alloy-rlp = { workspace = true, features = ["arrayvec"] }
alloy-trie = { workspace = true, features = ["serde"] }
bytes.workspace = true
derive_more.workspace = true
serde.workspace = true
nybbles = { workspace = true, features = ["serde", "rlp"] }
[dev-dependencies]
arbitrary = { workspace = true, features = ["derive"] }
assert_matches.workspace = true
proptest.workspace = true
proptest-derive.workspace = true
serde_json.workspace = true
test-fuzz.workspace = true
toml.workspace = true

View File

@ -0,0 +1,22 @@
use alloy_primitives::{B256, U256};
use alloy_rlp::{RlpDecodable, RlpEncodable};
/// An Ethereum account as represented in the trie.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)]
pub struct TrieAccount {
/// Account nonce.
pub nonce: u64,
/// Account balance.
pub balance: U256,
/// Account's storage root.
pub storage_root: B256,
/// Hash of the account's bytecode.
pub code_hash: B256,
}
impl TrieAccount {
/// Get account's storage root.
pub const fn storage_root(&self) -> B256 {
self.storage_root
}
}

View File

@ -0,0 +1,9 @@
//! MPT hash builder implementation.
mod state;
pub use state::HashBuilderState;
mod value;
pub(crate) use value::StoredHashBuilderValue;
pub use alloy_trie::hash_builder::*;

View File

@ -0,0 +1,171 @@
use super::StoredHashBuilderValue;
use crate::{StoredTrieMask, TrieMask};
use alloy_trie::{hash_builder::HashBuilderValue, HashBuilder};
use bytes::Buf;
use nybbles::Nibbles;
use reth_codecs::{derive_arbitrary, Compact};
use serde::{Deserialize, Serialize};
/// The hash builder state for storing in the database.
/// Check the `reth-trie` crate for more info on hash builder.
#[derive_arbitrary(compact)]
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct HashBuilderState {
/// The current key.
pub key: Vec<u8>,
/// The builder stack.
pub stack: Vec<Vec<u8>>,
/// The current node value.
pub value: HashBuilderValue,
/// Group masks.
pub groups: Vec<TrieMask>,
/// Tree masks.
pub tree_masks: Vec<TrieMask>,
/// Hash masks.
pub hash_masks: Vec<TrieMask>,
/// Flag indicating if the current node is stored in the database.
pub stored_in_database: bool,
}
impl From<HashBuilderState> for HashBuilder {
fn from(state: HashBuilderState) -> Self {
Self {
key: Nibbles::from_nibbles_unchecked(state.key),
stack: state.stack,
value: state.value,
groups: state.groups,
tree_masks: state.tree_masks,
hash_masks: state.hash_masks,
stored_in_database: state.stored_in_database,
updated_branch_nodes: None,
proof_retainer: None,
rlp_buf: Vec::with_capacity(32),
}
}
}
impl From<HashBuilder> for HashBuilderState {
fn from(state: HashBuilder) -> Self {
Self {
key: state.key.into(),
stack: state.stack,
value: state.value,
groups: state.groups,
tree_masks: state.tree_masks,
hash_masks: state.hash_masks,
stored_in_database: state.stored_in_database,
}
}
}
impl Compact for HashBuilderState {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
let mut len = 0;
len += self.key.to_compact(buf);
buf.put_u16(self.stack.len() as u16);
len += 2;
for item in &self.stack {
buf.put_u16(item.len() as u16);
buf.put_slice(&item[..]);
len += 2 + item.len();
}
len += StoredHashBuilderValue(self.value).to_compact(buf);
buf.put_u16(self.groups.len() as u16);
len += 2;
for item in &self.groups {
len += StoredTrieMask(*item).to_compact(buf);
}
buf.put_u16(self.tree_masks.len() as u16);
len += 2;
for item in &self.tree_masks {
len += StoredTrieMask(*item).to_compact(buf);
}
buf.put_u16(self.hash_masks.len() as u16);
len += 2;
for item in &self.hash_masks {
len += StoredTrieMask(*item).to_compact(buf);
}
buf.put_u8(self.stored_in_database as u8);
len += 1;
len
}
fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
let (key, mut buf) = Vec::from_compact(buf, 0);
let stack_len = buf.get_u16() as usize;
let mut stack = Vec::with_capacity(stack_len);
for _ in 0..stack_len {
let item_len = buf.get_u16() as usize;
stack.push(Vec::from(&buf[..item_len]));
buf.advance(item_len);
}
let (StoredHashBuilderValue(value), mut buf) = StoredHashBuilderValue::from_compact(buf, 0);
let groups_len = buf.get_u16() as usize;
let mut groups = Vec::with_capacity(groups_len);
for _ in 0..groups_len {
let (StoredTrieMask(item), rest) = StoredTrieMask::from_compact(buf, 0);
groups.push(item);
buf = rest;
}
let tree_masks_len = buf.get_u16() as usize;
let mut tree_masks = Vec::with_capacity(tree_masks_len);
for _ in 0..tree_masks_len {
let (StoredTrieMask(item), rest) = StoredTrieMask::from_compact(buf, 0);
tree_masks.push(item);
buf = rest;
}
let hash_masks_len = buf.get_u16() as usize;
let mut hash_masks = Vec::with_capacity(hash_masks_len);
for _ in 0..hash_masks_len {
let (StoredTrieMask(item), rest) = StoredTrieMask::from_compact(buf, 0);
hash_masks.push(item);
buf = rest;
}
let stored_in_database = buf.get_u8() != 0;
(Self { key, stack, value, groups, tree_masks, hash_masks, stored_in_database }, buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn hash_builder_state_regression() {
let mut state = HashBuilderState::default();
state.stack.push(vec![]);
let mut buf = vec![];
let len = state.clone().to_compact(&mut buf);
let (decoded, _) = HashBuilderState::from_compact(&buf, len);
assert_eq!(state, decoded);
}
proptest! {
#[test]
fn hash_builder_state_roundtrip(state: HashBuilderState) {
let mut buf = vec![];
let len = state.clone().to_compact(&mut buf);
let (decoded, _) = HashBuilderState::from_compact(&buf, len);
assert_eq!(state, decoded);
}
}
}

View File

@ -0,0 +1,43 @@
use alloy_primitives::B256;
use alloy_trie::hash_builder::HashBuilderValue;
use bytes::Buf;
use reth_codecs::Compact;
/// A wrapper around `HashBuilderValue` that implements `Compact`.
pub(crate) struct StoredHashBuilderValue(pub(crate) HashBuilderValue);
impl Compact for StoredHashBuilderValue {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
match self.0 {
HashBuilderValue::Hash(hash) => {
buf.put_u8(0);
1 + hash.to_compact(buf)
}
HashBuilderValue::Bytes(bytes) => {
buf.put_u8(1);
1 + bytes.to_compact(buf)
}
}
}
// # Panics
//
// A panic will be triggered if a HashBuilderValue variant greater than 1 is passed from the
// database.
fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
match buf.get_u8() {
0 => {
let (hash, buf) = B256::from_compact(buf, 32);
(Self(HashBuilderValue::Hash(hash)), buf)
}
1 => {
let (bytes, buf) = Vec::from_compact(buf, 0);
(Self(HashBuilderValue::Bytes(bytes)), buf)
}
_ => unreachable!("Junk data in database: unknown HashBuilderValue variant"),
}
}
}

View File

@ -0,0 +1,34 @@
//! Commonly used types for trie usage.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
// TODO: remove when https://github.com/proptest-rs/proptest/pull/427 is merged
#![allow(unknown_lints, non_local_definitions)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
/// The implementation of hash builder.
pub mod hash_builder;
mod account;
pub use account::TrieAccount;
mod mask;
pub(crate) use mask::StoredTrieMask;
mod nibbles;
pub use nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey};
pub mod nodes;
pub use nodes::StoredBranchNode;
mod storage;
pub use storage::StorageTrieEntry;
mod subnode;
pub use subnode::StoredSubNode;
pub use alloy_trie::{proof, BranchNodeCompact, HashBuilder, TrieMask, EMPTY_ROOT_HASH};

View File

@ -0,0 +1,20 @@
use super::TrieMask;
use bytes::Buf;
use reth_codecs::Compact;
pub(crate) struct StoredTrieMask(pub(crate) TrieMask);
impl Compact for StoredTrieMask {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
buf.put_u16(self.0.get());
2
}
fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
let mask = buf.get_u16();
(Self(TrieMask::new(mask)), buf)
}
}

View File

@ -0,0 +1,120 @@
use bytes::Buf;
use derive_more::Deref;
use reth_codecs::Compact;
use serde::{Deserialize, Serialize};
pub use nybbles::Nibbles;
/// The representation of nibbles of the merkle trie stored in the database.
#[derive(
Clone,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
derive_more::Index,
)]
pub struct StoredNibbles(pub Nibbles);
impl From<Nibbles> for StoredNibbles {
#[inline]
fn from(value: Nibbles) -> Self {
Self(value)
}
}
impl From<Vec<u8>> for StoredNibbles {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self(Nibbles::from_nibbles_unchecked(value))
}
}
impl PartialEq<[u8]> for StoredNibbles {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.0.as_slice() == other
}
}
impl PartialOrd<[u8]> for StoredNibbles {
#[inline]
fn partial_cmp(&self, other: &[u8]) -> Option<std::cmp::Ordering> {
self.0.as_slice().partial_cmp(other)
}
}
impl core::borrow::Borrow<[u8]> for StoredNibbles {
#[inline]
fn borrow(&self) -> &[u8] {
self.0.as_slice()
}
}
impl Compact for StoredNibbles {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
buf.put_slice(self.0.as_slice());
self.0.len()
}
fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
let nibbles = &buf[..len];
buf.advance(len);
(Self(Nibbles::from_nibbles_unchecked(nibbles)), buf)
}
}
/// The representation of nibbles of the merkle trie stored in the database.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash, Deref)]
pub struct StoredNibblesSubKey(pub Nibbles);
impl From<Nibbles> for StoredNibblesSubKey {
#[inline]
fn from(value: Nibbles) -> Self {
Self(value)
}
}
impl From<Vec<u8>> for StoredNibblesSubKey {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self(Nibbles::from_nibbles_unchecked(value))
}
}
impl From<StoredNibblesSubKey> for Nibbles {
#[inline]
fn from(value: StoredNibblesSubKey) -> Self {
value.0
}
}
impl Compact for StoredNibblesSubKey {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
assert!(self.0.len() <= 64);
// right-pad with zeros
buf.put_slice(&self.0[..]);
static ZERO: &[u8; 64] = &[0; 64];
buf.put_slice(&ZERO[self.0.len()..]);
buf.put_u8(self.0.len() as u8);
64 + 1
}
fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
let len = buf[64] as usize;
(Self(Nibbles::from_nibbles_unchecked(&buf[..len])), &buf[65..])
}
}

View File

@ -0,0 +1,93 @@
use crate::StoredTrieMask;
use alloy_primitives::B256;
use alloy_trie::BranchNodeCompact;
use bytes::Buf;
use reth_codecs::Compact;
use serde::{Deserialize, Serialize};
/// Wrapper around `BranchNodeCompact` that implements `Compact`.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StoredBranchNode(pub BranchNodeCompact);
impl Compact for StoredBranchNode {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
let BranchNodeCompact { state_mask, tree_mask, hash_mask, root_hash, hashes } = self.0;
let mut buf_size = 0;
buf_size += StoredTrieMask(state_mask).to_compact(buf);
buf_size += StoredTrieMask(tree_mask).to_compact(buf);
buf_size += StoredTrieMask(hash_mask).to_compact(buf);
if let Some(root_hash) = root_hash {
buf_size += B256::len_bytes();
buf.put_slice(root_hash.as_slice());
}
for hash in &hashes {
buf_size += B256::len_bytes();
buf.put_slice(hash.as_slice());
}
buf_size
}
fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
let hash_len = B256::len_bytes();
// Assert the buffer is long enough to contain the masks and the hashes.
assert_eq!(buf.len() % hash_len, 6);
// Consume the masks.
let (StoredTrieMask(state_mask), buf) = StoredTrieMask::from_compact(buf, 0);
let (StoredTrieMask(tree_mask), buf) = StoredTrieMask::from_compact(buf, 0);
let (StoredTrieMask(hash_mask), buf) = StoredTrieMask::from_compact(buf, 0);
let mut buf = buf;
let mut num_hashes = buf.len() / hash_len;
let mut root_hash = None;
// Check if the root hash is present
if hash_mask.count_ones() as usize + 1 == num_hashes {
root_hash = Some(B256::from_slice(&buf[..hash_len]));
buf.advance(hash_len);
num_hashes -= 1;
}
// Consume all remaining hashes.
let mut hashes = Vec::<B256>::with_capacity(num_hashes);
for _ in 0..num_hashes {
hashes.push(B256::from_slice(&buf[..hash_len]));
buf.advance(hash_len);
}
(Self(BranchNodeCompact::new(state_mask, tree_mask, hash_mask, hashes, root_hash)), buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::hex;
#[test]
fn node_encoding() {
let n = BranchNodeCompact::new(
0xf607,
0x0005,
0x4004,
vec![
hex!("90d53cd810cc5d4243766cd4451e7b9d14b736a1148b26b3baac7617f617d321").into(),
hex!("cc35c964dda53ba6c0b87798073a9628dbc9cd26b5cce88eb69655a9c609caf1").into(),
],
Some(hex!("aaaabbbb0006767767776fffffeee44444000005567645600000000eeddddddd").into()),
);
let mut out = Vec::new();
let compact_len = StoredBranchNode(n.clone()).to_compact(&mut out);
assert_eq!(StoredBranchNode::from_compact(&out, compact_len).0 .0, n);
}
}

View File

@ -0,0 +1,6 @@
//! Various branch nodes produced by the hash builder.
mod branch;
pub use branch::StoredBranchNode;
pub use alloy_trie::nodes::*;

View File

@ -0,0 +1,33 @@
use super::{BranchNodeCompact, StoredBranchNode, StoredNibblesSubKey};
use reth_codecs::Compact;
use serde::{Deserialize, Serialize};
/// Account storage trie node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
pub struct StorageTrieEntry {
/// The nibbles of the intermediate node
pub nibbles: StoredNibblesSubKey,
/// Encoded node.
pub node: BranchNodeCompact,
}
// NOTE: Removing main_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
impl Compact for StorageTrieEntry {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
let nibbles_len = self.nibbles.to_compact(buf);
let node_len = StoredBranchNode(self.node).to_compact(buf);
nibbles_len + node_len
}
fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 33);
let (node, buf) = StoredBranchNode::from_compact(buf, len - 33);
let this = Self { nibbles, node: node.0 };
(this, buf)
}
}

View File

@ -0,0 +1,95 @@
use super::{BranchNodeCompact, StoredBranchNode};
use bytes::Buf;
use reth_codecs::Compact;
/// Walker sub node for storing intermediate state root calculation state in the database.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct StoredSubNode {
/// The key of the current node.
pub key: Vec<u8>,
/// The index of the next child to visit.
pub nibble: Option<u8>,
/// The node itself.
pub node: Option<BranchNodeCompact>,
}
impl Compact for StoredSubNode {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
let mut len = 0;
buf.put_u16(self.key.len() as u16);
buf.put_slice(&self.key[..]);
len += 2 + self.key.len();
if let Some(nibble) = self.nibble {
buf.put_u8(1);
buf.put_u8(nibble);
len += 2;
} else {
buf.put_u8(0);
len += 1;
}
if let Some(node) = self.node {
buf.put_u8(1);
len += 1;
len += StoredBranchNode(node).to_compact(buf);
} else {
len += 1;
buf.put_u8(0);
}
len
}
fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
let key_len = buf.get_u16() as usize;
let key = Vec::from(&buf[..key_len]);
buf.advance(key_len);
let nibbles_exists = buf.get_u8() != 0;
let nibble = if nibbles_exists { Some(buf.get_u8()) } else { None };
let node_exists = buf.get_u8() != 0;
let node = if node_exists {
let (node, rest) = StoredBranchNode::from_compact(buf, 0);
buf = rest;
Some(node.0)
} else {
None
};
(Self { key, nibble, node }, buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TrieMask;
use alloy_primitives::B256;
#[test]
fn subnode_roundtrip() {
let subnode = StoredSubNode {
key: vec![],
nibble: None,
node: Some(BranchNodeCompact {
state_mask: TrieMask::new(1),
tree_mask: TrieMask::new(0),
hash_mask: TrieMask::new(1),
hashes: vec![B256::ZERO],
root_hash: None,
}),
};
let mut encoded = vec![];
subnode.clone().to_compact(&mut encoded);
let (decoded, _) = StoredSubNode::from_compact(&encoded[..], 0);
assert_eq!(subnode, decoded);
}
}