mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: improve Nibbles-related code (#5631)
This commit is contained in:
@ -223,7 +223,7 @@ impl Command {
|
||||
(Some(in_mem), Some(incr)) => {
|
||||
pretty_assertions::assert_eq!(in_mem.0, incr.0, "Nibbles don't match");
|
||||
if in_mem.1 != incr.1 &&
|
||||
matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.inner.len() > self.skip_node_depth.unwrap_or_default())
|
||||
matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.len() > self.skip_node_depth.unwrap_or_default())
|
||||
{
|
||||
in_mem_mismatched.push(in_mem);
|
||||
incremental_mismatched.push(incr);
|
||||
|
||||
@ -311,7 +311,7 @@ impl Command {
|
||||
"Nibbles don't match"
|
||||
);
|
||||
if incremental.1 != clean.1 &&
|
||||
clean.0.inner.len() > self.skip_node_depth.unwrap_or_default()
|
||||
clean.0.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
incremental_account_mismatched.push(incremental);
|
||||
clean_account_mismatched.push(clean);
|
||||
@ -340,8 +340,7 @@ impl Command {
|
||||
match (incremental_storage_trie_iter.next(), clean_storage_trie_iter.next()) {
|
||||
(Some(incremental), Some(clean)) => {
|
||||
if incremental != clean &&
|
||||
clean.1.nibbles.inner.len() >
|
||||
self.skip_node_depth.unwrap_or_default()
|
||||
clean.1.nibbles.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
first_mismatched_storage = Some((incremental, clean));
|
||||
break
|
||||
|
||||
@ -3,17 +3,12 @@ use reth_primitives::trie::Nibbles;
|
||||
|
||||
/// Benchmarks the nibble unpacking.
|
||||
pub fn nibbles_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("Nibbles unpack", |b| {
|
||||
let mut g = c.benchmark_group("nibbles");
|
||||
g.bench_function("unpack", |b| {
|
||||
let raw = (1..=32).collect::<Vec<u8>>();
|
||||
b.iter(|| {
|
||||
Nibbles::unpack(&raw);
|
||||
})
|
||||
b.iter(|| Nibbles::unpack(&raw))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default();
|
||||
targets = nibbles_benchmark
|
||||
}
|
||||
criterion_group!(benches, nibbles_benchmark);
|
||||
criterion_main!(benches);
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use super::{
|
||||
nodes::{rlp_hash, BranchNode, ExtensionNode, LeafNode},
|
||||
nodes::{word_rlp, BranchNode, ExtensionNode, LeafNode},
|
||||
BranchNodeCompact, Nibbles, TrieMask,
|
||||
};
|
||||
use crate::{constants::EMPTY_ROOT_HASH, keccak256, Bytes, B256};
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Debug,
|
||||
@ -62,7 +63,7 @@ pub struct HashBuilder {
|
||||
impl From<HashBuilderState> for HashBuilder {
|
||||
fn from(state: HashBuilderState) -> Self {
|
||||
Self {
|
||||
key: Nibbles::from_hex(state.key),
|
||||
key: Nibbles::new_unchecked(state.key),
|
||||
stack: state.stack,
|
||||
value: state.value,
|
||||
groups: state.groups,
|
||||
@ -79,7 +80,7 @@ impl From<HashBuilderState> for HashBuilder {
|
||||
impl From<HashBuilder> for HashBuilderState {
|
||||
fn from(state: HashBuilder) -> Self {
|
||||
Self {
|
||||
key: state.key.hex_data.to_vec(),
|
||||
key: state.key.to_vec(),
|
||||
stack: state.stack,
|
||||
value: state.value,
|
||||
groups: state.groups,
|
||||
@ -155,7 +156,7 @@ impl HashBuilder {
|
||||
if !self.key.is_empty() {
|
||||
self.update(&key);
|
||||
} else if key.is_empty() {
|
||||
self.stack.push(rlp_hash(value));
|
||||
self.stack.push(word_rlp(&value));
|
||||
}
|
||||
self.set_key_value(key, value);
|
||||
self.stored_in_database = stored_in_database;
|
||||
@ -166,7 +167,7 @@ impl HashBuilder {
|
||||
// Clears the internal state
|
||||
if !self.key.is_empty() {
|
||||
self.update(&Nibbles::default());
|
||||
self.key.hex_data.0.clear();
|
||||
self.key.clear();
|
||||
self.value = HashBuilderValue::Bytes(vec![]);
|
||||
}
|
||||
self.current_root()
|
||||
@ -209,7 +210,7 @@ impl HashBuilder {
|
||||
tracing::Level::TRACE,
|
||||
"loop",
|
||||
i,
|
||||
current = crate::hex::encode(¤t.hex_data),
|
||||
?current,
|
||||
?build_extensions
|
||||
);
|
||||
let _enter = span.enter();
|
||||
@ -217,7 +218,7 @@ impl HashBuilder {
|
||||
let preceding_exists = !self.groups.is_empty();
|
||||
let preceding_len: usize = self.groups.len().saturating_sub(1);
|
||||
|
||||
let common_prefix_len = succeeding.common_prefix_length(¤t);
|
||||
let common_prefix_len = succeeding.common_prefix_length(current.as_slice());
|
||||
let len = std::cmp::max(preceding_len, common_prefix_len);
|
||||
assert!(len < current.len());
|
||||
|
||||
@ -241,7 +242,7 @@ impl HashBuilder {
|
||||
trace!(
|
||||
target: "trie::hash_builder",
|
||||
?extra_digit,
|
||||
groups = self.groups.iter().map(|x| format!("{x:?}")).collect::<Vec<_>>().join(","),
|
||||
groups = ?self.groups.iter().format(", "),
|
||||
);
|
||||
|
||||
// Adjust the tree masks for exporting to the DB
|
||||
@ -256,7 +257,7 @@ impl HashBuilder {
|
||||
trace!(target: "trie::hash_builder", "skipping {} nibbles", len_from);
|
||||
|
||||
// The key without the common prefix
|
||||
let short_node_key = current.slice_from(len_from);
|
||||
let short_node_key = current.slice(len_from..);
|
||||
trace!(target: "trie::hash_builder", ?short_node_key);
|
||||
|
||||
// Concatenate the 2 nodes together
|
||||
@ -276,7 +277,7 @@ impl HashBuilder {
|
||||
}
|
||||
HashBuilderValue::Hash(hash) => {
|
||||
trace!(target: "trie::hash_builder", ?hash, "pushing branch node hash");
|
||||
self.stack.push(rlp_hash(*hash));
|
||||
self.stack.push(word_rlp(hash));
|
||||
|
||||
if self.stored_in_database {
|
||||
self.tree_masks[current.len() - 1] |=
|
||||
@ -302,7 +303,7 @@ impl HashBuilder {
|
||||
}, "extension node rlp");
|
||||
self.rlp_buf.clear();
|
||||
self.stack.push(extension_node.rlp(&mut self.rlp_buf));
|
||||
self.retain_proof_from_buf(¤t.slice(0, len_from));
|
||||
self.retain_proof_from_buf(¤t.slice(..len_from));
|
||||
self.resize_masks(len_from);
|
||||
}
|
||||
|
||||
@ -353,7 +354,7 @@ impl HashBuilder {
|
||||
|
||||
self.rlp_buf.clear();
|
||||
let rlp = branch_node.rlp(state_mask, &mut self.rlp_buf);
|
||||
self.retain_proof_from_buf(¤t.slice(0, len));
|
||||
self.retain_proof_from_buf(¤t.slice(..len));
|
||||
|
||||
// Clears the stack from the branch node elements
|
||||
let first_child_idx = self.stack.len() - state_mask.count_ones() as usize;
|
||||
@ -403,7 +404,7 @@ impl HashBuilder {
|
||||
// Send it over to the provided channel which will handle it on the
|
||||
// other side of the HashBuilder
|
||||
trace!(target: "trie::hash_builder", node = ?n, "intermediate node");
|
||||
let common_prefix = current.slice(0, len);
|
||||
let common_prefix = current.slice(..len);
|
||||
if let Some(nodes) = self.updated_branch_nodes.as_mut() {
|
||||
nodes.insert(common_prefix, n);
|
||||
}
|
||||
@ -563,7 +564,7 @@ mod tests {
|
||||
|
||||
let (_, updates) = hb.split();
|
||||
|
||||
let update = updates.get(&Nibbles::from(hex!("01").as_slice())).unwrap();
|
||||
let update = updates.get(&Nibbles::new_unchecked(hex!("01"))).unwrap();
|
||||
assert_eq!(update.state_mask, TrieMask::new(0b1111)); // 1st nibble: 0, 1, 2, 3
|
||||
assert_eq!(update.tree_mask, TrieMask::new(0));
|
||||
assert_eq!(update.hash_mask, TrieMask::new(6)); // in the 1st nibble, the ones with 1 and 2 are branches with `hashes`
|
||||
@ -633,7 +634,7 @@ mod tests {
|
||||
|
||||
let mut hb2 = HashBuilder::default();
|
||||
// Insert the branch with the `0x6` shared prefix.
|
||||
hb2.add_branch(Nibbles::from_hex(vec![0x6]), branch_node_hash, false);
|
||||
hb2.add_branch(Nibbles::new_unchecked([0x6]), branch_node_hash, false);
|
||||
|
||||
let expected = trie_root(raw_input.clone());
|
||||
assert_eq!(hb.root(), expected);
|
||||
|
||||
@ -32,27 +32,32 @@ pub struct TrieMask(u16);
|
||||
|
||||
impl TrieMask {
|
||||
/// Creates a new `TrieMask` from the given inner value.
|
||||
#[inline]
|
||||
pub fn new(inner: u16) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
/// Creates a new `TrieMask` from the given nibble.
|
||||
#[inline]
|
||||
pub fn from_nibble(nibble: u8) -> Self {
|
||||
Self(1u16 << nibble)
|
||||
}
|
||||
|
||||
/// Returns `true` if the current `TrieMask` is a subset of `other`.
|
||||
pub fn is_subset_of(&self, other: &Self) -> bool {
|
||||
*self & *other == *self
|
||||
#[inline]
|
||||
pub fn is_subset_of(self, other: Self) -> bool {
|
||||
self & other == self
|
||||
}
|
||||
|
||||
/// Returns `true` if a given bit is set in a mask.
|
||||
pub fn is_bit_set(&self, index: u8) -> bool {
|
||||
#[inline]
|
||||
pub fn is_bit_set(self, index: u8) -> bool {
|
||||
self.0 & (1u16 << index) != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the mask is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
#[inline]
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ mod subnode;
|
||||
|
||||
pub use self::{
|
||||
mask::TrieMask,
|
||||
nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey},
|
||||
nibbles::{Nibbles, StoredNibblesSubKey},
|
||||
storage::StorageTrieEntry,
|
||||
subnode::StoredSubNode,
|
||||
};
|
||||
|
||||
@ -3,28 +3,30 @@ use alloy_rlp::RlpEncodableWrapper;
|
||||
use derive_more::{Deref, From, Index};
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The nibbles are the keys for the AccountsTrie and the subkeys for the StorageTrie.
|
||||
#[main_codec]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct StoredNibbles {
|
||||
/// The inner nibble bytes
|
||||
pub inner: Bytes,
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for StoredNibbles {
|
||||
fn from(inner: Vec<u8>) -> Self {
|
||||
Self { inner: inner.into() }
|
||||
}
|
||||
}
|
||||
use std::{borrow::Borrow, ops::RangeBounds};
|
||||
|
||||
/// 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(StoredNibbles);
|
||||
pub struct StoredNibblesSubKey(pub Nibbles);
|
||||
|
||||
impl From<Nibbles> for StoredNibblesSubKey {
|
||||
#[inline]
|
||||
fn from(value: Nibbles) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for StoredNibblesSubKey {
|
||||
fn from(inner: Vec<u8>) -> Self {
|
||||
Self(StoredNibbles { inner: inner.into() })
|
||||
#[inline]
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
Self(Nibbles::new_unchecked(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoredNibblesSubKey> for Nibbles {
|
||||
#[inline]
|
||||
fn from(value: StoredNibblesSubKey) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,73 +35,122 @@ impl Compact for StoredNibblesSubKey {
|
||||
where
|
||||
B: bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
assert!(self.inner.len() <= 64);
|
||||
let mut padded = vec![0; 64];
|
||||
padded[..self.inner.len()].copy_from_slice(&self.inner[..]);
|
||||
buf.put_slice(&padded);
|
||||
buf.put_u8(self.inner.len() as 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;
|
||||
let inner = Vec::from(&buf[..len]).into();
|
||||
(Self(StoredNibbles { inner }), &buf[65..])
|
||||
(Self(Nibbles::new_unchecked(buf[..len].to_vec())), &buf[65..])
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure representing a sequence of nibbles.
|
||||
///
|
||||
/// A nibble is a 4-bit value, and this structure is used to store
|
||||
/// the nibble sequence representing the keys in a Merkle Patricia Trie (MPT).
|
||||
/// Using nibbles simplifies trie operations and enables consistent key
|
||||
/// representation in the MPT.
|
||||
/// A nibble is a 4-bit value, and this structure is used to store the nibble sequence representing
|
||||
/// the keys in a Merkle Patricia Trie (MPT).
|
||||
/// Using nibbles simplifies trie operations and enables consistent key representation in the MPT.
|
||||
///
|
||||
/// The `hex_data` field is a `Vec<u8>` that stores the nibbles, with each
|
||||
/// `u8` value containing a single nibble. This means that each byte in
|
||||
/// `hex_data` has its upper 4 bits set to zero and the lower 4 bits
|
||||
/// The internal representation is a shared heap-allocated vector ([`Bytes`]) that stores one nibble
|
||||
/// per byte. This means that each byte has its upper 4 bits set to zero and the lower 4 bits
|
||||
/// representing the nibble value.
|
||||
#[main_codec]
|
||||
#[derive(
|
||||
Default, Clone, Eq, PartialEq, RlpEncodableWrapper, PartialOrd, Ord, Hash, Index, From, Deref,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
RlpEncodableWrapper,
|
||||
Index,
|
||||
From,
|
||||
Deref,
|
||||
)]
|
||||
pub struct Nibbles {
|
||||
/// The inner representation of the nibble sequence.
|
||||
pub hex_data: Bytes,
|
||||
}
|
||||
pub struct Nibbles(Bytes);
|
||||
|
||||
impl From<&[u8]> for Nibbles {
|
||||
fn from(slice: &[u8]) -> Self {
|
||||
Nibbles::from_hex(slice.to_vec())
|
||||
impl From<Vec<u8>> for Nibbles {
|
||||
#[inline]
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
Self::new_unchecked(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<&[u8; N]> for Nibbles {
|
||||
fn from(arr: &[u8; N]) -> Self {
|
||||
Nibbles::from_hex(arr.to_vec())
|
||||
impl From<Nibbles> for Vec<u8> {
|
||||
#[inline]
|
||||
fn from(value: Nibbles) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Nibbles {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Nibbles").field("hex_data", &crate::hex::encode(&self.hex_data)).finish()
|
||||
impl From<Nibbles> for Bytes {
|
||||
#[inline]
|
||||
fn from(value: Nibbles) -> Self {
|
||||
value.into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for Nibbles {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.as_slice() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Nibbles> for [u8] {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Nibbles) -> bool {
|
||||
self == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<[u8]> for Nibbles {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &[u8]) -> Option<std::cmp::Ordering> {
|
||||
self.as_slice().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Nibbles> for [u8] {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Nibbles) -> Option<std::cmp::Ordering> {
|
||||
self.partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for Nibbles {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Nibbles {
|
||||
/// Creates a new [Nibbles] instance from bytes.
|
||||
pub fn from_hex<T: Into<Bytes>>(hex: T) -> Self {
|
||||
Nibbles { hex_data: hex.into() }
|
||||
/// Creates a new [`Nibbles`] instance from nibble bytes, without checking their validity.
|
||||
#[inline]
|
||||
pub fn new_unchecked<T: Into<Bytes>>(nibbles: T) -> Self {
|
||||
Self(nibbles.into())
|
||||
}
|
||||
|
||||
/// Take a byte array (slice or vector) as input and convert it into a [Nibbles] struct
|
||||
/// containing the nibbles (half-bytes or 4 bits) that make up the input byte data.
|
||||
/// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4
|
||||
/// bits) that make up the input byte data.
|
||||
pub fn unpack<T: AsRef<[u8]>>(data: T) -> Self {
|
||||
let mut vec = Vec::with_capacity(data.as_ref().len() * 2);
|
||||
for byte in data.as_ref() {
|
||||
vec.push(byte / 16);
|
||||
vec.push(byte % 16);
|
||||
let data = data.as_ref();
|
||||
let mut nibbles = Vec::with_capacity(data.len() * 2);
|
||||
for &byte in data {
|
||||
nibbles.push(byte >> 4);
|
||||
nibbles.push(byte & 0x0f);
|
||||
}
|
||||
Nibbles { hex_data: Bytes::from(vec) }
|
||||
Self(nibbles.into())
|
||||
}
|
||||
|
||||
/// Packs the nibbles stored in the struct into a byte vector.
|
||||
@ -109,22 +160,14 @@ impl Nibbles {
|
||||
/// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and
|
||||
/// added to the packed byte vector.
|
||||
pub fn pack(&self) -> Vec<u8> {
|
||||
let length = (self.len() + 1) / 2;
|
||||
if length == 0 {
|
||||
Vec::new()
|
||||
} else {
|
||||
self.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, nibble)| {
|
||||
if index % 2 == 0 {
|
||||
let next_nibble = self.get(index + 1).unwrap_or(&0);
|
||||
Some((*nibble << 4) + *next_nibble)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
let packed_len = (self.len() + 1) / 2;
|
||||
let mut v = Vec::with_capacity(packed_len);
|
||||
for i in 0..packed_len {
|
||||
let hi = *unsafe { self.get_unchecked(i * 2) };
|
||||
let lo = self.get(i * 2 + 1).copied().unwrap_or(0);
|
||||
v.push((hi << 4) | lo);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
/// Encodes a given path leaf as a compact array of bytes, where each byte represents two
|
||||
@ -155,19 +198,19 @@ impl Nibbles {
|
||||
/// # use reth_primitives::trie::Nibbles;
|
||||
///
|
||||
/// // Extension node with an even path length:
|
||||
/// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C, 0x0D]);
|
||||
/// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]);
|
||||
/// assert_eq!(nibbles.encode_path_leaf(false), vec![0x00, 0xAB, 0xCD]);
|
||||
///
|
||||
/// // Extension node with an odd path length:
|
||||
/// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C]);
|
||||
/// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]);
|
||||
/// assert_eq!(nibbles.encode_path_leaf(false), vec![0x1A, 0xBC]);
|
||||
///
|
||||
/// // Leaf node with an even path length:
|
||||
/// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C, 0x0D]);
|
||||
/// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]);
|
||||
/// assert_eq!(nibbles.encode_path_leaf(true), vec![0x20, 0xAB, 0xCD]);
|
||||
///
|
||||
/// // Leaf node with an odd path length:
|
||||
/// let nibbles = Nibbles::from_hex(vec![0x0A, 0x0B, 0x0C]);
|
||||
/// let nibbles = Nibbles::new_unchecked(&[0x0A, 0x0B, 0x0C]);
|
||||
/// assert_eq!(nibbles.encode_path_leaf(true), vec![0x3A, 0xBC]);
|
||||
/// ```
|
||||
pub fn encode_path_leaf(&self, is_leaf: bool) -> Vec<u8> {
|
||||
@ -192,14 +235,14 @@ impl Nibbles {
|
||||
}
|
||||
|
||||
/// Increments the nibble sequence by one.
|
||||
pub fn increment(&self) -> Option<Nibbles> {
|
||||
let mut incremented = self.hex_data.to_vec();
|
||||
pub fn increment(&self) -> Option<Self> {
|
||||
let mut incremented = self.0.to_vec();
|
||||
|
||||
for nibble in incremented.iter_mut().rev() {
|
||||
assert!(*nibble < 0x10);
|
||||
debug_assert!(*nibble < 0x10);
|
||||
if *nibble < 0xf {
|
||||
*nibble += 1;
|
||||
return Some(Nibbles::from_hex(incremented))
|
||||
return Some(Self::new_unchecked(incremented))
|
||||
} else {
|
||||
*nibble = 0;
|
||||
}
|
||||
@ -210,27 +253,37 @@ impl Nibbles {
|
||||
|
||||
/// The last element of the hex vector is used to determine whether the nibble sequence
|
||||
/// represents a leaf or an extension node. If the last element is 0x10 (16), then it's a leaf.
|
||||
#[inline]
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
self.hex_data[self.hex_data.len() - 1] == 16
|
||||
self.last() == Some(16)
|
||||
}
|
||||
|
||||
/// Returns `true` if the current nibble sequence starts with the given prefix.
|
||||
pub fn has_prefix(&self, other: &Self) -> bool {
|
||||
#[inline]
|
||||
pub fn has_prefix(&self, other: &[u8]) -> bool {
|
||||
self.starts_with(other)
|
||||
}
|
||||
|
||||
/// Returns the nibble at the given index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn at(&self, i: usize) -> usize {
|
||||
self.hex_data[i] as usize
|
||||
self.0[i] as usize
|
||||
}
|
||||
|
||||
/// Returns the last nibble of the current nibble sequence.
|
||||
#[inline]
|
||||
pub fn last(&self) -> Option<u8> {
|
||||
self.hex_data.last().copied()
|
||||
self.0.last().copied()
|
||||
}
|
||||
|
||||
/// Returns the length of the common prefix between the current nibble sequence and the given.
|
||||
pub fn common_prefix_length(&self, other: &Nibbles) -> usize {
|
||||
#[inline]
|
||||
pub fn common_prefix_length(&self, other: &[u8]) -> usize {
|
||||
let len = std::cmp::min(self.len(), other.len());
|
||||
for i in 0..len {
|
||||
if self[i] != other[i] {
|
||||
@ -240,34 +293,67 @@ impl Nibbles {
|
||||
len
|
||||
}
|
||||
|
||||
/// Slice the current nibbles from the given start index to the end.
|
||||
pub fn slice_from(&self, index: usize) -> Nibbles {
|
||||
self.slice(index, self.hex_data.len())
|
||||
/// Returns a reference to the underlying [`Bytes`].
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &Bytes {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the nibbles as a byte slice.
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the underlying [`Bytes`].
|
||||
#[inline]
|
||||
pub fn into_bytes(self) -> Bytes {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Slice the current nibbles within the provided index range.
|
||||
pub fn slice(&self, start: usize, end: usize) -> Nibbles {
|
||||
Nibbles::from_hex(self.hex_data[start..end].to_vec())
|
||||
#[inline]
|
||||
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
|
||||
Self(self.0.slice(range))
|
||||
}
|
||||
|
||||
/// Join two nibbles together.
|
||||
pub fn join(&self, b: &Nibbles) -> Nibbles {
|
||||
#[inline]
|
||||
pub fn join(&self, b: &Self) -> Self {
|
||||
let mut hex_data = Vec::with_capacity(self.len() + b.len());
|
||||
hex_data.extend_from_slice(self);
|
||||
hex_data.extend_from_slice(b);
|
||||
Nibbles::from_hex(hex_data)
|
||||
Self::new_unchecked(hex_data)
|
||||
}
|
||||
|
||||
/// Pushes a nibble to the end of the current nibbles.
|
||||
///
|
||||
/// **Note**: This method re-allocates on each call.
|
||||
#[inline]
|
||||
pub fn push(&mut self, nibble: u8) {
|
||||
self.extend([nibble]);
|
||||
}
|
||||
|
||||
/// Extend the current nibbles with another nibbles.
|
||||
///
|
||||
/// **Note**: This method re-allocates on each call.
|
||||
#[inline]
|
||||
pub fn extend(&mut self, b: impl AsRef<[u8]>) {
|
||||
let mut bytes = self.hex_data.to_vec();
|
||||
let mut bytes = self.0.to_vec();
|
||||
bytes.extend_from_slice(b.as_ref());
|
||||
self.hex_data = bytes.into();
|
||||
self.0 = bytes.into();
|
||||
}
|
||||
|
||||
/// Truncate the current nibbles to the given length.
|
||||
/// Truncates the current nibbles to the given length.
|
||||
#[inline]
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
self.hex_data.0.truncate(len)
|
||||
self.0.truncate(len);
|
||||
}
|
||||
|
||||
/// Clears the current nibbles.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,7 +365,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hashed_regression() {
|
||||
let nibbles = Nibbles::from_hex(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b"));
|
||||
let nibbles = Nibbles::new_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b"));
|
||||
let path = nibbles.encode_path_leaf(true);
|
||||
let expected = hex!("351464a4233f1852b5c47037e997f1ba852317ca924bf0f064a45f2b9710aa4b");
|
||||
assert_eq!(path, expected);
|
||||
@ -295,7 +381,7 @@ mod tests {
|
||||
(vec![0xa, 0xb, 0x2, 0x0], vec![0xab, 0x20]),
|
||||
(vec![0xa, 0xb, 0x2, 0x7], vec![0xab, 0x27]),
|
||||
] {
|
||||
let nibbles = Nibbles::from_hex(input);
|
||||
let nibbles = Nibbles::new_unchecked(input);
|
||||
let encoded = nibbles.pack();
|
||||
assert_eq!(encoded, expected);
|
||||
}
|
||||
|
||||
@ -123,8 +123,8 @@ impl BranchNodeCompact {
|
||||
) -> Self {
|
||||
let (state_mask, tree_mask, hash_mask) =
|
||||
(state_mask.into(), tree_mask.into(), hash_mask.into());
|
||||
assert!(tree_mask.is_subset_of(&state_mask));
|
||||
assert!(hash_mask.is_subset_of(&state_mask));
|
||||
assert!(tree_mask.is_subset_of(state_mask));
|
||||
assert!(hash_mask.is_subset_of(state_mask));
|
||||
assert_eq!(hash_mask.count_ones() as usize, hashes.len());
|
||||
Self { state_mask, tree_mask, hash_mask, hashes, root_hash }
|
||||
}
|
||||
|
||||
@ -54,25 +54,22 @@ impl std::fmt::Debug for LeafNode<'_> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::hex_literal::hex;
|
||||
use crate::hex;
|
||||
|
||||
// From manual regression test
|
||||
#[test]
|
||||
fn encode_leaf_node_nibble() {
|
||||
let nibble = Nibbles { hex_data: hex!("0604060f").into() };
|
||||
let nibble = Nibbles::new_unchecked(hex!("0604060f"));
|
||||
let encoded = nibble.encode_path_leaf(true);
|
||||
let expected = hex!("20646f").to_vec();
|
||||
assert_eq!(encoded, expected);
|
||||
assert_eq!(encoded, hex!("20646f"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rlp_leaf_node_roundtrip() {
|
||||
let nibble = Nibbles { hex_data: hex!("0604060f").into() };
|
||||
let val = hex!("76657262").to_vec();
|
||||
let nibble = Nibbles::new_unchecked(hex!("0604060f"));
|
||||
let val = hex!("76657262");
|
||||
let leaf = LeafNode::new(&nibble, &val);
|
||||
let rlp = leaf.rlp(&mut vec![]);
|
||||
|
||||
let expected = hex!("c98320646f8476657262").to_vec();
|
||||
assert_eq!(rlp, expected);
|
||||
assert_eq!(rlp, hex!("c98320646f8476657262"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,28 +3,30 @@ use alloy_rlp::EMPTY_STRING_CODE;
|
||||
use std::ops::Range;
|
||||
|
||||
mod branch;
|
||||
mod extension;
|
||||
mod leaf;
|
||||
pub use branch::{BranchNode, BranchNodeCompact};
|
||||
|
||||
pub use self::{
|
||||
branch::{BranchNode, BranchNodeCompact},
|
||||
extension::ExtensionNode,
|
||||
leaf::LeafNode,
|
||||
};
|
||||
mod extension;
|
||||
pub use extension::ExtensionNode;
|
||||
|
||||
mod leaf;
|
||||
pub use leaf::LeafNode;
|
||||
|
||||
/// The range of valid child indexes.
|
||||
pub const CHILD_INDEX_RANGE: Range<u8> = 0..16;
|
||||
|
||||
/// Given an RLP encoded node, returns either RLP(node) or RLP(keccak(RLP(node)))
|
||||
#[inline]
|
||||
fn rlp_node(rlp: &[u8]) -> Vec<u8> {
|
||||
if rlp.len() < B256::len_bytes() {
|
||||
rlp.to_vec()
|
||||
} else {
|
||||
rlp_hash(keccak256(rlp))
|
||||
word_rlp(&keccak256(rlp))
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimization for quick encoding of a hash as RLP
|
||||
pub fn rlp_hash(hash: B256) -> Vec<u8> {
|
||||
[[EMPTY_STRING_CODE + B256::len_bytes() as u8].as_slice(), hash.0.as_slice()].concat()
|
||||
/// Optimization for quick encoding of a 32-byte word as RLP.
|
||||
// TODO: this could return [u8; 33] but Vec is needed everywhere this function is used
|
||||
#[inline]
|
||||
pub fn word_rlp(word: &B256) -> Vec<u8> {
|
||||
[&[EMPTY_STRING_CODE + B256::len_bytes() as u8][..], &word[..]].concat()
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ impl_compression_for_compact!(
|
||||
Receipt,
|
||||
TxType,
|
||||
StorageEntry,
|
||||
StoredNibbles,
|
||||
Nibbles,
|
||||
BranchNodeCompact,
|
||||
StoredNibblesSubKey,
|
||||
StorageTrieEntry,
|
||||
|
||||
@ -36,7 +36,7 @@ use crate::{
|
||||
};
|
||||
use reth_primitives::{
|
||||
stage::StageCheckpoint,
|
||||
trie::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey},
|
||||
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey},
|
||||
Account, Address, BlockHash, BlockNumber, Bytecode, Header, IntegerList, PruneCheckpoint,
|
||||
PruneSegment, Receipt, StorageEntry, TransactionSignedNoHash, TxHash, TxNumber, B256,
|
||||
};
|
||||
@ -384,7 +384,7 @@ dupsort!(
|
||||
|
||||
table!(
|
||||
/// Stores the current state's Merkle Patricia Tree.
|
||||
( AccountsTrie ) StoredNibbles | BranchNodeCompact
|
||||
( AccountsTrie ) Nibbles | BranchNodeCompact
|
||||
);
|
||||
|
||||
dupsort!(
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
};
|
||||
use reth_codecs::Compact;
|
||||
use reth_primitives::{
|
||||
trie::{StoredNibbles, StoredNibblesSubKey},
|
||||
trie::{Nibbles, StoredNibblesSubKey},
|
||||
Address, PruneSegment, B256,
|
||||
};
|
||||
|
||||
@ -102,18 +102,18 @@ impl Decode for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for StoredNibbles {
|
||||
impl Encode for Nibbles {
|
||||
type Encoded = Vec<u8>;
|
||||
|
||||
// Delegate to the Compact implementation
|
||||
fn encode(self) -> Self::Encoded {
|
||||
let mut buf = Vec::with_capacity(self.inner.len());
|
||||
let mut buf = Vec::with_capacity(self.len());
|
||||
self.to_compact(&mut buf);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for StoredNibbles {
|
||||
impl Decode for Nibbles {
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
||||
let buf = value.as_ref();
|
||||
Ok(Self::from_compact(buf, buf.len()).0)
|
||||
|
||||
@ -35,8 +35,7 @@ fn create_transactions_for_sender(
|
||||
.unwrap()
|
||||
.current();
|
||||
|
||||
let mut nonce = 0;
|
||||
for tx in txs.iter_mut() {
|
||||
for (nonce, tx) in txs.iter_mut().enumerate() {
|
||||
// reject pre-eip1559 tx types, if there is a legacy tx, replace it with an eip1559 tx
|
||||
if tx.is_legacy() || tx.is_eip2930() {
|
||||
*tx = MockTransaction::eip1559();
|
||||
@ -47,8 +46,7 @@ fn create_transactions_for_sender(
|
||||
}
|
||||
|
||||
tx.set_sender(sender);
|
||||
tx.set_nonce(nonce);
|
||||
nonce += 1;
|
||||
tx.set_nonce(nonce as u64);
|
||||
}
|
||||
|
||||
txs
|
||||
|
||||
@ -16,14 +16,13 @@ pub trait PrefixSetAbstraction: Default {
|
||||
fn contains(&mut self, key: Nibbles) -> bool;
|
||||
}
|
||||
|
||||
/// Abstractions used for benching
|
||||
impl PrefixSetAbstraction for PrefixSetMut {
|
||||
fn insert(&mut self, key: Nibbles) {
|
||||
self.insert(key)
|
||||
PrefixSetMut::insert(self, key)
|
||||
}
|
||||
|
||||
fn contains(&mut self, key: Nibbles) -> bool {
|
||||
PrefixSetMut::contains(self, key)
|
||||
PrefixSetMut::contains(self, &key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,12 +94,12 @@ fn generate_test_data(size: usize) -> (Vec<Nibbles>, Vec<Nibbles>, Vec<bool>) {
|
||||
let mut preload = vec(vec(any::<u8>(), 32), size).new_tree(&mut runner).unwrap().current();
|
||||
preload.dedup();
|
||||
preload.sort();
|
||||
let preload = preload.into_iter().map(|hash| Nibbles::from(&hash[..])).collect::<Vec<_>>();
|
||||
let preload = preload.into_iter().map(Nibbles::new_unchecked).collect::<Vec<_>>();
|
||||
|
||||
let mut input = vec(vec(any::<u8>(), 0..=32), size).new_tree(&mut runner).unwrap().current();
|
||||
input.dedup();
|
||||
input.sort();
|
||||
let input = input.into_iter().map(|bytes| Nibbles::from(&bytes[..])).collect::<Vec<_>>();
|
||||
let input = input.into_iter().map(Nibbles::new_unchecked).collect::<Vec<_>>();
|
||||
|
||||
let expected = input
|
||||
.iter()
|
||||
@ -145,11 +144,11 @@ mod implementations {
|
||||
fn contains(&mut self, prefix: Nibbles) -> bool {
|
||||
let range = match self.last_checked.as_ref() {
|
||||
// presumably never hit
|
||||
Some(last) if &prefix < last => (Bound::Unbounded, Bound::Excluded(last)),
|
||||
Some(last) if prefix < *last => (Bound::Unbounded, Bound::Excluded(last)),
|
||||
Some(last) => (Bound::Included(last), Bound::Unbounded),
|
||||
None => (Bound::Unbounded, Bound::Unbounded),
|
||||
};
|
||||
for key in self.keys.range(range) {
|
||||
for key in self.keys.range::<Nibbles, _>(range) {
|
||||
if key.has_prefix(&prefix) {
|
||||
self.last_checked = Some(prefix);
|
||||
return true
|
||||
|
||||
@ -22,13 +22,14 @@ pub use loader::{LoadedPrefixSets, PrefixSetLoader};
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use reth_primitives::trie::Nibbles;
|
||||
/// use reth_trie::prefix_set::PrefixSetMut;
|
||||
///
|
||||
/// let mut prefix_set = PrefixSetMut::default();
|
||||
/// prefix_set.insert(b"key1");
|
||||
/// prefix_set.insert(b"key2");
|
||||
///
|
||||
/// assert_eq!(prefix_set.contains(b"key"), true);
|
||||
/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb]));
|
||||
/// prefix_set.insert(Nibbles::new_unchecked(&[0xa, 0xb, 0xc]));
|
||||
/// assert!(prefix_set.contains(&[0xa, 0xb]));
|
||||
/// assert!(prefix_set.contains(&[0xa, 0xb, 0xc]));
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PrefixSetMut {
|
||||
@ -49,26 +50,24 @@ where
|
||||
impl PrefixSetMut {
|
||||
/// Returns `true` if any of the keys in the set has the given prefix or
|
||||
/// if the given prefix is a prefix of any key in the set.
|
||||
pub fn contains<T: Into<Nibbles>>(&mut self, prefix: T) -> bool {
|
||||
pub fn contains(&mut self, prefix: &[u8]) -> bool {
|
||||
if !self.sorted {
|
||||
self.keys.sort();
|
||||
self.keys.dedup();
|
||||
self.sorted = true;
|
||||
}
|
||||
|
||||
let prefix = prefix.into();
|
||||
|
||||
while self.index > 0 && self.keys[self.index] > prefix {
|
||||
while self.index > 0 && self.keys[self.index] > *prefix {
|
||||
self.index -= 1;
|
||||
}
|
||||
|
||||
for (idx, key) in self.keys[self.index..].iter().enumerate() {
|
||||
if key.has_prefix(&prefix) {
|
||||
if key.has_prefix(prefix) {
|
||||
self.index += idx;
|
||||
return true
|
||||
}
|
||||
|
||||
if key > &prefix {
|
||||
if *key > *prefix {
|
||||
self.index += idx;
|
||||
return false
|
||||
}
|
||||
@ -78,9 +77,9 @@ impl PrefixSetMut {
|
||||
}
|
||||
|
||||
/// Inserts the given `nibbles` into the set.
|
||||
pub fn insert<T: Into<Nibbles>>(&mut self, nibbles: T) {
|
||||
pub fn insert(&mut self, nibbles: Nibbles) {
|
||||
self.sorted = false;
|
||||
self.keys.push(nibbles.into());
|
||||
self.keys.push(nibbles);
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the set.
|
||||
@ -159,10 +158,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_contains_with_multiple_inserts_and_duplicates() {
|
||||
let mut prefix_set = PrefixSetMut::default();
|
||||
prefix_set.insert(b"123");
|
||||
prefix_set.insert(b"124");
|
||||
prefix_set.insert(b"456");
|
||||
prefix_set.insert(b"123"); // Duplicate
|
||||
prefix_set.insert(Nibbles::new_unchecked(b"123"));
|
||||
prefix_set.insert(Nibbles::new_unchecked(b"124"));
|
||||
prefix_set.insert(Nibbles::new_unchecked(b"456"));
|
||||
prefix_set.insert(Nibbles::new_unchecked(b"123")); // Duplicate
|
||||
|
||||
assert!(prefix_set.contains(b"12"));
|
||||
assert!(prefix_set.contains(b"45"));
|
||||
|
||||
@ -937,7 +937,7 @@ mod tests {
|
||||
assert_eq!(account_updates.len(), 2);
|
||||
|
||||
let (nibbles1a, node1a) = account_updates.first().unwrap();
|
||||
assert_eq!(nibbles1a.inner[..], [0xB]);
|
||||
assert_eq!(nibbles1a[..], [0xB]);
|
||||
assert_eq!(node1a.state_mask, TrieMask::new(0b1011));
|
||||
assert_eq!(node1a.tree_mask, TrieMask::new(0b0001));
|
||||
assert_eq!(node1a.hash_mask, TrieMask::new(0b1001));
|
||||
@ -945,7 +945,7 @@ mod tests {
|
||||
assert_eq!(node1a.hashes.len(), 2);
|
||||
|
||||
let (nibbles2a, node2a) = account_updates.last().unwrap();
|
||||
assert_eq!(nibbles2a.inner[..], [0xB, 0x0]);
|
||||
assert_eq!(nibbles2a[..], [0xB, 0x0]);
|
||||
assert_eq!(node2a.state_mask, TrieMask::new(0b10001));
|
||||
assert_eq!(node2a.tree_mask, TrieMask::new(0b00000));
|
||||
assert_eq!(node2a.hash_mask, TrieMask::new(0b10000));
|
||||
@ -963,7 +963,7 @@ mod tests {
|
||||
assert_eq!(storage_updates.len(), 1);
|
||||
|
||||
let (nibbles3, node3) = storage_updates.first().unwrap();
|
||||
assert!(nibbles3.inner.is_empty());
|
||||
assert!(nibbles3.is_empty());
|
||||
assert_eq!(node3.state_mask, TrieMask::new(0b1010));
|
||||
assert_eq!(node3.tree_mask, TrieMask::new(0b0000));
|
||||
assert_eq!(node3.hash_mask, TrieMask::new(0b0010));
|
||||
@ -1004,7 +1004,7 @@ mod tests {
|
||||
assert_eq!(account_updates.len(), 2);
|
||||
|
||||
let (nibbles1b, node1b) = account_updates.first().unwrap();
|
||||
assert_eq!(nibbles1b.inner[..], [0xB]);
|
||||
assert_eq!(nibbles1b[..], [0xB]);
|
||||
assert_eq!(node1b.state_mask, TrieMask::new(0b1011));
|
||||
assert_eq!(node1b.tree_mask, TrieMask::new(0b0001));
|
||||
assert_eq!(node1b.hash_mask, TrieMask::new(0b1011));
|
||||
@ -1014,7 +1014,7 @@ mod tests {
|
||||
assert_eq!(node1a.hashes[1], node1b.hashes[2]);
|
||||
|
||||
let (nibbles2b, node2b) = account_updates.last().unwrap();
|
||||
assert_eq!(nibbles2b.inner[..], [0xB, 0x0]);
|
||||
assert_eq!(nibbles2b[..], [0xB, 0x0]);
|
||||
assert_eq!(node2a, node2b);
|
||||
tx.commit().unwrap();
|
||||
let tx = factory.provider_rw().unwrap();
|
||||
@ -1057,7 +1057,7 @@ mod tests {
|
||||
assert_eq!(account_updates.len(), 1);
|
||||
|
||||
let (nibbles1c, node1c) = account_updates.first().unwrap();
|
||||
assert_eq!(nibbles1c.inner[..], [0xB]);
|
||||
assert_eq!(nibbles1c[..], [0xB]);
|
||||
|
||||
assert_eq!(node1c.state_mask, TrieMask::new(0b1011));
|
||||
assert_eq!(node1c.tree_mask, TrieMask::new(0b0000));
|
||||
@ -1114,7 +1114,7 @@ mod tests {
|
||||
assert_eq!(account_updates.len(), 1);
|
||||
|
||||
let (nibbles1d, node1d) = account_updates.first().unwrap();
|
||||
assert_eq!(nibbles1d.inner[..], [0xB]);
|
||||
assert_eq!(nibbles1d[..], [0xB]);
|
||||
|
||||
assert_eq!(node1d.state_mask, TrieMask::new(0b1011));
|
||||
assert_eq!(node1d.tree_mask, TrieMask::new(0b0000));
|
||||
@ -1143,7 +1143,7 @@ mod tests {
|
||||
.iter()
|
||||
.filter_map(|entry| match entry {
|
||||
(TrieKey::AccountNode(nibbles), TrieOp::Update(node)) => {
|
||||
Some((nibbles.inner[..].into(), node.clone()))
|
||||
Some((nibbles.clone(), node.clone()))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@ -1170,7 +1170,7 @@ mod tests {
|
||||
let mut account_updates = HashMap::new();
|
||||
for item in walker {
|
||||
let (key, node) = item.unwrap();
|
||||
account_updates.insert(key.inner[..].into(), node);
|
||||
account_updates.insert(key, node);
|
||||
}
|
||||
|
||||
assert_trie_updates(&account_updates);
|
||||
@ -1231,7 +1231,7 @@ mod tests {
|
||||
.iter()
|
||||
.filter_map(|entry| match entry {
|
||||
(TrieKey::StorageNode(_, nibbles), TrieOp::Update(node)) => {
|
||||
Some((nibbles.inner[..].into(), node.clone()))
|
||||
Some((nibbles.clone().into(), node.clone()))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@ -1296,11 +1296,11 @@ mod tests {
|
||||
fn assert_trie_updates(account_updates: &HashMap<Nibbles, BranchNodeCompact>) {
|
||||
assert_eq!(account_updates.len(), 2);
|
||||
|
||||
let node = account_updates.get(&vec![0x3].as_slice().into()).unwrap();
|
||||
let node = account_updates.get(&[0x3][..]).unwrap();
|
||||
let expected = BranchNodeCompact::new(0b0011, 0b0001, 0b0000, vec![], None);
|
||||
assert_eq!(node, &expected);
|
||||
|
||||
let node = account_updates.get(&vec![0x3, 0x0, 0xA, 0xF].as_slice().into()).unwrap();
|
||||
let node = account_updates.get(&[0x3, 0x0, 0xA, 0xF][..]).unwrap();
|
||||
assert_eq!(node.state_mask, TrieMask::new(0b101100000));
|
||||
assert_eq!(node.tree_mask, TrieMask::new(0b000000000));
|
||||
assert_eq!(node.hash_mask, TrieMask::new(0b001000000));
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use super::TrieCursor;
|
||||
use crate::updates::TrieKey;
|
||||
use reth_db::{cursor::DbCursorRO, tables, DatabaseError};
|
||||
use reth_primitives::trie::{BranchNodeCompact, StoredNibbles};
|
||||
use reth_primitives::trie::{BranchNodeCompact, Nibbles};
|
||||
|
||||
/// A cursor over the account trie.
|
||||
#[derive(Debug)]
|
||||
@ -18,20 +18,20 @@ impl<C> TrieCursor for AccountTrieCursor<C>
|
||||
where
|
||||
C: DbCursorRO<tables::AccountsTrie>,
|
||||
{
|
||||
type Key = StoredNibbles;
|
||||
type Key = Nibbles;
|
||||
|
||||
fn seek_exact(
|
||||
&mut self,
|
||||
key: Self::Key,
|
||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||
Ok(self.0.seek_exact(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
||||
Ok(self.0.seek_exact(key)?.map(|value| (value.0.to_vec(), value.1)))
|
||||
}
|
||||
|
||||
fn seek(
|
||||
&mut self,
|
||||
key: Self::Key,
|
||||
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
|
||||
Ok(self.0.seek(key)?.map(|value| (value.0.inner.to_vec(), value.1)))
|
||||
Ok(self.0.seek(key)?.map(|value| (value.0.to_vec(), value.1)))
|
||||
}
|
||||
|
||||
fn current(&mut self) -> Result<Option<TrieKey>, DatabaseError> {
|
||||
@ -80,13 +80,13 @@ mod tests {
|
||||
}
|
||||
|
||||
let db_data = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
|
||||
assert_eq!(db_data[0].0.inner.to_vec(), data[0]);
|
||||
assert_eq!(db_data[1].0.inner.to_vec(), data[1]);
|
||||
assert_eq!(db_data[2].0.inner.to_vec(), data[2]);
|
||||
assert_eq!(db_data[3].0.inner.to_vec(), data[3]);
|
||||
assert_eq!(db_data[0].0.to_vec(), data[0]);
|
||||
assert_eq!(db_data[1].0.to_vec(), data[1]);
|
||||
assert_eq!(db_data[2].0.to_vec(), data[2]);
|
||||
assert_eq!(db_data[3].0.to_vec(), data[3]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.inner.to_vec()),
|
||||
cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.to_vec()),
|
||||
Some(data[1].clone())
|
||||
);
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ where
|
||||
.cursor
|
||||
.seek_by_key_subkey(self.hashed_address, key.clone())?
|
||||
.filter(|e| e.nibbles == key)
|
||||
.map(|value| (value.nibbles.inner.to_vec(), value.node)))
|
||||
.map(|value| (value.nibbles.to_vec(), value.node)))
|
||||
}
|
||||
|
||||
fn seek(
|
||||
@ -48,7 +48,7 @@ where
|
||||
Ok(self
|
||||
.cursor
|
||||
.seek_by_key_subkey(self.hashed_address, key)?
|
||||
.map(|value| (value.nibbles.inner.to_vec(), value.node)))
|
||||
.map(|value| (value.nibbles.to_vec(), value.node)))
|
||||
}
|
||||
|
||||
fn current(&mut self) -> Result<Option<TrieKey>, DatabaseError> {
|
||||
|
||||
@ -39,14 +39,14 @@ impl From<StoredSubNode> for CursorSubNode {
|
||||
Some(n) => n as i8,
|
||||
None => -1,
|
||||
};
|
||||
Self { key: Nibbles::from_hex(value.key), nibble, node: value.node }
|
||||
Self { key: Nibbles::new_unchecked(value.key), nibble, node: value.node }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CursorSubNode> for StoredSubNode {
|
||||
fn from(value: CursorSubNode) -> Self {
|
||||
let nibble = if value.nibble >= 0 { Some(value.nibble as u8) } else { None };
|
||||
Self { key: value.key.hex_data.to_vec(), nibble, node: value.node }
|
||||
Self { key: value.key.to_vec(), nibble, node: value.node }
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ impl CursorSubNode {
|
||||
pub fn full_key(&self) -> Nibbles {
|
||||
let mut out = self.key.clone();
|
||||
if self.nibble >= 0 {
|
||||
out.extend([self.nibble as u8]);
|
||||
out.push(self.nibble as u8);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use reth_db::{
|
||||
transaction::{DbTx, DbTxMut},
|
||||
};
|
||||
use reth_primitives::{
|
||||
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey},
|
||||
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey},
|
||||
B256,
|
||||
};
|
||||
use std::collections::{hash_map::IntoIter, HashMap};
|
||||
@ -14,7 +14,7 @@ use std::collections::{hash_map::IntoIter, HashMap};
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum TrieKey {
|
||||
/// A node in the account trie.
|
||||
AccountNode(StoredNibbles),
|
||||
AccountNode(Nibbles),
|
||||
/// A node in the storage trie.
|
||||
StorageNode(B256, StoredNibblesSubKey),
|
||||
/// Storage trie of an account.
|
||||
@ -78,9 +78,11 @@ impl TrieUpdates {
|
||||
|
||||
/// Extend the updates with account trie updates.
|
||||
pub fn extend_with_account_updates(&mut self, updates: HashMap<Nibbles, BranchNodeCompact>) {
|
||||
self.extend(updates.into_iter().map(|(nibbles, node)| {
|
||||
(TrieKey::AccountNode(nibbles.hex_data.to_vec().into()), TrieOp::Update(node))
|
||||
}));
|
||||
self.extend(
|
||||
updates
|
||||
.into_iter()
|
||||
.map(|(nibbles, node)| (TrieKey::AccountNode(nibbles), TrieOp::Update(node))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Extend the updates with storage trie updates.
|
||||
@ -90,10 +92,7 @@ impl TrieUpdates {
|
||||
updates: HashMap<Nibbles, BranchNodeCompact>,
|
||||
) {
|
||||
self.extend(updates.into_iter().map(|(nibbles, node)| {
|
||||
(
|
||||
TrieKey::StorageNode(hashed_address, nibbles.hex_data.to_vec().into()),
|
||||
TrieOp::Update(node),
|
||||
)
|
||||
(TrieKey::StorageNode(hashed_address, nibbles.into()), TrieOp::Update(node))
|
||||
}));
|
||||
}
|
||||
|
||||
@ -122,7 +121,7 @@ impl TrieUpdates {
|
||||
}
|
||||
}
|
||||
TrieOp::Update(node) => {
|
||||
if !nibbles.inner.is_empty() {
|
||||
if !nibbles.is_empty() {
|
||||
account_trie_cursor.upsert(nibbles, node)?;
|
||||
}
|
||||
}
|
||||
@ -136,7 +135,7 @@ impl TrieUpdates {
|
||||
TrieOp::Update(..) => unreachable!("Cannot update full storage trie."),
|
||||
},
|
||||
TrieKey::StorageNode(hashed_address, nibbles) => {
|
||||
if !nibbles.inner.is_empty() {
|
||||
if !nibbles.is_empty() {
|
||||
// Delete the old entry if it exists.
|
||||
if storage_trie_cursor
|
||||
.seek_by_key_subkey(hashed_address, nibbles.clone())?
|
||||
|
||||
@ -122,16 +122,16 @@ impl<C: TrieCursor> TrieWalker<C> {
|
||||
fn node(&mut self, exact: bool) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
|
||||
let key = self.key().expect("key must exist");
|
||||
let entry = if exact {
|
||||
self.cursor.seek_exact(key.hex_data.to_vec().into())?
|
||||
self.cursor.seek_exact(key.to_vec().into())?
|
||||
} else {
|
||||
self.cursor.seek(key.hex_data.to_vec().into())?
|
||||
self.cursor.seek(key.to_vec().into())?
|
||||
};
|
||||
|
||||
if let Some((_, node)) = &entry {
|
||||
assert!(!node.state_mask.is_empty());
|
||||
}
|
||||
|
||||
Ok(entry.map(|(k, v)| (Nibbles::from_hex(k), v)))
|
||||
Ok(entry.map(|(k, v)| (Nibbles::new_unchecked(k), v)))
|
||||
}
|
||||
|
||||
/// Consumes the next node in the trie, updating the stack.
|
||||
@ -313,7 +313,7 @@ mod tests {
|
||||
// We're traversing the path in lexigraphical order.
|
||||
for expected in expected {
|
||||
let got = walker.advance().unwrap();
|
||||
assert_eq!(got.unwrap(), Nibbles::from(&expected[..]));
|
||||
assert_eq!(got.unwrap(), Nibbles::new_unchecked(expected.clone()));
|
||||
}
|
||||
|
||||
// There should be 8 paths traversed in total from 3 branches.
|
||||
@ -361,26 +361,26 @@ mod tests {
|
||||
|
||||
// No changes
|
||||
let mut cursor = TrieWalker::new(&mut trie, Default::default());
|
||||
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![]))); // root
|
||||
assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([]))); // root
|
||||
assert!(cursor.can_skip_current_node); // due to root_hash
|
||||
cursor.advance().unwrap(); // skips to the end of trie
|
||||
assert_eq!(cursor.key(), None);
|
||||
|
||||
// We insert something that's not part of the existing trie/prefix.
|
||||
let mut changed = PrefixSetMut::default();
|
||||
changed.insert(&[0xF, 0x1]);
|
||||
changed.insert(Nibbles::new_unchecked([0xF, 0x1]));
|
||||
let mut cursor = TrieWalker::new(&mut trie, changed.freeze());
|
||||
|
||||
// Root node
|
||||
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![])));
|
||||
assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([])));
|
||||
// Should not be able to skip state due to the changed values
|
||||
assert!(!cursor.can_skip_current_node);
|
||||
cursor.advance().unwrap();
|
||||
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x2])));
|
||||
assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2])));
|
||||
cursor.advance().unwrap();
|
||||
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x2, 0x1])));
|
||||
assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x2, 0x1])));
|
||||
cursor.advance().unwrap();
|
||||
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![0x4])));
|
||||
assert_eq!(cursor.key(), Some(Nibbles::new_unchecked([0x4])));
|
||||
|
||||
cursor.advance().unwrap();
|
||||
assert_eq!(cursor.key(), None); // the end of trie
|
||||
|
||||
@ -113,7 +113,7 @@ HashedStorage {
|
||||
U256 StorageValue
|
||||
}
|
||||
AccountsTrie {
|
||||
StoredNibbles Nibbles "PK"
|
||||
Nibbles "PK"
|
||||
BranchNodeCompact Node
|
||||
}
|
||||
StoragesTrie {
|
||||
|
||||
Reference in New Issue
Block a user