arnaucube
arnaucube/sonobePublic Self-hosted repository
Implement HyperNova's IVC into the FoldingScheme trait (#116)

- implement the IVC `FoldingScheme` trait for HyperNova
- refactor Nova's preprocess logic to make it simplier to use
- add to Decider trait (& Nova's DeciderEth) a preprocess method
- get rid of the `init_nova_ivc_params` and `init_ivc_and_decider_params` methods in `examples` since this is achieved with the `FS::preprocess` & `Decider::preprocess` methods
  - (update the examples code to the simplified interface using
    FS::preprocess & Decider::preprocess)
arnaucube root@arnaucube.com
Thu, 04 Jul 2024 11:14:31 +0200
commit

b5667968f48feacbccb1c3f5407f00f7e4892a4a

parent

456dc9f7a1b1ea420cb2867b26650fc968a42faa

M examples/circom_full_flow.rs → examples/circom_full_flow.rs

@@ -21,9 +21,10 @@ use folding_schemes::{

commitment::{kzg::KZG, pedersen::Pedersen}, folding::nova::{ decider_eth::{prepare_calldata, Decider as DeciderEth}, - Nova, + Nova, PreprocessorParam, }, frontend::{circom::CircomFCircuit, FCircuit}, + transcript::poseidon::poseidon_canonical_config, Decider, FoldingScheme, }; use solidity_verifiers::{

@@ -32,9 +33,6 @@ utils::get_function_selector_for_nova_cyclefold_verifier,

verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider, NovaCycleFoldVerifierKey, }; - -mod utils; -use utils::init_ivc_and_decider_params; fn main() { // set the initial state

@@ -66,12 +64,8 @@

let f_circuit_params = (r1cs_path, wasm_path, 1, 2); let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap(); - let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = - init_ivc_and_decider_params::<CircomFCircuit<Fr>>(f_circuit.clone()); - - pub type NOVA = - Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; - pub type DECIDERETH_FCircuit = DeciderEth< + pub type N = Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; + pub type D = DeciderEth< G1, GVar, G2,

@@ -80,30 +74,36 @@ CircomFCircuit<Fr>,

KZG<'static, Bn254>, Pedersen<G2>, Groth16<Bn254>, - NOVA, + N, >; + + let poseidon_config = poseidon_canonical_config::<Fr>(); + let mut rng = rand::rngs::OsRng; + + // prepare the Nova prover & verifier params + let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone()); + let (fs_pp, fs_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap(); // initialize the folding scheme engine, in our case we use Nova - let mut nova = NOVA::init(&fs_prover_params, f_circuit.clone(), z_0).unwrap(); + let mut nova = N::init(&fs_pp, f_circuit.clone(), z_0).unwrap(); + + // prepare the Decider prover & verifier params + let (decider_pp, decider_vp) = D::preprocess(&mut rng, &(fs_pp, fs_vp), nova.clone()).unwrap(); + // run n steps of the folding iteration for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { let start = Instant::now(); - nova.prove_step(external_inputs_at_step.clone()).unwrap(); + nova.prove_step(rng, external_inputs_at_step.clone()) + .unwrap(); println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } - let rng = rand::rngs::OsRng; let start = Instant::now(); - let proof = DECIDERETH_FCircuit::prove( - (g16_pk, fs_prover_params.cs_params.clone()), - rng, - nova.clone(), - ) - .unwrap(); + let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); println!("generated Decider proof: {:?}", start.elapsed()); - let verified = DECIDERETH_FCircuit::verify( - (g16_vk.clone(), kzg_vk.clone()), + let verified = D::verify( + decider_vp.clone(), nova.i, nova.z_0.clone(), nova.z_i.clone(),

@@ -131,7 +131,7 @@ )

.unwrap(); // prepare the setup params for the solidity verifier - let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len())); + let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((decider_vp, f_circuit.state_len())); // generate the solidity code let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
M examples/external_inputs.rs → examples/external_inputs.rs

@@ -21,12 +21,10 @@ use core::marker::PhantomData;

use std::time::Instant; use folding_schemes::commitment::{kzg::KZG, pedersen::Pedersen}; -use folding_schemes::folding::nova::Nova; +use folding_schemes::folding::nova::{Nova, PreprocessorParam}; use folding_schemes::frontend::FCircuit; -use folding_schemes::{Error, FoldingScheme}; -mod utils; use folding_schemes::transcript::poseidon::poseidon_canonical_config; -use utils::init_nova_ivc_params; +use folding_schemes::{Error, FoldingScheme}; /// This is the circuit that we want to fold, it implements the FCircuit trait. The parameter z_i /// denotes the current state which contains 1 element, and z_{i+1} denotes the next state which we

@@ -65,14 +63,14 @@ ///

/// The last state z_i is used together with the external input w_i as inputs to compute the new /// state z_{i+1}. #[derive(Clone, Debug)] -pub struct ExternalInputsCircuits<F: PrimeField> +pub struct ExternalInputsCircuit<F: PrimeField> where F: Absorb, { _f: PhantomData<F>, poseidon_config: PoseidonConfig<F>, } -impl<F: PrimeField> FCircuit<F> for ExternalInputsCircuits<F> +impl<F: PrimeField> FCircuit<F> for ExternalInputsCircuit<F> where F: Absorb, {

@@ -128,14 +126,14 @@ use super::*;

use ark_r1cs_std::R1CSVar; use ark_relations::r1cs::ConstraintSystem; - // test to check that the ExternalInputsCircuits computes the same values inside and outside the circuit + // test to check that the ExternalInputsCircuit computes the same values inside and outside the circuit #[test] fn test_f_circuit() { let poseidon_config = poseidon_canonical_config::<Fr>(); let cs = ConstraintSystem::<Fr>::new_ref(); - let circuit = ExternalInputsCircuits::<Fr>::new(poseidon_config).unwrap(); + let circuit = ExternalInputsCircuit::<Fr>::new(poseidon_config).unwrap(); let z_i = vec![Fr::from(1_u32)]; let external_inputs = vec![Fr::from(3_u32)];

@@ -170,33 +168,35 @@ ];

assert_eq!(external_inputs.len(), num_steps); let poseidon_config = poseidon_canonical_config::<Fr>(); - let F_circuit = ExternalInputsCircuits::<Fr>::new(poseidon_config).unwrap(); - - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = - init_nova_ivc_params::<ExternalInputsCircuits<Fr>>(F_circuit.clone()); + let F_circuit = ExternalInputsCircuit::<Fr>::new(poseidon_config.clone()).unwrap(); /// The idea here is that eventually we could replace the next line chunk that defines the - /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` + /// `type N = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` /// trait, and the rest of our code would be working without needing to be updated. - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2, GVar2, - ExternalInputsCircuits<Fr>, + ExternalInputsCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, >; + let mut rng = rand::rngs::OsRng; + + println!("Prepare Nova's ProverParams & VerifierParams"); + let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit.clone()); + let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap(); + println!("Initialize FoldingScheme"); - let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap(); + let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap(); // compute a step of the IVC for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { let start = Instant::now(); folding_scheme - .prove_step(external_inputs_at_step.clone()) + .prove_step(rng, external_inputs_at_step.clone()) .unwrap(); println!("Nova::prove_step {}: {:?}", i, start.elapsed()); }

@@ -209,8 +209,8 @@

let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances(); println!("Run the Nova's IVC verifier"); - NOVA::verify( - verifier_params, + N::verify( + nova_vp, initial_state.clone(), folding_scheme.state(), // latest state Fr::from(num_steps as u32),
M examples/full_flow.rs → examples/full_flow.rs

@@ -19,16 +19,14 @@ use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};

use std::marker::PhantomData; use std::time::Instant; -mod utils; -use utils::init_ivc_and_decider_params; - use folding_schemes::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::nova::{ decider_eth::{prepare_calldata, Decider as DeciderEth}, - Nova, + Nova, PreprocessorParam, }, frontend::FCircuit, + transcript::poseidon::poseidon_canonical_config, Decider, Error, FoldingScheme, }; use solidity_verifiers::{

@@ -82,11 +80,9 @@ // set the initial state

let z_0 = vec![Fr::from(3_u32)]; let f_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); - let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = - init_ivc_and_decider_params::<CubicFCircuit<Fr>>(f_circuit); - pub type NOVA = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; - pub type DECIDERETH_FCircuit = DeciderEth< + pub type N = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; + pub type D = DeciderEth< G1, GVar, G2,

@@ -95,30 +91,35 @@ CubicFCircuit<Fr>,

KZG<'static, Bn254>, Pedersen<G2>, Groth16<Bn254>, - NOVA, + N, >; + let poseidon_config = poseidon_canonical_config::<Fr>(); + let mut rng = rand::rngs::OsRng; + + // prepare the Nova prover & verifier params + let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit); + let (fs_pp, fs_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap(); + // initialize the folding scheme engine, in our case we use Nova - let mut nova = NOVA::init(&fs_prover_params, f_circuit, z_0).unwrap(); + let mut nova = N::init(&fs_pp, f_circuit, z_0).unwrap(); + + // prepare the Decider prover & verifier params + let (decider_pp, decider_vp) = D::preprocess(&mut rng, &(fs_pp, fs_vp), nova.clone()).unwrap(); + // run n steps of the folding iteration for i in 0..n_steps { let start = Instant::now(); - nova.prove_step(vec![]).unwrap(); + nova.prove_step(rng, vec![]).unwrap(); println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } - let rng = rand::rngs::OsRng; let start = Instant::now(); - let proof = DECIDERETH_FCircuit::prove( - (g16_pk, fs_prover_params.cs_params.clone()), - rng, - nova.clone(), - ) - .unwrap(); + let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); println!("generated Decider proof: {:?}", start.elapsed()); - let verified = DECIDERETH_FCircuit::verify( - (g16_vk.clone(), kzg_vk.clone()), + let verified = D::verify( + decider_vp.clone(), nova.i, nova.z_0.clone(), nova.z_i.clone(),

@@ -146,7 +147,7 @@ )

.unwrap(); // prepare the setup params for the solidity verifier - let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len())); + let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((decider_vp, f_circuit.state_len())); // generate the solidity code let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
M examples/multi_inputs.rs → examples/multi_inputs.rs

@@ -14,11 +14,10 @@ use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};

use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use folding_schemes::commitment::{kzg::KZG, pedersen::Pedersen}; -use folding_schemes::folding::nova::Nova; +use folding_schemes::folding::nova::{Nova, PreprocessorParam}; use folding_schemes::frontend::FCircuit; +use folding_schemes::transcript::poseidon::poseidon_canonical_config; use folding_schemes::{Error, FoldingScheme}; -mod utils; -use utils::init_nova_ivc_params; /// This is the circuit that we want to fold, it implements the FCircuit trait. The parameter z_i /// denotes the current state which contains 5 elements, and z_{i+1} denotes the next state which

