//! This library implements core components of Nova. #![allow(non_snake_case)] #![allow(clippy::type_complexity)] #![deny(missing_docs)] pub mod bellperson; mod circuit; mod commitments; mod constants; pub mod errors; pub mod gadgets; pub mod nifs; pub mod pasta; mod poseidon; pub mod r1cs; pub mod traits; use crate::bellperson::{ r1cs::{NovaShape, NovaWitness}, shape_cs::ShapeCS, solver::SatisfyingAssignment, }; use crate::poseidon::ROConstantsCircuit; // TODO: make this a trait so we can use it without the concrete implementation use ::bellperson::{Circuit, ConstraintSystem}; use circuit::{NIFSVerifierCircuit, NIFSVerifierCircuitInputs, NIFSVerifierCircuitParams}; use constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use core::marker::PhantomData; use errors::NovaError; use ff::Field; use r1cs::{ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }; use traits::{Group, HashFuncConstantsTrait, HashFuncTrait, StepCircuit}; type ROConstants = <::HashFunc as HashFuncTrait<::Base, ::Scalar>>::Constants; /// A type that holds public parameters of Nova pub struct PublicParams where G1: Group::Scalar>, G2: Group::Scalar>, C1: StepCircuit + Clone, C2: StepCircuit + Clone, { _ro_consts_primary: ROConstants, ro_consts_circuit_primary: ROConstantsCircuit<::Base>, r1cs_gens_primary: R1CSGens, r1cs_shape_primary: R1CSShape, _ro_consts_secondary: ROConstants, ro_consts_circuit_secondary: ROConstantsCircuit<::Base>, r1cs_gens_secondary: R1CSGens, r1cs_shape_secondary: R1CSShape, c_primary: C1, c_secondary: C2, params_primary: NIFSVerifierCircuitParams, params_secondary: NIFSVerifierCircuitParams, } impl PublicParams where G1: Group::Scalar>, G2: Group::Scalar>, C1: StepCircuit + Clone, C2: StepCircuit + Clone, { /// Create a new `PublicParams` pub fn setup(c_primary: C1, c_secondary: C2) -> Self { let params_primary = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true); let params_secondary = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false); let _ro_consts_primary: ROConstants = ROConstants::::new(); let _ro_consts_secondary: ROConstants = ROConstants::::new(); let ro_consts_circuit_primary: ROConstantsCircuit<::Base> = ROConstantsCircuit::new(); let ro_consts_circuit_secondary: ROConstantsCircuit<::Base> = ROConstantsCircuit::new(); // Initialize gens for the primary let circuit_primary: NIFSVerifierCircuit = NIFSVerifierCircuit::new( params_primary.clone(), None, c_primary.clone(), ro_consts_circuit_primary.clone(), ); let mut cs: ShapeCS = ShapeCS::new(); let _ = circuit_primary.synthesize(&mut cs); let (r1cs_shape_primary, r1cs_gens_primary) = (cs.r1cs_shape(), cs.r1cs_gens()); // Initialize gens for the secondary let circuit_secondary: NIFSVerifierCircuit = NIFSVerifierCircuit::new( params_secondary.clone(), None, c_secondary.clone(), ro_consts_circuit_secondary.clone(), ); let mut cs: ShapeCS = ShapeCS::new(); let _ = circuit_secondary.synthesize(&mut cs); let (r1cs_shape_secondary, r1cs_gens_secondary) = (cs.r1cs_shape(), cs.r1cs_gens()); Self { _ro_consts_primary, ro_consts_circuit_primary, r1cs_gens_primary, r1cs_shape_primary, _ro_consts_secondary, ro_consts_circuit_secondary, r1cs_gens_secondary, r1cs_shape_secondary, c_primary, c_secondary, params_primary, params_secondary, } } } /// A SNARK that proves the correct execution of an incremental computation pub struct RecursiveSNARK where G1: Group::Scalar>, G2: Group::Scalar>, C1: StepCircuit + Clone, C2: StepCircuit + Clone, { r_W_primary: RelaxedR1CSWitness, r_U_primary: RelaxedR1CSInstance, l_w_primary: R1CSWitness, l_u_primary: R1CSInstance, r_W_secondary: RelaxedR1CSWitness, r_U_secondary: RelaxedR1CSInstance, l_w_secondary: R1CSWitness, l_u_secondary: R1CSInstance, _p_c1: PhantomData, _p_c2: PhantomData, } impl RecursiveSNARK where G1: Group::Scalar>, G2: Group::Scalar>, C1: StepCircuit + Clone, C2: StepCircuit + Clone, { /// Create a new `RecursiveSNARK` pub fn prove( pp: &PublicParams, z0_primary: G1::Scalar, z0_secondary: G2::Scalar, ) -> Result { // Execute the base case for the primary let mut cs_primary: SatisfyingAssignment = SatisfyingAssignment::new(); let inputs_primary: NIFSVerifierCircuitInputs = NIFSVerifierCircuitInputs::new( pp.r1cs_shape_secondary.get_digest(), ::Base::zero(), z0_primary, None, None, None, None, ); let circuit_primary: NIFSVerifierCircuit = NIFSVerifierCircuit::new( pp.params_primary.clone(), Some(inputs_primary), pp.c_primary.clone(), pp.ro_consts_circuit_primary.clone(), ); let _ = circuit_primary.synthesize(&mut cs_primary); let (u_primary, w_primary) = cs_primary .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.r1cs_gens_primary) .map_err(|_e| NovaError::UnSat)?; // check if the base case is satisfied pp.r1cs_shape_primary .is_sat(&pp.r1cs_gens_primary, &u_primary, &w_primary) .map_err(|_e| NovaError::UnSat)?; // Execute the base case for the secondary let mut cs_secondary: SatisfyingAssignment = SatisfyingAssignment::new(); let inputs_secondary: NIFSVerifierCircuitInputs = NIFSVerifierCircuitInputs::new( pp.r1cs_shape_primary.get_digest(), ::Base::zero(), z0_secondary, None, None, Some(u_primary.clone()), None, ); let circuit_secondary: NIFSVerifierCircuit = NIFSVerifierCircuit::new( pp.params_secondary.clone(), Some(inputs_secondary), pp.c_secondary.clone(), pp.ro_consts_circuit_secondary.clone(), ); let _ = circuit_secondary.synthesize(&mut cs_secondary); let (u_secondary, w_secondary) = cs_secondary .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.r1cs_gens_secondary) .map_err(|_e| NovaError::UnSat)?; // check if the base case is satisfied pp.r1cs_shape_secondary .is_sat(&pp.r1cs_gens_secondary, &u_secondary, &w_secondary) .map_err(|_e| NovaError::UnSat)?; Ok(Self { r_W_primary: RelaxedR1CSWitness::::default(&pp.r1cs_shape_primary), r_U_primary: RelaxedR1CSInstance::::default( &pp.r1cs_gens_primary, &pp.r1cs_shape_primary, ), l_w_primary: w_primary, l_u_primary: u_primary, r_W_secondary: RelaxedR1CSWitness::::default(&pp.r1cs_shape_secondary), r_U_secondary: RelaxedR1CSInstance::::default( &pp.r1cs_gens_secondary, &pp.r1cs_shape_secondary, ), l_w_secondary: w_secondary, l_u_secondary: u_secondary, _p_c1: Default::default(), _p_c2: Default::default(), }) } /// Verify the correctness of the `RecursiveSNARK` pub fn verify(&self, pp: &PublicParams) -> Result<(), NovaError> { pp.r1cs_shape_primary.is_sat_relaxed( &pp.r1cs_gens_primary, &self.r_U_primary, &self.r_W_primary, )?; pp.r1cs_shape_primary .is_sat(&pp.r1cs_gens_primary, &self.l_u_primary, &self.l_w_primary)?; pp.r1cs_shape_secondary.is_sat_relaxed( &pp.r1cs_gens_secondary, &self.r_U_secondary, &self.r_W_secondary, )?; pp.r1cs_shape_secondary.is_sat( &pp.r1cs_gens_secondary, &self.l_u_secondary, &self.l_w_secondary, )?; Ok(()) } } /// A SNARK that proves the knowledge of a valid `RecursiveSNARK` pub struct CompressedSNARKTrivial { W: RelaxedR1CSWitness, } impl CompressedSNARKTrivial { /// Produces a proof of a instance given its satisfying witness `W`. pub fn prove(W: &RelaxedR1CSWitness) -> Result, NovaError> { Ok(Self { W: W.clone() }) } /// Verifies the proof of a folded instance `U` given its shape `S` public parameters `gens` pub fn verify( &self, gens: &R1CSGens, S: &R1CSShape, U: &RelaxedR1CSInstance, ) -> Result<(), NovaError> { // check that the witness is a valid witness to the folded instance `U` S.is_sat_relaxed(gens, U, &self.W) } } #[cfg(test)] mod tests { use super::*; type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; use ff::PrimeField; use std::marker::PhantomData; #[derive(Clone, Debug)] struct TestCircuit { _p: PhantomData, } impl StepCircuit for TestCircuit where F: PrimeField, { fn synthesize>( &self, _cs: &mut CS, z: AllocatedNum, ) -> Result, SynthesisError> { Ok(z) } } #[test] fn test_base_case() { // produce public parameters let pp = PublicParams::< G1, G2, TestCircuit<::Base>, TestCircuit<::Base>, >::setup( TestCircuit { _p: Default::default(), }, TestCircuit { _p: Default::default(), }, ); // produce a recursive SNARK let res = RecursiveSNARK::prove( &pp, ::Base::zero(), ::Base::zero(), ); assert!(res.is_ok()); let recursive_snark = res.unwrap(); // verify the recursive SNARK let res = recursive_snark.verify(&pp); assert!(res.is_ok()); } }