openvm_recursion_circuit/proof_shape/pvs/
air.rs1use std::borrow::Borrow;
2
3use openvm_circuit_primitives::{
4 utils::not, AlignedBorrow, ColumnsAir, StructReflection, StructReflectionHelper, SubAir,
5};
6use openvm_stark_backend::{
7 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
8};
9use p3_air::{Air, AirBuilder, BaseAir};
10use p3_field::{PrimeCharacteristicRing, PrimeField32};
11use p3_matrix::Matrix;
12
13use crate::{
14 bus::{PublicValuesBus, PublicValuesBusMessage, TranscriptBus, TranscriptBusMessage},
15 proof_shape::bus::{NumPublicValuesBus, NumPublicValuesMessage},
16 subairs::nested_for_loop::{NestedForLoopIoCols, NestedForLoopSubAir},
17};
18
19#[repr(C)]
20#[derive(AlignedBorrow, Debug, StructReflection)]
21pub struct PublicValuesCols<F> {
22 pub is_valid: F,
23
24 pub proof_idx: F,
25 pub air_idx: F,
26 pub pv_idx: F,
27
28 pub is_first_in_proof: F,
29 pub is_first_in_air: F,
30
31 pub tidx: F,
32 pub value: F,
33}
34
35#[derive(ColumnsAir)]
36#[columns_via(PublicValuesCols<u8>)]
37pub struct PublicValuesAir {
38 pub public_values_bus: PublicValuesBus,
39 pub num_pvs_bus: NumPublicValuesBus,
40 pub transcript_bus: TranscriptBus,
41 pub(crate) continuations_enabled: bool,
42}
43
44impl<F> BaseAir<F> for PublicValuesAir {
45 fn width(&self) -> usize {
46 PublicValuesCols::<F>::width()
47 }
48}
49impl<F> BaseAirWithPublicValues<F> for PublicValuesAir {}
50impl<F> PartitionedBaseAir<F> for PublicValuesAir {}
51
52impl<AB: AirBuilder + InteractionBuilder> Air<AB> for PublicValuesAir
53where
54 AB::F: PrimeField32,
55{
56 fn eval(&self, builder: &mut AB) {
57 let main = builder.main();
58
59 let (local, next) = (
60 main.row_slice(0).expect("window should have two elements"),
61 main.row_slice(1).expect("window should have two elements"),
62 );
63 let local: &PublicValuesCols<AB::Var> = (*local).borrow();
64 let next: &PublicValuesCols<AB::Var> = (*next).borrow();
65
66 NestedForLoopSubAir::<1> {}.eval(
67 builder,
68 (
69 NestedForLoopIoCols {
70 is_enabled: local.is_valid,
71 counter: [local.proof_idx],
72 is_first: [local.is_first_in_proof],
73 }
74 .map_into(),
75 NestedForLoopIoCols {
76 is_enabled: next.is_valid,
77 counter: [next.proof_idx],
78 is_first: [next.is_first_in_proof],
79 }
80 .map_into(),
81 ),
82 );
83 builder.assert_bool(local.is_first_in_air);
85 builder
86 .when(local.is_first_in_proof)
87 .assert_one(local.is_first_in_air);
88 builder
89 .when(local.is_first_in_air)
90 .assert_one(local.is_valid);
91 builder
92 .when(next.is_valid * (next.air_idx - local.air_idx))
93 .assert_one(next.is_first_in_air);
94 builder
95 .when(local.is_first_in_air)
96 .assert_zero(local.pv_idx);
97
98 let is_same_air = local.is_valid * next.is_valid * not(next.is_first_in_air);
99 self.num_pvs_bus.receive(
100 builder,
101 local.proof_idx,
102 NumPublicValuesMessage {
103 air_idx: local.air_idx.into(),
104 tidx: local.tidx - local.pv_idx,
105 num_pvs: local.pv_idx + AB::Expr::ONE,
106 },
107 local.is_valid - is_same_air.clone(),
108 );
109
110 let mut when_same_air = builder.when(is_same_air);
111 when_same_air.assert_eq(local.air_idx, next.air_idx);
112 when_same_air.assert_eq(next.pv_idx, local.pv_idx + AB::Expr::ONE);
113 when_same_air.assert_eq(next.tidx, local.tidx + AB::Expr::ONE);
114
115 self.public_values_bus.send(
116 builder,
117 local.proof_idx,
118 PublicValuesBusMessage {
119 air_idx: local.air_idx,
120 pv_idx: local.pv_idx,
121 value: local.value,
122 },
123 local.is_valid,
124 );
125 if self.continuations_enabled {
126 self.public_values_bus.send(
127 builder,
128 local.proof_idx,
129 PublicValuesBusMessage {
130 air_idx: local.air_idx,
131 pv_idx: local.pv_idx,
132 value: local.value,
133 },
134 local.is_valid,
135 );
136 }
137
138 self.transcript_bus.receive(
140 builder,
141 local.proof_idx,
142 TranscriptBusMessage {
143 tidx: local.tidx.into(),
144 value: local.value.into(),
145 is_sample: AB::Expr::ZERO,
146 },
147 local.is_valid,
148 );
149 }
150}