@@ -124,14 +123,13 @@ ];

let F_circuit = MultiInputsFCircuit::<Fr>::new(()).unwrap(); - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = - init_nova_ivc_params::<MultiInputsFCircuit<Fr>>(F_circuit); + let poseidon_config = poseidon_canonical_config::<Fr>(); + let mut rng = rand::rngs::OsRng; /// The idea here is that eventually we could replace the next line chunk that defines the - /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` + /// `type N = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` /// trait, and the rest of our code would be working without needing to be updated. - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2,

@@ -141,21 +139,25 @@ KZG<'static, Bn254>,

Pedersen<Projective2>, >; + println!("Prepare Nova ProverParams & VerifierParams"); + let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit); + let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap(); + println!("Initialize FoldingScheme"); - let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap(); + let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap(); // compute a step of the IVC for i in 0..num_steps { let start = Instant::now(); - folding_scheme.prove_step(vec![]).unwrap(); + folding_scheme.prove_step(rng, vec![]).unwrap(); println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances(); println!("Run the Nova's IVC verifier"); - NOVA::verify( - verifier_params, + N::verify( + nova_vp, initial_state.clone(), folding_scheme.state(), // latest state Fr::from(num_steps as u32),
M examples/sha256.rs → examples/sha256.rs

@@ -20,11 +20,10 @@ use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};

use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use folding_schemes::commitment::{kzg::KZG, pedersen::Pedersen}; -use folding_schemes::folding::nova::Nova; +use folding_schemes::folding::nova::{Nova, PreprocessorParam}; use folding_schemes::frontend::FCircuit; +use folding_schemes::transcript::poseidon::poseidon_canonical_config; use folding_schemes::{Error, FoldingScheme}; -mod utils; -use utils::init_nova_ivc_params; /// This is the circuit that we want to fold, it implements the FCircuit trait. /// The parameter z_i denotes the current state, and z_{i+1} denotes the next state which we get by

@@ -109,13 +108,10 @@ let initial_state = vec![Fr::from(1_u32)];

let F_circuit = Sha256FCircuit::<Fr>::new(()).unwrap(); - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = init_nova_ivc_params::<Sha256FCircuit<Fr>>(F_circuit); - /// The idea here is that eventually we could replace the next line chunk that defines the - /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` + /// `type N = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` /// trait, and the rest of our code would be working without needing to be updated. - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2,

@@ -125,21 +121,27 @@ KZG<'static, Bn254>,

Pedersen<Projective2>, >; - println!("Initialize FoldingScheme"); - let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap(); + let poseidon_config = poseidon_canonical_config::<Fr>(); + let mut rng = rand::rngs::OsRng; + + println!("Prepare Nova ProverParams & VerifierParams"); + let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit); + let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap(); + println!("Initialize FoldingScheme"); + let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap(); // compute a step of the IVC for i in 0..num_steps { let start = Instant::now(); - folding_scheme.prove_step(vec![]).unwrap(); + folding_scheme.prove_step(rng, vec![]).unwrap(); println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances(); println!("Run the Nova's IVC verifier"); - NOVA::verify( - verifier_params, + N::verify( + nova_vp, initial_state, folding_scheme.state(), // latest state Fr::from(num_steps as u32),
D examples/utils.rs

@@ -1,99 +0,0 @@

-#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(clippy::upper_case_acronyms)] -#![allow(dead_code)] -use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1}; -use ark_crypto_primitives::snark::SNARK; -use ark_groth16::{Groth16, ProvingKey, VerifyingKey as G16VerifierKey}; -use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2}; -use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; -use ark_std::Zero; -use std::time::Instant; - -use folding_schemes::{ - commitment::{ - kzg::{ProverKey as KZGProverKey, KZG}, - pedersen::Pedersen, - CommitmentScheme, - }, - folding::nova::{ - decider_eth_circuit::DeciderEthCircuit, get_r1cs, Nova, ProverParams, VerifierParams, - }, - frontend::FCircuit, - transcript::poseidon::poseidon_canonical_config, - FoldingScheme, -}; - -// This method computes the Nova's Prover & Verifier parameters for the example. -// Warning: this method is only for testing purposes. For a real world use case those parameters -// should be generated carefully (both the PoseidonConfig and the PedersenParams). -#[allow(clippy::type_complexity)] -pub(crate) fn init_nova_ivc_params<FC: FCircuit<Fr>>( - F_circuit: FC, -) -> ( - ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>, - VerifierParams<G1, G2>, - KZGVerifierKey<Bn254>, -) { - let mut rng = ark_std::test_rng(); - let poseidon_config = poseidon_canonical_config::<Fr>(); - - // get the CM & CF_CM len - let (r1cs, cf_r1cs) = get_r1cs::<G1, GVar, G2, GVar2, FC>(&poseidon_config, F_circuit).unwrap(); - let cs_len = r1cs.A.n_rows; - let cf_cs_len = cf_r1cs.A.n_rows; - - // let (pedersen_params, _) = Pedersen::<G1>::setup(&mut rng, cf_len).unwrap(); - let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) = - KZG::<Bn254>::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap(); - - let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> { - poseidon_config: poseidon_config.clone(), - cs_params: kzg_pk.clone(), - cf_cs_params: cf_pedersen_params, - }; - let fs_verifier_params = VerifierParams::<G1, G2> { - poseidon_config: poseidon_config.clone(), - r1cs, - cf_r1cs, - }; - (fs_prover_params, fs_verifier_params, kzg_vk) -} - -/// Initializes Nova parameters and DeciderEth parameters. Only for test purposes. -#[allow(clippy::type_complexity)] -pub(crate) fn init_ivc_and_decider_params<FC: FCircuit<Fr>>( - f_circuit: FC, -) -> ( - ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>, - KZGVerifierKey<Bn254>, - ProvingKey<Bn254>, - G16VerifierKey<Bn254>, -) { - let mut rng = rand::rngs::OsRng; - let start = Instant::now(); - let (fs_prover_params, _, kzg_vk) = init_nova_ivc_params::<FC>(f_circuit.clone()); - println!("generated Nova folding params: {:?}", start.elapsed()); - - pub type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>; - let z_0 = vec![Fr::zero(); f_circuit.state_len()]; - let nova = NOVA::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap(); - - let decider_circuit = - DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>( - nova.clone(), - ) - .unwrap(); - let start = Instant::now(); - let (g16_pk, g16_vk) = - Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap(); - println!( - "generated G16 (Decider circuit) params: {:?}", - start.elapsed() - ); - (fs_prover_params, kzg_vk, g16_pk, g16_vk) -} - -fn main() {}
M folding-schemes/src/commitment/ipa.rs → folding-schemes/src/commitment/ipa.rs

@@ -54,6 +54,13 @@ type Proof = (Proof<C>, C::ScalarField, C::ScalarField); // (proof, v=p(x), r=blinding factor)

type ProverChallenge = (C::ScalarField, C, Vec<C::ScalarField>); type Challenge = (C::ScalarField, C, Vec<C::ScalarField>); + fn is_hiding() -> bool { + if H { + return true; + } + false + } + fn setup( mut rng: impl RngCore, len: usize,
M folding-schemes/src/commitment/kzg.rs → folding-schemes/src/commitment/kzg.rs

@@ -93,6 +93,13 @@ type Proof = Proof<E::G1>;

type ProverChallenge = E::ScalarField; type Challenge = E::ScalarField; + fn is_hiding() -> bool { + if H { + return true; + } + false + } + /// setup returns the tuple (ProverKey, VerifierKey). For real world deployments the setup must /// be computed in the most trustless way possible, usually through a MPC ceremony. fn setup(
M folding-schemes/src/commitment/mod.rs → folding-schemes/src/commitment/mod.rs

@@ -18,6 +18,8 @@ type Proof: Clone + Debug;

type ProverChallenge: Clone + Debug; type Challenge: Clone + Debug; + fn is_hiding() -> bool; + fn setup( rng: impl RngCore, len: usize,
M folding-schemes/src/commitment/pedersen.rs → folding-schemes/src/commitment/pedersen.rs

@@ -38,6 +38,13 @@ type Proof = Proof<C>;

type ProverChallenge = (C::ScalarField, Vec<C::ScalarField>, C, C::ScalarField); type Challenge = C::ScalarField; + fn is_hiding() -> bool { + if H { + return true; + } + false + } + fn setup( mut rng: impl RngCore, len: usize,
M folding-schemes/src/folding/circuits/cyclefold.rs → folding-schemes/src/folding/circuits/cyclefold.rs

@@ -460,9 +460,9 @@ use ark_relations::r1cs::ConstraintSystem;

use ark_std::UniformRand; use super::*; - use crate::folding::nova::get_cm_coordinates; use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs; use crate::transcript::poseidon::poseidon_canonical_config; + use crate::utils::get_cm_coordinates; #[test] fn test_committed_instance_cyclefold_var() {
M folding-schemes/src/folding/hypernova/cccs.rs → folding-schemes/src/folding/hypernova/cccs.rs

@@ -8,10 +8,7 @@ use ark_std::rand::Rng;

use super::Witness; use crate::ccs::CCS; -use crate::commitment::{ - pedersen::{Params as PedersenParams, Pedersen}, - CommitmentScheme, -}; +use crate::commitment::CommitmentScheme; 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};

@@ -27,10 +24,10 @@ pub x: Vec<C::ScalarField>,

} impl<F: PrimeField> CCS<F> { - pub fn to_cccs<R: Rng, C: CurveGroup>( + pub fn to_cccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>( &self, rng: &mut R, - pedersen_params: &PedersenParams<C>, + cs_params: &CS::ProverParams, z: &[C::ScalarField], ) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error> where

@@ -38,8 +35,14 @@ // enforce that CCS's F is the C::ScalarField

C: CurveGroup<ScalarField = F>, { let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec(); - let r_w = C::ScalarField::rand(rng); - let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?; + + // if the commitment scheme is set to be hiding, set the random blinding parameter + let r_w = if CS::is_hiding() { + C::ScalarField::rand(rng) + } else { + C::ScalarField::zero() + }; + let C = CS::commit(cs_params, &w, &r_w)?; Ok(( CCCS::<C> {

@@ -95,19 +98,13 @@ x: vec![C::ScalarField::zero(); l],

} } - /// Perform the check of the CCCS instance described at section 4.1 + /// Perform the check of the CCCS instance described at section 4.1, + /// notice that this method does not check the commitment correctness pub fn check_relation( &self, - pedersen_params: &PedersenParams<C>, ccs: &CCS<C::ScalarField>, w: &Witness<C::ScalarField>, ) -> Result<(), Error> { - // check that C is the commitment of w. Notice that this is not verifying a Pedersen - // opening, but checking that the commitment comes from committing to the witness. - if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? { - return Err(Error::NotSatisfied); - } - // check CCCS relation let z: Vec<C::ScalarField> = [vec![C::ScalarField::one()], self.x.clone(), w.w.to_vec()].concat();
M folding-schemes/src/folding/hypernova/circuits.rs → folding-schemes/src/folding/hypernova/circuits.rs

@@ -596,7 +596,7 @@ }

/// Returns the cs (ConstraintSystem) and the CCS out of the AugmentedFCircuit #[allow(clippy::type_complexity)] - fn compute_cs_ccs( + pub fn compute_cs_ccs( &self, ) -> Result<(ConstraintSystem<C1::ScalarField>, CCS<C1::ScalarField>), Error> { let cs = ConstraintSystem::<C1::ScalarField>::new_ref();

@@ -720,8 +720,14 @@ i + FpVar::<CF1<C1>>::one(),

z_0.clone(), z_i1.clone(), )?; - let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(C1::ScalarField::zero())))?; - x.enforce_equal(&u_i1_x)?; + let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( + &crh_params, + FpVar::<CF1<C1>>::one(), + z_0.clone(), + z_i1.clone(), + )?; + let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(u_i1_x_base.value()?)))?; + x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?; // convert rho_bits to a `NonNativeFieldVar` let rho_nonnat = {

