Skip to content

Commit

Permalink
feat: Implemented namespace hash digest (#101)
Browse files Browse the repository at this point in the history
* feat : Implementing helper fns

* feat : Draft PR (namespace hash digest and namespace merkle tree)

* feat : Implemented namespace message digest and added tests

---------

Co-authored-by: Brandon Roberts <[email protected]>
  • Loading branch information
Akashneelesh and b-j-roberts committed Feb 16, 2024
1 parent d5fcd3d commit 01c1ad6
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/tree.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ mod binary {
}
}
mod namespace {
mod hasher;
mod merkle_tree;
#[cfg(test)]
mod tests {
mod test_hasher;
mod test_merkle_tree;
}
}
Expand Down
63 changes: 63 additions & 0 deletions src/tree/namespace/hasher.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use alexandria_bytes::Bytes;
use alexandria_bytes::BytesTrait;
use alexandria_math::U256BitShift;
use blobstream_sn::tree::consts::{LEAF_PREFIX, NODE_PREFIX, parity_share_namespace};
use blobstream_sn::tree::namespace::merkle_tree::{
Namespace, NamespaceNode, NamespaceMerkleMultiproof, NamespaceMerkleProof
};

fn leaf_digest(namespace: Namespace, data: @Bytes) -> NamespaceNode {
let mut bytes = BytesTrait::new_empty();
bytes.append_u8(LEAF_PREFIX);
bytes.append_u8(namespace.version);
append_bytes28(ref bytes, namespace.id);
bytes.concat(data);
return NamespaceNode { min: namespace, max: namespace, digest: bytes.sha256() };
}

fn namespace_min(l: Namespace, r: Namespace) -> Namespace {
if l < r {
return l;
}
return r;
}

fn namespace_max(l: Namespace, r: Namespace) -> Namespace {
if l > r {
return l;
}
return r;
}

fn node_digest(left: NamespaceNode, right: NamespaceNode) -> NamespaceNode {
let mut min: Namespace = namespace_min(left.min, right.min);
let mut max: Namespace = namespace_max(left.max, right.max);
if (left.min == parity_share_namespace()) {
max = parity_share_namespace();
} else if (right.min == parity_share_namespace()) {
max = left.max;
}

let mut bytes = BytesTrait::new_empty();
bytes.append_u8(NODE_PREFIX);
bytes.append_u8(left.min.version);
append_bytes28(ref bytes, left.min.id);
bytes.append_u8(left.max.version);
append_bytes28(ref bytes, left.max.id);
bytes.append_u256(left.digest);
bytes.append_u8(right.min.version);
append_bytes28(ref bytes, right.min.id);
bytes.append_u8(right.max.version);
append_bytes28(ref bytes, right.max.id);
bytes.append_u256(right.digest);

return NamespaceNode { min: min, max: max, digest: bytes.sha256() };
}

fn append_bytes28(ref self: Bytes, value: bytes31) {
let mut value_u256: u256 = value.into();
value_u256 = U256BitShift::shl(value_u256, 32);
let mut bytes28Bytes: Bytes = BytesTrait::new(28, array![value_u256.high, value_u256.low]);
self.concat(@bytes28Bytes);
}

4 changes: 2 additions & 2 deletions src/tree/namespace/merkle_tree.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct NamespaceMerkleProof {
key: u256,
num_leaves: u256,
}

// mod NamespaceMerkleTree {
// use super::{Namespace, NamespaceNode, NamespaceMerkleMultiproof, NamespaceMerkleProof};
// use alexandria_bytes::BytesTrait;
Expand Down Expand Up @@ -86,6 +87,7 @@ impl NamespacePartialOrd of PartialOrd<Namespace> {
}
}
}

