openvm_continuations/circuit/root/def_paths/
air.rs1use std::borrow::Borrow;
2
3use openvm_circuit::{
4 arch::instructions::DEFERRAL_AS, system::memory::dimensions::MemoryDimensions,
5};
6use openvm_circuit_primitives::{
7 utils::{and, assert_array_eq, not},
8 ColumnsAir, StructReflection, StructReflectionHelper, SubAir,
9};
10use openvm_recursion_circuit::bus::Poseidon2CompressBus;
11use openvm_recursion_circuit_derive::AlignedBorrow;
12use openvm_stark_backend::{
13 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
14};
15use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
16use p3_air::{Air, AirBuilder, BaseAir};
17use p3_field::{Field, PrimeCharacteristicRing};
18use p3_matrix::Matrix;
19
20use crate::{
21 circuit::{
22 root::bus::{
23 DeferralAccPathBus, DeferralAccPathMessage, DeferralMerkleRootsBus,
24 DeferralMerkleRootsMessage,
25 },
26 subair::{MerklePathRowView, MerklePathSubAir, MerklePathSubAirContext},
27 },
28 utils::zero_hash,
29 CommitBytes,
30};
31
32#[repr(C)]
33#[derive(AlignedBorrow, StructReflection)]
34pub struct DeferralAccMerklePathsCols<F> {
35 pub is_valid: F,
36 pub is_right_child: F,
37
38 pub initial_node_commit: [F; DIGEST_SIZE],
39 pub initial_sibling: [F; DIGEST_SIZE],
40 pub final_node_commit: [F; DIGEST_SIZE],
41 pub final_sibling: [F; DIGEST_SIZE],
42
43 pub row_idx_exp_2: F,
44 pub merkle_path_branch_bits: F,
45
46 pub depth: F,
47 pub is_skip: F,
48 pub is_within_deferral_as: F,
49 pub is_unset: F,
50}
51
52#[derive(ColumnsAir)]
53#[columns_via(DeferralAccMerklePathsCols<u8>)]
54pub struct DeferralAccMerklePathsAir {
55 pub merkle_path_subair: MerklePathSubAir,
56 pub def_acc_paths_bus: DeferralAccPathBus,
57 pub def_merkle_roots_bus: DeferralMerkleRootsBus,
58 pub address_height: usize,
59 pub zero_hash: CommitBytes,
60}
61
62impl DeferralAccMerklePathsAir {
63 pub fn new(
64 poseidon2_compress_bus: Poseidon2CompressBus,
65 def_acc_paths_bus: DeferralAccPathBus,
66 memory_merkle_roots_bus: DeferralMerkleRootsBus,
67 memory_dimensions: MemoryDimensions,
68 ) -> Self {
69 assert!(memory_dimensions.addr_space_height > 1);
70 let pv_start_idx = memory_dimensions.label_to_index((DEFERRAL_AS, 0));
71 let merkle_path_branch_bits =
72 u32::try_from(pv_start_idx).expect("merkle_path_branch_bits must fit in u32");
73 let expected_proof_len = memory_dimensions.overall_height();
74 let zero_hash = zero_hash(1).into();
75 Self {
76 merkle_path_subair: MerklePathSubAir::new(
77 poseidon2_compress_bus,
78 expected_proof_len,
79 merkle_path_branch_bits,
80 ),
81 def_acc_paths_bus,
82 def_merkle_roots_bus: memory_merkle_roots_bus,
83 address_height: memory_dimensions.address_height,
84 zero_hash,
85 }
86 }
87}
88
89impl<F> BaseAir<F> for DeferralAccMerklePathsAir {
90 fn width(&self) -> usize {
91 DeferralAccMerklePathsCols::<u8>::width()
92 }
93}
94impl<F> BaseAirWithPublicValues<F> for DeferralAccMerklePathsAir {}
95impl<F> PartitionedBaseAir<F> for DeferralAccMerklePathsAir {}
96
97impl<AB: AirBuilder + InteractionBuilder> Air<AB> for DeferralAccMerklePathsAir {
98 fn eval(&self, builder: &mut AB) {
99 let main = builder.main();
100 let (local, next) = (
101 main.row_slice(0).expect("window should have two elements"),
102 main.row_slice(1).expect("window should have two elements"),
103 );
104 let local: &DeferralAccMerklePathsCols<AB::Var> = (*local).borrow();
105 let next: &DeferralAccMerklePathsCols<AB::Var> = (*next).borrow();
106
107 builder.when_first_row().assert_zero(local.depth);
115 builder
116 .when_transition()
117 .when(local.is_valid * next.is_valid)
118 .assert_one(next.depth - local.depth);
119
120 let is_set = not(next.is_unset);
121 let is_next_start_depth = (local.is_skip - next.is_skip) * (AB::Expr::TWO - next.is_valid)
122 + local.is_unset
123 * local.is_valid
124 * (local.is_valid - AB::Expr::ONE)
125 * AB::F::TWO.inverse();
126
127 self.def_acc_paths_bus.receive(
128 builder,
129 DeferralAccPathMessage {
130 initial_acc_hash: next.initial_node_commit.map(|v| v * is_set.clone()),
131 final_acc_hash: next.final_node_commit.map(|v| v * is_set.clone()),
132 depth: next.depth * is_set,
133 is_unset: next.is_unset.into(),
134 },
135 is_next_start_depth.clone(),
136 );
137
138 assert_array_eq::<_, _, AB::Expr, _>(
139 &mut builder
140 .when(local.is_unset)
141 .when(local.is_valid)
142 .when_ne(local.is_valid, AB::F::ONE),
143 local.initial_node_commit,
144 self.zero_hash.into(),
145 );
146
147 builder.assert_bool(local.is_unset);
152 builder
153 .when_first_row()
154 .when(local.is_unset)
155 .assert_one(local.is_right_child);
156 builder
157 .when(and(local.is_valid, next.is_valid))
158 .assert_eq(local.is_unset, next.is_unset);
159
160 builder.assert_bool(local.is_within_deferral_as);
168 builder
169 .when_first_row()
170 .assert_one(local.is_within_deferral_as);
171 builder
172 .when_transition()
173 .assert_bool(local.is_within_deferral_as - next.is_within_deferral_as);
174 builder
175 .when_transition()
176 .when(local.is_within_deferral_as - next.is_within_deferral_as)
177 .assert_eq(next.depth, AB::Expr::from_usize(self.address_height));
178 builder
179 .when_last_row()
180 .assert_zero(local.is_within_deferral_as);
181 builder
182 .when(not(local.is_within_deferral_as))
183 .assert_zero(is_next_start_depth);
184
185 assert_array_eq(
186 &mut builder.when(local.is_within_deferral_as),
187 local.initial_sibling,
188 local.final_sibling,
189 );
190
191 assert_array_eq(
192 &mut builder.when(and(local.is_within_deferral_as, local.is_unset)),
193 local.initial_node_commit,
194 local.final_node_commit,
195 );
196
197 self.merkle_path_subair.eval(
201 builder,
202 (
203 MerklePathSubAirContext {
204 local: MerklePathRowView {
205 is_valid: &local.is_valid,
206 is_right_child: &local.is_right_child,
207 node_commit: &local.initial_node_commit,
208 sibling: &local.initial_sibling,
209 row_idx_exp_2: &local.row_idx_exp_2,
210 merkle_path_branch_bits: &local.merkle_path_branch_bits,
211 },
212 next: MerklePathRowView {
213 is_valid: &next.is_valid,
214 is_right_child: &next.is_right_child,
215 node_commit: &next.initial_node_commit,
216 sibling: &next.initial_sibling,
217 row_idx_exp_2: &next.row_idx_exp_2,
218 merkle_path_branch_bits: &next.merkle_path_branch_bits,
219 },
220 },
221 local.is_skip.into(),
222 next.is_skip.into(),
223 local.is_unset.into(),
224 ),
225 );
226
227 self.merkle_path_subair.eval(
228 builder,
229 (
230 MerklePathSubAirContext {
231 local: MerklePathRowView {
232 is_valid: &local.is_valid,
233 is_right_child: &local.is_right_child,
234 node_commit: &local.final_node_commit,
235 sibling: &local.final_sibling,
236 row_idx_exp_2: &local.row_idx_exp_2,
237 merkle_path_branch_bits: &local.merkle_path_branch_bits,
238 },
239 next: MerklePathRowView {
240 is_valid: &next.is_valid,
241 is_right_child: &next.is_right_child,
242 node_commit: &next.final_node_commit,
243 sibling: &next.final_sibling,
244 row_idx_exp_2: &next.row_idx_exp_2,
245 merkle_path_branch_bits: &next.merkle_path_branch_bits,
246 },
247 },
248 local.is_skip.into(),
249 next.is_skip.into(),
250 local.is_unset.into(),
251 ),
252 );
253
254 self.def_merkle_roots_bus.receive(
258 builder,
259 DeferralMerkleRootsMessage {
260 initial_root: local.initial_node_commit,
261 final_root: local.final_node_commit,
262 },
263 local.is_valid * (AB::Expr::ONE - next.is_valid).square(),
264 );
265 }
266}