@@ -817,15 +823,14 @@ nimfs::NIMFS,

utils::{compute_c, compute_sigmas_thetas}, Witness, }, - nova::{ - get_cm_coordinates, traits::NovaR1CS, CommittedInstance, Witness as NovaWitness, - }, + nova::{traits::NovaR1CS, CommittedInstance, Witness as NovaWitness}, }, frontend::tests::CubicFCircuit, transcript::{ poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar}, Transcript, }, + utils::get_cm_coordinates, }; #[test]

@@ -858,13 +863,17 @@

// Create the LCCCS instances out of z_lcccs let mut lcccs_instances = Vec::new(); for z_i in z_lcccs.iter() { - let (inst, _) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (inst, _) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(inst); } // Create the CCCS instance out of z_cccs let mut cccs_instances = Vec::new(); for z_i in z_cccs.iter() { - let (inst, _) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (inst, _) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(inst); }

@@ -950,7 +959,9 @@ // Create the LCCCS instances out of z_lcccs

let mut lcccs_instances = Vec::new(); let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { - let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (running_instance, w) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); }

@@ -958,7 +969,9 @@ // Create the CCCS instance out of z_cccs

let mut cccs_instances = Vec::new(); let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { - let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (new_instance, w) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); }

@@ -996,9 +1009,7 @@ .unwrap();

assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs - .check_relation(&pedersen_params, &ccs, &folded_witness) - .unwrap(); + folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); // allocate circuit inputs let cs = ConstraintSystem::<Fr>::new_ref();

@@ -1042,7 +1053,9 @@

let i = Fr::from(3_u32); let z_0 = vec![Fr::from(3_u32)]; let z_i = vec![Fr::from(3_u32)]; - let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); + let (lcccs, _) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .unwrap(); let h = lcccs .clone() .hash(&poseidon_config, i, z_0.clone(), z_i.clone())

@@ -1104,11 +1117,11 @@ let z_0 = vec![Fr::from(3_u32)];

let mut z_i = z_0.clone(); // prepare the dummy instances - let W_dummy = Witness::<Fr>::new(vec![Fr::zero(); ccs.n - ccs.l - 1]); + let W_dummy = Witness::<Fr>::dummy(&ccs); let U_dummy = LCCCS::<Projective>::dummy(ccs.l, ccs.t, ccs.s); let w_dummy = W_dummy.clone(); let u_dummy = CCCS::<Projective>::dummy(ccs.l); - let (cf_w_dummy, cf_u_dummy): (NovaWitness<Projective2>, CommittedInstance<Projective2>) = + let (cf_W_dummy, cf_U_dummy): (NovaWitness<Projective2>, CommittedInstance<Projective2>) = cf_r1cs.dummy_instance(); // set the initial dummy instances

@@ -1116,8 +1129,8 @@ let mut W_i = W_dummy.clone();

let mut U_i = U_dummy.clone(); let mut w_i = w_dummy.clone(); let mut u_i = u_dummy.clone(); - let mut cf_W_i = cf_w_dummy.clone(); - let mut cf_U_i = cf_u_dummy.clone(); + 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, Fr::zero(), z_0.clone(), z_i.clone()) .unwrap(),

@@ -1128,28 +1141,19 @@ let n_steps: usize = 4;

let mut iFr = Fr::zero(); for i in 0..n_steps { let start = Instant::now(); - let mut transcript_p: PoseidonTranscript<Projective> = - PoseidonTranscript::<Projective>::new(&poseidon_config.clone()); - let (nimfs_proof, U_i1, W_i1, rho_bits) = - NIMFS::<Projective, PoseidonTranscript<Projective>>::prove( - &mut transcript_p, - &ccs, - &[U_i.clone()], - &[u_i.clone()], - &[W_i.clone()], - &[w_i.clone()], - ) - .unwrap(); - - // sanity check: check the folded instance relation - U_i1.check_relation(&pedersen_params, &ccs, &W_i1).unwrap(); let z_i1 = F_circuit.step_native(i, z_i.clone(), vec![]).unwrap(); - let u_i1_x = U_i1 - .hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone()) - .unwrap(); + + let (U_i1, W_i1); if i == 0 { + 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, Fr::one(), z_0.clone(), z_i1.clone()) + .unwrap(); + // 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).unwrap();

@@ -1160,8 +1164,8 @@ _c2: PhantomData,

_gc2: PhantomData, poseidon_config: poseidon_config.clone(), ccs: ccs.clone(), - i: Some(iFr), - i_usize: Some(i), + i: Some(Fr::zero()), + i_usize: Some(0), z_0: Some(z_0.clone()), z_i: Some(z_i.clone()), external_inputs: Some(vec![]),

@@ -1170,7 +1174,7 @@ U_i: Some(U_i.clone()),

U_i1_C: Some(U_i1.C), F: F_circuit, x: Some(u_i1_x), - nimfs_proof: Some(nimfs_proof), + nimfs_proof: None, // cyclefold values cf_u_i_cmW: None,

@@ -1179,6 +1183,27 @@ cf_x: Some(cf_u_i1_x),

cf_cmT: None, }; } else { + let mut transcript_p: PoseidonTranscript<Projective> = + PoseidonTranscript::<Projective>::new(&poseidon_config.clone()); + let (rho_bits, nimfs_proof); + (nimfs_proof, U_i1, W_i1, rho_bits) = + NIMFS::<Projective, PoseidonTranscript<Projective>>::prove( + &mut transcript_p, + &ccs, + &[U_i.clone()], + &[u_i.clone()], + &[W_i.clone()], + &[w_i.clone()], + ) + .unwrap(); + + // sanity check: check the folded instance relation + U_i1.check_relation(&ccs, &W_i1).unwrap(); + + let u_i1_x = U_i1 + .hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone()) + .unwrap(); + let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); // CycleFold part: // get the vector used as public inputs 'x' in the CycleFold circuit

@@ -1260,8 +1285,10 @@ assert_eq!(r1cs_x_i1[0], augmented_f_circuit.x.unwrap());

let r1cs_z = [vec![Fr::one()], r1cs_x_i1.clone(), r1cs_w_i1.clone()].concat(); // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we // assign them directly to w_i, u_i. - (u_i, w_i) = ccs.to_cccs(&mut rng, &pedersen_params, &r1cs_z).unwrap(); - u_i.check_relation(&pedersen_params, &ccs, &w_i).unwrap(); + (u_i, w_i) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &r1cs_z) + .unwrap(); + u_i.check_relation(&ccs, &w_i).unwrap(); // sanity checks assert_eq!(w_i.w, r1cs_w_i1);

@@ -1284,9 +1311,9 @@ U_i = U_i1.clone();

W_i = W_i1.clone(); // check the new LCCCS instance relation - U_i.check_relation(&pedersen_params, &ccs, &W_i).unwrap(); + U_i.check_relation(&ccs, &W_i).unwrap(); // check the new CCCS instance relation - u_i.check_relation(&pedersen_params, &ccs, &w_i).unwrap(); + u_i.check_relation(&ccs, &w_i).unwrap(); // check the CycleFold instance relation cf_r1cs
M folding-schemes/src/folding/hypernova/lcccs.rs → folding-schemes/src/folding/hypernova/lcccs.rs

@@ -11,10 +11,7 @@ use ark_std::Zero;

use super::Witness; use crate::ccs::CCS; -use crate::commitment::{ - pedersen::{Params as PedersenParams, Pedersen}, - CommitmentScheme, -}; +use crate::commitment::CommitmentScheme; use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul;

@@ -36,10 +33,10 @@ pub v: Vec<C::ScalarField>,

} impl<F: PrimeField> CCS<F> { - pub fn to_lcccs<R: Rng, C: CurveGroup>( + pub fn to_lcccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>( &self, rng: &mut R, - pedersen_params: &PedersenParams<C>, + cs_params: &CS::ProverParams, z: &[C::ScalarField], ) -> Result<(LCCCS<C>, Witness<C::ScalarField>), Error> where

@@ -47,8 +44,13 @@ // enforce that CCS's F is the C::ScalarField

C: CurveGroup<ScalarField = F>, { let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec(); - let r_w = C::ScalarField::rand(rng); - let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?; + // if the commitment scheme is set to be hiding, set the random blinding parameter + let r_w = if CS::is_hiding() { + C::ScalarField::rand(rng) + } else { + C::ScalarField::zero() + }; + let C = CS::commit(cs_params, &w, &r_w)?; let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect();

@@ -91,19 +93,13 @@ v: vec![C::ScalarField::zero(); t],

} } - /// Perform the check of the LCCCS instance described at section 4.2 + /// Perform the check of the LCCCS instance described at section 4.2, + /// notice that this method does not check the commitment correctness pub fn check_relation( &self, - pedersen_params: &PedersenParams<C>, ccs: &CCS<C::ScalarField>, w: &Witness<C::ScalarField>, ) -> Result<(), Error> { - // check that C is the commitment of w. Notice that this is not verifying a Pedersen - // opening, but checking that the Commitment comes from committing to the witness. - if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? { - return Err(Error::NotSatisfied); - } - // check CCS relation let z: Vec<C::ScalarField> = [vec![self.u], self.x.clone(), w.w.to_vec()].concat();

@@ -172,6 +168,7 @@ use crate::ccs::{

r1cs::R1CS, tests::{get_test_ccs, get_test_z}, }; + use crate::commitment::pedersen::Pedersen; use crate::utils::hypercube::BooleanHypercube; use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};

@@ -206,14 +203,16 @@ let mut rng = test_rng();

