Skip to content

Commit

Permalink
Merge branch 'main' into c0np4nn4/main
Browse files Browse the repository at this point in the history
  • Loading branch information
rkrasiuk committed Oct 30, 2024
2 parents 2b6178d + 37498a9 commit 5c3bb95
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 67 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.2](https://github.com/alloy-rs/trie/releases/tag/v0.7.2) - 2024-10-16

### Features

- [mask] Unset bit, count set bits, index of the first set bit ([#58](https://github.com/alloy-rs/trie/issues/58))
- `RlpNode::as_hash` ([#57](https://github.com/alloy-rs/trie/issues/57))

## [0.7.1](https://github.com/alloy-rs/trie/releases/tag/v0.7.1) - 2024-10-14

### Bug Fixes

- Use vector of arbitrary length for leaf node value ([#56](https://github.com/alloy-rs/trie/issues/56))

### Miscellaneous Tasks

- Release 0.7.1
- Allow `Zlib` in `deny.toml` ([#54](https://github.com/alloy-rs/trie/issues/54))

## [0.7.0](https://github.com/alloy-rs/trie/releases/tag/v0.7.0) - 2024-10-14

### Bug Fixes
Expand All @@ -13,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Miscellaneous Tasks

- Release 0.7.0
- [meta] Add CODEOWNERS ([#47](https://github.com/alloy-rs/trie/issues/47))

### Performance
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "alloy-trie"
version = "0.7.0"
version = "0.7.2"
authors = [
"rkrasiuk <rokrassyuk@gmail.com>",
"gakonst <me@gakonst.com>",
Expand Down
51 changes: 31 additions & 20 deletions src/hash_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ impl HashBuilder {

fn current_root(&self) -> B256 {
if let Some(node_ref) = self.stack.last() {
if node_ref.len() == B256::len_bytes() + 1 {
B256::from_slice(&node_ref[1..])
if let Some(hash) = node_ref.as_hash() {
hash
} else {
keccak256(node_ref)
}
Expand Down Expand Up @@ -492,20 +492,26 @@ mod tests {
let mut hb = HashBuilder::default().with_updates(true);

// We have 1 branch node update to be stored at 0x01, indicated by the first nibble.
// That branch root node has 2 branch node children present at 0x1 and 0x2.
// - 0x1 branch: It has the 2 empty items, at `0` and `1`.
// - 0x2 branch: It has the 2 empty items, at `0` and `2`.
// That branch root node has 4 children:
// - Leaf at nibble `0`: It has an empty value.
// - Branch at nibble `1`: It has 2 leaf nodes with empty values at nibbles `0` and `1`.
// - Branch at nibble `2`: It has 2 leaf nodes with empty values at nibbles `0` and `2`.
// - Leaf at nibble `3`: It has an empty value.
//
// This is enough information to construct the intermediate node value:
// 1. State Mask: 0b111. The children of the branch + the branch value at `0`, `1` and `2`.
// 2. Hash Mask: 0b110. Of the above items, `1` and `2` correspond to sub-branch nodes.
// 3. Tree Mask: 0b000.
// 4. Hashes: The 2 sub-branch roots, at `1` and `2`, calculated by hashing
// the 0th and 1st element for the 0x1 branch (according to the 3rd nibble),
// and the 0th and 2nd element for the 0x2 branch (according to the 3rd nibble).
// This basically means that every BranchNodeCompact is capable of storing up to 2 levels
// deep of nodes (?).
// 1. State Mask: 0b1111. All children of the branch node set at nibbles `0`, `1`, `2` and
// `3`.
// 2. Hash Mask: 0b0110. Of the above items, nibbles `1` and `2` correspond to children that
// are branch nodes.
// 3. Tree Mask: 0b0000. None of the children are stored in the database (yet).
// 4. Hashes: Hashes of the 2 sub-branch roots, at nibbles `1` and `2`. Calculated by
// hashing the 0th and 1st element for the branch at nibble `1` , and the 0th and 2nd
// element for the branch at nibble `2`. This basically means that every
// BranchNodeCompact is capable of storing up to 2 levels deep of nodes (?).
let data = BTreeMap::from([
(
// Leaf located at nibble `0` of the branch root node that doesn't result in
// creating another branch node
hex!("1000000000000000000000000000000000000000000000000000000000000000").to_vec(),
Vec::new(),
),
Expand All @@ -526,7 +532,8 @@ mod tests {
Vec::new(),
),
(
// unrelated leaf
// Leaf located at nibble `3` of the branch root node that doesn't result in
// creating another branch node
hex!("1320000000000000000000000000000000000000000000000000000000000000").to_vec(),
Vec::new(),
),
Expand All @@ -540,10 +547,14 @@ mod tests {
let (_, updates) = hb.split();

let update = updates.get(&Nibbles::from_nibbles_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`
assert_eq!(update.hashes.len(), 2); // calculated while the builder is running
// Nibbles 0, 1, 2, 3 have children
assert_eq!(update.state_mask, TrieMask::new(0b1111));
// None of the children are stored in the database
assert_eq!(update.tree_mask, TrieMask::new(0b0000));
// Children under nibbles `1` and `2` are branche nodes with `hashes`
assert_eq!(update.hash_mask, TrieMask::new(0b0110));
// Calculated when running the hash builder
assert_eq!(update.hashes.len(), 2);

assert_eq!(_root, triehash_trie_root(data));
}
Expand Down Expand Up @@ -579,8 +590,8 @@ mod tests {
#[test]
fn manual_branch_node_ok() {
let raw_input = vec![
(hex!("646f").to_vec(), RlpNode::from_raw(&hex!("76657262")).unwrap()),
(hex!("676f6f64").to_vec(), RlpNode::from_raw(&hex!("7075707079")).unwrap()),
(hex!("646f").to_vec(), hex!("76657262").to_vec()),
(hex!("676f6f64").to_vec(), hex!("7075707079").to_vec()),
];
let expected = triehash_trie_root(raw_input.clone());

Expand Down
23 changes: 23 additions & 0 deletions src/mask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,31 @@ impl TrieMask {
self.0 == 0
}

/// Returns the number of bits set in the mask.
#[inline]
pub const fn count_bits(self) -> u8 {
self.0.count_ones() as u8
}

/// Returns the index of the first bit set in the mask, or `None` if the mask is empty.
#[inline]
pub const fn first_set_bit_index(self) -> Option<u8> {
if self.is_empty() {
None
} else {
Some(self.0.trailing_zeros() as u8)
}
}

/// Set bit at a specified index.
#[inline]
pub fn set_bit(&mut self, index: u8) {
self.0 |= 1u16 << index;
}

/// Unset bit at a specified index.
#[inline]
pub fn unset_bit(&mut self, index: u8) {
self.0 &= !(1u16 << index);
}
}
28 changes: 12 additions & 16 deletions src/nodes/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,11 @@ impl ExactSizeIterator for BranchChildrenIter<'_> {
/// A struct representing a branch node in an Ethereum trie.
///
/// A branch node can have up to 16 children, each corresponding to one of the possible nibble
/// values (0 to 15) in the trie's path.
/// values (`0` to `f`) in the trie's path.
///
/// The masks in a BranchNode are used to efficiently represent and manage information about the
/// presence and types of its children. They are bitmasks, where each bit corresponds to a nibble
/// (half-byte, or 4 bits) value from 0 to 15.
/// (half-byte, or 4 bits) value from `0` to `f`.
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BranchNodeCompact {
Expand All @@ -262,17 +262,16 @@ pub struct BranchNodeCompact {
/// child exists for the nibble value i. If the bit is unset (0), it means there is no child
/// for that nibble value.
pub state_mask: TrieMask,
/// The bitmask representing the internal (unhashed) children at the
/// respective nibble positions in the trie. If the bit at position `i` (counting from the
/// right) is set (1) and also present in the state_mask, it indicates that the
/// corresponding child at the nibble value `i` is an internal child. If the bit is unset
/// (0), it means the child is not an internal child.
/// The bitmask representing the children at the respective nibble positions in the trie that
/// are also stored in the database. If the bit at position `i` (counting from the right)
/// is set (1) and also present in the state_mask, it indicates that the corresponding
/// child at the nibble value `i` is stored in the database. If the bit is unset (0), it means
/// the child is not stored in the database.
pub tree_mask: TrieMask,
/// The bitmask representing the hashed children at the respective nibble
/// positions in the trie. If the bit at position `i` (counting from the right) is set (1) and
/// also present in the state_mask, it indicates that the corresponding child at the nibble
/// value `i` is a hashed child. If the bit is unset (0), it means the child is not a
/// hashed child.
/// The bitmask representing the hashed children at the respective nibble positions in the
/// trie. If the bit at position `i` (counting from the right) is set (1) and also present
/// in the state_mask, it indicates that the corresponding child at the nibble value `i` is
/// a hashed child. If the bit is unset (0), it means the child is not a hashed child.
pub hash_mask: TrieMask,
/// Collection of hashes associated with the children of the branch node.
/// Each child hash is calculated by hashing two consecutive sub-branch roots.
Expand Down Expand Up @@ -334,10 +333,7 @@ mod tests {
let encoded = alloy_rlp::encode(&sparse_node);
assert_eq!(BranchNode::decode(&mut &encoded[..]).unwrap(), sparse_node);

let leaf_child = LeafNode::new(
Nibbles::from_nibbles(hex!("0203")),
RlpNode::from_raw(&hex!("1234")).unwrap(),
);
let leaf_child = LeafNode::new(Nibbles::from_nibbles(hex!("0203")), hex!("1234").to_vec());
let mut buf = vec![];
let leaf_rlp = leaf_child.as_ref().rlp(&mut buf);
let branch_with_leaf = BranchNode::new(vec![leaf_rlp.clone()], TrieMask::new(0b0010));
Expand Down
8 changes: 4 additions & 4 deletions src/nodes/leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct LeafNode {
/// The key for this leaf node.
pub key: Nibbles,
/// The node value.
pub value: RlpNode,
pub value: Vec<u8>,
}

impl fmt::Debug for LeafNode {
Expand Down Expand Up @@ -58,7 +58,7 @@ impl Decodable for LeafNode {
};

let key = unpack_path_to_nibbles(first, &encoded_key[1..]);
let value = RlpNode::decode(&mut bytes)?;
let value = Bytes::decode(&mut bytes)?.into();
Ok(Self { key, value })
}
}
Expand All @@ -71,7 +71,7 @@ impl LeafNode {
pub const ODD_FLAG: u8 = 0x30;

/// Creates a new leaf node with the given key and value.
pub const fn new(key: Nibbles, value: RlpNode) -> Self {
pub const fn new(key: Nibbles, value: Vec<u8>) -> Self {
Self { key, value }
}

Expand Down Expand Up @@ -155,7 +155,7 @@ mod tests {
fn rlp_leaf_node_roundtrip() {
let nibble = Nibbles::from_nibbles_unchecked(hex!("0604060f"));
let val = hex!("76657262");
let leaf = LeafNode::new(nibble, RlpNode::from_raw(&val).unwrap());
let leaf = LeafNode::new(nibble, val.to_vec());
let rlp = leaf.as_ref().rlp(&mut vec![]);
assert_eq!(rlp.as_ref(), hex!("c98320646f8476657262"));
assert_eq!(LeafNode::decode(&mut &rlp[..]).unwrap(), leaf);
Expand Down
9 changes: 5 additions & 4 deletions src/nodes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Various branch nodes produced by the hash builder.
use alloy_primitives::B256;
use alloy_primitives::{Bytes, B256};
use alloy_rlp::{Decodable, Encodable, Header, EMPTY_STRING_CODE};
use core::ops::Range;
use nybbles::Nibbles;
Expand Down Expand Up @@ -112,7 +112,8 @@ impl Decodable for TrieNode {

let key = unpack_path_to_nibbles(first, &encoded_key[1..]);
let node = if key_flag == LeafNode::EVEN_FLAG || key_flag == LeafNode::ODD_FLAG {
Self::Leaf(LeafNode::new(key, RlpNode::decode(&mut items.remove(0))?))
let value = Bytes::decode(&mut items.remove(0))?.into();
Self::Leaf(LeafNode::new(key, value))
} else {
// We don't decode value because it is expected to be RLP encoded.
Self::Extension(ExtensionNode::new(
Expand Down Expand Up @@ -270,7 +271,7 @@ mod tests {
fn rlp_zero_value_leaf_roundtrip() {
let leaf = TrieNode::Leaf(LeafNode::new(
Nibbles::from_nibbles_unchecked(hex!("0604060f")),
RlpNode::from_raw(&alloy_rlp::encode(alloy_primitives::U256::ZERO)).unwrap(),
alloy_rlp::encode(alloy_primitives::U256::ZERO),
));
let rlp = leaf.rlp(&mut vec![]);
assert_eq!(rlp[..], hex!("c68320646f8180"));
Expand All @@ -282,7 +283,7 @@ mod tests {
// leaf
let leaf = TrieNode::Leaf(LeafNode::new(
Nibbles::from_nibbles_unchecked(hex!("0604060f")),
RlpNode::from_raw(&hex!("76657262")).unwrap(),
hex!("76657262").to_vec(),
));
let rlp = leaf.rlp(&mut vec![]);
assert_eq!(rlp[..], hex!("c98320646f8476657262"));
Expand Down
10 changes: 10 additions & 0 deletions src/nodes/rlp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ impl RlpNode {
pub fn as_slice(&self) -> &[u8] {
&self.0
}

/// Returns hash if this is an RLP-encoded hash
#[inline]
pub fn as_hash(&self) -> Option<B256> {
if self.len() == B256::len_bytes() + 1 {
Some(B256::from_slice(&self.0[1..]))
} else {
None
}
}
}

#[cfg(feature = "arbitrary")]
Expand Down
Loading

0 comments on commit 5c3bb95

Please sign in to comment.