use nebu::Goldilocks;
use nox::TraceRow;
use hemera::field::Goldilocks as HGold;
use super::particle::HashAux;
use super::selector;
use super::verifier_steps::eq_step;
use crate::types::{CCSInstance, CCSWitness, CommitError};
const BLOCK_ROWS: usize = 25;
fn reg(row: &TraceRow, i: usize) -> Goldilocks {
Goldilocks::new(row.r()[i]).canonicalize()
}
pub fn build_hash_binding_steps_from_trace(
trace: &[TraceRow],
aux: &[HashAux],
) -> Result<Vec<(CCSInstance, CCSWitness)>, CommitError> {
let mut steps = Vec::new();
let mut aux_idx = 0;
let mut i = 0;
while i < trace.len() {
if trace[i].r()[0] != 15 {
i += 1;
continue;
}
let block_start = i;
while i < trace.len() && trace[i].r()[0] == 15 {
i += 1;
}
let block = &trace[block_start..i];
if block.len() < 2 {
continue;
}
let ha = aux.get(aux_idx).ok_or(CommitError::TraceOverflow)?;
aux_idx += 1;
if block.len() != BLOCK_ROWS {
return Err(CommitError::HashBinding);
}
let mut rate_h = [HGold::ZERO; 8];
for (j, r) in ha.rate.iter().enumerate() {
rate_h[j] = HGold::new(r.canonicalize().as_u64());
}
let states: Vec<[Goldilocks; 16]> = hemera::StepSponge::absorb(&rate_h)
.map(|s| core::array::from_fn(|j| Goldilocks::new(s[j].as_canonical_u64())))
.collect();
let block_steps_start = steps.len();
for (k, row) in block[..BLOCK_ROWS - 1].iter().enumerate() {
let st = &states[k];
for j in 0..4 {
steps.push(eq_step(st[j], reg(row, 4 + j)));
}
for j in 0..4 {
steps.push(eq_step(st[4 + j], reg(row, 10 + j)));
}
steps.push(eq_step(Goldilocks::new(k as u64), reg(row, 14)));
}
let sq = &block[BLOCK_ROWS - 1];
let last = &states[BLOCK_ROWS - 2];
for j in 0..4 {
steps.push(eq_step(last[j], reg(sq, 4 + j)));
}
for j in 0..4 {
steps.push(eq_step(last[4 + j], reg(sq, 10 + j)));
}
steps.push(eq_step(Goldilocks::new((BLOCK_ROWS - 1) as u64), reg(sq, 14)));
if steps[block_steps_start..]
.iter()
.any(|(inst, wit)| !selector::is_satisfied(inst, wit))
{
return Err(CommitError::HashBinding);
}
}
Ok(steps)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_trace_yields_no_binding_steps() {
let steps = build_hash_binding_steps_from_trace(&[], &[]).unwrap();
assert!(steps.is_empty());
}
}