Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TreeHash for bitfield #18

Merged
merged 4 commits into from
Dec 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions tree_hash/Cargo.toml
Original file line number Diff line number Diff line change
@@ -13,13 +13,14 @@ categories = ["cryptography::cryptocurrencies"]
[dependencies]
alloy-primitives = "0.8.0"
ethereum_hashing = "0.7.0"
ethereum_ssz = "0.8.0"
smallvec = "1.6.1"
typenum = "1.12.0"

[dev-dependencies]
rand = "0.8.5"
tree_hash_derive = { path = "../tree_hash_derive", version = "0.8.0" }
ethereum_ssz = "0.7"
ethereum_ssz_derive = "0.7"
ethereum_ssz_derive = "0.8.0"

[features]
arbitrary = ["alloy-primitives/arbitrary"]
93 changes: 93 additions & 0 deletions tree_hash/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::*;
use alloy_primitives::{Address, B256, U128, U256};
use ssz::{Bitfield, Fixed, Variable};
use std::sync::Arc;
use typenum::Unsigned;

fn int_to_hash256(int: u64) -> Hash256 {
let mut bytes = [0; HASHSIZE];
@@ -197,9 +199,68 @@
}
}

/// A helper function providing common functionality for finding the Merkle root of some bytes that
/// represent a bitfield.
pub fn bitfield_bytes_tree_hash_root<N: Unsigned>(bytes: &[u8]) -> Hash256 {
let byte_size = (N::to_usize() + 7) / 8;
let leaf_count = (byte_size + BYTES_PER_CHUNK - 1) / BYTES_PER_CHUNK;

let mut hasher = MerkleHasher::with_leaves(leaf_count);

hasher
.write(bytes)

Check warning on line 211 in tree_hash/src/impls.rs

Codecov / codecov/patch

tree_hash/src/impls.rs#L211

Added line #L211 was not covered by tests
.expect("bitfield should not exceed tree hash leaf limit");

hasher
.finish()
.expect("bitfield tree hash buffer should not exceed leaf limit")
}

impl<N: Unsigned + Clone> TreeHash for Bitfield<Variable<N>> {
fn tree_hash_type() -> TreeHashType {
TreeHashType::List

Check warning on line 221 in tree_hash/src/impls.rs

Codecov / codecov/patch

tree_hash/src/impls.rs#L220-L221

Added lines #L220 - L221 were not covered by tests
}

fn tree_hash_packed_encoding(&self) -> PackedEncoding {
unreachable!("List should never be packed.")
}

fn tree_hash_packing_factor() -> usize {
unreachable!("List should never be packed.")
}

fn tree_hash_root(&self) -> Hash256 {
// Note: we use `as_slice` because it does _not_ have the length-delimiting bit set (or
// present).
let root = bitfield_bytes_tree_hash_root::<N>(self.as_slice());
mix_in_length(&root, self.len())
}
}

impl<N: Unsigned + Clone> TreeHash for Bitfield<Fixed<N>> {
fn tree_hash_type() -> TreeHashType {
TreeHashType::Vector

Check warning on line 242 in tree_hash/src/impls.rs

Codecov / codecov/patch

tree_hash/src/impls.rs#L241-L242

Added lines #L241 - L242 were not covered by tests
}

fn tree_hash_packed_encoding(&self) -> PackedEncoding {
unreachable!("Vector should never be packed.")
}

fn tree_hash_packing_factor() -> usize {
unreachable!("Vector should never be packed.")
}

fn tree_hash_root(&self) -> Hash256 {
bitfield_bytes_tree_hash_root::<N>(self.as_slice())
}
}

#[cfg(test)]
mod test {
use super::*;
use ssz::{BitList, BitVector};
use std::str::FromStr;
use typenum::{U32, U8};

#[test]
fn bool() {
@@ -237,4 +298,36 @@
]
);
}

#[test]
fn bitvector() {
let empty_bitvector = BitVector::<U8>::new();
assert_eq!(empty_bitvector.tree_hash_root(), Hash256::ZERO);

let small_bitvector_bytes = vec![0xff_u8, 0xee, 0xdd, 0xcc];
let small_bitvector =
BitVector::<U32>::from_bytes(small_bitvector_bytes.clone().into()).unwrap();
assert_eq!(
small_bitvector.tree_hash_root().as_slice()[..4],
small_bitvector_bytes
);
}

#[test]
fn bitlist() {
let empty_bitlist = BitList::<U8>::with_capacity(8).unwrap();
assert_eq!(
empty_bitlist.tree_hash_root(),
Hash256::from_str("0x5ac78d953211aa822c3ae6e9b0058e42394dd32e5992f29f9c12da3681985130")
.unwrap()
);

let mut small_bitlist = BitList::<U32>::with_capacity(4).unwrap();
small_bitlist.set(1, true).unwrap();
assert_eq!(
small_bitlist.tree_hash_root(),
Hash256::from_str("0x7eb03d394d83a389980b79897207be3a6512d964cb08978bb7f3cfc0db8cfb8a")
.unwrap()
);
}
}
Loading