feat: use nybbles crate (#5766)

This commit is contained in:
DaniPopes
2023-12-15 13:27:49 +02:00
committed by GitHub
parent e622f38540
commit 26a91c69f4
12 changed files with 124 additions and 644 deletions

15
Cargo.lock generated
View File

@@ -4577,6 +4577,20 @@ dependencies = [
"libc",
]
[[package]]
name = "nybbles"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47dddada2357f8e7786f4f4d837db7bdddec02c7c3e5da7840d92c70390f6dc0"
dependencies = [
"alloy-rlp",
"arbitrary",
"const-hex",
"proptest",
"serde",
"smallvec",
]
[[package]]
name = "object"
version = "0.32.1"
@@ -6209,6 +6223,7 @@ dependencies = [
"itertools 0.11.0",
"modular-bitfield",
"num_enum 0.7.1",
"nybbles",
"once_cell",
"plain_hasher",
"pprof",

View File

@@ -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.len() > self.skip_node_depth.unwrap_or_default())
matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.0.len() > self.skip_node_depth.unwrap_or_default())
{
in_mem_mismatched.push(in_mem);
incremental_mismatched.push(incr);

View File

@@ -311,7 +311,7 @@ impl Command {
"Nibbles don't match"
);
if incremental.1 != clean.1 &&
clean.0.len() > self.skip_node_depth.unwrap_or_default()
clean.0 .0.len() > self.skip_node_depth.unwrap_or_default()
{
incremental_account_mismatched.push(incremental);
clean_account_mismatched.push(clean);

View File

@@ -20,6 +20,7 @@ revm-primitives = { workspace = true, features = ["serde"] }
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
alloy-rlp = { workspace = true, features = ["arrayvec"] }
ethers-core = { workspace = true, default-features = false, optional = true }
nybbles = { version = "0.1", features = ["serde", "rlp"] }
# crypto
secp256k1 = { workspace = true, features = ["global-context", "recovery"] }
@@ -86,6 +87,7 @@ arbitrary = [
"revm-primitives/arbitrary",
"reth-rpc-types/arbitrary",
"reth-ethereum-forks/arbitrary",
"nybbles/arbitrary",
"dep:arbitrary",
"dep:proptest",
"dep:proptest-derive",

View File

@@ -21,7 +21,7 @@ mod subnode;
pub use self::{
account::TrieAccount,
mask::TrieMask,
nibbles::{Nibbles, StoredNibblesSubKey},
nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey},
storage::StorageTrieEntry,
subnode::StoredSubNode,
};

View File

@@ -1,15 +1,76 @@
use crate::Bytes;
use bytes::Buf;
use derive_more::{Deref, From, Index};
use derive_more::Deref;
use reth_codecs::Compact;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{
borrow::Borrow,
fmt,
mem::MaybeUninit,
ops::{Bound, RangeBounds},
};
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)]
@@ -57,600 +118,3 @@ impl Compact for StoredNibblesSubKey {
(Self(Nibbles::from_nibbles_unchecked(&buf[..len])), &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.
///
/// The internal representation is a [`SmallVec`] 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.
#[derive(
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Index,
From,
Deref,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
pub struct Nibbles(SmallVec<[u8; 64]>);
// Override `SmallVec::from` since it's not specialized for `Copy` types.
impl Clone for Nibbles {
#[inline]
fn clone(&self) -> Self {
Self(SmallVec::from_slice(&self.0))
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.0.clone_from(&source.0);
}
}
impl alloy_rlp::Encodable for Nibbles {
#[inline]
fn length(&self) -> usize {
alloy_rlp::Encodable::length(self.as_slice())
}
#[inline]
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
alloy_rlp::Encodable::encode(self.as_slice(), out)
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl proptest::arbitrary::Arbitrary for Nibbles {
type Parameters = ();
type Strategy = proptest::strategy::Map<
proptest::collection::VecStrategy<std::ops::RangeInclusive<u8>>,
fn(Vec<u8>) -> Self,
>;
#[inline]
fn arbitrary_with((): ()) -> Self::Strategy {
use proptest::prelude::*;
proptest::collection::vec(0x0..=0xf, 0..64).prop_map(Self::from_nibbles_unchecked)
}
}
impl Compact for Nibbles {
fn to_compact<B>(self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
buf.put_slice(self.as_slice());
self.len()
}
fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
let nibbles = &buf[..len];
buf.advance(len);
(Nibbles::from_nibbles_unchecked(nibbles), buf)
}
}
impl fmt::Debug for Nibbles {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Nibbles").field(&crate::hex::encode(self.as_slice())).finish()
}
}
impl From<Vec<u8>> for Nibbles {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self(SmallVec::from_vec(value))
}
}
impl From<Nibbles> for Vec<u8> {
#[inline]
fn from(value: Nibbles) -> Self {
value.0.into_vec()
}
}
impl From<Nibbles> for Bytes {
#[inline]
fn from(value: Nibbles) -> Self {
value.0.into_vec().into()
}
}
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 Extend<u8> for Nibbles {
#[inline]
fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl Nibbles {
/// Creates a new empty [`Nibbles`] instance.
#[inline]
pub const fn new() -> Self {
Self(SmallVec::new_const())
}
/// Creates a new [`Nibbles`] instance with the given capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self(SmallVec::with_capacity(capacity))
}
/// Creates a new [`Nibbles`] instance from nibble bytes, without checking their validity.
#[inline]
pub fn from_nibbles_unchecked<T: AsRef<[u8]>>(nibbles: T) -> Self {
Self(SmallVec::from_slice(nibbles.as_ref()))
}
/// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4
/// bits) that make up the input byte data.
#[inline]
pub fn unpack<T: AsRef<[u8]>>(data: T) -> Self {
let data = data.as_ref();
if data.len() <= 32 {
// SAFETY: checked length.
unsafe { Self::unpack_stack(data) }
} else {
Self::unpack_heap(data)
}
}
/// Unpacks on the stack.
///
/// # Safety
///
/// `data.len()` must be less than or equal to 32.
unsafe fn unpack_stack(data: &[u8]) -> Self {
let mut nibbles = MaybeUninit::<[u8; 64]>::uninit();
Self::unpack_to(data, nibbles.as_mut_ptr().cast());
let unpacked_len = data.len() * 2;
Self(SmallVec::from_buf_and_len_unchecked(nibbles, unpacked_len))
}
/// Unpacks on the heap.
fn unpack_heap(data: &[u8]) -> Self {
// Collect into a vec directly to avoid the smallvec overhead since we know this is going on
// the heap.
let unpacked_len = data.len() * 2;
let mut nibbles = Vec::with_capacity(unpacked_len);
// SAFETY: enough capacity.
unsafe { Self::unpack_to(data, nibbles.as_mut_ptr()) };
// SAFETY: within capacity and `unpack_to` initialized the memory.
unsafe { nibbles.set_len(unpacked_len) };
Self(SmallVec::from_vec(nibbles))
}
/// Unpacks into the given pointer.
///
/// # Safety
///
/// `ptr` must be valid for at least `data.len() * 2` bytes.
#[inline]
unsafe fn unpack_to(data: &[u8], ptr: *mut u8) {
for (i, &byte) in data.iter().enumerate() {
ptr.add(i * 2).write(byte >> 4);
ptr.add(i * 2 + 1).write(byte & 0x0f);
}
}
/// Packs the nibbles stored in the struct into a byte vector.
///
/// This method combines each pair of consecutive nibbles into a single byte,
/// effectively reducing the size of the data by a factor of two.
/// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and
/// added to the packed byte vector.
#[inline]
pub fn pack(&self) -> SmallVec<[u8; 32]> {
if self.len() <= 64 {
// SAFETY: checked length.
unsafe { self.pack_stack() }
} else {
self.pack_heap()
}
}
/// Packs on the stack.
///
/// # Safety
///
/// `self.len()` must be less than or equal to 32.
unsafe fn pack_stack(&self) -> SmallVec<[u8; 32]> {
let mut nibbles = MaybeUninit::<[u8; 32]>::uninit();
self.pack_to(nibbles.as_mut_ptr().cast());
let packed_len = (self.len() + 1) / 2;
SmallVec::from_buf_and_len_unchecked(nibbles, packed_len)
}
/// Packs on the heap.
fn pack_heap(&self) -> SmallVec<[u8; 32]> {
// Collect into a vec directly to avoid the smallvec overhead since we know this is going on
// the heap.
let packed_len = (self.len() + 1) / 2;
let mut vec = Vec::with_capacity(packed_len);
// SAFETY: enough capacity.
unsafe { self.pack_to(vec.as_mut_ptr()) };
// SAFETY: within capacity and `pack_to` initialized the memory.
unsafe { vec.set_len(packed_len) };
SmallVec::from_vec(vec)
}
/// Packs into the given pointer.
///
/// # Safety
///
/// `ptr` must be valid for at least `self.len() / 2 + IS_ODD` bytes.
#[inline]
unsafe fn pack_to(&self, ptr: *mut u8) {
for i in 0..self.len() / 2 {
ptr.add(i).write(self.get_byte_unchecked(i * 2));
}
if self.len() % 2 != 0 {
let i = self.len() / 2;
ptr.add(i).write(self.last().unwrap_unchecked() << 4);
}
}
/// Gets the byte at the given index by combining two consecutive nibbles.
///
/// # Safety
///
/// `i..i + 1` must be in range.
#[inline]
unsafe fn get_byte_unchecked(&self, i: usize) -> u8 {
debug_assert!(i + 1 < self.len(), "index {i}..{} out of bounds of {}", i + 1, self.len());
let hi = *self.get_unchecked(i);
let lo = *self.get_unchecked(i + 1);
(hi << 4) | lo
}
/// Encodes a given path leaf as a compact array of bytes, where each byte represents two
/// "nibbles" (half-bytes or 4 bits) of the original hex data, along with additional information
/// about the leaf itself.
///
/// The method takes the following input:
/// `is_leaf`: A boolean value indicating whether the current node is a leaf node or not.
///
/// The first byte of the encoded vector is set based on the `is_leaf` flag and the parity of
/// the hex data length (even or odd number of nibbles).
/// - If the node is an extension with even length, the header byte is `0x00`.
/// - If the node is an extension with odd length, the header byte is `0x10 + <first nibble>`.
/// - If the node is a leaf with even length, the header byte is `0x20`.
/// - If the node is a leaf with odd length, the header byte is `0x30 + <first nibble>`.
///
/// If there is an odd number of nibbles, store the first nibble in the lower 4 bits of the
/// first byte of encoded.
///
/// # Returns
///
/// A vector containing the compact byte representation of the nibble sequence, including the
/// header byte.
///
/// This vector's length is `self.len() / 2 + 1`. For stack-allocated nibbles, this is at most
/// 33 bytes, so 36 was chosen as the stack capacity to round up to the next usize-aligned
/// size.
///
/// # Examples
///
/// ```
/// # use reth_primitives::trie::Nibbles;
///
/// // Extension node with an even path length:
/// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]);
/// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x00, 0xAB, 0xCD]);
///
/// // Extension node with an odd path length:
/// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]);
/// assert_eq!(nibbles.encode_path_leaf(false)[..], [0x1A, 0xBC]);
///
/// // Leaf node with an even path length:
/// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]);
/// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x20, 0xAB, 0xCD]);
///
/// // Leaf node with an odd path length:
/// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C]);
/// assert_eq!(nibbles.encode_path_leaf(true)[..], [0x3A, 0xBC]);
/// ```
pub fn encode_path_leaf(&self, is_leaf: bool) -> SmallVec<[u8; 36]> {
let encoded_len = self.len() / 2 + 1;
let mut encoded = SmallVec::with_capacity(encoded_len);
// SAFETY: enough capacity.
unsafe { self.encode_path_leaf_to(is_leaf, encoded.as_mut_ptr()) };
// SAFETY: within capacity and `encode_path_leaf_to` initialized the memory.
unsafe { encoded.set_len(encoded_len) };
encoded
}
/// # Safety
///
/// `ptr` must be valid for at least `self.len() / 2 + 1` bytes.
#[inline]
unsafe fn encode_path_leaf_to(&self, is_leaf: bool, ptr: *mut u8) {
let odd_nibbles = self.len() % 2 != 0;
*ptr = self.encode_path_leaf_first_byte(is_leaf, odd_nibbles);
let mut nibble_idx = if odd_nibbles { 1 } else { 0 };
for i in 0..self.len() / 2 {
ptr.add(i + 1).write(self.get_byte_unchecked(nibble_idx));
nibble_idx += 2;
}
}
#[inline]
fn encode_path_leaf_first_byte(&self, is_leaf: bool, odd_nibbles: bool) -> u8 {
match (is_leaf, odd_nibbles) {
(true, true) => 0x30 | self[0],
(true, false) => 0x20,
(false, true) => 0x10 | self[0],
(false, false) => 0x00,
}
}
/// Increments the nibble sequence by one.
pub fn increment(&self) -> Option<Self> {
let mut incremented = self.clone();
for nibble in incremented.0.iter_mut().rev() {
debug_assert!(*nibble <= 0xf);
if *nibble < 0xf {
*nibble += 1;
return Some(incremented)
} else {
*nibble = 0;
}
}
None
}
/// 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.last() == Some(16)
}
/// Returns `true` if the current nibble sequence starts with the given prefix.
#[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[i] as usize
}
/// Returns the first nibble of the current nibble sequence.
#[inline]
pub fn first(&self) -> Option<u8> {
self.0.first().copied()
}
/// Returns the last nibble of the current nibble sequence.
#[inline]
pub fn last(&self) -> Option<u8> {
self.0.last().copied()
}
/// Returns the length of the common prefix between the current nibble sequence and the given.
#[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] {
return i
}
}
len
}
/// Returns the nibbles as a byte slice.
#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.0
}
/// Slice the current nibbles within the provided index range.
///
/// # Panics
///
/// Panics if the range is out of bounds.
#[inline]
#[track_caller]
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len(),
};
Self::from_nibbles_unchecked(&self[start..end])
}
/// Join two nibbles together.
#[inline]
pub fn join(&self, b: &Self) -> Self {
let mut nibbles = SmallVec::with_capacity(self.len() + b.len());
nibbles.extend_from_slice(self);
nibbles.extend_from_slice(b);
Self(nibbles)
}
/// Pushes a nibble to the end of the current nibbles.
#[inline]
pub fn push(&mut self, nibble: u8) {
self.0.push(nibble);
}
/// Extend the current nibbles with another nibbles.
#[inline]
pub fn extend_from_slice(&mut self, b: impl AsRef<[u8]>) {
self.0.extend_from_slice(b.as_ref());
}
/// Truncates the current nibbles to the given length.
#[inline]
pub fn truncate(&mut self, new_len: usize) {
self.0.truncate(new_len);
}
/// Clears the current nibbles.
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hex;
use proptest::prelude::*;
#[test]
fn hashed_regression() {
let nibbles = Nibbles::from_nibbles_unchecked(hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b"));
let path = nibbles.encode_path_leaf(true);
let expected = hex!("351464a4233f1852b5c47037e997f1ba852317ca924bf0f064a45f2b9710aa4b");
assert_eq!(path[..], expected);
}
#[test]
fn pack_nibbles() {
let tests = [
(&[][..], &[][..]),
(&[0xa], &[0xa0]),
(&[0xa, 0x0], &[0xa0]),
(&[0xa, 0xb], &[0xab]),
(&[0xa, 0xb, 0x2], &[0xab, 0x20]),
(&[0xa, 0xb, 0x2, 0x0], &[0xab, 0x20]),
(&[0xa, 0xb, 0x2, 0x7], &[0xab, 0x27]),
];
for (input, expected) in tests {
assert!(input.iter().all(|&x| x <= 0xf));
let nibbles = Nibbles::from_nibbles_unchecked(input);
let encoded = nibbles.pack();
assert_eq!(&encoded[..], expected);
}
}
#[test]
fn slice() {
const RAW: &[u8] = &hex!("05010406040a040203030f010805020b050c04070003070e0909070f010b0a0805020301070c0a0902040b0f000f0006040a04050f020b090701000a0a040b");
#[track_caller]
fn test_slice(range: impl RangeBounds<usize>, expected: &[u8]) {
let nibbles = Nibbles::from_nibbles_unchecked(RAW);
let sliced = nibbles.slice(range);
assert_eq!(sliced, Nibbles::from_nibbles_unchecked(expected));
assert_eq!(sliced.as_slice(), expected);
}
test_slice(0..0, &[]);
test_slice(0..1, &[0x05]);
test_slice(1..1, &[]);
test_slice(1..=1, &[0x01]);
test_slice(0..=1, &[0x05, 0x01]);
test_slice(0..2, &[0x05, 0x01]);
test_slice(..0, &[]);
test_slice(..1, &[0x05]);
test_slice(..=1, &[0x05, 0x01]);
test_slice(..2, &[0x05, 0x01]);
test_slice(.., RAW);
test_slice(..RAW.len(), RAW);
test_slice(0.., RAW);
test_slice(0..RAW.len(), RAW);
}
proptest! {
#[test]
fn pack_unpack_roundtrip(input in any::<Vec<u8>>()) {
let nibbles = Nibbles::unpack(&input);
prop_assert!(nibbles.iter().all(|&nibble| nibble <= 0xf));
let packed = nibbles.pack();
prop_assert_eq!(&packed[..], input);
}
#[test]
fn encode_path_first_byte(input in any::<Vec<u8>>()) {
prop_assume!(!input.is_empty());
let input = Nibbles::unpack(input);
prop_assert!(input.iter().all(|&nibble| nibble <= 0xf));
let input_is_odd = input.len() % 2 == 1;
let compact_leaf = input.encode_path_leaf(true);
let leaf_flag = compact_leaf[0];
// Check flag
assert_ne!(leaf_flag & 0x20, 0);
assert_eq!(input_is_odd, (leaf_flag & 0x10) != 0);
if input_is_odd {
assert_eq!(leaf_flag & 0x0f, input.first().unwrap());
}
let compact_extension = input.encode_path_leaf(false);
let extension_flag = compact_extension[0];
// Check first byte
assert_eq!(extension_flag & 0x20, 0);
assert_eq!(input_is_odd, (extension_flag & 0x10) != 0);
if input_is_odd {
assert_eq!(extension_flag & 0x0f, input.first().unwrap());
}
}
}
}

