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_continuations::{
8 circuit::{
9 deferral::DeferralCircuitPvs,
10 root::{
11 bus::{
12 DeferralAccPathBus, DeferralAccPathMessage, DeferralMerkleRootsBus,
13 DeferralMerkleRootsMessage, MemoryMerkleCommitBus, MemoryMerkleCommitMessage,
14 },
15 NUM_DIGESTS_IN_VM_COMMIT,
16 },
17 subair::{HashSliceCtx, HashSliceSubAir},
18 utils::{assert_vk_commit_eq, assert_vk_commit_unset, vk_commit_components},
19 },
20 utils::{digests_to_poseidon2_input, pad_slice_to_poseidon2_input},
21 CommitBytes, VkCommitBytes,
22};
23use openvm_recursion_circuit::{
24 bus::{
25 CachedCommitBus, CachedCommitBusMessage, FinalTranscriptStateBus,
26 FinalTranscriptStateMessage, Poseidon2CompressBus, Poseidon2CompressMessage, PreHashBus,
27 PreHashMessage, PublicValuesBus, PublicValuesBusMessage,
28 },
29 primitives::bus::{RangeCheckerBus, RangeCheckerBusMessage},
30 utils::assert_zeros,
31};
32use openvm_recursion_circuit_derive::AlignedBorrow;
33use openvm_stark_backend::{
34 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
35};
36use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
37use openvm_verify_stark_host::pvs::{
38 DeferralPvs, VerifierBasePvs, VerifierDefPvs, VkCommit, VmPvs, CONSTRAINT_EVAL_AIR_ID,
39 CONSTRAINT_EVAL_CACHED_INDEX, DEF_PVS_AIR_ID, LOG_MAX_RECURSION_DEPTH, VERIFIER_PVS_AIR_ID,
40 VM_PVS_AIR_ID,
41};
42use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir};
43use p3_field::{Field, PrimeCharacteristicRing};
44use p3_matrix::Matrix;
45
46use crate::{
47 bus::{OutputCommitBus, OutputCommitMessage, OutputValBus, OutputValMessage},
48 output::VALS_IN_DIGEST,
49};
50
51#[repr(C)]
52#[derive(AlignedBorrow, StructReflection)]
53pub struct DeferredVerifyPvsCols<F> {
54 pub child_verifier_pvs: VerifierBasePvs<F>,
55 pub child_vm_pvs: VmPvs<F>,
56
57 pub recursion_depth_minus_one_inv: F,
58
59 pub program_commit_hash: [F; DIGEST_SIZE],
60 pub initial_root_hash: [F; DIGEST_SIZE],
61 pub initial_pc_hash: [F; DIGEST_SIZE],
62
63 pub intermediate_exe_commit: [F; DIGEST_SIZE],
64 pub intermediate_vk_states: [[F; POSEIDON2_WIDTH]; NUM_DIGESTS_IN_VM_COMMIT - 1],
65
66 pub app_exe_commit: [F; DIGEST_SIZE],
67 pub app_vm_commit: [F; DIGEST_SIZE],
68 pub final_transcript_state: [F; POSEIDON2_WIDTH],
69}
70
71#[derive(ColumnsAir)]
72#[columns_via(DeferredVerifyPvsCols<u8>)]
73pub struct DeferredVerifyPvsAir {
74 pub public_values_bus: PublicValuesBus,
75 pub cached_commit_bus: CachedCommitBus,
76 pub pre_hash_bus: PreHashBus,
77 pub range_bus: RangeCheckerBus,
78 pub poseidon2_compress_bus: Poseidon2CompressBus,
79 pub hash_slice_subair: HashSliceSubAir,
80 pub memory_merkle_commit_bus: MemoryMerkleCommitBus,
81 pub output_val_bus: OutputValBus,
82 pub output_commit_bus: OutputCommitBus,
83 pub final_state_bus: FinalTranscriptStateBus,
84
85 pub def_acc_paths_bus: DeferralAccPathBus,
86 pub def_merkle_roots_bus: DeferralMerkleRootsBus,
87
88 pub expected_internal_recursive_vk_commit: VkCommitBytes,
89 pub expected_def_hook_commit: Option<CommitBytes>,
90
91 pub def_idx: usize,
92}
93
94impl<F> BaseAir<F> for DeferredVerifyPvsAir {
95 fn width(&self) -> usize {
96 DeferredVerifyPvsCols::<u8>::width()
97 + if self.expected_def_hook_commit.is_some() {
98 RecursiveDeferredVerifyCols::<u8>::width()
99 } else {
100 0
101 }
102 }
103}
104impl<F> BaseAirWithPublicValues<F> for DeferredVerifyPvsAir {
105 fn num_public_values(&self) -> usize {
106 DeferralCircuitPvs::<u8>::width()
107 }
108}
109impl<F> PartitionedBaseAir<F> for DeferredVerifyPvsAir {}
110
111impl<AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues> Air<AB>
112 for DeferredVerifyPvsAir
113{
114 fn eval(&self, builder: &mut AB) {
115 let main = builder.main();
116 let local = main.row_slice(0).expect("window should have one elements");
117
118 let base_cols_width = DeferredVerifyPvsCols::<AB::Var>::width();
119 let (base_local, rec_local) = local.split_at(base_cols_width);
120 let local: &DeferredVerifyPvsCols<AB::Var> = (*base_local).borrow();
121
122 if let Some(def_hook_commit) = self.expected_def_hook_commit {
123 let rec: &RecursiveDeferredVerifyCols<AB::Var> = (*rec_local).borrow();
124 self.eval_deferrals(builder, local, rec, def_hook_commit);
125 }
126
127 let success = AB::F::from_u32(ExitCode::Success as u32);
133 builder.assert_eq(local.child_vm_pvs.exit_code, success);
134 builder.assert_one(local.child_vm_pvs.is_terminate);
135
136 builder.assert_eq(local.child_verifier_pvs.internal_flag, AB::F::TWO);
141
142 let depth_minus_one = local.child_verifier_pvs.recursion_depth - AB::F::ONE;
143 let is_depth_not_one = depth_minus_one.clone() * local.recursion_depth_minus_one_inv;
144 let is_depth_one = AB::Expr::ONE - is_depth_not_one.clone();
145
146 builder.assert_bool(is_depth_one.clone());
147 builder
148 .when(is_depth_one.clone())
149 .assert_one(local.child_verifier_pvs.recursion_depth);
150
151 self.range_bus.lookup_key(
152 builder,
153 RangeCheckerBusMessage {
154 value: depth_minus_one,
155 max_bits: AB::Expr::from_u8(LOG_MAX_RECURSION_DEPTH),
156 },
157 AB::F::ONE,
158 );
159
160 self.memory_merkle_commit_bus.send(
168 builder,
169 MemoryMerkleCommitMessage {
170 merkle_root: local.child_vm_pvs.final_root,
171 },
172 AB::F::ONE,
173 );
174
175 let vm_pvs_id = AB::Expr::from_usize(VM_PVS_AIR_ID);
180 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
181
182 for (pv_idx, value) in local.child_vm_pvs.as_slice().iter().enumerate() {
183 self.public_values_bus.receive(
184 builder,
185 AB::F::ZERO,
186 PublicValuesBusMessage {
187 air_idx: vm_pvs_id.clone(),
188 pv_idx: AB::Expr::from_usize(pv_idx),
189 value: (*value).into(),
190 },
191 AB::F::ONE,
192 );
193 }
194
195 for (pv_idx, value) in local.child_verifier_pvs.as_slice().iter().enumerate() {
196 self.public_values_bus.receive(
197 builder,
198 AB::F::ZERO,
199 PublicValuesBusMessage {
200 air_idx: verifier_pvs_id.clone(),
201 pv_idx: AB::Expr::from_usize(pv_idx),
202 value: (*value).into(),
203 },
204 AB::F::ONE,
205 );
206 }
207
208 let cached_commit = from_fn(|i| {
217 local
218 .child_verifier_pvs
219 .internal_for_leaf_vk_commit
220 .cached_commit[i]
221 * is_depth_one.clone()
222 + local
223 .child_verifier_pvs
224 .internal_recursive_vk_commit
225 .cached_commit[i]
226 * is_depth_not_one.clone()
227 });
228 self.cached_commit_bus.receive(
229 builder,
230 AB::F::ZERO,
231 CachedCommitBusMessage {
232 air_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_AIR_ID),
233 cached_idx: AB::Expr::from_usize(CONSTRAINT_EVAL_CACHED_INDEX),
234 global_cached_idx: AB::Expr::ZERO,
235 cached_commit,
236 },
237 AB::F::ONE,
238 );
239
240 self.pre_hash_bus.receive(
241 builder,
242 AB::F::ZERO,
243 PreHashMessage::<AB::F> {
244 vk_pre_hash: self
245 .expected_internal_recursive_vk_commit
246 .vk_pre_hash
247 .into(),
248 },
249 AB::F::ONE,
250 );
251
252 assert_vk_commit_unset(
253 &mut builder.when(is_depth_one),
254 local.child_verifier_pvs.internal_recursive_vk_commit,
255 );
256
257 assert_vk_commit_eq(
258 &mut builder.when(is_depth_not_one),
259 local.child_verifier_pvs.internal_recursive_vk_commit,
260 VkCommit::<AB::Expr>::from(self.expected_internal_recursive_vk_commit),
261 );
262
263 let vk_commit_components: Vec<_> = vk_commit_components(&local.child_verifier_pvs)
269 .into_iter()
270 .map(|c| c.map(Into::into))
271 .collect();
272 self.hash_slice_subair.eval(
273 builder,
274 HashSliceCtx {
275 elements: vk_commit_components.as_slice(),
276 intermediate: local
277 .intermediate_vk_states
278 .map(|v| v.map(Into::into))
279 .as_slice(),
280 result: &local.app_vm_commit.map(Into::into),
281 enabled: &AB::Expr::ONE,
282 },
283 );
284
285 self.poseidon2_compress_bus.lookup_key(
291 builder,
292 Poseidon2CompressMessage {
293 input: pad_slice_to_poseidon2_input(
294 &local.child_vm_pvs.program_commit.map(Into::into),
295 AB::Expr::ZERO,
296 ),
297 output: local.program_commit_hash.map(Into::into),
298 },
299 AB::F::ONE,
300 );
301
302 self.poseidon2_compress_bus.lookup_key(
303 builder,
304 Poseidon2CompressMessage {
305 input: pad_slice_to_poseidon2_input(
306 &local.child_vm_pvs.initial_root.map(Into::into),
307 AB::Expr::ZERO,
308 ),
309 output: local.initial_root_hash.map(Into::into),
310 },
311 AB::F::ONE,
312 );
313
314 self.poseidon2_compress_bus.lookup_key(
315 builder,
316 Poseidon2CompressMessage {
317 input: pad_slice_to_poseidon2_input(
318 &[local.child_vm_pvs.initial_pc.into()],
319 AB::Expr::ZERO,
320 ),
321 output: local.initial_pc_hash.map(Into::into),
322 },
323 AB::F::ONE,
324 );
325
326 self.poseidon2_compress_bus.lookup_key(
327 builder,
328 Poseidon2CompressMessage {
329 input: digests_to_poseidon2_input(
330 local.program_commit_hash,
331 local.initial_root_hash,
332 ),
333 output: local.intermediate_exe_commit,
334 },
335 AB::F::ONE,
336 );
337
338 self.poseidon2_compress_bus.lookup_key(
339 builder,
340 Poseidon2CompressMessage {
341 input: digests_to_poseidon2_input(
342 local.intermediate_exe_commit,
343 local.initial_pc_hash,
344 ),
345 output: local.app_exe_commit,
346 },
347 AB::F::ONE,
348 );
349
350 let &DeferralCircuitPvs::<_> {
359 input_commit,
360 output_commit,
361 def_idx,
362 } = builder.public_values().borrow();
363
364 builder.assert_eq(def_idx, AB::Expr::from_usize(self.def_idx));
365
366 self.final_state_bus.receive(
367 builder,
368 AB::Expr::ZERO,
369 FinalTranscriptStateMessage {
370 state: local.final_transcript_state,
371 },
372 AB::F::ONE,
373 );
374
375 self.poseidon2_compress_bus.lookup_key(
376 builder,
377 Poseidon2CompressMessage {
378 input: local.final_transcript_state.map(Into::into),
379 output: input_commit.map(Into::into),
380 },
381 AB::F::ONE,
382 );
383
384 let mut output_idx = 0;
385 for exe_val in local.app_exe_commit.chunks_exact(VALS_IN_DIGEST) {
386 self.output_val_bus.send(
387 builder,
388 OutputValMessage {
389 values: from_fn(|i| exe_val[i].into()),
390 idx: AB::Expr::from_u8(output_idx),
391 },
392 AB::F::ONE,
393 );
394 output_idx += 1;
395 }
396
397 for vk_val in local.app_vm_commit.chunks_exact(VALS_IN_DIGEST) {
398 self.output_val_bus.send(
399 builder,
400 OutputValMessage {
401 values: from_fn(|i| vk_val[i].into()),
402 idx: AB::Expr::from_u8(output_idx),
403 },
404 AB::F::ONE,
405 );
406 output_idx += 1;
407 }
408
409 self.output_commit_bus.receive(
410 builder,
411 OutputCommitMessage {
412 commit: output_commit,
413 },
414 AB::F::ONE,
415 );
416 }
417}
418
419#[repr(C)]
420#[derive(AlignedBorrow)]
421pub struct RecursiveDeferredVerifyCols<F> {
422 pub child_def_verifier_pvs: VerifierDefPvs<F>,
423 pub child_def_pvs: DeferralPvs<F>,
424}
425
426#[repr(C)]
427#[derive(AlignedBorrow)]
428pub struct DeferredVerifyCombinedPvs<F> {
429 pub base: DeferredVerifyPvsCols<F>,
430 pub rec: RecursiveDeferredVerifyCols<F>,
431}
432
433impl DeferredVerifyPvsAir {
434 fn eval_deferrals<AB>(
435 &self,
436 builder: &mut AB,
437 base: &DeferredVerifyPvsCols<AB::Var>,
438 rec: &RecursiveDeferredVerifyCols<AB::Var>,
439 expected_def_hook_commit: CommitBytes,
440 ) where
441 AB: AirBuilder + InteractionBuilder + AirBuilderWithPublicValues,
442 {
443 let verifier_pvs = rec.child_def_verifier_pvs;
444 let def_pvs = rec.child_def_pvs;
445
446 let def_pvs_idx = AB::Expr::from_usize(DEF_PVS_AIR_ID);
450 let verifier_pvs_id = AB::Expr::from_usize(VERIFIER_PVS_AIR_ID);
451 let base_verifier_pvs_width = VerifierBasePvs::<u8>::width();
452
453 for (pv_idx, value) in verifier_pvs.as_slice().iter().enumerate() {
454 self.public_values_bus.receive(
455 builder,
456 AB::F::ZERO,
457 PublicValuesBusMessage {
458 air_idx: verifier_pvs_id.clone(),
459 pv_idx: AB::Expr::from_usize(pv_idx + base_verifier_pvs_width),
460 value: (*value).into(),
461 },
462 AB::F::ONE,
463 );
464 }
465
466 for (pv_idx, value) in def_pvs.as_slice().iter().enumerate() {
467 self.public_values_bus.receive(
468 builder,
469 AB::F::ZERO,
470 PublicValuesBusMessage {
471 air_idx: def_pvs_idx.clone(),
472 pv_idx: AB::Expr::from_usize(pv_idx),
473 value: (*value).into(),
474 },
475 AB::F::ONE,
476 );
477 }
478
479 builder
484 .assert_zero(verifier_pvs.deferral_flag * (verifier_pvs.deferral_flag - AB::Expr::TWO));
485 assert_array_eq::<_, _, AB::Expr, _>(
486 &mut builder.when(verifier_pvs.deferral_flag),
487 verifier_pvs.def_hook_commit,
488 expected_def_hook_commit.into(),
489 );
490
491 let mut when_no_deferral = builder.when_ne(verifier_pvs.deferral_flag, AB::Expr::TWO);
498 when_no_deferral.assert_zero(def_pvs.depth);
499 assert_zeros(&mut when_no_deferral, def_pvs.initial_acc_hash);
500 assert_zeros(&mut when_no_deferral, def_pvs.final_acc_hash);
501 assert_zeros(&mut when_no_deferral, verifier_pvs.def_hook_commit);
502
503 builder.assert_zero(def_pvs.node_idx);
504
505 self.def_acc_paths_bus.send(
506 builder,
507 DeferralAccPathMessage {
508 initial_acc_hash: def_pvs.initial_acc_hash.map(Into::into),
509 final_acc_hash: def_pvs.final_acc_hash.map(Into::into),
510 depth: def_pvs.depth.into(),
511 is_unset: (AB::Expr::TWO - verifier_pvs.deferral_flag) * AB::F::TWO.inverse(),
512 },
513 AB::F::ONE,
514 );
515
516 self.def_merkle_roots_bus.send(
517 builder,
518 DeferralMerkleRootsMessage {
519 initial_root: base.child_vm_pvs.initial_root,
520 final_root: base.child_vm_pvs.final_root,
521 },
522 AB::F::ONE,
523 );
524 }
525}