let n_rows = 2_u32.pow(5) as usize; let n_cols = 2_u32.pow(5) as usize; - let r1cs = R1CS::rand(&mut rng, n_rows, n_cols); + let r1cs = R1CS::<Fr>::rand(&mut rng, n_rows, n_cols); let ccs = CCS::from_r1cs(r1cs); let z: Vec<Fr> = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect(); let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); - let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap(); + let (lcccs, _) = ccs + .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z) + .unwrap(); // with our test vector coming from R1CS, v should have length 3 assert_eq!(lcccs.v.len(), 3);

@@ -245,7 +244,9 @@

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); // Compute v_j with the right z - let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap(); + let (lcccs, _) = ccs + .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z) + .unwrap(); // with our test vector coming from R1CS, v should have length 3 assert_eq!(lcccs.v.len(), 3);
M folding-schemes/src/folding/hypernova/mod.rs → folding-schemes/src/folding/hypernova/mod.rs

@@ -1,12 +1,42 @@

/// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf) -use crate::ccs::CCS; -use ark_ff::PrimeField; +use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; +use ark_ec::{CurveGroup, Group}; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; +use ark_std::rand::RngCore; +use ark_std::{One, Zero}; +use core::marker::PhantomData; +use std::fmt::Debug; pub mod cccs; pub mod circuits; +use circuits::AugmentedFCircuit; pub mod lcccs; pub mod nimfs; pub mod utils; +use cccs::CCCS; +use lcccs::LCCCS; +use nimfs::NIMFS; + +use crate::commitment::CommitmentScheme; +use crate::folding::circuits::{ + cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, + CF2, +}; +use crate::folding::nova::{ + get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, Witness as NovaWitness, +}; +use crate::frontend::FCircuit; +use crate::utils::get_cm_coordinates; +use crate::Error; +use crate::FoldingScheme; +use crate::{ + ccs::{ + r1cs::{extract_w_x, R1CS}, + CCS, + }, + 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)]

@@ -25,3 +55,556 @@ pub fn dummy(ccs: &CCS<F>) -> Self {

Witness::<F>::new(vec![F::zero(); ccs.n - ccs.l - 1]) } } + +#[derive(Debug, Clone)] +pub struct PreprocessorParam<C1, C2, FC, CS1, CS2> +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ + pub poseidon_config: PoseidonConfig<C1::ScalarField>, + pub F: FC, + // cs_params & cf_cs_params: if not provided, will be generated at the preprocess method + pub cs_params: Option<CS1::ProverParams>, + pub cf_cs_params: Option<CS2::ProverParams>, +} + +#[derive(Debug, Clone)] +pub struct ProverParams<C1, C2, CS1, CS2> +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ + pub poseidon_config: PoseidonConfig<C1::ScalarField>, + pub cs_params: CS1::ProverParams, + pub cf_cs_params: CS2::ProverParams, + // if ccs is set, it will be used, if not, it will be computed at runtime + pub ccs: Option<CCS<C1::ScalarField>>, +} + +#[derive(Debug, Clone)] +pub struct VerifierParams< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +> { + pub poseidon_config: PoseidonConfig<C1::ScalarField>, + pub ccs: CCS<C1::ScalarField>, + pub cf_r1cs: R1CS<C2::ScalarField>, + pub cs_params: CS1::ProverParams, + pub cf_cs_params: CS2::ProverParams, +} + +/// Implements HyperNova+CycleFold's IVC, described in +/// [HyperNova](https://eprint.iacr.org/2023/573.pdf) and +/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait +#[derive(Clone, Debug)] +pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +where + C1: CurveGroup, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, + C2: CurveGroup, + GC2: CurveVar<C2, CF2<C2>>, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ + _gc1: PhantomData<GC1>, + _c2: PhantomData<C2>, + _gc2: PhantomData<GC2>, + + /// CCS of the Augmented Function circuit + pub ccs: CCS<C1::ScalarField>, + /// R1CS of the CycleFold circuit + pub cf_r1cs: R1CS<C2::ScalarField>, + pub poseidon_config: PoseidonConfig<C1::ScalarField>, + /// CommitmentScheme::ProverParams over C1 + pub cs_params: CS1::ProverParams, + /// CycleFold CommitmentScheme::ProverParams, over C2 + pub cf_cs_params: CS2::ProverParams, + /// F circuit, the circuit that is being folded + pub F: FC, + pub i: C1::ScalarField, + /// initial state + pub z_0: Vec<C1::ScalarField>, + /// current i-th state + pub z_i: Vec<C1::ScalarField>, + /// HyperNova instances + pub W_i: Witness<C1::ScalarField>, + pub U_i: LCCCS<C1>, + pub w_i: Witness<C1::ScalarField>, + pub u_i: CCCS<C1>, + + /// CycleFold running instance + pub cf_W_i: NovaWitness<C2>, + pub cf_U_i: CommittedInstance<C2>, +} + +impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC> + for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +where + C1: CurveGroup, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, + C2: CurveGroup, + GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, + <C1 as CurveGroup>::BaseField: PrimeField, + <C2 as CurveGroup>::BaseField: PrimeField, + <C1 as Group>::ScalarField: Absorb, + <C2 as Group>::ScalarField: Absorb, + C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>, + for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, + for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, +{ + type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2>; + type ProverParam = ProverParams<C1, C2, CS1, CS2>; + type VerifierParam = VerifierParams<C1, C2, CS1, CS2>; + type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>); + type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>); + type CFInstance = (CommittedInstance<C2>, NovaWitness<C2>); + + fn preprocess( + mut rng: impl RngCore, + prep_param: &Self::PreprocessorParam, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty( + &prep_param.poseidon_config, + prep_param.F.clone(), + None, + )?; + let ccs = augmented_f_circuit.ccs.clone(); + + let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(); + let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?; + + // if cs_params & cf_cs_params exist, use them, if not, generate new ones + let cs_params: CS1::ProverParams; + let cf_cs_params: CS2::ProverParams; + if prep_param.cs_params.is_some() && prep_param.cf_cs_params.is_some() { + cs_params = prep_param.clone().cs_params.unwrap(); + cf_cs_params = prep_param.clone().cf_cs_params.unwrap(); + } else { + (cs_params, _) = CS1::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); + (cf_cs_params, _) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1).unwrap(); + } + + let pp = ProverParams::<C1, C2, CS1, CS2> { + poseidon_config: prep_param.poseidon_config.clone(), + cs_params: cs_params.clone(), + cf_cs_params: cf_cs_params.clone(), + ccs: Some(ccs.clone()), + }; + let vp = VerifierParams::<C1, C2, CS1, CS2> { + poseidon_config: prep_param.poseidon_config.clone(), + ccs, + cf_r1cs, + cs_params: cs_params.clone(), + cf_cs_params: cf_cs_params.clone(), + }; + Ok((pp, vp)) + } + + /// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`. + fn init(pp: &Self::ProverParam, F: FC, z_0: Vec<C1::ScalarField>) -> Result<Self, Error> { + // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS + // and R1CS respectively + let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty( + &pp.poseidon_config, + F.clone(), + pp.ccs.clone(), + )?; + let ccs = augmented_f_circuit.ccs.clone(); + + let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(); + let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?; + + // setup the dummy instances + let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs); + let U_dummy = LCCCS::<C1>::dummy(ccs.l, ccs.t, ccs.s); + let w_dummy = W_dummy.clone(); + let mut u_dummy = CCCS::<C1>::dummy(ccs.l); + let (cf_W_dummy, cf_U_dummy): (NovaWitness<C2>, CommittedInstance<C2>) = + cf_r1cs.dummy_instance(); + u_dummy.x = vec![ + U_dummy.hash( + &pp.poseidon_config, + C1::ScalarField::zero(), + z_0.clone(), + z_0.clone(), + )?, + cf_U_dummy.hash_cyclefold(&pp.poseidon_config)?, + ]; + + // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the + // R1CS that we're working with. + Ok(Self { + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + ccs, + cf_r1cs, + poseidon_config: pp.poseidon_config.clone(), + cs_params: pp.cs_params.clone(), + cf_cs_params: pp.cf_cs_params.clone(), + F, + i: C1::ScalarField::zero(), + z_0: z_0.clone(), + z_i: z_0, + W_i: W_dummy, + U_i: U_dummy, + w_i: w_dummy, + u_i: u_dummy, + // cyclefold running instance + cf_W_i: cf_W_dummy, + cf_U_i: cf_U_dummy, + }) + } + + /// Implements IVC.P of HyperNova+CycleFold + fn prove_step( + &mut self, + mut rng: impl RngCore, + external_inputs: Vec<C1::ScalarField>, + ) -> Result<(), Error> { + let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>; + + if self.z_i.len() != self.F.state_len() { + return Err(Error::NotSameLength( + "z_i.len()".to_string(), + self.z_i.len(), + "F.state_len()".to_string(), + self.F.state_len(), + )); + } + if external_inputs.len() != self.F.external_inputs_len() { + return Err(Error::NotSameLength( + "F.external_inputs_len()".to_string(), + self.F.external_inputs_len(), + "external_inputs.len()".to_string(), + external_inputs.len(), + )); + } + + if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) { + return Err(Error::MaxStep); + } + + let mut i_bytes: [u8; 8] = [0; 8]; + i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]); + let i_usize: usize = usize::from_le_bytes(i_bytes); + + let z_i1 = self + .F + .step_native(i_usize, self.z_i.clone(), external_inputs.clone())?; + + // u_{i+1}.x[1] = H(cf_U_{i+1}) + let cf_u_i1_x: C1::ScalarField; + let (U_i1, W_i1); + + if self.i == C1::ScalarField::zero() { + W_i1 = Witness::<C1::ScalarField>::dummy(&self.ccs); + U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); + + let u_i1_x = U_i1.hash( + &self.poseidon_config, + 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)?; + + augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { + _c2: PhantomData, + _gc2: PhantomData, + poseidon_config: self.poseidon_config.clone(), + ccs: self.ccs.clone(), + i: Some(C1::ScalarField::zero()), + i_usize: Some(0), + z_0: Some(self.z_0.clone()), + z_i: Some(self.z_i.clone()), + external_inputs: Some(external_inputs.clone()), + u_i_C: Some(self.u_i.C), + U_i: Some(self.U_i.clone()), + U_i1_C: Some(U_i1.C), + F: self.F.clone(), + x: Some(u_i1_x), + nimfs_proof: None, + + // cyclefold values + cf_u_i_cmW: None, + cf_U_i: None, + cf_x: Some(cf_u_i1_x), + cf_cmT: None, + }; + } else { + let mut transcript_p: PoseidonTranscript<C1> = + PoseidonTranscript::<C1>::new(&self.poseidon_config); + 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()], + )?; + + // 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, + 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)?; + // CycleFold part: + // get the vector used as public inputs 'x' in the CycleFold circuit + // cyclefold circuit for cmW + let cf_u_i_x = [ + vec![rho_Fq], + get_cm_coordinates(&self.U_i.C), + get_cm_coordinates(&self.u_i.C), + get_cm_coordinates(&U_i1.C), + ] + .concat(); + + let cf_circuit = CycleFoldCircuit::<C1, GC1> { + _gc: PhantomData, + r_bits: Some(rho_bits.clone()), + p1: Some(self.U_i.clone().C), + p2: Some(self.u_i.clone().C), + x: Some(cf_u_i_x.clone()), + }; + + 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, + self.cf_r1cs.clone(), + self.cf_cs_params.clone(), + self.cf_W_i.clone(), // CycleFold running instance witness + self.cf_U_i.clone(), // CycleFold running instance + cf_u_i_x, + cf_circuit, + )?; + + cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?; + + augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { + _c2: PhantomData, + _gc2: PhantomData, + poseidon_config: self.poseidon_config.clone(), + ccs: self.ccs.clone(), + i: Some(self.i), + i_usize: Some(i_usize), + z_0: Some(self.z_0.clone()), + z_i: Some(self.z_i.clone()), + external_inputs: Some(external_inputs), + u_i_C: Some(self.u_i.C), + U_i: Some(self.U_i.clone()), + U_i1_C: Some(U_i1.C), + F: self.F.clone(), + x: Some(u_i1_x), + nimfs_proof: Some(nimfs_proof), + + // cyclefold values + cf_u_i_cmW: Some(cf_u_i.cmW), + cf_U_i: Some(self.cf_U_i.clone()), + cf_x: Some(cf_u_i1_x), + cf_cmT: Some(cf_cmT), + }; + + // assign the next round instances + self.cf_W_i = cf_W_i1; + self.cf_U_i = cf_U_i1; + } + + let (cs, _) = augmented_f_circuit.compute_cs_ccs()?; + + #[cfg(test)] + assert!(cs.is_satisfied()?); + + let (r1cs_w_i1, r1cs_x_i1) = extract_w_x::<C1::ScalarField>(&cs); // includes 1 and public inputs + + let r1cs_z = [ + vec![C1::ScalarField::one()], + r1cs_x_i1.clone(), + r1cs_w_i1.clone(), + ] + .concat(); + // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we + // assign them directly to w_i, u_i. + let (u_i, w_i) = self + .ccs + .to_cccs::<_, C1, CS1>(&mut rng, &self.cs_params, &r1cs_z)?; + self.u_i = u_i.clone(); + self.w_i = w_i.clone(); + + // set values for next iteration + self.i += C1::ScalarField::one(); + // assign z_{i+1} into z_i + self.z_i = z_i1.clone(); + self.U_i = U_i1.clone(); + self.W_i = W_i1.clone(); + + #[cfg(test)] + { + // check the new LCCCS instance relation + self.U_i.check_relation(&self.ccs, &self.W_i)?; + // check the new CCCS instance relation + self.u_i.check_relation(&self.ccs, &self.w_i)?; + } + + Ok(()) + } + + fn state(&self) -> Vec<C1::ScalarField> { + self.z_i.clone() + } + + fn instances( + &self, + ) -> ( + Self::RunningInstance, + Self::IncomingInstance, + Self::CFInstance, + ) { + ( + (self.U_i.clone(), self.W_i.clone()), + (self.u_i.clone(), self.w_i.clone()), + (self.cf_U_i.clone(), self.cf_W_i.clone()), + ) + } + + /// Implements IVC.V of HyperNova+CycleFold. Notice that this method does not include the + /// commitments verification, which is done in the Decider. + fn verify( + vp: Self::VerifierParam, + z_0: Vec<C1::ScalarField>, // initial state + z_i: Vec<C1::ScalarField>, // last state + num_steps: C1::ScalarField, + running_instance: Self::RunningInstance, + incoming_instance: Self::IncomingInstance, + cyclefold_instance: Self::CFInstance, + ) -> Result<(), Error> { + if num_steps == C1::ScalarField::zero() { + if z_0 != z_i { + return Err(Error::IVCVerificationFail); + } + return Ok(()); + } + + let (U_i, W_i) = running_instance; + let (u_i, w_i) = incoming_instance; + let (cf_U_i, cf_W_i) = cyclefold_instance; + if u_i.x.len() != 2 || U_i.x.len() != 2 { + return Err(Error::IVCVerificationFail); + } + + // 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, 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)?; + if expected_cf_u_i_x != u_i.x[1] { + return Err(Error::IVCVerificationFail); + } + + // check LCCCS satisfiability + U_i.check_relation(&vp.ccs, &W_i)?; + // check CCCS satisfiability + u_i.check_relation(&vp.ccs, &w_i)?; + + // check CycleFold's RelaxedR1CS satisfiability + vp.cf_r1cs + .check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::commitment::kzg::KZG; + use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; + use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + + use super::*; + use crate::commitment::pedersen::Pedersen; + use crate::frontend::tests::CubicFCircuit; + use crate::transcript::poseidon::poseidon_canonical_config; + + #[test] + pub fn test_ivc() { + let poseidon_config = poseidon_canonical_config::<Fr>(); + + let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); + + // run the test using Pedersen commitments on both sides of the curve cycle + test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>( + poseidon_config.clone(), + F_circuit, + ); + // run the test using KZG for the commitments on the main curve, and Pedersen for the + // commitments on the secondary curve + test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit); + } + + // test_ivc allowing to choose the CommitmentSchemes + fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>( + poseidon_config: PoseidonConfig<Fr>, + F_circuit: CubicFCircuit<Fr>, + ) { + let mut rng = ark_std::test_rng(); + + type HN<CS1, CS2> = + HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; + + let prep_param = PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2> { + poseidon_config, + F: F_circuit, + cs_params: None, + cf_cs_params: None, + }; + let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap(); + + let z_0 = vec![Fr::from(3_u32)]; + let mut hypernova = HN::init(&prover_params, F_circuit, z_0.clone()).unwrap(); + + let num_steps: usize = 3; + for _ in 0..num_steps { + hypernova.prove_step(&mut rng, vec![]).unwrap(); + } + assert_eq!(Fr::from(num_steps as u32), hypernova.i); + + let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances(); + HN::verify( + verifier_params, + z_0, + hypernova.z_i, + hypernova.i, + running_instance, + incoming_instance, + cyclefold_instance, + ) + .unwrap(); + } +}
M folding-schemes/src/folding/hypernova/nimfs.rs → folding-schemes/src/folding/hypernova/nimfs.rs

