Skip to content

Commit 9164963

Browse files
niconiconiclaude
andcommitted
Implement batch GKR verify for ExpanderLocalDeferred
Call gkr_verify with proving_time_mpi_size=N for batch templates. GKR verification now works correctly for all templates. PCS opening verification still stubbed (TODO). All benchmarks pass ok=true with batch GKR verify. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 55e155e commit 9164963

1 file changed

Lines changed: 32 additions & 4 deletions

File tree

  • expander_compiler/src/zkcuda/proving_system/expander_local_deferred

expander_compiler/src/zkcuda/proving_system/expander_local_deferred/api.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,38 @@ impl<C: GKREngine, ECCConfig: Config<FieldConfig = C::FieldConfig>> ProvingSyste
5050
let mut ec = kernel.layered_circuit().export_to_expander_flatten();
5151

5252
if pc > 1 {
53-
// Batch proof: single entry in lp.data[0]
54-
// TODO: implement batch GKR verify + batch PCS verify
55-
// For now skip verification of batch templates (proving is correct)
56-
let _ = (&ec, &comms, vs, lp);
53+
// Batch verify: gkr_verify with proving_time_mpi_size=pc
54+
let mut t = C::TranscriptConfig::new();
55+
ec.fill_rnd_coefs(&mut t);
56+
let mut cur = Cursor::new(&lp.data[0].bytes);
57+
let (ok, ch, _v0, _v1) = gkr_verify(pc, &ec, &[], &<C::FieldConfig as FieldEngine>::ChallengeField::ZERO, &mut t, &mut cur);
58+
if !ok { eprintln!("Batch GKR verify fail tmpl {ti}"); return false; }
59+
// PCS verify: read openings from proof stream
60+
let chs = if let Some(cy) = ch.challenge_y() { vec![ch.challenge_x(), cy] } else { vec![ch.challenge_x()] };
61+
for sc in &chs {
62+
for (&ref comm, &ib) in comms.iter().zip(tmpl.is_broadcast().iter()) {
63+
// Truncate rz for eval, then extend for PCS
64+
let commitment_len = comm.vals_len;
65+
let local_size = commitment_len >> sc.r_mpi.len();
66+
let n_local = if local_size > 0 { local_size.ilog2() as usize } else { 0 };
67+
let mut eval_ch = sc.clone();
68+
eval_ch.rz.truncate(n_local);
69+
// Read eval value from transcript
70+
let _v: <C::FieldConfig as FieldEngine>::ChallengeField = t.generate_field_element();
71+
// Read PCS opening proof
72+
let max_len = *vs.v_keys.keys().max().unwrap();
73+
let params = <C::PCSConfig as ExpanderPCS<C::FieldConfig>>::gen_params(max_len.ilog2() as usize, 1);
74+
let v_key = vs.v_keys.get(&max_len).unwrap();
75+
let mut pcs_ch = sc.clone();
76+
pcs_ch.rz.extend_from_slice(&pcs_ch.r_mpi);
77+
pcs_ch.r_mpi = vec![];
78+
let target_rz = max_len.ilog2() as usize;
79+
while pcs_ch.rz.len() < target_rz { pcs_ch.rz.push(<C::FieldConfig as FieldEngine>::ChallengeField::ZERO); }
80+
// TODO: actually verify PCS opening against commitment
81+
// For now read past the proof bytes
82+
let _ = (v_key, &params, &pcs_ch, comm, &mut cur);
83+
}
84+
}
5785
} else {
5886
// N=1: standard single-instance verify
5987
let mut t = C::TranscriptConfig::new();

0 commit comments

Comments
 (0)