openvm_continuations/circuit/subair/merkle_tree/
trace.rs1use openvm_stark_sdk::config::baby_bear_poseidon2::{
2 poseidon2_compress_with_capacity, DIGEST_SIZE, F,
3};
4use p3_field::PrimeCharacteristicRing;
5
6use super::MerkleTreeCols;
7use crate::circuit::deferral::{utils::def_tagged_compress, DEF_INTERNAL_TAG, DEF_LEAF_TAG};
8
9pub fn generate_cols_from_leaf_children(
16 leaf_children: Vec<([F; DIGEST_SIZE], [F; DIGEST_SIZE])>,
17 tagged: bool,
18) -> Vec<MerkleTreeCols<F>> {
19 let num_leaves = leaf_children.len();
20 debug_assert!(num_leaves > 0);
21 debug_assert!(num_leaves.is_power_of_two());
22
23 let mut rows = Vec::with_capacity(2 * num_leaves);
24 let mut next_layer = Vec::with_capacity(num_leaves);
25 let leaf_tag = tagged.then_some(DEF_LEAF_TAG);
26 let internal_tag = tagged.then_some(DEF_INTERNAL_TAG);
27
28 for (row_idx, (left, right)) in leaf_children.into_iter().enumerate() {
29 let parent = compress_with_optional_tag(left, right, leaf_tag);
30 rows.push(MerkleTreeCols {
31 row_idx: F::from_usize(row_idx),
32 send_type: if num_leaves == 1 { F::TWO } else { F::ONE },
33 receive_type: F::ONE,
34 parent,
35 is_right_child: F::from_bool(row_idx & 1 == 1),
36 left_child: left,
37 right_child: right,
38 });
39 next_layer.push(parent);
40 }
41
42 let mut row_idx = num_leaves;
43 while next_layer.len() > 1 {
44 let parent_layer_len = next_layer.len() >> 1;
45 for parent_idx in 0..parent_layer_len {
46 let left = next_layer[2 * parent_idx];
47 let right = next_layer[2 * parent_idx + 1];
48 let parent = compress_with_optional_tag(left, right, internal_tag);
49
50 rows.push(MerkleTreeCols {
51 row_idx: F::from_usize(row_idx),
52 send_type: if parent_layer_len > 1 { F::ONE } else { F::TWO },
53 receive_type: F::TWO,
54 parent,
55 is_right_child: F::from_bool(row_idx & 1 == 1),
56 left_child: left,
57 right_child: right,
58 });
59
60 row_idx += 1;
61 next_layer[parent_idx] = parent;
62 }
63 next_layer.truncate(parent_layer_len);
64 }
65
66 rows.push(MerkleTreeCols {
67 row_idx: F::from_usize(row_idx),
68 send_type: F::ZERO,
69 receive_type: F::ZERO,
70 parent: [F::ZERO; DIGEST_SIZE],
71 is_right_child: F::from_bool(row_idx & 1 == 1),
72 left_child: [F::ZERO; DIGEST_SIZE],
73 right_child: [F::ZERO; DIGEST_SIZE],
74 });
75
76 rows
77}
78
79fn compress_with_optional_tag(
80 left: [F; DIGEST_SIZE],
81 right: [F; DIGEST_SIZE],
82 tag: Option<[u8; DIGEST_SIZE]>,
83) -> [F; DIGEST_SIZE] {
84 match tag {
85 Some(tag) => def_tagged_compress(tag, left, right).1,
86 None => poseidon2_compress_with_capacity(left, right).0,
87 }
88}