Browse Source

Unify the computation of digests and challenges in different folding schemes (#94)

* Remove the trait bound `C::BaseField: PrimeField` for better DX

* Methods in `TranscriptVar` now exactly matches the ones in `Transcript`

* Add `ProtoGalaxyTranscriptVar` and `CommittedInstanceVar` for protogalaxy

* betas are unnecessary in "plain" (incoming) instances

* Absorb the result of `get_challenge_nbits` as well

* `ProtoGalaxyTranscript` now allows absorbing mulitple instances

* Always return `Result<(), SynthesisError>` in `ProtoGalaxyTranscriptVar`

* Impl `Transcript{Var}` for `PoseidonSponge{Var}` directly and remove `PoseidonTranscript{Var}`

* `Transcript::absorb_point` doesn't need to return `Error`

* Add `AbsorbNonNative` trait for hashing non-native values

Note that now `absorb_point` only supports hashing points whose BaseField is equal to the sponge's field

* More efficient `TranscriptVar::absorb_point` by securely removing `is_inf`

* Use `sponge` and `transcript` consistently

* Clarify the usage of `AbsorbNonNative{Gadget}`

* Generic `sponge` and `transcript` params

* Avoid unstable `associated_type_bounds`

* Reuse `sponge` in hypernova

* Clean up redundant imports

* Remove unstable code

* Clarify the usage of `absorb_point` and `absorb_nonnative`
update-nifs-interface
winderica 4 months ago
committed by GitHub
parent
commit
16d51d757b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
36 changed files with 1028 additions and 1031 deletions
  1. +0
    -1
      folding-schemes/src/arith/ccs.rs
  2. +0
    -1
      folding-schemes/src/arith/r1cs.rs
  3. +19
    -19
      folding-schemes/src/commitment/ipa.rs
  4. +8
    -7
      folding-schemes/src/commitment/kzg.rs
  5. +9
    -9
      folding-schemes/src/commitment/mod.rs
  6. +10
    -10
      folding-schemes/src/commitment/pedersen.rs
  7. +54
    -92
      folding-schemes/src/folding/circuits/cyclefold.rs
  8. +34
    -40
      folding-schemes/src/folding/circuits/nonnative/affine.rs
  9. +43
    -10
      folding-schemes/src/folding/circuits/nonnative/uint.rs
  10. +46
    -70
      folding-schemes/src/folding/circuits/sum_check.rs
  11. +22
    -0
      folding-schemes/src/folding/hypernova/cccs.rs
  12. +67
    -114
      folding-schemes/src/folding/hypernova/circuits.rs
  13. +35
    -27
      folding-schemes/src/folding/hypernova/lcccs.rs
  14. +38
    -32
      folding-schemes/src/folding/hypernova/mod.rs
  15. +36
    -73
      folding-schemes/src/folding/hypernova/nimfs.rs
  16. +0
    -1
      folding-schemes/src/folding/hypernova/utils.rs
  17. +74
    -100
      folding-schemes/src/folding/nova/circuits.rs
  18. +1
    -3
      folding-schemes/src/folding/nova/decider_eth.rs
  19. +50
    -63
      folding-schemes/src/folding/nova/decider_eth_circuit.rs
  20. +103
    -91
      folding-schemes/src/folding/nova/mod.rs
  21. +11
    -8
      folding-schemes/src/folding/nova/nifs.rs
  22. +19
    -23
      folding-schemes/src/folding/protogalaxy/folding.rs
  23. +10
    -0
      folding-schemes/src/folding/protogalaxy/mod.rs
  24. +36
    -17
      folding-schemes/src/folding/protogalaxy/traits.rs
  25. +1
    -2
      folding-schemes/src/frontend/circom/mod.rs
  26. +0
    -1
      folding-schemes/src/frontend/circom/utils.rs
  27. +1
    -3
      folding-schemes/src/frontend/mod.rs
  28. +89
    -17
      folding-schemes/src/transcript/mod.rs
  29. +123
    -96
      folding-schemes/src/transcript/poseidon.rs
  30. +48
    -51
      folding-schemes/src/utils/espresso/sum_check/mod.rs
  31. +15
    -19
      folding-schemes/src/utils/espresso/sum_check/prover.rs
  32. +7
    -8
      folding-schemes/src/utils/espresso/sum_check/structs.rs
  33. +12
    -12
      folding-schemes/src/utils/espresso/sum_check/verifier.rs
  34. +0
    -2
      folding-schemes/src/utils/espresso/virtual_polynomial.rs
  35. +1
    -1
      folding-schemes/src/utils/lagrange_poly.rs
  36. +6
    -8
      solidity-verifiers/src/verifiers/kzg.rs

+ 0
- 1
folding-schemes/src/arith/ccs.rs

@ -114,7 +114,6 @@ impl CCS {
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z}; use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
use ark_ff::PrimeField;
use ark_pallas::Fr; use ark_pallas::Fr;
pub fn get_test_ccs<F: PrimeField>() -> CCS<F> { pub fn get_test_ccs<F: PrimeField>() -> CCS<F> {

+ 0
- 1
folding-schemes/src/arith/r1cs.rs

@ -140,7 +140,6 @@ pub mod tests {
use super::*; use super::*;
use crate::utils::vec::tests::{to_F_matrix, to_F_vec}; use crate::utils::vec::tests::{to_F_matrix, to_F_vec};
use ark_ff::PrimeField;
use ark_pallas::Fr; use ark_pallas::Fr;
pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> { pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {

+ 19
- 19
folding-schemes/src/commitment/ipa.rs

@ -97,7 +97,7 @@ impl CommitmentScheme for IPA {
fn prove( fn prove(
params: &Self::ProverParams, params: &Self::ProverParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
P: &C, // commitment P: &C, // commitment
a: &[C::ScalarField], // vector a: &[C::ScalarField], // vector
blind: &C::ScalarField, blind: &C::ScalarField,
@ -131,7 +131,7 @@ impl CommitmentScheme for IPA {
r = vec![]; r = vec![];
} }
transcript.absorb_point(P)?;
transcript.absorb_nonnative(P);
let x = transcript.get_challenge(); // challenge value at which we evaluate let x = transcript.get_challenge(); // challenge value at which we evaluate
let s = transcript.get_challenge(); let s = transcript.get_challenge();
let U = C::generator().mul(s); let U = C::generator().mul(s);
@ -162,8 +162,8 @@ impl CommitmentScheme for IPA {
R[j] = C::msm_unchecked(&G[..m], &a[m..]) + U.mul(inner_prod(&a[m..], &b[..m])?); R[j] = C::msm_unchecked(&G[..m], &a[m..]) + U.mul(inner_prod(&a[m..], &b[..m])?);
} }
// get challenge for the j-th round // get challenge for the j-th round
transcript.absorb_point(&L[j])?;
transcript.absorb_point(&R[j])?;
transcript.absorb_nonnative(&L[j]);
transcript.absorb_nonnative(&R[j]);
u[j] = transcript.get_challenge(); u[j] = transcript.get_challenge();
let uj = u[j]; let uj = u[j];
@ -225,21 +225,21 @@ impl CommitmentScheme for IPA {
fn verify( fn verify(
params: &Self::VerifierParams, params: &Self::VerifierParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
P: &C, // commitment P: &C, // commitment
proof: &Self::Proof, proof: &Self::Proof,
) -> Result<(), Error> { ) -> Result<(), Error> {
let (p, _r) = (proof.0.clone(), proof.1); let (p, _r) = (proof.0.clone(), proof.1);
let k = p.L.len(); let k = p.L.len();
transcript.absorb_point(P)?;
transcript.absorb_nonnative(P);
let x = transcript.get_challenge(); // challenge value at which we evaluate let x = transcript.get_challenge(); // challenge value at which we evaluate
let s = transcript.get_challenge(); let s = transcript.get_challenge();
let U = C::generator().mul(s); let U = C::generator().mul(s);
let mut u: Vec<C::ScalarField> = vec![C::ScalarField::zero(); k]; let mut u: Vec<C::ScalarField> = vec![C::ScalarField::zero(); k];
for i in (0..k).rev() { for i in (0..k).rev() {
transcript.absorb_point(&p.L[i])?;
transcript.absorb_point(&p.R[i])?;
transcript.absorb_nonnative(&p.L[i]);
transcript.absorb_nonnative(&p.R[i]);
u[i] = transcript.get_challenge(); u[i] = transcript.get_challenge();
} }
let challenge = (x, U, u); let challenge = (x, U, u);
@ -566,15 +566,15 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
use ark_ec::Group; use ark_ec::Group;
use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget};
use ark_r1cs_std::eq::EqGadget;
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand;
use std::ops::Mul; use std::ops::Mul;
use super::*; use super::*;
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
use crate::transcript::poseidon::poseidon_canonical_config;
#[test] #[test]
fn test_ipa() { fn test_ipa() {
@ -592,9 +592,9 @@ mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
// init Prover's transcript // init Prover's transcript
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
// a is the vector that we're committing // a is the vector that we're committing
let a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) let a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
@ -636,9 +636,9 @@ mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
// init Prover's transcript // init Prover's transcript
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
let mut a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) let mut a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
.take(d / 2) .take(d / 2)
@ -666,15 +666,15 @@ mod tests {
// circuit // circuit
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
transcript_v.absorb_point(&cm).unwrap();
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_v.absorb_nonnative(&cm);
let challenge = transcript_v.get_challenge(); // challenge value at which we evaluate let challenge = transcript_v.get_challenge(); // challenge value at which we evaluate
let s = transcript_v.get_challenge(); let s = transcript_v.get_challenge();
let U = Projective::generator().mul(s); let U = Projective::generator().mul(s);
let mut u: Vec<Fr> = vec![Fr::zero(); k]; let mut u: Vec<Fr> = vec![Fr::zero(); k];
for i in (0..k).rev() { for i in (0..k).rev() {
transcript_v.absorb_point(&proof.0.L[i]).unwrap();
transcript_v.absorb_point(&proof.0.R[i]).unwrap();
transcript_v.absorb_nonnative(&proof.0.L[i]);
transcript_v.absorb_nonnative(&proof.0.R[i]);
u[i] = transcript_v.get_challenge(); u[i] = transcript_v.get_challenge();
} }

+ 8
- 7
folding-schemes/src/commitment/kzg.rs

@ -156,13 +156,13 @@ where
/// the Pairing trait. /// the Pairing trait.
fn prove( fn prove(
params: &Self::ProverParams, params: &Self::ProverParams,
transcript: &mut impl Transcript<E::G1>,
transcript: &mut impl Transcript<E::ScalarField>,
cm: &E::G1, cm: &E::G1,
v: &[E::ScalarField], v: &[E::ScalarField],
_blind: &E::ScalarField, _blind: &E::ScalarField,
_rng: Option<&mut dyn RngCore>, _rng: Option<&mut dyn RngCore>,
) -> Result<Self::Proof, Error> { ) -> Result<Self::Proof, Error> {
transcript.absorb_point(cm)?;
transcript.absorb_nonnative(cm);
let challenge = transcript.get_challenge(); let challenge = transcript.get_challenge();
Self::prove_with_challenge(params, challenge, v, _blind, _rng) Self::prove_with_challenge(params, challenge, v, _blind, _rng)
} }
@ -214,11 +214,11 @@ where
fn verify( fn verify(
params: &Self::VerifierParams, params: &Self::VerifierParams,
transcript: &mut impl Transcript<E::G1>,
transcript: &mut impl Transcript<E::ScalarField>,
cm: &E::G1, cm: &E::G1,
proof: &Self::Proof, proof: &Self::Proof,
) -> Result<(), Error> { ) -> Result<(), Error> {
transcript.absorb_point(cm)?;
transcript.absorb_nonnative(cm);
let challenge = transcript.get_challenge(); let challenge = transcript.get_challenge();
Self::verify_with_challenge(params, challenge, cm, proof) Self::verify_with_challenge(params, challenge, cm, proof)
} }
@ -286,17 +286,18 @@ fn convert_to_bigints(p: &[F]) -> Vec {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ark_bn254::{Bn254, Fr, G1Projective as G1}; use ark_bn254::{Bn254, Fr, G1Projective as G1};
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};
use super::*; use super::*;
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
use crate::transcript::poseidon::poseidon_canonical_config;
#[test] #[test]
fn test_kzg_commitment_scheme() { fn test_kzg_commitment_scheme() {
let mut rng = &mut test_rng(); let mut rng = &mut test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_p = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
let transcript_v = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
let n = 10; let n = 10;
let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) =

+ 9
- 9
folding-schemes/src/commitment/mod.rs

@ -34,7 +34,7 @@ pub trait CommitmentScheme: Clone + Debug
fn prove( fn prove(
params: &Self::ProverParams, params: &Self::ProverParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
cm: &C, cm: &C,
v: &[C::ScalarField], v: &[C::ScalarField],
blind: &C::ScalarField, blind: &C::ScalarField,
@ -53,7 +53,7 @@ pub trait CommitmentScheme: Clone + Debug
fn verify( fn verify(
params: &Self::VerifierParams, params: &Self::VerifierParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
cm: &C, cm: &C,
proof: &Self::Proof, proof: &Self::Proof,
) -> Result<(), Error>; ) -> Result<(), Error>;
@ -72,7 +72,10 @@ pub trait CommitmentScheme: Clone + Debug
mod tests { mod tests {
use super::*; use super::*;
use ark_bn254::{Bn254, Fr, G1Projective as G1}; use ark_bn254::{Bn254, Fr, G1Projective as G1};
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_crypto_primitives::sponge::{
poseidon::{PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge,
};
use ark_poly_commit::kzg10::VerifierKey; use ark_poly_commit::kzg10::VerifierKey;
use ark_std::Zero; use ark_std::Zero;
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};
@ -80,10 +83,7 @@ mod tests {
use super::ipa::IPA; use super::ipa::IPA;
use super::kzg::{ProverKey, KZG}; use super::kzg::{ProverKey, KZG};
use super::pedersen::Pedersen; use super::pedersen::Pedersen;
use crate::transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript},
Transcript,
};
use crate::transcript::poseidon::poseidon_canonical_config;
#[test] #[test]
fn test_homomorphic_property_using_Commitment_trait() { fn test_homomorphic_property_using_Commitment_trait() {
@ -153,7 +153,7 @@ mod tests {
let v_3: Vec<C::ScalarField> = v_1.iter().zip(v_2).map(|(a, b)| *a + (r * b)).collect(); let v_3: Vec<C::ScalarField> = v_1.iter().zip(v_2).map(|(a, b)| *a + (r * b)).collect();
// compute the proof of the cm_3 // compute the proof of the cm_3
let transcript_p = &mut PoseidonTranscript::<C>::new(poseidon_config);
let transcript_p = &mut PoseidonSponge::<C::ScalarField>::new(poseidon_config);
let proof = CS::prove( let proof = CS::prove(
prover_params, prover_params,
transcript_p, transcript_p,
@ -165,7 +165,7 @@ mod tests {
.unwrap(); .unwrap();
// verify the opening proof // verify the opening proof
let transcript_v = &mut PoseidonTranscript::<C>::new(poseidon_config);
let transcript_v = &mut PoseidonSponge::<C::ScalarField>::new(poseidon_config);
CS::verify(verifier_params, transcript_v, &cm_3, &proof).unwrap(); CS::verify(verifier_params, transcript_v, &cm_3, &proof).unwrap();
} }
} }

+ 10
- 10
folding-schemes/src/commitment/pedersen.rs

@ -81,13 +81,13 @@ impl CommitmentScheme for Pedersen {
fn prove( fn prove(
params: &Self::ProverParams, params: &Self::ProverParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
cm: &C, cm: &C,
v: &[C::ScalarField], v: &[C::ScalarField],
r: &C::ScalarField, // blinding factor r: &C::ScalarField, // blinding factor
_rng: Option<&mut dyn RngCore>, _rng: Option<&mut dyn RngCore>,
) -> Result<Self::Proof, Error> { ) -> Result<Self::Proof, Error> {
transcript.absorb_point(cm)?;
transcript.absorb_nonnative(cm);
let r1 = transcript.get_challenge(); let r1 = transcript.get_challenge();
let d = transcript.get_challenges(v.len()); let d = transcript.get_challenges(v.len());
@ -98,7 +98,7 @@ impl CommitmentScheme for Pedersen {
R += params.h.mul(r1); R += params.h.mul(r1);
} }
transcript.absorb_point(&R)?;
transcript.absorb_nonnative(&R);
let e = transcript.get_challenge(); let e = transcript.get_challenge();
let challenge = (r1, d, R, e); let challenge = (r1, d, R, e);
@ -133,14 +133,14 @@ impl CommitmentScheme for Pedersen {
fn verify( fn verify(
params: &Self::VerifierParams, params: &Self::VerifierParams,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
cm: &C, cm: &C,
proof: &Proof<C>, proof: &Proof<C>,
) -> Result<(), Error> { ) -> Result<(), Error> {
transcript.absorb_point(cm)?;
transcript.absorb_nonnative(cm);
transcript.get_challenge(); // r_1 transcript.get_challenge(); // r_1
transcript.get_challenges(proof.u.len()); // d transcript.get_challenges(proof.u.len()); // d
transcript.absorb_point(&proof.R)?;
transcript.absorb_nonnative(&proof.R);
let e = transcript.get_challenge(); let e = transcript.get_challenge();
Self::verify_with_challenge(params, e, cm, proof) Self::verify_with_challenge(params, e, cm, proof)
} }
@ -217,14 +217,14 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand;
use super::*; use super::*;
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
use crate::transcript::poseidon::poseidon_canonical_config;
#[test] #[test]
fn test_pedersen() { fn test_pedersen() {
@ -240,9 +240,9 @@ mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
// init Prover's transcript // init Prover's transcript
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
.take(n) .take(n)

+ 54
- 92
folding-schemes/src/folding/circuits/cyclefold.rs

@ -1,23 +1,13 @@
/// Contains [CycleFold](https://eprint.iacr.org/2023/1192.pdf) related circuits and functions that /// Contains [CycleFold](https://eprint.iacr.org/2023/1192.pdf) related circuits and functions that
/// are shared across the different folding schemes /// are shared across the different folding schemes
use ark_crypto_primitives::{
crh::{
poseidon::constraints::{CRHGadget, CRHParametersVar},
CRHSchemeGadget,
},
sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge,
},
};
use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::{BigInteger, Field, PrimeField, ToConstraintField};
use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge};
use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode}, alloc::{AllocVar, AllocationMode},
boolean::Boolean, boolean::Boolean,
eq::EqGadget, eq::EqGadget,
fields::{fp::FpVar, FieldVar},
fields::fp::FpVar,
groups::GroupOpsBounds, groups::GroupOpsBounds,
prelude::CurveVar, prelude::CurveVar,
ToConstraintFieldGadget, ToConstraintFieldGadget,
@ -26,7 +16,7 @@ use ark_relations::r1cs::{
ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError,
}; };
use ark_std::fmt::Debug; use ark_std::fmt::Debug;
use ark_std::{One, Zero};
use ark_std::Zero;
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use super::{nonnative::uint::NonNativeUintVar, CF2}; use super::{nonnative::uint::NonNativeUintVar, CF2};
@ -35,6 +25,7 @@ use crate::commitment::CommitmentScheme;
use crate::constants::N_BITS_RO; use crate::constants::N_BITS_RO;
use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness}; use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness};
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar};
use crate::Error; use crate::Error;
/// Public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]| /// Public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]|
@ -77,7 +68,7 @@ where
} }
} }
impl<C, GC> ToConstraintFieldGadget<CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
impl<C, GC> AbsorbNonNativeGadget<C::BaseField> for CycleFoldCommittedInstanceVar<C, GC>
where where
C: CurveGroup, C: CurveGroup,
GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>, GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>,
@ -87,25 +78,24 @@ where
/// Extracts the underlying field elements from `CycleFoldCommittedInstanceVar`, in the order /// Extracts the underlying field elements from `CycleFoldCommittedInstanceVar`, in the order
/// of `u`, `x`, `cmE.x`, `cmE.y`, `cmW.x`, `cmW.y`, `cmE.is_inf || cmW.is_inf` (|| is for /// of `u`, `x`, `cmE.x`, `cmE.y`, `cmW.x`, `cmW.y`, `cmE.is_inf || cmW.is_inf` (|| is for
/// concat). /// concat).
fn to_constraint_field(&self) -> Result<Vec<FpVar<CF2<C>>>, SynthesisError> {
fn to_native_sponge_field_elements(&self) -> Result<Vec<FpVar<CF2<C>>>, SynthesisError> {
let mut cmE_elems = self.cmE.to_constraint_field()?; let mut cmE_elems = self.cmE.to_constraint_field()?;
let mut cmW_elems = self.cmW.to_constraint_field()?; let mut cmW_elems = self.cmW.to_constraint_field()?;
let cmE_is_inf = cmE_elems.pop().unwrap();
let cmW_is_inf = cmW_elems.pop().unwrap();
// Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate
let is_inf = cmE_is_inf.double()? + cmW_is_inf;
// See `transcript/poseidon.rs: TranscriptVar::absorb_point` for details
// why the last element is unnecessary.
cmE_elems.pop();
cmW_elems.pop();
Ok([ Ok([
self.u.to_constraint_field()?,
self.u.to_native_sponge_field_elements()?,
self.x self.x
.iter() .iter()
.map(|i| i.to_constraint_field())
.map(|i| i.to_native_sponge_field_elements())
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?
.concat(), .concat(),
cmE_elems, cmE_elems,
cmW_elems, cmW_elems,
vec![is_inf],
] ]
.concat()) .concat())
} }
@ -124,16 +114,16 @@ where
/// parameters, so they can be reused in other gadgets avoiding recalculating (reconstraining) /// parameters, so they can be reused in other gadgets avoiding recalculating (reconstraining)
/// them. /// them.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn hash(
pub fn hash<S: CryptographicSponge, T: TranscriptVar<CF2<C>, S>>(
self, self,
crh_params: &CRHParametersVar<CF2<C>>,
sponge: &T,
pp_hash: FpVar<CF2<C>>, // public params hash pp_hash: FpVar<CF2<C>>, // public params hash
) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), SynthesisError> { ) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), SynthesisError> {
let U_vec = self.to_constraint_field()?;
Ok((
CRHGadget::evaluate(crh_params, &[vec![pp_hash], U_vec.clone()].concat())?,
U_vec,
))
let mut sponge = sponge.clone();
let U_vec = self.to_native_sponge_field_elements()?;
sponge.absorb(&pp_hash)?;
sponge.absorb(&U_vec)?;
Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec))
} }
} }
@ -254,65 +244,33 @@ where
<C as CurveGroup>::BaseField: Absorb, <C as CurveGroup>::BaseField: Absorb,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
pub fn get_challenge_native(
poseidon_config: &PoseidonConfig<C::BaseField>,
pub fn get_challenge_native<T: Transcript<C::BaseField>>(
transcript: &mut T,
pp_hash: C::BaseField, // public params hash pp_hash: C::BaseField, // public params hash
U_i: CommittedInstance<C>, U_i: CommittedInstance<C>,
u_i: CommittedInstance<C>, u_i: CommittedInstance<C>,
cmT: C, cmT: C,
) -> Result<Vec<bool>, Error> {
let mut sponge = PoseidonSponge::<C::BaseField>::new(poseidon_config);
let mut U_vec = U_i.to_field_elements().unwrap();
let mut u_vec = u_i.to_field_elements().unwrap();
let (cmT_x, cmT_y, cmT_is_inf) = match cmT.into_affine().xy() {
Some((&x, &y)) => (x, y, C::BaseField::zero()),
None => (
C::BaseField::zero(),
C::BaseField::zero(),
C::BaseField::one(),
),
};
let U_cm_is_inf = U_vec.pop().unwrap();
let u_cm_is_inf = u_vec.pop().unwrap();
// Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf`
// to save constraints for sponge.squeeze_bits in the corresponding circuit
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double() + cmT_is_inf;
let input = [vec![pp_hash], U_vec, u_vec, vec![cmT_x, cmT_y, is_inf]].concat();
sponge.absorb(&input);
let bits = sponge.squeeze_bits(N_BITS_RO);
Ok(bits)
) -> Vec<bool> {
transcript.absorb(&pp_hash);
transcript.absorb_nonnative(&U_i);
transcript.absorb_nonnative(&u_i);
transcript.absorb_point(&cmT);
transcript.squeeze_bits(N_BITS_RO)
} }
// compatible with the native get_challenge_native // compatible with the native get_challenge_native
pub fn get_challenge_gadget(
cs: ConstraintSystemRef<C::BaseField>,
poseidon_config: &PoseidonConfig<C::BaseField>,
pub fn get_challenge_gadget<S: CryptographicSponge, T: TranscriptVar<C::BaseField, S>>(
transcript: &mut T,
pp_hash: FpVar<C::BaseField>, // public params hash pp_hash: FpVar<C::BaseField>, // public params hash
mut U_i_vec: Vec<FpVar<C::BaseField>>,
U_i_vec: Vec<FpVar<C::BaseField>>,
u_i: CycleFoldCommittedInstanceVar<C, GC>, u_i: CycleFoldCommittedInstanceVar<C, GC>,
cmT: GC, cmT: GC,
) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> { ) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> {
let mut sponge = PoseidonSpongeVar::<C::BaseField>::new(cs, poseidon_config);
let mut u_i_vec = u_i.to_constraint_field()?;
let mut cmT_vec = cmT.to_constraint_field()?;
let U_cm_is_inf = U_i_vec.pop().unwrap();
let u_cm_is_inf = u_i_vec.pop().unwrap();
let cmT_is_inf = cmT_vec.pop().unwrap();
// Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf`
// to save constraints for sponge.squeeze_bits
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double()? + cmT_is_inf;
let input = [vec![pp_hash], U_i_vec, u_i_vec, cmT_vec, vec![is_inf]].concat();
sponge.absorb(&input)?;
let bits = sponge.squeeze_bits(N_BITS_RO)?;
Ok(bits)
transcript.absorb(&pp_hash)?;
transcript.absorb(&U_i_vec)?;
transcript.absorb_nonnative(&u_i)?;
transcript.absorb_point(&cmT)?;
transcript.squeeze_bits(N_BITS_RO)
} }
} }
@ -383,7 +341,7 @@ where
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2>( pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2>(
poseidon_config: &PoseidonConfig<C1::ScalarField>,
transcript: &mut impl Transcript<C1::ScalarField>,
cf_r1cs: R1CS<C2::ScalarField>, cf_r1cs: R1CS<C2::ScalarField>,
cf_cs_params: CS2::ProverParams, cf_cs_params: CS2::ProverParams,
pp_hash: C1::ScalarField, // public params hash pp_hash: C1::ScalarField, // public params hash
@ -445,12 +403,12 @@ where
)?; )?;
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native( let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
poseidon_config,
transcript,
pp_hash, pp_hash,
cf_U_i.clone(), cf_U_i.clone(),
cf_u_i.clone(), cf_u_i.clone(),
cf_cmT, cf_cmT,
)?;
);
let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits)) let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
.expect("cf_r_bits out of bounds"); .expect("cf_r_bits out of bounds");
@ -463,9 +421,11 @@ where
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective}; use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective};
use ark_ff::BigInteger;
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge},
};
use ark_r1cs_std::R1CSVar; use ark_r1cs_std::R1CSVar;
use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand; use ark_std::UniformRand;
use super::*; use super::*;
@ -583,6 +543,7 @@ pub mod tests {
fn test_cyclefold_challenge_gadget() { fn test_cyclefold_challenge_gadget() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fq>(); let poseidon_config = poseidon_canonical_config::<Fq>();
let mut transcript = PoseidonSponge::<Fq>::new(&poseidon_config);
let u_i = CommittedInstance::<Projective> { let u_i = CommittedInstance::<Projective> {
cmE: Projective::zero(), // zero on purpose, so we test also the zero point case cmE: Projective::zero(), // zero on purpose, so we test also the zero point case
@ -605,13 +566,12 @@ pub mod tests {
// compute the challenge natively // compute the challenge natively
let pp_hash = Fq::from(42u32); // only for test let pp_hash = Fq::from(42u32); // only for test
let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native( let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native(
&poseidon_config,
&mut transcript,
pp_hash, pp_hash,
U_i.clone(), U_i.clone(),
u_i.clone(), u_i.clone(),
cmT, cmT,
)
.unwrap();
);
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let u_iVar = let u_iVar =
@ -625,13 +585,14 @@ pub mod tests {
}) })
.unwrap(); .unwrap();
let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap(); let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap();
let mut transcript_var =
PoseidonSpongeVar::<Fq>::new(ConstraintSystem::<Fq>::new_ref(), &poseidon_config);
let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget( let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget(
cs.clone(),
&poseidon_config,
&mut transcript_var,
pp_hashVar, pp_hashVar,
U_iVar.to_constraint_field().unwrap(),
U_iVar.to_native_sponge_field_elements().unwrap(),
u_iVar, u_iVar,
cmTVar, cmTVar,
) )
@ -649,6 +610,7 @@ pub mod tests {
fn test_cyclefold_hash_gadget() { fn test_cyclefold_hash_gadget() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fq>(); let poseidon_config = poseidon_canonical_config::<Fq>();
let sponge = PoseidonSponge::<Fq>::new(&poseidon_config);
let U_i = CommittedInstance::<Projective> { let U_i = CommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng), cmE: Projective::rand(&mut rng),
@ -659,7 +621,7 @@ pub mod tests {
.collect(), .collect(),
}; };
let pp_hash = Fq::from(42u32); // only for test let pp_hash = Fq::from(42u32); // only for test
let h = U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
let h = U_i.hash_cyclefold(&sponge, pp_hash);
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let U_iVar = let U_iVar =
@ -670,7 +632,7 @@ pub mod tests {
let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let (hVar, _) = U_iVar let (hVar, _) = U_iVar
.hash( .hash(
&CRHParametersVar::new_constant(cs.clone(), poseidon_config).unwrap(),
&PoseidonSpongeVar::new(cs.clone(), &poseidon_config),
pp_hashVar, pp_hashVar,
) )
.unwrap(); .unwrap();

+ 34
- 40
folding-schemes/src/folding/circuits/nonnative/affine.rs

@ -1,5 +1,4 @@
use ark_ec::{AffineRepr, CurveGroup}; use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::PrimeField;
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode}, alloc::{AllocVar, AllocationMode},
fields::fp::FpVar, fields::fp::FpVar,
@ -9,16 +8,15 @@ use ark_relations::r1cs::{Namespace, SynthesisError};
use ark_std::Zero; use ark_std::Zero;
use core::borrow::Borrow; use core::borrow::Borrow;
use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget};
use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar}; use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native /// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
/// field, over the constraint field. It is not intended to perform operations, but just to contain /// field, over the constraint field. It is not intended to perform operations, but just to contain
/// the affine coordinates in order to perform hash operations of the point. /// the affine coordinates in order to perform hash operations of the point.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NonNativeAffineVar<C: CurveGroup>
where
<C as CurveGroup>::BaseField: PrimeField,
{
pub struct NonNativeAffineVar<C: CurveGroup> {
pub x: NonNativeUintVar<C::ScalarField>, pub x: NonNativeUintVar<C::ScalarField>,
pub y: NonNativeUintVar<C::ScalarField>, pub y: NonNativeUintVar<C::ScalarField>,
} }
@ -26,7 +24,6 @@ where
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C> impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
where where
C: CurveGroup, C: CurveGroup,
<C as CurveGroup>::BaseField: PrimeField,
{ {
fn new_variable<T: Borrow<C>>( fn new_variable<T: Borrow<C>>(
cs: impl Into<Namespace<C::ScalarField>>, cs: impl Into<Namespace<C::ScalarField>>,
@ -37,21 +34,18 @@ where
let cs = cs.into(); let cs = cs.into();
let affine = val.borrow().into_affine(); let affine = val.borrow().into_affine();
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
let xy = affine.xy().unwrap_or(zero_point);
let zero = (&C::BaseField::zero(), &C::BaseField::zero());
let (x, y) = affine.xy().unwrap_or(zero);
let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.0), mode)?;
let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.1), mode)?;
let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*x), mode)?;
let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*y), mode)?;
Ok(Self { x, y }) Ok(Self { x, y })
}) })
} }
} }
impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C>
where
<C as CurveGroup>::BaseField: PrimeField,
{
impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C> {
// Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
// the circuit. // the circuit.
fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> { fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
@ -63,50 +57,50 @@ where
/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field` /// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn nonnative_affine_to_field_elements<C: CurveGroup>(
fn nonnative_affine_to_field_elements<C: CurveGroup>(
p: C, p: C,
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
where
<C as CurveGroup>::BaseField: PrimeField,
{
) -> (Vec<C::ScalarField>, Vec<C::ScalarField>) {
let affine = p.into_affine(); let affine = p.into_affine();
if affine.is_zero() {
let x = nonnative_field_to_field_elements(&C::BaseField::zero());
let y = nonnative_field_to_field_elements(&C::BaseField::zero());
return Ok((x, y));
}
let zero = (&C::BaseField::zero(), &C::BaseField::zero());
let (x, y) = affine.xy().unwrap_or(zero);
let (x, y) = affine.xy().unwrap();
let x = nonnative_field_to_field_elements(x); let x = nonnative_field_to_field_elements(x);
let y = nonnative_field_to_field_elements(y); let y = nonnative_field_to_field_elements(y);
Ok((x, y))
(x, y)
} }
impl<C: CurveGroup> NonNativeAffineVar<C>
where
<C as CurveGroup>::BaseField: PrimeField,
{
// A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization
// type (which is the default optimization type for arkworks' Groth16).
// Used for extracting a list of field elements of type `C::ScalarField` from the public input
impl<C: CurveGroup> NonNativeAffineVar<C> {
// Extracts a list of field elements of type `C::ScalarField` from the public input
// `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
// `FpVar` in-circuit. // `FpVar` in-circuit.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> { pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
let affine = p.into_affine(); let affine = p.into_affine();
if affine.is_zero() {
let x = NonNativeUintVar::inputize(C::BaseField::zero());
let y = NonNativeUintVar::inputize(C::BaseField::zero());
return Ok((x, y));
}
let zero = (&C::BaseField::zero(), &C::BaseField::zero());
let (x, y) = affine.xy().unwrap_or(zero);
let (x, y) = affine.xy().unwrap();
let x = NonNativeUintVar::inputize(*x); let x = NonNativeUintVar::inputize(*x);
let y = NonNativeUintVar::inputize(*y); let y = NonNativeUintVar::inputize(*y);
Ok((x, y)) Ok((x, y))
} }
} }
impl<C: CurveGroup> AbsorbNonNative<C::ScalarField> for C {
fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::ScalarField>) {
let (x, y) = nonnative_affine_to_field_elements(*self);
dest.extend(x);
dest.extend(y);
}
}
impl<C: CurveGroup> AbsorbNonNativeGadget<C::ScalarField> for NonNativeAffineVar<C> {
fn to_native_sponge_field_elements(
&self,
) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
self.to_constraint_field()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -132,7 +126,7 @@ mod tests {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let p = Projective::rand(&mut rng); let p = Projective::rand(&mut rng);
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap(); let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
let (x, y) = nonnative_affine_to_field_elements(p).unwrap();
let (x, y) = nonnative_affine_to_field_elements(p);
assert_eq!( assert_eq!(
pVar.to_constraint_field().unwrap().value().unwrap(), pVar.to_constraint_field().unwrap().value().unwrap(),
[x, y].concat() [x, y].concat()

+ 43
- 10
folding-schemes/src/folding/circuits/nonnative/uint.rs

@ -3,7 +3,7 @@ use std::{
cmp::{max, min}, cmp::{max, min},
}; };
use ark_ff::{BigInteger, One, PrimeField, Zero};
use ark_ff::{BigInteger, Field, One, PrimeField, Zero};
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode}, alloc::{AllocVar, AllocationMode},
boolean::Boolean, boolean::Boolean,
@ -16,7 +16,10 @@ use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use num_bigint::BigUint; use num_bigint::BigUint;
use num_integer::Integer; use num_integer::Integer;
use crate::utils::gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget};
use crate::{
transcript::{AbsorbNonNative, AbsorbNonNativeGadget},
utils::gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget},
};
/// `LimbVar` represents a single limb of a non-native unsigned integer in the /// `LimbVar` represents a single limb of a non-native unsigned integer in the
/// circuit. /// circuit.
@ -229,7 +232,7 @@ impl AllocVar for NonNativeUintVar {
} }
} }
impl<F: PrimeField, G: PrimeField> AllocVar<G, F> for NonNativeUintVar<F> {
impl<F: PrimeField, G: Field> AllocVar<G, F> for NonNativeUintVar<F> {
fn new_variable<T: Borrow<G>>( fn new_variable<T: Borrow<G>>(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
@ -237,7 +240,8 @@ impl AllocVar for NonNativeUintVar {
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
let cs = cs.into().cs(); let cs = cs.into().cs();
let v = f()?; let v = f()?;
let v = v.borrow();
assert_eq!(G::extension_degree(), 1);
let v = v.borrow().to_base_prime_field_elements().next().unwrap();
let mut limbs = vec![]; let mut limbs = vec![];
@ -256,8 +260,12 @@ impl AllocVar for NonNativeUintVar {
} }
impl<F: PrimeField> NonNativeUintVar<F> { impl<F: PrimeField> NonNativeUintVar<F> {
pub fn inputize<T: PrimeField>(x: T) -> Vec<F> {
x.into_bigint()
pub fn inputize<T: Field>(x: T) -> Vec<F> {
assert_eq!(T::extension_degree(), 1);
x.to_base_prime_field_elements()
.next()
.unwrap()
.into_bigint()
.to_bits_le() .to_bits_le()
.chunks(Self::bits_per_limb()) .chunks(Self::bits_per_limb())
.map(|chunk| F::from_bigint(F::BigInt::from_bits_le(chunk)).unwrap()) .map(|chunk| F::from_bigint(F::BigInt::from_bits_le(chunk)).unwrap())
@ -802,14 +810,40 @@ impl]>> From for NonNativeUintVar {
} }
} }
// If we impl `AbsorbNonNative` directly for `PrimeField`, rustc will complain
// that this impl conflicts with the impl for `CurveGroup`.
// Therefore, we instead impl `AbsorbNonNative` for a slice of `PrimeField` as a
// workaround.
impl<TargetField: PrimeField, BaseField: PrimeField> AbsorbNonNative<BaseField>
for [TargetField]
{
fn to_native_sponge_field_elements(&self, dest: &mut Vec<BaseField>) {
self.iter()
.for_each(|x| dest.extend(&nonnative_field_to_field_elements(x)));
}
}
impl<F: PrimeField> AbsorbNonNativeGadget<F> for NonNativeUintVar<F> {
fn to_native_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
self.to_constraint_field()
}
}
/// The out-circuit counterpart of `NonNativeUintVar::to_constraint_field` /// The out-circuit counterpart of `NonNativeUintVar::to_constraint_field`
pub fn nonnative_field_to_field_elements<TargetField: PrimeField, BaseField: PrimeField>(
pub(super) fn nonnative_field_to_field_elements<TargetField: Field, BaseField: PrimeField>(
f: &TargetField, f: &TargetField,
) -> Vec<BaseField> { ) -> Vec<BaseField> {
let bits = f.into_bigint().to_bits_le();
assert_eq!(TargetField::extension_degree(), 1);
let bits = f
.to_base_prime_field_elements()
.next()
.unwrap()
.into_bigint()
.to_bits_le();
let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1; let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1;
let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
let num_limbs =
(TargetField::BasePrimeField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
let mut limbs = bits let mut limbs = bits
.chunks(bits_per_limb) .chunks(bits_per_limb)
@ -897,7 +931,6 @@ mod tests {
use std::error::Error; use std::error::Error;
use super::*; use super::*;
use ark_ff::Field;
use ark_pallas::{Fq, Fr}; use ark_pallas::{Fq, Fr};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};

+ 46
- 70
folding-schemes/src/folding/circuits/sum_check.rs

@ -3,8 +3,7 @@
/// - Typings to better stick to ark_poly's API /// - Typings to better stick to ark_poly's API
/// - Uses `folding-schemes`' own `TranscriptVar` trait and `PoseidonTranscriptVar` struct /// - Uses `folding-schemes`' own `TranscriptVar` trait and `PoseidonTranscriptVar` struct
/// - API made closer to gadgets found in `folding-schemes` /// - API made closer to gadgets found in `folding-schemes`
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group};
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, Absorb, CryptographicSponge};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
use ark_r1cs_std::{ use ark_r1cs_std::{
@ -19,7 +18,7 @@ use std::{borrow::Borrow, marker::PhantomData};
use crate::utils::espresso::sum_check::SumCheck; use crate::utils::espresso::sum_check::SumCheck;
use crate::utils::virtual_polynomial::VPAuxInfo; use crate::utils::virtual_polynomial::VPAuxInfo;
use crate::{ use crate::{
transcript::{poseidon::PoseidonTranscript, TranscriptVar},
transcript::TranscriptVar,
utils::sum_check::{structs::IOPProof, IOPSumCheck}, utils::sum_check::{structs::IOPProof, IOPSumCheck},
}; };
@ -82,35 +81,27 @@ impl DensePolynomialVar {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct IOPProofVar<C: CurveGroup> {
pub struct IOPProofVar<F: PrimeField> {
// We have to be generic over a CurveGroup because instantiating a IOPProofVar will call IOPSumCheck which requires a CurveGroup // We have to be generic over a CurveGroup because instantiating a IOPProofVar will call IOPSumCheck which requires a CurveGroup
pub proofs: Vec<DensePolynomialVar<C::ScalarField>>, // = IOPProof.proofs
pub claim: FpVar<C::ScalarField>,
pub proofs: Vec<DensePolynomialVar<F>>,
pub claim: FpVar<F>,
} }
impl<C: CurveGroup> AllocVar<IOPProof<C::ScalarField>, C::ScalarField> for IOPProofVar<C>
where
<C as Group>::ScalarField: Absorb,
{
fn new_variable<T: Borrow<IOPProof<C::ScalarField>>>(
cs: impl Into<Namespace<C::ScalarField>>,
impl<F: PrimeField + Absorb> AllocVar<IOPProof<F>, F> for IOPProofVar<F> {
fn new_variable<T: Borrow<IOPProof<F>>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode, mode: AllocationMode,
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
f().and_then(|c| { f().and_then(|c| {
let cs = cs.into(); let cs = cs.into();
let cp: &IOPProof<C::ScalarField> = c.borrow();
let claim = IOPSumCheck::<C, PoseidonTranscript<C>>::extract_sum(cp);
let claim = FpVar::<C::ScalarField>::new_variable(cs.clone(), || Ok(claim), mode)?;
let mut proofs =
Vec::<DensePolynomialVar<C::ScalarField>>::with_capacity(cp.proofs.len());
let cp: &IOPProof<F> = c.borrow();
let claim = IOPSumCheck::<F, PoseidonSponge<F>>::extract_sum(cp);
let claim = FpVar::<F>::new_variable(cs.clone(), || Ok(claim), mode)?;
let mut proofs = Vec::<DensePolynomialVar<F>>::with_capacity(cp.proofs.len());
for proof in cp.proofs.iter() { for proof in cp.proofs.iter() {
let poly = DensePolynomial::from_coefficients_slice(&proof.coeffs); let poly = DensePolynomial::from_coefficients_slice(&proof.coeffs);
let proof = DensePolynomialVar::<C::ScalarField>::new_variable(
cs.clone(),
|| Ok(poly),
mode,
)?;
let proof = DensePolynomialVar::<F>::new_variable(cs.clone(), || Ok(poly), mode)?;
proofs.push(proof); proofs.push(proof);
} }
Ok(Self { proofs, claim }) Ok(Self { proofs, claim })
@ -149,28 +140,28 @@ impl AllocVar, F> for VPAuxInfoVar {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SumCheckVerifierGadget<C: CurveGroup> {
_f: PhantomData<C>,
pub struct SumCheckVerifierGadget<F: PrimeField> {
_f: PhantomData<F>,
} }
impl<C: CurveGroup> SumCheckVerifierGadget<C> {
impl<F: PrimeField> SumCheckVerifierGadget<F> {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn verify(
iop_proof_var: &IOPProofVar<C>,
poly_aux_info_var: &VPAuxInfoVar<C::ScalarField>,
transcript_var: &mut impl TranscriptVar<C::ScalarField>,
enabled: Boolean<C::ScalarField>,
) -> Result<(Vec<FpVar<C::ScalarField>>, Vec<FpVar<C::ScalarField>>), SynthesisError> {
pub fn verify<S: CryptographicSponge, T: TranscriptVar<F, S>>(
iop_proof_var: &IOPProofVar<F>,
poly_aux_info_var: &VPAuxInfoVar<F>,
transcript_var: &mut T,
enabled: Boolean<F>,
) -> Result<(Vec<FpVar<F>>, Vec<FpVar<F>>), SynthesisError> {
let mut e_vars = vec![iop_proof_var.claim.clone()]; let mut e_vars = vec![iop_proof_var.claim.clone()];
let mut r_vars: Vec<FpVar<C::ScalarField>> = Vec::new();
transcript_var.absorb(poly_aux_info_var.num_variables.clone())?;
transcript_var.absorb(poly_aux_info_var.max_degree.clone())?;
let mut r_vars: Vec<FpVar<F>> = Vec::new();
transcript_var.absorb(&poly_aux_info_var.num_variables)?;
transcript_var.absorb(&poly_aux_info_var.max_degree)?;
for poly_var in iop_proof_var.proofs.iter() { for poly_var in iop_proof_var.proofs.iter() {
let res = poly_var.eval_at_one() + poly_var.eval_at_zero(); let res = poly_var.eval_at_one() + poly_var.eval_at_zero();
let e_var = e_vars.last().ok_or(SynthesisError::Unsatisfiable)?; let e_var = e_vars.last().ok_or(SynthesisError::Unsatisfiable)?;
res.conditional_enforce_equal(e_var, &enabled)?; res.conditional_enforce_equal(e_var, &enabled)?;
transcript_var.absorb_vec(&poly_var.coeffs)?;
transcript_var.absorb(&poly_var.coeffs)?;
let r_i_var = transcript_var.get_challenge()?; let r_i_var = transcript_var.get_challenge()?;
e_vars.push(poly_var.evaluate(&r_i_var)); e_vars.push(poly_var.evaluate(&r_i_var));
r_vars.push(r_i_var); r_vars.push(r_i_var);
@ -182,49 +173,35 @@ impl SumCheckVerifierGadget {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_ec::CurveGroup;
use ark_ff::Field;
use ark_pallas::{Fr, Projective};
use ark_poly::{
univariate::DensePolynomial, DenseMultilinearExtension, DenseUVPolynomial,
MultilinearExtension, Polynomial,
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig},
}; };
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
use ark_pallas::Fr;
use ark_poly::{DenseMultilinearExtension, MultilinearExtension, Polynomial};
use ark_r1cs_std::R1CSVar;
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use std::sync::Arc; use std::sync::Arc;
use super::*; use super::*;
use crate::{ use crate::{
folding::circuits::sum_check::{IOPProofVar, VPAuxInfoVar},
transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar,
},
utils::{
sum_check::{structs::IOPProof, IOPSumCheck, SumCheck},
virtual_polynomial::VirtualPolynomial,
},
transcript::poseidon::poseidon_canonical_config,
utils::virtual_polynomial::VirtualPolynomial,
}; };
pub type TestSumCheckProof<F> = (VirtualPolynomial<F>, PoseidonConfig<F>, IOPProof<F>); pub type TestSumCheckProof<F> = (VirtualPolynomial<F>, PoseidonConfig<F>, IOPProof<F>);
/// Primarily used for testing the sumcheck gadget /// Primarily used for testing the sumcheck gadget
/// Returns a random virtual polynomial, the poseidon config used and the associated sumcheck proof /// Returns a random virtual polynomial, the poseidon config used and the associated sumcheck proof
pub fn get_test_sumcheck_proof<C: CurveGroup>(
pub fn get_test_sumcheck_proof<F: PrimeField + Absorb>(
num_vars: usize, num_vars: usize,
) -> TestSumCheckProof<C::ScalarField>
where
<C as ark_ec::Group>::ScalarField: Absorb,
{
) -> TestSumCheckProof<F> {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config: PoseidonConfig<C::ScalarField> =
poseidon_canonical_config::<C::ScalarField>();
let mut poseidon_transcript_prove = PoseidonTranscript::<C>::new(&poseidon_config);
let poseidon_config: PoseidonConfig<F> = poseidon_canonical_config::<F>();
let mut poseidon_transcript_prove = PoseidonSponge::<F>::new(&poseidon_config);
let poly_mle = DenseMultilinearExtension::rand(num_vars, &mut rng); let poly_mle = DenseMultilinearExtension::rand(num_vars, &mut rng);
let virtual_poly =
VirtualPolynomial::new_from_mle(&Arc::new(poly_mle), C::ScalarField::ONE);
let sum_check: IOPProof<C::ScalarField> = IOPSumCheck::<C, PoseidonTranscript<C>>::prove(
let virtual_poly = VirtualPolynomial::new_from_mle(&Arc::new(poly_mle), F::ONE);
let sum_check: IOPProof<F> = IOPSumCheck::<F, PoseidonSponge<F>>::prove(
&virtual_poly, &virtual_poly,
&mut poseidon_transcript_prove, &mut poseidon_transcript_prove,
) )
@ -237,15 +214,15 @@ mod tests {
for num_vars in 1..15 { for num_vars in 1..15 {
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let (virtual_poly, poseidon_config, sum_check) = let (virtual_poly, poseidon_config, sum_check) =
get_test_sumcheck_proof::<Projective>(num_vars);
let mut poseidon_var: PoseidonTranscriptVar<Fr> =
PoseidonTranscriptVar::new(cs.clone(), &poseidon_config);
get_test_sumcheck_proof::<Fr>(num_vars);
let mut poseidon_var: PoseidonSpongeVar<Fr> =
PoseidonSpongeVar::new(cs.clone(), &poseidon_config);
let iop_proof_var = let iop_proof_var =
IOPProofVar::<Projective>::new_witness(cs.clone(), || Ok(&sum_check)).unwrap();
IOPProofVar::<Fr>::new_witness(cs.clone(), || Ok(&sum_check)).unwrap();
let poly_aux_info_var = let poly_aux_info_var =
VPAuxInfoVar::<Fr>::new_witness(cs.clone(), || Ok(virtual_poly.aux_info)).unwrap(); VPAuxInfoVar::<Fr>::new_witness(cs.clone(), || Ok(virtual_poly.aux_info)).unwrap();
let enabled = Boolean::<Fr>::new_witness(cs.clone(), || Ok(true)).unwrap(); let enabled = Boolean::<Fr>::new_witness(cs.clone(), || Ok(true)).unwrap();
let res = SumCheckVerifierGadget::<Projective>::verify(
let res = SumCheckVerifierGadget::<Fr>::verify(
&iop_proof_var, &iop_proof_var,
&poly_aux_info_var, &poly_aux_info_var,
&mut poseidon_var, &mut poseidon_var,
@ -256,8 +233,7 @@ mod tests {
let (circuit_evals, r_challenges) = res.unwrap(); let (circuit_evals, r_challenges) = res.unwrap();
// 1. assert claim from circuit is equal to the one from the sum-check // 1. assert claim from circuit is equal to the one from the sum-check
let claim: Fr =
IOPSumCheck::<Projective, PoseidonTranscript<Projective>>::extract_sum(&sum_check);
let claim: Fr = IOPSumCheck::<Fr, PoseidonSponge<Fr>>::extract_sum(&sum_check);
assert_eq!(circuit_evals[0].value().unwrap(), claim); assert_eq!(circuit_evals[0].value().unwrap(), claim);
// 2. assert that all in-circuit evaluations are equal to the ones from the sum-check // 2. assert that all in-circuit evaluations are equal to the ones from the sum-check

+ 22
- 0
folding-schemes/src/folding/hypernova/cccs.rs

@ -1,3 +1,4 @@
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_std::One; use ark_std::One;
@ -9,6 +10,7 @@ use ark_std::rand::Rng;
use super::Witness; use super::Witness;
use crate::arith::{ccs::CCS, Arith}; use crate::arith::{ccs::CCS, Arith};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::transcript::AbsorbNonNative;
use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul; use crate::utils::vec::mat_vec_mul;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial}; use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
@ -118,6 +120,26 @@ impl CCCS {
} }
} }
impl<C: CurveGroup> Absorb for CCCS<C>
where
C::ScalarField: Absorb,
{
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
// This is never called
unimplemented!()
}
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
// We cannot call `to_native_sponge_field_elements(dest)` directly, as
// `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
// but here `F` is a generic `PrimeField`.
self.C
.to_native_sponge_field_elements_as_vec()
.to_sponge_field_elements(dest);
self.x.to_sponge_field_elements(dest);
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use ark_pallas::Fr; use ark_pallas::Fr;

+ 67
- 114
folding-schemes/src/folding/hypernova/circuits.rs

@ -1,7 +1,8 @@
/// Implementation of [HyperNova](https://eprint.iacr.org/2023/573.pdf) circuits /// Implementation of [HyperNova](https://eprint.iacr.org/2023/573.pdf) circuits
use ark_crypto_primitives::crh::{
poseidon::constraints::{CRHGadget, CRHParametersVar},
CRHSchemeGadget,
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge},
CryptographicSponge,
}; };
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_ec::{CurveGroup, Group}; use ark_ec::{CurveGroup, Group};
@ -45,10 +46,7 @@ use crate::utils::virtual_polynomial::VPAuxInfo;
use crate::Error; use crate::Error;
use crate::{ use crate::{
arith::{ccs::CCS, r1cs::extract_r1cs}, arith::{ccs::CCS, r1cs::extract_r1cs},
transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar,
},
transcript::TranscriptVar,
}; };
/// Committed CCS instance /// Committed CCS instance
@ -142,12 +140,13 @@ where
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn hash( pub fn hash(
self, self,
crh_params: &CRHParametersVar<CF1<C>>,
sponge: &PoseidonSpongeVar<CF1<C>>,
pp_hash: FpVar<CF1<C>>, pp_hash: FpVar<CF1<C>>,
i: FpVar<CF1<C>>, i: FpVar<CF1<C>>,
z_0: Vec<FpVar<CF1<C>>>, z_0: Vec<FpVar<CF1<C>>>,
z_i: Vec<FpVar<CF1<C>>>, z_i: Vec<FpVar<CF1<C>>>,
) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> { ) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> {
let mut sponge = sponge.clone();
let U_vec = [ let U_vec = [
self.C.to_constraint_field()?, self.C.to_constraint_field()?,
vec![self.u], vec![self.u],
@ -156,18 +155,19 @@ where
self.v, self.v,
] ]
.concat(); .concat();
let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat();
Ok((
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?,
U_vec,
))
sponge.absorb(&pp_hash)?;
sponge.absorb(&i)?;
sponge.absorb(&z_0)?;
sponge.absorb(&z_i)?;
sponge.absorb(&U_vec)?;
Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec))
} }
} }
/// ProofVar defines a multifolding proof /// ProofVar defines a multifolding proof
#[derive(Debug)] #[derive(Debug)]
pub struct ProofVar<C: CurveGroup> { pub struct ProofVar<C: CurveGroup> {
pub sc_proof: IOPProofVar<C>,
pub sc_proof: IOPProofVar<C::ScalarField>,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub sigmas_thetas: (Vec<Vec<FpVar<CF1<C>>>>, Vec<Vec<FpVar<CF1<C>>>>), pub sigmas_thetas: (Vec<Vec<FpVar<CF1<C>>>>, Vec<Vec<FpVar<CF1<C>>>>),
} }
@ -185,7 +185,7 @@ where
f().and_then(|val| { f().and_then(|val| {
let cs = cs.into(); let cs = cs.into();
let sc_proof = IOPProofVar::<C>::new_variable(
let sc_proof = IOPProofVar::<C::ScalarField>::new_variable(
cs.clone(), cs.clone(),
|| Ok(val.borrow().sc_proof.clone()), || Ok(val.borrow().sc_proof.clone()),
mode, mode,
@ -223,12 +223,11 @@ where
/// Runs (in-circuit) the NIMFS.V, which outputs the new folded LCCCS instance together with /// Runs (in-circuit) the NIMFS.V, which outputs the new folded LCCCS instance together with
/// the rho_bits, which will be used in other parts of the AugmentedFCircuit /// the rho_bits, which will be used in other parts of the AugmentedFCircuit
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn verify(
pub fn verify<S: CryptographicSponge, T: TranscriptVar<C::ScalarField, S>>(
cs: ConstraintSystemRef<CF1<C>>, cs: ConstraintSystemRef<CF1<C>>,
// only used the CCS params, not the matrices // only used the CCS params, not the matrices
ccs: &CCS<C::ScalarField>, ccs: &CCS<C::ScalarField>,
mut transcript: impl TranscriptVar<C::ScalarField>,
transcript: &mut T,
running_instances: &[LCCCSVar<C>], running_instances: &[LCCCSVar<C>],
new_instances: &[CCCSVar<C>], new_instances: &[CCCSVar<C>],
proof: ProofVar<C>, proof: ProofVar<C>,
@ -244,24 +243,24 @@ where
U_i.v.clone(), U_i.v.clone(),
] ]
.concat(); .concat();
transcript.absorb_vec(&v)?;
transcript.absorb(&v)?;
} }
for u_i in new_instances { for u_i in new_instances {
let v = [u_i.C.to_constraint_field()?, u_i.x.clone()].concat(); let v = [u_i.C.to_constraint_field()?, u_i.x.clone()].concat();
transcript.absorb_vec(&v)?;
transcript.absorb(&v)?;
} }
// get the challenges // get the challenges
let gamma_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"gamma"); let gamma_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"gamma");
let gamma_scalar: FpVar<CF1<C>> = let gamma_scalar: FpVar<CF1<C>> =
FpVar::<CF1<C>>::new_constant(cs.clone(), gamma_scalar_raw)?; FpVar::<CF1<C>>::new_constant(cs.clone(), gamma_scalar_raw)?;
transcript.absorb(gamma_scalar)?;
transcript.absorb(&gamma_scalar)?;
let gamma: FpVar<CF1<C>> = transcript.get_challenge()?; let gamma: FpVar<CF1<C>> = transcript.get_challenge()?;
let beta_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"beta"); let beta_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"beta");
let beta_scalar: FpVar<CF1<C>> = let beta_scalar: FpVar<CF1<C>> =
FpVar::<CF1<C>>::new_constant(cs.clone(), beta_scalar_raw)?; FpVar::<CF1<C>>::new_constant(cs.clone(), beta_scalar_raw)?;
transcript.absorb(beta_scalar)?;
transcript.absorb(&beta_scalar)?;
let beta: Vec<FpVar<CF1<C>>> = transcript.get_challenges(ccs.s)?; let beta: Vec<FpVar<CF1<C>>> = transcript.get_challenges(ccs.s)?;
let vp_aux_info_raw = VPAuxInfo::<C::ScalarField> { let vp_aux_info_raw = VPAuxInfo::<C::ScalarField> {
@ -283,10 +282,10 @@ where
} }
// verify the interactive part of the sumcheck // verify the interactive part of the sumcheck
let (e_vars, r_vars) = SumCheckVerifierGadget::<C>::verify(
let (e_vars, r_vars) = SumCheckVerifierGadget::<C::ScalarField>::verify(
&proof.sc_proof, &proof.sc_proof,
&vp_aux_info, &vp_aux_info,
&mut transcript,
transcript,
enabled.clone(), enabled.clone(),
)?; )?;
@ -312,7 +311,7 @@ where
// get the folding challenge // get the folding challenge
let rho_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"rho"); let rho_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"rho");
let rho_scalar: FpVar<CF1<C>> = FpVar::<CF1<C>>::new_constant(cs.clone(), rho_scalar_raw)?; let rho_scalar: FpVar<CF1<C>> = FpVar::<CF1<C>>::new_constant(cs.clone(), rho_scalar_raw)?;
transcript.absorb(rho_scalar)?;
transcript.absorb(&rho_scalar)?;
let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(N_BITS_RO)?; let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(N_BITS_RO)?;
let rho = Boolean::le_bits_to_fp_var(&rho_bits)?; let rho = Boolean::le_bits_to_fp_var(&rho_bits)?;
@ -560,10 +559,10 @@ where
let w_i = W_i.clone(); let w_i = W_i.clone();
let u_i = CCCS::<C1>::dummy(ccs.l); let u_i = CCCS::<C1>::dummy(ccs.l);
let mut transcript_p: PoseidonTranscript<C1> =
PoseidonTranscript::<C1>::new(&self.poseidon_config.clone());
let mut transcript_p: PoseidonSponge<C1::ScalarField> =
PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config.clone());
// since this is only for the number of constraints, no need to absorb the pp_hash here // since this is only for the number of constraints, no need to absorb the pp_hash here
let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&[U_i.clone()], &[U_i.clone()],
@ -671,10 +670,7 @@ where
})?; })?;
let cf_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf_cmT.unwrap_or_else(C2::zero)))?; let cf_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf_cmT.unwrap_or_else(C2::zero)))?;
let crh_params = CRHParametersVar::<C1::ScalarField>::new_constant(
cs.clone(),
self.poseidon_config.clone(),
)?;
let sponge = PoseidonSpongeVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config);
// get z_{i+1} from the F circuit // get z_{i+1} from the F circuit
let i_usize = self.i_usize.unwrap_or(0); let i_usize = self.i_usize.unwrap_or(0);
@ -689,14 +685,14 @@ where
// P.1. Compute u_i.x // P.1. Compute u_i.x
// u_i.x[0] = H(i, z_0, z_i, U_i) // u_i.x[0] = H(i, z_0, z_i, U_i)
let (u_i_x, _) = U_i.clone().hash( let (u_i_x, _) = U_i.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
i.clone(), i.clone(),
z_0.clone(), z_0.clone(),
z_i.clone(), z_i.clone(),
)?; )?;
// u_i.x[1] = H(cf_U_i) // u_i.x[1] = H(cf_U_i)
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?;
// P.2. Construct u_i // P.2. Construct u_i
let u_i = CCCSVar::<C1> { let u_i = CCCSVar::<C1> {
@ -712,13 +708,12 @@ where
// Notice that NIMFSGadget::fold_committed_instance does not fold C. We set `U_i1.C` to // Notice that NIMFSGadget::fold_committed_instance does not fold C. We set `U_i1.C` to
// unconstrained witnesses `U_i1_C` respectively. Its correctness will be checked on the // unconstrained witnesses `U_i1_C` respectively. Its correctness will be checked on the
// other curve. // other curve.
let mut transcript =
PoseidonTranscriptVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config);
transcript.absorb(pp_hash.clone())?;
let mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config);
transcript.absorb(&pp_hash)?;
let (mut U_i1, rho_bits) = NIMFSGadget::<C1>::verify( let (mut U_i1, rho_bits) = NIMFSGadget::<C1>::verify(
cs.clone(), cs.clone(),
&self.ccs.clone(), &self.ccs.clone(),
transcript,
&mut transcript,
&[U_i.clone()], &[U_i.clone()],
&[u_i.clone()], &[u_i.clone()],
nimfs_proof, nimfs_proof,
@ -728,14 +723,14 @@ where
// P.4.a compute and check the first output of F' // P.4.a compute and check the first output of F'
let (u_i1_x, _) = U_i1.clone().hash( let (u_i1_x, _) = U_i1.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
i + FpVar::<CF1<C1>>::one(), i + FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?; )?;
let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
FpVar::<CF1<C1>>::one(), FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
@ -776,8 +771,7 @@ where
// compute cf_r = H(cf_u_i, cf_U_i, cf_cmT) // compute cf_r = H(cf_u_i, cf_U_i, cf_cmT)
// cf_r_bits is denoted by rho* in the paper. // cf_r_bits is denoted by rho* in the paper.
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
&mut transcript,
pp_hash.clone(), pp_hash.clone(),
cf_U_i_vec, cf_U_i_vec,
cf_u_i.clone(), cf_u_i.clone(),
@ -802,10 +796,10 @@ where
// P.4.b compute and check the second output of F' // P.4.b compute and check the second output of F'
// Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // Base case: u_{i+1}.x[1] == H(cf_U_{\bot})
// Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1}) // Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1})
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge, pp_hash.clone())?;
let (cf_u_i1_x_base, _) = let (cf_u_i1_x_base, _) =
CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)?
.hash(&crh_params, pp_hash)?;
.hash(&sponge, pp_hash)?;
let cf_x = FpVar::new_input(cs.clone(), || { let cf_x = FpVar::new_input(cs.clone(), || {
Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?))
})?; })?;
@ -820,34 +814,23 @@ mod tests {
use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective}; use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective};
use ark_ff::BigInteger; use ark_ff::BigInteger;
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};
use std::time::Instant; use std::time::Instant;
use super::*; use super::*;
use crate::{ use crate::{
arith::{ arith::{
ccs::{
tests::{get_test_ccs, get_test_z},
CCS,
},
ccs::tests::{get_test_ccs, get_test_z},
r1cs::extract_w_x, r1cs::extract_w_x,
}, },
commitment::{pedersen::Pedersen, CommitmentScheme}, commitment::{pedersen::Pedersen, CommitmentScheme},
folding::{ folding::{
circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
hypernova::{
nimfs::NIMFS,
utils::{compute_c, compute_sigmas_thetas},
Witness,
},
nova::{traits::NovaR1CS, CommittedInstance, Witness as NovaWitness},
hypernova::utils::{compute_c, compute_sigmas_thetas},
nova::{traits::NovaR1CS, Witness as NovaWitness},
}, },
frontend::tests::CubicFCircuit, frontend::tests::CubicFCircuit,
transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar},
Transcript,
},
transcript::poseidon::poseidon_canonical_config,
utils::get_cm_coordinates, utils::get_cm_coordinates,
}; };
@ -996,12 +979,11 @@ mod tests {
// Prover's transcript // Prover's transcript
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
// Run the prover side of the multifolding // Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) = let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,
@ -1012,11 +994,10 @@ mod tests {
.unwrap(); .unwrap();
// Verifier's transcript // Verifier's transcript
let mut transcript_v: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
// Run the verifier side of the multifolding // Run the verifier side of the multifolding
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v, &mut transcript_v,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,
@ -1039,13 +1020,13 @@ mod tests {
.unwrap(); .unwrap();
let proofVar = let proofVar =
ProofVar::<Projective>::new_witness(cs.clone(), || Ok(proof.clone())).unwrap(); ProofVar::<Projective>::new_witness(cs.clone(), || Ok(proof.clone())).unwrap();
let transcriptVar = PoseidonTranscriptVar::<Fr>::new(cs.clone(), &poseidon_config);
let mut transcriptVar = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
let enabled = Boolean::<Fr>::new_witness(cs.clone(), || Ok(true)).unwrap(); let enabled = Boolean::<Fr>::new_witness(cs.clone(), || Ok(true)).unwrap();
let (folded_lcccsVar, _) = NIMFSGadget::<Projective>::verify( let (folded_lcccsVar, _) = NIMFSGadget::<Projective>::verify(
cs.clone(), cs.clone(),
&ccs, &ccs,
transcriptVar,
&mut transcriptVar,
&lcccs_instancesVar, &lcccs_instancesVar,
&cccs_instancesVar, &cccs_instancesVar,
proofVar, proofVar,
@ -1061,6 +1042,7 @@ mod tests {
pub fn test_lcccs_hash() { pub fn test_lcccs_hash() {
let mut rng = test_rng(); let mut rng = test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let ccs = get_test_ccs(); let ccs = get_test_ccs();
let z1 = get_test_z::<Fr>(3); let z1 = get_test_z::<Fr>(3);
@ -1077,12 +1059,11 @@ mod tests {
.unwrap(); .unwrap();
let h = lcccs let h = lcccs
.clone() .clone()
.hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone())
.unwrap();
.hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone());
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
let spongeVar = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
let pp_hashVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let pp_hashVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let iVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(i)).unwrap(); let iVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(i)).unwrap();
let z_0Var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap(); let z_0Var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap();
@ -1091,7 +1072,7 @@ mod tests {
let (hVar, _) = lcccsVar let (hVar, _) = lcccsVar
.clone() .clone()
.hash( .hash(
&crh_params,
&spongeVar,
pp_hashVar, pp_hashVar,
iVar.clone(), iVar.clone(),
z_0Var.clone(), z_0Var.clone(),
@ -1108,6 +1089,7 @@ mod tests {
pub fn test_augmented_f_circuit() { pub fn test_augmented_f_circuit() {
let mut rng = test_rng(); let mut rng = test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let start = Instant::now(); let start = Instant::now();
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
@ -1161,15 +1143,8 @@ mod tests {
let mut cf_W_i = cf_W_dummy.clone(); let mut cf_W_i = cf_W_dummy.clone();
let mut cf_U_i = cf_U_dummy.clone(); let mut cf_U_i = cf_U_dummy.clone();
u_i.x = vec![ u_i.x = vec![
U_i.hash(
&poseidon_config,
pp_hash,
Fr::zero(),
z_0.clone(),
z_i.clone(),
)
.unwrap(),
cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap(),
U_i.hash(&sponge, pp_hash, Fr::zero(), z_0.clone(), z_i.clone()),
cf_U_i.hash_cyclefold(&sponge, pp_hash),
]; ];
let n_steps: usize = 4; let n_steps: usize = 4;
@ -1185,19 +1160,11 @@ mod tests {
W_i1 = Witness::<Fr>::dummy(&ccs); W_i1 = Witness::<Fr>::dummy(&ccs);
U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s);
let u_i1_x = U_i1
.hash(
&poseidon_config,
pp_hash,
Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap();
let u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), z_0.clone(), z_i1.clone());
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit // input in the AugmentedFCircuit
let cf_u_i1_x = cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
augmented_f_circuit = augmented_f_circuit =
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> { AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
@ -1225,12 +1192,12 @@ mod tests {
cf_cmT: None, cf_cmT: None,
}; };
} else { } else {
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config.clone());
let mut transcript_p: PoseidonSponge<Fr> =
PoseidonSponge::<Fr>::new(&poseidon_config.clone());
transcript_p.absorb(&pp_hash); transcript_p.absorb(&pp_hash);
let (rho_bits, nimfs_proof); let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) = (nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&[U_i.clone()], &[U_i.clone()],
@ -1243,15 +1210,8 @@ mod tests {
// sanity check: check the folded instance relation // sanity check: check the folded instance relation
U_i1.check_relation(&ccs, &W_i1).unwrap(); U_i1.check_relation(&ccs, &W_i1).unwrap();
let u_i1_x = U_i1
.hash(
&poseidon_config,
pp_hash,
iFr + Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap();
let u_i1_x =
U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone());
let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
// CycleFold part: // CycleFold part:
@ -1282,7 +1242,7 @@ mod tests {
Pedersen<Projective>, Pedersen<Projective>,
Pedersen<Projective2>, Pedersen<Projective2>,
>( >(
&poseidon_config,
&mut transcript_p,
cf_r1cs.clone(), cf_r1cs.clone(),
cf_pedersen_params.clone(), cf_pedersen_params.clone(),
pp_hash, pp_hash,
@ -1295,7 +1255,7 @@ mod tests {
// hash the CycleFold folded instance, which is used as the 2nd public input in the // hash the CycleFold folded instance, which is used as the 2nd public input in the
// AugmentedFCircuit // AugmentedFCircuit
let cf_u_i1_x = cf_U_i1.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
let cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, pp_hash);
augmented_f_circuit = augmented_f_circuit =
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> { AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
@ -1346,16 +1306,9 @@ mod tests {
assert_eq!(u_i.x, r1cs_x_i1); assert_eq!(u_i.x, r1cs_x_i1);
assert_eq!(u_i.x[0], augmented_f_circuit.x.unwrap()); assert_eq!(u_i.x[0], augmented_f_circuit.x.unwrap());
assert_eq!(u_i.x[1], augmented_f_circuit.cf_x.unwrap()); assert_eq!(u_i.x[1], augmented_f_circuit.cf_x.unwrap());
let expected_u_i1_x = U_i1
.hash(
&poseidon_config,
pp_hash,
iFr + Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap();
let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
let expected_u_i1_x =
U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone());
let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
// u_i is already u_i1 at this point, check that has the expected value at x[0] // u_i is already u_i1 at this point, check that has the expected value at x[0]
assert_eq!(u_i.x[0], expected_u_i1_x); assert_eq!(u_i.x[0], expected_u_i1_x);
assert_eq!(u_i.x[1], expected_cf_U_i1_x); assert_eq!(u_i.x[1], expected_cf_U_i1_x);

+ 35
- 27
folding-schemes/src/folding/hypernova/lcccs.rs

@ -1,7 +1,4 @@
use ark_crypto_primitives::{
crh::{poseidon::CRH, CRHScheme},
sponge::{poseidon::PoseidonConfig, Absorb},
};
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group}; use ark_ec::{CurveGroup, Group};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_poly::DenseMultilinearExtension; use ark_poly::DenseMultilinearExtension;
@ -12,7 +9,7 @@ use ark_std::Zero;
use super::Witness; use super::Witness;
use crate::arith::ccs::CCS; use crate::arith::ccs::CCS;
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::transcript::{AbsorbNonNative, Transcript};
use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul; use crate::utils::vec::mat_vec_mul;
use crate::Error; use crate::Error;
@ -118,6 +115,29 @@ impl LCCCS {
} }
} }
impl<C: CurveGroup> Absorb for LCCCS<C>
where
C::ScalarField: Absorb,
{
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
// This is never called
unimplemented!()
}
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
// We cannot call `to_native_sponge_field_elements(dest)` directly, as
// `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
// but here `F` is a generic `PrimeField`.
self.C
.to_native_sponge_field_elements_as_vec()
.to_sponge_field_elements(dest);
self.u.to_sponge_field_elements(dest);
self.x.to_sponge_field_elements(dest);
self.r_x.to_sponge_field_elements(dest);
self.v.to_sponge_field_elements(dest);
}
}
impl<C: CurveGroup> LCCCS<C> impl<C: CurveGroup> LCCCS<C>
where where
<C as Group>::ScalarField: Absorb, <C as Group>::ScalarField: Absorb,
@ -126,32 +146,21 @@ where
/// [`LCCCS`].hash implements the committed instance hash compatible with the gadget /// [`LCCCS`].hash implements the committed instance hash compatible with the gadget
/// implemented in nova/circuits.rs::CommittedInstanceVar.hash. /// implemented in nova/circuits.rs::CommittedInstanceVar.hash.
/// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the LCCCS. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the LCCCS.
pub fn hash(
pub fn hash<T: Transcript<C::ScalarField>>(
&self, &self,
poseidon_config: &PoseidonConfig<C::ScalarField>,
sponge: &T,
pp_hash: C::ScalarField, pp_hash: C::ScalarField,
i: C::ScalarField, i: C::ScalarField,
z_0: Vec<C::ScalarField>, z_0: Vec<C::ScalarField>,
z_i: Vec<C::ScalarField>, z_i: Vec<C::ScalarField>,
) -> Result<C::ScalarField, Error> {
let (C_x, C_y) = nonnative_affine_to_field_elements::<C>(self.C)?;
CRH::<C::ScalarField>::evaluate(
poseidon_config,
vec![
vec![pp_hash, i],
z_0,
z_i,
C_x,
C_y,
vec![self.u],
self.x.clone(),
self.r_x.clone(),
self.v.clone(),
]
.concat(),
)
.map_err(|e| Error::Other(e.to_string()))
) -> C::ScalarField {
let mut sponge = sponge.clone();
sponge.absorb(&pp_hash);
sponge.absorb(&i);
sponge.absorb(&z_0);
sponge.absorb(&z_i);
sponge.absorb(&self);
sponge.squeeze_field_elements(1)[0]
} }
} }
@ -161,7 +170,6 @@ pub mod tests {
use ark_std::test_rng; use ark_std::test_rng;
use ark_std::One; use ark_std::One;
use ark_std::UniformRand; use ark_std::UniformRand;
use ark_std::Zero;
use std::sync::Arc; use std::sync::Arc;
use super::*; use super::*;

+ 38
- 32
folding-schemes/src/folding/hypernova/mod.rs

@ -1,5 +1,8 @@
/// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf) /// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf)
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_crypto_primitives::sponge::{
poseidon::{PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge,
};
use ark_ec::{CurveGroup, Group}; use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
@ -18,6 +21,10 @@ use cccs::CCCS;
use lcccs::LCCCS; use lcccs::LCCCS;
use nimfs::NIMFS; use nimfs::NIMFS;
use crate::arith::{
ccs::CCS,
r1cs::{extract_w_x, R1CS},
};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{ use crate::folding::circuits::{
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
@ -31,13 +38,6 @@ use crate::frontend::FCircuit;
use crate::utils::{get_cm_coordinates, pp_hash}; use crate::utils::{get_cm_coordinates, pp_hash};
use crate::Error; use crate::Error;
use crate::FoldingScheme; use crate::FoldingScheme;
use crate::{
arith::{
ccs::CCS,
r1cs::{extract_w_x, R1CS},
},
transcript::{poseidon::PoseidonTranscript, Transcript},
};
/// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment. /// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -233,6 +233,8 @@ where
z_0: Vec<C1::ScalarField>, z_0: Vec<C1::ScalarField>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let (pp, vp) = params; let (pp, vp) = params;
// `sponge` is for digest computation.
let sponge = PoseidonSponge::<C1::ScalarField>::new(&pp.poseidon_config);
// prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS
// and R1CS respectively // and R1CS respectively
@ -258,13 +260,13 @@ where
cf_r1cs.dummy_instance(); cf_r1cs.dummy_instance();
u_dummy.x = vec![ u_dummy.x = vec![
U_dummy.hash( U_dummy.hash(
&pp.poseidon_config,
&sponge,
pp_hash, pp_hash,
C1::ScalarField::zero(), C1::ScalarField::zero(),
z_0.clone(), z_0.clone(),
z_0.clone(), z_0.clone(),
)?,
cf_U_dummy.hash_cyclefold(&pp.poseidon_config, pp_hash)?,
),
cf_U_dummy.hash_cyclefold(&sponge, pp_hash),
]; ];
// W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
@ -299,6 +301,9 @@ where
mut rng: impl RngCore, mut rng: impl RngCore,
external_inputs: Vec<C1::ScalarField>, external_inputs: Vec<C1::ScalarField>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// `sponge` is for digest computation.
let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>; let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
if self.z_i.len() != self.F.state_len() { if self.z_i.len() != self.F.state_len() {
@ -339,18 +344,16 @@ where
U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s);
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config,
&sponge,
self.pp_hash, self.pp_hash,
C1::ScalarField::one(), C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?;
);
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit // input in the AugmentedFCircuit
cf_u_i1_x = self
.cf_U_i
.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData, _c2: PhantomData,
@ -377,30 +380,31 @@ where
cf_cmT: None, cf_cmT: None,
}; };
} else { } else {
let mut transcript_p: PoseidonTranscript<C1> =
PoseidonTranscript::<C1>::new(&self.poseidon_config);
let mut transcript_p: PoseidonSponge<C1::ScalarField> =
PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
transcript_p.absorb(&self.pp_hash); transcript_p.absorb(&self.pp_hash);
let (rho_bits, nimfs_proof); let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
&mut transcript_p,
&self.ccs,
&[self.U_i.clone()],
&[self.u_i.clone()],
&[self.W_i.clone()],
&[self.w_i.clone()],
)?;
(nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
&mut transcript_p,
&self.ccs,
&[self.U_i.clone()],
&[self.u_i.clone()],
&[self.W_i.clone()],
&[self.w_i.clone()],
)?;
// sanity check: check the folded instance relation // sanity check: check the folded instance relation
#[cfg(test)] #[cfg(test)]
U_i1.check_relation(&self.ccs, &W_i1)?; U_i1.check_relation(&self.ccs, &W_i1)?;
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config,
&sponge,
self.pp_hash, self.pp_hash,
self.i + C1::ScalarField::one(), self.i + C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?;
);
let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)) let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits))
.ok_or(Error::OutOfBounds)?; .ok_or(Error::OutOfBounds)?;
@ -425,7 +429,7 @@ where
let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) =
fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>( fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>(
&self.poseidon_config,
&mut transcript_p,
self.cf_r1cs.clone(), self.cf_r1cs.clone(),
self.cf_cs_params.clone(), self.cf_cs_params.clone(),
self.pp_hash, self.pp_hash,
@ -435,7 +439,7 @@ where
cf_circuit, cf_circuit,
)?; )?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData, _c2: PhantomData,
@ -541,6 +545,8 @@ where
} }
return Ok(()); return Ok(());
} }
// `sponge` is for digest computation.
let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
let (U_i, W_i) = running_instance; let (U_i, W_i) = running_instance;
let (u_i, w_i) = incoming_instance; let (u_i, w_i) = incoming_instance;
@ -553,12 +559,12 @@ where
// check that u_i's output points to the running instance // check that u_i's output points to the running instance
// u_i.X[0] == H(i, z_0, z_i, U_i) // u_i.X[0] == H(i, z_0, z_i, U_i)
let expected_u_i_x = U_i.hash(&vp.poseidon_config, pp_hash, num_steps, z_0, z_i.clone())?;
let expected_u_i_x = U_i.hash(&sponge, pp_hash, num_steps, z_0, z_i.clone());
if expected_u_i_x != u_i.x[0] { if expected_u_i_x != u_i.x[0] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
// u_i.X[1] == H(cf_U_i) // u_i.X[1] == H(cf_U_i)
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config, pp_hash)?;
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
if expected_cf_u_i_x != u_i.x[1] { if expected_cf_u_i_x != u_i.x[1] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }

