diff --git a/Cargo.toml b/Cargo.toml index 9e9a58c..94b72ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ alloy-primitives = { version = "0.8.5", default-features = false, features = [ ] } alloy-rlp = { version = "0.3.8", default-features = false, features = [ "derive", + "arrayvec", ] } arrayvec = { version = "0.7", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index 4bd8cd0..cca2fcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,9 @@ pub mod proof; mod mask; pub use mask::TrieMask; +#[allow(missing_docs)] +pub mod root; + #[doc(hidden)] pub use alloy_primitives::map::HashMap; diff --git a/src/root.rs b/src/root.rs new file mode 100644 index 0000000..c1a52db --- /dev/null +++ b/src/root.rs @@ -0,0 +1,49 @@ +use alloc::vec::Vec; +use alloy_primitives::B256; +use alloy_rlp::Encodable; +use nybbles::Nibbles; + +use crate::{HashBuilder, EMPTY_ROOT_HASH}; + +/// Adjust the index of an item for rlp encoding. +pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { + if i > 0x7f { + i + } else if i == 0x7f || i + 1 == len { + 0 + } else { + i + 1 + } +} + +/// Compute a trie root of the collection of rlp encodable items. +pub fn ordered_trie_root(items: &[T]) -> B256 { + ordered_trie_root_with_encoder(items, |item, buf| item.encode(buf)) +} + +/// Compute a trie root of the collection of items with a custom encoder. +pub fn ordered_trie_root_with_encoder(items: &[T], mut encode: F) -> B256 +where + F: FnMut(&T, &mut Vec), +{ + if items.is_empty() { + return EMPTY_ROOT_HASH; + } + + let mut value_buffer = Vec::new(); + + let mut hb = HashBuilder::default(); + let items_len = items.len(); + for i in 0..items_len { + let index = adjust_index_for_rlp(i, items_len); + + let index_buffer = alloy_rlp::encode_fixed_size(&index); + + value_buffer.clear(); + encode(&items[index], &mut value_buffer); + + hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + } + + hb.root() +}