@@ -32,19 +32,21 @@ }

impl<C: CurveGroup> NIMFSProof<C> { pub fn dummy(ccs: &CCS<C::ScalarField>, mu: usize, nu: usize) -> Self { + // use 'C::ScalarField::one()' instead of 'zero()' to enforce the NIMFSProof to have the + // same in-circuit representation to match the number of constraints of an actual proof. NIMFSProof::<C> { sc_proof: SumCheckProof::<C::ScalarField> { - point: vec![C::ScalarField::zero(); ccs.d], + point: vec![C::ScalarField::one(); ccs.s], proofs: vec![ IOPProverMessage { - coeffs: vec![C::ScalarField::zero(); ccs.t + 1] + coeffs: vec![C::ScalarField::one(); ccs.t + 1] }; ccs.s ], }, sigmas_thetas: SigmasThetas( - vec![vec![C::ScalarField::zero(); ccs.t]; mu], - vec![vec![C::ScalarField::zero(); ccs.t]; nu], + vec![vec![C::ScalarField::one(); ccs.t]; mu], + vec![vec![C::ScalarField::one(); ccs.t]; nu], ), } }

@@ -432,11 +434,15 @@

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); - let (lcccs, w1) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); - let (cccs, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z2).unwrap(); + let (lcccs, w1) = ccs + .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .unwrap(); + let (cccs, w2) = ccs + .to_cccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z2) + .unwrap(); - lcccs.check_relation(&pedersen_params, &ccs, &w1).unwrap(); - cccs.check_relation(&pedersen_params, &ccs, &w2).unwrap(); + lcccs.check_relation(&ccs, &w1).unwrap(); + cccs.check_relation(&ccs, &w2).unwrap(); let mut rng = test_rng(); let rho = Fr::rand(&mut rng);

@@ -453,9 +459,7 @@ let w_folded =

NIMFS::<Projective, PoseidonTranscript<Projective>>::fold_witness(&[w1], &[w2], rho); // check lcccs relation - folded - .check_relation(&pedersen_params, &ccs, &w_folded) - .unwrap(); + folded.check_relation(&ccs, &w_folded).unwrap(); } /// Perform multifolding of an LCCCS instance with a CCCS instance (as described in the paper)

@@ -474,9 +478,13 @@ // Generate another satisfying witness

let z_2 = get_test_z(4); // Create the LCCCS instance out of z_1 - let (running_instance, w1) = ccs.to_lcccs(&mut rng, &pedersen_params, &z_1).unwrap(); + let (running_instance, w1) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1) + .unwrap(); // Create the CCCS instance out of z_2 - let (new_instance, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z_2).unwrap(); + let (new_instance, w2) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2) + .unwrap(); // Prover's transcript let poseidon_config = poseidon_canonical_config::<Fr>();

@@ -513,9 +521,7 @@ .unwrap();

assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs - .check_relation(&pedersen_params, &ccs, &folded_witness) - .unwrap(); + folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); } /// Perform multiple steps of multifolding of an LCCCS instance with a CCCS instance

@@ -530,8 +536,9 @@ Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();

// LCCCS witness let z_1 = get_test_z(2); - let (mut running_instance, mut w1) = - ccs.to_lcccs(&mut rng, &pedersen_params, &z_1).unwrap(); + let (mut running_instance, mut w1) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1) + .unwrap(); let poseidon_config = poseidon_canonical_config::<Fr>();

@@ -548,7 +555,9 @@ for i in 3..n {

// CCS witness let z_2 = get_test_z(i); - let (new_instance, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z_2).unwrap(); + let (new_instance, w2) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2) + .unwrap(); // run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) =

@@ -574,9 +583,7 @@ .unwrap();

assert_eq!(folded_lcccs, folded_lcccs_v); // check that the folded instance with the folded witness holds the LCCCS relation - folded_lcccs - .check_relation(&pedersen_params, &ccs, &folded_witness) - .unwrap(); + folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); running_instance = folded_lcccs; w1 = folded_witness;

@@ -612,7 +619,9 @@ // Create the LCCCS instances out of z_lcccs

let mut lcccs_instances = Vec::new(); let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { - let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (running_instance, w) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); }

@@ -620,7 +629,9 @@ // Create the CCCS instance out of z_cccs

let mut cccs_instances = Vec::new(); let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { - let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (new_instance, w) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); }

@@ -660,9 +671,7 @@ .unwrap();

assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs - .check_relation(&pedersen_params, &ccs, &folded_witness) - .unwrap(); + folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); } /// Test that generates mu>1 and nu>1 instances, and folds them in a single multifolding step