impl NamespacePartialEq of PartialEq<Namespace> {
#[inline(always)]
fn eq(lhs: @Namespace, rhs: @Namespace) -> bool {
Expand All @@ -109,8 +111,6 @@ impl NamespacePartialEq of PartialEq<Namespace> {
}
}


fn namespace_node_eq(first: NamespaceNode, second: NamespaceNode) -> bool {
return first.min == second.min && first.max == second.max && (first.digest == second.digest);
}

112 changes: 112 additions & 0 deletions src/tree/namespace/tests/test_hasher.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use alexandria_bytes::BytesTrait;
use blobstream_sn::tree::consts::{parity_share_namespace};
use blobstream_sn::tree::namespace::hasher::{leaf_digest, node_digest};
use blobstream_sn::tree::namespace::merkle_tree::{Namespace, NamespaceNode, namespace_node_eq};
use core::option::OptionTrait;
use core::traits::Into;

#[test]
fn leaf_digest_empty_test() {
let bytesval: bytes31 = bytes31_const::<
0x00000000000000000000000000000000000000000000000000000000
>();

let nid: Namespace = Namespace { version: 0x00, id: bytesval };

let expected: NamespaceNode = NamespaceNode {
min: nid,
max: nid,
digest: 0x0679246d6c4216de0daa08e5523fb2674db2b6599c3b72ff946b488a15290b62
};

let data = BytesTrait::new_empty();

let node: NamespaceNode = leaf_digest(nid, @data);

let res = namespace_node_eq(node, expected);
assert!(res, "Not equal to expected digest");
}

#[test]
fn test_leaf_digest_some() {
let bytesval: bytes31 = bytes31_const::<
0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde
>();
let nid = Namespace { version: 0xde, id: bytesval, };

let expected = NamespaceNode {
min: nid,
max: nid,
digest: 0x3624c7f7169cb5bbd0d010b851ebd0edca10b2a1b126f5fb1a6d5e0d98356e63
};

let mut data = BytesTrait::new_empty();
data.append_u8(0x69);

let node = leaf_digest(nid, @data);
assert!(node.digest == expected.digest, "Not equal to expected digest");
}

#[test]
fn test_node_digest() {
let nid_left = Namespace {
version: 0x00,
id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000000>()
};
let nid_right = Namespace {
version: 0xde,
id: bytes31_const::<0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde>()
};
let expected = NamespaceNode {
min: nid_left,
max: nid_right,
digest: 0x95cad48bc181484c851004cf772abe767391e19549d3b8192b55b1d654a71bcd
};
let left = NamespaceNode {
min: nid_left,
max: nid_left,
digest: 0xdb55da3fc3098e9c42311c6013304ff36b19ef73d12ea932054b5ad51df4f49d
};

let right = NamespaceNode {
min: nid_right,
max: nid_right,
digest: 0xc75cb66ae28d8ebc6eded002c28a8ba0d06d3a78c6b5cbf9b2ade051f0775ac4
};
let node = node_digest(left, right);
let res = namespace_node_eq(node, expected);
assert!(res, "Not equal to expected digest");
}

#[test]
fn test_node_parity() {
let nid_min = Namespace {
version: 0x00,
id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000000>()
};
let nid_max = Namespace {
version: 0xde,
id: bytes31_const::<0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde>()
};
let expected = NamespaceNode {
min: nid_min,
max: nid_max,
digest: 0xc6960f535d4ab0aed075aed34a116725e8035012ceffe5405ae72abe3bcaa28f
};

let left = NamespaceNode {
min: nid_min,
max: nid_max,
digest: 0xdb55da3fc3098e9c42311c6013304ff36b19ef73d12ea932054b5ad51df4f49d
};

let right = NamespaceNode {
min: parity_share_namespace(),
max: parity_share_namespace(),
digest: 0xc75cb66ae28d8ebc6eded002c28a8ba0d06d3a78c6b5cbf9b2ade051f0775ac4
};

let node = node_digest(left, right);
let res = namespace_node_eq(node, expected);
assert!(res, "Not equal to expected digest");
}
2 changes: 1 addition & 1 deletion src/tree/tests/test_consts.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ fn constants_test() {
assert!(MAX_HEIGHT == 256, "max height");
assert!(NODE_PREFIX == 0x01, "node prefix");
}

#[test]
fn parity_share_namespace_test() {
let parity_id = bytes31_const::<0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF>();
let result = parity_share_namespace();
assert!(result.version == 0xFF, "parity namespace version");
assert!(result.id == parity_id, "parity namespace id");
}

0 comments on commit 01c1ad6

Please sign in to comment.