1use alloc::vec::Vec;
2
3use miden_crypto::hash::{
4 Digest,
5 blake::{Blake3_256, Blake3Digest},
6};
7
8use crate::{
9 LookupByIdx, Operation, Word,
10 mast::{DecoratorId, MastForest, MastForestError, MastNode, MastNodeId, node::MastNodeExt},
11};
12
13pub type DecoratorFingerprint = Blake3Digest<32>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
24pub struct MastNodeFingerprint {
25 mast_root: Word,
26 decorator_root: Option<DecoratorFingerprint>,
27}
28
29impl MastNodeFingerprint {
32 pub fn new(mast_root: Word) -> Self {
34 Self { mast_root, decorator_root: None }
35 }
36
37 pub fn with_decorator_root(mast_root: Word, decorator_root: DecoratorFingerprint) -> Self {
40 Self {
41 mast_root,
42 decorator_root: Some(decorator_root),
43 }
44 }
45
46 pub fn from_mast_node(
52 forest: &MastForest,
53 hash_by_node_id: &impl LookupByIdx<MastNodeId, MastNodeFingerprint>,
54 node: &MastNode,
55 ) -> Result<MastNodeFingerprint, MastForestError> {
56 match node {
57 MastNode::Block(node) => {
58 let mut bytes_to_hash = Vec::new();
59
60 for decorator_id in node.before_enter() {
62 bytes_to_hash.extend(forest[*decorator_id].fingerprint().as_bytes());
63 }
64
65 for (idx, decorator_id) in node.indexed_decorator_iter() {
67 bytes_to_hash.extend(idx.to_le_bytes());
68 bytes_to_hash.extend(forest[decorator_id].fingerprint().as_bytes());
69 }
70
71 for decorator_id in node.after_exit() {
73 bytes_to_hash.extend(forest[*decorator_id].fingerprint().as_bytes());
74 }
75
76 for (op_idx, op) in node.operations().enumerate() {
79 if let Operation::U32assert2(inner_value)
80 | Operation::Assert(inner_value)
81 | Operation::MpVerify(inner_value) = op
82 {
83 let op_idx: u32 = op_idx
84 .try_into()
85 .expect("there are more than 2^{32}-1 operations in basic block");
86
87 bytes_to_hash.push(op.op_code());
89 bytes_to_hash.extend(op_idx.to_le_bytes());
92 let inner_value = u64::from(*inner_value);
93 bytes_to_hash.extend(inner_value.to_le_bytes());
94 }
95 }
96
97 if bytes_to_hash.is_empty() {
98 Ok(MastNodeFingerprint::new(node.digest()))
99 } else {
100 let decorator_root = Blake3_256::hash(&bytes_to_hash);
101 Ok(MastNodeFingerprint::with_decorator_root(node.digest(), decorator_root))
102 }
103 },
104 other_node => {
105 let mut children = Vec::new();
106 other_node.for_each_child(|child_id| children.push(child_id));
107 fingerprint_from_parts(
108 forest,
109 hash_by_node_id,
110 other_node.before_enter(),
111 other_node.after_exit(),
112 &children,
113 other_node.digest(),
114 )
115 },
116 }
117 }
118}
119
120impl MastNodeFingerprint {
123 pub fn mast_root(&self) -> &Word {
124 &self.mast_root
125 }
126}
127
128fn fingerprint_from_parts(
129 forest: &MastForest,
130 hash_by_node_id: &impl LookupByIdx<MastNodeId, MastNodeFingerprint>,
131 before_enter_ids: &[DecoratorId],
132 after_exit_ids: &[DecoratorId],
133 children_ids: &[MastNodeId],
134 node_digest: Word,
135) -> Result<MastNodeFingerprint, MastForestError> {
136 let pre_decorator_hash_bytes =
137 before_enter_ids.iter().flat_map(|&id| forest[id].fingerprint().as_bytes());
138 let post_decorator_hash_bytes =
139 after_exit_ids.iter().flat_map(|&id| forest[id].fingerprint().as_bytes());
140
141 let children_decorator_roots = children_ids
142 .iter()
143 .filter_map(|child_id| {
144 hash_by_node_id
145 .get(*child_id)
146 .ok_or(MastForestError::ChildFingerprintMissing(*child_id))
147 .map(|child_fingerprint| child_fingerprint.decorator_root)
148 .transpose()
149 })
150 .collect::<Result<Vec<DecoratorFingerprint>, MastForestError>>()?;
151
152 if pre_decorator_hash_bytes.clone().next().is_none()
156 && post_decorator_hash_bytes.clone().next().is_none()
157 && children_decorator_roots.is_empty()
158 {
159 Ok(MastNodeFingerprint::new(node_digest))
160 } else {
161 let decorator_bytes_to_hash: Vec<u8> = pre_decorator_hash_bytes
162 .chain(post_decorator_hash_bytes)
163 .chain(
164 children_decorator_roots
165 .into_iter()
166 .flat_map(|decorator_root| decorator_root.as_bytes()),
167 )
168 .collect();
169
170 let decorator_root = Blake3_256::hash(&decorator_bytes_to_hash);
171 Ok(MastNodeFingerprint::with_decorator_root(node_digest, decorator_root))
172 }
173}
174
175#[cfg(test)]
179mod tests {
180 use alloc::collections::BTreeMap;
181
182 use super::*;
183 use crate::{
184 Decorator, Felt, Operation,
185 mast::{BasicBlockNode, MastNode},
186 };
187
188 fn basic_block_with_ops(ops: Vec<Operation>) -> MastNode {
190 BasicBlockNode::new(ops, Vec::new()).unwrap().into()
191 }
192
193 fn add_trace_decorator(forest: &mut MastForest, value: u8) -> DecoratorId {
195 forest.add_decorator(Decorator::Trace(value.into())).unwrap()
196 }
197
198 #[test]
199 fn basic_block_fingerprint_different_before_decorators() {
200 let mut forest = MastForest::new();
201 let deco1 = add_trace_decorator(&mut forest, 1);
202 let deco2 = add_trace_decorator(&mut forest, 2);
203
204 let mut block1 = basic_block_with_ops(vec![Operation::Add, Operation::Mul]);
206 let mut block2 = basic_block_with_ops(vec![Operation::Add, Operation::Mul]);
207
208 block1.append_before_enter(&[deco1]);
209 block2.append_before_enter(&[deco2]);
210
211 let empty_map = BTreeMap::new();
213 let fp1 = MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block1).unwrap();
214 let fp2 = MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block2).unwrap();
215
216 assert_ne!(
218 fp1, fp2,
219 "Basic blocks with different before_enter decorators should have different fingerprints"
220 );
221 }
222
223 #[test]
224 fn basic_block_fingerprint_different_after_decorators() {
225 let mut forest = MastForest::new();
226 let deco1 = add_trace_decorator(&mut forest, 1);
227 let deco2 = add_trace_decorator(&mut forest, 2);
228
229 let mut block1 = basic_block_with_ops(vec![Operation::Add, Operation::Mul]);
231 let mut block2 = basic_block_with_ops(vec![Operation::Add, Operation::Mul]);
232
233 block1.append_after_exit(&[deco1]);
234 block2.append_after_exit(&[deco2]);
235
236 let empty_map = BTreeMap::new();
238 let fp1 = MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block1).unwrap();
239 let fp2 = MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block2).unwrap();
240
241 assert_ne!(
243 fp1, fp2,
244 "Basic blocks with different after_exit decorators should have different fingerprints"
245 );
246 }
247
248 #[test]
249 fn basic_block_fingerprint_different_assert_opcodes_no_decorators() {
250 let forest = MastForest::new();
251 let error_code = Felt::new(42);
252
253 let block_assert = basic_block_with_ops(vec![Operation::Assert(error_code)]);
255 let block_u32assert2 = basic_block_with_ops(vec![Operation::U32assert2(error_code)]);
256 let block_mpverify = basic_block_with_ops(vec![Operation::MpVerify(error_code)]);
257
258 let empty_map = BTreeMap::new();
260 let fp_assert =
261 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_assert).unwrap();
262 let fp_u32assert2 =
263 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_u32assert2).unwrap();
264 let fp_mpverify =
265 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_mpverify).unwrap();
266
267 assert_ne!(
269 fp_assert, fp_u32assert2,
270 "Basic blocks with Assert vs U32assert2 should have different fingerprints"
271 );
272 assert_ne!(
273 fp_assert, fp_mpverify,
274 "Basic blocks with Assert vs MpVerify should have different fingerprints"
275 );
276 assert_ne!(
277 fp_u32assert2, fp_mpverify,
278 "Basic blocks with U32assert2 vs MpVerify should have different fingerprints"
279 );
280 }
281
282 #[test]
283 fn basic_block_fingerprint_different_assert_values_no_decorators() {
284 let forest = MastForest::new();
285 let error_code_1 = Felt::new(42);
286 let error_code_2 = Felt::new(123);
287
288 let block_assert_1 = basic_block_with_ops(vec![Operation::Assert(error_code_1)]);
290 let block_assert_2 = basic_block_with_ops(vec![Operation::Assert(error_code_2)]);
291
292 let block_u32assert2_1 = basic_block_with_ops(vec![Operation::U32assert2(error_code_1)]);
293 let block_u32assert2_2 = basic_block_with_ops(vec![Operation::U32assert2(error_code_2)]);
294
295 let block_mpverify_1 = basic_block_with_ops(vec![Operation::MpVerify(error_code_1)]);
296 let block_mpverify_2 = basic_block_with_ops(vec![Operation::MpVerify(error_code_2)]);
297
298 let empty_map = BTreeMap::new();
300 let fp_assert_1 =
301 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_assert_1).unwrap();
302 let fp_assert_2 =
303 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_assert_2).unwrap();
304
305 let fp_u32assert2_1 =
306 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_u32assert2_1).unwrap();
307 let fp_u32assert2_2 =
308 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_u32assert2_2).unwrap();
309
310 let fp_mpverify_1 =
311 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_mpverify_1).unwrap();
312 let fp_mpverify_2 =
313 MastNodeFingerprint::from_mast_node(&forest, &empty_map, &block_mpverify_2).unwrap();
314
315 assert_ne!(
317 fp_assert_1, fp_assert_2,
318 "Basic blocks with Assert operations with different error codes should have different fingerprints"
319 );
320 assert_ne!(
321 fp_u32assert2_1, fp_u32assert2_2,
322 "Basic blocks with U32assert2 operations with different error codes should have different fingerprints"
323 );
324 assert_ne!(
325 fp_mpverify_1, fp_mpverify_2,
326 "Basic blocks with MpVerify operations with different error codes should have different fingerprints"
327 );
328 }
329}