@@ -710,7 +719,9 @@ // Create the LCCCS instances out of z_lcccs

let mut lcccs_instances = Vec::new(); let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { - let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (running_instance, w) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); }

@@ -718,7 +729,9 @@ // Create the CCCS instance out of z_cccs

let mut cccs_instances = Vec::new(); let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { - let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap(); + let (new_instance, w) = ccs + .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); }

@@ -748,9 +761,7 @@

assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs - .check_relation(&pedersen_params, &ccs, &folded_witness) - .unwrap(); + folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); } } }
M folding-schemes/src/folding/hypernova/utils.rs → folding-schemes/src/folding/hypernova/utils.rs

@@ -239,7 +239,9 @@

// Initialize a multifolding object let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); - let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); + let (lcccs_instance, _) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .unwrap(); let sigmas_thetas = compute_sigmas_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime).unwrap();

@@ -287,7 +289,9 @@

// Initialize a multifolding object let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); - let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); + let (lcccs_instance, _) = ccs + .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .unwrap(); // Compute g(x) with that r_x let g = compute_g::<Projective>(
M folding-schemes/src/folding/nova/decider_eth.rs → folding-schemes/src/folding/nova/decider_eth.rs

@@ -62,12 +62,13 @@ C2: CurveGroup,

GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, + // CS1 is a KZG commitment, where challenge is C1::Fr elem CS1: CommitmentScheme< C1, ProverChallenge = C1::ScalarField, Challenge = C1::ScalarField, Proof = KZGProof<C1>, - >, // KZG commitment, where challenge is C1::Fr elem + >, // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider CS2: CommitmentScheme<C2, ProverParams = PedersenParams<C2>>, S: SNARK<C1::ScalarField>,

@@ -77,20 +78,52 @@ <C2 as CurveGroup>::BaseField: PrimeField,

<C1 as Group>::ScalarField: Absorb, <C2 as Group>::ScalarField: Absorb, C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>, + for<'b> &'b GC1: GroupOpsBounds<'b, C1, GC1>, for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>, // constrain FS into Nova, since this is a Decider specifically for Nova Nova<C1, GC1, C2, GC2, FC, CS1, CS2>: From<FS>, + crate::folding::nova::ProverParams<C1, C2, CS1, CS2>: + From<<FS as FoldingScheme<C1, C2, FC>>::ProverParam>, + crate::folding::nova::VerifierParams<C1, C2, CS1, CS2>: + From<<FS as FoldingScheme<C1, C2, FC>>::VerifierParam>, { + type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof<C1, CS1, S>; type VerifierParam = (S::VerifyingKey, CS1::VerifierParams); type PublicInput = Vec<C1::ScalarField>; - type CommittedInstanceWithWitness = (); type CommittedInstance = CommittedInstance<C1>; + fn preprocess( + mut rng: impl RngCore + CryptoRng, + prep_param: &Self::PreprocessorParam, + fs: FS, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + let circuit = + DeciderEthCircuit::<C1, GC1, C2, GC2, CS1, CS2>::from_nova::<FC>(fs.into()).unwrap(); + + // get the Groth16 specific setup for the circuit + let (g16_pk, g16_vk) = S::circuit_specific_setup(circuit, &mut rng).unwrap(); + + // get the FoldingScheme prover & verifier params from Nova + #[allow(clippy::type_complexity)] + let nova_pp: + <Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::ProverParam = + prep_param.0.clone().into() + ; + #[allow(clippy::type_complexity)] + let nova_vp: + <Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::VerifierParam = + prep_param.1.clone().into(); + + let pp = (g16_pk, nova_pp.cs_pp); + let vp = (g16_vk, nova_vp.cs_vp); + Ok((pp, vp)) + } + fn prove( - pp: Self::ProverParam, mut rng: impl RngCore + CryptoRng, + pp: Self::ProverParam, folding_scheme: FS, ) -> Result<Self::Proof, Error> { let (snark_pk, cs_pk): (S::ProvingKey, CS1::ProverParams) = pp;

@@ -281,13 +314,13 @@ }

#[cfg(test)] pub mod tests { - use super::*; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_groth16::Groth16; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; use std::time::Instant; + use super::*; use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG}; use crate::commitment::pedersen::Pedersen; use crate::folding::nova::{get_cs_params_len, ProverParams};

@@ -297,7 +330,7 @@

#[test] fn test_decider() { // use Nova as FoldingScheme - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2,

@@ -306,7 +339,7 @@ CubicFCircuit<Fr>,

KZG<'static, Bn254>, Pedersen<Projective2>, >; - type DECIDER = Decider< + type D = Decider< Projective, GVar, Projective2,

@@ -315,7 +348,7 @@ CubicFCircuit<Fr>,

KZG<'static, Bn254>, Pedersen<Projective2>, Groth16<Bn254>, // here we define the Snark to use in the decider - NOVA, // here we define the FoldingScheme to use + N, // here we define the FoldingScheme to use >; let mut rng = ark_std::test_rng();

@@ -339,17 +372,17 @@

let prover_params = ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cs_params: kzg_pk.clone(), - cf_cs_params: cf_pedersen_params, + cs_pp: kzg_pk.clone(), + cf_cs_pp: cf_pedersen_params, }; let start = Instant::now(); - let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); + let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap(); println!("Nova initialized, {:?}", start.elapsed()); let start = Instant::now(); - nova.prove_step(vec![]).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); println!("prove_step, {:?}", start.elapsed()); - nova.prove_step(vec![]).unwrap(); // do a 2nd step + nova.prove_step(&mut rng, vec![]).unwrap(); // do a 2nd step // generate Groth16 setup let circuit = DeciderEthCircuit::<

@@ -371,13 +404,13 @@

// decider proof generation let start = Instant::now(); let decider_pp = (g16_pk, kzg_pk); - let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap(); + let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); println!("Decider prove, {:?}", start.elapsed()); // decider proof verification let start = Instant::now(); let decider_vp = (g16_vk, kzg_vk); - let verified = DECIDER::verify( + let verified = D::verify( decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof, ) .unwrap();
M folding-schemes/src/folding/nova/decider_eth_circuit.rs → folding-schemes/src/folding/nova/decider_eth_circuit.rs

@@ -264,7 +264,7 @@ nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2>,

) -> Result<Self, Error> { // compute the U_{i+1}, W_{i+1} let (T, cmT) = NIFS::<C1, CS1>::compute_cmT( - &nova.cs_params, + &nova.cs_pp, &nova.r1cs.clone(), &nova.w_i.clone(), &nova.u_i.clone(),

@@ -315,7 +315,7 @@ E_len: nova.W_i.E.len(),

cf_E_len: nova.cf_W_i.E.len(), r1cs: nova.r1cs, cf_r1cs: nova.cf_r1cs, - cf_pedersen_params: nova.cf_cs_params, + cf_pedersen_params: nova.cf_cs_pp, poseidon_config: nova.poseidon_config, i: Some(nova.i), z_0: Some(nova.z_0),

@@ -438,7 +438,7 @@ {

// imports here instead of at the top of the file, so we avoid having multiple // `#[cfg(not(test))]` use crate::commitment::pedersen::PedersenGadget; - use crate::folding::nova::cyclefold::{CycleFoldCommittedInstanceVar, CF_IO_LEN}; + use crate::folding::circuits::cyclefold::{CycleFoldCommittedInstanceVar, CF_IO_LEN}; use ark_r1cs_std::ToBitsGadget; let cf_u_dummy_native = CommittedInstance::<C2>::dummy(CF_IO_LEN);

@@ -597,7 +597,6 @@ }

#[cfg(test)] pub mod tests { - use super::*; use ark_crypto_primitives::crh::{ sha256::{ constraints::{Sha256Gadget, UnitVar},

@@ -611,14 +610,14 @@ use ark_relations::r1cs::ConstraintSystem;

use ark_std::{One, UniformRand}; use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; + use super::*; + use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; + use crate::ccs::r1cs::{extract_r1cs, extract_w_x}; use crate::commitment::pedersen::Pedersen; use crate::folding::nova::{get_cs_params_len, ProverParams, VerifierParams}; use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit}; use crate::transcript::poseidon::poseidon_canonical_config; use crate::FoldingScheme; - - use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; - use crate::ccs::r1cs::{extract_r1cs, extract_w_x}; #[test] fn test_relaxed_r1cs_small_gadget_handcrafted() {

@@ -786,11 +785,11 @@

let prover_params = ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cs_params: pedersen_params, - cf_cs_params: cf_pedersen_params, + cs_pp: pedersen_params.clone(), + cf_cs_pp: cf_pedersen_params.clone(), }; - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2,

@@ -801,16 +800,23 @@ Pedersen<Projective2>,

>; // generate a Nova instance and do a step of it - let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); - nova.prove_step(vec![]).unwrap(); + let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); let ivc_v = nova.clone(); - let verifier_params = VerifierParams::<Projective, Projective2> { + let verifier_params = VerifierParams::< + Projective, + Projective2, + Pedersen<Projective>, + Pedersen<Projective2>, + > { poseidon_config: poseidon_config.clone(), r1cs: ivc_v.clone().r1cs, cf_r1cs: ivc_v.clone().cf_r1cs, + cs_vp: pedersen_params, + cf_cs_vp: cf_pedersen_params, }; let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances(); - NOVA::verify( + N::verify( verifier_params, z_0, ivc_v.z_i,
M folding-schemes/src/folding/nova/mod.rs → folding-schemes/src/folding/nova/mod.rs

@@ -10,6 +10,7 @@ use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};

use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::fmt::Debug; +use ark_std::rand::RngCore; use ark_std::{One, Zero}; use core::marker::PhantomData; use std::usize;

@@ -24,7 +25,7 @@ },

CF2, }; use crate::frontend::FCircuit; -use crate::utils::vec::is_zero_vec; +use crate::utils::{get_cm_coordinates, vec::is_zero_vec}; use crate::Error; use crate::FoldingScheme;

@@ -187,6 +188,44 @@ }

} #[derive(Debug, Clone)] +pub struct PreprocessorParam<C1, C2, FC, CS1, CS2> +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ + pub poseidon_config: PoseidonConfig<C1::ScalarField>, + pub F: FC, + // cs params if not provided, will be generated at the preprocess method + pub cs_pp: Option<CS1::ProverParams>, + pub cs_vp: Option<CS1::VerifierParams>, + pub cf_cs_pp: Option<CS2::ProverParams>, + pub cf_cs_vp: Option<CS2::VerifierParams>, +} + +impl<C1, C2, FC, CS1, CS2> PreprocessorParam<C1, C2, FC, CS1, CS2> +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ + pub fn new(poseidon_config: PoseidonConfig<C1::ScalarField>, F: FC) -> Self { + Self { + poseidon_config, + F, + cs_pp: None, + cs_vp: None, + cf_cs_pp: None, + cf_cs_vp: None, + } + } +} + +#[derive(Debug, Clone)] pub struct ProverParams<C1, C2, CS1, CS2> where C1: CurveGroup,

