use lens::PcsError;
use lens::rspcs::wire::{Reader, Writer};
use nebu::{Fp3, Goldilocks};
use super::acc::AccProof;
use super::decide::Decider;
use super::ivc::IvcProof;
use super::ivc::Key;
use super::params::{BV, Params};
use super::perm;
use super::relation::{COLS, PUB_NOX, PUB_V};
use super::state::{State, StateV};
use super::step::StepProof;
use super::word::{Arity, Digest, LeafOpening};
use crate::machine::layout::{W1, W2};
type R<T> = Result<T, PcsError>;
pub(crate) fn digest(w: &mut Writer, d: &Digest) {
for &x in d {
w.base(x);
}
}
pub(crate) fn read_digest(r: &mut Reader<'_>) -> R<Digest> {
Ok([r.base()?, r.base()?, r.base()?, r.base()?])
}
pub(crate) fn exts(w: &mut Writer, xs: &[Fp3]) {
for &x in xs {
w.ext(x);
}
}
pub(crate) fn read_exts(r: &mut Reader<'_>, k: usize) -> R<Vec<Fp3>> {
(0..k).map(|_| r.ext()).collect()
}
pub(crate) fn bases(w: &mut Writer, xs: &[Goldilocks]) {
for &x in xs {
w.base(x);
}
}
pub(crate) fn read_bases(r: &mut Reader<'_>, k: usize) -> R<Vec<Goldilocks>> {
(0..k).map(|_| r.base()).collect()
}
fn state(w: &mut Writer, s: &State) {
for (v, ext) in s.items() {
if ext {
w.ext(v);
} else {
w.base(v.c0);
}
}
}
fn read_state(r: &mut Reader<'_>, p: &Params) -> R<State> {
let template = super::program::dummy_state(p);
let mut vals = Vec::new();
for (_, ext) in template.items() {
vals.push(if ext {
r.ext()?
} else {
Fp3::from_base(r.base()?)
});
}
Ok(StateV::from_items(&p.dims, &vals))
}
pub(crate) fn multi_siblings(
paths: &[(usize, &LeafOpening)],
log_leaves: usize,
arity: Arity,
) -> Vec<Digest> {
let mut out = Vec::new();
let mut known: Vec<usize> = paths.iter().map(|p| p.0).collect();
let (mut off, mut div) = (0, 1usize);
for a in arity.levels(log_leaves) {
let mut next = Vec::with_capacity(known.len());
let mut i = 0;
while i < known.len() {
let base = known[i] / a * a;
let mut present = Vec::new();
while i < known.len() && known[i] / a * a == base {
present.push(known[i]);
i += 1;
}
let own = present[0];
let path = paths.iter().find(|p| p.0 / div == own).expect("a path").1;
for c in base..base + a {
if !present.contains(&c) {
let rank = c - base - usize::from(c > own);
out.push(path.path[off + rank]);
}
}
next.push(base / a);
}
known = next;
off += a - 1;
div *= a;
}
out
}
fn acc_proof(w: &mut Writer, a: &AccProof, p: &Params) {
exts(w, &a.sumcheck);
exts(w, &a.evals);
w.u64(a.comb_nonce);
digest(w, &a.root);
exts(w, &a.ood);
w.u64(a.query_nonce);
let leaves: Vec<usize> = a.openings.iter().map(|q| leaf_index(&q[0], p)).collect();
for &l in &leaves {
w.u32(l);
}
let mut distinct = leaves.clone();
distinct.sort_unstable();
distinct.dedup();
let depth = p.cfg.layout.log_leaves() as usize;
for word in 0..4 {
let ops: Vec<(usize, &LeafOpening)> = distinct
.iter()
.map(|&l| {
(
l,
&a.openings[leaves.iter().position(|&x| x == l).expect("leaf")][word],
)
})
.collect();
for (_, op) in &ops {
if word == 0 {
exts(w, &op.symbols);
} else {
bases(w, &op.symbols.iter().map(|s| s.c0).collect::<Vec<_>>());
}
}
let sib = multi_siblings(&ops, depth, p.arity);
w.u32(sib.len());
for d in &sib {
digest(w, d);
}
}
}
fn leaf_index(op: &LeafOpening, _p: &Params) -> usize {
op.leaf.expect("an opening knows its leaf")
}
fn read_acc(r: &mut Reader<'_>, p: &Params) -> R<AccProof> {
let sumcheck = read_exts(r, 2 * p.vars)?;
let evals = read_exts(r, 4)?;
let comb_nonce = r.u64()?;
let root = read_digest(r)?;
let ood = read_exts(r, p.cfg.ood)?;
let query_nonce = r.u64()?;
let depth = p.cfg.layout.log_leaves() as usize;
let count = 1usize << depth;
let width = 1usize << p.cfg.layout.log_width;
let leaves: Vec<usize> = (0..p.cfg.queries).map(|_| r.u32()).collect::<R<_>>()?;
if leaves.iter().any(|&l| l >= count) {
return Err(PcsError::Malformed);
}
let mut distinct = leaves.clone();
distinct.sort_unstable();
distinct.dedup();
let mut per_word: Vec<Vec<LeafOpening>> = Vec::with_capacity(4);
for word in 0..4 {
let syms: Vec<Vec<Fp3>> = distinct
.iter()
.map(|_| {
if word == 0 {
read_exts(r, width)
} else {
Ok(read_bases(r, width)?
.into_iter()
.map(Fp3::from_base)
.collect())
}
})
.collect::<R<_>>()?;
let ns = r.count(32)?;
let sib: Vec<Digest> = (0..ns).map(|_| read_digest(r)).collect::<R<_>>()?;
per_word.push(expand(&distinct, syms, &sib, depth, word == 0, p.arity)?);
}
let openings = leaves
.iter()
.map(|l| {
let q = distinct.binary_search(l).expect("leaf");
(0..4).map(|wd| per_word[wd][q].clone()).collect()
})
.collect();
Ok(AccProof {
sumcheck,
evals,
comb_nonce,
root,
ood,
query_nonce,
openings,
})
}
struct Group {
kids: Vec<Result<usize, usize>>,
}
pub(crate) fn expand(
leaves: &[usize],
syms: Vec<Vec<Fp3>>,
sib: &[Digest],
log_leaves: usize,
ext: bool,
arity: Arity,
) -> R<Vec<LeafOpening>> {
let width = syms.first().map_or(0, |s| s.len());
if syms.iter().any(|s| s.len() != width) {
return Err(PcsError::Malformed);
}
let fans = arity.levels(log_leaves);
let mut levels: Vec<Vec<Group>> = Vec::with_capacity(fans.len());
let mut parent: Vec<Vec<usize>> = Vec::with_capacity(fans.len());
let mut pos: Vec<usize> = leaves.to_vec();
let mut used = 0usize;
for &a in &fans {
let mut groups = Vec::new();
let mut par = vec![0usize; pos.len()];
let mut next = Vec::new();
let mut i = 0;
while i < pos.len() {
let base = pos[i] / a * a;
let mut kids: Vec<Result<usize, usize>> = Vec::with_capacity(a);
for c in base..base + a {
if i < pos.len() && pos[i] == c {
par[i] = groups.len();
kids.push(Ok(i));
i += 1;
} else {
if used >= sib.len() {
return Err(PcsError::Merkle);
}
kids.push(Err(used));
used += 1;
}
}
groups.push(Group { kids });
next.push(base / a);
}
levels.push(groups);
parent.push(par);
pos = next;
}
if used != sib.len() {
return Err(PcsError::Merkle);
}
let input = |kids: &[Digest]| -> [Goldilocks; 16] {
if kids.len() == 4 {
perm::node4_input([kids[0], kids[1], kids[2], kids[3]])
} else {
perm::node_input(kids[0], kids[1])
}
};
let solo = |g: &Group| g.kids.iter().filter(|k| k.is_ok()).count() == 1;
let ld = super::word::hash_leaves_with(syms.len(), width, ext, |j, t| syms[j][t], true);
let mut dig: Vec<Vec<Option<Digest>>> = Vec::with_capacity(fans.len() + 1);
dig.push(ld.into_iter().map(Some).collect());
for g in &levels {
dig.push(vec![None; g.len()]);
}
let chains = leaves.len();
let threads = crate::recursion::wrap::verifier_threads()
.min(chains.div_ceil(3))
.max(1);
let done: Vec<Vec<(usize, usize, Digest)>> =
crate::recursion::pool::map_chunks(chains, threads, |r| {
let mut out = Vec::new();
let mut cur: Vec<(usize, Digest)> =
r.map(|q| (q, dig[0][q].expect("leaf digest"))).collect();
for (l, groups) in levels.iter().enumerate() {
let mut ins = Vec::new();
let mut gs = Vec::new();
for &(k, d) in &cur {
let g = parent[l][k];
if !solo(&groups[g]) {
continue;
}
let kids: Vec<Digest> = groups[g]
.kids
.iter()
.map(|c| match *c {
Ok(_) => d,
Err(i) => sib[i],
})
.collect();
ins.push(input(&kids));
gs.push(g);
}
if ins.is_empty() {
break;
}
perm::permute_many_remember(&mut ins);
cur = gs
.iter()
.zip(&ins)
.map(|(&g, s)| (g, perm::head(s)))
.collect();
for &(g, d) in &cur {
out.push((l + 1, g, d));
}
}
out
});
for (l, g, d) in done.into_iter().flatten() {
dig[l][g] = Some(d);
}
for (l, groups) in levels.iter().enumerate() {
let todo: Vec<usize> = (0..groups.len())
.filter(|&g| dig[l + 1][g].is_none())
.collect();
if todo.is_empty() {
continue;
}
let mut ins: Vec<[Goldilocks; 16]> = Vec::with_capacity(todo.len());
for &g in &todo {
let kids: Vec<Digest> = groups[g]
.kids
.iter()
.map(|c| match *c {
Ok(k) => dig[l][k].expect("a computed child"),
Err(i) => sib[i],
})
.collect();
ins.push(input(&kids));
}
perm::permute_many_remember(&mut ins);
for (&g, s) in todo.iter().zip(&ins) {
dig[l + 1][g] = Some(perm::head(s));
}
}
let mut paths: Vec<Vec<Digest>> = vec![Vec::with_capacity(arity.path_len(log_leaves)); chains];
let mut anc: Vec<usize> = (0..chains).collect();
for (l, groups) in levels.iter().enumerate() {
for (q, a) in anc.iter_mut().enumerate() {
let g = parent[l][*a];
for c in &groups[g].kids {
match *c {
Ok(k) if k == *a => {}
Ok(k) => paths[q].push(dig[l][k].expect("a computed node")),
Err(i) => paths[q].push(sib[i]),
}
}
*a = g;
}
}
Ok(syms
.into_iter()
.zip(paths)
.zip(leaves)
.map(|((symbols, path), &l)| LeafOpening {
symbols,
path,
leaf: Some(l),
})
.collect())
}
fn step(w: &mut Writer, s: &StepProof, p: &Params) {
for d in &s.roots {
digest(w, d);
}
for a in &s.ood {
exts(w, a);
}
bases(w, &s.b_in);
bases(w, &s.b_out);
bases(w, &s.b_v);
for m in &s.zerocheck {
exts(w, m);
}
for v in [
&s.local, &s.next, &s.pub_nox, &s.pub_v, &s.fold_g, &s.fold_pn, &s.fold_pv, &s.shift,
] {
exts(w, v);
}
exts(w, &s.vals);
acc_proof(w, &s.acc, p);
}
fn read_step(r: &mut Reader<'_>, p: &Params) -> R<StepProof> {
let roots = [read_digest(r)?, read_digest(r)?, read_digest(r)?];
let ood = [
read_exts(r, p.fresh)?,
read_exts(r, p.fresh)?,
read_exts(r, p.fresh)?,
];
let b_in = read_bases(r, W1 + W2)?;
let b_out = read_bases(r, W1 + W2)?;
let b_v = read_bases(r, BV)?;
let zerocheck = (0..p.n)
.map(|_| read_exts(r, p.zc_deg()))
.collect::<R<_>>()?;
let local = read_exts(r, COLS)?;
let next = read_exts(r, COLS)?;
let pub_nox = read_exts(r, PUB_NOX)?;
let pub_v = read_exts(r, PUB_V)?;
let fold_g = read_exts(r, p.g_deg - 1)?;
let fold_pn = read_exts(r, p.pn_deg - 1)?;
let fold_pv = read_exts(r, p.pv_deg - 1)?;
let shift = read_exts(r, 2 * p.n)?;
let v = read_exts(r, 3)?;
let acc = read_acc(r, p)?;
Ok(StepProof {
roots,
ood,
b_in,
b_out,
b_v,
zerocheck,
local,
next,
pub_nox,
pub_v,
fold_g,
fold_pn,
fold_pv,
shift,
vals: [v[0], v[1], v[2]],
acc,
})
}
fn decider_ext(k: &Key) -> [bool; 2] {
[true, k.key_ext]
}
pub(crate) fn decider(w: &mut Writer, d: &Decider, k: &Key) {
exts(w, &d.key);
super::whir::wire::write(w, &k.dcfg, &decider_ext(k), &d.whir);
}
pub(crate) fn read_decider(r: &mut Reader<'_>, k: &Key) -> R<Decider> {
let v = read_exts(r, 2)?;
let whir = super::whir::wire::read(r, &k.dcfg, &decider_ext(k))?;
Ok(Decider {
key: [v[0], v[1]],
whir,
})
}
impl IvcProof {
pub fn sizes(&self, k: &Key) -> [(&'static str, usize); 5] {
let p = &k.params;
let len = |f: &dyn Fn(&mut Writer)| {
let mut w = Writer::default();
f(&mut w);
w.buf.len()
};
let all = len(&|w| step(w, &self.step, p));
let acc = len(&|w| acc_proof(w, &self.step.acc, p));
[
("header", 1 + 8 + 8 + 32),
("state", len(&|w| state(w, &self.state))),
("step", all - acc),
("accumulation", acc),
("decider", len(&|w| decider(w, &self.decider, k))),
]
}
pub fn to_bytes(&self, k: &Key) -> Vec<u8> {
let p = &k.params;
let mut w = Writer::default();
w.u8(self.log_rows as u8);
w.u64(self.start);
w.u64(self.segments);
digest(&mut w, &self.chain);
state(&mut w, &self.state);
step(&mut w, &self.step, p);
decider(&mut w, &self.decider, k);
w.buf
}
pub fn from_bytes(
bytes: &[u8],
params: impl Fn(u32) -> Result<std::sync::Arc<super::ivc::Key>, String>,
) -> Result<Self, String> {
let e = |e: PcsError| format!("recursive proof: {e}");
let mut r = Reader::new(bytes);
let log_rows = u32::from(r.u8().map_err(e)?);
let key = params(log_rows)?;
let p = &key.params;
let start = r.u64().map_err(e)?;
let segments = r.u64().map_err(e)?;
let chain = read_digest(&mut r).map_err(e)?;
let st = read_state(&mut r, p).map_err(e)?;
let stp = read_step(&mut r, p).map_err(e)?;
let decider = read_decider(&mut r, &key).map_err(e)?;
r.finish().map_err(e)?;
Ok(Self {
log_rows,
start,
segments,
chain,
state: st,
step: stp,
decider,
})
}
}