From b5667968f48feacbccb1c3f5407f00f7e4892a4a Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 4 Jul 2024 11:14:31 +0200 Subject: [PATCH] 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) --- examples/circom_full_flow.rs | 46 +- examples/external_inputs.rs | 40 +- examples/full_flow.rs | 43 +- examples/multi_inputs.rs | 26 +- examples/sha256.rs | 28 +- examples/utils.rs | 99 --- folding-schemes/src/commitment/ipa.rs | 7 + folding-schemes/src/commitment/kzg.rs | 7 + folding-schemes/src/commitment/mod.rs | 2 + folding-schemes/src/commitment/pedersen.rs | 7 + .../src/folding/circuits/cyclefold.rs | 2 +- folding-schemes/src/folding/hypernova/cccs.rs | 29 +- .../src/folding/hypernova/circuits.rs | 113 ++-- .../src/folding/hypernova/lcccs.rs | 39 +- folding-schemes/src/folding/hypernova/mod.rs | 587 +++++++++++++++++- .../src/folding/hypernova/nimfs.rs | 75 ++- .../src/folding/hypernova/utils.rs | 8 +- .../src/folding/nova/decider_eth.rs | 61 +- .../src/folding/nova/decider_eth_circuit.rs | 34 +- folding-schemes/src/folding/nova/mod.rs | 206 +++--- folding-schemes/src/folding/nova/serialize.rs | 53 +- folding-schemes/src/folding/nova/traits.rs | 2 + folding-schemes/src/lib.rs | 41 +- folding-schemes/src/utils/mod.rs | 11 + .../src/verifiers/nova_cyclefold.rs | 39 +- 25 files changed, 1142 insertions(+), 463 deletions(-) delete mode 100644 examples/utils.rs diff --git a/examples/circom_full_flow.rs b/examples/circom_full_flow.rs index 71d0ed9..65d38e8 100644 --- a/examples/circom_full_flow.rs +++ b/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::{ @@ -33,9 +34,6 @@ use solidity_verifiers::{ NovaCycleFoldVerifierKey, }; -mod utils; -use utils::init_ivc_and_decider_params; - fn main() { // set the initial state let z_0 = vec![Fr::from(3_u32)]; @@ -66,12 +64,8 @@ fn main() { let f_circuit_params = (r1cs_path, wasm_path, 1, 2); let f_circuit = CircomFCircuit::::new(f_circuit_params).unwrap(); - let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = - init_ivc_and_decider_params::>(f_circuit.clone()); - - pub type NOVA = - Nova, KZG<'static, Bn254>, Pedersen>; - pub type DECIDERETH_FCircuit = DeciderEth< + pub type N = Nova, KZG<'static, Bn254>, Pedersen>; + pub type D = DeciderEth< G1, GVar, G2, @@ -80,30 +74,36 @@ fn main() { KZG<'static, Bn254>, Pedersen, Groth16, - NOVA, + N, >; + let poseidon_config = poseidon_canonical_config::(); + 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 @@ fn main() { .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); diff --git a/examples/external_inputs.rs b/examples/external_inputs.rs index a923bf1..5e7b57e 100644 --- a/examples/external_inputs.rs +++ b/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 @@ use utils::init_nova_ivc_params; /// 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 +pub struct ExternalInputsCircuit where F: Absorb, { _f: PhantomData, poseidon_config: PoseidonConfig, } -impl FCircuit for ExternalInputsCircuits +impl FCircuit for ExternalInputsCircuit where F: Absorb, { @@ -128,14 +126,14 @@ pub mod tests { 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::(); let cs = ConstraintSystem::::new_ref(); - let circuit = ExternalInputsCircuits::::new(poseidon_config).unwrap(); + let circuit = ExternalInputsCircuit::::new(poseidon_config).unwrap(); let z_i = vec![Fr::from(1_u32)]; let external_inputs = vec![Fr::from(3_u32)]; @@ -170,33 +168,35 @@ fn main() { assert_eq!(external_inputs.len(), num_steps); let poseidon_config = poseidon_canonical_config::(); - let F_circuit = ExternalInputsCircuits::::new(poseidon_config).unwrap(); - - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = - init_nova_ivc_params::>(F_circuit.clone()); + let F_circuit = ExternalInputsCircuit::::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, + ExternalInputsCircuit, KZG<'static, Bn254>, Pedersen, >; + 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 @@ fn main() { 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), diff --git a/examples/full_flow.rs b/examples/full_flow.rs index 6cf154f..e592d3d 100644 --- a/examples/full_flow.rs +++ b/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 @@ fn main() { let z_0 = vec![Fr::from(3_u32)]; let f_circuit = CubicFCircuit::::new(()).unwrap(); - let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = - init_ivc_and_decider_params::>(f_circuit); - pub type NOVA = Nova, KZG<'static, Bn254>, Pedersen>; - pub type DECIDERETH_FCircuit = DeciderEth< + pub type N = Nova, KZG<'static, Bn254>, Pedersen>; + pub type D = DeciderEth< G1, GVar, G2, @@ -95,30 +91,35 @@ fn main() { KZG<'static, Bn254>, Pedersen, Groth16, - NOVA, + N, >; + let poseidon_config = poseidon_canonical_config::(); + 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 @@ fn main() { .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); diff --git a/examples/multi_inputs.rs b/examples/multi_inputs.rs index bb15082..5856459 100644 --- a/examples/multi_inputs.rs +++ b/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 @@ fn main() { let F_circuit = MultiInputsFCircuit::::new(()).unwrap(); - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = - init_nova_ivc_params::>(F_circuit); + let poseidon_config = poseidon_canonical_config::(); + 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 @@ fn main() { Pedersen, >; + 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), diff --git a/examples/sha256.rs b/examples/sha256.rs index 714eb01..03c3c67 100644 --- a/examples/sha256.rs +++ b/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 @@ fn main() { let F_circuit = Sha256FCircuit::::new(()).unwrap(); - println!("Prepare Nova ProverParams & VerifierParams"); - let (prover_params, verifier_params, _) = init_nova_ivc_params::>(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 @@ fn main() { Pedersen, >; - println!("Initialize FoldingScheme"); - let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap(); + let poseidon_config = poseidon_canonical_config::(); + 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), diff --git a/examples/utils.rs b/examples/utils.rs deleted file mode 100644 index 35ce55b..0000000 --- a/examples/utils.rs +++ /dev/null @@ -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>( - F_circuit: FC, -) -> ( - ProverParams, Pedersen>, - VerifierParams, - KZGVerifierKey, -) { - let mut rng = ark_std::test_rng(); - let poseidon_config = poseidon_canonical_config::(); - - // get the CM & CF_CM len - let (r1cs, cf_r1cs) = get_r1cs::(&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::::setup(&mut rng, cf_len).unwrap(); - let (kzg_pk, kzg_vk): (KZGProverKey, KZGVerifierKey) = - KZG::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); - - let fs_prover_params = ProverParams::, Pedersen> { - poseidon_config: poseidon_config.clone(), - cs_params: kzg_pk.clone(), - cf_cs_params: cf_pedersen_params, - }; - let fs_verifier_params = VerifierParams:: { - 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>( - f_circuit: FC, -) -> ( - ProverParams, Pedersen>, - KZGVerifierKey, - ProvingKey, - G16VerifierKey, -) { - let mut rng = rand::rngs::OsRng; - let start = Instant::now(); - let (fs_prover_params, _, kzg_vk) = init_nova_ivc_params::(f_circuit.clone()); - println!("generated Nova folding params: {:?}", start.elapsed()); - - pub type NOVA = Nova, Pedersen>; - 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::, Pedersen>::from_nova::( - nova.clone(), - ) - .unwrap(); - let start = Instant::now(); - let (g16_pk, g16_vk) = - Groth16::::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() {} diff --git a/folding-schemes/src/commitment/ipa.rs b/folding-schemes/src/commitment/ipa.rs index 18060ff..5645d43 100644 --- a/folding-schemes/src/commitment/ipa.rs +++ b/folding-schemes/src/commitment/ipa.rs @@ -54,6 +54,13 @@ impl CommitmentScheme for IPA { type ProverChallenge = (C::ScalarField, C, Vec); type Challenge = (C::ScalarField, C, Vec); + fn is_hiding() -> bool { + if H { + return true; + } + false + } + fn setup( mut rng: impl RngCore, len: usize, diff --git a/folding-schemes/src/commitment/kzg.rs b/folding-schemes/src/commitment/kzg.rs index b8254b2..47a4cbd 100644 --- a/folding-schemes/src/commitment/kzg.rs +++ b/folding-schemes/src/commitment/kzg.rs @@ -93,6 +93,13 @@ where 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( diff --git a/folding-schemes/src/commitment/mod.rs b/folding-schemes/src/commitment/mod.rs index 7b1add7..9fa9a13 100644 --- a/folding-schemes/src/commitment/mod.rs +++ b/folding-schemes/src/commitment/mod.rs @@ -18,6 +18,8 @@ pub trait CommitmentScheme: Clone + Debug type ProverChallenge: Clone + Debug; type Challenge: Clone + Debug; + fn is_hiding() -> bool; + fn setup( rng: impl RngCore, len: usize, diff --git a/folding-schemes/src/commitment/pedersen.rs b/folding-schemes/src/commitment/pedersen.rs index 82d07a0..a906f21 100644 --- a/folding-schemes/src/commitment/pedersen.rs +++ b/folding-schemes/src/commitment/pedersen.rs @@ -38,6 +38,13 @@ impl CommitmentScheme for Pedersen { type ProverChallenge = (C::ScalarField, Vec, C, C::ScalarField); type Challenge = C::ScalarField; + fn is_hiding() -> bool { + if H { + return true; + } + false + } + fn setup( mut rng: impl RngCore, len: usize, diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index 184f018..920e24d 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -460,9 +460,9 @@ pub mod tests { 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() { diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index 1494049..0f4c11c 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/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 struct CCCS { } impl CCS { - pub fn to_cccs( + pub fn to_cccs>( &self, rng: &mut R, - pedersen_params: &PedersenParams, + cs_params: &CS::ProverParams, z: &[C::ScalarField], ) -> Result<(CCCS, Witness), Error> where @@ -38,8 +35,14 @@ impl CCS { C: CurveGroup, { let w: Vec = z[(1 + self.l)..].to_vec(); - let r_w = C::ScalarField::rand(rng); - let C = Pedersen::::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:: { @@ -95,19 +98,13 @@ impl CCCS { } } - /// 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, ccs: &CCS, w: &Witness, ) -> 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::::commit(pedersen_params, &w.w, &w.r_w)? { - return Err(Error::NotSatisfied); - } - // check CCCS relation let z: Vec = [vec![C::ScalarField::one()], self.x.clone(), w.w.to_vec()].concat(); diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 80ac548..36116f8 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -596,7 +596,7 @@ where /// 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, CCS), Error> { let cs = ConstraintSystem::::new_ref(); @@ -720,8 +720,14 @@ where 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::>::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 @@ mod tests { 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 @@ mod tests { // 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>(&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>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(inst); } @@ -950,7 +959,9 @@ mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); } @@ -958,7 +969,9 @@ mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); } @@ -996,9 +1009,7 @@ mod tests { 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::::new_ref(); @@ -1042,7 +1053,9 @@ mod tests { 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>(&mut rng, &pedersen_params, &z1) + .unwrap(); let h = lcccs .clone() .hash(&poseidon_config, i, z_0.clone(), z_i.clone()) @@ -1104,11 +1117,11 @@ mod tests { let mut z_i = z_0.clone(); // prepare the dummy instances - let W_dummy = Witness::::new(vec![Fr::zero(); ccs.n - ccs.l - 1]); + let W_dummy = Witness::::dummy(&ccs); let U_dummy = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); let w_dummy = W_dummy.clone(); let u_dummy = CCCS::::dummy(ccs.l); - let (cf_w_dummy, cf_u_dummy): (NovaWitness, CommittedInstance) = + let (cf_W_dummy, cf_U_dummy): (NovaWitness, CommittedInstance) = cf_r1cs.dummy_instance(); // set the initial dummy instances @@ -1116,8 +1129,8 @@ mod tests { 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 @@ mod tests { let mut iFr = Fr::zero(); for i in 0..n_steps { let start = Instant::now(); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config.clone()); - let (nimfs_proof, U_i1, W_i1, rho_bits) = - NIMFS::>::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::::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 @@ mod tests { _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 @@ mod tests { 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 @@ mod tests { cf_cmT: None, }; } else { + let mut transcript_p: PoseidonTranscript = + PoseidonTranscript::::new(&poseidon_config.clone()); + let (rho_bits, nimfs_proof); + (nimfs_proof, U_i1, W_i1, rho_bits) = + NIMFS::>::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 @@ mod tests { 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>(&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 @@ mod tests { 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 diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index f17bf9c..76b169a 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/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 struct LCCCS { } impl CCS { - pub fn to_lcccs( + pub fn to_lcccs>( &self, rng: &mut R, - pedersen_params: &PedersenParams, + cs_params: &CS::ProverParams, z: &[C::ScalarField], ) -> Result<(LCCCS, Witness), Error> where @@ -47,8 +44,13 @@ impl CCS { C: CurveGroup, { let w: Vec = z[(1 + self.l)..].to_vec(); - let r_w = C::ScalarField::rand(rng); - let C = Pedersen::::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 = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect(); @@ -91,19 +93,13 @@ impl LCCCS { } } - /// 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, ccs: &CCS, w: &Witness, ) -> 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::::commit(pedersen_params, &w.w, &w.r_w)? { - return Err(Error::NotSatisfied); - } - // check CCS relation let z: Vec = [vec![self.u], self.x.clone(), w.w.to_vec()].concat(); @@ -172,6 +168,7 @@ pub mod tests { 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 @@ pub mod tests { 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::::rand(&mut rng, n_rows, n_cols); let ccs = CCS::from_r1cs(r1cs); let z: Vec = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect(); let (pedersen_params, _) = Pedersen::::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>(&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 @@ pub mod tests { let (pedersen_params, _) = Pedersen::::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>(&mut rng, &pedersen_params, &z) + .unwrap(); // with our test vector coming from R1CS, v should have length 3 assert_eq!(lcccs.v.len(), 3); diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index 5a79969..56aae65 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/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 @@ impl Witness { Witness::::new(vec![F::zero(); ccs.n - ccs.l - 1]) } } + +#[derive(Debug, Clone)] +pub struct PreprocessorParam +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + pub poseidon_config: PoseidonConfig, + pub F: FC, + // cs_params & cf_cs_params: if not provided, will be generated at the preprocess method + pub cs_params: Option, + pub cf_cs_params: Option, +} + +#[derive(Debug, Clone)] +pub struct ProverParams +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + pub poseidon_config: PoseidonConfig, + 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>, +} + +#[derive(Debug, Clone)] +pub struct VerifierParams< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +> { + pub poseidon_config: PoseidonConfig, + pub ccs: CCS, + pub cf_r1cs: R1CS, + 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 +where + C1: CurveGroup, + GC1: CurveVar> + ToConstraintFieldGadget>, + C2: CurveGroup, + GC2: CurveVar>, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + + /// CCS of the Augmented Function circuit + pub ccs: CCS, + /// R1CS of the CycleFold circuit + pub cf_r1cs: R1CS, + pub poseidon_config: PoseidonConfig, + /// 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, + /// current i-th state + pub z_i: Vec, + /// HyperNova instances + pub W_i: Witness, + pub U_i: LCCCS, + pub w_i: Witness, + pub u_i: CCCS, + + /// CycleFold running instance + pub cf_W_i: NovaWitness, + pub cf_U_i: CommittedInstance, +} + +impl FoldingScheme + for HyperNova +where + C1: CurveGroup, + GC1: CurveVar> + ToConstraintFieldGadget>, + C2: CurveGroup, + GC2: CurveVar> + ToConstraintFieldGadget>, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, + ::BaseField: PrimeField, + ::BaseField: PrimeField, + ::ScalarField: Absorb, + ::ScalarField: Absorb, + C1: CurveGroup, + for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, + for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, +{ + type PreprocessorParam = PreprocessorParam; + type ProverParam = ProverParams; + type VerifierParam = VerifierParams; + type RunningInstance = (LCCCS, Witness); + type IncomingInstance = (CCCS, Witness); + type CFInstance = (CommittedInstance, NovaWitness); + + fn preprocess( + mut rng: impl RngCore, + prep_param: &Self::PreprocessorParam, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + let augmented_f_circuit = AugmentedFCircuit::::empty( + &prep_param.poseidon_config, + prep_param.F.clone(), + None, + )?; + let ccs = augmented_f_circuit.ccs.clone(); + + let cf_circuit = CycleFoldCircuit::::empty(); + let cf_r1cs = get_r1cs_from_cs::(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:: { + 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:: { + 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) -> Result { + // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS + // and R1CS respectively + let augmented_f_circuit = AugmentedFCircuit::::empty( + &pp.poseidon_config, + F.clone(), + pp.ccs.clone(), + )?; + let ccs = augmented_f_circuit.ccs.clone(); + + let cf_circuit = CycleFoldCircuit::::empty(); + let cf_r1cs = get_r1cs_from_cs::(cf_circuit)?; + + // setup the dummy instances + let W_dummy = Witness::::dummy(&ccs); + let U_dummy = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); + let w_dummy = W_dummy.clone(); + let mut u_dummy = CCCS::::dummy(ccs.l); + let (cf_W_dummy, cf_U_dummy): (NovaWitness, CommittedInstance) = + 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, + ) -> Result<(), Error> { + let augmented_f_circuit: AugmentedFCircuit; + + 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::::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:: { + _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 = + PoseidonTranscript::::new(&self.poseidon_config); + let (rho_bits, nimfs_proof); + (nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::>::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:: { + _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::( + &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:: { + _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::(&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 { + 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, // initial state + z_i: Vec, // 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::(); + + let F_circuit = CubicFCircuit::::new(()).unwrap(); + + // run the test using Pedersen commitments on both sides of the curve cycle + test_ivc_opt::, Pedersen>( + 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::, Pedersen>(poseidon_config, F_circuit); + } + + // test_ivc allowing to choose the CommitmentSchemes + fn test_ivc_opt, CS2: CommitmentScheme>( + poseidon_config: PoseidonConfig, + F_circuit: CubicFCircuit, + ) { + let mut rng = ark_std::test_rng(); + + type HN = + HyperNova, CS1, CS2>; + + let prep_param = PreprocessorParam::, 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(); + } +} diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index 805d34e..f919c5d 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -32,19 +32,21 @@ pub struct NIMFSProof { impl NIMFSProof { pub fn dummy(ccs: &CCS, 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:: { sc_proof: SumCheckProof:: { - 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 @@ pub mod tests { let (pedersen_params, _) = Pedersen::::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>(&mut rng, &pedersen_params, &z1) + .unwrap(); + let (cccs, w2) = ccs + .to_cccs::<_, Projective, Pedersen>(&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 @@ pub mod tests { NIMFS::>::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 @@ pub mod tests { 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>(&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>(&mut rng, &pedersen_params, &z_2) + .unwrap(); // Prover's transcript let poseidon_config = poseidon_canonical_config::(); @@ -513,9 +521,7 @@ pub mod tests { 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 @@ pub mod tests { // 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>(&mut rng, &pedersen_params, &z_1) + .unwrap(); let poseidon_config = poseidon_canonical_config::(); @@ -548,7 +555,9 @@ pub mod tests { // 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>(&mut rng, &pedersen_params, &z_2) + .unwrap(); // run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = @@ -574,9 +583,7 @@ pub mod tests { 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 @@ pub mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); } @@ -620,7 +629,9 @@ pub mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); } @@ -660,9 +671,7 @@ pub mod tests { 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 @@ pub mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w); } @@ -718,7 +729,9 @@ pub mod tests { 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>(&mut rng, &pedersen_params, z_i) + .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w); } @@ -748,9 +761,7 @@ pub mod tests { 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(); } } } diff --git a/folding-schemes/src/folding/hypernova/utils.rs b/folding-schemes/src/folding/hypernova/utils.rs index 6951d37..d4cf3a6 100644 --- a/folding-schemes/src/folding/hypernova/utils.rs +++ b/folding-schemes/src/folding/hypernova/utils.rs @@ -239,7 +239,9 @@ pub mod tests { // Initialize a multifolding object let (pedersen_params, _) = Pedersen::::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>(&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 @@ pub mod tests { // Initialize a multifolding object let (pedersen_params, _) = Pedersen::::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>(&mut rng, &pedersen_params, &z1) + .unwrap(); // Compute g(x) with that r_x let g = compute_g::( diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 3c1bf15..2e669bb 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -62,12 +62,13 @@ where GC1: CurveVar> + ToConstraintFieldGadget>, GC2: CurveVar> + ToConstraintFieldGadget>, FC: FCircuit, + // CS1 is a KZG commitment, where challenge is C1::Fr elem CS1: CommitmentScheme< C1, ProverChallenge = C1::ScalarField, Challenge = C1::ScalarField, Proof = KZGProof, - >, // 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>, S: SNARK, @@ -77,20 +78,52 @@ where ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, + 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: From, + crate::folding::nova::ProverParams: + From<>::ProverParam>, + crate::folding::nova::VerifierParams: + From<>::VerifierParam>, { + type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof; type VerifierParam = (S::VerifyingKey, CS1::VerifierParams); type PublicInput = Vec; - type CommittedInstanceWithWitness = (); type CommittedInstance = CommittedInstance; + fn preprocess( + mut rng: impl RngCore + CryptoRng, + prep_param: &Self::PreprocessorParam, + fs: FS, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + let circuit = + DeciderEthCircuit::::from_nova::(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: + as FoldingScheme>::ProverParam = + prep_param.0.clone().into() + ; + #[allow(clippy::type_complexity)] + let nova_vp: + as FoldingScheme>::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 { let (snark_pk, cs_pk): (S::ProvingKey, CS1::ProverParams) = pp; @@ -281,13 +314,13 @@ fn point2_to_eth_format(p: ark_bn254::G2Affine) -> Result, Error> { #[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 @@ pub mod tests { #[test] fn test_decider() { // use Nova as FoldingScheme - type NOVA = Nova< + type N = Nova< Projective, GVar, Projective2, @@ -306,7 +339,7 @@ pub mod tests { KZG<'static, Bn254>, Pedersen, >; - type DECIDER = Decider< + type D = Decider< Projective, GVar, Projective2, @@ -315,7 +348,7 @@ pub mod tests { KZG<'static, Bn254>, Pedersen, Groth16, // 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 @@ pub mod tests { let prover_params = ProverParams::, Pedersen> { 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 @@ pub mod tests { // 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(); diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 9ec17cc..de58e63 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -264,7 +264,7 @@ where ) -> Result { // compute the U_{i+1}, W_{i+1} let (T, cmT) = NIFS::::compute_cmT( - &nova.cs_params, + &nova.cs_pp, &nova.r1cs.clone(), &nova.w_i.clone(), &nova.u_i.clone(), @@ -315,7 +315,7 @@ where 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 @@ where // 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::::dummy(CF_IO_LEN); @@ -597,7 +597,6 @@ where #[cfg(test)] pub mod tests { - use super::*; use ark_crypto_primitives::crh::{ sha256::{ constraints::{Sha256Gadget, UnitVar}, @@ -611,15 +610,15 @@ pub mod tests { 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() { let r1cs: R1CS = get_test_r1cs(); @@ -786,11 +785,11 @@ pub mod tests { let prover_params = ProverParams::, Pedersen> { 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 @@ pub mod tests { >; // 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:: { + let verifier_params = VerifierParams::< + Projective, + Projective2, + Pedersen, + Pedersen, + > { 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, diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 541b2e3..bcc1564 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -10,6 +10,7 @@ use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldG 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 @@ use crate::folding::circuits::{ 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; @@ -186,6 +187,44 @@ where } } +#[derive(Debug, Clone)] +pub struct PreprocessorParam +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + pub poseidon_config: PoseidonConfig, + pub F: FC, + // cs params if not provided, will be generated at the preprocess method + pub cs_pp: Option, + pub cs_vp: Option, + pub cf_cs_pp: Option, + pub cf_cs_vp: Option, +} + +impl PreprocessorParam +where + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + pub fn new(poseidon_config: PoseidonConfig, 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 where @@ -195,15 +234,23 @@ where CS2: CommitmentScheme, { pub poseidon_config: PoseidonConfig, - 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 { +pub struct VerifierParams +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ pub poseidon_config: PoseidonConfig, pub r1cs: R1CS, pub cf_r1cs: R1CS, + 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 @@ where pub cf_r1cs: R1CS, pub poseidon_config: PoseidonConfig, /// 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 @@ where for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { - type PreprocessorParam = (Self::ProverParam, FC); + type PreprocessorParam = PreprocessorParam; type ProverParam = ProverParams; - type VerifierParam = VerifierParams; - type CommittedInstanceWithWitness = (CommittedInstance, Witness); - type CFCommittedInstanceWithWitness = (CommittedInstance, Witness); + type VerifierParam = VerifierParams; + type RunningInstance = (CommittedInstance, Witness); + type IncomingInstance = (CommittedInstance, Witness); + type CFInstance = (CommittedInstance, Witness); 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::(&prover_params.poseidon_config, F_circuit.clone())?; + get_r1cs::(&prep_param.poseidon_config, prep_param.F.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:: { - poseidon_config: prover_params.poseidon_config.clone(), + let prover_params = ProverParams:: { + poseidon_config: prep_param.poseidon_config.clone(), + cs_pp: cs_pp.clone(), + cf_cs_pp: cf_cs_pp.clone(), + }; + let verifier_params = VerifierParams:: { + 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 @@ where 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 @@ where } /// Implements IVC.P of Nova+CycleFold - fn prove_step(&mut self, external_inputs: Vec) -> Result<(), Error> { + fn prove_step( + &mut self, + _rng: impl RngCore, + external_inputs: Vec, + ) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit; if self.z_i.len() != self.F.state_len() { @@ -535,7 +612,7 @@ where self.i += C1::ScalarField::one(); self.z_i = z_i1; self.w_i = Witness::::new(w_i1, self.r1cs.A.n_rows); - self.u_i = self.w_i.commit::(&self.cs_params, x_i1)?; + self.u_i = self.w_i.commit::(&self.cs_pp, x_i1)?; self.W_i = W_i1; self.U_i = U_i1; @@ -552,12 +629,13 @@ where fn state(&self) -> Vec { 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 @@ where ) } - /// 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, // initial state z_i: Vec, // 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 @@ where // computes T and cmT for the AugmentedFCircuit fn compute_cmT(&self) -> Result<(Vec, C1), Error> { NIFS::::compute_cmT( - &self.cs_params, + &self.cs_pp, &self.r1cs, &self.w_i, &self.u_i, @@ -680,7 +766,7 @@ where fold_cyclefold_circuit::( &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 @@ where 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(cm: &C) -> Vec { - 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 @@ pub mod tests { /// AugmentedFCircuit #[test] fn test_ivc() { - let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); let F_circuit = CubicFCircuit::::new(()).unwrap(); - let (cs_len, cf_cs_len) = - get_cs_params_len::>( - &poseidon_config, - F_circuit, - ) - .unwrap(); - let (kzg_pk, _): (KZGProverKey, KZGVerifierKey) = - KZG::::setup(&mut rng, cs_len).unwrap(); - let (pedersen_params, _) = Pedersen::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); - // run the test using Pedersen commitments on both sides of the curve cycle test_ivc_opt::, Pedersen>( 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::, Pedersen>( - poseidon_config, - kzg_pk, - cf_pedersen_params, - F_circuit, - ); + test_ivc_opt::, Pedersen>(poseidon_config, F_circuit); } // test_ivc allowing to choose the CommitmentSchemes fn test_ivc_opt, CS2: CommitmentScheme>( poseidon_config: PoseidonConfig, - cs_params: CS1::ProverParams, - cf_cs_params: CS2::ProverParams, F_circuit: CubicFCircuit, ) { - type NOVA = - Nova, CS1, CS2>; + let mut rng = ark_std::test_rng(); + type N = Nova, CS1, CS2>; - let prover_params = ProverParams:: { - poseidon_config: poseidon_config.clone(), - cs_params, - cf_cs_params, + let prep_param = PreprocessorParam::, 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:: { - poseidon_config, - r1cs: nova.clone().r1cs, - cf_r1cs: nova.clone().cf_r1cs, - }; let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); - NOVA::::verify( + N::::verify( verifier_params, z_0, nova.z_i, diff --git a/folding-schemes/src/folding/nova/serialize.rs b/folding-schemes/src/folding/nova/serialize.rs index 170f29f..d4682fd 100644 --- a/folding-schemes/src/folding/nova/serialize.rs +++ b/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; @@ -17,6 +10,14 @@ 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 CanonicalSerialize for Nova where C1: CurveGroup, @@ -150,8 +151,8 @@ where _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 @@ where #[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 @@ pub mod tests { 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 @@ pub mod tests { let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); // Initialize nova and make multiple `prove_step()` - type NOVA = - Nova, CS1, CS2>; + type N = Nova< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit, + KZG<'static, Bn254>, + Pedersen, + >; let prover_params = ProverParams::, Pedersen> { 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 @@ pub mod tests { 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); diff --git a/folding-schemes/src/folding/nova/traits.rs b/folding-schemes/src/folding/nova/traits.rs index 6b50da8..2ff3f83 100644 --- a/folding-schemes/src/folding/nova/traits.rs +++ b/folding-schemes/src/folding/nova/traits.rs @@ -39,6 +39,7 @@ where (w_dummy, u_dummy) } + // notice that this method does not check the commitment correctness fn check_instance_relation( &self, W: &Witness, @@ -52,6 +53,7 @@ where self.check_relation(&Z) } + // notice that this method does not check the commitment correctness fn check_relaxed_instance_relation( &self, W: &Witness, diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index 903e7b1..cb70231 100644 --- a/folding-schemes/src/lib.rs +++ b/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 @@ where C2::BaseField: PrimeField, FC: FCircuit, { - 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 @@ where z_0: Vec, // initial state ) -> Result; - fn prove_step(&mut self, external_inputs: Vec) -> Result<(), Error>; + fn prove_step( + &mut self, + rng: impl RngCore, + external_inputs: Vec, + ) -> Result<(), Error>; // returns the state at the current step fn state(&self) -> Vec; @@ -136,9 +141,9 @@ where fn instances( &self, ) -> ( - Self::CommittedInstanceWithWitness, - Self::CommittedInstanceWithWitness, - Self::CFCommittedInstanceWithWitness, + Self::RunningInstance, + Self::IncomingInstance, + Self::CFInstance, ); fn verify( @@ -147,9 +152,9 @@ where z_i: Vec, // 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 @@ pub trait Decider< C1: CurveGroup, 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; diff --git a/folding-schemes/src/utils/mod.rs b/folding-schemes/src/utils/mod.rs index 5361804..72aff2d 100644 --- a/folding-schemes/src/utils/mod.rs +++ b/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 @@ pub fn powers_of(x: F, n: usize) -> Vec { } c } + +/// returns the coordinates of a commitment point. This is compatible with the arkworks +/// GC.to_constraint_field()[..2] +pub fn get_cm_coordinates(cm: &C) -> Vec { + 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] +} diff --git a/solidity-verifiers/src/verifiers/nova_cyclefold.rs b/solidity-verifiers/src/verifiers/nova_cyclefold.rs index 55c9499..236a15d 100644 --- a/solidity-verifiers/src/verifiers/nova_cyclefold.rs +++ b/solidity-verifiers/src/verifiers/nova_cyclefold.rs @@ -85,15 +85,16 @@ impl From<(Groth16VerifierKey, KZG10VerifierKey, usize)> for NovaCycleFoldVerifi // implements From assuming that the 'batchCheck' method from the KZG10 template will not be used // in the NovaCycleFoldDecider verifier contract -impl From<(VerifyingKey, VerifierKey, usize)> for NovaCycleFoldVerifierKey { - fn from(value: (VerifyingKey, VerifierKey, usize)) -> Self { - let g16_vk = Groth16VerifierKey::from(value.0); +impl From<((VerifyingKey, VerifierKey), usize)> for NovaCycleFoldVerifierKey { + fn from(value: ((VerifyingKey, VerifierKey), 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 @@ mod tests { 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 @@ mod tests { #[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::::builder() .template(nova_cyclefold_vk) @@ -296,8 +297,8 @@ mod tests { let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); let fs_prover_params = ProverParams::, Pedersen> { 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 @@ mod tests { >; 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::::verify(