1use binius_core::Word;
5use binius_frontend::{CircuitBuilder, Wire};
6
7use super::double_sha256::double_sha256;
8
9#[derive(Debug, Copy, Clone)]
11pub enum SiblingSide {
12 Left,
13 Right,
14}
15
16impl SiblingSide {
17 pub const fn to_word(self) -> Word {
22 match self {
23 SiblingSide::Left => Word::ZERO,
24 SiblingSide::Right => Word::ALL_ONE,
25 }
26 }
27}
28
29pub fn merkle_path(
38 builder: &CircuitBuilder,
39 mut leaf: [Wire; 4],
40 siblings: &[([Wire; 4], Wire)],
41 length: Wire,
42) -> [Wire; 4] {
43 for (i, (sibling, is_right)) in siblings.iter().enumerate() {
44 let message: Vec<Wire> = (0..4)
47 .map(|j| builder.select(*is_right, leaf[j], sibling[j]))
48 .chain((0..4).map(|j| builder.select(*is_right, sibling[j], leaf[j])))
49 .collect();
50 let digest = double_sha256(builder, &message);
51
52 let within_length = builder.icmp_ult(builder.add_constant_64(i as u64), length);
54 leaf = std::array::from_fn(|j| builder.select(within_length, digest[j], leaf[j]));
55 }
56 leaf
57}
58
59#[cfg(test)]
60mod tests {
61 use std::array;
62
63 use hex_literal::hex;
64
65 use super::*;
66
67 fn check_merkle_path(
69 max_path_len: usize,
70 leaf_value: [u8; 32],
71 siblings_value: &[([u8; 32], SiblingSide)],
72 length_value: u64,
73 root_value: [u8; 32],
74 ) -> anyhow::Result<()> {
75 let builder = CircuitBuilder::new();
76 let leaf: [Wire; 4] = array::from_fn(|_| builder.add_witness());
77 let siblings: Vec<([Wire; 4], Wire)> = std::iter::repeat_with(|| {
78 (array::from_fn(|_| builder.add_witness()), builder.add_witness())
79 })
80 .take(max_path_len)
81 .collect();
82 let root: [Wire; 4] = array::from_fn(|_| builder.add_witness());
83 let length = builder.add_witness();
84 builder.assert_eq_v("root", merkle_path(&builder, leaf, &siblings, length), root);
85 let circuit = builder.build();
86
87 let mut filler = circuit.new_witness_filler();
88 filler.pack_bytes_le(&leaf, &leaf_value);
89 for ((sibling, is_right), (value, side)) in siblings.iter().zip(siblings_value) {
90 filler.pack_bytes_le(sibling, value);
91 filler[*is_right] = side.to_word();
92 }
93 filler.pack_bytes_le(&root, &root_value);
94 filler[length] = Word(length_value);
95 circuit.populate_wire_witness(&mut filler)?;
96
97 let constraint_system = circuit.constraint_system();
98 constraint_system.verify(&filler.into_value_vec())?;
99 Ok(())
100 }
101
102 const LEAF: [u8; 32] = hex!("a2b6b171aae6007508e5c8fabec6b662bad3e4594e09405cac7b249e5f1e5155");
103 const ROOT: [u8; 32] = hex!("5802c63ef536216cf01a0dd0b32c01f5e31536aa773eb6e1d46fd42f66516eba");
104 const SIBLING_0: [u8; 32] =
105 hex!("1346be1a16a09b5fcc5bca52d39c2529396f0fa6a654f3978807ff79eaf91d66");
106 const SIBLING_1: [u8; 32] =
107 hex!("557cc3606e7197ff5a7b6cda46e409445b1ab58d8d4ebf1bc3d95764c32ad877");
108
109 #[test]
110 fn test_valid() {
111 let siblings = [
112 (SIBLING_0, SiblingSide::Right),
113 (SIBLING_1, SiblingSide::Left),
114 ];
115 check_merkle_path(2, LEAF, &siblings, 2, ROOT).unwrap();
116 }
117
118 #[test]
119 fn test_invalid_side() {
120 let siblings = [
122 (SIBLING_0, SiblingSide::Right),
123 (SIBLING_1, SiblingSide::Right),
124 ];
125 check_merkle_path(2, LEAF, &siblings, 2, ROOT).unwrap_err();
126 }
127
128 #[test]
129 fn test_invalid_path() {
130 let wrong = hex!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
131 let siblings = [(SIBLING_0, SiblingSide::Right), (wrong, SiblingSide::Left)];
132 check_merkle_path(2, LEAF, &siblings, 2, ROOT).unwrap_err();
133 }
134
135 #[test]
137 fn test_valid_long() {
138 let leaf = hex!("6f2f044a225e8b293c6e54cf2771bf4d17ba8904b1f61cf9c392965dcbda0b83");
139 let root = hex!("fc01df2139954b36cebc3fa6fbf6a7160a67d34b67e5c4aa2a7ce46f5bb42a83");
140 let siblings = [
141 (
142 hex!("783089645b0bc42d44e9d6a7ea62adf7a8a2adc6b7f0173d663369217b771b86"),
143 SiblingSide::Right,
144 ),
145 (
146 hex!("1eeeeb0cac1753a10ade3b34bd5bf0e005cdec82545abdafa38685c45e5f8ce5"),
147 SiblingSide::Right,
148 ),
149 (
150 hex!("e0d7426d603f1a817938cf366c8933d32185625fc821e3b1e964cb5f8e421501"),
151 SiblingSide::Right,
152 ),
153 (
154 hex!("7af6e333025422cf892198d216f146d70efe64119071ce0ee96fd195640230df"),
155 SiblingSide::Left,
156 ),
157 (
158 hex!("d848bf00d7563a26c9a43ad8cc2fa558f6a299629be20a078a6b197dcf15fc31"),
159 SiblingSide::Right,
160 ),
161 (
162 hex!("b643abf3df379ac748494a5eb3025299265fff543571f8b71935e533f672c9e8"),
163 SiblingSide::Right,
164 ),
165 (
166 hex!("b07d3ebc129da3ae9d1b9daee64daf74f8504ca5f9194cd006edee48b1bf4d00"),
167 SiblingSide::Right,
168 ),
169 (
170 hex!("4cd4173f585e793e48aa479269f38cd986b600c494135e9de33118a8e4ac03ed"),
171 SiblingSide::Right,
172 ),
173 (
174 hex!("4b5e59b8d22762cfc2906fa597b29c7eab7cd52d4b0cea9269e84e2aebce4101"),
175 SiblingSide::Right,
176 ),
177 (
178 hex!("2321cd016cb8f1a29f1bad981418bed2776bf61b1a729ca86a54f14790ce822b"),
179 SiblingSide::Right,
180 ),
181 (
182 hex!("fecdc8a219a271a9a969fdebf38068ffeaf25b7af353ee99e759eb0d05604218"),
183 SiblingSide::Right,
184 ),
185 (
186 hex!("1736c19cc6de7296453811916ddedba46c9bbd61a3450ad3dfb8bddb698b6ad0"),
187 SiblingSide::Right,
188 ),
189 ];
190 check_merkle_path(30, leaf, &siblings, 12, root).unwrap();
191 }
192}