@@ -195,15 +234,23 @@ CS1: CommitmentScheme<C1>,

CS2: CommitmentScheme<C2>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, - pub cs_params: CS1::ProverParams, - pub cf_cs_params: CS2::ProverParams, + pub cs_pp: CS1::ProverParams, + pub cf_cs_pp: CS2::ProverParams, } #[derive(Debug, Clone)] -pub struct VerifierParams<C1: CurveGroup, C2: CurveGroup> { +pub struct VerifierParams<C1, C2, CS1, CS2> +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, +{ pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub r1cs: R1CS<C1::ScalarField>, pub cf_r1cs: R1CS<C2::ScalarField>, + pub cs_vp: CS1::VerifierParams, + pub cf_cs_vp: CS2::VerifierParams, } /// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and

@@ -228,9 +275,9 @@ /// R1CS of the CycleFold circuit

pub cf_r1cs: R1CS<C2::ScalarField>, pub poseidon_config: PoseidonConfig<C1::ScalarField>, /// CommitmentScheme::ProverParams over C1 - pub cs_params: CS1::ProverParams, + pub cs_pp: CS1::ProverParams, /// CycleFold CommitmentScheme::ProverParams, over C2 - pub cf_cs_params: CS2::ProverParams, + pub cf_cs_pp: CS2::ProverParams, /// F circuit, the circuit that is being folded pub F: FC, pub i: C1::ScalarField,

@@ -267,24 +314,50 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { - type PreprocessorParam = (Self::ProverParam, FC); + type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2>; type ProverParam = ProverParams<C1, C2, CS1, CS2>; - type VerifierParam = VerifierParams<C1, C2>; - type CommittedInstanceWithWitness = (CommittedInstance<C1>, Witness<C1>); - type CFCommittedInstanceWithWitness = (CommittedInstance<C2>, Witness<C2>); + type VerifierParam = VerifierParams<C1, C2, CS1, CS2>; + type RunningInstance = (CommittedInstance<C1>, Witness<C1>); + type IncomingInstance = (CommittedInstance<C1>, Witness<C1>); + type CFInstance = (CommittedInstance<C2>, Witness<C2>); fn preprocess( + mut rng: impl RngCore, prep_param: &Self::PreprocessorParam, ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { - let (prover_params, F_circuit) = prep_param; + let (r1cs, cf_r1cs) = + get_r1cs::<C1, GC1, C2, GC2, FC>(&prep_param.poseidon_config, prep_param.F.clone())?; - let (r1cs, cf_r1cs) = - get_r1cs::<C1, GC1, C2, GC2, FC>(&prover_params.poseidon_config, F_circuit.clone())?; + // if cs params exist, use them, if not, generate new ones + let cs_pp: CS1::ProverParams; + let cs_vp: CS1::VerifierParams; + let cf_cs_pp: CS2::ProverParams; + let cf_cs_vp: CS2::VerifierParams; + if prep_param.cs_pp.is_some() + && prep_param.cf_cs_pp.is_some() + && prep_param.cs_vp.is_some() + && prep_param.cf_cs_vp.is_some() + { + cs_pp = prep_param.clone().cs_pp.unwrap(); + cs_vp = prep_param.clone().cs_vp.unwrap(); + cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap(); + cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap(); + } else { + (cs_pp, cs_vp) = CS1::setup(&mut rng, r1cs.A.n_rows).unwrap(); + (cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_rows).unwrap(); + } - let verifier_params = VerifierParams::<C1, C2> { - poseidon_config: prover_params.poseidon_config.clone(), + let prover_params = ProverParams::<C1, C2, CS1, CS2> { + poseidon_config: prep_param.poseidon_config.clone(), + cs_pp: cs_pp.clone(), + cf_cs_pp: cf_cs_pp.clone(), + }; + let verifier_params = VerifierParams::<C1, C2, CS1, CS2> { + poseidon_config: prep_param.poseidon_config.clone(), r1cs, cf_r1cs, + cs_vp: cs_vp.clone(), + cf_cs_vp: cf_cs_vp.clone(), }; Ok((prover_params.clone(), verifier_params)) }

@@ -322,8 +395,8 @@ _gc2: PhantomData,

r1cs, cf_r1cs, poseidon_config: pp.poseidon_config.clone(), - cs_params: pp.cs_params.clone(), - cf_cs_params: pp.cf_cs_params.clone(), + cs_pp: pp.cs_pp.clone(), + cf_cs_pp: pp.cf_cs_pp.clone(), F, i: C1::ScalarField::zero(), z_0: z_0.clone(),

@@ -339,7 +412,11 @@ })

} /// Implements IVC.P of Nova+CycleFold - fn prove_step(&mut self, external_inputs: Vec<C1::ScalarField>) -> Result<(), Error> { + fn prove_step( + &mut self, + _rng: impl RngCore, + external_inputs: Vec<C1::ScalarField>, + ) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>; if self.z_i.len() != self.F.state_len() {

@@ -535,7 +612,7 @@ // set values for next iteration

self.i += C1::ScalarField::one(); self.z_i = z_i1; self.w_i = Witness::<C1>::new(w_i1, self.r1cs.A.n_rows); - self.u_i = self.w_i.commit::<CS1>(&self.cs_params, x_i1)?; + self.u_i = self.w_i.commit::<CS1>(&self.cs_pp, x_i1)?; self.W_i = W_i1; self.U_i = U_i1;

@@ -552,12 +629,13 @@

fn state(&self) -> Vec<C1::ScalarField> { self.z_i.clone() } + fn instances( &self, ) -> ( - Self::CommittedInstanceWithWitness, - Self::CommittedInstanceWithWitness, - Self::CFCommittedInstanceWithWitness, + Self::RunningInstance, + Self::IncomingInstance, + Self::CFInstance, ) { ( (self.U_i.clone(), self.W_i.clone()),

@@ -566,16 +644,24 @@ (self.cf_U_i.clone(), self.cf_W_i.clone()),

) } - /// Implements IVC.V of Nova+CycleFold + /// Implements IVC.V of Nova+CycleFold. Notice that this method does not include the + /// commitments verification, which is done in the Decider. fn verify( vp: Self::VerifierParam, z_0: Vec<C1::ScalarField>, // initial state z_i: Vec<C1::ScalarField>, // last state num_steps: C1::ScalarField, - running_instance: Self::CommittedInstanceWithWitness, - incoming_instance: Self::CommittedInstanceWithWitness, - cyclefold_instance: Self::CFCommittedInstanceWithWitness, + running_instance: Self::RunningInstance, + incoming_instance: Self::IncomingInstance, + cyclefold_instance: Self::CFInstance, ) -> Result<(), Error> { + if num_steps == C1::ScalarField::zero() { + if z_0 != z_i { + return Err(Error::IVCVerificationFail); + } + return Ok(()); + } + let (U_i, W_i) = running_instance; let (u_i, w_i) = incoming_instance; let (cf_U_i, cf_W_i) = cyclefold_instance;

@@ -631,7 +717,7 @@ {

// computes T and cmT for the AugmentedFCircuit fn compute_cmT(&self) -> Result<(Vec<C1::ScalarField>, C1), Error> { NIFS::<C1, CS1>::compute_cmT( - &self.cs_params, + &self.cs_pp, &self.r1cs, &self.w_i, &self.u_i,

@@ -680,7 +766,7 @@ > {

fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>( &self.poseidon_config, self.cf_r1cs.clone(), - self.cf_cs_params.clone(), + self.cf_cs_pp.clone(), cf_W_i, cf_U_i, cf_u_i_x,

@@ -753,23 +839,13 @@ let (r1cs, cf_r1cs) = get_r1cs::<C1, GC1, C2, GC2, FC>(poseidon_config, F_circuit)?;

Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows)) } -/// returns the coordinates of a commitment point. This is compatible with the arkworks -/// GC.to_constraint_field()[..2] -pub(crate) fn get_cm_coordinates<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> { - let zero = (&C::BaseField::zero(), &C::BaseField::zero()); - let cm = cm.into_affine(); - let (cm_x, cm_y) = cm.xy().unwrap_or(zero); - vec![*cm_x, *cm_y] -} - #[cfg(test)] pub mod tests { - use super::*; - use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG}; + use crate::commitment::kzg::KZG; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; - use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; + use super::*; use crate::commitment::pedersen::Pedersen; use crate::frontend::tests::CubicFCircuit; use crate::transcript::poseidon::poseidon_canonical_config;

@@ -778,71 +854,49 @@ /// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the

/// AugmentedFCircuit #[test] fn test_ivc() { - let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::<Fr>(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); - - let (cs_len, cf_cs_len) = - get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( - &poseidon_config, - F_circuit, - ) - .unwrap(); - let (kzg_pk, _): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) = - KZG::<Bn254>::setup(&mut rng, cs_len).unwrap(); - let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap(); // run the test using Pedersen commitments on both sides of the curve cycle test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>( poseidon_config.clone(), - pedersen_params, - cf_pedersen_params.clone(), F_circuit, ); // run the test using KZG for the commitments on the main curve, and Pedersen for the // commitments on the secondary curve - test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>( - poseidon_config, - kzg_pk, - cf_pedersen_params, - F_circuit, - ); + test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit); } // test_ivc allowing to choose the CommitmentSchemes fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>( poseidon_config: PoseidonConfig<Fr>, - cs_params: CS1::ProverParams, - cf_cs_params: CS2::ProverParams, F_circuit: CubicFCircuit<Fr>, ) { - type NOVA<CS1, CS2> = - Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; + let mut rng = ark_std::test_rng(); + type N<CS1, CS2> = Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; - let prover_params = ProverParams::<Projective, Projective2, CS1, CS2> { - poseidon_config: poseidon_config.clone(), - cs_params, - cf_cs_params, + let prep_param = PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2> { + poseidon_config, + F: F_circuit, + cs_pp: None, + cs_vp: None, + cf_cs_pp: None, + cf_cs_vp: None, }; + let (prover_params, verifier_params) = N::preprocess(&mut rng, &prep_param).unwrap(); let z_0 = vec![Fr::from(3_u32)]; - let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); + let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap(); let num_steps: usize = 3; for _ in 0..num_steps { - nova.prove_step(vec![]).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); } assert_eq!(Fr::from(num_steps as u32), nova.i); - let verifier_params = VerifierParams::<Projective, Projective2> { - poseidon_config, - r1cs: nova.clone().r1cs, - cf_r1cs: nova.clone().cf_r1cs, - }; let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); - NOVA::<CS1, CS2>::verify( + N::<CS1, CS2>::verify( verifier_params, z_0, nova.z_i,
M folding-schemes/src/folding/nova/serialize.rs → folding-schemes/src/folding/nova/serialize.rs

