1use std::{array::from_fn, borrow::Borrow};
2
3use openvm_circuit::arch::POSEIDON2_WIDTH;
4use openvm_circuit_primitives::{
5 utils::{and, assert_array_eq, not},
6 ColumnsAir, StructReflection, StructReflectionHelper, SubAir,
7};
8use openvm_recursion_circuit::{
9 bus::{
10 CachedCommitBus, CachedCommitBusMessage, PreHashBus, PreHashMessage, PublicValuesBus,
11 PublicValuesBusMessage,
12 },
13 primitives::bus::{RangeCheckerBus, RangeCheckerBusMessage},
14 utils::assert_zeros,
15};
16use openvm_recursion_circuit_derive::AlignedBorrow;
17use openvm_stark_backend::{
18 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
19};
20use openvm_verify_stark_host::pvs::{
21 VerifierBasePvs, VerifierDefPvs, CONSTRAINT_EVAL_AIR_ID, CONSTRAINT_EVAL_CACHED_INDEX,
22 LOG_MAX_RECURSION_DEPTH, VERIFIER_PVS_AIR_ID,
23};
24use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir};
25use p3_field::{Field, PrimeCharacteristicRing};
26use p3_matrix::Matrix;
27
28use crate::circuit::{
29 inner::bus::{PvsAirConsistencyBus, PvsAirConsistencyMessage},
30 root::NUM_DIGESTS_IN_VM_COMMIT,
31 subair::{HashSliceCtx, HashSliceSubAir},
32 utils::{assert_vk_commit_eq, assert_vk_commit_unset, vk_commit_components},
33};
34
35#[repr(C)]
36#[derive(AlignedBorrow, StructReflection)]
37pub struct VerifierPvsCols<F> {
38 pub proof_idx: F,
39 pub is_valid: F,
40 pub has_verifier_pvs: F,
41
42 pub recursion_flag: F,
43 pub depth_inv: F,
44
45 pub child_pvs: VerifierBasePvs<F>,
46}
47
48#[derive(ColumnsAir)]
49#[columns_via(VerifierPvsCols<u8>)]
50pub struct VerifierPvsAir {
51 pub public_values_bus: PublicValuesBus,
52 pub cached_commit_bus: CachedCommitBus,
53 pub pre_hash_bus: PreHashBus,
54 pub range_bus: RangeCheckerBus,
55 pub pvs_air_consistency_bus: PvsAirConsistencyBus,
56 pub deferral_config: VerifierDeferralConfig,
57}
58
59impl<F> BaseAir<F> for VerifierPvsAir {
60 fn width(&self) -> usize {
61 VerifierPvsCols::<u8>::width() + self.deferral_config.width()
62 }
63}
64impl<F> BaseAirWithPublicValues<F> for VerifierPvsAir {
65 fn num_public_values(&self) -> usize {
66 VerifierBasePvs::<u8>::width() + self.deferral_config.num_public_values()
67 }
68}
69impl<F> PartitionedBaseAir<F> for VerifierPvsAir {}
70
71impl<AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues> Air<AB> for VerifierPvsAir {
72 fn eval(&self, builder: &mut AB) {
73 let main = builder.main();
74 let (local, next) = (
75 main.row_slice(0).expect("window should have two elements"),
76 main.row_slice(1).expect("window should have two elements"),
77 );
78
79 let base_cols_width = VerifierPvsCols::<AB::Var>::width();
80 let (base_local, def_local) = local.split_at(base_cols_width);
81 let (base_next, def_next) = next.split_at(base_cols_width);
82
83 let local: &VerifierPvsCols<AB::Var> = (*base_local).borrow();
84 let next: &VerifierPvsCols<AB::Var> = (*base_next).borrow();
85
86 let (vk_commit_cond, deferral_flag, consistency_mult) = match &self.deferral_config {
93 VerifierDeferralConfig::Enabled {
94 hash_slice_subair: subair,
95 } => {
96 let def_local: &VerifierDeferralCols<AB::Var> = (*def_local).borrow();
97 let def_next: &VerifierDeferralCols<AB::Var> = (*def_next).borrow();
98 self.eval_deferrals(builder, local, next, def_local, def_next, subair)
99 }
100 VerifierDeferralConfig::Disabled => {
101 debug_assert_eq!(def_local.len(), 0);
102 (
103 and(local.is_valid, next.is_valid),
104 AB::Expr::ZERO,
105 AB::Expr::ONE,
106 )
107 }
108 };
109
110 builder.assert_bool(local.is_valid);
114 builder.when_first_row().assert_one(local.is_valid);
115 builder
116 .when_transition()
117 .assert_bool(local.is_valid - next.is_valid);
118
119 builder.when_first_row().assert_zero(local.proof_idx);
120 builder
121 .when_transition()
122 .when(and(local.is_valid, next.is_valid))
123 .assert_eq(local.proof_idx + AB::F::ONE, next.proof_idx);
124
125 builder.assert_bool(local.has_verifier_pvs);
126 builder
127 .when(local.has_verifier_pvs)
128 .assert_one(local.is_valid);
129
130 let both_valid = and(local.is_valid, next.is_valid);
151 let mut when_both_valid = builder.when(both_valid.clone());
152
153 when_both_valid.assert_eq(local.has_verifier_pvs, next.has_verifier_pvs);
154 when_both_valid.assert_eq(local.recursion_flag, next.recursion_flag);
155 when_both_valid.assert_eq(local.child_pvs.internal_flag, next.child_pvs.internal_flag);
156 when_both_valid.assert_eq(
157 local.child_pvs.recursion_depth,
158 next.child_pvs.recursion_depth,
159 );
160
161 assert_vk_commit_eq(
162 &mut when_both_valid,
163 local.child_pvs.internal_recursive_vk_commit,
164 next.child_pvs.internal_recursive_vk_commit,
165 );
166
167 let mut when_vk_compare = builder.when(vk_commit_cond);
169
170 assert_vk_commit_eq(
171 &mut when_vk_compare,
172 local.child_pvs.app_vk_commit,
173 next.child_pvs.app_vk_commit,
174 );
175 assert_vk_commit_eq(
176 &mut when_vk_compare,
177 local.child_pvs.leaf_vk_commit,
178 next.child_pvs.leaf_vk_commit,
179 );
180 assert_vk_commit_eq(
181 &mut when_vk_compare,
182 local.child_pvs.internal_for_leaf_vk_commit,
183 next.child_pvs.internal_for_leaf_vk_commit,
184 );
185
186 builder.assert_tern(local.child_pvs.internal_flag);
188 builder.assert_tern(local.recursion_flag);
189
190 let half = AB::F::TWO.inverse();
193 let is_recursion_flag_two =
194 (local.recursion_flag - AB::F::ONE) * local.recursion_flag * half;
195
196 builder
197 .when_ne(local.recursion_flag, AB::F::TWO)
198 .assert_eq(local.child_pvs.recursion_depth, local.recursion_flag);
199 builder
200 .when_ne(local.child_pvs.recursion_depth, AB::F::ZERO)
201 .when_ne(local.child_pvs.recursion_depth, AB::F::ONE)
202 .assert_eq(local.recursion_flag, AB::F::TWO);
203 builder.assert_eq(
204 is_recursion_flag_two.clone(),
205 local.child_pvs.recursion_depth
206 * (local.child_pvs.recursion_depth - AB::F::ONE)
207 * local.depth_inv,
208 );
209
210 builder
212 .when_ne(local.child_pvs.internal_flag, AB::F::TWO)
213 .assert_zero(local.child_pvs.recursion_depth);
214
215 self.range_bus.lookup_key(
217 builder,
218 RangeCheckerBusMessage {
219 value: local.child_pvs.recursion_depth.into(),
220 max_bits: AB::Expr::from_u8(LOG_MAX_RECURSION_DEPTH),
221 },
222 local.is_valid,
223 );
224
225 builder
227 .when(local.recursion_flag)
228 .assert_eq(local.child_pvs.internal_flag, AB::F::TWO);
229 builder
230 .when_ne(local.recursion_flag, AB::F::ONE)
231 .when_ne(local.recursion_flag, AB::F::TWO)
232 .assert_bool(local.child_pvs.internal_flag);
233
234 let is_leaf = not(local.has_verifier_pvs);
236 let is_internal = local.has_verifier_pvs;
237
238 builder
239 .when(is_leaf.clone())
240 .assert_zero(local.child_pvs.internal_flag);
241
242 assert_vk_commit_unset(
243 &mut builder.when(is_leaf.clone()),
244 local.child_pvs.app_vk_commit,
245 );
246 assert_vk_commit_unset(
247 &mut builder.when(
248 (local.child_pvs.internal_flag - AB::F::ONE)
249 * (local.child_pvs.internal_flag - AB::F::TWO),
250 ),
251 local.child_pvs.leaf_vk_commit,
252 );
253 assert_vk_commit_unset(
254 &mut builder.when(local.child_pvs.internal_flag - AB::F::TWO),
255 local.child_pvs.internal_for_leaf_vk_commit,
256 );
257 assert_vk_commit_unset(
258 &mut builder.when(local.recursion_flag - AB::F::TWO),
259 local.child_pvs.internal_recursive_vk_commit,
260 );
261
262 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
267
268 for (pv_idx, value) in local.child_pvs.as_slice().iter().enumerate() {
269 self.public_values_bus.receive(
270 builder,
271 local.proof_idx,
272 PublicValuesBusMessage {
273 air_idx: verifier_pvs_id.clone(),
274 pv_idx: AB::Expr::from_usize(pv_idx),
275 value: (*value).into(),
276 },
277 local.is_valid * is_internal,
278 );
279 }
280
281 let is_internal_flag_zero = (local.child_pvs.internal_flag - AB::F::ONE)
287 * (local.child_pvs.internal_flag - AB::F::TWO)
288 * half;
289 let is_internal_flag_one =
290 (AB::Expr::TWO - local.child_pvs.internal_flag) * local.child_pvs.internal_flag;
291 let is_recursion_flag_one = (AB::Expr::TWO - local.recursion_flag) * local.recursion_flag;
292 let cached_commit = from_fn(|i| {
293 is_internal_flag_zero.clone() * local.child_pvs.app_vk_commit.cached_commit[i]
294 + is_internal_flag_one.clone() * local.child_pvs.leaf_vk_commit.cached_commit[i]
295 + is_recursion_flag_one.clone()
296 * local.child_pvs.internal_for_leaf_vk_commit.cached_commit[i]
297 + is_recursion_flag_two.clone()
298 * local.child_pvs.internal_recursive_vk_commit.cached_commit[i]
299 });
300
301 self.cached_commit_bus.receive(
302 builder,
303 local.proof_idx,
304 CachedCommitBusMessage {
305 air_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_AIR_ID),
306 cached_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_CACHED_INDEX),
307 global_cached_idx: AB::Expr::ZERO,
308 cached_commit,
309 },
310 local.is_valid * is_internal,
311 );
312
313 self.pvs_air_consistency_bus.add_key_with_lookups(
318 builder,
319 local.proof_idx,
320 PvsAirConsistencyMessage {
321 deferral_flag,
322 has_verifier_pvs: local.has_verifier_pvs.into(),
323 },
324 local.is_valid * consistency_mult,
325 );
326
327 let base_pvs_width = VerifierBasePvs::<AB::Var>::width();
334 let &VerifierBasePvs::<_> {
335 internal_flag,
336 app_vk_commit,
337 leaf_vk_commit,
338 internal_for_leaf_vk_commit,
339 recursion_depth,
340 internal_recursive_vk_commit,
341 } = builder.public_values()[0..base_pvs_width].borrow();
342
343 builder
345 .when(and(local.is_valid, is_leaf.clone()))
346 .assert_zero(internal_flag);
347
348 builder
350 .when(
351 local.is_valid
352 * (local.child_pvs.internal_flag - AB::F::ONE)
353 * (local.child_pvs.internal_flag - AB::F::TWO),
354 )
355 .assert_zero(recursion_depth);
356
357 builder
359 .when(is_internal)
360 .when_ne(local.child_pvs.internal_flag, AB::F::TWO)
361 .assert_eq(internal_flag, local.child_pvs.internal_flag + AB::F::ONE);
362
363 assert_vk_commit_eq(
365 &mut builder.when_first_row().when(is_internal),
366 local.child_pvs.app_vk_commit,
367 app_vk_commit,
368 );
369
370 builder
372 .when(local.child_pvs.internal_flag)
373 .assert_zero(internal_flag.into() - AB::F::TWO);
374 assert_vk_commit_eq(
375 &mut builder.when_first_row().when(local.child_pvs.internal_flag),
376 local.child_pvs.leaf_vk_commit,
377 leaf_vk_commit,
378 );
379
380 builder
382 .when(local.child_pvs.internal_flag)
383 .assert_one(recursion_depth.into() - local.child_pvs.recursion_depth);
384
385 assert_vk_commit_eq(
388 &mut builder.when_first_row().when(local.recursion_flag),
389 local.child_pvs.internal_for_leaf_vk_commit,
390 internal_for_leaf_vk_commit,
391 );
392
393 assert_vk_commit_eq(
396 &mut builder.when(local.recursion_flag * (local.recursion_flag - AB::F::ONE)),
397 local.child_pvs.internal_recursive_vk_commit,
398 internal_recursive_vk_commit,
399 );
400
401 let internal_flag = internal_flag.into();
409
410 let is_pvs_internal_flag_zero = (internal_flag.clone() - AB::Expr::ONE)
411 * (internal_flag.clone() - AB::Expr::TWO)
412 * half;
413 let is_pvs_internal_flag_one =
414 (AB::Expr::TWO - internal_flag.clone()) * internal_flag.clone();
415 let is_internal_flag_two =
416 (local.child_pvs.internal_flag - AB::Expr::ONE) * local.child_pvs.internal_flag * half;
417
418 let vk_pre_hash = from_fn(|i| {
419 is_pvs_internal_flag_zero.clone() * app_vk_commit.vk_pre_hash[i].into()
420 + is_pvs_internal_flag_one.clone() * leaf_vk_commit.vk_pre_hash[i].into()
421 + is_internal_flag_one.clone() * internal_for_leaf_vk_commit.vk_pre_hash[i].into()
422 + is_internal_flag_two.clone() * internal_recursive_vk_commit.vk_pre_hash[i].into()
423 });
424
425 self.pre_hash_bus.receive(
426 builder,
427 local.proof_idx,
428 PreHashMessage { vk_pre_hash },
429 local.is_valid,
430 );
431 }
432}
433
434pub enum VerifierDeferralConfig {
439 Enabled { hash_slice_subair: HashSliceSubAir },
440 Disabled,
441}
442
443impl VerifierDeferralConfig {
444 pub fn width(&self) -> usize {
445 match self {
446 VerifierDeferralConfig::Enabled { .. } => VerifierDeferralCols::<u8>::width(),
447 VerifierDeferralConfig::Disabled => 0,
448 }
449 }
450
451 pub fn num_public_values(&self) -> usize {
452 match self {
453 VerifierDeferralConfig::Enabled { .. } => VerifierDefPvs::<u8>::width(),
454 VerifierDeferralConfig::Disabled => 0,
455 }
456 }
457}
458
459#[repr(C)]
460#[derive(AlignedBorrow)]
461pub struct VerifierDeferralCols<F> {
462 pub is_last: F,
463 pub intermediate_states: [[F; POSEIDON2_WIDTH]; NUM_DIGESTS_IN_VM_COMMIT - 1],
464 pub child_pvs: VerifierDefPvs<F>,
465}
466
467#[repr(C)]
468#[derive(AlignedBorrow)]
469pub struct VerifierCombinedPvs<F> {
470 pub base: VerifierBasePvs<F>,
471 pub def: VerifierDefPvs<F>,
472}
473
474impl VerifierPvsAir {
475 fn eval_deferrals<AB>(
476 &self,
477 builder: &mut AB,
478 base_local: &VerifierPvsCols<AB::Var>,
479 base_next: &VerifierPvsCols<AB::Var>,
480 def_local: &VerifierDeferralCols<AB::Var>,
481 def_next: &VerifierDeferralCols<AB::Var>,
482 hash_slice_subair: &HashSliceSubAir,
483 ) -> (AB::Expr, AB::Expr, AB::Expr)
484 where
485 AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues,
486 {
487 let delta = def_next.child_pvs.deferral_flag - def_local.child_pvs.deferral_flag;
496 builder.assert_tern(def_local.child_pvs.deferral_flag);
497
498 builder.assert_bool(def_local.is_last);
500 builder
501 .when(def_local.is_last)
502 .assert_one(base_local.is_valid);
503 builder
504 .when(and(base_local.is_valid, not(def_local.is_last)))
505 .assert_one(base_next.is_valid);
506 builder
507 .when(def_local.is_last)
508 .assert_zero(base_next.is_valid * base_next.proof_idx);
509 builder
510 .when_last_row()
511 .when(base_local.is_valid)
512 .assert_one(def_local.is_last);
513
514 builder.when_transition().assert_bool(delta.clone());
516
517 builder
520 .when_transition()
521 .when(def_local.child_pvs.deferral_flag)
522 .assert_zero(delta.clone());
523
524 builder
526 .when_transition()
527 .when(base_local.proof_idx)
528 .assert_zero(delta.clone());
529
530 builder
532 .when(def_local.child_pvs.deferral_flag)
533 .when_ne(def_local.child_pvs.deferral_flag, AB::F::ONE)
534 .assert_one(def_local.is_last);
535
536 builder
538 .when_transition()
539 .when(delta.clone())
540 .assert_one(def_next.is_last);
541
542 builder
549 .when(delta.clone())
550 .assert_eq(base_local.child_pvs.internal_flag, AB::F::TWO);
551 builder
552 .when(def_local.child_pvs.deferral_flag)
553 .when_ne(def_local.child_pvs.deferral_flag, AB::F::ONE)
554 .assert_eq(base_local.child_pvs.internal_flag, AB::F::TWO);
555
556 assert_zeros(
558 &mut builder.when(base_local.child_pvs.internal_flag - AB::F::TWO),
559 def_local.child_pvs.def_hook_commit,
560 );
561
562 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
567 let pvs_offset = VerifierBasePvs::<AB::Var>::width();
568
569 for (pv_idx, value) in def_local.child_pvs.as_slice().iter().enumerate() {
570 self.public_values_bus.receive(
571 builder,
572 base_local.proof_idx,
573 PublicValuesBusMessage {
574 air_idx: verifier_pvs_id.clone(),
575 pv_idx: AB::Expr::from_usize(pv_idx + pvs_offset),
576 value: (*value).into(),
577 },
578 base_local.is_valid * base_local.has_verifier_pvs,
579 );
580 }
581
582 let &VerifierCombinedPvs::<_> {
587 base: base_pvs,
588 def: def_pvs,
589 } = builder.public_values().borrow();
590
591 let base_pvs_ref: &VerifierBasePvs<_> = base_pvs.as_slice().borrow();
592 let &VerifierBasePvs::<_> { internal_flag, .. } = base_pvs_ref;
593
594 let &VerifierDefPvs::<_> {
595 deferral_flag,
596 def_hook_commit,
597 } = def_pvs.as_slice().borrow();
598
599 builder
601 .when(delta.clone())
602 .assert_eq(deferral_flag, AB::F::TWO);
603 builder
604 .when_ne(delta.clone(), AB::F::ONE)
605 .when_ne(delta.clone(), -AB::F::ONE)
606 .assert_eq(deferral_flag, def_local.child_pvs.deferral_flag);
607
608 assert_array_eq(
610 &mut builder
611 .when(base_local.recursion_flag)
612 .when(def_local.child_pvs.deferral_flag),
613 def_local.child_pvs.def_hook_commit,
614 def_hook_commit,
615 );
616
617 let is_child_def_hook_vk_defined = base_local.child_pvs.internal_flag
620 * (base_local.child_pvs.internal_flag - AB::Expr::ONE)
621 * def_local.child_pvs.deferral_flag;
622
623 assert_array_eq(
624 &mut builder.when(is_child_def_hook_vk_defined),
625 def_local.child_pvs.def_hook_commit,
626 def_hook_commit,
627 );
628
629 let compute_def_hook_commit = internal_flag.into()
632 * (internal_flag.into() - AB::Expr::ONE)
633 * deferral_flag.into()
634 * (AB::Expr::TWO - deferral_flag.into())
635 * AB::F::TWO.inverse();
636
637 hash_slice_subair.eval(
638 builder,
639 HashSliceCtx {
640 elements: &vk_commit_components(base_pvs_ref)
641 .into_iter()
642 .map(|c| c.map(Into::into))
643 .collect::<Vec<_>>(),
644 intermediate: def_local
645 .intermediate_states
646 .map(|v| v.map(Into::into))
647 .as_slice(),
648 result: &def_hook_commit.map(Into::into),
649 enabled: &compute_def_hook_commit,
650 },
651 );
652
653 let vk_commit_cond =
660 and(base_local.is_valid, not(def_local.is_last)) * (AB::Expr::ONE - delta);
661 let deferral_flag = def_local.child_pvs.deferral_flag.into();
662 let consistency_mult = base_local.has_verifier_pvs + AB::Expr::ONE;
663
664 (vk_commit_cond, deferral_flag, consistency_mult)
665 }
666}