poseidon_merkle/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7#![doc = include_str!("../README.md")]
8#![no_std]
9#![deny(clippy::pedantic)]
10
11#[cfg(feature = "zk")]
12pub mod zk;
13
14use dusk_bls12_381::BlsScalar;
15use dusk_bytes::Serializable;
16use dusk_merkle::Aggregate;
17use dusk_poseidon::{Domain, Hash};
18
19pub const ARITY: usize = 4;
20
21/// An alias for a tree containing `Item<T>`.
22pub type Tree<T, const H: usize> = dusk_merkle::Tree<Item<T>, H, ARITY>;
23
24/// An alias for an opening of a tree containing `Item<T>`.
25pub type Opening<T, const H: usize> = dusk_merkle::Opening<Item<T>, H, ARITY>;
26
27/// A type that wraps a piece of data `T` together with a poseidon hash - i.e. a
28/// [`BlsScalar`].
29///
30/// It implements [`Aggregate`] for any `T` that also implements the trait,
31/// allowing for the construction of a poseidon tree without the need to define
32/// where the aggregation of hashes is predefined.
33///
34/// # Example
35/// ```rust
36/// use std::cmp::{max, min};
37///
38/// use dusk_bls12_381::BlsScalar;
39/// use dusk_merkle::Aggregate;
40/// use dusk_poseidon::{Domain, Hash};
41/// use poseidon_merkle::{ARITY, Item, Tree as PoseidonTree};
42///
43/// const H: usize = 17;
44///
45/// // Leaf struct that carries some data and the current block-height.
46/// struct Leaf {
47/// leaf_data: BlsScalar,
48/// bh: usize,
49/// }
50///
51/// // A node of the merkle tree that keeps track of the min and max
52/// // block-height of all of it's children nodes.
53/// struct BHRange {
54/// min: Option<usize>,
55/// max: Option<usize>,
56/// }
57///
58/// // Implement `Aggragate` only for the `BHRange`
59/// impl Aggregate<ARITY> for BHRange {
60/// const EMPTY_SUBTREE: BHRange = BHRange {
61/// min: None,
62/// max: None,
63/// };
64///
65/// fn aggregate(items: [&Self; ARITY]) -> Self {
66/// let mut parent = Self::EMPTY_SUBTREE;
67///
68/// for child in items {
69/// parent.min = match (parent.min, child.min) {
70/// (Some(parent_min), Some(child_min)) => {
71/// Some(min(parent_min, child_min))
72/// }
73/// (Some(parent_min), None) => Some(parent_min),
74/// (None, Some(child_min)) => Some(child_min),
75/// (None, None) => None,
76/// };
77/// parent.max = match (parent.max, child.max) {
78/// (Some(parent_max), Some(child_max)) => {
79/// Some(max(parent_max, child_max))
80/// }
81/// (Some(parent_max), None) => Some(parent_max),
82/// (None, Some(child_max)) => Some(child_max),
83/// (None, None) => None,
84/// }
85/// }
86///
87/// parent
88/// }
89/// }
90///
91/// // Create a merkle tree using the poseidon-hash for each level
92/// type Tree = PoseidonTree<BHRange, H>;
93/// let mut tree = Tree::new();
94///
95/// let leaf = Leaf {
96/// leaf_data: BlsScalar::from(42),
97/// bh: 42,
98/// };
99///
100/// let item = Item {
101/// hash: Hash::digest(Domain::Other, &[leaf.leaf_data])[0],
102/// data: BHRange {
103/// min: Some(leaf.bh),
104/// max: Some(leaf.bh),
105/// },
106/// };
107/// tree.insert(42, item);
108/// ```
109#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
110#[cfg_attr(
111 feature = "rkyv-impl",
112 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
113 archive_attr(derive(bytecheck::CheckBytes))
114)]
115pub struct Item<T> {
116 pub hash: BlsScalar,
117 pub data: T,
118}
119
120impl<T> Item<T> {
121 /// Create a new Item for the merkle tree
122 pub fn new(hash: BlsScalar, data: T) -> Self {
123 Self { hash, data }
124 }
125}
126
127impl<T> Aggregate<ARITY> for Item<T>
128where
129 T: Aggregate<ARITY>,
130{
131 const EMPTY_SUBTREE: Self = Item {
132 hash: BlsScalar::zero(),
133 data: T::EMPTY_SUBTREE,
134 };
135
136 fn aggregate(items: [&Self; ARITY]) -> Self {
137 let empty = &T::EMPTY_SUBTREE;
138
139 let mut level_hashes = [BlsScalar::zero(); ARITY];
140 let mut level_data = [empty; ARITY];
141
142 // grab hashes and data
143 items.into_iter().enumerate().for_each(|(i, item)| {
144 level_hashes[i] = item.hash;
145 level_data[i] = &item.data;
146 });
147
148 // create new aggregated item with the hash being the poseidon hash of
149 // the previous level
150 Item {
151 hash: Hash::digest(Domain::Merkle4, &level_hashes)[0],
152 data: T::aggregate(level_data),
153 }
154 }
155}
156
157impl Serializable<32> for Item<()> {
158 type Error = <BlsScalar as Serializable<32>>::Error;
159
160 fn from_bytes(buf: &[u8; 32]) -> Result<Self, Self::Error>
161 where
162 Self: Sized,
163 {
164 Ok(Item {
165 hash: <BlsScalar as Serializable<32>>::from_bytes(buf)?,
166 data: (),
167 })
168 }
169
170 fn to_bytes(&self) -> [u8; 32] {
171 self.hash.to_bytes()
172 }
173}