@@ -1,10 +1,3 @@

-use super::{circuits::AugmentedFCircuit, Nova, ProverParams}; -pub use super::{CommittedInstance, Witness}; -pub use crate::folding::circuits::{cyclefold::CycleFoldCircuit, CF2}; -use crate::{ - ccs::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1, - frontend::FCircuit, -}; use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField;

@@ -16,6 +9,14 @@ use ark_relations::r1cs::ConstraintSynthesizer;

use ark_relations::r1cs::ConstraintSystem; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Write}; use std::marker::PhantomData; + +use super::{circuits::AugmentedFCircuit, Nova, ProverParams}; +use super::{CommittedInstance, Witness}; +use crate::folding::circuits::{cyclefold::CycleFoldCircuit, CF2}; +use crate::{ + ccs::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1, + frontend::FCircuit, +}; impl<C1, GC1, C2, GC2, FC, CS1, CS2> CanonicalSerialize for Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where

@@ -150,8 +151,8 @@ Ok(Nova {

_gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, - cs_params: prover_params.cs_params, - cf_cs_params: prover_params.cf_cs_params, + cs_pp: prover_params.cs_pp, + cf_cs_pp: prover_params.cf_cs_pp, i, z_0, z_i,

@@ -171,6 +172,12 @@ }

#[cfg(test)] pub mod tests { + use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; + use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; + use ark_serialize::{CanonicalSerialize, Compress, Validate}; + use std::{fs, io::Write}; + use crate::{ commitment::{ kzg::{ProverKey as KZGProverKey, KZG},

@@ -182,11 +189,6 @@ frontend::{tests::CubicFCircuit, FCircuit},

transcript::poseidon::poseidon_canonical_config, FoldingScheme, }; - use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; - use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; - use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; - use ark_serialize::{CanonicalSerialize, Compress, Validate}; - use std::{fs, io::Write}; #[test] fn test_serde_nova() {

@@ -204,21 +206,28 @@ KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();

let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap(); // Initialize nova and make multiple `prove_step()` - type NOVA<CS1, CS2> = - Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; + type N = Nova< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + KZG<'static, Bn254>, + Pedersen<Projective2>, + >; let prover_params = ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cs_params: kzg_pk.clone(), - cf_cs_params: cf_pedersen_params.clone(), + cs_pp: kzg_pk.clone(), + cf_cs_pp: cf_pedersen_params.clone(), }; let z_0 = vec![Fr::from(3_u32)]; - let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); + let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap(); let num_steps: usize = 3; for _ in 0..num_steps { - nova.prove_step(vec![]).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); } let mut writer = vec![];

@@ -257,8 +266,8 @@ assert_eq!(nova.i, deserialized_nova.i);

let num_steps: usize = 3; for _ in 0..num_steps { - deserialized_nova.prove_step(vec![]).unwrap(); - nova.prove_step(vec![]).unwrap(); + deserialized_nova.prove_step(&mut rng, vec![]).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); } assert_eq!(deserialized_nova.w_i, nova.w_i);
M folding-schemes/src/folding/nova/traits.rs → folding-schemes/src/folding/nova/traits.rs

@@ -39,6 +39,7 @@ let u_dummy = CommittedInstance::<C>::dummy(self.l);

(w_dummy, u_dummy) } + // notice that this method does not check the commitment correctness fn check_instance_relation( &self, W: &Witness<C>,

@@ -52,6 +53,7 @@ let Z: Vec<C::ScalarField> = [vec![U.u], U.x.to_vec(), W.W.to_vec()].concat();

self.check_relation(&Z) } + // notice that this method does not check the commitment correctness fn check_relaxed_instance_relation( &self, W: &Witness<C>,
M folding-schemes/src/lib.rs → folding-schemes/src/lib.rs

@@ -1,7 +1,6 @@

#![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] -#![allow(clippy::upper_case_acronyms)] use ark_ec::{pairing::Pairing, CurveGroup}; use ark_ff::PrimeField;

@@ -110,13 +109,15 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

C2::BaseField: PrimeField, FC: FCircuit<C1::ScalarField>, { - type PreprocessorParam: Debug; - type ProverParam: Debug; - type VerifierParam: Debug; - type CommittedInstanceWithWitness: Debug; - type CFCommittedInstanceWithWitness: Debug; // CycleFold CommittedInstance & Witness + type PreprocessorParam: Debug + Clone; + type ProverParam: Debug + Clone; + type VerifierParam: Debug + Clone; + type RunningInstance: Debug; // contains the CommittedInstance + Witness + type IncomingInstance: Debug; // contains the CommittedInstance + Witness + type CFInstance: Debug; // CycleFold CommittedInstance & Witness fn preprocess( + rng: impl RngCore, prep_param: &Self::PreprocessorParam, ) -> Result<(Self::ProverParam, Self::VerifierParam), Error>;

@@ -126,7 +127,11 @@ step_circuit: FC,

z_0: Vec<C1::ScalarField>, // initial state ) -> Result<Self, Error>; - fn prove_step(&mut self, external_inputs: Vec<C1::ScalarField>) -> Result<(), Error>; + fn prove_step( + &mut self, + rng: impl RngCore, + external_inputs: Vec<C1::ScalarField>, + ) -> Result<(), Error>; // returns the state at the current step fn state(&self) -> Vec<C1::ScalarField>;

@@ -136,9 +141,9 @@ // (running_instance, incoming_instance, cyclefold_instance)

fn instances( &self, ) -> ( - Self::CommittedInstanceWithWitness, - Self::CommittedInstanceWithWitness, - Self::CFCommittedInstanceWithWitness, + Self::RunningInstance, + Self::IncomingInstance, + Self::CFInstance, ); fn verify(

@@ -147,9 +152,9 @@ z_0: Vec<C1::ScalarField>, // initial state

z_i: Vec<C1::ScalarField>, // last state // number of steps between the initial state and the last state num_steps: C1::ScalarField, - running_instance: Self::CommittedInstanceWithWitness, - incoming_instance: Self::CommittedInstanceWithWitness, - cyclefold_instance: Self::CFCommittedInstanceWithWitness, + running_instance: Self::RunningInstance, + incoming_instance: Self::IncomingInstance, + cyclefold_instance: Self::CFInstance, ) -> Result<(), Error>; }

@@ -162,16 +167,22 @@ > where

C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>, C2::BaseField: PrimeField, { + type PreprocessorParam: Debug; type ProverParam: Clone; type Proof; type VerifierParam; type PublicInput: Debug; - type CommittedInstanceWithWitness: Debug; type CommittedInstance: Clone + Debug; + fn preprocess( + rng: impl RngCore + CryptoRng, + prep_param: &Self::PreprocessorParam, + fs: FS, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error>; + fn prove( - pp: Self::ProverParam, rng: impl RngCore + CryptoRng, + pp: Self::ProverParam, folding_scheme: FS, ) -> Result<Self::Proof, Error>;
M folding-schemes/src/utils/mod.rs → folding-schemes/src/utils/mod.rs

@@ -1,4 +1,6 @@

+use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::PrimeField; +use ark_std::Zero; pub mod gadgets; pub mod hypercube;

@@ -21,3 +23,12 @@ c[i] = c[i - 1] * x;

} c } + +/// returns the coordinates of a commitment point. This is compatible with the arkworks +/// GC.to_constraint_field()[..2] +pub fn get_cm_coordinates<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> { + let zero = (&C::BaseField::zero(), &C::BaseField::zero()); + let cm = cm.into_affine(); + let (cm_x, cm_y) = cm.xy().unwrap_or(zero); + vec![*cm_x, *cm_y] +}
M solidity-verifiers/src/verifiers/nova_cyclefold.rs → solidity-verifiers/src/verifiers/nova_cyclefold.rs

@@ -85,15 +85,16 @@ }

// implements From assuming that the 'batchCheck' method from the KZG10 template will not be used // in the NovaCycleFoldDecider verifier contract -impl From<(VerifyingKey<Bn254>, VerifierKey<Bn254>, usize)> for NovaCycleFoldVerifierKey { - fn from(value: (VerifyingKey<Bn254>, VerifierKey<Bn254>, usize)) -> Self { - let g16_vk = Groth16VerifierKey::from(value.0); +impl From<((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)> for NovaCycleFoldVerifierKey { + fn from(value: ((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)) -> Self { + let decider_vp = value.0; + let g16_vk = Groth16VerifierKey::from(decider_vp.0); // pass `Vec::new()` since batchCheck will not be used - let kzg_vk = KZG10VerifierKey::from((value.1, Vec::new())); + let kzg_vk = KZG10VerifierKey::from((decider_vp.1, Vec::new())); Self { g16_vk, kzg_vk, - z_len: value.2, + z_len: value.1, } } }

@@ -258,7 +259,7 @@ fn nova_cyclefold_vk_serde_roundtrip() {

let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN); let mut bytes = vec![]; - let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, 1)); + let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1)); nova_cyclefold_vk .serialize_protocol_verifier_key(&mut bytes)

@@ -272,7 +273,7 @@

#[test] fn nova_cyclefold_decider_template_renders() { let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN); - let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, 1)); + let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1)); let decider_solidity_code = HeaderInclusion::<NovaCycleFoldDecider>::builder() .template(nova_cyclefold_vk)

@@ -296,8 +297,8 @@ KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();

let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap(); let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> { poseidon_config: poseidon_config.clone(), - cs_params: kzg_pk.clone(), - cf_cs_params: cf_pedersen_params, + cs_pp: kzg_pk.clone(), + cf_cs_pp: cf_pedersen_params, }; (fs_prover_params, kzg_vk) }

@@ -371,22 +372,22 @@ NOVA_FCircuit<FC>,

>; let f_circuit = FC::new(()).unwrap(); - let nova_cyclefold_vk = - NovaCycleFoldVerifierKey::from((g16_vk.clone(), kzg_vk.clone(), f_circuit.state_len())); + let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(( + (g16_vk.clone(), kzg_vk.clone()), + f_circuit.state_len(), + )); + + let mut rng = rand::rngs::OsRng; let mut nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0).unwrap(); for _ in 0..n_steps { - nova.prove_step(vec![]).unwrap(); + nova.prove_step(&mut rng, vec![]).unwrap(); } - let rng = rand::rngs::OsRng; let start = Instant::now(); - let proof = DECIDERETH_FCircuit::prove( - (g16_pk, fs_prover_params.cs_params.clone()), - rng, - nova.clone(), - ) - .unwrap(); + let proof = + DECIDERETH_FCircuit::prove(rng, (g16_pk, fs_prover_params.cs_pp.clone()), nova.clone()) + .unwrap(); println!("generated Decider proof: {:?}", start.elapsed()); let verified = DECIDERETH_FCircuit::<FC>::verify(