openvm_keccak256_circuit/keccakf_op/
air.rs1use std::{borrow::Borrow, iter};
2
3use itertools::izip;
4use openvm_circuit::{
5 arch::{ExecutionBridge, ExecutionState},
6 system::memory::{
7 offline_checker::{MemoryBridge, MemoryWriteAuxCols},
8 MemoryAddress,
9 },
10};
11use openvm_circuit_primitives::{bitwise_op_lookup::BitwiseOperationLookupBus, ColumnsAir};
12use openvm_instructions::riscv::{
13 RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS,
14};
15use openvm_keccak256_transpiler::KeccakfOpcode;
16use openvm_rv32im_circuit::adapters::abstract_compose;
17use openvm_stark_backend::{
18 interaction::{InteractionBuilder, PermutationCheckBus},
19 p3_air::{Air, BaseAir},
20 p3_field::PrimeCharacteristicRing,
21 p3_matrix::Matrix,
22 BaseAirWithPublicValues, PartitionedBaseAir,
23};
24
25use crate::{
26 keccakf_op::columns::{KeccakfOpCols, NUM_KECCAKF_OP_COLS},
27 KECCAK_WORD_SIZE,
28};
29
30#[derive(Clone, Copy, Debug, derive_new::new, ColumnsAir)]
31#[columns_via(KeccakfOpCols<u8>)]
32pub struct KeccakfOpAir {
33 pub execution_bridge: ExecutionBridge,
34 pub memory_bridge: MemoryBridge,
35 pub bitwise_lookup_bus: BitwiseOperationLookupBus,
36 pub keccakf_state_bus: PermutationCheckBus,
41 pub ptr_max_bits: usize,
42 pub(super) offset: usize,
43}
44
45impl<F> BaseAirWithPublicValues<F> for KeccakfOpAir {}
46impl<F> PartitionedBaseAir<F> for KeccakfOpAir {}
47impl<F> BaseAir<F> for KeccakfOpAir {
48 fn width(&self) -> usize {
49 NUM_KECCAKF_OP_COLS
50 }
51}
52
53impl<AB: InteractionBuilder> Air<AB> for KeccakfOpAir {
54 fn eval(&self, builder: &mut AB) {
55 let main = builder.main();
56
57 let local = main.row_slice(0).unwrap();
58 let local: &KeccakfOpCols<_> = (*local).borrow();
59
60 let is_valid = local.is_valid;
61 builder.assert_bool(is_valid);
62
63 let start_timestamp = local.timestamp;
64 let mut timestamp_delta = 0usize;
65 let mut timestamp_pp = || {
66 timestamp_delta += 1;
67 start_timestamp + AB::F::from_usize(timestamp_delta - 1)
68 };
69 let rd_ptr = local.rd_ptr;
71 let buffer_ptr_limbs = local.buffer_ptr_limbs;
72 self.memory_bridge
73 .read(
74 MemoryAddress::new(AB::F::from_u32(RV32_REGISTER_AS), rd_ptr),
75 buffer_ptr_limbs,
76 timestamp_pp(),
77 &local.rd_aux,
78 )
79 .eval(builder, is_valid);
80 {
82 assert!(self.ptr_max_bits >= RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1));
83 let limb_shift = AB::F::from_usize(
84 1 << (RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.ptr_max_bits),
85 );
86 let need_range_check = [
87 buffer_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
88 buffer_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
89 ];
90 for pair in need_range_check.chunks_exact(2) {
91 self.bitwise_lookup_bus
92 .send_range(pair[0] * limb_shift, pair[1] * limb_shift)
93 .eval(builder, is_valid);
94 }
95 }
96 let buffer_ptr: AB::Expr = abstract_compose(local.buffer_ptr_limbs);
98
99 for pair in local.postimage.chunks_exact(2) {
105 self.bitwise_lookup_bus
106 .send_range(pair[0], pair[1])
107 .eval(builder, is_valid);
108 }
109
110 for (word_idx, (prev_word, post_word, base_aux)) in izip!(
113 local.preimage.chunks_exact(KECCAK_WORD_SIZE),
114 local.postimage.chunks_exact(KECCAK_WORD_SIZE),
115 local.buffer_word_aux
116 )
117 .enumerate()
118 {
119 let ptr = buffer_ptr.clone() + AB::F::from_usize(word_idx * KECCAK_WORD_SIZE);
130 let prev_data: &[_; KECCAK_WORD_SIZE] = prev_word.try_into().unwrap();
131 let data: &[_; KECCAK_WORD_SIZE] = post_word.try_into().unwrap();
133 let write_aux = MemoryWriteAuxCols {
134 base: base_aux,
135 prev_data: *prev_data,
136 };
137 self.memory_bridge
138 .write(
139 MemoryAddress::new(AB::F::from_u32(RV32_MEMORY_AS), ptr),
140 *data,
141 timestamp_pp(),
142 &write_aux,
143 )
144 .eval(builder, is_valid);
145 }
146
147 self.execution_bridge
149 .execute_and_increment_pc(
150 AB::Expr::from_usize(KeccakfOpcode::KECCAKF as usize + self.offset),
151 [
152 rd_ptr.into(),
153 AB::Expr::ZERO,
154 AB::Expr::ZERO,
155 AB::Expr::from_u32(RV32_REGISTER_AS),
156 AB::Expr::from_u32(RV32_MEMORY_AS),
157 ],
158 ExecutionState::new(local.pc, local.timestamp),
159 AB::F::from_usize(timestamp_delta),
160 )
161 .eval(builder, is_valid);
162
163 self.keccakf_state_bus.send(
171 builder,
172 iter::empty()
173 .chain([AB::Expr::ZERO, local.timestamp.into()])
174 .chain(
175 local
176 .preimage
177 .chunks(2)
178 .map(|pair| pair[0] + pair[1] * AB::F::from_u32(256)),
179 ),
180 is_valid,
181 );
182 self.keccakf_state_bus.send(
183 builder,
184 iter::empty()
185 .chain([AB::Expr::ONE, local.timestamp.into()])
186 .chain(
187 local
188 .postimage
189 .chunks(2)
190 .map(|pair| pair[0] + pair[1] * AB::F::from_u32(256)),
191 ),
192 is_valid,
193 );
194 }
195}