1use std::marker::PhantomData;
2
3use itertools::izip;
4use ndarray::s;
5use openvm_circuit::{
6 arch::ExecutionBridge,
7 system::{
8 memory::{offline_checker::MemoryBridge, MemoryAddress},
9 SystemPort,
10 },
11};
12use openvm_circuit_primitives::{
13 bitwise_op_lookup::BitwiseOperationLookupBus, utils::compose, ColumnsAir,
14};
15use openvm_instructions::riscv::{
16 RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS,
17};
18use openvm_sha2_air::Sha2BlockHasherSubairConfig;
19use openvm_stark_backend::{
20 interaction::{BusIndex, InteractionBuilder, PermutationCheckBus},
21 p3_air::{Air, AirBuilder, BaseAir},
22 p3_field::PrimeCharacteristicRing,
23 p3_matrix::Matrix,
24 BaseAirWithPublicValues, PartitionedBaseAir,
25};
26
27use super::config::Sha2MainChipConfig;
28use crate::{MessageType, Sha2ColsRef, SHA2_READ_SIZE, SHA2_WRITE_SIZE};
29
30#[derive(Clone, Debug)]
31pub struct Sha2MainAir<C: Sha2MainChipConfig> {
32 pub execution_bridge: ExecutionBridge,
33 pub memory_bridge: MemoryBridge,
34 pub bitwise_lookup_bus: BitwiseOperationLookupBus,
35 pub sha2_bus: PermutationCheckBus,
36 pub ptr_max_bits: usize,
39 pub offset: usize,
40 _phantom: PhantomData<C>,
41}
42
43impl<C: Sha2MainChipConfig> ColumnsAir for Sha2MainAir<C> {}
46
47impl<C: Sha2MainChipConfig> Sha2MainAir<C> {
48 pub fn new(
49 SystemPort {
50 execution_bus,
51 program_bus,
52 memory_bridge,
53 }: SystemPort,
54 bitwise_lookup_bus: BitwiseOperationLookupBus,
55 ptr_max_bits: usize,
56 self_bus_idx: BusIndex,
57 offset: usize,
58 ) -> Self {
59 Self {
60 execution_bridge: ExecutionBridge::new(execution_bus, program_bus),
61 memory_bridge,
62 bitwise_lookup_bus,
63 sha2_bus: PermutationCheckBus::new(self_bus_idx),
64 ptr_max_bits,
65 offset,
66 _phantom: PhantomData,
67 }
68 }
69}
70
71impl<F, C: Sha2MainChipConfig> BaseAirWithPublicValues<F> for Sha2MainAir<C> {}
72impl<F, C: Sha2MainChipConfig> PartitionedBaseAir<F> for Sha2MainAir<C> {}
73impl<F, C: Sha2MainChipConfig> BaseAir<F> for Sha2MainAir<C> {
74 fn width(&self) -> usize {
75 C::MAIN_CHIP_WIDTH
76 }
77}
78
79impl<AB: InteractionBuilder, C: Sha2MainChipConfig + Sha2BlockHasherSubairConfig> Air<AB>
80 for Sha2MainAir<C>
81{
82 fn eval(&self, builder: &mut AB) {
83 let main = builder.main();
84 let (local, next) = (main.row_slice(0).unwrap(), main.row_slice(1).unwrap());
85
86 let local: Sha2ColsRef<AB::Var> = Sha2ColsRef::from::<C>(&local[..C::MAIN_CHIP_WIDTH]);
87 let next: Sha2ColsRef<AB::Var> = Sha2ColsRef::from::<C>(&next[..C::MAIN_CHIP_WIDTH]);
88
89 let mut timestamp_delta = 0;
90 let mut timestamp_pp = || {
91 timestamp_delta += 1;
92 local.instruction.from_state.timestamp + AB::F::from_usize(timestamp_delta - 1)
93 };
94
95 self.eval_block(builder, &local, &next);
96 self.eval_instruction(builder, &local, &mut timestamp_pp);
97 self.eval_reads(builder, &local, &mut timestamp_pp);
98 self.eval_writes(builder, &local, &mut timestamp_pp);
99 }
100}
101
102impl<C: Sha2MainChipConfig + Sha2BlockHasherSubairConfig> Sha2MainAir<C> {
103 pub fn eval_block<AB: InteractionBuilder>(
104 &self,
105 builder: &mut AB,
106 local: &Sha2ColsRef<AB::Var>,
107 next: &Sha2ColsRef<AB::Var>,
108 ) {
109 builder.assert_bool(*local.instruction.is_enabled);
110 builder
111 .when_transition()
112 .when_ne(*local.instruction.is_enabled, AB::Expr::ONE)
113 .assert_zero(*next.instruction.is_enabled);
114
115 builder
116 .when_first_row()
117 .when(*local.instruction.is_enabled)
118 .assert_zero(*local.block.request_id);
119
120 builder
121 .when_transition()
122 .when(*next.instruction.is_enabled)
123 .assert_eq(
124 *next.block.request_id,
125 *local.block.request_id + AB::Expr::ONE,
126 );
127
128 let prev_state_as_u16s: Vec<AB::Expr> = local
129 .block
130 .prev_state
131 .exact_chunks(C::WORD_U8S)
132 .into_iter()
133 .flat_map(|word| {
134 word.as_slice()
135 .unwrap()
136 .chunks_exact(2)
137 .map(|x| x[1] * AB::F::from_u64(1 << 8) + x[0])
138 .collect::<Vec<_>>()
139 })
140 .collect();
141
142 self.sha2_bus.send(
144 builder,
145 [
146 AB::Expr::from_u8(MessageType::State as u8),
147 (*local.block.request_id).into(),
148 ]
149 .into_iter()
150 .chain(prev_state_as_u16s)
151 .chain(local.block.new_state.into_iter().copied().map(|x| x.into())),
152 *local.instruction.is_enabled,
153 );
154
155 self.sha2_bus.send(
157 builder,
158 [
159 AB::Expr::from_u8(MessageType::Message1 as u8),
160 (*local.block.request_id).into(),
161 ]
162 .into_iter()
163 .chain(
164 local
165 .block
166 .message_bytes
167 .iter()
168 .take(C::BLOCK_BYTES / 2)
169 .map(|x| (*x).into()),
170 ),
171 *local.instruction.is_enabled,
172 );
173
174 self.sha2_bus.send(
176 builder,
177 [
178 AB::Expr::from_u8(MessageType::Message2 as u8),
179 (*local.block.request_id).into(),
180 ]
181 .into_iter()
182 .chain(
183 local
184 .block
185 .message_bytes
186 .iter()
187 .skip(C::BLOCK_BYTES / 2)
188 .map(|x| (*x).into()),
189 ),
190 *local.instruction.is_enabled,
191 );
192 }
193
194 pub fn eval_instruction<AB: InteractionBuilder>(
195 &self,
196 builder: &mut AB,
197 local: &Sha2ColsRef<AB::Var>,
198 timestamp_pp: &mut impl FnMut() -> AB::Expr,
199 ) {
200 for (&ptr, val, aux) in izip!(
201 [
202 local.instruction.dst_reg_ptr,
203 local.instruction.state_reg_ptr,
204 local.instruction.input_reg_ptr
205 ],
206 [
207 local.instruction.dst_ptr_limbs,
208 local.instruction.state_ptr_limbs,
209 local.instruction.input_ptr_limbs
210 ],
211 &local.mem.register_aux,
212 ) {
213 self.memory_bridge
214 .read::<_, _, SHA2_READ_SIZE>(
215 MemoryAddress::new(AB::Expr::from_u32(RV32_REGISTER_AS), ptr),
216 val.to_vec().try_into().unwrap_or_else(|_| panic!()), timestamp_pp(),
218 aux,
219 )
220 .eval(builder, *local.instruction.is_enabled);
221 }
222
223 assert!(self.ptr_max_bits >= RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1));
225 let shift = AB::Expr::from_usize(
226 1 << (RV32_REGISTER_NUM_LIMBS * RV32_CELL_BITS - self.ptr_max_bits),
227 );
228 let needs_range_check = [
229 local.instruction.dst_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
230 local.instruction.state_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
231 local.instruction.input_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
232 local.instruction.input_ptr_limbs[RV32_REGISTER_NUM_LIMBS - 1],
233 ];
234 for pair in needs_range_check.chunks_exact(2) {
235 self.bitwise_lookup_bus
236 .send_range(pair[0] * shift.clone(), pair[1] * shift.clone())
237 .eval(builder, *local.instruction.is_enabled);
238 }
239
240 self.execution_bridge
241 .execute_and_increment_pc(
242 AB::Expr::from_usize(C::OPCODE as usize + self.offset),
243 [
244 (*local.instruction.dst_reg_ptr).into(),
245 (*local.instruction.state_reg_ptr).into(),
246 (*local.instruction.input_reg_ptr).into(),
247 AB::Expr::from_u32(RV32_REGISTER_AS),
248 AB::Expr::from_u32(RV32_MEMORY_AS),
249 ],
250 *local.instruction.from_state,
251 AB::F::from_usize(C::TIMESTAMP_DELTA),
252 )
253 .eval(builder, *local.instruction.is_enabled);
254 }
255
256 pub fn eval_reads<AB: InteractionBuilder>(
257 &self,
258 builder: &mut AB,
259 local: &Sha2ColsRef<AB::Var>,
260 timestamp_pp: &mut impl FnMut() -> AB::Expr,
261 ) {
262 let input_ptr_val = compose(&local.instruction.input_ptr_limbs.to_vec(), RV32_CELL_BITS);
263 for i in 0..C::BLOCK_READS {
264 self.memory_bridge
265 .read::<_, _, SHA2_READ_SIZE>(
266 MemoryAddress::new(
267 AB::Expr::from_u32(RV32_MEMORY_AS),
268 input_ptr_val.clone() + AB::F::from_usize(i * SHA2_READ_SIZE),
269 ),
270 local
271 .block
272 .message_bytes
273 .slice(s![i * SHA2_READ_SIZE..(i + 1) * SHA2_READ_SIZE])
274 .to_vec()
275 .try_into()
276 .unwrap_or_else(|_| {
277 panic!("message bytes is not the correct size");
278 }),
279 timestamp_pp(),
280 &local.mem.input_reads[i],
281 )
282 .eval(builder, *local.instruction.is_enabled);
283 }
284
285 let state_ptr_val = compose(&local.instruction.state_ptr_limbs.to_vec(), RV32_CELL_BITS);
286 for i in 0..C::STATE_READS {
287 self.memory_bridge
288 .read::<_, _, SHA2_READ_SIZE>(
289 MemoryAddress::new(
290 AB::Expr::from_u32(RV32_MEMORY_AS),
291 state_ptr_val.clone() + AB::F::from_usize(i * SHA2_READ_SIZE),
292 ),
293 local
294 .block
295 .prev_state
296 .slice(s![i * SHA2_READ_SIZE..(i + 1) * SHA2_READ_SIZE])
297 .to_vec()
298 .try_into()
299 .unwrap_or_else(|_| {
300 panic!("prev state is not the correct size");
301 }),
302 timestamp_pp(),
303 &local.mem.state_reads[i],
304 )
305 .eval(builder, *local.instruction.is_enabled);
306 }
307 }
308
309 pub fn eval_writes<AB: InteractionBuilder>(
310 &self,
311 builder: &mut AB,
312 local: &Sha2ColsRef<AB::Var>,
313 timestamp_pp: &mut impl FnMut() -> AB::Expr,
314 ) {
315 let dst_ptr_val = compose(&local.instruction.dst_ptr_limbs.to_vec(), RV32_CELL_BITS);
316 for i in 0..C::STATE_READS {
317 self.memory_bridge
318 .write::<_, _, SHA2_WRITE_SIZE>(
319 MemoryAddress::new(
320 AB::Expr::from_u32(RV32_MEMORY_AS),
321 dst_ptr_val.clone() + AB::F::from_usize(i * SHA2_READ_SIZE),
322 ),
323 local
324 .block
325 .new_state
326 .slice(s![i * SHA2_READ_SIZE..(i + 1) * SHA2_READ_SIZE])
327 .to_vec()
328 .try_into()
329 .unwrap_or_else(|_| {
330 panic!("new state is not the correct size");
331 }),
332 timestamp_pp(),
333 &local.mem.write_aux[i],
334 )
335 .eval(builder, *local.instruction.is_enabled);
336 }
337 }
338}