Skip to content

Commit

Permalink
build: add binary and edge node tree hash implementation (#37)
Browse files Browse the repository at this point in the history
* build: add binary and edge node tree hash implementation

* refactor: move TreeHashFunctionImpl to production and add tests
  • Loading branch information
aner-starkware authored Apr 15, 2024
1 parent 6af2975 commit ac3f787
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 47 deletions.
2 changes: 1 addition & 1 deletion crates/committer/src/hash/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::types::Felt;
pub(crate) struct HashInputPair(pub Felt, pub Felt);

#[allow(dead_code)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct HashOutput(pub Felt);

#[allow(dead_code)]
Expand Down
4 changes: 2 additions & 2 deletions crates/committer/src/patricia_merkle_tree/filled_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub(crate) enum NodeData<L: LeafDataTrait> {

#[allow(dead_code)]
pub(crate) struct BinaryData {
left_hash: HashOutput,
right_hash: HashOutput,
pub(crate) left_hash: HashOutput,
pub(crate) right_hash: HashOutput,
}

#[allow(dead_code)]
Expand Down
27 changes: 0 additions & 27 deletions crates/committer/src/patricia_merkle_tree/test_utils.rs

This file was deleted.

42 changes: 37 additions & 5 deletions crates/committer/src/patricia_merkle_tree/types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use crate::hash::types::{HashFunction, HashOutput};
use crate::patricia_merkle_tree::filled_node::NodeData;
use std::marker::PhantomData;

use crate::hash::types::{HashFunction, HashInputPair, HashOutput};
use crate::patricia_merkle_tree::filled_node::{BinaryData, LeafData, NodeData};
use crate::types::Felt;

#[cfg(test)]
#[path = "test_utils.rs"]
mod test_utils;
#[cfg(test)]
#[path = "types_test.rs"]
pub mod types_test;
Expand All @@ -14,6 +13,39 @@ pub(crate) trait TreeHashFunction<L: LeafDataTrait, H: HashFunction> {
fn compute_node_hash(node_data: NodeData<L>) -> HashOutput;
}

pub(crate) struct TreeHashFunctionImpl<H: HashFunction> {
_hash_function: PhantomData<H>,
}

/// Implementation of TreeHashFunction.
// TODO(Aner, 11/4/25): Implement the function for LeafData::StorageValue and LeafData::StateTreeTuple
// TODO(Aner, 11/4/24): Verify the correctness of the implementation.
impl<H: HashFunction> TreeHashFunction<LeafData, H> for TreeHashFunctionImpl<H> {
fn compute_node_hash(node_data: NodeData<LeafData>) -> HashOutput {
match node_data {
NodeData::Binary(BinaryData {
left_hash,
right_hash,
}) => H::compute_hash(HashInputPair(left_hash.0, right_hash.0)),
NodeData::Edge(EdgeData {
bottom_hash: hash_output,
path_to_bottom: PathToBottom { path, length },
}) => HashOutput(
H::compute_hash(HashInputPair(hash_output.0, path.0)).0 + Felt::from(length.0),
),
NodeData::Leaf(leaf_data) => match leaf_data {
LeafData::StorageValue(_) => todo!(),
LeafData::CompiledClassHash(compiled_class_hash) => {
HashOutput(compiled_class_hash.0)
}
LeafData::StateTreeTuple { .. } => {
todo!()
}
},
}
}
}

#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct NodeIndex(pub Felt);
Expand Down
57 changes: 56 additions & 1 deletion crates/committer/src/patricia_merkle_tree/types_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use crate::patricia_merkle_tree::types::{EdgePath, EdgePathLength, NodeIndex, PathToBottom};
use crate::hash::types::{HashFunction, HashInputPair, HashOutput, PedersenHashFunction};
use crate::patricia_merkle_tree::filled_node::{BinaryData, NodeData};
use crate::patricia_merkle_tree::types::TreeHashFunction;
use crate::patricia_merkle_tree::types::{
EdgePath, EdgePathLength, NodeIndex, PathToBottom, TreeHashFunctionImpl,
};
use crate::types::Felt;
use rstest::rstest;
#[rstest]
Expand All @@ -22,3 +27,53 @@ fn test_compute_bottom_index(
let expected = NodeIndex(Felt::from(expected));
assert_eq!(bottom_index, expected);
}

#[rstest]
#[case(Felt::ONE, Felt::TWO, Felt::from_hex("0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026").unwrap())]
#[case(Felt::from(0xBE_u128), Felt::from(0xA0BEE_u128), Felt::from_hex("0x4e8f149d7d5adb77a8c85b631a3acb6fb9aa5ecb06ea4ec105753629243e380").unwrap())]
#[case(Felt::from(0x1234_u128), Felt::from(0xABCD_u128), Felt::from_hex("0x615bb8d47888d2987ad0c63fc06e9e771930986a4dd8adc55617febfcf3639e").unwrap())]
fn test_tree_hash_function_impl_binary_node(
#[case] left_hash: Felt,
#[case] right_hash: Felt,
#[case] expected_hash: Felt,
) {
let hash_output = TreeHashFunctionImpl::<PedersenHashFunction>::compute_node_hash(
NodeData::Binary(BinaryData {
left_hash: HashOutput(left_hash),
right_hash: HashOutput(right_hash),
}),
);
assert_eq!(
hash_output,
PedersenHashFunction::compute_hash(HashInputPair(left_hash, right_hash))
);
assert_eq!(hash_output, HashOutput(expected_hash));
}

#[rstest]
#[case(Felt::ONE, Felt::TWO, 3, Felt::from_hex("0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58029").unwrap())]
#[case(Felt::from(0xBE_u128), Felt::from(0xA0BEE_u128), 0xBB, Felt::from_hex("0x4e8f149d7d5adb77a8c85b631a3acb6fb9aa5ecb06ea4ec105753629243e43b").unwrap())]
#[case(Felt::from(0x1234ABCD_u128),Felt::from(42_u128),6, Felt::from_hex("0x1d937094c09b5f8e26a662d21911871e3cbc6858d55cc49af9848ea6fed4e9").unwrap())]
fn test_tree_hash_function_impl_edge_node(
#[case] bottom_hash: Felt,
#[case] edge_path: Felt,
#[case] length: u8,
#[case] expected_hash: Felt,
) {
use crate::patricia_merkle_tree::types::EdgeData;

let hash_output =
TreeHashFunctionImpl::<PedersenHashFunction>::compute_node_hash(NodeData::Edge(EdgeData {
bottom_hash: HashOutput(bottom_hash),
path_to_bottom: PathToBottom {
path: EdgePath(edge_path),
length: EdgePathLength(length),
},
}));
let direct_hash_computation = HashOutput(
PedersenHashFunction::compute_hash(HashInputPair(bottom_hash, edge_path)).0
+ Felt::from(length),
);
assert_eq!(hash_output, HashOutput(expected_hash));
assert_eq!(hash_output, direct_hash_computation);
}
31 changes: 20 additions & 11 deletions crates/committer/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use starknet_types_core::felt::Felt as StarknetTypesFelt;
use starknet_types_core::felt::{Felt as StarknetTypesFelt, FromStrError};

#[derive(Eq, PartialEq, Clone, Copy, Debug, Default, Hash, derive_more::Add)]
pub(crate) struct Felt(StarknetTypesFelt);

impl From<StarknetTypesFelt> for Felt {
fn from(felt: StarknetTypesFelt) -> Self {
Self(felt)
}
}

impl From<u128> for Felt {
fn from(value: u128) -> Self {
Self(value.into())
}
#[macro_export]
macro_rules! impl_from {
($to:ty, $from:ty, $($other_from: ty),+) => {
$crate::impl_from!($to, $from);
$crate::impl_from!($to $(, $other_from)*);
};
($to:ty, $from:ty) => {
impl From<$from> for $to {
fn from(value: $from) -> Self {
Self(value.into())
}
}
};
}
impl_from!(Felt, StarknetTypesFelt, u128, u8);

impl From<Felt> for StarknetTypesFelt {
fn from(felt: Felt) -> Self {
Expand All @@ -39,4 +43,9 @@ impl Felt {
pub fn pow(&self, exponent: impl Into<u128>) -> Self {
Self(self.0.pow(exponent.into()))
}

/// Parse a hex-encoded number into `Felt`.
pub fn from_hex(hex_string: &str) -> Result<Self, FromStrError> {
Ok(StarknetTypesFelt::from_hex(hex_string)?.into())
}
}

0 comments on commit ac3f787

Please sign in to comment.