+ 36
- 73
folding-schemes/src/folding/hypernova/nimfs.rs

@ -13,7 +13,6 @@ use super::{
}; };
use crate::arith::ccs::CCS; use crate::arith::ccs::CCS;
use crate::constants::N_BITS_RO; use crate::constants::N_BITS_RO;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::transcript::Transcript; use crate::transcript::Transcript;
use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage}; use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage};
use crate::utils::sum_check::{IOPSumCheck, SumCheck}; use crate::utils::sum_check::{IOPSumCheck, SumCheck};
@ -58,12 +57,12 @@ pub struct SigmasThetas(pub Vec>, pub Vec>);
#[derive(Debug)] #[derive(Debug)]
/// Implements the Non-Interactive Multi Folding Scheme described in section 5 of /// Implements the Non-Interactive Multi Folding Scheme described in section 5 of
/// [HyperNova](https://eprint.iacr.org/2023/573.pdf) /// [HyperNova](https://eprint.iacr.org/2023/573.pdf)
pub struct NIMFS<C: CurveGroup, T: Transcript<C>> {
pub struct NIMFS<C: CurveGroup, T: Transcript<C::ScalarField>> {
pub _c: PhantomData<C>, pub _c: PhantomData<C>,
pub _t: PhantomData<T>, pub _t: PhantomData<T>,
} }
impl<C: CurveGroup, T: Transcript<C>> NIMFS<C, T>
impl<C: CurveGroup, T: Transcript<C::ScalarField>> NIMFS<C, T>
where where
<C as Group>::ScalarField: Absorb, <C as Group>::ScalarField: Absorb,
C::BaseField: PrimeField, C::BaseField: PrimeField,
@ -178,7 +177,7 @@ where
/// contains the sumcheck proof and the helper sumcheck claim sigmas and thetas. /// contains the sumcheck proof and the helper sumcheck claim sigmas and thetas.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn prove( pub fn prove(
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
ccs: &CCS<C::ScalarField>, ccs: &CCS<C::ScalarField>,
running_instances: &[LCCCS<C>], running_instances: &[LCCCS<C>],
new_instances: &[CCCS<C>], new_instances: &[CCCS<C>],
@ -186,24 +185,8 @@ where
w_cccs: &[Witness<C::ScalarField>], w_cccs: &[Witness<C::ScalarField>],
) -> Result<(NIMFSProof<C>, LCCCS<C>, Witness<C::ScalarField>, Vec<bool>), Error> { ) -> Result<(NIMFSProof<C>, LCCCS<C>, Witness<C::ScalarField>, Vec<bool>), Error> {
// absorb instances to transcript // absorb instances to transcript
for U_i in running_instances {
let (C_x, C_y) = nonnative_affine_to_field_elements::<C>(U_i.C)?;
let v = [
C_x,
C_y,
vec![U_i.u],
U_i.x.clone(),
U_i.r_x.clone(),
U_i.v.clone(),
]
.concat();
transcript.absorb_vec(&v);
}
for u_i in new_instances {
let (C_x, C_y) = nonnative_affine_to_field_elements::<C>(u_i.C)?;
let v = [C_x, C_y, u_i.x.clone()].concat();
transcript.absorb_vec(&v);
}
transcript.absorb(&running_instances);
transcript.absorb(&new_instances);
if running_instances.is_empty() { if running_instances.is_empty() {
return Err(Error::Empty); return Err(Error::Empty);
@ -247,7 +230,7 @@ where
let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta)?; let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta)?;
// Step 3: Run the sumcheck prover // Step 3: Run the sumcheck prover
let sumcheck_proof = IOPSumCheck::<C, T>::prove(&g, transcript)
let sumcheck_proof = IOPSumCheck::<C::ScalarField, T>::prove(&g, transcript)
.map_err(|err| Error::SumCheckProveError(err.to_string()))?; .map_err(|err| Error::SumCheckProveError(err.to_string()))?;
// Step 2: dig into the sumcheck and extract r_x_prime // Step 2: dig into the sumcheck and extract r_x_prime
@ -290,31 +273,15 @@ where
/// into a single LCCCS instance. /// into a single LCCCS instance.
/// Returns the folded LCCCS instance. /// Returns the folded LCCCS instance.
pub fn verify( pub fn verify(
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
ccs: &CCS<C::ScalarField>, ccs: &CCS<C::ScalarField>,
running_instances: &[LCCCS<C>], running_instances: &[LCCCS<C>],
new_instances: &[CCCS<C>], new_instances: &[CCCS<C>],
proof: NIMFSProof<C>, proof: NIMFSProof<C>,
) -> Result<LCCCS<C>, Error> { ) -> Result<LCCCS<C>, Error> {
// absorb instances to transcript // absorb instances to transcript
for U_i in running_instances {
let (C_x, C_y) = nonnative_affine_to_field_elements::<C>(U_i.C)?;
let v = [
C_x,
C_y,
vec![U_i.u],
U_i.x.clone(),
U_i.r_x.clone(),
U_i.v.clone(),
]
.concat();
transcript.absorb_vec(&v);
}
for u_i in new_instances {
let (C_x, C_y) = nonnative_affine_to_field_elements::<C>(u_i.C)?;
let v = [C_x, C_y, u_i.x.clone()].concat();
transcript.absorb_vec(&v);
}
transcript.absorb(&running_instances);
transcript.absorb(&new_instances);
if running_instances.is_empty() { if running_instances.is_empty() {
return Err(Error::Empty); return Err(Error::Empty);
@ -349,9 +316,13 @@ where
} }
// Verify the interactive part of the sumcheck // Verify the interactive part of the sumcheck
let sumcheck_subclaim =
IOPSumCheck::<C, T>::verify(sum_v_j_gamma, &proof.sc_proof, &vp_aux_info, transcript)
.map_err(|err| Error::SumCheckVerifyError(err.to_string()))?;
let sumcheck_subclaim = IOPSumCheck::<C::ScalarField, T>::verify(
sum_v_j_gamma,
&proof.sc_proof,
&vp_aux_info,
transcript,
)
.map_err(|err| Error::SumCheckVerifyError(err.to_string()))?;
// Step 2: Dig into the sumcheck claim and extract the randomness used // Step 2: Dig into the sumcheck claim and extract the randomness used
let r_x_prime = sumcheck_subclaim.point.clone(); let r_x_prime = sumcheck_subclaim.point.clone();
@ -413,7 +384,8 @@ pub mod tests {
Arith, Arith,
}; };
use crate::transcript::poseidon::poseidon_canonical_config; use crate::transcript::poseidon::poseidon_canonical_config;
use crate::transcript::poseidon::PoseidonTranscript;
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
use ark_crypto_primitives::sponge::CryptographicSponge;
use ark_std::test_rng; use ark_std::test_rng;
use ark_std::UniformRand; use ark_std::UniformRand;
@ -450,7 +422,7 @@ pub mod tests {
let mut rng = test_rng(); let mut rng = test_rng();
let rho = Fr::rand(&mut rng); let rho = Fr::rand(&mut rng);
let folded = NIMFS::<Projective, PoseidonTranscript<Projective>>::fold(
let folded = NIMFS::<Projective, PoseidonSponge<Fr>>::fold(
&[lcccs], &[lcccs],
&[cccs], &[cccs],
&sigmas_thetas, &sigmas_thetas,
@ -458,8 +430,7 @@ pub mod tests {
rho, rho,
); );
let w_folded =
NIMFS::<Projective, PoseidonTranscript<Projective>>::fold_witness(&[w1], &[w2], rho);
let w_folded = NIMFS::<Projective, PoseidonSponge<Fr>>::fold_witness(&[w1], &[w2], rho);
// check lcccs relation // check lcccs relation
folded.check_relation(&ccs, &w_folded).unwrap(); folded.check_relation(&ccs, &w_folded).unwrap();
@ -491,13 +462,12 @@ pub mod tests {
// Prover's transcript // Prover's transcript
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
// Run the prover side of the multifolding // Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) = let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&[running_instance.clone()], &[running_instance.clone()],
@ -508,12 +478,11 @@ pub mod tests {
.unwrap(); .unwrap();
// Verifier's transcript // Verifier's transcript
let mut transcript_v: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
// Run the verifier side of the multifolding // Run the verifier side of the multifolding
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v, &mut transcript_v,
&ccs, &ccs,
&[running_instance.clone()], &[running_instance.clone()],
@ -545,12 +514,10 @@ pub mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
let mut transcript_v: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
let n: usize = 10; let n: usize = 10;
@ -564,7 +531,7 @@ pub mod tests {
// run the prover side of the multifolding // run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) = let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&[running_instance.clone()], &[running_instance.clone()],
@ -575,7 +542,7 @@ pub mod tests {
.unwrap(); .unwrap();
// run the verifier side of the multifolding // run the verifier side of the multifolding
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v, &mut transcript_v,
&ccs, &ccs,
&[running_instance.clone()], &[running_instance.clone()],
@ -641,13 +608,12 @@ pub mod tests {
// Prover's transcript // Prover's transcript
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
// Run the prover side of the multifolding // Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) = let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,
@ -658,12 +624,11 @@ pub mod tests {
.unwrap(); .unwrap();
// Verifier's transcript // Verifier's transcript
let mut transcript_v: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
// Run the verifier side of the multifolding // Run the verifier side of the multifolding
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v, &mut transcript_v,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,
@ -690,13 +655,11 @@ pub mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
// Prover's transcript // Prover's transcript
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
// Verifier's transcript // Verifier's transcript
let mut transcript_v: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init"));
let n_steps = 3; let n_steps = 3;
@ -741,7 +704,7 @@ pub mod tests {
// Run the prover side of the multifolding // Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) = let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,
@ -752,7 +715,7 @@ pub mod tests {
.unwrap(); .unwrap();
// Run the verifier side of the multifolding // Run the verifier side of the multifolding
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v, &mut transcript_v,
&ccs, &ccs,
&lcccs_instances, &lcccs_instances,

+ 0
- 1
folding-schemes/src/folding/hypernova/utils.rs

@ -176,7 +176,6 @@ pub mod tests {
use crate::utils::hypercube::BooleanHypercube; use crate::utils::hypercube::BooleanHypercube;
use crate::utils::mle::matrix_to_dense_mle; use crate::utils::mle::matrix_to_dense_mle;
use crate::utils::multilinear_polynomial::tests::fix_last_variables; use crate::utils::multilinear_polynomial::tests::fix_last_variables;
use crate::utils::virtual_polynomial::eq_eval;
/// Given M(x,y) matrix and a random field element `r`, test that ~M(r,y) is is an s'-variable polynomial which /// Given M(x,y) matrix and a random field element `r`, test that ~M(r,y) is is an s'-variable polynomial which
/// compresses every column j of the M(x,y) matrix by performing a random linear combination between the elements /// compresses every column j of the M(x,y) matrix by performing a random linear combination between the elements

+ 74
- 100
folding-schemes/src/folding/nova/circuits.rs

@ -1,11 +1,7 @@
/// contains [Nova](https://eprint.iacr.org/2021/370.pdf) related circuits /// contains [Nova](https://eprint.iacr.org/2021/370.pdf) related circuits
use ark_crypto_primitives::crh::{
poseidon::constraints::{CRHGadget, CRHParametersVar},
CRHSchemeGadget,
};
use ark_crypto_primitives::sponge::{ use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
constraints::{AbsorbGadget, CryptographicSpongeVar},
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig},
Absorb, CryptographicSponge, Absorb, CryptographicSponge,
}; };
use ark_ec::{CurveGroup, Group}; use ark_ec::{CurveGroup, Group};
@ -17,6 +13,7 @@ use ark_r1cs_std::{
fields::{fp::FpVar, FieldVar}, fields::{fp::FpVar, FieldVar},
groups::GroupOpsBounds, groups::GroupOpsBounds,
prelude::CurveVar, prelude::CurveVar,
uint8::UInt8,
R1CSVar, ToConstraintFieldGadget, R1CSVar, ToConstraintFieldGadget,
}; };
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
@ -29,13 +26,11 @@ use crate::folding::circuits::{
cyclefold::{ cyclefold::{
CycleFoldChallengeGadget, CycleFoldCommittedInstanceVar, NIFSFullGadget, CF_IO_LEN, CycleFoldChallengeGadget, CycleFoldCommittedInstanceVar, NIFSFullGadget, CF_IO_LEN,
}, },
nonnative::{
affine::{nonnative_affine_to_field_elements, NonNativeAffineVar},
uint::NonNativeUintVar,
},
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
CF1, CF2, CF1, CF2,
}; };
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar};
/// CommittedInstanceVar contains the u, x, cmE and cmW values which are folded on the main Nova /// CommittedInstanceVar contains the u, x, cmE and cmW values which are folded on the main Nova
/// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are /// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are
@ -78,6 +73,26 @@ where
} }
} }
impl<C> AbsorbGadget<C::ScalarField> for CommittedInstanceVar<C>
where
C: CurveGroup,
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
{
fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
unimplemented!()
}
fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
Ok([
vec![self.u.clone()],
self.x.clone(),
self.cmE.to_constraint_field()?,
self.cmW.to_constraint_field()?,
]
.concat())
}
}
impl<C> CommittedInstanceVar<C> impl<C> CommittedInstanceVar<C>
where where
C: CurveGroup, C: CurveGroup,
@ -91,26 +106,22 @@ where
/// Additionally it returns the vector of the field elements from the self parameters, so they /// Additionally it returns the vector of the field elements from the self parameters, so they
/// can be reused in other gadgets avoiding recalculating (reconstraining) them. /// can be reused in other gadgets avoiding recalculating (reconstraining) them.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn hash(
pub fn hash<S: CryptographicSponge, T: TranscriptVar<CF1<C>, S>>(
self, self,
crh_params: &CRHParametersVar<CF1<C>>,
sponge: &T,
pp_hash: FpVar<CF1<C>>, pp_hash: FpVar<CF1<C>>,
i: FpVar<CF1<C>>, i: FpVar<CF1<C>>,
z_0: Vec<FpVar<CF1<C>>>, z_0: Vec<FpVar<CF1<C>>>,
z_i: Vec<FpVar<CF1<C>>>, z_i: Vec<FpVar<CF1<C>>>,
) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> { ) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> {
let U_vec = [
vec![self.u],
self.x,
self.cmE.to_constraint_field()?,
self.cmW.to_constraint_field()?,
]
.concat();
let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat();
Ok((
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?,
U_vec,
))
let mut sponge = sponge.clone();
let U_vec = self.to_sponge_field_elements()?;
sponge.absorb(&pp_hash)?;
sponge.absorb(&i)?;
sponge.absorb(&z_0)?;
sponge.absorb(&z_i)?;
sponge.absorb(&U_vec)?;
Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec))
} }
} }
@ -174,67 +185,33 @@ where
<C as CurveGroup>::BaseField: PrimeField, <C as CurveGroup>::BaseField: PrimeField,
<C as Group>::ScalarField: Absorb, <C as Group>::ScalarField: Absorb,
{ {
pub fn get_challenge_native(
poseidon_config: &PoseidonConfig<C::ScalarField>,
pub fn get_challenge_native<T: Transcript<C::ScalarField>>(
transcript: &mut T,
pp_hash: C::ScalarField, // public params hash pp_hash: C::ScalarField, // public params hash
U_i: CommittedInstance<C>, U_i: CommittedInstance<C>,
u_i: CommittedInstance<C>, u_i: CommittedInstance<C>,
cmT: C, cmT: C,
) -> Result<Vec<bool>, SynthesisError> {
let (U_cmE_x, U_cmE_y) = nonnative_affine_to_field_elements::<C>(U_i.cmE)?;
let (U_cmW_x, U_cmW_y) = nonnative_affine_to_field_elements::<C>(U_i.cmW)?;
let (u_cmE_x, u_cmE_y) = nonnative_affine_to_field_elements::<C>(u_i.cmE)?;
let (u_cmW_x, u_cmW_y) = nonnative_affine_to_field_elements::<C>(u_i.cmW)?;
let (cmT_x, cmT_y) = nonnative_affine_to_field_elements::<C>(cmT)?;
let mut sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
let input = vec![
vec![pp_hash],
vec![U_i.u],
U_i.x.clone(),
U_cmE_x,
U_cmE_y,
U_cmW_x,
U_cmW_y,
vec![u_i.u],
u_i.x.clone(),
u_cmE_x,
u_cmE_y,
u_cmW_x,
u_cmW_y,
cmT_x,
cmT_y,
]
.concat();
sponge.absorb(&input);
let bits = sponge.squeeze_bits(N_BITS_RO);
Ok(bits)
) -> Vec<bool> {
transcript.absorb(&pp_hash);
transcript.absorb(&U_i);
transcript.absorb(&u_i);
transcript.absorb_nonnative(&cmT);
transcript.squeeze_bits(N_BITS_RO)
} }
// compatible with the native get_challenge_native // compatible with the native get_challenge_native
pub fn get_challenge_gadget(
cs: ConstraintSystemRef<C::ScalarField>,
poseidon_config: &PoseidonConfig<C::ScalarField>,
pub fn get_challenge_gadget<S: CryptographicSponge, T: TranscriptVar<CF1<C>, S>>(
transcript: &mut T,
pp_hash: FpVar<CF1<C>>, // public params hash pp_hash: FpVar<CF1<C>>, // public params hash
U_i_vec: Vec<FpVar<CF1<C>>>, // apready processed input, so we don't have to recompute these values U_i_vec: Vec<FpVar<CF1<C>>>, // apready processed input, so we don't have to recompute these values
u_i: CommittedInstanceVar<C>, u_i: CommittedInstanceVar<C>,
cmT: NonNativeAffineVar<C>, cmT: NonNativeAffineVar<C>,
) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> { ) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> {
let mut sponge = PoseidonSpongeVar::<C::ScalarField>::new(cs, poseidon_config);
let input: Vec<FpVar<C::ScalarField>> = [
vec![pp_hash],
U_i_vec,
vec![u_i.u.clone()],
u_i.x.clone(),
u_i.cmE.to_constraint_field()?,
u_i.cmW.to_constraint_field()?,
cmT.to_constraint_field()?,
]
.concat();
sponge.absorb(&input)?;
let bits = sponge.squeeze_bits(N_BITS_RO)?;
Ok(bits)
transcript.absorb(&pp_hash)?;
transcript.absorb(&U_i_vec)?;
transcript.absorb(&u_i)?;
transcript.absorb_nonnative(&cmT)?;
transcript.squeeze_bits(N_BITS_RO)
} }
} }
@ -367,10 +344,10 @@ where
let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?; let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?;
let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?; let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?;
let crh_params = CRHParametersVar::<C1::ScalarField>::new_constant(
cs.clone(),
self.poseidon_config.clone(),
)?;
// `sponge` is for digest computation.
let sponge = PoseidonSpongeVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config);
// `transcript` is for challenge generation.
let mut transcript = sponge.clone();
// get z_{i+1} from the F circuit // get z_{i+1} from the F circuit
let i_usize = self.i_usize.unwrap_or(0); let i_usize = self.i_usize.unwrap_or(0);
@ -384,14 +361,14 @@ where
// P.1. Compute u_i.x // P.1. Compute u_i.x
// u_i.x[0] = H(i, z_0, z_i, U_i) // u_i.x[0] = H(i, z_0, z_i, U_i)
let (u_i_x, U_i_vec) = U_i.clone().hash( let (u_i_x, U_i_vec) = U_i.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
i.clone(), i.clone(),
z_0.clone(), z_0.clone(),
z_i.clone(), z_i.clone(),
)?; )?;
// u_i.x[1] = H(cf_U_i) // u_i.x[1] = H(cf_U_i)
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?;
// P.2. Construct u_i // P.2. Construct u_i
let u_i = CommittedInstanceVar { let u_i = CommittedInstanceVar {
@ -411,8 +388,7 @@ where
// compute r = H(u_i, U_i, cmT) // compute r = H(u_i, U_i, cmT)
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget( let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
&mut transcript,
pp_hash.clone(), pp_hash.clone(),
U_i_vec, U_i_vec,
u_i.clone(), u_i.clone(),
@ -438,14 +414,14 @@ where
// Base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{\bot}) // Base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{\bot})
// Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1}) // Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1})
let (u_i1_x, _) = U_i1.clone().hash( let (u_i1_x, _) = U_i1.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
i + FpVar::<CF1<C1>>::one(), i + FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?; )?;
let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
FpVar::<CF1<C1>>::one(), FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
@ -499,8 +475,7 @@ where
// compute cf1_r = H(cf1_u_i, cf_U_i, cf1_cmT) // compute cf1_r = H(cf1_u_i, cf_U_i, cf1_cmT)
// cf_r_bits is denoted by rho* in the paper. // cf_r_bits is denoted by rho* in the paper.
let cf1_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf1_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
&mut transcript,
pp_hash.clone(), pp_hash.clone(),
cf_U_i_vec, cf_U_i_vec,
cf1_u_i.clone(), cf1_u_i.clone(),
@ -523,10 +498,9 @@ where
// same for cf2_r: // same for cf2_r:
let cf2_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf2_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
&mut transcript,
pp_hash.clone(), pp_hash.clone(),
cf1_U_i1.to_constraint_field()?,
cf1_U_i1.to_native_sponge_field_elements()?,
cf2_u_i.clone(), cf2_u_i.clone(),
cf2_cmT.clone(), cf2_cmT.clone(),
)?; )?;
@ -547,10 +521,10 @@ where
// P.4.b compute and check the second output of F' // P.4.b compute and check the second output of F'
// Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // Base case: u_{i+1}.x[1] == H(cf_U_{\bot})
// Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1}) // Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1})
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge, pp_hash.clone())?;
let (cf_u_i1_x_base, _) = let (cf_u_i1_x_base, _) =
CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)?
.hash(&crh_params, pp_hash)?;
.hash(&sponge, pp_hash)?;
let cf_x = FpVar::new_input(cs.clone(), || { let cf_x = FpVar::new_input(cs.clone(), || {
Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?))
})?; })?;
@ -564,6 +538,7 @@ where
pub mod tests { pub mod tests {
use super::*; use super::*;
use ark_bn254::{Fr, G1Projective as Projective}; use ark_bn254::{Fr, G1Projective as Projective};
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
use ark_ff::BigInteger; use ark_ff::BigInteger;
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand; use ark_std::UniformRand;
@ -628,6 +603,7 @@ pub mod tests {
fn test_committed_instance_hash() { fn test_committed_instance_hash() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let pp_hash = Fr::from(42u32); // only for test let pp_hash = Fr::from(42u32); // only for test
let i = Fr::from(3_u32); let i = Fr::from(3_u32);
@ -641,9 +617,7 @@ pub mod tests {
}; };
// compute the CommittedInstance hash natively // compute the CommittedInstance hash natively
let h = ci
.hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone())
.unwrap();
let h = ci.hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone());
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
@ -654,11 +628,11 @@ pub mod tests {
let ciVar = let ciVar =
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(ci.clone())).unwrap(); CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(ci.clone())).unwrap();
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
let sponge = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
// compute the CommittedInstance hash in-circuit // compute the CommittedInstance hash in-circuit
let (hVar, _) = ciVar let (hVar, _) = ciVar
.hash(&crh_params, pp_hashVar, iVar, z_0Var, z_iVar)
.hash(&sponge, pp_hashVar, iVar, z_0Var, z_iVar)
.unwrap(); .unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
@ -671,6 +645,7 @@ pub mod tests {
fn test_challenge_gadget() { fn test_challenge_gadget() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript = PoseidonSponge::<Fr>::new(&poseidon_config);
let u_i = CommittedInstance::<Projective> { let u_i = CommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng), cmE: Projective::rand(&mut rng),
@ -690,13 +665,12 @@ pub mod tests {
// compute the challenge natively // compute the challenge natively
let r_bits = ChallengeGadget::<Projective>::get_challenge_native( let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
&poseidon_config,
&mut transcript,
pp_hash, pp_hash,
U_i.clone(), U_i.clone(),
u_i.clone(), u_i.clone(),
cmT, cmT,
)
.unwrap();
);
let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
@ -708,6 +682,7 @@ pub mod tests {
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone())) CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
.unwrap(); .unwrap();
let cmTVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(cmT)).unwrap(); let cmTVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(cmT)).unwrap();
let mut transcriptVar = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
// compute the challenge in-circuit // compute the challenge in-circuit
let U_iVar_vec = [ let U_iVar_vec = [
@ -718,8 +693,7 @@ pub mod tests {
] ]
.concat(); .concat();
let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget( let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget(
cs.clone(),
&poseidon_config,
&mut transcriptVar,
pp_hashVar, pp_hashVar,
U_iVar_vec, U_iVar_vec,
u_iVar, u_iVar,

+ 1
- 3
folding-schemes/src/folding/nova/decider_eth.rs

@ -317,13 +317,11 @@ fn point2_to_eth_format(p: ark_bn254::G2Affine) -> Result, Error> {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
use ark_groth16::Groth16;
use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective};
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use std::time::Instant; use std::time::Instant;
use super::*; use super::*;
use crate::commitment::kzg::KZG;
use crate::commitment::pedersen::Pedersen; use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::PreprocessorParam; use crate::folding::nova::PreprocessorParam;
use crate::frontend::tests::CubicFCircuit; use crate::frontend::tests::CubicFCircuit;

+ 50
- 63
folding-schemes/src/folding/nova/decider_eth_circuit.rs

@ -1,7 +1,10 @@
/// This file implements the onchain (Ethereum's EVM) decider circuit. For non-ethereum use cases, /// This file implements the onchain (Ethereum's EVM) decider circuit. For non-ethereum use cases,
/// other more efficient approaches can be used. /// other more efficient approaches can be used.
use ark_crypto_primitives::crh::poseidon::constraints::CRHParametersVar;
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge,
};
use ark_ec::{CurveGroup, Group}; use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_poly::Polynomial; use ark_poly::Polynomial;
@ -23,18 +26,12 @@ use super::{circuits::ChallengeGadget, nifs::NIFS};
use crate::arith::r1cs::R1CS; use crate::arith::r1cs::R1CS;
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
use crate::folding::circuits::{ use crate::folding::circuits::{
nonnative::{
affine::{nonnative_affine_to_field_elements, NonNativeAffineVar},
uint::NonNativeUintVar,
},
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
CF1, CF2, CF1, CF2,
}; };
use crate::folding::nova::{circuits::CommittedInstanceVar, CommittedInstance, Nova, Witness}; use crate::folding::nova::{circuits::CommittedInstanceVar, CommittedInstance, Nova, Witness};
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar,
};
use crate::transcript::{Transcript, TranscriptVar};
use crate::utils::{ use crate::utils::{
gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget}, gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget},
vec::poly_from_vec, vec::poly_from_vec,
@ -264,6 +261,8 @@ where
pub fn from_nova<FC: FCircuit<C1::ScalarField>>( pub fn from_nova<FC: FCircuit<C1::ScalarField>>(
nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2>, nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&nova.poseidon_config);
// compute the U_{i+1}, W_{i+1} // compute the U_{i+1}, W_{i+1}
let (T, cmT) = NIFS::<C1, CS1>::compute_cmT( let (T, cmT) = NIFS::<C1, CS1>::compute_cmT(
&nova.cs_pp, &nova.cs_pp,
@ -274,12 +273,12 @@ where
&nova.U_i.clone(), &nova.U_i.clone(),
)?; )?;
let r_bits = ChallengeGadget::<C1>::get_challenge_native( let r_bits = ChallengeGadget::<C1>::get_challenge_native(
&nova.poseidon_config,
&mut transcript,
nova.pp_hash, nova.pp_hash,
nova.U_i.clone(), nova.U_i.clone(),
nova.u_i.clone(), nova.u_i.clone(),
cmT, cmT,
)?;
);
let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
.ok_or(Error::OutOfBounds)?; .ok_or(Error::OutOfBounds)?;
let (W_i1, U_i1) = NIFS::<C1, CS1>::fold_instances( let (W_i1, U_i1) = NIFS::<C1, CS1>::fold_instances(
@ -288,7 +287,7 @@ where
// compute the KZG challenges used as inputs in the circuit // compute the KZG challenges used as inputs in the circuit
let (kzg_challenge_W, kzg_challenge_E) = let (kzg_challenge_W, kzg_challenge_E) =
KZGChallengesGadget::<C1>::get_challenges_native(&nova.poseidon_config, U_i1.clone())?;
KZGChallengesGadget::<C1>::get_challenges_native(&mut transcript, U_i1.clone());
// get KZG evals // get KZG evals
let mut W = W_i1.W.clone(); let mut W = W_i1.W.clone();
@ -410,10 +409,10 @@ where
Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
let crh_params = CRHParametersVar::<C1::ScalarField>::new_constant(
cs.clone(),
self.poseidon_config.clone(),
)?;
// `sponge` is for digest computation.
let sponge = PoseidonSpongeVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config);
// `transcript` is for challenge generation.
let mut transcript = sponge.clone();
// 1. check RelaxedR1CS of U_{i+1} // 1. check RelaxedR1CS of U_{i+1}
let z_U1: Vec<FpVar<CF1<C1>>> = let z_U1: Vec<FpVar<CF1<C1>>> =
@ -429,7 +428,7 @@ where
// 3.a u_i.x[0] == H(i, z_0, z_i, U_i) // 3.a u_i.x[0] == H(i, z_0, z_i, U_i)
let (u_i_x, U_i_vec) = U_i.clone().hash( let (u_i_x, U_i_vec) = U_i.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(), pp_hash.clone(),
i.clone(), i.clone(),
z_0.clone(), z_0.clone(),
@ -465,7 +464,7 @@ where
})?; })?;
// 3.b u_i.x[1] == H(cf_U_i) // 3.b u_i.x[1] == H(cf_U_i)
let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?;
(u_i.x[1]).enforce_equal(&cf_u_i_x)?; (u_i.x[1]).enforce_equal(&cf_u_i_x)?;
// 4. check Pedersen commitments of cf_U_i.{cmE, cmW} // 4. check Pedersen commitments of cf_U_i.{cmE, cmW}
@ -498,12 +497,23 @@ where
RelaxedR1CSGadget::check_nonnative(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?; RelaxedR1CSGadget::check_nonnative(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?;
} }
// 6. check KZG challenges
let (incircuit_c_W, incircuit_c_E) = KZGChallengesGadget::<C1>::get_challenges_gadget(
cs.clone(),
&self.poseidon_config,
U_i1.clone(),
// 8.a, 6.a compute NIFS.V and KZG challenges.
// We need to ensure the order of challenge generation is the same as
// the native counterpart, so we first compute the challenges here and
// do the actual checks later.
let cmT =
NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?;
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
&mut transcript,
pp_hash,
U_i_vec,
u_i.clone(),
cmT.clone(),
)?; )?;
let (incircuit_c_W, incircuit_c_E) =
KZGChallengesGadget::<C1>::get_challenges_gadget(&mut transcript, U_i1.clone())?;
// 6.b check KZG challenges
incircuit_c_W.enforce_equal(&kzg_c_W)?; incircuit_c_W.enforce_equal(&kzg_c_W)?;
incircuit_c_E.enforce_equal(&kzg_c_E)?; incircuit_c_E.enforce_equal(&kzg_c_E)?;
@ -516,18 +526,8 @@ where
// incircuit_eval_W.enforce_equal(&eval_W)?; // incircuit_eval_W.enforce_equal(&eval_W)?;
// incircuit_eval_E.enforce_equal(&eval_E)?; // incircuit_eval_E.enforce_equal(&eval_E)?;
// 8. compute the NIFS.V challenge and check that matches the one from the public input (so we
// 8.b check the NIFS.V challenge matches the one from the public input (so we
// avoid the verifier computing it) // avoid the verifier computing it)
let cmT =
NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?;
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
pp_hash,
U_i_vec,
u_i.clone(),
cmT.clone(),
)?;
let r_Fr = Boolean::le_bits_to_fp_var(&r_bits)?; let r_Fr = Boolean::le_bits_to_fp_var(&r_bits)?;
// check that the in-circuit computed r is equal to the inputted r // check that the in-circuit computed r is equal to the inputted r
let r = let r =
@ -569,38 +569,28 @@ where
<C as CurveGroup>::BaseField: PrimeField, <C as CurveGroup>::BaseField: PrimeField,
C::ScalarField: Absorb, C::ScalarField: Absorb,
{ {
pub fn get_challenges_native(
poseidon_config: &PoseidonConfig<C::ScalarField>,
pub fn get_challenges_native<T: Transcript<C::ScalarField>>(
transcript: &mut T,
U_i: CommittedInstance<C>, U_i: CommittedInstance<C>,
) -> Result<(C::ScalarField, C::ScalarField), Error> {
let (cmE_x_limbs, cmE_y_limbs) = nonnative_affine_to_field_elements(U_i.cmE)?;
let (cmW_x_limbs, cmW_y_limbs) = nonnative_affine_to_field_elements(U_i.cmW)?;
let transcript = &mut PoseidonTranscript::<C>::new(poseidon_config);
) -> (C::ScalarField, C::ScalarField) {
// compute the KZG challenges, which are computed in-circuit and checked that it matches // compute the KZG challenges, which are computed in-circuit and checked that it matches
// the inputted one // the inputted one
transcript.absorb_vec(&cmW_x_limbs);
transcript.absorb_vec(&cmW_y_limbs);
transcript.absorb_nonnative(&U_i.cmW);
let challenge_W = transcript.get_challenge(); let challenge_W = transcript.get_challenge();
transcript.absorb_vec(&cmE_x_limbs);
transcript.absorb_vec(&cmE_y_limbs);
transcript.absorb_nonnative(&U_i.cmE);
let challenge_E = transcript.get_challenge(); let challenge_E = transcript.get_challenge();
Ok((challenge_W, challenge_E))
(challenge_W, challenge_E)
} }
// compatible with the native get_challenges_native // compatible with the native get_challenges_native
pub fn get_challenges_gadget(
cs: ConstraintSystemRef<C::ScalarField>,
poseidon_config: &PoseidonConfig<C::ScalarField>,
pub fn get_challenges_gadget<S: CryptographicSponge, T: TranscriptVar<CF1<C>, S>>(
transcript: &mut T,
U_i: CommittedInstanceVar<C>, U_i: CommittedInstanceVar<C>,
) -> Result<(FpVar<C::ScalarField>, FpVar<C::ScalarField>), SynthesisError> { ) -> Result<(FpVar<C::ScalarField>, FpVar<C::ScalarField>), SynthesisError> {
let mut transcript =
PoseidonTranscriptVar::<CF1<C>>::new(cs.clone(), &poseidon_config.clone());
transcript.absorb_vec(&U_i.cmW.to_constraint_field()?[..])?;
transcript.absorb(&U_i.cmW.to_constraint_field()?)?;
let challenge_W = transcript.get_challenge()?; let challenge_W = transcript.get_challenge()?;
transcript.absorb_vec(&U_i.cmE.to_constraint_field()?[..])?;
transcript.absorb(&U_i.cmE.to_constraint_field()?)?;
let challenge_E = transcript.get_challenge()?; let challenge_E = transcript.get_challenge()?;
Ok((challenge_W, challenge_E)) Ok((challenge_W, challenge_E))
@ -852,6 +842,7 @@ pub mod tests {
fn test_kzg_challenge_gadget() { fn test_kzg_challenge_gadget() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript = PoseidonSponge::<Fr>::new(&poseidon_config);
let U_i = CommittedInstance::<Projective> { let U_i = CommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng), cmE: Projective::rand(&mut rng),
@ -862,21 +853,17 @@ pub mod tests {
// compute the challenge natively // compute the challenge natively
let (challenge_W, challenge_E) = let (challenge_W, challenge_E) =
KZGChallengesGadget::<Projective>::get_challenges_native(&poseidon_config, U_i.clone())
.unwrap();
KZGChallengesGadget::<Projective>::get_challenges_native(&mut transcript, U_i.clone());
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let U_iVar = let U_iVar =
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone())) CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
.unwrap(); .unwrap();
let mut transcript_var = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
let (challenge_W_Var, challenge_E_Var) = let (challenge_W_Var, challenge_E_Var) =
KZGChallengesGadget::<Projective>::get_challenges_gadget(
cs.clone(),
&poseidon_config,
U_iVar,
)
.unwrap();
KZGChallengesGadget::<Projective>::get_challenges_gadget(&mut transcript_var, U_iVar)
.unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
// check that the natively computed and in-circuit computed hashes match // check that the natively computed and in-circuit computed hashes match

+ 103
- 91
folding-schemes/src/folding/nova/mod.rs

@ -1,11 +1,11 @@
/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf). /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
use ark_crypto_primitives::{
crh::{poseidon::CRH, CRHScheme},
sponge::{poseidon::PoseidonConfig, Absorb},
use ark_crypto_primitives::sponge::{
poseidon::{PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge,
}; };
use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::{BigInteger, Field, PrimeField, ToConstraintField};
use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
@ -14,19 +14,18 @@ use ark_std::rand::RngCore;
use ark_std::{One, Zero}; use ark_std::{One, Zero};
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::arith::r1cs::{extract_r1cs, extract_w_x, R1CS};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
nonnative::{
affine::nonnative_affine_to_field_elements, uint::nonnative_field_to_field_elements,
},
CF2,
};
use crate::folding::circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit};
use crate::folding::circuits::CF2;
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::utils::{get_cm_coordinates, pp_hash, vec::is_zero_vec};
use crate::transcript::{AbsorbNonNative, Transcript};
use crate::utils::vec::is_zero_vec;
use crate::Error; use crate::Error;
use crate::FoldingScheme; use crate::FoldingScheme;
use crate::{
arith::r1cs::{extract_r1cs, extract_w_x, R1CS},
utils::{get_cm_coordinates, pp_hash},
};
pub mod circuits; pub mod circuits;
pub mod decider_eth; pub mod decider_eth;
@ -57,6 +56,54 @@ impl CommittedInstance {
} }
} }
impl<C: CurveGroup> Absorb for CommittedInstance<C>
where
C::ScalarField: Absorb,
{
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
// This is never called
unimplemented!()
}
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
self.u.to_sponge_field_elements(dest);
self.x.to_sponge_field_elements(dest);
// We cannot call `to_native_sponge_field_elements(dest)` directly, as
// `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
// but here `F` is a generic `PrimeField`.
self.cmE
.to_native_sponge_field_elements_as_vec()
.to_sponge_field_elements(dest);
self.cmW
.to_native_sponge_field_elements_as_vec()
.to_sponge_field_elements(dest);
}
}
impl<C: CurveGroup> AbsorbNonNative<C::BaseField> for CommittedInstance<C>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
{
// Compatible with the in-circuit `CycleFoldCommittedInstanceVar::to_native_sponge_field_elements`
// in `cyclefold.rs`.
fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::BaseField>) {
[self.u].to_native_sponge_field_elements(dest);
self.x.to_native_sponge_field_elements(dest);
let (cmE_x, cmE_y) = match self.cmE.into_affine().xy() {
Some((&x, &y)) => (x, y),
None => (C::BaseField::zero(), C::BaseField::zero()),
};
let (cmW_x, cmW_y) = match self.cmW.into_affine().xy() {
Some((&x, &y)) => (x, y),
None => (C::BaseField::zero(), C::BaseField::zero()),
};
cmE_x.to_sponge_field_elements(dest);
cmE_y.to_sponge_field_elements(dest);
cmW_x.to_sponge_field_elements(dest);
cmW_y.to_sponge_field_elements(dest);
}
}
impl<C: CurveGroup> CommittedInstance<C> impl<C: CurveGroup> CommittedInstance<C>
where where
<C as Group>::ScalarField: Absorb, <C as Group>::ScalarField: Absorb,
@ -66,67 +113,21 @@ where
/// nova/circuits.rs::CommittedInstanceVar.hash. /// nova/circuits.rs::CommittedInstanceVar.hash.
/// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the
/// `CommittedInstance`. /// `CommittedInstance`.
pub fn hash(
pub fn hash<T: Transcript<C::ScalarField>>(
&self, &self,
poseidon_config: &PoseidonConfig<C::ScalarField>,
sponge: &T,
pp_hash: C::ScalarField, // public params hash pp_hash: C::ScalarField, // public params hash
i: C::ScalarField, i: C::ScalarField,
z_0: Vec<C::ScalarField>, z_0: Vec<C::ScalarField>,
z_i: Vec<C::ScalarField>, z_i: Vec<C::ScalarField>,
) -> Result<C::ScalarField, Error> {
let (cmE_x, cmE_y) = nonnative_affine_to_field_elements::<C>(self.cmE)?;
let (cmW_x, cmW_y) = nonnative_affine_to_field_elements::<C>(self.cmW)?;
CRH::<C::ScalarField>::evaluate(
poseidon_config,
vec![
vec![pp_hash, i],
z_0,
z_i,
vec![self.u],
self.x.clone(),
cmE_x,
cmE_y,
cmW_x,
cmW_y,
]
.concat(),
)
.map_err(|e| Error::Other(e.to_string()))
}
}
impl<C: CurveGroup> ToConstraintField<C::BaseField> for CommittedInstance<C>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
{
fn to_field_elements(&self) -> Option<Vec<C::BaseField>> {
let u = nonnative_field_to_field_elements(&self.u);
let x = self
.x
.iter()
.flat_map(nonnative_field_to_field_elements)
.collect::<Vec<_>>();
let (cmE_x, cmE_y, cmE_is_inf) = match self.cmE.into_affine().xy() {
Some((&x, &y)) => (x, y, C::BaseField::zero()),
None => (
C::BaseField::zero(),
C::BaseField::zero(),
C::BaseField::one(),
),
};
let (cmW_x, cmW_y, cmW_is_inf) = match self.cmW.into_affine().xy() {
Some((&x, &y)) => (x, y, C::BaseField::zero()),
None => (
C::BaseField::zero(),
C::BaseField::zero(),
C::BaseField::one(),
),
};
// Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate in the corresponding circuit
let is_inf = cmE_is_inf.double() + cmW_is_inf;
Some([u, x, vec![cmE_x, cmE_y, cmW_x, cmW_y, is_inf]].concat())
) -> C::ScalarField {
let mut sponge = sponge.clone();
sponge.absorb(&pp_hash);
sponge.absorb(&i);
sponge.absorb(&z_0);
sponge.absorb(&z_i);
sponge.absorb(&self);
sponge.squeeze_field_elements(1)[0]
} }
} }
@ -137,16 +138,15 @@ where
/// hash_cyclefold implements the committed instance hash compatible with the gadget implemented in /// hash_cyclefold implements the committed instance hash compatible with the gadget implemented in
/// nova/cyclefold.rs::CycleFoldCommittedInstanceVar.hash. /// nova/cyclefold.rs::CycleFoldCommittedInstanceVar.hash.
/// Returns `H(U_i)`, where `U_i` is the `CommittedInstance` for CycleFold. /// Returns `H(U_i)`, where `U_i` is the `CommittedInstance` for CycleFold.
pub fn hash_cyclefold(
pub fn hash_cyclefold<T: Transcript<C::BaseField>>(
&self, &self,
poseidon_config: &PoseidonConfig<C::BaseField>,
sponge: &T,
pp_hash: C::BaseField, // public params hash pp_hash: C::BaseField, // public params hash
) -> Result<C::BaseField, Error> {
CRH::<C::BaseField>::evaluate(
poseidon_config,
[vec![pp_hash], self.to_field_elements().unwrap()].concat(),
)
.map_err(|e| Error::Other(e.to_string()))
) -> C::BaseField {
let mut sponge = sponge.clone();
sponge.absorb(&pp_hash);
sponge.absorb_nonnative(self);
sponge.squeeze_field_elements(1)[0]
} }
} }
@ -453,6 +453,11 @@ where
_rng: impl RngCore, _rng: impl RngCore,
external_inputs: Vec<C1::ScalarField>, external_inputs: Vec<C1::ScalarField>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// `sponge` is for digest computation.
let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
// `transcript` is for challenge generation.
let mut transcript = sponge.clone();
let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>; let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
if self.z_i.len() != self.F.state_len() { if self.z_i.len() != self.F.state_len() {
@ -488,12 +493,12 @@ where
// r_bits is the r used to the RLC of the F' instances // r_bits is the r used to the RLC of the F' instances
let r_bits = ChallengeGadget::<C1>::get_challenge_native( let r_bits = ChallengeGadget::<C1>::get_challenge_native(
&self.poseidon_config,
&mut transcript,
self.pp_hash, self.pp_hash,
self.U_i.clone(), self.U_i.clone(),
self.u_i.clone(), self.u_i.clone(),
cmT, cmT,
)?;
);
let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
.ok_or(Error::OutOfBounds)?; .ok_or(Error::OutOfBounds)?;
let r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&r_bits)) let r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&r_bits))
@ -507,19 +512,17 @@ where
// folded instance output (public input, x) // folded instance output (public input, x)
// u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1}) // u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1})
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config,
&sponge,
self.pp_hash, self.pp_hash,
self.i + C1::ScalarField::one(), self.i + C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?;
);
// u_{i+1}.x[1] = H(cf_U_{i+1}) // u_{i+1}.x[1] = H(cf_U_{i+1})
let cf_u_i1_x: C1::ScalarField; let cf_u_i1_x: C1::ScalarField;
if self.i == C1::ScalarField::zero() { if self.i == C1::ScalarField::zero() {
cf_u_i1_x = self
.cf_U_i
.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
// base case // base case
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_gc2: PhantomData, _gc2: PhantomData,
@ -584,16 +587,22 @@ where
// fold self.cf_U_i + cfW_U -> folded running with cfW // fold self.cf_U_i + cfW_U -> folded running with cfW
let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, _) = self.fold_cyclefold_circuit( let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, _) = self.fold_cyclefold_circuit(
&mut transcript,
self.cf_W_i.clone(), // CycleFold running instance witness self.cf_W_i.clone(), // CycleFold running instance witness
self.cf_U_i.clone(), // CycleFold running instance self.cf_U_i.clone(), // CycleFold running instance
cfW_u_i_x, cfW_u_i_x,
cfW_circuit, cfW_circuit,
)?; )?;
// fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE
let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) =
self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?;
let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = self.fold_cyclefold_circuit(
&mut transcript,
cfW_W_i1,
cfW_U_i1.clone(),
cfE_u_i_x,
cfE_circuit,
)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_gc2: PhantomData, _gc2: PhantomData,
@ -697,6 +706,8 @@ where
incoming_instance: Self::IncomingInstance, incoming_instance: Self::IncomingInstance,
cyclefold_instance: Self::CFInstance, cyclefold_instance: Self::CFInstance,
) -> Result<(), Error> { ) -> Result<(), Error> {
let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
if num_steps == C1::ScalarField::zero() { if num_steps == C1::ScalarField::zero() {
if z_0 != z_i { if z_0 != z_i {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
@ -716,12 +727,12 @@ where
// check that u_i's output points to the running instance // check that u_i's output points to the running instance
// u_i.X[0] == H(i, z_0, z_i, U_i) // u_i.X[0] == H(i, z_0, z_i, U_i)
let expected_u_i_x = U_i.hash(&vp.poseidon_config, pp_hash, num_steps, z_0, z_i.clone())?;
let expected_u_i_x = U_i.hash(&sponge, pp_hash, num_steps, z_0, z_i.clone());
if expected_u_i_x != u_i.x[0] { if expected_u_i_x != u_i.x[0] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
// u_i.X[1] == H(cf_U_i) // u_i.X[1] == H(cf_U_i)
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config, pp_hash)?;
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
if expected_cf_u_i_x != u_i.x[1] { if expected_cf_u_i_x != u_i.x[1] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
@ -790,8 +801,9 @@ where
{ {
// folds the given cyclefold circuit and its instances // folds the given cyclefold circuit and its instances
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn fold_cyclefold_circuit(
fn fold_cyclefold_circuit<T: Transcript<C1::ScalarField>>(
&self, &self,
transcript: &mut T,
cf_W_i: Witness<C2>, // witness of the running instance cf_W_i: Witness<C2>, // witness of the running instance
cf_U_i: CommittedInstance<C2>, // running instance cf_U_i: CommittedInstance<C2>, // running instance
cf_u_i_x: Vec<C2::ScalarField>, cf_u_i_x: Vec<C2::ScalarField>,
@ -808,7 +820,7 @@ where
Error, Error,
> { > {
fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>( fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>(
&self.poseidon_config,
transcript,
self.cf_r1cs.clone(), self.cf_r1cs.clone(),
self.cf_cs_pp.clone(), self.cf_cs_pp.clone(),
self.pp_hash, self.pp_hash,

+ 11
- 8
folding-schemes/src/folding/nova/nifs.rs

@ -183,7 +183,7 @@ where
} }
pub fn prove_commitments( pub fn prove_commitments(
tr: &mut impl Transcript<C>,
tr: &mut impl Transcript<C::ScalarField>,
cs_prover_params: &CS::ProverParams, cs_prover_params: &CS::ProverParams,
w: &Witness<C>, w: &Witness<C>,
ci: &CommittedInstance<C>, ci: &CommittedInstance<C>,
@ -200,7 +200,10 @@ where
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use ark_crypto_primitives::sponge::poseidon::PoseidonConfig;
use ark_crypto_primitives::sponge::{
poseidon::{PoseidonConfig, PoseidonSponge},
CryptographicSponge,
};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_pallas::{Fr, Projective}; use ark_pallas::{Fr, Projective};
use ark_std::{ops::Mul, UniformRand}; use ark_std::{ops::Mul, UniformRand};
@ -209,7 +212,7 @@ pub mod tests {
use crate::commitment::pedersen::{Params as PedersenParams, Pedersen}; use crate::commitment::pedersen::{Params as PedersenParams, Pedersen};
use crate::folding::nova::circuits::ChallengeGadget; use crate::folding::nova::circuits::ChallengeGadget;
use crate::folding::nova::traits::NovaR1CS; use crate::folding::nova::traits::NovaR1CS;
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
use crate::transcript::poseidon::poseidon_canonical_config;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub(crate) fn prepare_simple_fold_inputs<C>() -> ( pub(crate) fn prepare_simple_fold_inputs<C>() -> (
@ -258,16 +261,16 @@ pub mod tests {
.unwrap(); .unwrap();
let poseidon_config = poseidon_canonical_config::<C::ScalarField>(); let poseidon_config = poseidon_canonical_config::<C::ScalarField>();
let mut transcript = PoseidonSponge::<C::ScalarField>::new(&poseidon_config);
let pp_hash = C::ScalarField::from(42u32); // only for test let pp_hash = C::ScalarField::from(42u32); // only for test
let r_bits = ChallengeGadget::<C>::get_challenge_native( let r_bits = ChallengeGadget::<C>::get_challenge_native(
&poseidon_config,
&mut transcript,
pp_hash, pp_hash,
ci1.clone(), ci1.clone(),
ci2.clone(), ci2.clone(),
cmT, cmT,
)
.unwrap();
);
let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
let (w3, ci3) = let (w3, ci3) =
@ -364,9 +367,9 @@ pub mod tests {
.unwrap(); .unwrap();
// init Prover's transcript // init Prover's transcript
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
// prove the ci3.cmE, ci3.cmW, cmT commitments // prove the ci3.cmE, ci3.cmW, cmT commitments
let cm_proofs = NIFS::<Projective, Pedersen<Projective>>::prove_commitments( let cm_proofs = NIFS::<Projective, Pedersen<Projective>>::prove_commitments(

+ 19
- 23
folding-schemes/src/folding/protogalaxy/folding.rs

@ -6,12 +6,10 @@ use ark_poly::{
univariate::{DensePolynomial, SparsePolynomial}, univariate::{DensePolynomial, SparsePolynomial},
DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial, DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial,
}; };
use ark_std::log2;
use ark_std::{cfg_into_iter, Zero};
use ark_std::{cfg_into_iter, log2, Zero};
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::marker::PhantomData; use std::marker::PhantomData;
use super::traits::ProtoGalaxyTranscript;
use super::utils::{all_powers, betas_star, exponential_powers}; use super::utils::{all_powers, betas_star, exponential_powers};
use super::ProtoGalaxyError; use super::ProtoGalaxyError;
use super::{CommittedInstance, Witness}; use super::{CommittedInstance, Witness};
@ -36,7 +34,7 @@ where
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
/// implements the non-interactive Prover from the folding scheme described in section 4 /// implements the non-interactive Prover from the folding scheme described in section 4
pub fn prove( pub fn prove(
transcript: &mut (impl Transcript<C> + ProtoGalaxyTranscript<C>),
transcript: &mut impl Transcript<C::ScalarField>,
r1cs: &R1CS<C::ScalarField>, r1cs: &R1CS<C::ScalarField>,
// running instance // running instance
instance: &CommittedInstance<C>, instance: &CommittedInstance<C>,
@ -81,10 +79,8 @@ where
} }
// absorb the committed instances // absorb the committed instances
transcript.absorb_committed_instance(instance)?;
for ci in vec_instances.iter() {
transcript.absorb_committed_instance(ci)?;
}
transcript.absorb(instance);
transcript.absorb(&vec_instances);
let delta = transcript.get_challenge(); let delta = transcript.get_challenge();
let deltas = exponential_powers(delta, t); let deltas = exponential_powers(delta, t);
@ -95,7 +91,7 @@ where
let F_X: SparsePolynomial<C::ScalarField> = let F_X: SparsePolynomial<C::ScalarField> =
calc_f_from_btree(&f_w, &instance.betas, &deltas).expect("Error calculating F[x]"); calc_f_from_btree(&f_w, &instance.betas, &deltas).expect("Error calculating F[x]");
let F_X_dense = DensePolynomial::from(F_X.clone()); let F_X_dense = DensePolynomial::from(F_X.clone());
transcript.absorb_vec(&F_X_dense.coeffs);
transcript.absorb(&F_X_dense.coeffs);
let alpha = transcript.get_challenge(); let alpha = transcript.get_challenge();
@ -187,7 +183,7 @@ where
return Err(Error::ProtoGalaxy(ProtoGalaxyError::RemainderNotZero)); return Err(Error::ProtoGalaxy(ProtoGalaxyError::RemainderNotZero));
} }
transcript.absorb_vec(&K_X.coeffs);
transcript.absorb(&K_X.coeffs);
let gamma = transcript.get_challenge(); let gamma = transcript.get_challenge();
@ -223,7 +219,7 @@ where
/// implements the non-interactive Verifier from the folding scheme described in section 4 /// implements the non-interactive Verifier from the folding scheme described in section 4
pub fn verify( pub fn verify(
transcript: &mut (impl Transcript<C> + ProtoGalaxyTranscript<C>),
transcript: &mut impl Transcript<C::ScalarField>,
r1cs: &R1CS<C::ScalarField>, r1cs: &R1CS<C::ScalarField>,
// running instance // running instance
instance: &CommittedInstance<C>, instance: &CommittedInstance<C>,
@ -237,15 +233,13 @@ where
let n = r1cs.A.n_cols; let n = r1cs.A.n_cols;
// absorb the committed instances // absorb the committed instances
transcript.absorb_committed_instance(instance)?;
for ci in vec_instances.iter() {
transcript.absorb_committed_instance(ci)?;
}
transcript.absorb(instance);
transcript.absorb(&vec_instances);
let delta = transcript.get_challenge(); let delta = transcript.get_challenge();
let deltas = exponential_powers(delta, t); let deltas = exponential_powers(delta, t);
transcript.absorb_vec(&F_coeffs);
transcript.absorb(&F_coeffs);
let alpha = transcript.get_challenge(); let alpha = transcript.get_challenge();
let alphas = all_powers(alpha, n); let alphas = all_powers(alpha, n);
@ -266,7 +260,7 @@ where
let K_X: DensePolynomial<C::ScalarField> = let K_X: DensePolynomial<C::ScalarField> =
DensePolynomial::<C::ScalarField>::from_coefficients_vec(K_coeffs); DensePolynomial::<C::ScalarField>::from_coefficients_vec(K_coeffs);
transcript.absorb_vec(&K_X.coeffs);
transcript.absorb(&K_X.coeffs);
let gamma = transcript.get_challenge(); let gamma = transcript.get_challenge();
@ -380,12 +374,14 @@ fn eval_f(r1cs: &R1CS, w: &[F]) -> Result, Error> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
use ark_crypto_primitives::sponge::CryptographicSponge;
use ark_pallas::{Fr, Projective}; use ark_pallas::{Fr, Projective};
use ark_std::UniformRand; use ark_std::UniformRand;
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z}; use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::commitment::{pedersen::Pedersen, CommitmentScheme};
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
use crate::transcript::poseidon::poseidon_canonical_config;
pub(crate) fn check_instance<C: CurveGroup>( pub(crate) fn check_instance<C: CurveGroup>(
r1cs: &R1CS<C::ScalarField>, r1cs: &R1CS<C::ScalarField>,
@ -495,7 +491,7 @@ mod tests {
.unwrap(); .unwrap();
let instance_i = CommittedInstance::<Projective> { let instance_i = CommittedInstance::<Projective> {
phi: phi_i, phi: phi_i,
betas: betas.clone(),
betas: vec![],
e: Fr::zero(), e: Fr::zero(),
}; };
witnesses.push(witness_i); witnesses.push(witness_i);
@ -513,8 +509,8 @@ mod tests {
// init Prover & Verifier's transcript // init Prover & Verifier's transcript
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
let (folded_instance, folded_witness, F_coeffs, K_coeffs) = Folding::<Projective>::prove( let (folded_instance, folded_witness, F_coeffs, K_coeffs) = Folding::<Projective>::prove(
&mut transcript_p, &mut transcript_p,
@ -553,8 +549,8 @@ mod tests {
// init Prover & Verifier's transcript // init Prover & Verifier's transcript
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
let (mut running_witness, mut running_instance, _, _) = prepare_inputs(0); let (mut running_witness, mut running_instance, _, _) = prepare_inputs(0);

+ 10
- 0
folding-schemes/src/folding/protogalaxy/mod.rs

@ -1,8 +1,11 @@
/// Implements the scheme described in [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf) /// Implements the scheme described in [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf)
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_r1cs_std::fields::fp::FpVar;
use thiserror::Error; use thiserror::Error;
use super::circuits::nonnative::affine::NonNativeAffineVar;
pub mod folding; pub mod folding;
pub mod traits; pub mod traits;
pub(crate) mod utils; pub(crate) mod utils;
@ -14,6 +17,13 @@ pub struct CommittedInstance {
e: C::ScalarField, e: C::ScalarField,
} }
#[derive(Clone, Debug)]
pub struct CommittedInstanceVar<C: CurveGroup> {
phi: NonNativeAffineVar<C>,
betas: Vec<FpVar<C::ScalarField>>,
e: FpVar<C::ScalarField>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Witness<F: PrimeField> { pub struct Witness<F: PrimeField> {
w: Vec<F>, w: Vec<F>,

+ 36
- 17
folding-schemes/src/folding/protogalaxy/traits.rs

@ -1,23 +1,42 @@
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group};
use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb};
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget};
use ark_relations::r1cs::SynthesisError;
use super::CommittedInstance;
use crate::transcript::{poseidon::PoseidonTranscript, Transcript};
use crate::Error;
use super::{CommittedInstance, CommittedInstanceVar};
use crate::transcript::AbsorbNonNative;
/// ProtoGalaxyTranscript extends [`Transcript`] with the method to absorb ProtoGalaxy's
/// CommittedInstance.
pub trait ProtoGalaxyTranscript<C: CurveGroup>: Transcript<C> {
fn absorb_committed_instance(&mut self, ci: &CommittedInstance<C>) -> Result<(), Error> {
self.absorb_point(&ci.phi)?;
self.absorb_vec(&ci.betas);
self.absorb(&ci.e);
Ok(())
// Implements the trait for absorbing ProtoGalaxy's CommittedInstance.
impl<C: CurveGroup> Absorb for CommittedInstance<C>
where
C::ScalarField: Absorb,
{
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
unimplemented!()
}
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
self.phi
.to_native_sponge_field_elements_as_vec()
.to_sponge_field_elements(dest);
self.betas.to_sponge_field_elements(dest);
self.e.to_sponge_field_elements(dest);
} }
} }
// Implements ProtoGalaxyTranscript for PoseidonTranscript
impl<C: CurveGroup> ProtoGalaxyTranscript<C> for PoseidonTranscript<C> where
<C as Group>::ScalarField: Absorb
{
// Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit.
impl<C: CurveGroup> AbsorbGadget<C::ScalarField> for CommittedInstanceVar<C> {
fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
unimplemented!()
}
fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
Ok([
self.phi.to_constraint_field()?,
self.betas.to_sponge_field_elements()?,
self.e.to_sponge_field_elements()?,
]
.concat())
}
} }

+ 1
- 2
folding-schemes/src/frontend/circom/mod.rs

@ -207,8 +207,7 @@ impl CircomFCircuit {
pub mod tests { pub mod tests {
use super::*; use super::*;
use ark_bn254::Fr; use ark_bn254::Fr;
use ark_r1cs_std::alloc::AllocVar;
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
use ark_relations::r1cs::ConstraintSystem;
// Tests the step_native function of CircomFCircuit. // Tests the step_native function of CircomFCircuit.
#[test] #[test]

+ 0
- 1
folding-schemes/src/frontend/circom/utils.rs

@ -110,7 +110,6 @@ mod tests {
use ark_circom::circom::{CircomBuilder, CircomConfig}; use ark_circom::circom::{CircomBuilder, CircomConfig};
use ark_circom::CircomCircuit; use ark_circom::CircomCircuit;
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
use std::path::PathBuf;
//To generate .r1cs and .wasm files, run the below command in the terminal. //To generate .r1cs and .wasm files, run the below command in the terminal.
//bash ./folding-schemes/src/frontend/circom/test_folder/compile.sh //bash ./folding-schemes/src/frontend/circom/test_folder/compile.sh

+ 1
- 3
folding-schemes/src/frontend/mod.rs

@ -52,9 +52,7 @@ pub mod tests {
use super::*; use super::*;
use ark_bn254::Fr; use ark_bn254::Fr;
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
use ark_relations::r1cs::{
ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
use core::marker::PhantomData; use core::marker::PhantomData;
/// CubicFCircuit is a struct that implements the FCircuit trait, for the R1CS example circuit /// CubicFCircuit is a struct that implements the FCircuit trait, for the R1CS example circuit

+ 89
- 17
folding-schemes/src/transcript/mod.rs

@ -1,31 +1,103 @@
use crate::Error;
use ark_crypto_primitives::sponge::{constraints::CryptographicSpongeVar, CryptographicSponge};
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar};
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use ark_std::fmt::Debug;
use ark_r1cs_std::{
boolean::Boolean, fields::fp::FpVar, groups::CurveVar, ToConstraintFieldGadget,
};
use ark_relations::r1cs::SynthesisError;
pub mod poseidon; pub mod poseidon;
pub trait Transcript<C: CurveGroup> {
type TranscriptConfig: Debug;
/// An interface for objects that can be absorbed by a `Transcript`.
///
/// Matches `Absorb` in `ark-crypto-primitives`.
pub trait AbsorbNonNative<F: PrimeField> {
/// Converts the object into field elements that can be absorbed by a `Transcript`.
/// Append the list to `dest`
fn to_native_sponge_field_elements(&self, dest: &mut Vec<F>);
fn new(config: &Self::TranscriptConfig) -> Self;
fn absorb(&mut self, v: &C::ScalarField);
fn absorb_vec(&mut self, v: &[C::ScalarField]);
fn absorb_point(&mut self, v: &C) -> Result<(), Error>;
fn get_challenge(&mut self) -> C::ScalarField;
/// Converts the object into field elements that can be absorbed by a `Transcript`.
/// Return the list as `Vec`
fn to_native_sponge_field_elements_as_vec(&self) -> Vec<F> {
let mut result = Vec::new();
self.to_native_sponge_field_elements(&mut result);
result
}
}
/// An interface for objects that can be absorbed by a `TranscriptVar` whose constraint field
/// is `F`.
///
/// Matches `AbsorbGadget` in `ark-crypto-primitives`.
pub trait AbsorbNonNativeGadget<F: PrimeField> {
/// Converts the object into field elements that can be absorbed by a `TranscriptVar`.
fn to_native_sponge_field_elements(&self) -> Result<Vec<FpVar<F>>, SynthesisError>;
}
pub trait Transcript<F: PrimeField>: CryptographicSponge {
/// `absorb_point` is for absorbing points whose `BaseField` is the field of
/// the sponge, i.e., the type `C` of these points should satisfy
/// `C::BaseField = F`.
///
/// If the sponge field `F` is `C::ScalarField`, call `absorb_nonnative`
/// instead.
fn absorb_point<C: CurveGroup<BaseField = F>>(&mut self, v: &C);
/// `absorb_nonnative` is for structs that contain non-native (field or
/// group) elements, including:
///
/// - A field element of type `T: PrimeField` that will be absorbed into a
/// sponge that operates in another field `F != T`.
/// - A group element of type `C: CurveGroup` that will be absorbed into a
/// sponge that operates in another field `F != C::BaseField`, e.g.,
/// `F = C::ScalarField`.
/// - A `CommittedInstance` on the secondary curve (used for CycleFold) that
/// will be absorbed into a sponge that operates in the (scalar field of
/// the) primary curve.
///
/// Note that although a `CommittedInstance` for `AugmentedFCircuit` on
/// the primary curve also contains non-native elements, we still regard
/// it as native, because the sponge is on the same curve.
fn absorb_nonnative<V: AbsorbNonNative<F>>(&mut self, v: &V);
fn get_challenge(&mut self) -> F;
/// get_challenge_nbits returns a field element of size nbits /// get_challenge_nbits returns a field element of size nbits
fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool>; fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool>;
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField>;
fn get_challenges(&mut self, n: usize) -> Vec<F>;
} }
pub trait TranscriptVar<F: PrimeField> {
type TranscriptVarConfig: Debug;
pub trait TranscriptVar<F: PrimeField, S: CryptographicSponge>:
CryptographicSpongeVar<F, S>
{
/// `absorb_point` is for absorbing points whose `BaseField` is the field of
/// the sponge, i.e., the type `C` of these points should satisfy
/// `C::BaseField = F`.
///
/// If the sponge field `F` is `C::ScalarField`, call `absorb_nonnative`
/// instead.
fn absorb_point<C: CurveGroup<BaseField = F>, GC: CurveVar<C, F> + ToConstraintFieldGadget<F>>(
&mut self,
v: &GC,
) -> Result<(), SynthesisError>;
/// `absorb_nonnative` is for structs that contain non-native (field or
/// group) elements, including:
///
/// - A field element of type `T: PrimeField` that will be absorbed into a
/// sponge that operates in another field `F != T`.
/// - A group element of type `C: CurveGroup` that will be absorbed into a
/// sponge that operates in another field `F != C::BaseField`, e.g.,
/// `F = C::ScalarField`.
/// - A `CommittedInstance` on the secondary curve (used for CycleFold) that
/// will be absorbed into a sponge that operates in the (scalar field of
/// the) primary curve.
///
/// Note that although a `CommittedInstance` for `AugmentedFCircuit` on
/// the primary curve also contains non-native elements, we still regard
/// it as native, because the sponge is on the same curve.
fn absorb_nonnative<V: AbsorbNonNativeGadget<F>>(
&mut self,
v: &V,
) -> Result<(), SynthesisError>;
fn new(cs: ConstraintSystemRef<F>, poseidon_config: &Self::TranscriptVarConfig) -> Self;
fn absorb(&mut self, v: FpVar<F>) -> Result<(), SynthesisError>;
fn absorb_vec(&mut self, v: &[FpVar<F>]) -> Result<(), SynthesisError>;
fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError>; fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError>;
/// returns the bit representation of the challenge, we use its output in-circuit for the /// returns the bit representation of the challenge, we use its output in-circuit for the
/// `GC.scalar_mul_le` method. /// `GC.scalar_mul_le` method.

+ 123
- 96
folding-schemes/src/transcript/poseidon.rs

@ -3,114 +3,86 @@ use ark_crypto_primitives::sponge::{
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
Absorb, CryptographicSponge, Absorb, CryptographicSponge,
}; };
use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::{BigInteger, Field, PrimeField};
use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar};
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use ark_std::Zero;
use crate::transcript::Transcript;
use crate::Error;
use super::TranscriptVar;
/// PoseidonTranscript implements the Transcript trait using the Poseidon hash
pub struct PoseidonTranscript<C: CurveGroup>
where
<C as Group>::ScalarField: Absorb,
{
sponge: PoseidonSponge<C::ScalarField>,
}
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{
boolean::Boolean, fields::fp::FpVar, groups::CurveVar, ToConstraintFieldGadget,
};
use ark_relations::r1cs::SynthesisError;
impl<C: CurveGroup> Transcript<C> for PoseidonTranscript<C>
where
<C as Group>::ScalarField: Absorb,
{
type TranscriptConfig = PoseidonConfig<C::ScalarField>;
use super::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar};
fn new(poseidon_config: &Self::TranscriptConfig) -> Self {
let sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
Self { sponge }
}
fn absorb(&mut self, v: &C::ScalarField) {
self.sponge.absorb(&v);
impl<F: PrimeField + Absorb> Transcript<F> for PoseidonSponge<F> {
// Compatible with the in-circuit `TranscriptVar::absorb_point`
fn absorb_point<C: CurveGroup<BaseField = F>>(&mut self, p: &C) {
let (x, y) = match p.into_affine().xy() {
Some((&x, &y)) => (x, y),
None => (C::BaseField::zero(), C::BaseField::zero()),
};
self.absorb(&x);
self.absorb(&y);
} }
fn absorb_vec(&mut self, v: &[C::ScalarField]) {
self.sponge.absorb(&v);
fn absorb_nonnative<V: AbsorbNonNative<F>>(&mut self, v: &V) {
self.absorb(&v.to_native_sponge_field_elements_as_vec());
} }
fn absorb_point(&mut self, p: &C) -> Result<(), Error> {
self.sponge.absorb(&prepare_point(p)?);
Ok(())
}
fn get_challenge(&mut self) -> C::ScalarField {
let c = self.sponge.squeeze_field_elements(1);
self.sponge.absorb(&c[0]);
fn get_challenge(&mut self) -> F {
let c = self.squeeze_field_elements(1);
self.absorb(&c[0]);
c[0] c[0]
} }
fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool> { fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool> {
self.sponge.squeeze_bits(nbits)
let bits = self.squeeze_bits(nbits);
self.absorb(&F::from(F::BigInt::from_bits_le(&bits)));
bits
} }
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField> {
let c = self.sponge.squeeze_field_elements(n);
self.sponge.absorb(&c);
fn get_challenges(&mut self, n: usize) -> Vec<F> {
let c = self.squeeze_field_elements(n);
self.absorb(&c);
c c
} }
} }
// Returns the point coordinates in Fr, so it can be absorbed by the transcript. It does not work
// over bytes in order to have a logic that can be reproduced in-circuit.
fn prepare_point<C: CurveGroup>(p: &C) -> Result<Vec<C::ScalarField>, Error> {
let affine = p.into_affine();
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
let xy = affine.xy().unwrap_or(zero_point);
let x_bi =
xy.0.to_base_prime_field_elements()
.next()
.expect("a")
.into_bigint();
let y_bi =
xy.1.to_base_prime_field_elements()
.next()
.expect("a")
.into_bigint();
Ok(vec![
C::ScalarField::from_le_bytes_mod_order(x_bi.to_bytes_le().as_ref()),
C::ScalarField::from_le_bytes_mod_order(y_bi.to_bytes_le().as_ref()),
])
}
/// PoseidonTranscriptVar implements the gadget compatible with PoseidonTranscript
pub struct PoseidonTranscriptVar<F: PrimeField> {
sponge: PoseidonSpongeVar<F>,
}
impl<F: PrimeField> TranscriptVar<F> for PoseidonTranscriptVar<F> {
type TranscriptVarConfig = PoseidonConfig<F>;
fn new(cs: ConstraintSystemRef<F>, poseidon_config: &Self::TranscriptVarConfig) -> Self {
let sponge = PoseidonSpongeVar::<F>::new(cs, poseidon_config);
Self { sponge }
impl<F: PrimeField> TranscriptVar<F, PoseidonSponge<F>> for PoseidonSpongeVar<F> {
fn absorb_point<
C: CurveGroup<BaseField = F>,
GC: CurveVar<C, F> + ToConstraintFieldGadget<F>,
>(
&mut self,
v: &GC,
) -> Result<(), SynthesisError> {
let mut vec = v.to_constraint_field()?;
// The last element in the vector tells whether the point is infinity,
// but we can in fact avoid absorbing it without loss of soundness.
// This is because the `to_constraint_field` method internally invokes
// [`ProjectiveVar::to_afine`](https://github.com/arkworks-rs/r1cs-std/blob/4020fbc22625621baa8125ede87abaeac3c1ca26/src/groups/curves/short_weierstrass/mod.rs#L160-L195),
// which guarantees that an infinity point is represented as `(0, 0)`,
// but the y-coordinate of a non-infinity point is never 0 (for why, see
// https://crypto.stackexchange.com/a/108242 ).
vec.pop();
self.absorb(&vec)
} }
fn absorb(&mut self, v: FpVar<F>) -> Result<(), SynthesisError> {
self.sponge.absorb(&v)
}
fn absorb_vec(&mut self, v: &[FpVar<F>]) -> Result<(), SynthesisError> {
self.sponge.absorb(&v)
fn absorb_nonnative<V: AbsorbNonNativeGadget<F>>(
&mut self,
v: &V,
) -> Result<(), SynthesisError> {
self.absorb(&v.to_native_sponge_field_elements()?)
} }
fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError> { fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError> {
let c = self.sponge.squeeze_field_elements(1)?;
self.sponge.absorb(&c[0])?;
let c = self.squeeze_field_elements(1)?;
self.absorb(&c[0])?;
Ok(c[0].clone()) Ok(c[0].clone())
} }
/// returns the bit representation of the challenge, we use its output in-circuit for the /// returns the bit representation of the challenge, we use its output in-circuit for the
/// `GC.scalar_mul_le` method. /// `GC.scalar_mul_le` method.
fn get_challenge_nbits(&mut self, nbits: usize) -> Result<Vec<Boolean<F>>, SynthesisError> { fn get_challenge_nbits(&mut self, nbits: usize) -> Result<Vec<Boolean<F>>, SynthesisError> {
self.sponge.squeeze_bits(nbits)
let bits = self.squeeze_bits(nbits)?;
self.absorb(&Boolean::le_bits_to_fp_var(&bits)?)?;
Ok(bits)
} }
fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> { fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
let c = self.sponge.squeeze_field_elements(n)?;
self.sponge.absorb(&c)?;
let c = self.squeeze_field_elements(n)?;
self.absorb(&c)?;
Ok(c) Ok(c)
} }
} }
@ -147,12 +119,17 @@ pub fn poseidon_canonical_config() -> PoseidonConfig {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use crate::folding::circuits::nonnative::affine::NonNativeAffineVar;
use super::*; use super::*;
use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as G1};
use ark_grumpkin::Projective;
use ark_r1cs_std::{alloc::AllocVar, groups::CurveVar, R1CSVar};
use ark_bn254::{constraints::GVar, g1::Config, Fq, Fr, G1Projective as G1};
use ark_ec::Group;
use ark_ff::UniformRand;
use ark_r1cs_std::{
alloc::AllocVar, groups::curves::short_weierstrass::ProjectiveVar, R1CSVar,
};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use std::ops::Mul;
use ark_std::test_rng;
// Test with value taken from https://github.com/iden3/circomlibjs/blob/43cc582b100fc3459cf78d903a6f538e5d7f38ee/test/poseidon.js#L32 // Test with value taken from https://github.com/iden3/circomlibjs/blob/43cc582b100fc3459cf78d903a6f538e5d7f38ee/test/poseidon.js#L32
#[test] #[test]
@ -178,19 +155,69 @@ pub mod tests {
); );
} }
#[test]
fn test_transcript_and_transcriptvar_absorb_native_point() {
// use 'native' transcript
let config = poseidon_canonical_config::<Fq>();
let mut tr = PoseidonSponge::<Fq>::new(&config);
let rng = &mut test_rng();
let p = G1::rand(rng);
tr.absorb_point(&p);
let c = tr.get_challenge();
// use 'gadget' transcript
let cs = ConstraintSystem::<Fq>::new_ref();
let mut tr_var = PoseidonSpongeVar::<Fq>::new(cs.clone(), &config);
let p_var = ProjectiveVar::<Config, FpVar<Fq>>::new_witness(
ConstraintSystem::<Fq>::new_ref(),
|| Ok(p),
)
.unwrap();
tr_var.absorb_point(&p_var).unwrap();
let c_var = tr_var.get_challenge().unwrap();
// assert that native & gadget transcripts return the same challenge
assert_eq!(c, c_var.value().unwrap());
}
#[test]
fn test_transcript_and_transcriptvar_absorb_nonnative_point() {
// use 'native' transcript
let config = poseidon_canonical_config::<Fr>();
let mut tr = PoseidonSponge::<Fr>::new(&config);
let rng = &mut test_rng();
let p = G1::rand(rng);
tr.absorb_nonnative(&p);
let c = tr.get_challenge();
// use 'gadget' transcript
let cs = ConstraintSystem::<Fr>::new_ref();
let mut tr_var = PoseidonSpongeVar::<Fr>::new(cs.clone(), &config);
let p_var =
NonNativeAffineVar::<G1>::new_witness(ConstraintSystem::<Fr>::new_ref(), || Ok(p))
.unwrap();
tr_var.absorb_nonnative(&p_var).unwrap();
let c_var = tr_var.get_challenge().unwrap();
// assert that native & gadget transcripts return the same challenge
assert_eq!(c, c_var.value().unwrap());
}
#[test] #[test]
fn test_transcript_and_transcriptvar_get_challenge() { fn test_transcript_and_transcriptvar_get_challenge() {
// use 'native' transcript // use 'native' transcript
let config = poseidon_canonical_config::<Fr>(); let config = poseidon_canonical_config::<Fr>();
let mut tr = PoseidonTranscript::<G1>::new(&config);
let mut tr = PoseidonSponge::<Fr>::new(&config);
tr.absorb(&Fr::from(42_u32)); tr.absorb(&Fr::from(42_u32));
let c = tr.get_challenge(); let c = tr.get_challenge();
// use 'gadget' transcript // use 'gadget' transcript
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let mut tr_var = PoseidonTranscriptVar::<Fr>::new(cs.clone(), &config);
let mut tr_var = PoseidonSpongeVar::<Fr>::new(cs.clone(), &config);
let v = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::from(42_u32))).unwrap(); let v = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::from(42_u32))).unwrap();
tr_var.absorb(v).unwrap();
tr_var.absorb(&v).unwrap();
let c_var = tr_var.get_challenge().unwrap(); let c_var = tr_var.get_challenge().unwrap();
// assert that native & gadget transcripts return the same challenge // assert that native & gadget transcripts return the same challenge
@ -203,7 +230,7 @@ pub mod tests {
// use 'native' transcript // use 'native' transcript
let config = poseidon_canonical_config::<Fq>(); let config = poseidon_canonical_config::<Fq>();
let mut tr = PoseidonTranscript::<Projective>::new(&config);
let mut tr = PoseidonSponge::<Fq>::new(&config);
tr.absorb(&Fq::from(42_u32)); tr.absorb(&Fq::from(42_u32));
// get challenge from native transcript // get challenge from native transcript
@ -211,9 +238,9 @@ pub mod tests {
// use 'gadget' transcript // use 'gadget' transcript
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let mut tr_var = PoseidonTranscriptVar::<Fq>::new(cs.clone(), &config);
let mut tr_var = PoseidonSpongeVar::<Fq>::new(cs.clone(), &config);
let v = FpVar::<Fq>::new_witness(cs.clone(), || Ok(Fq::from(42_u32))).unwrap(); let v = FpVar::<Fq>::new_witness(cs.clone(), || Ok(Fq::from(42_u32))).unwrap();
tr_var.absorb(v).unwrap();
tr_var.absorb(&v).unwrap();
// get challenge from circuit transcript // get challenge from circuit transcript
let c_var = tr_var.get_challenge_nbits(nbits).unwrap(); let c_var = tr_var.get_challenge_nbits(nbits).unwrap();
@ -226,7 +253,7 @@ pub mod tests {
// native c*P // native c*P
let c_Fr = Fr::from_bigint(BigInteger::from_bits_le(&c_bits)).unwrap(); let c_Fr = Fr::from_bigint(BigInteger::from_bits_le(&c_bits)).unwrap();
let cP_native = P.mul(c_Fr);
let cP_native = P * c_Fr;
// native c*P using mul_bits_be (notice the .rev to convert the LE to BE) // native c*P using mul_bits_be (notice the .rev to convert the LE to BE)
let cP_native_bits = P.mul_bits_be(c_bits.into_iter().rev()); let cP_native_bits = P.mul_bits_be(c_bits.into_iter().rev());

+ 48
- 51
folding-schemes/src/utils/espresso/sum_check/mod.rs

@ -13,7 +13,7 @@ use crate::{
transcript::Transcript, transcript::Transcript,
utils::virtual_polynomial::{VPAuxInfo, VirtualPolynomial}, utils::virtual_polynomial::{VPAuxInfo, VirtualPolynomial},
}; };
use ark_ec::CurveGroup;
use ark_crypto_primitives::sponge::Absorb;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_poly::univariate::DensePolynomial; use ark_poly::univariate::DensePolynomial;
use ark_poly::{DenseMultilinearExtension, DenseUVPolynomial, Polynomial}; use ark_poly::{DenseMultilinearExtension, DenseUVPolynomial, Polynomial};
@ -22,7 +22,6 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc};
use crate::utils::sum_check::structs::IOPProverMessage; use crate::utils::sum_check::structs::IOPProverMessage;
use crate::utils::sum_check::structs::IOPVerifierState; use crate::utils::sum_check::structs::IOPVerifierState;
use ark_ff::Field;
use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
use structs::{IOPProof, IOPProverState}; use structs::{IOPProof, IOPProverState};
@ -31,7 +30,7 @@ pub mod structs;
pub mod verifier; pub mod verifier;
/// A generic sum-check trait over a curve group /// A generic sum-check trait over a curve group
pub trait SumCheck<C: CurveGroup> {
pub trait SumCheck<F: PrimeField> {
type VirtualPolynomial; type VirtualPolynomial;
type VPAuxInfo; type VPAuxInfo;
type MultilinearExtension; type MultilinearExtension;
@ -40,27 +39,27 @@ pub trait SumCheck {
type SumCheckSubClaim: Clone + Debug + Default + PartialEq; type SumCheckSubClaim: Clone + Debug + Default + PartialEq;
/// Extract sum from the proof /// Extract sum from the proof
fn extract_sum(proof: &Self::SumCheckProof) -> C::ScalarField;
fn extract_sum(proof: &Self::SumCheckProof) -> F;
/// Generate proof of the sum of polynomial over {0,1}^`num_vars` /// Generate proof of the sum of polynomial over {0,1}^`num_vars`
/// ///
/// The polynomial is represented in the form of a VirtualPolynomial. /// The polynomial is represented in the form of a VirtualPolynomial.
fn prove( fn prove(
poly: &Self::VirtualPolynomial, poly: &Self::VirtualPolynomial,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<F>,
) -> Result<Self::SumCheckProof, PolyIOPErrors>; ) -> Result<Self::SumCheckProof, PolyIOPErrors>;
/// Verify the claimed sum using the proof /// Verify the claimed sum using the proof
fn verify( fn verify(
sum: C::ScalarField,
sum: F,
proof: &Self::SumCheckProof, proof: &Self::SumCheckProof,
aux_info: &Self::VPAuxInfo, aux_info: &Self::VPAuxInfo,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<F>,
) -> Result<Self::SumCheckSubClaim, PolyIOPErrors>; ) -> Result<Self::SumCheckSubClaim, PolyIOPErrors>;
} }
/// Trait for sum check protocol prover side APIs. /// Trait for sum check protocol prover side APIs.
pub trait SumCheckProver<C: CurveGroup>
pub trait SumCheckProver<F: PrimeField>
where where
Self: Sized, Self: Sized,
{ {
@ -77,12 +76,12 @@ where
/// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2). /// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
fn prove_round_and_update_state( fn prove_round_and_update_state(
&mut self, &mut self,
challenge: &Option<C::ScalarField>,
challenge: &Option<F>,
) -> Result<Self::ProverMessage, PolyIOPErrors>; ) -> Result<Self::ProverMessage, PolyIOPErrors>;
} }
/// Trait for sum check protocol verifier side APIs. /// Trait for sum check protocol verifier side APIs.
pub trait SumCheckVerifier<C: CurveGroup> {
pub trait SumCheckVerifier<F: PrimeField> {
type VPAuxInfo; type VPAuxInfo;
type ProverMessage; type ProverMessage;
type Challenge; type Challenge;
@ -100,7 +99,7 @@ pub trait SumCheckVerifier {
fn verify_round_and_update_state( fn verify_round_and_update_state(
&mut self, &mut self,
prover_msg: &Self::ProverMessage, prover_msg: &Self::ProverMessage,
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<F>,
) -> Result<Self::Challenge, PolyIOPErrors>; ) -> Result<Self::Challenge, PolyIOPErrors>;
/// This function verifies the deferred checks in the interactive version of /// This function verifies the deferred checks in the interactive version of
@ -113,7 +112,7 @@ pub trait SumCheckVerifier {
/// Larger field size guarantees smaller soundness error. /// Larger field size guarantees smaller soundness error.
fn check_and_generate_subclaim( fn check_and_generate_subclaim(
&self, &self,
asserted_sum: &C::ScalarField,
asserted_sum: &F,
) -> Result<Self::SumCheckSubClaim, PolyIOPErrors>; ) -> Result<Self::SumCheckSubClaim, PolyIOPErrors>;
} }
@ -129,42 +128,42 @@ pub struct SumCheckSubClaim {
} }
#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)] #[derive(Clone, Debug, Default, Copy, PartialEq, Eq)]
pub struct IOPSumCheck<C: CurveGroup, T: Transcript<C>> {
pub struct IOPSumCheck<F: PrimeField, T: Transcript<F>> {
#[doc(hidden)] #[doc(hidden)]
phantom: PhantomData<C>,
phantom: PhantomData<F>,
#[doc(hidden)] #[doc(hidden)]
phantom2: PhantomData<T>, phantom2: PhantomData<T>,
} }
impl<C: CurveGroup, T: Transcript<C>> SumCheck<C> for IOPSumCheck<C, T> {
type SumCheckProof = IOPProof<C::ScalarField>;
type VirtualPolynomial = VirtualPolynomial<C::ScalarField>;
type VPAuxInfo = VPAuxInfo<C::ScalarField>;
type MultilinearExtension = Arc<DenseMultilinearExtension<C::ScalarField>>;
type SumCheckSubClaim = SumCheckSubClaim<C::ScalarField>;
impl<F: PrimeField + Absorb, T: Transcript<F>> SumCheck<F> for IOPSumCheck<F, T> {
type SumCheckProof = IOPProof<F>;
type VirtualPolynomial = VirtualPolynomial<F>;
type VPAuxInfo = VPAuxInfo<F>;
type MultilinearExtension = Arc<DenseMultilinearExtension<F>>;
type SumCheckSubClaim = SumCheckSubClaim<F>;
fn extract_sum(proof: &Self::SumCheckProof) -> C::ScalarField {
fn extract_sum(proof: &Self::SumCheckProof) -> F {
let start = start_timer!(|| "extract sum"); let start = start_timer!(|| "extract sum");
let poly = DensePolynomial::from_coefficients_vec(proof.proofs[0].coeffs.clone()); let poly = DensePolynomial::from_coefficients_vec(proof.proofs[0].coeffs.clone());
let res = poly.evaluate(&C::ScalarField::ONE) + poly.evaluate(&C::ScalarField::ZERO);
let res = poly.evaluate(&F::ONE) + poly.evaluate(&F::ZERO);
end_timer!(start); end_timer!(start);
res res
} }
fn prove( fn prove(
poly: &VirtualPolynomial<C::ScalarField>,
transcript: &mut impl Transcript<C>,
) -> Result<IOPProof<C::ScalarField>, PolyIOPErrors> {
transcript.absorb(&C::ScalarField::from(poly.aux_info.num_variables as u64));
transcript.absorb(&C::ScalarField::from(poly.aux_info.max_degree as u64));
let mut prover_state: IOPProverState<C> = IOPProverState::prover_init(poly)?;
let mut challenge: Option<C::ScalarField> = None;
let mut prover_msgs: Vec<IOPProverMessage<C::ScalarField>> =
poly: &VirtualPolynomial<F>,
transcript: &mut impl Transcript<F>,
) -> Result<IOPProof<F>, PolyIOPErrors> {
transcript.absorb(&F::from(poly.aux_info.num_variables as u64));
transcript.absorb(&F::from(poly.aux_info.max_degree as u64));
let mut prover_state: IOPProverState<F> = IOPProverState::prover_init(poly)?;
let mut challenge: Option<F> = None;
let mut prover_msgs: Vec<IOPProverMessage<F>> =
Vec::with_capacity(poly.aux_info.num_variables); Vec::with_capacity(poly.aux_info.num_variables);
for _ in 0..poly.aux_info.num_variables { for _ in 0..poly.aux_info.num_variables {
let prover_msg: IOPProverMessage<C::ScalarField> =
let prover_msg: IOPProverMessage<F> =
IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)?; IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)?;
transcript.absorb_vec(&prover_msg.coeffs);
transcript.absorb(&prover_msg.coeffs);
prover_msgs.push(prover_msg); prover_msgs.push(prover_msg);
challenge = Some(transcript.get_challenge()); challenge = Some(transcript.get_challenge());
} }
@ -178,17 +177,17 @@ impl> SumCheck for IOPSumCheck {
} }
fn verify( fn verify(
claimed_sum: C::ScalarField,
proof: &IOPProof<C::ScalarField>,
aux_info: &VPAuxInfo<C::ScalarField>,
transcript: &mut impl Transcript<C>,
) -> Result<SumCheckSubClaim<C::ScalarField>, PolyIOPErrors> {
transcript.absorb(&C::ScalarField::from(aux_info.num_variables as u64));
transcript.absorb(&C::ScalarField::from(aux_info.max_degree as u64));
claimed_sum: F,
proof: &IOPProof<F>,
aux_info: &VPAuxInfo<F>,
transcript: &mut impl Transcript<F>,
) -> Result<SumCheckSubClaim<F>, PolyIOPErrors> {
transcript.absorb(&F::from(aux_info.num_variables as u64));
transcript.absorb(&F::from(aux_info.max_degree as u64));
let mut verifier_state = IOPVerifierState::verifier_init(aux_info); let mut verifier_state = IOPVerifierState::verifier_init(aux_info);
for i in 0..aux_info.num_variables { for i in 0..aux_info.num_variables {
let prover_msg = proof.proofs.get(i).expect("proof is incomplete"); let prover_msg = proof.proofs.get(i).expect("proof is incomplete");
transcript.absorb_vec(&prover_msg.coeffs);
transcript.absorb(&prover_msg.coeffs);
IOPVerifierState::verify_round_and_update_state( IOPVerifierState::verify_round_and_update_state(
&mut verifier_state, &mut verifier_state,
prover_msg, prover_msg,
@ -204,16 +203,15 @@ impl> SumCheck for IOPSumCheck {
pub mod tests { pub mod tests {
use std::sync::Arc; use std::sync::Arc;
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
use ark_crypto_primitives::sponge::CryptographicSponge;
use ark_ff::Field; use ark_ff::Field;
use ark_pallas::Fr; use ark_pallas::Fr;
use ark_pallas::Projective;
use ark_poly::DenseMultilinearExtension; use ark_poly::DenseMultilinearExtension;
use ark_poly::MultilinearExtension; use ark_poly::MultilinearExtension;
use ark_std::test_rng; use ark_std::test_rng;
use crate::transcript::poseidon::poseidon_canonical_config; use crate::transcript::poseidon::poseidon_canonical_config;
use crate::transcript::poseidon::PoseidonTranscript;
use crate::transcript::Transcript;
use crate::utils::sum_check::SumCheck; use crate::utils::sum_check::SumCheck;
use crate::utils::virtual_polynomial::VirtualPolynomial; use crate::utils::virtual_polynomial::VirtualPolynomial;
@ -227,20 +225,19 @@ pub mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
// sum-check prove // sum-check prove
let mut poseidon_transcript_prove: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let sum_check = IOPSumCheck::<Projective, PoseidonTranscript<Projective>>::prove(
let mut poseidon_transcript_prove: PoseidonSponge<Fr> =
PoseidonSponge::<Fr>::new(&poseidon_config);
let sum_check = IOPSumCheck::<Fr, PoseidonSponge<Fr>>::prove(
&virtual_poly, &virtual_poly,
&mut poseidon_transcript_prove, &mut poseidon_transcript_prove,
) )
.unwrap(); .unwrap();
// sum-check verify // sum-check verify
let claimed_sum =
IOPSumCheck::<Projective, PoseidonTranscript<Projective>>::extract_sum(&sum_check);
let mut poseidon_transcript_verify: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config);
let res_verify = IOPSumCheck::<Projective, PoseidonTranscript<Projective>>::verify(
let claimed_sum = IOPSumCheck::<Fr, PoseidonSponge<Fr>>::extract_sum(&sum_check);
let mut poseidon_transcript_verify: PoseidonSponge<Fr> =
PoseidonSponge::<Fr>::new(&poseidon_config);
let res_verify = IOPSumCheck::<Fr, PoseidonSponge<Fr>>::verify(
claimed_sum, claimed_sum,
&sum_check, &sum_check,
&virtual_poly.aux_info, &virtual_poly.aux_info,

+ 15
- 19
folding-schemes/src/utils/espresso/sum_check/prover.rs

@ -14,11 +14,9 @@ use crate::utils::{
lagrange_poly::compute_lagrange_interpolated_poly, multilinear_polynomial::fix_variables, lagrange_poly::compute_lagrange_interpolated_poly, multilinear_polynomial::fix_variables,
virtual_polynomial::VirtualPolynomial, virtual_polynomial::VirtualPolynomial,
}; };
use ark_ec::CurveGroup;
use ark_ff::Field;
use ark_ff::{batch_inversion, PrimeField}; use ark_ff::{batch_inversion, PrimeField};
use ark_poly::DenseMultilinearExtension; use ark_poly::DenseMultilinearExtension;
use ark_std::{cfg_into_iter, end_timer, start_timer, vec::Vec};
use ark_std::{cfg_into_iter, end_timer, start_timer};
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator}; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator};
use std::sync::Arc; use std::sync::Arc;
@ -28,9 +26,9 @@ use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
// #[cfg(feature = "parallel")] // #[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
impl<C: CurveGroup> SumCheckProver<C> for IOPProverState<C> {
type VirtualPolynomial = VirtualPolynomial<C::ScalarField>;
type ProverMessage = IOPProverMessage<C::ScalarField>;
impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
type VirtualPolynomial = VirtualPolynomial<F>;
type ProverMessage = IOPProverMessage<F>;
/// Initialize the prover state to argue for the sum of the input polynomial /// Initialize the prover state to argue for the sum of the input polynomial
/// over {0,1}^`num_vars`. /// over {0,1}^`num_vars`.
@ -49,9 +47,7 @@ impl SumCheckProver for IOPProverState {
poly: polynomial.clone(), poly: polynomial.clone(),
extrapolation_aux: (1..polynomial.aux_info.max_degree) extrapolation_aux: (1..polynomial.aux_info.max_degree)
.map(|degree| { .map(|degree| {
let points = (0..1 + degree as u64)
.map(C::ScalarField::from)
.collect::<Vec<_>>();
let points = (0..1 + degree as u64).map(F::from).collect::<Vec<_>>();
let weights = barycentric_weights(&points); let weights = barycentric_weights(&points);
(points, weights) (points, weights)
}) })
@ -65,7 +61,7 @@ impl SumCheckProver for IOPProverState {
/// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2). /// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
fn prove_round_and_update_state( fn prove_round_and_update_state(
&mut self, &mut self,
challenge: &Option<C::ScalarField>,
challenge: &Option<F>,
) -> Result<Self::ProverMessage, PolyIOPErrors> { ) -> Result<Self::ProverMessage, PolyIOPErrors> {
// let start = // let start =
// start_timer!(|| format!("sum check prove {}-th round and update state", // start_timer!(|| format!("sum check prove {}-th round and update state",
@ -90,7 +86,7 @@ impl SumCheckProver for IOPProverState {
// g(r_1, ..., r_{m-1}, x_m ... x_n) // g(r_1, ..., r_{m-1}, x_m ... x_n)
// //
// eval g over r_m, and mutate g to g(r_1, ... r_m,, x_{m+1}... x_n) // eval g over r_m, and mutate g to g(r_1, ... r_m,, x_{m+1}... x_n)
let mut flattened_ml_extensions: Vec<DenseMultilinearExtension<C::ScalarField>> = self
let mut flattened_ml_extensions: Vec<DenseMultilinearExtension<F>> = self
.poly .poly
.flattened_ml_extensions .flattened_ml_extensions
.par_iter() .par_iter()
@ -124,7 +120,7 @@ impl SumCheckProver for IOPProverState {
self.round += 1; self.round += 1;
let products_list = self.poly.products.clone(); let products_list = self.poly.products.clone();
let mut products_sum = vec![C::ScalarField::ZERO; self.poly.aux_info.max_degree + 1];
let mut products_sum = vec![F::ZERO; self.poly.aux_info.max_degree + 1];
// Step 2: generate sum for the partial evaluated polynomial: // Step 2: generate sum for the partial evaluated polynomial:
// f(r_1, ... r_m,, x_{m+1}... x_n) // f(r_1, ... r_m,, x_{m+1}... x_n)
@ -134,8 +130,8 @@ impl SumCheckProver for IOPProverState {
.fold( .fold(
|| { || {
( (
vec![(C::ScalarField::ZERO, C::ScalarField::ZERO); products.len()],
vec![C::ScalarField::ZERO; products.len() + 1],
vec![(F::ZERO, F::ZERO); products.len()],
vec![F::ZERO; products.len() + 1],
) )
}, },
|(mut buf, mut acc), b| { |(mut buf, mut acc), b| {
@ -146,17 +142,17 @@ impl SumCheckProver for IOPProverState {
*eval = table[b << 1]; *eval = table[b << 1];
*step = table[(b << 1) + 1] - table[b << 1]; *step = table[(b << 1) + 1] - table[b << 1];
}); });
acc[0] += buf.iter().map(|(eval, _)| eval).product::<C::ScalarField>();
acc[0] += buf.iter().map(|(eval, _)| eval).product::<F>();
acc[1..].iter_mut().for_each(|acc| { acc[1..].iter_mut().for_each(|acc| {
buf.iter_mut().for_each(|(eval, step)| *eval += step as &_); buf.iter_mut().for_each(|(eval, step)| *eval += step as &_);
*acc += buf.iter().map(|(eval, _)| eval).product::<C::ScalarField>();
*acc += buf.iter().map(|(eval, _)| eval).product::<F>();
}); });
(buf, acc) (buf, acc)
}, },
) )
.map(|(_, partial)| partial) .map(|(_, partial)| partial)
.reduce( .reduce(
|| vec![C::ScalarField::ZERO; products.len() + 1],
|| vec![F::ZERO; products.len() + 1],
|mut sum, partial| { |mut sum, partial| {
sum.iter_mut() sum.iter_mut()
.zip(partial.iter()) .zip(partial.iter())
@ -168,7 +164,7 @@ impl SumCheckProver for IOPProverState {
let extraploation = cfg_into_iter!(0..self.poly.aux_info.max_degree - products.len()) let extraploation = cfg_into_iter!(0..self.poly.aux_info.max_degree - products.len())
.map(|i| { .map(|i| {
let (points, weights) = &self.extrapolation_aux[products.len() - 1]; let (points, weights) = &self.extrapolation_aux[products.len() - 1];
let at = C::ScalarField::from((products.len() + 1 + i) as u64);
let at = F::from((products.len() + 1 + i) as u64);
extrapolate(points, weights, &sum, &at) extrapolate(points, weights, &sum, &at)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -184,7 +180,7 @@ impl SumCheckProver for IOPProverState {
.map(|x| Arc::new(x.clone())) .map(|x| Arc::new(x.clone()))
.collect(); .collect();
let prover_poly = compute_lagrange_interpolated_poly::<C::ScalarField>(&products_sum);
let prover_poly = compute_lagrange_interpolated_poly::<F>(&products_sum);
Ok(IOPProverMessage { Ok(IOPProverMessage {
coeffs: prover_poly.coeffs, coeffs: prover_poly.coeffs,
}) })

+ 7
- 8
folding-schemes/src/utils/espresso/sum_check/structs.rs

@ -10,7 +10,6 @@
//! This module defines structs that are shared by all sub protocols. //! This module defines structs that are shared by all sub protocols.
use crate::utils::virtual_polynomial::VirtualPolynomial; use crate::utils::virtual_polynomial::VirtualPolynomial;
use ark_ec::CurveGroup;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_serialize::CanonicalSerialize; use ark_serialize::CanonicalSerialize;
@ -33,28 +32,28 @@ pub struct IOPProverMessage {
/// Prover State of a PolyIOP. /// Prover State of a PolyIOP.
#[derive(Debug)] #[derive(Debug)]
pub struct IOPProverState<C: CurveGroup> {
pub struct IOPProverState<F: PrimeField> {
/// sampled randomness given by the verifier /// sampled randomness given by the verifier
pub challenges: Vec<C::ScalarField>,
pub challenges: Vec<F>,
/// the current round number /// the current round number
pub(crate) round: usize, pub(crate) round: usize,
/// pointer to the virtual polynomial /// pointer to the virtual polynomial
pub(crate) poly: VirtualPolynomial<C::ScalarField>,
pub(crate) poly: VirtualPolynomial<F>,
/// points with precomputed barycentric weights for extrapolating smaller /// points with precomputed barycentric weights for extrapolating smaller
/// degree uni-polys to `max_degree + 1` evaluations. /// degree uni-polys to `max_degree + 1` evaluations.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub(crate) extrapolation_aux: Vec<(Vec<C::ScalarField>, Vec<C::ScalarField>)>,
pub(crate) extrapolation_aux: Vec<(Vec<F>, Vec<F>)>,
} }
/// Verifier State of a PolyIOP, generic over a curve group /// Verifier State of a PolyIOP, generic over a curve group
#[derive(Debug)] #[derive(Debug)]
pub struct IOPVerifierState<C: CurveGroup> {
pub struct IOPVerifierState<F: PrimeField> {
pub(crate) round: usize, pub(crate) round: usize,
pub(crate) num_vars: usize, pub(crate) num_vars: usize,
pub(crate) finished: bool, pub(crate) finished: bool,
/// a list storing the univariate polynomial in evaluation form sent by the /// a list storing the univariate polynomial in evaluation form sent by the
/// prover at each round /// prover at each round
pub(crate) polynomials_received: Vec<Vec<C::ScalarField>>,
pub(crate) polynomials_received: Vec<Vec<F>>,
/// a list storing the randomness sampled by the verifier at each round /// a list storing the randomness sampled by the verifier at each round
pub(crate) challenges: Vec<C::ScalarField>,
pub(crate) challenges: Vec<F>,
} }

+ 12
- 12
folding-schemes/src/utils/espresso/sum_check/verifier.rs

@ -14,7 +14,7 @@ use super::{
SumCheckSubClaim, SumCheckVerifier, SumCheckSubClaim, SumCheckVerifier,
}; };
use crate::{transcript::Transcript, utils::virtual_polynomial::VPAuxInfo}; use crate::{transcript::Transcript, utils::virtual_polynomial::VPAuxInfo};
use ark_ec::CurveGroup;
use ark_crypto_primitives::sponge::Absorb;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_poly::Polynomial; use ark_poly::Polynomial;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
@ -24,11 +24,11 @@ use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
impl<C: CurveGroup> SumCheckVerifier<C> for IOPVerifierState<C> {
type VPAuxInfo = VPAuxInfo<C::ScalarField>;
type ProverMessage = IOPProverMessage<C::ScalarField>;
type Challenge = C::ScalarField;
type SumCheckSubClaim = SumCheckSubClaim<C::ScalarField>;
impl<F: PrimeField + Absorb> SumCheckVerifier<F> for IOPVerifierState<F> {
type VPAuxInfo = VPAuxInfo<F>;
type ProverMessage = IOPProverMessage<F>;
type Challenge = F;
type SumCheckSubClaim = SumCheckSubClaim<F>;
/// Initialize the verifier's state. /// Initialize the verifier's state.
fn verifier_init(index_info: &Self::VPAuxInfo) -> Self { fn verifier_init(index_info: &Self::VPAuxInfo) -> Self {
@ -46,9 +46,9 @@ impl SumCheckVerifier for IOPVerifierState {
fn verify_round_and_update_state( fn verify_round_and_update_state(
&mut self, &mut self,
prover_msg: &<IOPVerifierState<C> as SumCheckVerifier<C>>::ProverMessage,
transcript: &mut impl Transcript<C>,
) -> Result<<IOPVerifierState<C> as SumCheckVerifier<C>>::Challenge, PolyIOPErrors> {
prover_msg: &<IOPVerifierState<F> as SumCheckVerifier<F>>::ProverMessage,
transcript: &mut impl Transcript<F>,
) -> Result<<IOPVerifierState<F> as SumCheckVerifier<F>>::Challenge, PolyIOPErrors> {
let start = let start =
start_timer!(|| format!("sum check verify {}-th round and update state", self.round)); start_timer!(|| format!("sum check verify {}-th round and update state", self.round));
@ -83,7 +83,7 @@ impl SumCheckVerifier for IOPVerifierState {
fn check_and_generate_subclaim( fn check_and_generate_subclaim(
&self, &self,
asserted_sum: &C::ScalarField,
asserted_sum: &F,
) -> Result<Self::SumCheckSubClaim, PolyIOPErrors> { ) -> Result<Self::SumCheckSubClaim, PolyIOPErrors> {
let start = start_timer!(|| "sum check check and generate subclaim"); let start = start_timer!(|| "sum check check and generate subclaim");
if !self.finished { if !self.finished {
@ -136,8 +136,8 @@ impl SumCheckVerifier for IOPVerifierState {
.take(self.num_vars) .take(self.num_vars)
{ {
let poly = DensePolynomial::from_coefficients_slice(coeffs); let poly = DensePolynomial::from_coefficients_slice(coeffs);
let eval_at_one: C::ScalarField = poly.iter().sum();
let eval_at_zero: C::ScalarField = poly.coeffs[0];
let eval_at_one: F = poly.iter().sum();
let eval_at_zero: F = poly.coeffs[0];
let eval = eval_at_one + eval_at_zero; let eval = eval_at_one + eval_at_zero;
// the deferred check during the interactive phase: // the deferred check during the interactive phase:

+ 0
- 2
folding-schemes/src/utils/espresso/virtual_polynomial.rs

@ -18,8 +18,6 @@ use rayon::prelude::*;
use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, sync::Arc}; use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, sync::Arc};
use thiserror::Error; use thiserror::Error;
use ark_std::string::String;
//-- aritherrors //-- aritherrors
/// A `enum` specifying the possible failure modes of the arithmetics. /// A `enum` specifying the possible failure modes of the arithmetics.
#[derive(Error, Debug)] #[derive(Error, Debug)]

+ 1
- 1
folding-schemes/src/utils/lagrange_poly.rs

@ -52,7 +52,7 @@ mod tests {
use crate::utils::lagrange_poly::compute_lagrange_interpolated_poly; use crate::utils::lagrange_poly::compute_lagrange_interpolated_poly;
use ark_pallas::Fr; use ark_pallas::Fr;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use ark_std::{vec::Vec, UniformRand};
use ark_std::UniformRand;
use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
#[test] #[test]

+ 6
- 8
solidity-verifiers/src/verifiers/kzg.rs

@ -78,7 +78,8 @@ mod tests {
utils::HeaderInclusion, utils::HeaderInclusion,
ProtocolVerifierKey, ProtocolVerifierKey,
}; };
use ark_bn254::{Bn254, Fr, G1Projective as G1};
use ark_bn254::{Bn254, Fr};
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
use ark_ec::{AffineRepr, CurveGroup}; use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_std::rand::{RngCore, SeedableRng}; use ark_std::rand::{RngCore, SeedableRng};
@ -89,10 +90,7 @@ mod tests {
use folding_schemes::{ use folding_schemes::{
commitment::{kzg::KZG, CommitmentScheme}, commitment::{kzg::KZG, CommitmentScheme},
transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript},
Transcript,
},
transcript::{poseidon::poseidon_canonical_config, Transcript},
}; };
use super::KZG10Verifier; use super::KZG10Verifier;
@ -133,8 +131,8 @@ mod tests {
fn kzg_verifier_accepts_and_rejects_proofs() { fn kzg_verifier_accepts_and_rejects_proofs() {
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64()); let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_p = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
let transcript_v = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN); let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec())); let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
@ -159,7 +157,7 @@ mod tests {
let (x_proof, y_proof) = proof_affine.xy().unwrap(); let (x_proof, y_proof) = proof_affine.xy().unwrap();
let y = proof.eval.into_bigint().to_bytes_be(); let y = proof.eval.into_bigint().to_bytes_be();
transcript_v.absorb_point(&cm).unwrap();
transcript_v.absorb_nonnative(&cm);
let x = transcript_v.get_challenge(); let x = transcript_v.get_challenge();
let x = x.into_bigint().to_bytes_be(); let x = x.into_bigint().to_bytes_be();

Loading…
Cancel
Save