mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-07 14:31:31 +01:00
feat: implement nova's zk layer (#127)
* feat: zk nova layer * chore: clippy + trigger CI * chore: add comment for `new` (generating a zk nova ivc proof) * chore: adding text reference to `sample` * chore: use `debug_assert` instead of `cfg(test)` * improve: pass `poseidon_config` by ref Co-authored-by: Carlos Pérez <37264926+CPerezz@users.noreply.github.com> * improve: pass `z_0` by ref Co-authored-by: Carlos Pérez <37264926+CPerezz@users.noreply.github.com> * improve: pass `r1cs` and `cf_r1cs` by ref Co-authored-by: Carlos Pérez <37264926+CPerezz@users.noreply.github.com> * chore: appropriate docs (2) * chore: pass by ref modifications * improve: use single sponge * fix: remove blinding the cyclefold instance, add verifier checks on the prover provided cyclefold intance * fix: assert that the sampled relaxed r1cs is correct * fix: check length of `u_i.x` --------- Co-authored-by: Carlos Pérez <37264926+CPerezz@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::folding::nova::{CommittedInstance, Witness};
|
||||
use crate::RngCore;
|
||||
use ark_crypto_primitives::sponge::Absorb;
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ark_std::rand::Rng;
|
||||
|
||||
use super::Arith;
|
||||
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
|
||||
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub, SparseMatrix};
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
@@ -92,6 +97,84 @@ impl<F: PrimeField> RelaxedR1CS<F> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Computes the E term, given A, B, C, z, u
|
||||
fn compute_E(
|
||||
A: &SparseMatrix<F>,
|
||||
B: &SparseMatrix<F>,
|
||||
C: &SparseMatrix<F>,
|
||||
z: &[F],
|
||||
u: &F,
|
||||
) -> Result<Vec<F>, Error> {
|
||||
let Az = mat_vec_mul(A, z)?;
|
||||
let Bz = mat_vec_mul(B, z)?;
|
||||
let AzBz = hadamard(&Az, &Bz)?;
|
||||
|
||||
let Cz = mat_vec_mul(C, z)?;
|
||||
let uCz = vec_scalar_mul(&Cz, u);
|
||||
vec_sub(&AzBz, &uCz)
|
||||
}
|
||||
|
||||
pub fn check_sampled_relaxed_r1cs(&self, u: F, E: &[F], z: &[F]) -> bool {
|
||||
let sampled = RelaxedR1CS {
|
||||
l: self.l,
|
||||
A: self.A.clone(),
|
||||
B: self.B.clone(),
|
||||
C: self.C.clone(),
|
||||
u,
|
||||
E: E.to_vec(),
|
||||
};
|
||||
sampled.check_relation(z).is_ok()
|
||||
}
|
||||
|
||||
// Implements sampling a (committed) RelaxedR1CS
|
||||
// See construction 5 in https://eprint.iacr.org/2023/573.pdf
|
||||
pub fn sample<C, CS>(
|
||||
&self,
|
||||
params: &CS::ProverParams,
|
||||
mut rng: impl RngCore,
|
||||
) -> Result<(CommittedInstance<C>, Witness<C>), Error>
|
||||
where
|
||||
C: CurveGroup,
|
||||
C: CurveGroup<ScalarField = F>,
|
||||
<C as Group>::ScalarField: Absorb,
|
||||
CS: CommitmentScheme<C, true>,
|
||||
{
|
||||
let u = C::ScalarField::rand(&mut rng);
|
||||
let rE = C::ScalarField::rand(&mut rng);
|
||||
let rW = C::ScalarField::rand(&mut rng);
|
||||
|
||||
let W = (0..self.A.n_cols - self.l - 1)
|
||||
.map(|_| F::rand(&mut rng))
|
||||
.collect();
|
||||
let x = (0..self.l).map(|_| F::rand(&mut rng)).collect::<Vec<F>>();
|
||||
let mut z = vec![u];
|
||||
z.extend(&x);
|
||||
z.extend(&W);
|
||||
|
||||
let E = RelaxedR1CS::compute_E(&self.A, &self.B, &self.C, &z, &u)?;
|
||||
|
||||
debug_assert!(
|
||||
z.len() == self.A.n_cols,
|
||||
"Length of z is {}, while A has {} columns.",
|
||||
z.len(),
|
||||
self.A.n_cols
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
self.check_sampled_relaxed_r1cs(u, &E, &z),
|
||||
"Sampled a non satisfiable relaxed R1CS, sampled u: {}, computed E: {:?}",
|
||||
u,
|
||||
E
|
||||
);
|
||||
|
||||
let witness = Witness { E, rE, W, rW };
|
||||
let mut cm_witness = witness.commit::<CS, true>(params, x)?;
|
||||
|
||||
// witness.commit() sets u to 1, we set it to the sampled u value
|
||||
cm_witness.u = u;
|
||||
Ok((cm_witness, witness))
|
||||
}
|
||||
}
|
||||
|
||||
/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
|
||||
@@ -138,9 +221,24 @@ pub fn extract_w_x<F: PrimeField>(cs: &ConstraintSystem<F>) -> (Vec<F>, Vec<F>)
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::utils::vec::tests::{to_F_matrix, to_F_vec};
|
||||
use crate::{
|
||||
commitment::pedersen::Pedersen,
|
||||
utils::vec::tests::{to_F_matrix, to_F_vec},
|
||||
};
|
||||
|
||||
use ark_pallas::Fr;
|
||||
use ark_pallas::{Fr, Projective};
|
||||
|
||||
#[test]
|
||||
pub fn sample_relaxed_r1cs() {
|
||||
let rng = rand::rngs::OsRng;
|
||||
let r1cs = get_test_r1cs::<Fr>();
|
||||
let (prover_params, _) = Pedersen::<Projective>::setup(rng, r1cs.A.n_rows).unwrap();
|
||||
|
||||
let relaxed_r1cs = r1cs.relax();
|
||||
let sampled =
|
||||
relaxed_r1cs.sample::<Projective, Pedersen<Projective, true>>(&prover_params, rng);
|
||||
assert!(sampled.is_ok());
|
||||
}
|
||||
|
||||
pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
|
||||
// R1CS for: x^3 + x + 5 = y (example from article
|
||||
|
||||
@@ -37,7 +37,7 @@ pub mod decider_eth_circuit;
|
||||
pub mod nifs;
|
||||
pub mod serialize;
|
||||
pub mod traits;
|
||||
|
||||
pub mod zk;
|
||||
use circuits::{AugmentedFCircuit, ChallengeGadget};
|
||||
use nifs::NIFS;
|
||||
use traits::NovaR1CS;
|
||||
@@ -957,25 +957,33 @@ pub mod tests {
|
||||
test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
|
||||
test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
|
||||
// run the test using KZG for the commitments on the main curve, and Pedersen for the
|
||||
// commitments on the secondary curve
|
||||
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit);
|
||||
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit, 3);
|
||||
}
|
||||
|
||||
// test_ivc allowing to choose the CommitmentSchemes
|
||||
fn test_ivc_opt<
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn test_ivc_opt<
|
||||
CS1: CommitmentScheme<Projective, H>,
|
||||
CS2: CommitmentScheme<Projective2, H>,
|
||||
const H: bool,
|
||||
>(
|
||||
poseidon_config: PoseidonConfig<Fr>,
|
||||
F_circuit: CubicFCircuit<Fr>,
|
||||
num_steps: usize,
|
||||
) -> (
|
||||
Vec<Fr>,
|
||||
Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>,
|
||||
) {
|
||||
let mut rng = ark_std::test_rng();
|
||||
|
||||
@@ -1009,7 +1017,6 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let num_steps: usize = 3;
|
||||
for _ in 0..num_steps {
|
||||
nova.prove_step(&mut rng, vec![], None).unwrap();
|
||||
}
|
||||
@@ -1018,13 +1025,15 @@ pub mod tests {
|
||||
let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
|
||||
Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify(
|
||||
nova_params.1, // Nova's verifier params
|
||||
z_0,
|
||||
nova.z_i,
|
||||
z_0.clone(),
|
||||
nova.z_i.clone(),
|
||||
nova.i,
|
||||
running_instance,
|
||||
incoming_instance,
|
||||
cyclefold_instance,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(z_0, nova)
|
||||
}
|
||||
}
|
||||
|
||||
419
folding-schemes/src/folding/nova/zk.rs
Normal file
419
folding-schemes/src/folding/nova/zk.rs
Normal file
@@ -0,0 +1,419 @@
|
||||
// Implements nova's zero-knowledge layer, as described in https://eprint.iacr.org/2023/573.pdf
|
||||
use crate::folding::nova::traits::NovaR1CS;
|
||||
use ark_crypto_primitives::sponge::CryptographicSponge;
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_std::{One, Zero};
|
||||
|
||||
use crate::{
|
||||
arith::r1cs::{RelaxedR1CS, R1CS},
|
||||
RngCore,
|
||||
};
|
||||
use ark_crypto_primitives::sponge::{
|
||||
poseidon::{PoseidonConfig, PoseidonSponge},
|
||||
Absorb,
|
||||
};
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_r1cs_std::{
|
||||
groups::{CurveVar, GroupOpsBounds},
|
||||
ToConstraintFieldGadget,
|
||||
};
|
||||
|
||||
use crate::{commitment::CommitmentScheme, folding::circuits::CF2, frontend::FCircuit, Error};
|
||||
|
||||
use super::{circuits::ChallengeGadget, nifs::NIFS, CommittedInstance, Nova, Witness};
|
||||
|
||||
// We use the same definition of a folding proof as in https://eprint.iacr.org/2023/969.pdf
|
||||
// It consists in the commitment to the T term
|
||||
pub struct FoldingProof<C: CurveGroup> {
|
||||
cmT: C,
|
||||
}
|
||||
|
||||
pub struct RandomizedIVCProof<C1: CurveGroup, C2: CurveGroup> {
|
||||
pub U_i: CommittedInstance<C1>,
|
||||
pub u_i: CommittedInstance<C1>,
|
||||
pub U_r: CommittedInstance<C1>,
|
||||
pub pi: FoldingProof<C1>,
|
||||
pub pi_prime: FoldingProof<C1>,
|
||||
pub W_i_prime: Witness<C1>,
|
||||
pub cf_U_i: CommittedInstance<C2>,
|
||||
pub cf_W_i: Witness<C2>,
|
||||
}
|
||||
|
||||
impl<C1: CurveGroup, C2: CurveGroup> RandomizedIVCProof<C1, C2>
|
||||
where
|
||||
<C1 as Group>::ScalarField: Absorb,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
{
|
||||
/// Computes challenge required before folding instances
|
||||
fn get_folding_challenge(
|
||||
sponge: &mut PoseidonSponge<C1::ScalarField>,
|
||||
pp_hash: C1::ScalarField,
|
||||
U_i: CommittedInstance<C1>,
|
||||
u_i: CommittedInstance<C1>,
|
||||
cmT: C1,
|
||||
) -> Result<C1::ScalarField, Error> {
|
||||
let r_bits = ChallengeGadget::<C1>::get_challenge_native(sponge, pp_hash, U_i, u_i, cmT);
|
||||
C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)).ok_or(Error::OutOfBounds)
|
||||
}
|
||||
|
||||
/// Compute a zero-knowledge proof of a Nova IVC proof
|
||||
/// It implements the prover of appendix D.4.in https://eprint.iacr.org/2023/573.pdf
|
||||
/// For further details on why folding is hiding, see lemma 9
|
||||
pub fn new<
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
CS1: CommitmentScheme<C1, true>,
|
||||
CS2: CommitmentScheme<C2, true>,
|
||||
>(
|
||||
nova: &Nova<C1, GC1, C2, GC2, FC, CS1, CS2, true>,
|
||||
mut rng: impl RngCore,
|
||||
) -> Result<RandomizedIVCProof<C1, C2>, Error>
|
||||
where
|
||||
<C1 as Group>::ScalarField: Absorb,
|
||||
<C2 as Group>::ScalarField: Absorb,
|
||||
<C2 as Group>::ScalarField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: Absorb,
|
||||
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||
GC2: ToConstraintFieldGadget<<C2 as CurveGroup>::BaseField>,
|
||||
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
|
||||
{
|
||||
let mut challenges_sponge = PoseidonSponge::<C1::ScalarField>::new(&nova.poseidon_config);
|
||||
|
||||
// I. Compute proof for 'regular' instances
|
||||
// 1. Fold the instance-witness pairs (U_i, W_i) with (u_i, w_i)
|
||||
// a. Compute T
|
||||
let (T, cmT) = NIFS::<C1, CS1, true>::compute_cmT(
|
||||
&nova.cs_pp,
|
||||
&nova.r1cs,
|
||||
&nova.w_i,
|
||||
&nova.u_i,
|
||||
&nova.W_i,
|
||||
&nova.U_i,
|
||||
)?;
|
||||
|
||||
// b. Compute folding challenge
|
||||
let r = RandomizedIVCProof::<C1, C2>::get_folding_challenge(
|
||||
&mut challenges_sponge,
|
||||
nova.pp_hash,
|
||||
nova.U_i.clone(),
|
||||
nova.u_i.clone(),
|
||||
cmT,
|
||||
)?;
|
||||
|
||||
// c. Compute fold
|
||||
let (W_f, U_f) = NIFS::<C1, CS1, true>::fold_instances(
|
||||
r, &nova.w_i, &nova.u_i, &nova.W_i, &nova.U_i, &T, cmT,
|
||||
)?;
|
||||
|
||||
// d. Store folding proof
|
||||
let pi = FoldingProof { cmT };
|
||||
|
||||
// 2. Sample a satisfying relaxed R1CS instance-witness pair (U_r, W_r)
|
||||
let relaxed_instance = nova.r1cs.clone().relax();
|
||||
let (U_r, W_r) = relaxed_instance.sample::<C1, CS1>(&nova.cs_pp, &mut rng)?;
|
||||
|
||||
// 3. Fold the instance-witness pair (U_f, W_f) with (U_r, W_r)
|
||||
// a. Compute T
|
||||
let (T_i_prime, cmT_i_prime) =
|
||||
NIFS::<C1, CS1, true>::compute_cmT(&nova.cs_pp, &nova.r1cs, &W_f, &U_f, &W_r, &U_r)?;
|
||||
|
||||
// b. Compute folding challenge
|
||||
let r_2 = RandomizedIVCProof::<C1, C2>::get_folding_challenge(
|
||||
&mut challenges_sponge,
|
||||
nova.pp_hash,
|
||||
U_f.clone(),
|
||||
U_r.clone(),
|
||||
cmT_i_prime,
|
||||
)?;
|
||||
|
||||
// c. Compute fold
|
||||
let (W_i_prime, _) = NIFS::<C1, CS1, true>::fold_instances(
|
||||
r_2,
|
||||
&W_f,
|
||||
&U_f,
|
||||
&W_r,
|
||||
&U_r,
|
||||
&T_i_prime,
|
||||
cmT_i_prime,
|
||||
)?;
|
||||
|
||||
// d. Store folding proof
|
||||
let pi_prime = FoldingProof { cmT: cmT_i_prime };
|
||||
|
||||
Ok(RandomizedIVCProof {
|
||||
U_i: nova.U_i.clone(),
|
||||
u_i: nova.u_i.clone(),
|
||||
U_r,
|
||||
pi,
|
||||
pi_prime,
|
||||
W_i_prime,
|
||||
cf_U_i: nova.cf_U_i.clone(),
|
||||
cf_W_i: nova.cf_W_i.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Verify a zero-knowledge proof of a Nova IVC proof
|
||||
/// It implements the verifier of appendix D.4. in https://eprint.iacr.org/2023/573.pdf
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn verify<
|
||||
CS1: CommitmentScheme<C1, true>,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
CS2: CommitmentScheme<C2, true>,
|
||||
>(
|
||||
r1cs: &R1CS<C1::ScalarField>,
|
||||
cf_r1cs: &R1CS<C2::ScalarField>,
|
||||
pp_hash: C1::ScalarField,
|
||||
poseidon_config: &PoseidonConfig<C1::ScalarField>,
|
||||
i: C1::ScalarField,
|
||||
z_0: Vec<C1::ScalarField>,
|
||||
z_i: Vec<C1::ScalarField>,
|
||||
proof: &RandomizedIVCProof<C1, C2>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
<C1 as Group>::ScalarField: Absorb,
|
||||
<C2 as Group>::ScalarField: Absorb,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: Absorb,
|
||||
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||
GC2: ToConstraintFieldGadget<<C2 as CurveGroup>::BaseField>,
|
||||
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
|
||||
{
|
||||
// Handles case where i=0
|
||||
if i == C1::ScalarField::zero() {
|
||||
if z_0 == z_i {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(Error::zkIVCVerificationFail);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Check that u_i.x is correct - including the cyclefold running instance
|
||||
// a. Check length
|
||||
if proof.u_i.x.len() != 2 {
|
||||
return Err(Error::IVCVerificationFail);
|
||||
}
|
||||
|
||||
// b. Check computed hashes are correct
|
||||
let mut sponge = PoseidonSponge::<C1::ScalarField>::new(poseidon_config);
|
||||
let expected_u_i_x = proof.U_i.hash(&sponge, pp_hash, i, z_0, z_i);
|
||||
if expected_u_i_x != proof.u_i.x[0] {
|
||||
return Err(Error::zkIVCVerificationFail);
|
||||
}
|
||||
|
||||
let expected_cf_u_i_x = proof.cf_U_i.hash_cyclefold(&sponge, pp_hash);
|
||||
if expected_cf_u_i_x != proof.u_i.x[1] {
|
||||
return Err(Error::IVCVerificationFail);
|
||||
}
|
||||
|
||||
// 2. Check that u_i values are correct
|
||||
if !proof.u_i.cmE.is_zero() || proof.u_i.u != C1::ScalarField::one() {
|
||||
return Err(Error::zkIVCVerificationFail);
|
||||
}
|
||||
|
||||
// 3. Obtain the U_f folded instance
|
||||
// a. Compute folding challenge
|
||||
let r = RandomizedIVCProof::<C1, C2>::get_folding_challenge(
|
||||
&mut sponge,
|
||||
pp_hash,
|
||||
proof.U_i.clone(),
|
||||
proof.u_i.clone(),
|
||||
proof.pi.cmT,
|
||||
)?;
|
||||
|
||||
// b. Get the U_f instance
|
||||
let U_f = NIFS::<C1, CS1, true>::fold_committed_instance(
|
||||
r,
|
||||
&proof.u_i,
|
||||
&proof.U_i,
|
||||
&proof.pi.cmT,
|
||||
);
|
||||
|
||||
// 4. Obtain the U^{\prime}_i folded instance
|
||||
// a. Compute folding challenge
|
||||
let r_2 = RandomizedIVCProof::<C1, C2>::get_folding_challenge(
|
||||
&mut sponge,
|
||||
pp_hash,
|
||||
U_f.clone(),
|
||||
proof.U_r.clone(),
|
||||
proof.pi_prime.cmT,
|
||||
)?;
|
||||
|
||||
// b. Compute fold
|
||||
let U_i_prime = NIFS::<C1, CS1, true>::fold_committed_instance(
|
||||
r_2,
|
||||
&U_f,
|
||||
&proof.U_r,
|
||||
&proof.pi_prime.cmT,
|
||||
);
|
||||
|
||||
// 5. Check that W^{\prime}_i is a satisfying witness
|
||||
let mut z = vec![U_i_prime.u];
|
||||
z.extend(&U_i_prime.x);
|
||||
z.extend(&proof.W_i_prime.W);
|
||||
let relaxed_r1cs = RelaxedR1CS {
|
||||
l: r1cs.l,
|
||||
A: r1cs.A.clone(),
|
||||
B: r1cs.B.clone(),
|
||||
C: r1cs.C.clone(),
|
||||
u: U_i_prime.u,
|
||||
E: proof.W_i_prime.E.clone(),
|
||||
};
|
||||
relaxed_r1cs.check_relation(&z)?;
|
||||
|
||||
// 6. Check that the cyclefold instance-witness pair satisfies the cyclefold relaxed r1cs
|
||||
cf_r1cs.check_relaxed_instance_relation(&proof.cf_W_i, &proof.cf_U_i)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
use crate::folding::nova::tests::test_ivc_opt;
|
||||
use crate::frontend::tests::CubicFCircuit;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
use ark_bn254::{Fr, G1Projective as Projective};
|
||||
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
// Tests zk proof generation and verification for a valid nova IVC proof
|
||||
#[test]
|
||||
fn test_zk_nova_ivc() {
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
let (_, nova) = test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
|
||||
let proof = RandomizedIVCProof::new(&nova, &mut rng).unwrap();
|
||||
let verify = RandomizedIVCProof::verify::<
|
||||
Pedersen<Projective, true>,
|
||||
GVar2,
|
||||
Pedersen<Projective2, true>,
|
||||
>(
|
||||
&nova.r1cs,
|
||||
&nova.cf_r1cs,
|
||||
nova.pp_hash,
|
||||
&nova.poseidon_config,
|
||||
nova.i,
|
||||
nova.z_0,
|
||||
nova.z_i,
|
||||
&proof,
|
||||
);
|
||||
assert!(verify.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zk_nova_when_i_is_zero() {
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
let (_, nova) = test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
0,
|
||||
);
|
||||
|
||||
let proof = RandomizedIVCProof::new(&nova, &mut rng).unwrap();
|
||||
let verify = RandomizedIVCProof::verify::<
|
||||
Pedersen<Projective, true>,
|
||||
GVar2,
|
||||
Pedersen<Projective2, true>,
|
||||
>(
|
||||
&nova.r1cs,
|
||||
&nova.cf_r1cs,
|
||||
nova.pp_hash,
|
||||
&nova.poseidon_config,
|
||||
nova.i,
|
||||
nova.z_0,
|
||||
nova.z_i,
|
||||
&proof,
|
||||
);
|
||||
assert!(verify.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zk_nova_verification_fails_with_wrong_running_instance() {
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
let (_, nova) = test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
let (sampled_committed_instance, _) = nova
|
||||
.r1cs
|
||||
.clone()
|
||||
.relax()
|
||||
.sample::<Projective, Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.unwrap();
|
||||
|
||||
// proof verification fails with incorrect running instance
|
||||
let mut nova_with_incorrect_running_instance = nova.clone();
|
||||
nova_with_incorrect_running_instance.U_i = sampled_committed_instance;
|
||||
let incorrect_proof =
|
||||
RandomizedIVCProof::new(&nova_with_incorrect_running_instance, &mut rng).unwrap();
|
||||
let verify = RandomizedIVCProof::verify::<
|
||||
Pedersen<Projective, true>,
|
||||
GVar2,
|
||||
Pedersen<Projective2, true>,
|
||||
>(
|
||||
&nova_with_incorrect_running_instance.r1cs,
|
||||
&nova_with_incorrect_running_instance.cf_r1cs,
|
||||
nova_with_incorrect_running_instance.pp_hash,
|
||||
&nova_with_incorrect_running_instance.poseidon_config,
|
||||
nova_with_incorrect_running_instance.i,
|
||||
nova_with_incorrect_running_instance.z_0,
|
||||
nova_with_incorrect_running_instance.z_i,
|
||||
&incorrect_proof,
|
||||
);
|
||||
assert!(verify.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zk_nova_verification_fails_with_wrong_running_witness() {
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
let (_, nova) = test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
let (_, sampled_committed_witness) = nova
|
||||
.r1cs
|
||||
.clone()
|
||||
.relax()
|
||||
.sample::<Projective, Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.unwrap();
|
||||
|
||||
// proof generation fails with incorrect running witness
|
||||
let mut nova_with_incorrect_running_witness = nova.clone();
|
||||
nova_with_incorrect_running_witness.W_i = sampled_committed_witness;
|
||||
let incorrect_proof =
|
||||
RandomizedIVCProof::new(&nova_with_incorrect_running_witness, &mut rng).unwrap();
|
||||
let verify = RandomizedIVCProof::verify::<
|
||||
Pedersen<Projective, true>,
|
||||
GVar2,
|
||||
Pedersen<Projective2, true>,
|
||||
>(
|
||||
&nova_with_incorrect_running_witness.r1cs,
|
||||
&nova_with_incorrect_running_witness.cf_r1cs,
|
||||
nova_with_incorrect_running_witness.pp_hash,
|
||||
&nova_with_incorrect_running_witness.poseidon_config,
|
||||
nova_with_incorrect_running_witness.i,
|
||||
nova_with_incorrect_running_witness.z_0,
|
||||
nova_with_incorrect_running_witness.z_i,
|
||||
&incorrect_proof,
|
||||
);
|
||||
assert!(verify.is_err());
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ pub enum Error {
|
||||
SNARKVerificationFail,
|
||||
#[error("IVC verification failed")]
|
||||
IVCVerificationFail,
|
||||
#[error("zkIVC verification failed")]
|
||||
zkIVCVerificationFail,
|
||||
#[error("R1CS instance is expected to not be relaxed")]
|
||||
R1CSUnrelaxedFail,
|
||||
#[error("Could not find the inner ConstraintSystem")]
|
||||
|
||||
Reference in New Issue
Block a user