1use std::{array::from_fn, borrow::Borrow};
2
3use itertools::{izip, Itertools};
4use openvm_circuit::{
5 arch::{ExecutionBridge, ExecutionState, DEFAULT_BLOCK_SIZE},
6 system::memory::{
7 offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols},
8 MemoryAddress,
9 },
10};
11use openvm_circuit_primitives::{
12 bitwise_op_lookup::BitwiseOperationLookupBus,
13 utils::{assert_array_eq, not},
14 ColumnsAir, StructReflection, StructReflectionHelper,
15};
16use openvm_circuit_primitives_derive::AlignedBorrow;
17use openvm_deferral_transpiler::DeferralOpcode;
18use openvm_instructions::{
19 program::DEFAULT_PC_STEP,
20 riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS},
21 LocalOpcode,
22};
23use openvm_stark_backend::{
24 interaction::InteractionBuilder,
25 p3_air::{Air, AirBuilder, BaseAir},
26 p3_field::PrimeCharacteristicRing,
27 p3_matrix::Matrix,
28 BaseAirWithPublicValues, PartitionedBaseAir,
29};
30use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
31use p3_field::PrimeField32;
32
33use crate::{
34 canonicity::{CanonicityAuxCols, CanonicitySubAir},
35 count::DeferralCircuitCountBus,
36 poseidon2::DeferralPoseidon2Bus,
37 utils::{
38 byte_commit_to_f, bytes_to_f, combine_output, split_memory_ops, COMMIT_NUM_BYTES,
39 DIGEST_MEMORY_OPS, F_NUM_BYTES, OUTPUT_TOTAL_BYTES, OUTPUT_TOTAL_MEMORY_OPS,
40 },
41};
42
43#[repr(C)]
44#[derive(AlignedBorrow, StructReflection)]
45pub struct DeferralOutputCols<T> {
46 pub is_valid: T,
49 pub is_first: T,
50 pub is_last: T,
51 pub section_idx: T,
52
53 pub from_state: ExecutionState<T>,
55 pub rd_ptr: T,
56 pub rs_ptr: T,
57 pub deferral_idx: T,
58
59 pub rd_val: [T; RV32_REGISTER_NUM_LIMBS],
61 pub rs_val: [T; RV32_REGISTER_NUM_LIMBS],
62 pub rd_aux: MemoryReadAuxCols<T>,
63 pub rs_aux: MemoryReadAuxCols<T>,
64
65 pub output_commit: [T; COMMIT_NUM_BYTES],
70 pub output_len: [T; F_NUM_BYTES],
71 pub output_commit_and_len_aux: [MemoryReadAuxCols<T>; OUTPUT_TOTAL_MEMORY_OPS],
72
73 pub output_commit_lt_aux: [CanonicityAuxCols<T>; DIGEST_SIZE],
76
77 pub sponge_inputs: [T; DIGEST_SIZE],
81 pub write_bytes_aux: [MemoryWriteAuxCols<T, DEFAULT_BLOCK_SIZE>; DIGEST_MEMORY_OPS],
82
83 pub poseidon2_res: [T; DIGEST_SIZE],
86}
87
88#[derive(Clone, Copy, Debug, derive_new::new, ColumnsAir)]
89#[columns_via(DeferralOutputCols<u8>)]
90pub struct DeferralOutputAir {
91 pub execution_bridge: ExecutionBridge,
92 pub memory_bridge: MemoryBridge,
93 pub count_bus: DeferralCircuitCountBus,
94 pub poseidon2_bus: DeferralPoseidon2Bus,
95 pub bitwise_bus: BitwiseOperationLookupBus,
96 pub address_bits: usize,
97}
98
99impl<F> BaseAir<F> for DeferralOutputAir {
100 fn width(&self) -> usize {
101 DeferralOutputCols::<F>::width()
102 }
103}
104impl<F> BaseAirWithPublicValues<F> for DeferralOutputAir {}
105impl<F> PartitionedBaseAir<F> for DeferralOutputAir {}
106
107impl<AB> Air<AB> for DeferralOutputAir
108where
109 AB: InteractionBuilder,
110 AB::F: PrimeField32,
111{
112 fn eval(&self, builder: &mut AB) {
113 let main = builder.main();
114 let local = main.row_slice(0).expect("window should have two elements");
115 let next = main.row_slice(1).expect("window should have two elements");
116 let local: &DeferralOutputCols<AB::Var> = (*local).borrow();
117 let next: &DeferralOutputCols<AB::Var> = (*next).borrow();
118
119 let is_transition = next.is_valid - next.is_first;
120 let is_last = local.is_valid - is_transition.clone();
121
122 builder.assert_bool(local.is_valid);
127 builder.assert_bool(local.is_first);
128
129 builder
130 .when_transition()
131 .assert_bool(local.is_valid - next.is_valid);
132 builder
133 .when_first_row()
134 .when(local.is_valid)
135 .assert_one(local.is_first);
136
137 builder.assert_eq(local.is_last, is_last);
138
139 builder
140 .when(not(local.is_valid))
141 .assert_zero(local.is_first);
142 builder
143 .when(not(local.is_valid))
144 .assert_zero(local.section_idx);
145
146 builder.when(local.is_first).assert_zero(local.section_idx);
147 builder
148 .when(is_transition.clone())
149 .assert_one(next.section_idx - local.section_idx);
150
151 let mut when_section_transition = builder.when(next.section_idx);
156
157 when_section_transition.assert_eq(local.from_state.pc, next.from_state.pc);
158 when_section_transition.assert_eq(local.from_state.timestamp, next.from_state.timestamp);
159 when_section_transition.assert_eq(local.rd_ptr, next.rd_ptr);
160 when_section_transition.assert_eq(local.rs_ptr, next.rs_ptr);
161 when_section_transition.assert_eq(local.deferral_idx, next.deferral_idx);
162
163 assert_array_eq(&mut when_section_transition, local.rd_val, next.rd_val);
164 assert_array_eq(&mut when_section_transition, local.rs_val, next.rs_val);
165
166 assert_array_eq(
167 &mut when_section_transition,
168 local.output_commit,
169 next.output_commit,
170 );
171 assert_array_eq(
172 &mut when_section_transition,
173 local.output_len,
174 next.output_len,
175 );
176
177 let output_commit_rcs = izip!(
180 local.output_commit.chunks_exact(F_NUM_BYTES),
181 local.output_commit_lt_aux
182 )
183 .map(|(bytes, aux)| {
184 CanonicitySubAir.assert_canonicity(builder, bytes, &aux, local.is_first.into())
185 })
186 .collect_vec();
187
188 for rc_pair in output_commit_rcs.chunks_exact(2) {
189 self.bitwise_bus
190 .send_range(rc_pair[0].clone(), rc_pair[1].clone())
191 .eval(builder, local.is_first);
192 }
193
194 let output_len = bytes_to_f(&local.output_len);
198 let mut initial_state = [AB::Expr::ZERO; DIGEST_SIZE];
199 initial_state[0] = local.deferral_idx.into();
200 initial_state[1] = output_len.clone();
201
202 assert_array_eq(
203 &mut builder.when(local.is_first),
204 initial_state,
205 local.sponge_inputs,
206 );
207
208 self.count_bus
209 .send(local.deferral_idx)
210 .eval(builder, local.is_first);
211
212 let mut when_last = builder.when(local.is_last);
215
216 when_last.assert_eq(
217 output_len,
218 local.section_idx * AB::Expr::from_usize(DIGEST_SIZE),
219 );
220 assert_array_eq(
221 &mut when_last,
222 byte_commit_to_f(&local.output_commit),
223 local.poseidon2_res,
224 );
225
226 let rhs = from_fn(|i| is_transition.clone() * local.poseidon2_res[i]);
229 self.poseidon2_bus
230 .lookup(next.sponge_inputs, rhs, next.poseidon2_res, next.is_last)
231 .eval(builder, next.is_valid);
232
233 debug_assert!(RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS >= self.address_bits);
240 let limb_shift =
241 AB::F::from_usize(1 << (RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.address_bits));
242
243 self.bitwise_bus
244 .send_range(
245 local.rd_val[RV32_REGISTER_NUM_LIMBS - 1] * limb_shift,
246 local.rs_val[RV32_REGISTER_NUM_LIMBS - 1] * limb_shift,
247 )
248 .eval(builder, local.is_first);
249
250 self.bitwise_bus
252 .send_range(
253 local.output_len[RV32_REGISTER_NUM_LIMBS - 1] * limb_shift,
254 AB::Expr::ZERO,
255 )
256 .eval(builder, local.is_first);
257
258 let d = AB::Expr::from_u32(RV32_REGISTER_AS);
260 let e = AB::Expr::from_u32(RV32_MEMORY_AS);
261
262 self.memory_bridge
263 .read(
264 MemoryAddress::new(d.clone(), local.rd_ptr),
265 local.rd_val,
266 local.from_state.timestamp,
267 &local.rd_aux,
268 )
269 .eval(builder, local.is_first);
270
271 self.memory_bridge
272 .read(
273 MemoryAddress::new(d.clone(), local.rs_ptr),
274 local.rs_val,
275 local.from_state.timestamp + AB::Expr::ONE,
276 &local.rs_aux,
277 )
278 .eval(builder, local.is_first);
279
280 let input_ptr = bytes_to_f(&local.rs_val);
284 let output_ptr = bytes_to_f(&local.rd_val);
285 let output_len_full = from_fn(|i| {
286 if i < F_NUM_BYTES {
287 local.output_len[i].into()
288 } else {
289 AB::Expr::ZERO
290 }
291 });
292 let output_commit_and_len =
293 combine_output(local.output_commit.map(Into::into), output_len_full);
294 let output_commit_and_len_chunks =
295 split_memory_ops::<_, OUTPUT_TOTAL_BYTES, OUTPUT_TOTAL_MEMORY_OPS>(
296 output_commit_and_len,
297 );
298
299 for (chunk_idx, (data, aux)) in output_commit_and_len_chunks
300 .into_iter()
301 .zip(&local.output_commit_and_len_aux)
302 .enumerate()
303 {
304 self.memory_bridge
305 .read(
306 MemoryAddress::new(
307 e.clone(),
308 input_ptr.clone() + AB::Expr::from_usize(chunk_idx * DEFAULT_BLOCK_SIZE),
309 ),
310 data,
311 local.from_state.timestamp + AB::Expr::from_usize(2 + chunk_idx),
312 aux,
313 )
314 .eval(builder, local.is_first);
315 }
316
317 let write_bytes_chunks =
318 split_memory_ops::<_, DIGEST_SIZE, DIGEST_MEMORY_OPS>(local.sponge_inputs);
319 let section_idx_minus_one = local.section_idx - AB::Expr::ONE;
320
321 for (chunk_idx, (data, aux)) in write_bytes_chunks
322 .into_iter()
323 .zip(&local.write_bytes_aux)
324 .enumerate()
325 {
326 for bytes in data.chunks(2) {
327 self.bitwise_bus
328 .send_range(bytes[0], bytes[1])
329 .eval(builder, local.is_valid - local.is_first);
330 }
331
332 self.memory_bridge
333 .write(
334 MemoryAddress::new(
335 e.clone(),
336 output_ptr.clone()
337 + (section_idx_minus_one.clone() * AB::Expr::from_usize(DIGEST_SIZE))
338 + AB::Expr::from_usize(chunk_idx * DEFAULT_BLOCK_SIZE),
339 ),
340 data,
341 local.from_state.timestamp
342 + AB::Expr::from_usize(2 + OUTPUT_TOTAL_MEMORY_OPS + chunk_idx)
343 + (section_idx_minus_one.clone() * AB::Expr::from_usize(DIGEST_MEMORY_OPS)),
344 aux,
345 )
346 .eval(builder, local.is_valid - local.is_first);
347 }
348
349 self.execution_bridge
352 .execute_and_increment_or_set_pc(
353 AB::Expr::from_usize(DeferralOpcode::OUTPUT.global_opcode_usize()),
354 [
355 local.rd_ptr.into(),
356 local.rs_ptr.into(),
357 local.deferral_idx.into(),
358 d,
359 e,
360 ],
361 local.from_state,
362 (local.section_idx * AB::Expr::from_usize(DIGEST_MEMORY_OPS))
363 + AB::Expr::from_usize(OUTPUT_TOTAL_MEMORY_OPS + 2),
364 (DEFAULT_PC_STEP, None),
365 )
366 .eval(builder, local.is_last);
367 }
368}