View File

@@ -9,17 +9,15 @@ use reth_primitives::{stage::StageCheckpoint, trie::*, *};
macro_rules! impl_compression_for_compact {
($($name:tt),+) => {
$(
impl Compress for $name
{
impl Compress for $name {
type Compressed = Vec<u8>;
fn compress_to_buf<B: bytes::BufMut + AsMut<[u8]>>(self, buf: &mut B) {
let _ = Compact::to_compact(self, buf);
let _ = Compact::to_compact(self, buf);
}
}
impl Decompress for $name
{
impl Decompress for $name {
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<$name, $crate::DatabaseError> {
let value = value.as_ref();
let (obj, _) = Compact::from_compact(&value, value.len());
@@ -37,8 +35,8 @@ impl_compression_for_compact!(
Receipt,
TxType,
StorageEntry,
Nibbles,
BranchNodeCompact,
StoredNibbles,
StoredNibblesSubKey,
StorageTrieEntry,
StoredBlockBodyIndices,

View File

@@ -36,7 +36,7 @@ use crate::{
};
use reth_primitives::{
stage::StageCheckpoint,
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey},
trie::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, 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 ) Nibbles | BranchNodeCompact
( AccountsTrie ) StoredNibbles | BranchNodeCompact
);
dupsort!(

View File

@@ -5,7 +5,7 @@ use crate::{
};
use reth_codecs::Compact;
use reth_primitives::{
trie::{Nibbles, StoredNibblesSubKey},
trie::{StoredNibbles, StoredNibblesSubKey},
Address, PruneSegment, B256,
};
@@ -102,18 +102,18 @@ impl Decode for String {
}
}
impl Encode for Nibbles {
impl Encode for StoredNibbles {
type Encoded = Vec<u8>;
// Delegate to the Compact implementation
fn encode(self) -> Self::Encoded {
let mut buf = Vec::with_capacity(self.len());
let mut buf = Vec::with_capacity(self.0.len());
self.to_compact(&mut buf);
buf
}
}
impl Decode for Nibbles {
impl Decode for StoredNibbles {
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
let buf = value.as_ref();
Ok(Self::from_compact(buf, buf.len()).0)

View File

@@ -1139,7 +1139,7 @@ mod tests {
.iter()
.filter_map(|entry| match entry {
(TrieKey::AccountNode(nibbles), TrieOp::Update(node)) => {
Some((nibbles.clone(), node.clone()))
Some((nibbles.0.clone(), node.clone()))
}
_ => None,
})
@@ -1163,12 +1163,13 @@ mod tests {
// read the account updates from the db
let mut accounts_trie = tx.tx_ref().cursor_read::<tables::AccountsTrie>().unwrap();
let walker = accounts_trie.walk(None).unwrap();
let mut account_updates = HashMap::new();
for item in walker {
let (key, node) = item.unwrap();
account_updates.insert(key, node);
}
let account_updates = walker
.into_iter()
.map(|item| {
let (key, node) = item.unwrap();
(key.0, node)
})
.collect();
assert_trie_updates(&account_updates);
}
@@ -1227,7 +1228,7 @@ mod tests {
.iter()
.filter_map(|entry| match entry {
(TrieKey::StorageNode(_, nibbles), TrieOp::Update(node)) => {
Some((nibbles.clone().into(), node.clone()))
Some((nibbles.0.clone(), node.clone()))
}
_ => None,
})

View File

@@ -1,7 +1,7 @@
use super::TrieCursor;
use crate::updates::TrieKey;
use reth_db::{cursor::DbCursorRO, tables, DatabaseError};
use reth_primitives::trie::{BranchNodeCompact, Nibbles};
use reth_primitives::trie::{BranchNodeCompact, StoredNibbles};
/// 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 = Nibbles;
type Key = StoredNibbles;
fn seek_exact(
&mut self,
key: Self::Key,
) -> Result<Option<(Vec<u8>, BranchNodeCompact)>, DatabaseError> {
Ok(self.0.seek_exact(key)?.map(|value| (value.0.to_vec(), value.1)))
Ok(self.0.seek_exact(key)?.map(|value| (value.0 .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.to_vec(), value.1)))
Ok(self.0.seek(key)?.map(|value| (value.0 .0.to_vec(), value.1)))
}
fn current(&mut self) -> Result<Option<TrieKey>, DatabaseError> {
@@ -79,13 +79,13 @@ mod tests {
}
let db_data = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
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!(db_data[0].0 .0.to_vec(), data[0]);
assert_eq!(db_data[1].0 .0.to_vec(), data[1]);
assert_eq!(db_data[2].0 .0.to_vec(), data[2]);
assert_eq!(db_data[3].0 .0.to_vec(), data[3]);
assert_eq!(
cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.to_vec()),
cursor.seek(hex!("0303040f").to_vec().into()).unwrap().map(|(k, _)| k.0.to_vec()),
Some(data[1].clone())
);
}

View File

@@ -5,7 +5,7 @@ use reth_db::{
transaction::{DbTx, DbTxMut},
};
use reth_primitives::{
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibblesSubKey},
trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, 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(Nibbles),
AccountNode(StoredNibbles),
/// A node in the storage trie.
StorageNode(B256, StoredNibblesSubKey),
/// Storage trie of an account.
@@ -79,9 +79,9 @@ 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), TrieOp::Update(node))),
updates.into_iter().map(|(nibbles, node)| {
(TrieKey::AccountNode(nibbles.into()), TrieOp::Update(node))
}),
);
}
@@ -121,7 +121,7 @@ impl TrieUpdates {
}
}
TrieOp::Update(node) => {
if !nibbles.is_empty() {
if !nibbles.0.is_empty() {
account_trie_cursor.upsert(nibbles, node)?;
}
}