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,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);
}