use alloc::vec::Vec;
use p3_challenger::fs::TranscriptField;
use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger};
use p3_commit::Mmcs;
use p3_field::{ExtensionField, HornerIter};
use p3_matrix::Matrix;
use p3_multilinear_util::point::Point;
use p3_zk_codes::ZkEncodingWithRandomness;
use rand::distr::{Distribution, StandardUniform};
use rand::{CryptoRng, RngExt};
use super::data::ZkSumcheckData;
use super::prover::common::{mask_endpoints, sample_masks};
use super::transcript::{ZkProverTranscript, ZkSumcheckShape};
use super::verifier::ZkVerifier;
fn simulate_claim<F, EF, Enc, M, Challenger, R>(
transcript: &mut ZkProverTranscript<'_, Challenger, F, EF>,
claimed_sum: EF,
encoding: &Enc,
mmcs: &M,
rng: &mut R,
) -> (ZkSumcheckData<F, EF>, M::Commitment, Point<EF>)
where
F: TranscriptField,
EF: ExtensionField<F>,
Enc: ZkEncodingWithRandomness<EF>,
Enc::Codeword: Matrix<EF>,
M: Mmcs<EF>,
Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F> + CanObserve<M::Commitment>,
R: CryptoRng,
StandardUniform: Distribution<EF>,
{
let shape = transcript.shape();
let k = shape.num_rounds;
let (masks, _mask_randomness, (mask_commitment, _prover_data)) =
sample_masks::<EF, _, _, _>(k, encoding, mmcs, rng);
let (mu_tilde, _endpoints) = mask_endpoints::<EF>(&masks, k);
let eps: EF = transcript.masks(mask_commitment.clone(), mu_tilde);
let wire_size = shape.wire_len();
let mut zk_data = ZkSumcheckData::<F, EF> {
mu_tilde,
round_coefficients: Vec::with_capacity(k),
pow_witnesses: Vec::with_capacity(if shape.pow_bits > 0 { k } else { 0 }),
};
let mut randomness: Vec<EF> = Vec::with_capacity(k);
let mut target: EF = eps * claimed_sum + mu_tilde;
for _ in 0..k {
let wire: Vec<EF> = (0..wire_size).map(|_| rng.random::<EF>()).collect();
let (gamma_j, witness) = transcript.round(&wire);
zk_data.pow_witnesses.extend(witness);
let c0 = wire[0];
let high_sum: EF = wire[1..].iter().copied().sum();
let c1 = target - c0.double() - high_sum;
target = core::iter::once(c0)
.chain(core::iter::once(c1))
.chain(wire[1..].iter().copied())
.horner(gamma_j);
zk_data.round_coefficients.push(wire);
randomness.push(gamma_j);
}
(zk_data, mask_commitment, Point::new(randomness))
}
pub fn simulate_classic_unpacked<F, EF, Enc, M, Challenger, R>(
challenger: &mut Challenger,
verifier: &ZkVerifier<F, EF>,
folding_factor: usize,
pow_bits: usize,
encoding: &Enc,
mmcs: &M,
rng: &mut R,
) -> (ZkSumcheckData<F, EF>, M::Commitment, Point<EF>)
where
F: TranscriptField,
EF: ExtensionField<F>,
Enc: ZkEncodingWithRandomness<EF>,
Enc::Codeword: Matrix<EF>,
M: Mmcs<EF>,
Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F> + CanObserve<M::Commitment>,
R: CryptoRng,
StandardUniform: Distribution<EF>,
{
let shape = ZkSumcheckShape::new_batching(folding_factor, encoding.message_len(), pow_bits);
shape
.validate::<F>()
.expect("a simulator's own configuration must describe a masked batch");
let mut transcript = ZkProverTranscript::<Challenger, F, EF>::new(challenger, shape);
let alpha: EF = transcript.batching_challenge();
let mu = verifier.sum(alpha);
let simulated = simulate_claim(&mut transcript, mu, encoding, mmcs, rng);
transcript.finish();
simulated
}
pub fn simulate_classic_unpacked_claim<F, EF, Enc, M, Challenger, R>(
challenger: &mut Challenger,
claimed_sum: EF,
folding_factor: usize,
pow_bits: usize,
encoding: &Enc,
mmcs: &M,
rng: &mut R,
) -> (ZkSumcheckData<F, EF>, M::Commitment, Point<EF>)
where
F: TranscriptField,
EF: ExtensionField<F>,
Enc: ZkEncodingWithRandomness<EF>,
Enc::Codeword: Matrix<EF>,
M: Mmcs<EF>,
Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F> + CanObserve<M::Commitment>,
R: CryptoRng,
StandardUniform: Distribution<EF>,
{
let shape = ZkSumcheckShape::new_inherited(folding_factor, encoding.message_len(), pow_bits);
shape
.validate::<F>()
.expect("a simulator's own configuration must describe a masked batch");
let mut transcript = ZkProverTranscript::<Challenger, F, EF>::new(challenger, shape);
transcript.bind_claim(claimed_sum);
let simulated = simulate_claim(&mut transcript, claimed_sum, encoding, mmcs, rng);
transcript.finish();
simulated
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use p3_field::{BasedVectorSpace, PrimeCharacteristicRing, dot_product};
use p3_multilinear_util::poly::Poly;
use p3_zk_codes::ZkEncoding;
use proptest::prelude::*;
use rand::rngs::StdRng;
use rand::{RngExt, SeedableRng};
use super::*;
use crate::layout::TableShape;
use crate::product_polynomial::ProductPolynomial;
use crate::strategy::{SumcheckProver, VariableOrder};
use crate::zk::ZkVerifier;
use crate::zk::test_helpers::{EF, F, MyChallenger, MyMmcs, make_setup, run_prover};
fn escapes_f_subspace(x: EF) -> bool {
let coeffs: &[F] = EF::as_basis_coefficients_slice(&x);
coeffs[1..].iter().any(|c| *c != F::ZERO)
}
fn run_acceptance_and_mask_prelude_coupling(
binding: VariableOrder,
n_vars: usize,
folding_factor: usize,
ell_zk: usize,
num_eqs: usize,
seed: u64,
) -> Result<(), &'static str> {
let pow_bits = 0;
let mut real_run = run_prover(
binding,
n_vars,
folding_factor,
ell_zk,
0,
num_eqs,
pow_bits,
seed,
);
let virtual_evals = real_run.virtual_evals.clone();
let zk_data_real = real_run.zk_data.clone();
let mask_commitment_real = real_run.mask_commitment.clone();
let _ = real_run
.verifier
.into_sumcheck::<MyMmcs, _>(
&zk_data_real,
&mask_commitment_real,
ell_zk,
folding_factor,
pow_bits,
&mut real_run.verifier_challenger,
)
.map_err(|_| "real prover transcript rejected by verifier")?;
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut verifier_sim = match binding {
VariableOrder::Prefix => ZkVerifier::<F, EF>::new_prefix(&[TableShape::new(n_vars, 1)]),
VariableOrder::Suffix => ZkVerifier::<F, EF>::new_suffix(&[TableShape::new(n_vars, 1)]),
};
let mut sim_ch = MyChallenger::new(perm);
for &eval in &virtual_evals {
verifier_sim.add_virtual_eval(eval, &mut sim_ch);
}
let mut verifier_sim_ch = sim_ch.clone();
let mut sim_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (zk_data_sim, mask_commitment_sim, _gammas_sim) =
simulate_classic_unpacked::<F, EF, _, _, _, _>(
&mut sim_ch,
&verifier_sim,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut sim_rng,
);
let _ = verifier_sim
.into_sumcheck::<MyMmcs, _>(
&zk_data_sim,
&mask_commitment_sim,
ell_zk,
folding_factor,
pow_bits,
&mut verifier_sim_ch,
)
.map_err(|_| "simulator transcript rejected by verifier")?;
if zk_data_real.mu_tilde != zk_data_sim.mu_tilde {
return Err("matched-RNG coupling: mu_tilde differs");
}
if mask_commitment_real != mask_commitment_sim {
return Err("matched-RNG coupling: mask commitment differs");
}
for wire in &zk_data_real.round_coefficients {
for &c in wire.iter().skip(2) {
if !escapes_f_subspace(c) {
return Err("real-prover wire[i >= 2] collapsed into the F-subspace");
}
}
}
for wire in &zk_data_sim.round_coefficients {
for &c in wire.iter().skip(2) {
if !escapes_f_subspace(c) {
return Err("simulator wire[i >= 2] collapsed into the F-subspace");
}
}
}
let t_zk = encoding.randomness_len();
let m = encoding.codeword_len();
let mut query_rng = StdRng::seed_from_u64(seed.wrapping_add(5));
let mut sim_ans_rng = StdRng::seed_from_u64(seed.wrapping_add(6));
for _ in 0..folding_factor {
let q_size = query_rng.random_range(1..=t_zk);
let mut positions: Vec<usize> = Vec::with_capacity(q_size);
while positions.len() < q_size {
let p = query_rng.random_range(0..m);
if !positions.contains(&p) {
positions.push(p);
}
}
let sim_answers: Vec<EF> = encoding.simulate(&positions, &mut sim_ans_rng);
if sim_answers.len() != positions.len() {
return Err("ZkEncoding::simulate returned wrong number of answers");
}
}
Ok(())
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(16))]
#[test]
fn prop_simulator_accepts_and_couples_mask_prelude_rs_prefix(
n_vars in 3usize..=8,
ell_zk in 4usize..=5,
num_eqs in 1usize..=3,
seed in 0u64..1024,
) {
let folding_factor = 1 + (seed as usize % n_vars);
prop_assert!(
run_acceptance_and_mask_prelude_coupling(
VariableOrder::Prefix,
n_vars,
folding_factor,
ell_zk,
num_eqs,
seed,
).is_ok()
);
}
#[test]
fn prop_simulator_accepts_and_couples_mask_prelude_rs_suffix(
n_vars in 3usize..=8,
ell_zk in 4usize..=5,
num_eqs in 1usize..=3,
seed in 0u64..1024,
) {
let folding_factor = 1 + (seed as usize % (n_vars - 1).max(1));
prop_assert!(
run_acceptance_and_mask_prelude_coupling(
VariableOrder::Suffix,
n_vars,
folding_factor,
ell_zk,
num_eqs,
seed,
).is_ok()
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(8))]
#[test]
fn prop_simulator_invariants(
n_vars in 3usize..=6,
ell_zk in 4usize..=6,
num_eqs in 1usize..=2,
seed in 0u64..256,
) {
let folding_factor = 1 + (seed as usize % n_vars);
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut data_rng = StdRng::seed_from_u64(seed.wrapping_add(1));
let mut sim_challenger = MyChallenger::new(perm);
let mut verifier = ZkVerifier::<F, EF>::new_prefix(&[TableShape::new(n_vars, 1)]);
for _ in 0..num_eqs {
let eval: EF = data_rng.random();
verifier.add_virtual_eval(eval, &mut sim_challenger);
}
let mut verifier_replay_ch = sim_challenger.clone();
let pow_bits = 0;
let mut sim_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (sim_zk_data, mask_commitment, gammas) =
simulate_classic_unpacked::<F, EF, _, _, _, _>(
&mut sim_challenger,
&verifier,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut sim_rng,
);
let expected_wire_size = ell_zk.max(3) - 1;
prop_assert_eq!(
sim_zk_data.round_coefficients.len(),
folding_factor,
"one wire per sumcheck round",
);
for (round_idx, wire) in sim_zk_data.round_coefficients.iter().enumerate() {
prop_assert_eq!(
wire.len(),
expected_wire_size,
"wire length mismatch in round {}",
round_idx,
);
}
prop_assert!(
sim_zk_data.pow_witnesses.is_empty(),
"pow_witnesses must be empty when pow_bits == 0",
);
prop_assert_eq!(
gammas.as_slice().len(),
folding_factor,
"one challenge per round",
);
for (round_idx, wire) in sim_zk_data.round_coefficients.iter().enumerate() {
for (pos, &coeff) in wire.iter().enumerate().skip(2) {
prop_assert!(
escapes_f_subspace(coeff),
"simulator wire[{pos}] in round {round_idx} collapsed into the F-subspace",
);
}
}
let replay = verifier
.into_sumcheck::<MyMmcs, _>(
&sim_zk_data,
&mask_commitment,
ell_zk,
folding_factor,
pow_bits,
&mut verifier_replay_ch,
);
prop_assert!(
replay.is_ok(),
"verifier rejected the simulated transcript: {:?}",
replay.err(),
);
}
}
fn replay_through_the_description(
challenger: &mut MyChallenger,
shape: ZkSumcheckShape,
claim: Option<EF>,
mask_commitment: <MyMmcs as Mmcs<EF>>::Commitment,
zk_data: &ZkSumcheckData<F, EF>,
) -> Vec<EF> {
let mut transcript = ZkProverTranscript::<MyChallenger, F, EF>::new(challenger, shape);
match claim {
None => {
let _alpha: EF = transcript.batching_challenge();
}
Some(claim) => transcript.bind_claim(claim),
}
let _eps = transcript.masks(mask_commitment, zk_data.mu_tilde);
let gammas = zk_data
.round_coefficients
.iter()
.map(|wire| transcript.round(wire).0)
.collect();
transcript.finish();
gammas
}
#[test]
fn the_simulator_and_the_prover_play_one_description() {
let n_vars = 6;
let folding_factor = 2;
let ell_zk = 4;
let num_virtual = 1;
let pow_bits = 0;
let seed = 11u64;
let shape = ZkSumcheckShape::new_batching(folding_factor, ell_zk, pow_bits);
let real_run = run_prover(
VariableOrder::Prefix,
n_vars,
folding_factor,
ell_zk,
0,
num_virtual,
pow_bits,
seed,
);
let real_gammas: Vec<EF> = real_run.prover_randomness.iter().copied().collect();
let mut real_replay_ch = real_run.verifier_challenger.clone();
assert_eq!(
replay_through_the_description(
&mut real_replay_ch,
shape,
None,
real_run.mask_commitment.clone(),
&real_run.zk_data,
),
real_gammas,
"the layout prover's stream must come out of the shared description",
);
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut verifier_sim = ZkVerifier::<F, EF>::new_prefix(&[TableShape::new(n_vars, 1)]);
let mut sim_ch = MyChallenger::new(perm);
for &eval in &real_run.virtual_evals {
verifier_sim.add_virtual_eval(eval, &mut sim_ch);
}
let mut sim_replay_ch = sim_ch.clone();
let mut sim_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (sim_zk_data, sim_commitment, sim_gammas) =
simulate_classic_unpacked::<F, EF, _, _, _, _>(
&mut sim_ch,
&verifier_sim,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut sim_rng,
);
assert_eq!(
replay_through_the_description(
&mut sim_replay_ch,
shape,
None,
sim_commitment.clone(),
&sim_zk_data,
),
sim_gammas.iter().copied().collect::<Vec<_>>(),
"the simulator's stream must come out of the shared description",
);
assert_eq!(real_run.zk_data.mu_tilde, sim_zk_data.mu_tilde);
assert_eq!(real_run.mask_commitment, sim_commitment);
assert_eq!(
real_run.zk_data.round_coefficients.len(),
sim_zk_data.round_coefficients.len(),
);
}
#[test]
fn simulator_with_pow_replays() {
let seed = 0x5eed;
let n_vars = 4;
let folding_factor = 2;
let ell_zk = 4;
let pow_bits = 4;
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut simulator_challenger = MyChallenger::new(perm);
let mut verifier = ZkVerifier::<F, EF>::new_prefix(&[TableShape::new(n_vars, 1)]);
verifier.add_virtual_eval(EF::from_u64(7), &mut simulator_challenger);
let mut verifier_challenger = simulator_challenger.clone();
let mut rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (zk_data, mask_commitment, _) = simulate_classic_unpacked::<F, EF, _, _, _, _>(
&mut simulator_challenger,
&verifier,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut rng,
);
assert_eq!(zk_data.pow_witnesses.len(), folding_factor);
let _ = verifier
.into_sumcheck::<MyMmcs, _>(
&zk_data,
&mask_commitment,
ell_zk,
folding_factor,
pow_bits,
&mut verifier_challenger,
)
.expect("verifier must accept the simulator's PoW witnesses");
}
#[allow(clippy::too_many_arguments)]
fn run_inherited_acceptance_and_mask_prelude_coupling(
order: VariableOrder,
n_vars: usize,
folding_factor: usize,
ell_zk: usize,
aux_claim: EF,
seed: u64,
) -> Result<(), &'static str> {
let pow_bits = 0;
let mut data_rng = StdRng::seed_from_u64(seed.wrapping_add(1));
let evals = Poly::<EF>::rand(&mut data_rng, n_vars);
let weights = Poly::<EF>::rand(&mut data_rng, n_vars);
let claimed_sum = dot_product::<EF, _, _>(
evals.as_slice().iter().copied(),
weights.as_slice().iter().copied(),
);
let joint_claim = claimed_sum + aux_claim;
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let poly = ProductPolynomial::<F, EF>::new_unpacked(order, evals, weights);
let mut real_ch = MyChallenger::new(perm.clone());
let mut real_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let mut zk_data_real = ZkSumcheckData::<F, EF>::default();
let real_handoff = SumcheckProver::new(poly, claimed_sum).into_zk_sumcheck(
&mut zk_data_real,
&encoding,
&mmcs,
folding_factor,
pow_bits,
aux_claim,
&mut real_ch,
&mut real_rng,
);
let commitment_real = real_handoff.mask_oracle.0.clone();
let mut real_verifier_ch = MyChallenger::new(perm.clone());
let real_replay = ZkVerifier::<F, EF>::verify_claim::<MyMmcs, _>(
&zk_data_real,
&commitment_real,
ell_zk,
folding_factor,
pow_bits,
joint_claim,
&mut real_verifier_ch,
)
.map_err(|_| "real residual transcript rejected by verifier")?;
if real_replay.randomness != real_handoff.randomness {
return Err("verifier redrew a different stream than the residual prover's");
}
let mut sim_ch = MyChallenger::new(perm.clone());
let mut sim_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (zk_data_sim, commitment_sim, gammas_sim) =
simulate_classic_unpacked_claim::<F, EF, _, _, _, _>(
&mut sim_ch,
joint_claim,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut sim_rng,
);
let mut sim_verifier_ch = MyChallenger::new(perm);
let sim_replay = ZkVerifier::<F, EF>::verify_claim::<MyMmcs, _>(
&zk_data_sim,
&commitment_sim,
ell_zk,
folding_factor,
pow_bits,
joint_claim,
&mut sim_verifier_ch,
)
.map_err(|_| "simulator transcript rejected by verifier")?;
if sim_replay.randomness != gammas_sim {
return Err("verifier redrew a different stream than the simulator's");
}
if zk_data_real.mu_tilde != zk_data_sim.mu_tilde {
return Err("matched-RNG coupling: mu_tilde differs");
}
if commitment_real != commitment_sim {
return Err("matched-RNG coupling: mask commitment differs");
}
let wire_len = ell_zk.max(3) - 1;
if zk_data_sim.round_coefficients.len() != folding_factor {
return Err("simulator emitted a wire count other than the round count");
}
if gammas_sim.as_slice().len() != folding_factor {
return Err("simulator emitted a challenge count other than the round count");
}
if zk_data_sim
.round_coefficients
.iter()
.any(|wire| wire.len() != wire_len)
{
return Err("simulator emitted a wire of the wrong width");
}
if !zk_data_sim.pow_witnesses.is_empty() {
return Err("simulator emitted a grinding witness with pow_bits == 0");
}
for wire in &zk_data_real.round_coefficients {
for &c in wire.iter().skip(2) {
if !escapes_f_subspace(c) {
return Err("residual-prover wire[i >= 2] collapsed into the F-subspace");
}
}
}
for wire in &zk_data_sim.round_coefficients {
for &c in wire.iter().skip(2) {
if !escapes_f_subspace(c) {
return Err("simulator wire[i >= 2] collapsed into the F-subspace");
}
}
}
Ok(())
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(16))]
#[test]
fn prop_inherited_simulator_accepts_and_couples_mask_prelude_rs(
n_vars in 2usize..=8,
ell_zk in 4usize..=5,
order_bit in 0usize..2,
seed in 0u64..1024,
) {
let folding_factor = 1 + (seed as usize % n_vars);
let order = if order_bit == 0 {
VariableOrder::Prefix
} else {
VariableOrder::Suffix
};
prop_assert!(
run_inherited_acceptance_and_mask_prelude_coupling(
order,
n_vars,
folding_factor,
ell_zk,
EF::ZERO,
seed,
).is_ok()
);
}
}
#[test]
fn both_simulators_hold_at_the_shortest_legal_mask() {
let ell_zk = 3;
let n_vars = 8;
let folding_factor = 2;
let seed = 0xF10;
for order in [VariableOrder::Prefix, VariableOrder::Suffix] {
let batching = run_acceptance_and_mask_prelude_coupling(
order,
n_vars,
folding_factor,
ell_zk,
1,
seed,
);
assert_eq!(
batching,
Ok(()),
"recorded-claims prelude, order = {order:?}: {}",
batching.err().unwrap_or_default(),
);
let inherited = run_inherited_acceptance_and_mask_prelude_coupling(
order,
n_vars,
folding_factor,
ell_zk,
EF::ZERO,
seed,
);
assert_eq!(
inherited,
Ok(()),
"inherited-claim prelude, order = {order:?}: {}",
inherited.err().unwrap_or_default(),
);
}
}
#[test]
fn the_inherited_simulator_runs_against_a_non_zero_auxiliary_claim() {
let n_vars = 6;
let folding_factor = 3;
let ell_zk = 4;
for aux in [1u64, 7, 12_345, 2_013_265_920] {
for order in [VariableOrder::Prefix, VariableOrder::Suffix] {
let aux_claim = EF::from_u64(aux);
assert_ne!(
aux_claim,
EF::ZERO,
"the fixture must exercise a live carry"
);
let outcome = run_inherited_acceptance_and_mask_prelude_coupling(
order,
n_vars,
folding_factor,
ell_zk,
aux_claim,
0x5EED + aux,
);
assert_eq!(
outcome,
Ok(()),
"aux = {aux}, order = {order:?}: {}",
outcome.err().unwrap_or_default(),
);
}
}
}
#[test]
fn the_inherited_simulator_and_the_residual_prover_play_one_description() {
let n_vars = 6;
let folding_factor = 2;
let ell_zk = 4;
let pow_bits = 0;
let seed = 11u64;
let aux_claim = EF::from_u64(7);
let shape = ZkSumcheckShape::new_inherited(folding_factor, ell_zk, pow_bits);
let mut data_rng = StdRng::seed_from_u64(seed.wrapping_add(1));
let evals = Poly::<EF>::rand(&mut data_rng, n_vars);
let weights = Poly::<EF>::rand(&mut data_rng, n_vars);
let claimed_sum = dot_product::<EF, _, _>(
evals.as_slice().iter().copied(),
weights.as_slice().iter().copied(),
);
let joint_claim = claimed_sum + aux_claim;
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let poly = ProductPolynomial::<F, EF>::new_unpacked(VariableOrder::Prefix, evals, weights);
let mut real_ch = MyChallenger::new(perm.clone());
let mut real_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let mut zk_data_real = ZkSumcheckData::<F, EF>::default();
let real_handoff = SumcheckProver::new(poly, claimed_sum).into_zk_sumcheck(
&mut zk_data_real,
&encoding,
&mmcs,
folding_factor,
pow_bits,
aux_claim,
&mut real_ch,
&mut real_rng,
);
let real_gammas: Vec<EF> = real_handoff.randomness.iter().copied().collect();
let mut real_replay_ch = MyChallenger::new(perm.clone());
assert_eq!(
replay_through_the_description(
&mut real_replay_ch,
shape,
Some(joint_claim),
real_handoff.mask_oracle.0.clone(),
&zk_data_real,
),
real_gammas,
"the residual prover's stream must come out of the shared description",
);
let mut sim_ch = MyChallenger::new(perm.clone());
let mut sim_rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (zk_data_sim, commitment_sim, gammas_sim) =
simulate_classic_unpacked_claim::<F, EF, _, _, _, _>(
&mut sim_ch,
joint_claim,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut sim_rng,
);
let mut sim_replay_ch = MyChallenger::new(perm);
assert_eq!(
replay_through_the_description(
&mut sim_replay_ch,
shape,
Some(joint_claim),
commitment_sim.clone(),
&zk_data_sim,
),
gammas_sim.iter().copied().collect::<Vec<_>>(),
"the simulator's stream must come out of the shared description",
);
assert_eq!(zk_data_real.mu_tilde, zk_data_sim.mu_tilde);
assert_eq!(real_handoff.mask_oracle.0, commitment_sim);
assert_eq!(
zk_data_real.round_coefficients.len(),
zk_data_sim.round_coefficients.len(),
);
}
#[test]
fn the_inherited_simulator_with_pow_replays() {
let seed = 0x5eed;
let folding_factor = 2;
let ell_zk = 4;
let pow_bits = 4;
let joint_claim = EF::from_u64(9);
let (perm, mmcs, encoding) = make_setup(seed, ell_zk);
let mut sim_ch = MyChallenger::new(perm.clone());
let mut rng = StdRng::seed_from_u64(seed.wrapping_add(2));
let (zk_data, mask_commitment, _) = simulate_classic_unpacked_claim::<F, EF, _, _, _, _>(
&mut sim_ch,
joint_claim,
folding_factor,
pow_bits,
&encoding,
&mmcs,
&mut rng,
);
assert_eq!(zk_data.pow_witnesses.len(), folding_factor);
let mut verifier_ch = MyChallenger::new(perm);
let _ = ZkVerifier::<F, EF>::verify_claim::<MyMmcs, _>(
&zk_data,
&mask_commitment,
ell_zk,
folding_factor,
pow_bits,
joint_claim,
&mut verifier_ch,
)
.expect("verifier must accept the simulator's PoW witnesses");
}
}