use core::panic;
use std::sync::Arc;
use proofman_fields::PrimeField64;
use rayon::prelude::*;
use pil2_std_lib::Std;
use proofman_common::{AirInstance, FromTrace, ProofmanResult, SetupCtx};
use proofman_util::{timer_start_trace, timer_stop_and_log_trace};
use zisk_common::OperationBlake2Data;
use zisk_pil::{Blake2brTrace, Blake2brTraceRow, Blake2brTraceRowOps};
use super::blake2_constants::{BLAKE2BR_TABLE_SIZE, CLOCKS, R1_G, R2_G, R3_G, R4_G, SIGMA};
use super::blake2_table::Blake2brTableSM;
const G_INDICES: [(usize, usize, usize, usize); CLOCKS] = [
(0, 4, 8, 12),
(1, 5, 9, 13),
(2, 6, 10, 14),
(3, 7, 11, 15),
(0, 5, 10, 15),
(1, 6, 11, 12),
(2, 7, 8, 13),
(3, 4, 9, 14),
];
const RANGE_CHECKED_LIMBS_PER_ROW: usize = 16;
const XOR_CHECKS_PER_ROW: usize = 32;
#[derive(Debug)]
pub struct Blake2Input {
pub addr_main: u32,
pub step_main: u64,
pub index: u64,
pub state_addr: u32,
pub input_addr: u32,
pub state: [u64; 16],
pub input: [u64; 16],
}
impl Blake2Input {
pub fn from(values: &OperationBlake2Data<u64>) -> Self {
Self {
addr_main: values[3] as u32,
step_main: values[4],
index: values[5],
state_addr: values[6] as u32,
input_addr: values[7] as u32,
state: values[8..24].try_into().unwrap(),
input: values[24..40].try_into().unwrap(),
}
}
}
pub struct Blake2SM<F: PrimeField64> {
pub std: Arc<Std<F>>,
pub num_available_blake2s: usize,
range_id: usize,
table_id: usize,
}
impl<F: PrimeField64> Blake2SM<F> {
pub fn new(std: Arc<Std<F>>) -> Arc<Self> {
let num_non_usable_rows = Blake2brTrace::<Blake2brTraceRow<F>>::NUM_ROWS % CLOCKS;
let num_available_blake2s = Blake2brTrace::<Blake2brTraceRow<F>>::NUM_ROWS / CLOCKS
- (num_non_usable_rows != 0) as usize;
let range_id = std.get_range_id(0, (1 << 16) - 1, None).expect("Failed to get range ID");
let table_id = std
.get_virtual_table_id(Blake2brTableSM::TABLE_ID)
.expect("Failed to get Blake2br table ID");
Arc::new(Self { std, num_available_blake2s, range_id, table_id })
}
#[inline(always)]
pub fn process_input<R: Blake2brTraceRowOps<F>>(
&self,
input: &Blake2Input,
trace: &mut [R],
range_checks: &mut [u32],
xor_checks: &mut [u32],
) {
let idx_usize = input.index as usize;
let s = &SIGMA[idx_usize];
trace[0].set_step_addr(input.step_main); trace[1].set_step_addr(input.addr_main as u64); trace[2].set_step_addr(input.state_addr as u64); trace[3].set_step_addr(input.input_addr as u64); trace[4].set_step_addr(input.state_addr as u64); trace[5].set_step_addr(input.input_addr as u64);
let mut v = input.state;
for (k, row) in trace.iter_mut().enumerate().take(CLOCKS) {
row.set_in_use(true);
row.set_round_idx_sel(idx_usize, true);
let x_limbs = u64_to_limbs16(input.input[2 * k]);
let y_limbs = u64_to_limbs16(input.input[2 * k + 1]);
row.set_all_x(&x_limbs);
row.set_all_y(&y_limbs);
for limb in x_limbs {
range_checks[limb as usize] += 1;
}
for limb in y_limbs {
range_checks[limb as usize] += 1;
}
let xs = input.input[s[2 * k]];
let ys = input.input[s[2 * k + 1]];
row.set_all_xs(&[xs as u32, (xs >> 32) as u32]);
row.set_all_ys(&[ys as u32, (ys >> 32) as u32]);
let (ia, ib, ic, id) = G_INDICES[k];
let (va, vb, vc, vd) = (v[ia], v[ib], v[ic], v[id]);
let va_p = va.wrapping_add(vb).wrapping_add(xs);
let vd_p = (vd ^ va_p).rotate_right(R1_G);
let vc_p = vc.wrapping_add(vd_p);
let vb_p = (vb ^ vc_p).rotate_right(R2_G);
let va_pp = va_p.wrapping_add(vb_p).wrapping_add(ys);
let vd_pp = (vd_p ^ va_pp).rotate_right(R3_G);
let vc_pp = vc_p.wrapping_add(vd_pp);
let z = vb_p ^ vc_pp;
let vb_pp = z.rotate_right(R4_G);
let va_limbs = u64_to_limbs16(va);
let vc_limbs = u64_to_limbs16(vc);
row.set_all_va(&va_limbs);
row.set_all_vc(&vc_limbs);
for limb in va_limbs {
range_checks[limb as usize] += 1;
}
for limb in vc_limbs {
range_checks[limb as usize] += 1;
}
let vb_bytes = vb.to_le_bytes();
let vd_bytes = vd.to_le_bytes();
row.set_all_vb(&vb_bytes);
row.set_all_vd(&vd_bytes);
let va_p_bytes = va_p.to_le_bytes();
let vd_p_bytes = vd_p.to_le_bytes();
let vc_p_bytes = vc_p.to_le_bytes();
let vb_p_bytes = vb_p.to_le_bytes();
let va_pp_bytes = va_pp.to_le_bytes();
let vd_pp_bytes = vd_pp.to_le_bytes();
let vc_pp_bytes = vc_pp.to_le_bytes();
let z_bytes = z.to_le_bytes();
row.set_all_va_prime(&va_p_bytes);
row.set_all_vd_prime(&vd_p_bytes);
row.set_all_vc_prime(&vc_p_bytes);
row.set_all_vb_prime(&vb_p_bytes);
row.set_all_va_prime_prime(&va_pp_bytes);
row.set_all_vd_prime_prime(&vd_pp_bytes);
row.set_all_vc_prime_prime(&vc_pp_bytes);
row.set_all_vb_pp_xor(&z_bytes);
row.set_all_vb_pp_t(&[(z >> 31) & 1 == 1, (z >> 63) & 1 == 1]);
for i in 0..8 {
let rows = [
Blake2brTableSM::calculate_table_row(vd_bytes[i], va_p_bytes[i]),
Blake2brTableSM::calculate_table_row(vb_bytes[i], vc_p_bytes[i]),
Blake2brTableSM::calculate_table_row(vd_p_bytes[i], va_pp_bytes[i]),
Blake2brTableSM::calculate_table_row(vb_p_bytes[i], vc_pp_bytes[i]),
];
for table_row in rows {
xor_checks[table_row as usize] += 1;
}
}
v[ia] = va_pp;
v[ib] = vb_pp;
v[ic] = vc_pp;
v[id] = vd_pp;
}
fn u64_to_limbs16(value: u64) -> [u16; 4] {
[value as u16, (value >> 16) as u16, (value >> 32) as u16, (value >> 48) as u16]
}
}
pub fn compute_witness<R: Blake2brTraceRowOps<F>>(
&self,
_sctx: &SetupCtx<F>,
inputs: &[Vec<Blake2Input>],
trace_buffer: Vec<F>,
) -> ProofmanResult<AirInstance<F>> {
let mut trace = Blake2brTrace::<R>::new_from_vec_zeroes(trace_buffer)?;
let num_rows = trace.num_rows();
let num_available_blake2s = self.num_available_blake2s;
let num_inputs = inputs.iter().map(|v| v.len()).sum::<usize>();
if num_inputs > num_available_blake2s {
panic!(
"Exceeded available Blake2s inputs: requested {}, but only {} are available.",
num_inputs, num_available_blake2s
);
}
let num_rows_filled = num_inputs * CLOCKS;
tracing::debug!(
"ยทยทยท Creating Blake2 instance [{} / {} rows filled {:.2}%]",
num_rows_filled,
num_rows,
num_rows_filled as f64 / num_rows as f64 * 100.0
);
timer_start_trace!(BLAKE2_TRACE);
let mut trace_rows = trace.buffer.as_mut_slice();
let mut par_traces = Vec::new();
let mut inputs_indexes = Vec::new();
for (i, inputs) in inputs.iter().enumerate() {
for (j, _) in inputs.iter().enumerate() {
let (head, tail) = trace_rows.split_at_mut(CLOCKS);
par_traces.push(head);
inputs_indexes.push((i, j));
trace_rows = tail;
}
}
let (mut range_checks, xor_checks) = par_traces
.into_par_iter()
.enumerate()
.fold(
|| (vec![0u32; 1 << 16], vec![0u32; BLAKE2BR_TABLE_SIZE]),
|(mut range_checks, mut xor_checks), (index, trace)| {
let input_index = inputs_indexes[index];
let input = &inputs[input_index.0][input_index.1];
self.process_input::<R>(input, trace, &mut range_checks, &mut xor_checks);
(range_checks, xor_checks)
},
)
.reduce(
|| (vec![0u32; 1 << 16], vec![0u32; BLAKE2BR_TABLE_SIZE]),
|(mut range_acc, mut xor_acc), (range, xor)| {
for (acc, val) in range_acc.iter_mut().zip(range) {
*acc += val;
}
for (acc, val) in xor_acc.iter_mut().zip(xor) {
*acc += val;
}
(range_acc, xor_acc)
},
);
trace.buffer[num_rows_filled..num_rows]
.par_iter_mut()
.for_each(|slot| *slot = R::default());
let num_padding_rows = (num_rows - num_rows_filled) as u32;
range_checks[0] += RANGE_CHECKED_LIMBS_PER_ROW as u32 * num_padding_rows;
timer_stop_and_log_trace!(BLAKE2_TRACE);
self.std.range_check_ranged(self.range_id, None, &range_checks);
let zero_row = Blake2brTableSM::calculate_table_row(0, 0) as usize;
xor_checks.into_par_iter().enumerate().for_each(|(row, mut value)| {
if row == zero_row {
value += XOR_CHECKS_PER_ROW as u32 * num_padding_rows;
}
if value > 0 {
self.std.inc_virtual_row(self.table_id, row as u32, value);
}
});
Ok(AirInstance::new_from_trace(FromTrace::new(&mut trace)))
}
}