1use std::{array::from_fn, borrow::Borrow};
2
3use openvm_circuit::arch::{ExitCode, POSEIDON2_WIDTH};
4use openvm_circuit_primitives::{
5 utils::assert_array_eq, ColumnsAir, StructReflection, StructReflectionHelper, SubAir,
6};
7use openvm_recursion_circuit::{
8 bus::{
9 CachedCommitBus, CachedCommitBusMessage, Poseidon2CompressBus, Poseidon2CompressMessage,
10 PreHashBus, PreHashMessage, PublicValuesBus, PublicValuesBusMessage,
11 },
12 primitives::bus::{RangeCheckerBus, RangeCheckerBusMessage},
13 utils::assert_zeros,
14};
15use openvm_recursion_circuit_derive::AlignedBorrow;
16use openvm_stark_backend::{
17 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
18};
19use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
20use openvm_verify_stark_host::pvs::{
21 DeferralPvs, VerifierBasePvs, VerifierDefPvs, VkCommit, VmPvs, CONSTRAINT_EVAL_AIR_ID,
22 CONSTRAINT_EVAL_CACHED_INDEX, DEF_PVS_AIR_ID, LOG_MAX_RECURSION_DEPTH, VERIFIER_PVS_AIR_ID,
23 VM_PVS_AIR_ID,
24};
25use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir};
26use p3_field::{Field, PrimeCharacteristicRing};
27use p3_matrix::Matrix;
28
29use crate::{
30 circuit::{
31 root::{
32 bus::{
33 DeferralAccPathBus, DeferralAccPathMessage, DeferralMerkleRootsBus,
34 DeferralMerkleRootsMessage, MemoryMerkleCommitBus, MemoryMerkleCommitMessage,
35 },
36 RootVerifierPvs, NUM_DIGESTS_IN_VM_COMMIT,
37 },
38 subair::{HashSliceCtx, HashSliceSubAir},
39 utils::{assert_vk_commit_eq, assert_vk_commit_unset, vk_commit_components},
40 },
41 utils::{digests_to_poseidon2_input, pad_slice_to_poseidon2_input},
42 CommitBytes, VkCommitBytes,
43};
44
45#[repr(C)]
46#[derive(AlignedBorrow, StructReflection)]
47pub struct RootVerifierPvsCols<F> {
48 pub child_verifier_pvs: VerifierBasePvs<F>,
49 pub child_vm_pvs: VmPvs<F>,
50
51 pub recursion_depth_minus_one_inv: F,
52
53 pub program_commit_hash: [F; DIGEST_SIZE],
54 pub initial_root_hash: [F; DIGEST_SIZE],
55 pub initial_pc_hash: [F; DIGEST_SIZE],
56 pub intermediate_exe_commit: [F; DIGEST_SIZE],
57
58 pub intermediate_vk_states: [[F; POSEIDON2_WIDTH]; NUM_DIGESTS_IN_VM_COMMIT - 1],
59}
60
61#[derive(ColumnsAir)]
62#[columns_via(RootVerifierPvsCols<u8>)]
63pub struct RootVerifierPvsAir {
64 pub public_values_bus: PublicValuesBus,
65 pub cached_commit_bus: CachedCommitBus,
66 pub pre_hash_bus: PreHashBus,
67 pub range_bus: RangeCheckerBus,
68 pub poseidon2_compress_bus: Poseidon2CompressBus,
69 pub memory_merkle_commit_bus: MemoryMerkleCommitBus,
70 pub def_acc_paths_bus: DeferralAccPathBus,
71 pub def_merkle_roots_bus: DeferralMerkleRootsBus,
72
73 pub hash_slice_subair: HashSliceSubAir,
74
75 pub expected_internal_recursive_vk_commit: VkCommitBytes,
76 pub expected_def_hook_commit: Option<CommitBytes>,
77}
78
79impl<F: Field> BaseAir<F> for RootVerifierPvsAir {
80 fn width(&self) -> usize {
81 RootVerifierPvsCols::<u8>::width()
82 + if self.expected_def_hook_commit.is_some() {
83 RootDefVerifierCols::<u8>::width()
84 } else {
85 0
86 }
87 }
88}
89impl<F: Field> BaseAirWithPublicValues<F> for RootVerifierPvsAir {
90 fn num_public_values(&self) -> usize {
91 RootVerifierPvs::<u8>::width()
92 }
93}
94impl<F: Field> PartitionedBaseAir<F> for RootVerifierPvsAir {}
95
96impl<AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues> Air<AB>
97 for RootVerifierPvsAir
98{
99 fn eval(&self, builder: &mut AB) {
100 let main = builder.main();
101 let local = main.row_slice(0).expect("window should have two elements");
102 let base_cols_width = RootVerifierPvsCols::<AB::Var>::width();
103 let (base_local, rec_local) = local.split_at(base_cols_width);
104 let local: &RootVerifierPvsCols<AB::Var> = (*base_local).borrow();
105
106 if let Some(def_hook_commit) = self.expected_def_hook_commit {
107 let def: &RootDefVerifierCols<AB::Var> = (*rec_local).borrow();
108 self.eval_deferrals(builder, local, def, def_hook_commit);
109 }
110
111 let success = AB::F::from_u32(ExitCode::Success as u32);
117 builder.assert_eq(local.child_vm_pvs.exit_code, success);
118 builder.assert_one(local.child_vm_pvs.is_terminate);
119
120 builder.assert_eq(local.child_verifier_pvs.internal_flag, AB::F::TWO);
125
126 let depth_minus_one = local.child_verifier_pvs.recursion_depth - AB::F::ONE;
127 let is_depth_not_one = depth_minus_one.clone() * local.recursion_depth_minus_one_inv;
128 let is_depth_one = AB::Expr::ONE - is_depth_not_one.clone();
129
130 builder.assert_bool(is_depth_one.clone());
131 builder
132 .when(is_depth_one.clone())
133 .assert_one(local.child_verifier_pvs.recursion_depth);
134
135 self.range_bus.lookup_key(
136 builder,
137 RangeCheckerBusMessage {
138 value: depth_minus_one,
139 max_bits: AB::Expr::from_u8(LOG_MAX_RECURSION_DEPTH),
140 },
141 AB::F::ONE,
142 );
143
144 self.memory_merkle_commit_bus.send(
151 builder,
152 MemoryMerkleCommitMessage {
153 merkle_root: local.child_vm_pvs.final_root,
154 },
155 AB::F::ONE,
156 );
157
158 let vm_pvs_id = AB::Expr::from_usize(VM_PVS_AIR_ID);
163 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
164
165 for (pv_idx, value) in local.child_vm_pvs.as_slice().iter().enumerate() {
166 self.public_values_bus.receive(
167 builder,
168 AB::F::ZERO,
169 PublicValuesBusMessage {
170 air_idx: vm_pvs_id.clone(),
171 pv_idx: AB::Expr::from_usize(pv_idx),
172 value: (*value).into(),
173 },
174 AB::F::ONE,
175 );
176 }
177
178 for (pv_idx, value) in local.child_verifier_pvs.as_slice().iter().enumerate() {
179 self.public_values_bus.receive(
180 builder,
181 AB::F::ZERO,
182 PublicValuesBusMessage {
183 air_idx: verifier_pvs_id.clone(),
184 pv_idx: AB::Expr::from_usize(pv_idx),
185 value: (*value).into(),
186 },
187 AB::F::ONE,
188 );
189 }
190
191 let cached_commit = from_fn(|i| {
200 local
201 .child_verifier_pvs
202 .internal_for_leaf_vk_commit
203 .cached_commit[i]
204 * is_depth_one.clone()
205 + local
206 .child_verifier_pvs
207 .internal_recursive_vk_commit
208 .cached_commit[i]
209 * is_depth_not_one.clone()
210 });
211 self.cached_commit_bus.receive(
212 builder,
213 AB::F::ZERO,
214 CachedCommitBusMessage {
215 air_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_AIR_ID),
216 cached_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_CACHED_INDEX),
217 global_cached_idx: AB::Expr::ZERO,
218 cached_commit,
219 },
220 AB::F::ONE,
221 );
222
223 self.pre_hash_bus.receive(
224 builder,
225 AB::F::ZERO,
226 PreHashMessage::<AB::F> {
227 vk_pre_hash: self
228 .expected_internal_recursive_vk_commit
229 .vk_pre_hash
230 .into(),
231 },
232 AB::F::ONE,
233 );
234
235 assert_vk_commit_unset(
236 &mut builder.when(is_depth_one),
237 local.child_verifier_pvs.internal_recursive_vk_commit,
238 );
239
240 assert_vk_commit_eq(
241 &mut builder.when(is_depth_not_one),
242 local.child_verifier_pvs.internal_recursive_vk_commit,
243 VkCommit::<AB::Expr>::from(self.expected_internal_recursive_vk_commit),
244 );
245
246 let &RootVerifierPvs::<_> {
253 app_exe_commit,
254 app_vm_commit,
255 } = builder.public_values().borrow();
256
257 let vk_commit_components: Vec<_> = vk_commit_components(&local.child_verifier_pvs)
258 .into_iter()
259 .map(|c| c.map(Into::into))
260 .collect();
261
262 self.hash_slice_subair.eval(
263 builder,
264 HashSliceCtx {
265 elements: vk_commit_components.as_slice(),
266 intermediate: local
267 .intermediate_vk_states
268 .map(|v| v.map(Into::into))
269 .as_slice(),
270 result: &app_vm_commit.map(Into::into),
271 enabled: &AB::Expr::ONE,
272 },
273 );
274
275 self.poseidon2_compress_bus.lookup_key(
281 builder,
282 Poseidon2CompressMessage {
283 input: pad_slice_to_poseidon2_input(
284 &local.child_vm_pvs.program_commit.map(Into::into),
285 AB::Expr::ZERO,
286 ),
287 output: local.program_commit_hash.map(Into::into),
288 },
289 AB::F::ONE,
290 );
291
292 self.poseidon2_compress_bus.lookup_key(
293 builder,
294 Poseidon2CompressMessage {
295 input: pad_slice_to_poseidon2_input(
296 &local.child_vm_pvs.initial_root.map(Into::into),
297 AB::Expr::ZERO,
298 ),
299 output: local.initial_root_hash.map(Into::into),
300 },
301 AB::F::ONE,
302 );
303
304 self.poseidon2_compress_bus.lookup_key(
305 builder,
306 Poseidon2CompressMessage {
307 input: pad_slice_to_poseidon2_input(
308 &[local.child_vm_pvs.initial_pc.into()],
309 AB::Expr::ZERO,
310 ),
311 output: local.initial_pc_hash.map(Into::into),
312 },
313 AB::F::ONE,
314 );
315
316 self.poseidon2_compress_bus.lookup_key(
317 builder,
318 Poseidon2CompressMessage {
319 input: digests_to_poseidon2_input(
320 local.program_commit_hash,
321 local.initial_root_hash,
322 ),
323 output: local.intermediate_exe_commit,
324 },
325 AB::F::ONE,
326 );
327
328 self.poseidon2_compress_bus.lookup_key(
329 builder,
330 Poseidon2CompressMessage {
331 input: digests_to_poseidon2_input(
332 local.intermediate_exe_commit,
333 local.initial_pc_hash,
334 )
335 .map(Into::into),
336 output: app_exe_commit.map(Into::into),
337 },
338 AB::F::ONE,
339 );
340 }
341}
342
343#[repr(C)]
344#[derive(AlignedBorrow)]
345pub struct RootDefVerifierCols<F> {
346 pub child_def_verifier_pvs: VerifierDefPvs<F>,
347 pub child_def_pvs: DeferralPvs<F>,
348}
349
350#[repr(C)]
351#[derive(AlignedBorrow)]
352pub struct RootVerifierCombinedPvs<F> {
353 pub base: RootVerifierPvsCols<F>,
354 pub def: RootDefVerifierCols<F>,
355}
356
357impl RootVerifierPvsAir {
358 fn eval_deferrals<AB>(
359 &self,
360 builder: &mut AB,
361 base: &RootVerifierPvsCols<AB::Var>,
362 def: &RootDefVerifierCols<AB::Var>,
363 expected_def_hook_commit: CommitBytes,
364 ) where
365 AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues,
366 {
367 let verifier_pvs = def.child_def_verifier_pvs;
368 let def_pvs = def.child_def_pvs;
369
370 let def_pvs_idx = AB::Expr::from_usize(DEF_PVS_AIR_ID);
374 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
375 let base_verifier_pvs_width = VerifierBasePvs::<u8>::width();
376
377 for (pv_idx, value) in verifier_pvs.as_slice().iter().enumerate() {
378 self.public_values_bus.receive(
379 builder,
380 AB::F::ZERO,
381 PublicValuesBusMessage {
382 air_idx: verifier_pvs_id.clone(),
383 pv_idx: AB::Expr::from_usize(pv_idx + base_verifier_pvs_width),
384 value: (*value).into(),
385 },
386 AB::F::ONE,
387 );
388 }
389
390 for (pv_idx, value) in def_pvs.as_slice().iter().enumerate() {
391 self.public_values_bus.receive(
392 builder,
393 AB::F::ZERO,
394 PublicValuesBusMessage {
395 air_idx: def_pvs_idx.clone(),
396 pv_idx: AB::Expr::from_usize(pv_idx),
397 value: (*value).into(),
398 },
399 AB::F::ONE,
400 );
401 }
402
403 builder
408 .assert_zero(verifier_pvs.deferral_flag * (verifier_pvs.deferral_flag - AB::Expr::TWO));
409 assert_array_eq::<_, _, AB::Expr, _>(
410 &mut builder.when(verifier_pvs.deferral_flag),
411 verifier_pvs.def_hook_commit,
412 expected_def_hook_commit.into(),
413 );
414
415 let mut when_no_deferral = builder.when_ne(verifier_pvs.deferral_flag, AB::Expr::TWO);
422 when_no_deferral.assert_zero(def_pvs.depth);
423 assert_zeros(&mut when_no_deferral, def_pvs.initial_acc_hash);
424 assert_zeros(&mut when_no_deferral, def_pvs.final_acc_hash);
425 assert_zeros(&mut when_no_deferral, verifier_pvs.def_hook_commit);
426
427 builder.assert_zero(def_pvs.node_idx);
428
429 self.def_acc_paths_bus.send(
430 builder,
431 DeferralAccPathMessage {
432 initial_acc_hash: def_pvs.initial_acc_hash.map(Into::into),
433 final_acc_hash: def_pvs.final_acc_hash.map(Into::into),
434 depth: def_pvs.depth.into(),
435 is_unset: (AB::Expr::TWO - verifier_pvs.deferral_flag) * AB::F::TWO.inverse(),
436 },
437 AB::F::ONE,
438 );
439
440 self.def_merkle_roots_bus.send(
441 builder,
442 DeferralMerkleRootsMessage {
443 initial_root: base.child_vm_pvs.initial_root,
444 final_root: base.child_vm_pvs.final_root,
445 },
446 AB::F::ONE,
447 );
448 }
449}