Source code for zisk/precompiles/sha256f/src/sha256f.rs

  1use core::panic;
  2use std::sync::Arc;
  3
  4use fields::PrimeField64;
  5use rayon::prelude::*;
  6
  7use pil_std_lib::Std;
  8use proofman_common::{AirInstance, FromTrace, ProofmanResult};
  9use proofman_util::{timer_start_trace, timer_stop_and_log_trace};
 10#[cfg(not(feature = "packed"))]
 11use zisk_pil::{Sha256fTrace, Sha256fTraceRow};
 12#[cfg(feature = "packed")]
 13use zisk_pil::{Sha256fTracePacked, Sha256fTraceRowPacked};
 14
 15#[cfg(feature = "packed")]
 16type Sha256fTraceRowType<F> = Sha256fTraceRowPacked<F>;
 17#[cfg(feature = "packed")]
 18type Sha256fTraceType<F> = Sha256fTracePacked<F>;
 19
 20#[cfg(not(feature = "packed"))]
 21type Sha256fTraceRowType<F> = Sha256fTraceRow<F>;
 22#[cfg(not(feature = "packed"))]
 23type Sha256fTraceType<F> = Sha256fTrace<F>;
 24
 25use super::{sha256f_constants::*, Sha256fInput};
 26
27/// The `Sha256fSM` struct encapsulates the logic of the Sha256f State Machine. 28pub struct Sha256fSM<F: PrimeField64> { 29 /// Reference to the PIL2 standard library. 30 pub std: Arc<Std<F>>, 31 32 /// Number of available sha256fs in the trace. 33 pub num_available_sha256fs: usize, 34 35 num_non_usable_rows: usize, 36 37 /// Range checks ID's 38 a_range_id: usize, 39 e_range_id: usize,
40}
41 42impl<F: PrimeField64> Sha256fSM<F> { 43 /// Creates a new Sha256f State Machine instance. 44 /// 45 /// # Returns
46 /// A new `Sha256fSM` instance. 47 pub fn new(std: Arc<Std<F>>) -> Arc<Self> { 48 // Compute some useful values 49 let num_available_sha256fs = Sha256fTraceType::<F>::NUM_ROWS / CLOCKS - 1; 50 let num_non_usable_rows = Sha256fTraceType::<F>::NUM_ROWS % CLOCKS; 51 52 let a_range_id = std.get_range_id(0, (1 << 3) - 1, None).expect("Failed to get range ID"); 53 let e_range_id = std.get_range_id(0, (1 << 3) - 1, None).expect("Failed to get range ID"); 54 55 Arc::new(Self { std, num_available_sha256fs, num_non_usable_rows, a_range_id, e_range_id })
56 } 57 58 /// Processes a slice of operation data, updating the trace and multiplicities. 59 /// 60 /// # Arguments 61 /// * `trace` - A mutable reference to the Sha256f trace. 62 /// * `num_circuits` - The number of circuits to process. 63 /// * `input` - The operation data to process. 64 /// * `multiplicity` - A mutable slice to update with multiplicities for the operation.
65 #[inline(always)] 66 pub fn process_input( 67 &self, 68 input: &Sha256fInput, 69 trace: &mut [Sha256fTraceRowType<F>], 70 ) -> ([u32; 8], [u32; 8]) { 71 let mut a_range_checks = [0u32; 8]; 72 let mut e_range_checks = [0u32; 8]; 73 74 let step_main = input.step_main; 75 let addr_main = input.addr_main; 76 let state_addr = input.state_addr; 77 let input_addr = input.input_addr; 78 let state = &input.state; 79 let input = &input.input; 80 81 // Fill the step_addr 82 trace[0].set_step_addr(step_main); // STEP_MAIN 83 trace[1].set_step_addr(addr_main as u64); // ADDR_OP 84 trace[2].set_step_addr(state_addr as u64); // ADDR_STATE 85 trace[3].set_step_addr(input_addr as u64); // ADDR_INPUT 86 trace[4].set_step_addr(state_addr as u64); // ADDR_IND_0 87 trace[5].set_step_addr(input_addr as u64); // ADDR_IND_1 88 89 // Activate the clk_0 selector 90 trace[0].set_in_use_clk_0(true); 91 92 // Activate the in_use selector 93 for r in trace.iter_mut().take(18) { 94 r.set_in_use(true); 95 } 96 97 // Compute the load state stage 98 let mut offset = 0; 99 let mut prev_state = [0u32; 8]; 100 for i in 0..CLOCKS_LOAD_STATE { 101 let word = state[i]; 102 let word_high = (word >> 32) as u32; 103 let word_low = (word & 0xFFFF_FFFF) as u32; 104 105 // Store the state as u32 for further processing 106 prev_state[2 * i] = word_high; 107 prev_state[2 * i + 1] = word_low; 108 109 let mut row = if i == 1 || i == 3 { offset + 1 } else { offset + 3 }; 110 111 // Locate the state bits in the trace 112 let is_a = i < 2; 113 for j in 0..32 { 114 let bit = ((word_high >> j) & 1) != 0; 115 if is_a { 116 trace[row].set_a(j, bit); 117 } else { 118 trace[row].set_e(j, bit); 119 } 120 } 121 row -= 1; 122 for j in 0..32 { 123 let bit = ((word_low >> j) & 1) != 0; 124 if is_a { 125 trace[row].set_a(j, bit); 126 } else { 127 trace[row].set_e(j, bit); 128 } 129 } 130 } 131 offset += CLOCKS_LOAD_STATE; 132 133 // Compute the load input stage 134 let mut w = [0u32; 16]; 135 for i in 0..CLOCKS_LOAD_INPUT { 136 let word = input[i / 2]; 137 138 // Store the input as u32 for further processing 139 w[i] = if i % 2 == 0 { (word >> 32) as u32 } else { (word & 0xFFFF_FFFF) as u32 }; 140 141 // Compute the a and e values for the current input 142 let [old_a, old_b, old_c, old_d, old_e, old_f, old_g, old_h] = prev_state; 143 let (a, e) = 144 compute_ae(old_a, old_b, old_c, old_d, old_e, old_f, old_g, old_h, w[i], RC[i]); 145 146 let (a_carry, a) = ((a >> 32) as u8, (a & 0xFFFF_FFFF) as u32); 147 let (e_carry, e) = ((e >> 32) as u8, (e & 0xFFFF_FFFF) as u32); 148 149 let row = offset + i; 150 151 // Locate the carry 152 trace[row].set_new_a_carry_bits(a_carry); 153 trace[row].set_new_e_carry_bits(e_carry); 154 a_range_checks[a_carry as usize] += 1; 155 e_range_checks[e_carry as usize] += 1; 156 157 // Locate the input bits in the trace 158 for j in 0..32 { 159 let bit_a = ((a >> j) & 1) != 0; 160 let bit_e = ((e >> j) & 1) != 0; 161 let bit_w = ((w[i] >> j) & 1) != 0; 162 trace[row].set_a(j, bit_a); 163 trace[row].set_e(j, bit_e); 164 trace[row].set_w(j, bit_w); 165 } 166 167 // Update prev_state for the next iteration 168 prev_state[7] = old_g; 169 prev_state[6] = old_f; 170 prev_state[5] = old_e; 171 prev_state[4] = e; 172 prev_state[3] = old_c; 173 prev_state[2] = old_b; 174 prev_state[1] = old_a; 175 prev_state[0] = a; 176 } 177 offset += CLOCKS_LOAD_INPUT; 178 179 // Compute the mixing stage 180 for i in 0..CLOCKS_MIXING { 181 let [old_w2, old_w7, old_w15, old_w16] = [ 182 w[CLOCKS_LOAD_INPUT - 2], 183 w[CLOCKS_LOAD_INPUT - 7], 184 w[CLOCKS_LOAD_INPUT - 15], 185 w[CLOCKS_LOAD_INPUT - 16], 186 ]; 187 let new_w = compute_w(old_w2, old_w7, old_w15, old_w16); 188 let (new_w_carry, new_w) = ((new_w >> 32) as u8, (new_w & 0xFFFF_FFFF) as u32); 189 190 let [old_a, old_b, old_c, old_d, old_e, old_f, old_g, old_h] = prev_state; 191 #[rustfmt::skip] 192 let (a, e) = compute_ae(old_a, old_b, old_c, old_d, old_e, old_f, old_g, old_h, new_w, RC[CLOCKS_LOAD_INPUT + i]); 193 194 let (a_carry, a) = ((a >> 32) as u8, (a & 0xFFFF_FFFF) as u32); 195 let (e_carry, e) = ((e >> 32) as u8, (e & 0xFFFF_FFFF) as u32); 196 197 let row = offset + i; 198 199 // Locate the carry 200 trace[row].set_new_a_carry_bits(a_carry); 201 trace[row].set_new_e_carry_bits(e_carry); 202 trace[row].set_new_w_carry_bits(new_w_carry); 203 a_range_checks[a_carry as usize] += 1; 204 e_range_checks[e_carry as usize] += 1; 205 206 for j in 0..32 { 207 let bit_a = ((a >> j) & 1) != 0; 208 let bit_e = ((e >> j) & 1) != 0; 209 let bit_w = ((new_w >> j) & 1) != 0; 210 trace[row].set_a(j, bit_a); 211 trace[row].set_e(j, bit_e); 212 trace[row].set_w(j, bit_w); 213 } 214 215 // Update prev_state for the next iteration 216 prev_state[7] = old_g; 217 prev_state[6] = old_f; 218 prev_state[5] = old_e; 219 prev_state[4] = e; 220 prev_state[3] = old_c; 221 prev_state[2] = old_b; 222 prev_state[1] = old_a; 223 prev_state[0] = a; 224 225 // Update the w array for the next iteration 226 for j in 0..15 { 227 w[j] = w[j + 1]; 228 } 229 w[15] = new_w; 230 } 231 offset += CLOCKS_MIXING; 232 233 for i in 0..CLOCKS_WRITE_STATE { 234 let prev = state[i]; 235 let prev_high = prev >> 32; 236 let prev_low = prev & 0xFFFF_FFFF; 237 238 let curr_high = (prev_state[2 * i]) as u64; 239 let curr_low = (prev_state[2 * i + 1]) as u64; 240 241 let new_high = curr_high + prev_high; 242 let new_low = curr_low + prev_low; 243 let (new_high_carry, new_high) = 244 ((new_high >> 32) as u8, (new_high & 0xFFFF_FFFF) as u32); 245 let (new_low_carry, new_low) = ((new_low >> 32) as u8, (new_low & 0xFFFF_FFFF) as u32); 246 247 let mut row = if i == 1 || i == 3 { offset + 1 } else { offset + 3 }; 248 249 // Locate the state bits in the trace 250 let is_a = i < 2; 251 if is_a { 252 trace[row].set_new_a_carry_bits(new_high_carry); 253 a_range_checks[new_high_carry as usize] += 1; 254 } else { 255 trace[row].set_new_e_carry_bits(new_high_carry); 256 e_range_checks[new_high_carry as usize] += 1; 257 } 258 259 for j in 0..32 { 260 let bit = ((new_high >> j) & 1) != 0; 261 if is_a { 262 trace[row].set_a(j, bit); 263 } else { 264 trace[row].set_e(j, bit); 265 } 266 } 267 row -= 1; 268 269 if is_a { 270 trace[row].set_new_a_carry_bits(new_low_carry); 271 a_range_checks[new_low_carry as usize] += 1; 272 } else { 273 trace[row].set_new_e_carry_bits(new_low_carry); 274 e_range_checks[new_low_carry as usize] += 1; 275 } 276 277 for j in 0..32 { 278 let bit = ((new_low >> j) & 1) != 0; 279 if is_a { 280 trace[row].set_a(j, bit); 281 } else { 282 trace[row].set_e(j, bit); 283 } 284 } 285 } 286 287 // Perform the zero range checks 288 a_range_checks[0] += CLOCKS_LOAD_STATE as u32; 289 e_range_checks[0] += CLOCKS_LOAD_STATE as u32; 290 291 return (a_range_checks, e_range_checks); 292 293 #[rustfmt::skip] 294 #[allow(clippy::too_many_arguments)] 295 fn compute_ae(old_a: u32, old_b: u32, old_c: u32, old_d: u32, old_e: u32, old_f: u32, old_g: u32, old_h: u32, w: u32, k: u32) -> (u64, u64) { 296 let s0 = rotate_right(old_a, 2) ^ rotate_right(old_a, 13) ^ rotate_right(old_a, 22); 297 let s1 = rotate_right(old_e, 6) ^ rotate_right(old_e, 11) ^ rotate_right(old_e, 25); 298 let t1 = (old_h as u64) + (s1 as u64) + (ch(old_e, old_f, old_g) as u64) + (k as u64) + (w as u64); 299 let t2 = (s0 as u64) + (maj(old_a, old_b, old_c) as u64); 300 let a = (t1 as u64) + (t2 as u64); 301 let e = (old_d as u64) + (t1 as u64); 302 (a, e) 303 // (s0 as u64, s1 as u64) 304 } 305 306 fn compute_w(old_w2: u32, old_w7: u32, old_w15: u32, old_w16: u32) -> u64 { 307 let s0 = rotate_right(old_w15, 7) ^ rotate_right(old_w15, 18) ^ shift_right(old_w15, 3); 308 let s1 = rotate_right(old_w2, 17) ^ rotate_right(old_w2, 19) ^ shift_right(old_w2, 10); 309 (s1 as u64) + (old_w7 as u64) + (s0 as u64) + (old_w16 as u64) 310 } 311 312 fn rotate_right(x: u32, n: u32) -> u32 { 313 x.rotate_right(n) 314 } 315 316 fn shift_right(x: u32, n: u32) -> u32 { 317 x >> n 318 } 319 320 fn maj(x: u32, y: u32, z: u32) -> u32 { 321 (x & y) ^ (x & z) ^ (y & z) 322 } 323 324 fn ch(x: u32, y: u32, z: u32) -> u32 { 325 (x & y) ^ (!x & z) 326 }
327 } 328 329 /// Computes the witness for a series of inputs and produces an `AirInstance`. 330 /// 331 /// # Arguments 332 /// * `sctx` - The setup context containing the setup data. 333 /// * `inputs` - A slice of operations to process. 334 /// 335 /// # Returns
336 /// An `AirInstance` containing the computed witness data. 337 pub fn compute_witness( 338 &self, 339 inputs: &[Vec<Sha256fInput>], 340 trace_buffer: Vec<F>, 341 ) -> ProofmanResult<AirInstance<F>> { 342 let mut sha256f_trace = Sha256fTraceType::new_from_vec_zeroes(trace_buffer)?; 343 let num_rows = sha256f_trace.num_rows(); 344 let num_available_sha256fs = self.num_available_sha256fs; 345 346 let mut a_range_checks = vec![0; 1 << 3]; 347 let mut e_range_checks = vec![0; 1 << 3]; 348 349 // Check that we can fit all the sha256fs in the trace 350 let num_inputs = inputs.iter().map(|v| v.len()).sum::<usize>(); 351 let num_rows_filled = num_inputs * CLOCKS; 352 let num_rows_needed = if num_inputs < num_available_sha256fs { 353 num_inputs * CLOCKS 354 } else if num_inputs == num_available_sha256fs { 355 num_rows 356 } else { 357 panic!( 358 "Exceeded available Sha256fs inputs: requested {}, but only {} are available.", 359 num_inputs, self.num_available_sha256fs 360 ); 361 }; 362 363 tracing::debug!( 364 "··· Creating Sha256f instance [{} / {} rows filled {:.2}%]", 365 num_rows_needed, 366 num_rows, 367 num_rows_needed as f64 / num_rows as f64 * 100.0 368 ); 369 370 timer_start_trace!(SHA256F_TRACE); 371 let mut trace_rows = sha256f_trace.buffer.as_mut_slice(); 372 let mut par_traces = Vec::new(); 373 let mut inputs_indexes = Vec::new(); 374 for (i, inputs) in inputs.iter().enumerate() { 375 for (j, _) in inputs.iter().enumerate() { 376 let (head, tail) = trace_rows.split_at_mut(CLOCKS); 377 par_traces.push(head); 378 inputs_indexes.push((i, j)); 379 trace_rows = tail; 380 } 381 } 382 383 // Fill the trace 384 let input_range_checks: Vec<([u32; 8], [u32; 8])> = par_traces 385 .into_par_iter() 386 .enumerate() 387 .map(|(index, trace)| { 388 let input_index = inputs_indexes[index]; 389 let input = &inputs[input_index.0][input_index.1]; 390 self.process_input(input, trace) 391 }) 392 .collect(); 393 394 for (a_inp_range_checks, e_inp_range_checks) in input_range_checks { 395 for i in 0..8 { 396 a_range_checks[i] += a_inp_range_checks[i]; 397 e_range_checks[i] += e_inp_range_checks[i]; 398 } 399 } 400 401 timer_stop_and_log_trace!(SHA256F_TRACE); 402 403 timer_start_trace!(SHA256F_PADDING); 404 // Set a = e = w = 0 for the state and input rows 405 let zero_row = Sha256fTraceRowType::<F>::default(); 406 407 // precompute compute_ae() with initial a = e = 0 (PC_A and PC_E) 408 // compute_w() with w = 0 is equal to 0, nothing to do 409 let mut mid_rows = [Sha256fTraceRowType::<F>::default(); 64]; 410 for i in 0..64 { 411 let a = PC_A[i]; 412 let e = PC_E[i]; 413 let (a_carry, a) = ((a >> 32) as u8, (a & 0xFFFF_FFFF) as u32); 414 let (e_carry, e) = ((e >> 32) as u8, (e & 0xFFFF_FFFF) as u32); 415 mid_rows[i].set_new_a_carry_bits(a_carry); 416 mid_rows[i].set_new_e_carry_bits(e_carry); 417 for j in 0..32 { 418 let bit_a = ((a >> j) & 1) != 0; 419 let bit_e = ((e >> j) & 1) != 0; 420 mid_rows[i].set_a(j, bit_a); 421 mid_rows[i].set_e(j, bit_e); 422 } 423 424 a_range_checks[a_carry as usize] += (num_available_sha256fs - num_inputs) as u32; 425 e_range_checks[e_carry as usize] += (num_available_sha256fs - num_inputs) as u32; 426 } 427 428 // At the end, we should have that a === 4'and e === 4'e 429 let mut final_rows = [Sha256fTraceRowType::<F>::default(); 4]; 430 for i in 0..4 { 431 let a = (PC_A[60 + i] & 0xFFFF_FFFF) as u32; 432 let e = (PC_E[60 + i] & 0xFFFF_FFFF) as u32; 433 for j in 0..32 { 434 let bit_a = ((a >> j) & 1) != 0; 435 let bit_e = ((e >> j) & 1) != 0; 436 final_rows[i].set_a(j, bit_a); 437 final_rows[i].set_e(j, bit_e); 438 } 439 } 440 441 const CLOCKS_OP: usize = CLOCKS_LOAD_STATE + CLOCKS_LOAD_INPUT + CLOCKS_MIXING; 442 // The last (CLOCKS + NUM_NON_USABLE_ROWS) have CLK_0 desactivated, so 443 // a trace full of zeroes passes the constraints 444 sha256f_trace.buffer[num_rows_filled..(num_rows - self.num_non_usable_rows - CLOCKS)] 445 .par_iter_mut() 446 .enumerate() 447 .for_each(|(elem, row)| { 448 let row_r = elem % CLOCKS; 449 if row_r < CLOCKS_LOAD_STATE { 450 *row = zero_row; 451 } else if row_r < CLOCKS_OP { 452 *row = mid_rows[row_r - CLOCKS_LOAD_STATE]; 453 } else { 454 *row = final_rows[row_r - CLOCKS_OP]; 455 } 456 }); 457 458 // Perform the zero range checks 459 let count_zeros = (num_available_sha256fs - num_inputs) 460 * (CLOCKS_LOAD_STATE + CLOCKS_WRITE_STATE) 461 + CLOCKS 462 + self.num_non_usable_rows; 463 a_range_checks[0] += count_zeros as u32; 464 e_range_checks[0] += count_zeros as u32; 465 466 self.std.range_checks(self.a_range_id, a_range_checks); 467 self.std.range_checks(self.e_range_id, e_range_checks); 468 469 timer_stop_and_log_trace!(SHA256F_PADDING); 470 471 Ok(AirInstance::new_from_trace(FromTrace::new(&mut sha256f_trace)))
472 }
473}