1use super::Position;
4use commonware_cryptography::Hasher as CHasher;
5
6pub trait Hasher<H: CHasher>: Send + Sync {
8 fn leaf_digest(&mut self, pos: Position, element: &[u8]) -> H::Digest;
10
11 fn node_digest(&mut self, pos: Position, left: &H::Digest, right: &H::Digest) -> H::Digest;
13
14 fn root<'a>(
17 &mut self,
18 size: Position,
19 peak_digests: impl Iterator<Item = &'a H::Digest>,
20 ) -> H::Digest;
21
22 fn digest(&mut self, data: &[u8]) -> H::Digest;
24
25 fn inner(&mut self) -> &mut H;
27
28 fn fork(&self) -> impl Hasher<H>;
32}
33
34pub struct Standard<H: CHasher> {
37 hasher: H,
38}
39
40impl<H: CHasher> Standard<H> {
41 pub fn new() -> Self {
43 Self { hasher: H::new() }
44 }
45
46 pub fn update_with_pos(&mut self, pos: Position) {
47 let pos = *pos;
48 self.hasher.update(&pos.to_be_bytes());
49 }
50
51 pub fn update_with_digest(&mut self, digest: &H::Digest) {
52 self.hasher.update(digest.as_ref());
53 }
54
55 pub fn update_with_element(&mut self, element: &[u8]) {
56 self.hasher.update(element);
57 }
58
59 pub fn finalize(&mut self) -> H::Digest {
60 self.hasher.finalize()
61 }
62}
63
64impl<H: CHasher> Default for Standard<H> {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl<H: CHasher> Hasher<H> for Standard<H> {
71 fn inner(&mut self) -> &mut H {
72 &mut self.hasher
73 }
74
75 fn fork(&self) -> impl Hasher<H> {
76 Standard { hasher: H::new() }
77 }
78
79 fn leaf_digest(&mut self, pos: Position, element: &[u8]) -> H::Digest {
80 self.update_with_pos(pos);
81 self.update_with_element(element);
82 self.finalize()
83 }
84
85 fn node_digest(&mut self, pos: Position, left: &H::Digest, right: &H::Digest) -> H::Digest {
86 self.update_with_pos(pos);
87 self.update_with_digest(left);
88 self.update_with_digest(right);
89 self.finalize()
90 }
91
92 fn root<'a>(
93 &mut self,
94 size: Position,
95 peak_digests: impl Iterator<Item = &'a H::Digest>,
96 ) -> H::Digest {
97 self.hasher.update(&size.to_be_bytes());
98 for digest in peak_digests {
99 self.update_with_digest(digest);
100 }
101 self.finalize()
102 }
103
104 fn digest(&mut self, data: &[u8]) -> H::Digest {
105 self.hasher.update(data);
106 self.finalize()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::mmr::{mem::Mmr, Position};
114 use alloc::vec::Vec;
115 use commonware_cryptography::{Hasher as CHasher, Sha256};
116
117 #[test]
118 fn test_leaf_digest_sha256() {
119 test_leaf_digest::<Sha256>();
120 }
121
122 #[test]
123 fn test_node_digest_sha256() {
124 test_node_digest::<Sha256>();
125 }
126
127 #[test]
128 fn test_root_sha256() {
129 test_root::<Sha256>();
130 }
131
132 fn test_digest<H: CHasher>(value: u8) -> H::Digest {
133 let mut hasher = H::new();
134 hasher.update(&[value]);
135 hasher.finalize()
136 }
137
138 fn test_leaf_digest<H: CHasher>() {
139 let mut mmr_hasher: Standard<H> = Standard::new();
140 let digest1 = test_digest::<H>(1);
142 let digest2 = test_digest::<H>(2);
143
144 let out = mmr_hasher.leaf_digest(Position::new(0), &digest1);
145 assert_ne!(out, test_digest::<H>(0), "hash should be non-zero");
146
147 let mut out2 = mmr_hasher.leaf_digest(Position::new(0), &digest1);
148 assert_eq!(out, out2, "hash should be re-computed consistently");
149
150 out2 = mmr_hasher.leaf_digest(Position::new(1), &digest1);
151 assert_ne!(out, out2, "hash should change with different pos");
152
153 out2 = mmr_hasher.leaf_digest(Position::new(0), &digest2);
154 assert_ne!(out, out2, "hash should change with different input digest");
155 }
156
157 fn test_node_digest<H: CHasher>() {
158 let mut mmr_hasher: Standard<H> = Standard::new();
159 let d1 = test_digest::<H>(1);
162 let d2 = test_digest::<H>(2);
163 let d3 = test_digest::<H>(3);
164
165 let out = mmr_hasher.node_digest(Position::new(0), &d1, &d2);
166 assert_ne!(out, test_digest::<H>(0), "hash should be non-zero");
167
168 let mut out2 = mmr_hasher.node_digest(Position::new(0), &d1, &d2);
169 assert_eq!(out, out2, "hash should be re-computed consistently");
170
171 out2 = mmr_hasher.node_digest(Position::new(1), &d1, &d2);
172 assert_ne!(out, out2, "hash should change with different pos");
173
174 out2 = mmr_hasher.node_digest(Position::new(0), &d3, &d2);
175 assert_ne!(
176 out, out2,
177 "hash should change with different first input hash"
178 );
179
180 out2 = mmr_hasher.node_digest(Position::new(0), &d1, &d3);
181 assert_ne!(
182 out, out2,
183 "hash should change with different second input hash"
184 );
185
186 out2 = mmr_hasher.node_digest(Position::new(0), &d2, &d1);
187 assert_ne!(
188 out, out2,
189 "hash should change when swapping order of inputs"
190 );
191 }
192
193 fn test_root<H: CHasher>() {
194 let mut mmr_hasher: Standard<H> = Standard::new();
195 let d1 = test_digest::<H>(1);
197 let d2 = test_digest::<H>(2);
198 let d3 = test_digest::<H>(3);
199 let d4 = test_digest::<H>(4);
200
201 let empty_vec: Vec<H::Digest> = Vec::new();
202 let empty_out = mmr_hasher.root(Position::new(0), empty_vec.iter());
203 assert_ne!(
204 empty_out,
205 test_digest::<H>(0),
206 "root of empty MMR should be non-zero"
207 );
208 assert_eq!(empty_out, Mmr::empty_mmr_root(mmr_hasher.inner()));
209
210 let digests = [d1, d2, d3, d4];
211 let out = mmr_hasher.root(Position::new(10), digests.iter());
212 assert_ne!(out, test_digest::<H>(0), "root should be non-zero");
213 assert_ne!(out, empty_out, "root should differ from empty MMR");
214
215 let mut out2 = mmr_hasher.root(Position::new(10), digests.iter());
216 assert_eq!(out, out2, "root should be computed consistently");
217
218 out2 = mmr_hasher.root(Position::new(11), digests.iter());
219 assert_ne!(out, out2, "root should change with different position");
220
221 let digests = [d1, d2, d4, d3];
222 out2 = mmr_hasher.root(Position::new(10), digests.iter());
223 assert_ne!(out, out2, "root should change with different digest order");
224
225 let digests = [d1, d2, d3];
226 out2 = mmr_hasher.root(Position::new(10), digests.iter());
227 assert_ne!(
228 out, out2,
229 "root should change with different number of hashes"
230 );
231 }
232}