pub mod particle;
pub mod patterns;
pub mod selector;
pub mod transcript;
pub mod verifier_steps;
pub use particle::{build_hash_steps_from_trace, HashAux, Z_LEN_HASH};
pub use patterns::build_step_ccs;
pub use selector::constraint_eval;
pub use transcript::build_transcript_steps;
pub use verifier_steps::{eq_step, verifier_steps};
use nebu::Goldilocks;
use nox::TraceRow;
use lens::{Commitment, Opening};
use crate::types::{CCSInstance, CCSWitness, CommitError};
pub struct AxisOpening {
pub commitment: Commitment,
pub point: Vec<Goldilocks>,
pub value: Goldilocks,
pub opening: Opening,
pub transcript_seed: Vec<u8>,
}
pub struct LookOpening {
pub commitment: Commitment,
pub point: Vec<Goldilocks>,
pub value: Goldilocks,
pub opening: Opening,
pub transcript_seed: Vec<u8>,
pub bbg_root: [Goldilocks; 4],
pub namespace: Goldilocks,
pub a_commit: Option<[Goldilocks; 4]>,
}
pub const fn reg_t(r: usize) -> usize { r }
pub const fn reg_t1(r: usize) -> usize { r + 16 }
pub const CONST_IDX: usize = 32;
pub const Z_LEN: usize = 33;
pub fn select_matrix(col: usize) -> crate::types::SparseMatrix {
let mut m = crate::types::SparseMatrix::new(1, Z_LEN);
m.set(0, col, Goldilocks::ONE);
m
}
pub fn witness_from_rows(row_t: &TraceRow, row_t1: &TraceRow) -> CCSWitness {
let mut z = Vec::with_capacity(Z_LEN);
for &v in row_t.r().iter() {
z.push(Goldilocks::new(v).canonicalize());
}
for &v in row_t1.r().iter() {
z.push(Goldilocks::new(v).canonicalize());
}
z.push(Goldilocks::ONE);
CCSWitness { z }
}
pub fn build_ccs_from_trace(trace: &[TraceRow]) -> Vec<(CCSInstance, CCSWitness)> {
if trace.len() < 2 {
return Vec::new();
}
trace.windows(2)
.map(|w| {
let tag_t = u8::try_from(w[0].r()[0]).unwrap_or(255);
let tag_t1 = u8::try_from(w[1].r()[0]).unwrap_or(255);
let instance = if is_multi_row(tag_t) && tag_t != tag_t1 {
patterns::trivial_ccs()
} else {
build_step_ccs(tag_t)
};
let witness = witness_from_rows(&w[0], &w[1]);
(instance, witness)
})
.collect()
}
fn is_multi_row(tag: u8) -> bool {
matches!(tag, 10..=15)
}
pub fn build_axis_steps_from_trace(
trace: &[TraceRow],
openings: &[AxisOpening],
) -> Result<Vec<(CCSInstance, CCSWitness)>, CommitError> {
let mut steps = Vec::new();
let mut opening_idx = 0;
for row in trace {
if row.r()[0] == 0 {
let ao = openings.get(opening_idx).ok_or(CommitError::TraceOverflow)?;
steps.extend(verifier_steps(&ao.commitment, &ao.point, ao.value, &ao.opening));
opening_idx += 1;
}
}
Ok(steps)
}
pub fn build_look_steps_from_trace(
trace: &[TraceRow],
openings: &[LookOpening],
) -> Result<Vec<(CCSInstance, CCSWitness)>, CommitError> {
let mut steps = Vec::new();
let mut opening_idx = 0;
for row in trace {
if row.r()[0] == 17 {
let lo = openings.get(opening_idx).ok_or(CommitError::TraceOverflow)?;
steps.extend(verifier_steps(&lo.commitment, &lo.point, lo.value, &lo.opening));
steps.push(eq_step(lo.bbg_root[0], Goldilocks::new(row.r()[4])));
steps.push(eq_step(lo.bbg_root[1], Goldilocks::new(row.r()[11])));
steps.push(eq_step(lo.bbg_root[2], Goldilocks::new(row.r()[12])));
steps.push(eq_step(lo.bbg_root[3], Goldilocks::new(row.r()[13])));
opening_idx += 1;
}
}
Ok(steps)
}
pub fn build_axis_transcript_steps(
trace: &[TraceRow],
openings: &[AxisOpening],
) -> Result<Vec<(CCSInstance, CCSWitness)>, CommitError> {
let mut steps = Vec::new();
let mut opening_idx = 0;
for row in trace {
if row.r()[0] == 0 {
let ao = openings.get(opening_idx).ok_or(CommitError::TraceOverflow)?;
steps.extend(build_transcript_steps(
&ao.transcript_seed, &ao.commitment, &ao.opening,
));
opening_idx += 1;
}
}
Ok(steps)
}
pub fn build_look_transcript_steps(
trace: &[TraceRow],
openings: &[LookOpening],
) -> Result<Vec<(CCSInstance, CCSWitness)>, CommitError> {
let mut steps = Vec::new();
let mut opening_idx = 0;
for row in trace {
if row.r()[0] == 17 {
let lo = openings.get(opening_idx).ok_or(CommitError::TraceOverflow)?;
steps.extend(build_transcript_steps(
&lo.transcript_seed, &lo.commitment, &lo.opening,
));
opening_idx += 1;
}
}
Ok(steps)
}
pub fn axis_eval_point(addr: u64) -> Vec<Goldilocks> {
if addr <= 1 {
return vec![];
}
let bits = 63 - addr.leading_zeros();
(0..bits)
.map(|i| {
if (addr >> i) & 1 == 1 { Goldilocks::ONE } else { Goldilocks::ZERO }
})
.collect()
}
pub fn look_openings_from_provider(
provider: &nox::BrakedownLookProvider,
) -> Vec<LookOpening> {
use lens::brakedown::Brakedown;
use lens::{Lens, Transcript as LensTranscript};
let poly = provider.poly();
let commitment = *provider.commitment();
let nox_openings = provider.drain_openings();
let k = poly.evals.len().trailing_zeros() as usize;
let cb = commitment.as_bytes();
let bbg_root = [
look_commitment_limb(cb, 0),
look_commitment_limb(cb, 1),
look_commitment_limb(cb, 2),
look_commitment_limb(cb, 3),
];
nox_openings.into_iter().map(|nox_op| {
let idx = nox_op.key.as_u64() as usize;
let point: Vec<Goldilocks> = (0..k)
.map(|j| {
if (idx >> j) & 1 == 1 { Goldilocks::ONE } else { Goldilocks::ZERO }
})
.collect();
let seed = {
let mut s = [0u8; 32];
s[..8].copy_from_slice(&nox_op.key.as_u64().to_le_bytes());
s[8..16].copy_from_slice(&nox_op.namespace.as_u64().to_le_bytes());
s[16..].copy_from_slice(&cb[0..16]);
s
};
let mut lt = LensTranscript::new(&seed);
let opening = Brakedown::open(poly, &point, &mut lt);
LookOpening {
commitment,
point,
value: nox_op.value,
opening,
transcript_seed: seed.to_vec(),
bbg_root,
namespace: nox_op.namespace,
a_commit: None,
}
}).collect()
}
fn look_commitment_limb(bytes: &[u8], k: usize) -> Goldilocks {
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[k * 8..k * 8 + 8]);
Goldilocks::new(u64::from_le_bytes(buf))
}
#[cfg(test)]
mod tests {
use super::*;
use nox::TraceRow;
use lens::brakedown::{Brakedown, MultilinearPoly};
use lens::{Lens, Transcript as LensTranscript};
#[test]
fn witness_has_correct_length() {
let t = TraceRow::default();
let t1 = TraceRow::default();
let w = witness_from_rows(&t, &t1);
assert_eq!(w.z.len(), Z_LEN);
assert_eq!(w.z[Z_LEN - 1], Goldilocks::ONE);
}
#[test]
fn build_from_two_row_trace_gives_one_step() {
let rows = vec![TraceRow::default(), TraceRow::default()];
let steps = build_ccs_from_trace(&rows);
assert_eq!(steps.len(), 1);
}
#[test]
fn axis_eval_point_addr_2_is_one_bit() {
let pt = axis_eval_point(2);
assert_eq!(pt.len(), 1);
assert_eq!(pt[0], Goldilocks::ZERO); }
#[test]
fn axis_eval_point_addr_3_is_one_bit() {
let pt = axis_eval_point(3);
assert_eq!(pt.len(), 1);
assert_eq!(pt[0], Goldilocks::ONE);
}
#[test]
fn axis_eval_point_addr_5_is_two_bits() {
let pt = axis_eval_point(5);
assert_eq!(pt.len(), 2);
assert_eq!(pt[0], Goldilocks::ONE); assert_eq!(pt[1], Goldilocks::ZERO); }
#[test]
fn axis_eval_point_addr_0_or_1_empty() {
assert!(axis_eval_point(0).is_empty());
assert!(axis_eval_point(1).is_empty());
}
#[test]
fn build_axis_steps_from_trace_empty_trace() {
let steps = build_axis_steps_from_trace(&[], &[]).unwrap();
assert!(steps.is_empty());
}
#[test]
fn build_axis_steps_produces_verifier_steps_for_each_axis_row() {
let poly = MultilinearPoly::new(
[1u64, 2, 3, 4].iter().map(|&v| Goldilocks::new(v)).collect()
);
let commitment = Brakedown::commit(&poly);
let point = vec![Goldilocks::ZERO, Goldilocks::ZERO];
let value = poly.evaluate(&point);
let opening = {
let mut lt = LensTranscript::new(b"axis-test");
Brakedown::open(&poly, &point, &mut lt)
};
let _trace = vec![TraceRow::default(), TraceRow::default()];
let ao = AxisOpening {
commitment, point, value, opening,
transcript_seed: b"axis-test".to_vec(),
};
let openings = [ao];
let trace_one = vec![TraceRow::default()];
let steps = build_axis_steps_from_trace(&trace_one, &openings).unwrap();
assert_eq!(steps.len(), 5);
}
#[test]
fn look_openings_from_provider_correct_value_and_count() {
use nox::{BrakedownLookProvider, LookProvider};
use lens::brakedown::MultilinearPoly;
use crate::ccs::selector::is_satisfied;
let evals: Vec<Goldilocks> = [10u64, 20, 30, 40].iter().map(|&v| Goldilocks::new(v)).collect();
let provider = BrakedownLookProvider::new(MultilinearPoly::new(evals));
let _ = provider.look(provider.commitment_field(), Goldilocks::ZERO, Goldilocks::new(0));
let _ = provider.look(provider.commitment_field(), Goldilocks::ZERO, Goldilocks::new(2));
let openings = look_openings_from_provider(&provider);
assert_eq!(openings.len(), 2);
assert_eq!(openings[0].value, Goldilocks::new(10));
assert_eq!(openings[0].point, vec![Goldilocks::ZERO, Goldilocks::ZERO]);
assert_eq!(openings[1].value, Goldilocks::new(30));
assert_eq!(openings[1].point, vec![Goldilocks::ZERO, Goldilocks::ONE]);
assert_eq!(openings[0].bbg_root[0], provider.commitment_field());
for lo in &openings {
let steps = verifier_steps(&lo.commitment, &lo.point, lo.value, &lo.opening);
for (i, (inst, wit)) in steps.iter().enumerate() {
assert!(is_satisfied(inst, wit), "opening step {i} not satisfied");
}
}
assert!(look_openings_from_provider(&provider).is_empty());
}
}