openvm_sha2_circuit/sha2_chips/block_hasher_chip/
trace.rs1use std::slice;
2
3use openvm_circuit::arch::get_record_from_slice;
4use openvm_circuit_primitives::{utils::next_power_of_two_or_zero, Chip};
5use openvm_cpu_backend::CpuBackend;
6use openvm_sha2_air::{
7 be_limbs_into_word, le_limbs_into_word, Sha2BlockHasherFillerHelper, Sha2RoundColsRef,
8 Sha2RoundColsRefMut,
9};
10use openvm_stark_backend::{
11 p3_field::{PrimeCharacteristicRing, PrimeField32},
12 p3_matrix::dense::RowMajorMatrix,
13 p3_maybe_rayon::prelude::*,
14 prover::AirProvingContext,
15 StarkProtocolConfig, Val,
16};
17
18use crate::{
19 Sha2BlockHasherChip, Sha2BlockHasherRoundColsRefMut, Sha2BlockHasherVmConfig, Sha2Config,
20 Sha2Metadata, Sha2RecordLayout, Sha2RecordMut, Sha2SharedRecords, INNER_OFFSET,
21};
22
23impl<R, SC, C: Sha2Config> Chip<R, CpuBackend<SC>> for Sha2BlockHasherChip<Val<SC>, C>
27where
28 Val<SC>: PrimeField32,
29 SC: StarkProtocolConfig,
30{
31 fn generate_proving_ctx(&self, _: R) -> AirProvingContext<CpuBackend<SC>> {
32 let mut records = self.records.lock().unwrap();
34 let mut records = records.take().unwrap();
35 let rows_used = records.num_records * C::ROWS_PER_BLOCK;
36
37 let height = next_power_of_two_or_zero(rows_used);
38 let trace = Val::<SC>::zero_vec(height * C::BLOCK_HASHER_WIDTH);
39 let mut trace_matrix = RowMajorMatrix::new(trace, C::BLOCK_HASHER_WIDTH);
40
41 self.fill_trace(&mut trace_matrix, &mut records, rows_used);
42
43 AirProvingContext::simple_no_pis(trace_matrix)
44 }
45}
46
47impl<F, C> Sha2BlockHasherChip<F, C>
48where
49 F: PrimeField32,
50 C: Sha2BlockHasherVmConfig,
51{
52 fn fill_trace(
53 &self,
54 trace_matrix: &mut RowMajorMatrix<F>,
55 records: &mut Sha2SharedRecords<F>,
56 rows_used: usize,
57 ) {
58 if rows_used == 0 {
59 return;
60 }
61
62 let trace = &mut trace_matrix.values[..];
63
64 let (records, prev_hashes): (Vec<_>, Vec<_>) = records
68 .matrix
69 .par_rows_mut()
70 .take(records.num_records)
71 .map(|mut record| {
72 let record: Sha2RecordMut = unsafe {
79 get_record_from_slice(
80 &mut record,
81 Sha2RecordLayout {
82 metadata: Sha2Metadata {
83 variant: C::VARIANT,
84 },
85 },
86 )
87 };
88
89 let prev_hash = (0..C::HASH_WORDS)
90 .map(|i| {
91 le_limbs_into_word::<C>(
92 &record.prev_state[i * C::WORD_U8S..(i + 1) * C::WORD_U8S]
93 .iter()
94 .map(|x| *x as u32)
95 .collect::<Vec<_>>(),
96 )
97 })
98 .collect::<Vec<_>>();
99
100 (record, prev_hash)
101 })
102 .unzip();
103
104 let prev_hashes_and_next_block_prev_hashes = prev_hashes.par_iter().zip(
106 prev_hashes[1..]
107 .par_iter()
108 .chain(prev_hashes[..1].par_iter()),
109 );
110
111 trace[..rows_used * C::BLOCK_HASHER_WIDTH]
113 .par_chunks_exact_mut(C::BLOCK_HASHER_WIDTH * C::ROWS_PER_BLOCK)
114 .zip(
115 records
116 .par_iter()
117 .zip(prev_hashes_and_next_block_prev_hashes),
118 )
119 .enumerate()
120 .for_each(
121 |(block_idx, (block_slice, (record, (prev_hash, next_block_prev_hash))))| {
122 self.fill_block_trace(
123 block_slice,
124 record.message_bytes,
125 block_idx + 1, prev_hash,
127 next_block_prev_hash,
128 block_idx,
129 );
130 },
131 );
132
133 let num_blocks = rows_used / C::ROWS_PER_BLOCK;
138 let first_dummy_row_cols_const = self.fill_first_dummy_row(
139 &mut trace[rows_used * C::BLOCK_HASHER_WIDTH..(rows_used + 1) * C::BLOCK_HASHER_WIDTH],
140 &prev_hashes[0],
141 num_blocks,
142 );
143
144 let padding_global_block_idx = (num_blocks + 1) as u32;
146 trace[(rows_used + 1) * C::BLOCK_HASHER_WIDTH..]
147 .par_chunks_exact_mut(C::BLOCK_HASHER_WIDTH)
148 .for_each(|row| {
149 self.inner.generate_default_row(
151 &mut Sha2RoundColsRefMut::from::<C>(
152 &mut row[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
153 ),
154 &prev_hashes[0],
155 Some(
156 first_dummy_row_cols_const
157 .work_vars
158 .carry_a
159 .as_slice()
160 .unwrap(),
161 ),
162 Some(
163 first_dummy_row_cols_const
164 .work_vars
165 .carry_e
166 .as_slice()
167 .unwrap(),
168 ),
169 padding_global_block_idx,
170 );
171 });
172
173 trace[C::BLOCK_HASHER_WIDTH..]
176 .par_chunks_mut(C::BLOCK_HASHER_WIDTH * C::ROWS_PER_BLOCK)
177 .take(rows_used / C::ROWS_PER_BLOCK)
178 .for_each(|chunk| {
179 self.inner
180 .generate_missing_cells(chunk, C::BLOCK_HASHER_WIDTH, INNER_OFFSET);
181 });
182
183 self.fill_wraparound(trace);
184 }
185
186 fn fill_wraparound(&self, trace: &mut [F]) {
191 let height = trace.len() / C::BLOCK_HASHER_WIDTH;
192 let last_row_start = (height - 1) * C::BLOCK_HASHER_WIDTH;
193
194 {
196 let (first_row, rest) = trace.split_at_mut(C::BLOCK_HASHER_WIDTH);
197 let last_row = &rest[(height - 2) * C::BLOCK_HASHER_WIDTH..];
198 let local = Sha2RoundColsRef::from::<C>(
199 &last_row[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
200 );
201 let mut next = Sha2RoundColsRefMut::from::<C>(
202 &mut first_row[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
203 );
204 Sha2BlockHasherFillerHelper::<C>::generate_intermed_4(local, &mut next);
205 }
206
207 {
209 let (first_row, rest) = trace.split_at_mut(C::BLOCK_HASHER_WIDTH);
210 let next = Sha2RoundColsRef::from::<C>(
211 &first_row[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
212 );
213 let last_row = &mut rest[last_row_start - C::BLOCK_HASHER_WIDTH..last_row_start];
214 let mut local = Sha2RoundColsRefMut::from::<C>(
215 &mut last_row[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
216 );
217 Sha2BlockHasherFillerHelper::<C>::generate_intermed_12(&mut local, next);
218 }
219 }
220
221 fn fill_first_dummy_row(
222 &self,
223 first_dummy_row_mut: &mut [F],
224 first_block_prev_hash: &[C::Word],
225 num_blocks: usize,
226 ) -> Sha2RoundColsRef<'_, F> {
227 let first_dummy_row_const =
228 unsafe { slice::from_raw_parts(first_dummy_row_mut.as_ptr(), C::BLOCK_HASHER_WIDTH) };
229 let first_dummy_row_cols_const = Sha2RoundColsRef::from::<C>(
230 &first_dummy_row_const[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
231 );
232
233 let first_dummy_row_mut = unsafe {
234 slice::from_raw_parts_mut(first_dummy_row_mut.as_mut_ptr(), C::BLOCK_HASHER_WIDTH)
235 };
236 let mut first_dummy_row_cols_mut: Sha2RoundColsRefMut<F> = Sha2RoundColsRefMut::from::<C>(
237 &mut first_dummy_row_mut[INNER_OFFSET..INNER_OFFSET + C::SUBAIR_ROUND_WIDTH],
238 );
239
240 self.inner.generate_default_row(
243 &mut first_dummy_row_cols_mut,
244 first_block_prev_hash,
245 None,
246 None,
247 (num_blocks + 1) as u32,
248 );
249
250 Sha2BlockHasherFillerHelper::<C>::generate_carry_ae(
256 first_dummy_row_cols_const.clone(),
257 &mut first_dummy_row_cols_mut,
258 );
259
260 first_dummy_row_cols_const
261 }
262}
263
264impl<F, C: Sha2BlockHasherVmConfig> Sha2BlockHasherChip<F, C> {
265 #[allow(clippy::too_many_arguments)]
266 fn fill_block_trace(
267 &self,
268 block_slice: &mut [F],
269 input: &[u8],
270 global_block_idx: usize, prev_hash: &[C::Word],
272 next_block_prev_hash: &[C::Word],
273 request_id: usize,
274 ) where
275 F: PrimeField32,
276 {
277 debug_assert_eq!(input.len(), C::BLOCK_U8S);
278 debug_assert_eq!(prev_hash.len(), C::HASH_WORDS);
279
280 block_slice
282 .par_chunks_exact_mut(C::BLOCK_HASHER_WIDTH)
283 .for_each(|row_slice| {
284 let cols = Sha2BlockHasherRoundColsRefMut::<F>::from::<C>(
286 &mut row_slice[..C::BLOCK_HASHER_WIDTH],
287 );
288 *cols.request_id = F::from_usize(request_id);
289 });
290
291 let input_words = (0..C::BLOCK_WORDS)
292 .map(|i| {
293 be_limbs_into_word::<C>(
294 &input[i * C::WORD_U8S..(i + 1) * C::WORD_U8S]
295 .iter()
296 .map(|x| *x as u32)
297 .collect::<Vec<_>>(),
298 )
299 })
300 .collect::<Vec<_>>();
301
302 self.inner.generate_block_trace(
304 block_slice,
305 C::BLOCK_HASHER_WIDTH,
306 INNER_OFFSET,
307 &input_words,
308 self.bitwise_lookup_chip.clone(),
309 prev_hash,
310 next_block_prev_hash,
311 global_block_idx as u32,
312 );
313 }
314}