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`
This commit is contained in:
winderica
2024-07-08 09:25:08 +01:00
committed by GitHub
parent c17fcf56c6
commit 16d51d757b
36 changed files with 1030 additions and 1033 deletions

View File

@@ -1,3 +1,4 @@
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_std::One;
@@ -9,6 +10,7 @@ use ark_std::rand::Rng;
use super::Witness;
use crate::arith::{ccs::CCS, Arith};
use crate::commitment::CommitmentScheme;
use crate::transcript::AbsorbNonNative;
use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
@@ -118,6 +120,26 @@ impl<C: CurveGroup> CCCS<C> {
}
}
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)]
pub mod tests {
use ark_pallas::Fr;

View File

@@ -1,7 +1,8 @@
/// 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_ec::{CurveGroup, Group};
@@ -45,10 +46,7 @@ use crate::utils::virtual_polynomial::VPAuxInfo;
use crate::Error;
use crate::{
arith::{ccs::CCS, r1cs::extract_r1cs},
transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar,
},
transcript::TranscriptVar,
};
/// Committed CCS instance
@@ -142,12 +140,13 @@ where
#[allow(clippy::type_complexity)]
pub fn hash(
self,
crh_params: &CRHParametersVar<CF1<C>>,
sponge: &PoseidonSpongeVar<CF1<C>>,
pp_hash: FpVar<CF1<C>>,
i: FpVar<CF1<C>>,
z_0: Vec<FpVar<CF1<C>>>,
z_i: Vec<FpVar<CF1<C>>>,
) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> {
let mut sponge = sponge.clone();
let U_vec = [
self.C.to_constraint_field()?,
vec![self.u],
@@ -156,18 +155,19 @@ where
self.v,
]
.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
#[derive(Debug)]
pub struct ProofVar<C: CurveGroup> {
pub sc_proof: IOPProofVar<C>,
pub sc_proof: IOPProofVar<C::ScalarField>,
#[allow(clippy::type_complexity)]
pub sigmas_thetas: (Vec<Vec<FpVar<CF1<C>>>>, Vec<Vec<FpVar<CF1<C>>>>),
}
@@ -185,7 +185,7 @@ where
f().and_then(|val| {
let cs = cs.into();
let sc_proof = IOPProofVar::<C>::new_variable(
let sc_proof = IOPProofVar::<C::ScalarField>::new_variable(
cs.clone(),
|| Ok(val.borrow().sc_proof.clone()),
mode,
@@ -223,12 +223,11 @@ where
/// 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
#[allow(clippy::type_complexity)]
pub fn verify(
pub fn verify<S: CryptographicSponge, T: TranscriptVar<C::ScalarField, S>>(
cs: ConstraintSystemRef<CF1<C>>,
// only used the CCS params, not the matrices
ccs: &CCS<C::ScalarField>,
mut transcript: impl TranscriptVar<C::ScalarField>,
transcript: &mut T,
running_instances: &[LCCCSVar<C>],
new_instances: &[CCCSVar<C>],
proof: ProofVar<C>,
@@ -244,24 +243,24 @@ where
U_i.v.clone(),
]
.concat();
transcript.absorb_vec(&v)?;
transcript.absorb(&v)?;
}
for u_i in new_instances {
let v = [u_i.C.to_constraint_field()?, u_i.x.clone()].concat();
transcript.absorb_vec(&v)?;
transcript.absorb(&v)?;
}
// get the challenges
let gamma_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"gamma");
let gamma_scalar: FpVar<CF1<C>> =
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 beta_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"beta");
let beta_scalar: FpVar<CF1<C>> =
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 vp_aux_info_raw = VPAuxInfo::<C::ScalarField> {
@@ -283,10 +282,10 @@ where
}
// 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,
&vp_aux_info,
&mut transcript,
transcript,
enabled.clone(),
)?;
@@ -312,7 +311,7 @@ where
// get the folding challenge
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)?;
transcript.absorb(rho_scalar)?;
transcript.absorb(&rho_scalar)?;
let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(N_BITS_RO)?;
let rho = Boolean::le_bits_to_fp_var(&rho_bits)?;
@@ -560,10 +559,10 @@ where
let w_i = W_i.clone();
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
let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
&mut transcript_p,
&ccs,
&[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 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
let i_usize = self.i_usize.unwrap_or(0);
@@ -689,14 +685,14 @@ where
// P.1. Compute u_i.x
// u_i.x[0] = H(i, z_0, z_i, U_i)
let (u_i_x, _) = U_i.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(),
i.clone(),
z_0.clone(),
z_i.clone(),
)?;
// 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
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
// unconstrained witnesses `U_i1_C` respectively. Its correctness will be checked on the
// 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(
cs.clone(),
&self.ccs.clone(),
transcript,
&mut transcript,
&[U_i.clone()],
&[u_i.clone()],
nimfs_proof,
@@ -728,14 +723,14 @@ where
// P.4.a compute and check the first output of F'
let (u_i1_x, _) = U_i1.clone().hash(
&crh_params,
&sponge,
pp_hash.clone(),
i + FpVar::<CF1<C1>>::one(),
z_0.clone(),
z_i1.clone(),
)?;
let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash(
&crh_params,
&sponge,
pp_hash.clone(),
FpVar::<CF1<C1>>::one(),
z_0.clone(),
@@ -776,8 +771,7 @@ where
// compute cf_r = H(cf_u_i, cf_U_i, cf_cmT)
// cf_r_bits is denoted by rho* in the paper.
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(),
&self.poseidon_config,
&mut transcript,
pp_hash.clone(),
cf_U_i_vec,
cf_u_i.clone(),
@@ -802,10 +796,10 @@ where
// P.4.b compute and check the second output of F'
// Base case: u_{i+1}.x[1] == H(cf_U_{\bot})
// 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, _) =
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(), || {
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_ff::BigInteger;
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 std::time::Instant;
use super::*;
use crate::{
arith::{
ccs::{
tests::{get_test_ccs, get_test_z},
CCS,
},
ccs::tests::{get_test_ccs, get_test_z},
r1cs::extract_w_x,
},
commitment::{pedersen::Pedersen, CommitmentScheme},
folding::{
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,
transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar},
Transcript,
},
transcript::poseidon::poseidon_canonical_config,
utils::get_cm_coordinates,
};
@@ -996,12 +979,11 @@ mod tests {
// Prover's transcript
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
let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&lcccs_instances,
@@ -1012,11 +994,10 @@ mod tests {
.unwrap();
// 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
let folded_lcccs_v = NIMFS::<Projective, PoseidonTranscript<Projective>>::verify(
let folded_lcccs_v = NIMFS::<Projective, PoseidonSponge<Fr>>::verify(
&mut transcript_v,
&ccs,
&lcccs_instances,
@@ -1039,13 +1020,13 @@ mod tests {
.unwrap();
let proofVar =
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 (folded_lcccsVar, _) = NIMFSGadget::<Projective>::verify(
cs.clone(),
&ccs,
transcriptVar,
&mut transcriptVar,
&lcccs_instancesVar,
&cccs_instancesVar,
proofVar,
@@ -1061,6 +1042,7 @@ mod tests {
pub fn test_lcccs_hash() {
let mut rng = test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let ccs = get_test_ccs();
let z1 = get_test_z::<Fr>(3);
@@ -1077,12 +1059,11 @@ mod tests {
.unwrap();
let h = lcccs
.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 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 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();
@@ -1091,7 +1072,7 @@ mod tests {
let (hVar, _) = lcccsVar
.clone()
.hash(
&crh_params,
&spongeVar,
pp_hashVar,
iVar.clone(),
z_0Var.clone(),
@@ -1108,6 +1089,7 @@ mod tests {
pub fn test_augmented_f_circuit() {
let mut rng = test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let start = Instant::now();
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_U_i = cf_U_dummy.clone();
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;
@@ -1185,19 +1160,11 @@ mod tests {
W_i1 = Witness::<Fr>::dummy(&ccs);
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
// 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 =
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
@@ -1225,12 +1192,12 @@ mod tests {
cf_cmT: None,
};
} 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);
let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&[U_i.clone()],
@@ -1243,15 +1210,8 @@ mod tests {
// sanity check: check the folded instance relation
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();
// CycleFold part:
@@ -1282,7 +1242,7 @@ mod tests {
Pedersen<Projective>,
Pedersen<Projective2>,
>(
&poseidon_config,
&mut transcript_p,
cf_r1cs.clone(),
cf_pedersen_params.clone(),
pp_hash,
@@ -1295,7 +1255,7 @@ mod tests {
// hash the CycleFold folded instance, which is used as the 2nd public input in the
// 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 =
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[0], augmented_f_circuit.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]
assert_eq!(u_i.x[0], expected_u_i1_x);
assert_eq!(u_i.x[1], expected_cf_U_i1_x);

View File

@@ -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_ff::PrimeField;
use ark_poly::DenseMultilinearExtension;
@@ -12,7 +9,7 @@ use ark_std::Zero;
use super::Witness;
use crate::arith::ccs::CCS;
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::vec::mat_vec_mul;
use crate::Error;
@@ -118,6 +115,29 @@ impl<C: CurveGroup> LCCCS<C> {
}
}
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>
where
<C as Group>::ScalarField: Absorb,
@@ -126,32 +146,21 @@ where
/// [`LCCCS`].hash implements the committed instance hash compatible with the gadget
/// 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.
pub fn hash(
pub fn hash<T: Transcript<C::ScalarField>>(
&self,
poseidon_config: &PoseidonConfig<C::ScalarField>,
sponge: &T,
pp_hash: C::ScalarField,
i: C::ScalarField,
z_0: 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::One;
use ark_std::UniformRand;
use ark_std::Zero;
use std::sync::Arc;
use super::*;

View File

@@ -1,5 +1,8 @@
/// 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_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
@@ -18,6 +21,10 @@ use cccs::CCCS;
use lcccs::LCCCS;
use nimfs::NIMFS;
use crate::arith::{
ccs::CCS,
r1cs::{extract_w_x, R1CS},
};
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
@@ -31,13 +38,6 @@ use crate::frontend::FCircuit;
use crate::utils::{get_cm_coordinates, pp_hash};
use crate::Error;
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.
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -233,6 +233,8 @@ where
z_0: Vec<C1::ScalarField>,
) -> Result<Self, Error> {
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
// and R1CS respectively
@@ -258,13 +260,13 @@ where
cf_r1cs.dummy_instance();
u_dummy.x = vec![
U_dummy.hash(
&pp.poseidon_config,
&sponge,
pp_hash,
C1::ScalarField::zero(),
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
@@ -299,6 +301,9 @@ where
mut rng: impl RngCore,
external_inputs: Vec<C1::ScalarField>,
) -> 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>;
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);
let u_i1_x = U_i1.hash(
&self.poseidon_config,
&sponge,
self.pp_hash,
C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;
);
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// 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> {
_c2: PhantomData,
@@ -377,30 +380,31 @@ where
cf_cmT: None,
};
} 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);
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
#[cfg(test)]
U_i1.check_relation(&self.ccs, &W_i1)?;
let u_i1_x = U_i1.hash(
&self.poseidon_config,
&sponge,
self.pp_hash,
self.i + C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;
);
let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits))
.ok_or(Error::OutOfBounds)?;
@@ -425,7 +429,7 @@ where
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>(
&self.poseidon_config,
&mut transcript_p,
self.cf_r1cs.clone(),
self.cf_cs_params.clone(),
self.pp_hash,
@@ -435,7 +439,7 @@ where
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> {
_c2: PhantomData,
@@ -541,6 +545,8 @@ where
}
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) = incoming_instance;
@@ -553,12 +559,12 @@ where
// check that u_i's output points to the running instance
// 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] {
return Err(Error::IVCVerificationFail);
}
// 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] {
return Err(Error::IVCVerificationFail);
}

View File

@@ -13,7 +13,6 @@ use super::{
};
use crate::arith::ccs::CCS;
use crate::constants::N_BITS_RO;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::transcript::Transcript;
use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage};
use crate::utils::sum_check::{IOPSumCheck, SumCheck};
@@ -58,12 +57,12 @@ pub struct SigmasThetas<F: PrimeField>(pub Vec<Vec<F>>, pub Vec<Vec<F>>);
#[derive(Debug)]
/// Implements the Non-Interactive Multi Folding Scheme described in section 5 of
/// [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 _t: PhantomData<T>,
}
impl<C: CurveGroup, T: Transcript<C>> NIMFS<C, T>
impl<C: CurveGroup, T: Transcript<C::ScalarField>> NIMFS<C, T>
where
<C as Group>::ScalarField: Absorb,
C::BaseField: PrimeField,
@@ -178,7 +177,7 @@ where
/// contains the sumcheck proof and the helper sumcheck claim sigmas and thetas.
#[allow(clippy::type_complexity)]
pub fn prove(
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
ccs: &CCS<C::ScalarField>,
running_instances: &[LCCCS<C>],
new_instances: &[CCCS<C>],
@@ -186,24 +185,8 @@ where
w_cccs: &[Witness<C::ScalarField>],
) -> Result<(NIMFSProof<C>, LCCCS<C>, Witness<C::ScalarField>, Vec<bool>), Error> {
// 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() {
return Err(Error::Empty);
@@ -247,7 +230,7 @@ where
let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta)?;
// 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()))?;
// Step 2: dig into the sumcheck and extract r_x_prime
@@ -290,31 +273,15 @@ where
/// into a single LCCCS instance.
/// Returns the folded LCCCS instance.
pub fn verify(
transcript: &mut impl Transcript<C>,
transcript: &mut impl Transcript<C::ScalarField>,
ccs: &CCS<C::ScalarField>,
running_instances: &[LCCCS<C>],
new_instances: &[CCCS<C>],
proof: NIMFSProof<C>,
) -> Result<LCCCS<C>, Error> {
// 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() {
return Err(Error::Empty);
@@ -349,9 +316,13 @@ where
}
// 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
let r_x_prime = sumcheck_subclaim.point.clone();
@@ -413,7 +384,8 @@ pub mod tests {
Arith,
};
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::UniformRand;
@@ -450,7 +422,7 @@ pub mod tests {
let mut rng = test_rng();
let rho = Fr::rand(&mut rng);
let folded = NIMFS::<Projective, PoseidonTranscript<Projective>>::fold(
let folded = NIMFS::<Projective, PoseidonSponge<Fr>>::fold(
&[lcccs],
&[cccs],
&sigmas_thetas,
@@ -458,8 +430,7 @@ pub mod tests {
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
folded.check_relation(&ccs, &w_folded).unwrap();
@@ -491,13 +462,12 @@ pub mod tests {
// Prover's transcript
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"));
// Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&[running_instance.clone()],
@@ -508,12 +478,11 @@ pub mod tests {
.unwrap();
// 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"));
// 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,
&ccs,
&[running_instance.clone()],
@@ -545,12 +514,10 @@ pub mod tests {
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"));
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"));
let n: usize = 10;
@@ -564,7 +531,7 @@ pub mod tests {
// run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&[running_instance.clone()],
@@ -575,7 +542,7 @@ pub mod tests {
.unwrap();
// 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,
&ccs,
&[running_instance.clone()],
@@ -641,13 +608,12 @@ pub mod tests {
// Prover's transcript
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"));
// Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&lcccs_instances,
@@ -658,12 +624,11 @@ pub mod tests {
.unwrap();
// 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"));
// 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,
&ccs,
&lcccs_instances,
@@ -690,13 +655,11 @@ pub mod tests {
let poseidon_config = poseidon_canonical_config::<Fr>();
// 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"));
// 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"));
let n_steps = 3;
@@ -741,7 +704,7 @@ pub mod tests {
// Run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
&mut transcript_p,
&ccs,
&lcccs_instances,
@@ -752,7 +715,7 @@ pub mod tests {
.unwrap();
// 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,
&ccs,
&lcccs_instances,

View File

@@ -176,7 +176,6 @@ pub mod tests {
use crate::utils::hypercube::BooleanHypercube;
use crate::utils::mle::matrix_to_dense_mle;
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
/// compresses every column j of the M(x,y) matrix by performing a random linear combination between the elements