diff --git a/CHANGELOG.md b/CHANGELOG.md index ea39951aa..783ce96cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Pending ### Breaking changes +- [\#348](https://github.com/arkworks-rs/snark/pull/334) Introduce a `Relation` trait, change the `SNARK` traits to use it, and break down `ConstraintSynthesizer` into three traits. ### Features - [\#347](https://github.com/arkworks-rs/snark/pull/347) Add `into_inner` function for `ConstraintSystemRef`. @@ -10,6 +11,7 @@ ### Bug fixes + ## v0.2.0 ### Breaking changes @@ -23,6 +25,8 @@ ### Bug fixes - [\#340](https://github.com/arkworks-rs/snark/pull/340) Compile with `panic='abort'` in release mode, for safety of the library across FFI boundaries. + + ## v0.1.0 This tag corresponds to the old `zexe` codebase. diff --git a/relations/src/lib.rs b/relations/src/lib.rs index cf249f777..ca57b78f2 100644 --- a/relations/src/lib.rs +++ b/relations/src/lib.rs @@ -16,3 +16,25 @@ extern crate ark_std; pub mod r1cs; + +/// An *indexed relation* is a set of triples of the form `(index, instance, witness)`. +pub trait Relation { + /// The index is a "large" but static part of the triple. Examples include + /// the circuit in the circuit satisfiability relation, and constraint + /// matrices in the R1CS relation. + type Index: Eq; + /// The instance is a "small" part of the triple. Like the index, it is publicly known. + type Instance: Eq; + /// The witness is a "large" but private part of the triple. + type Witness: Eq; +} + +/// An *indexed NP relation* is a relation with an efficient membership check. +pub trait NPRelation: Relation { + /// Checks whether the triple `(index, instance, witness)` is a member of the relation. + fn check_membership( + index: &Self::Index, + instance: &Self::Instance, + witness: &Self::Witness, + ) -> bool; +} diff --git a/relations/src/r1cs/constraint_system.rs b/relations/src/r1cs/constraint_system.rs index f3e7bd5eb..b5fb3f998 100644 --- a/relations/src/r1cs/constraint_system.rs +++ b/relations/src/r1cs/constraint_system.rs @@ -1,6 +1,8 @@ #[cfg(feature = "std")] use crate::r1cs::ConstraintTrace; -use crate::r1cs::{LcIndex, LinearCombination, Matrix, SynthesisError, Variable}; +use crate::r1cs::{ + ConstraintMatrices, Instance, LcIndex, LinearCombination, SynthesisError, Variable, Witness, +}; use ark_ff::Field; use ark_std::{ any::{Any, TypeId}, @@ -14,13 +16,20 @@ use ark_std::{ vec::Vec, }; -/// Computations are expressed in terms of rank-1 constraint systems (R1CS). -/// The `generate_constraints` method is called to generate constraints for -/// both CRS generation and for proving. -// TODO: Think: should we replace this with just a closure? -pub trait ConstraintSynthesizer { - /// Drives generation of new constraints inside `cs`. - fn generate_constraints(self, cs: ConstraintSystemRef) -> crate::r1cs::Result<()>; +/// Describes how to generate an R1CS index for a given high-level computation. +pub trait ConstraintGenerator { + /// Generates R1CS constraint matrices, and, optionally, the variable assignments + /// by modifying [`cs`] in place. The result is stored in [`cs`]. + /// + /// Variable assignments are generated if `!cs.is_in_setup_mode()`. + fn generate_constraints_and_variable_assignments( + &self, + cs: ConstraintSystemRef, + ) -> crate::r1cs::Result<()>; + + /// Generates an R1CS instance assignment by modifying [`cs`] in place. + /// The result is stored in [`cs`]. + fn generate_instance_assignment(&self, cs: ConstraintSystemRef) -> crate::r1cs::Result<()>; } /// An Rank-One `ConstraintSystem`. Enforces constraints of the form @@ -30,15 +39,17 @@ pub trait ConstraintSynthesizer { #[derive(Debug, Clone)] pub struct ConstraintSystem { /// The mode in which the constraint system is operating. `self` can either - /// be in setup mode (i.e., `self.mode == SynthesisMode::Setup`) or in - /// proving mode (i.e., `self.mode == SynthesisMode::Prove`). If we are - /// in proving mode, then we have the additional option of whether or + /// be in setup mode (`self.mode == SynthesisMode::Setup`), in + /// proving mode (`self.mode == SynthesisMode::Prove`), or in verifying mode + /// (`self.mode == SynthesisMode::Verify`). + /// + /// If we are in proving mode, then we have the additional option of whether or /// not to construct the A, B, and C matrices of the constraint system /// (see below). pub mode: SynthesisMode, /// The number of variables that are "public inputs" to the constraint - /// system. - pub num_instance_variables: usize, + /// system. This includes the 1 variable. + num_instance_variables: usize, /// The number of variables that are "private inputs" to the constraint /// system. pub num_witness_variables: usize, @@ -53,10 +64,10 @@ pub struct ConstraintSystem { /// Assignments to the public input variables. This is empty if `self.mode /// == SynthesisMode::Setup`. - pub instance_assignment: Vec, + instance_assignment: Vec, /// Assignments to the private input variables. This is empty if `self.mode /// == SynthesisMode::Setup`. - pub witness_assignment: Vec, + witness_assignment: Vec, /// Map for gadgets to cache computation results. pub cache_map: Rc>>>, @@ -85,7 +96,7 @@ pub enum SynthesisMode { /// Indicate to the `ConstraintSystem` that it should only generate /// constraint matrices and not populate the variable assignments. Setup, - /// Indicate to the `ConstraintSystem` that it populate the variable + /// Indicate to the `ConstraintSystem` that it should populate the witness variable /// assignments. If additionally `construct_matrices == true`, then generate /// the matrices as in the `Setup` case. Prove { @@ -93,6 +104,9 @@ pub enum SynthesisMode { /// the matrices as in the `Setup` case. construct_matrices: bool, }, + /// Indicate to the `ConstraintSystem` that it populate the instance variable + /// assignments. + Verify, } /// Defines the parameter to optimize for a `ConstraintSystem`. @@ -167,6 +181,11 @@ impl ConstraintSystem { self.mode == SynthesisMode::Setup } + /// Check whether `self.mode == SynthesisMode::Verify`. + pub fn is_in_verify_mode(&self) -> bool { + self.mode == SynthesisMode::Verify + } + /// Check whether this constraint system aims to optimize weight, /// number of constraints, or neither. pub fn optimization_goal(&self) -> OptimizationGoal { @@ -190,6 +209,7 @@ impl ConstraintSystem { match self.mode { SynthesisMode::Setup => true, SynthesisMode::Prove { construct_matrices } => construct_matrices, + SynthesisMode::Verify => false, } } @@ -216,6 +236,8 @@ impl ConstraintSystem { let index = self.num_instance_variables; self.num_instance_variables += 1; + // Only generate instance variable assignments when `self.mode` is either + // `SynthesisMode::Prove` or `SynthesisMode::Verify`. if !self.is_in_setup_mode() { self.instance_assignment.push(f()?); } @@ -231,7 +253,7 @@ impl ConstraintSystem { let index = self.num_witness_variables; self.num_witness_variables += 1; - if !self.is_in_setup_mode() { + if !(self.is_in_setup_mode() || self.is_in_verify_mode()) { self.witness_assignment.push(f()?); } Ok(Variable::Witness(index)) @@ -264,13 +286,14 @@ impl ConstraintSystem { self.a_constraints.push(a_index); self.b_constraints.push(b_index); self.c_constraints.push(c_index); + #[cfg(feature = "std")] + { + let trace = ConstraintTrace::capture(); + self.constraint_traces.push(trace); + } } self.num_constraints += 1; - #[cfg(feature = "std")] - { - let trace = ConstraintTrace::capture(); - self.constraint_traces.push(trace); - } + Ok(()) } @@ -510,24 +533,25 @@ impl ConstraintSystem { } } - /// Finalize the constraint system (either by outlining or inlining, + /// Optimize the constraint system (either by outlining or inlining, /// if an optimization goal is set). - pub fn finalize(&mut self) { - match self.optimization_goal { - OptimizationGoal::None => self.inline_all_lcs(), - OptimizationGoal::Constraints => self.inline_all_lcs(), - OptimizationGoal::Weight => self.outline_lcs(), - }; + pub fn optimize(&mut self) { + // In verify mode we don't have any linear combinations; all variables + // are instance variables, and there are no generated constraints + if !self.is_in_verify_mode() { + match self.optimization_goal { + OptimizationGoal::None => self.inline_all_lcs(), + OptimizationGoal::Constraints => self.inline_all_lcs(), + OptimizationGoal::Weight => self.outline_lcs(), + }; + } } /// This step must be called after constraint generation has completed, and /// after all symbolic LCs have been inlined into the places that they /// are used. pub fn to_matrices(&self) -> Option> { - if let SynthesisMode::Prove { - construct_matrices: false, - } = self.mode - { + if !self.should_construct_matrices() { None } else { let a: Vec<_> = self @@ -587,7 +611,7 @@ impl ConstraintSystem { /// the first unsatisfied constraint. If `self.is_in_setup_mode()`, outputs /// `Err(())`. pub fn which_is_unsatisfied(&self) -> crate::r1cs::Result> { - if self.is_in_setup_mode() { + if self.is_in_setup_mode() || self.is_in_verify_mode() { Err(SynthesisError::AssignmentMissing) } else { for i in 0..self.num_constraints { @@ -643,36 +667,6 @@ impl ConstraintSystem { } } } -/// The A, B and C matrices of a Rank-One `ConstraintSystem`. -/// Also contains metadata on the structure of the constraint system -/// and the matrices. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ConstraintMatrices { - /// The number of variables that are "public instances" to the constraint - /// system. - pub num_instance_variables: usize, - /// The number of variables that are "private witnesses" to the constraint - /// system. - pub num_witness_variables: usize, - /// The number of constraints in the constraint system. - pub num_constraints: usize, - /// The number of non_zero entries in the A matrix. - pub a_num_non_zero: usize, - /// The number of non_zero entries in the B matrix. - pub b_num_non_zero: usize, - /// The number of non_zero entries in the C matrix. - pub c_num_non_zero: usize, - - /// The A constraint matrix. This is empty when - /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`. - pub a: Matrix, - /// The B constraint matrix. This is empty when - /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`. - pub b: Matrix, - /// The C constraint matrix. This is empty when - /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`. - pub c: Matrix, -} /// A shared reference to a constraint system that can be stored in high level /// variables. @@ -769,7 +763,7 @@ impl ConstraintSystemRef { /// Consumes self to return the inner `ConstraintSystem`. Returns /// `None` if `Self::CS` is `None` or if any other references to - /// `Self::CS` exist. + /// `Self::CS` exist. pub fn into_inner(self) -> Option> { match self { Self::CS(a) => Rc::try_unwrap(a).ok().map(|s| s.into_inner()), @@ -807,6 +801,13 @@ impl ConstraintSystemRef { .map_or(false, |cs| cs.borrow().is_in_setup_mode()) } + /// Check whether `self.mode == SynthesisMode::Verify`. + #[inline] + pub fn is_in_verify_mode(&self) -> bool { + self.inner() + .map_or(false, |cs| cs.borrow().is_in_verify_mode()) + } + /// Returns the number of constraints. #[inline] pub fn num_constraints(&self) -> usize { @@ -880,7 +881,7 @@ impl ConstraintSystemRef { self.inner() .ok_or(SynthesisError::MissingCS) .and_then(|cs| { - if !self.is_in_setup_mode() { + if !(self.is_in_setup_mode() || self.is_in_verify_mode()) { // This is needed to avoid double-borrows, because `f` // might itself mutably borrow `cs` (eg: `f = || g.value()`). let value = f(); @@ -926,11 +927,11 @@ impl ConstraintSystemRef { } } - /// Finalize the constraint system (either by outlining or inlining, + /// Optimize the constraint system (either by outlining or inlining, /// if an optimization goal is set). - pub fn finalize(&self) { + pub fn optimize(&self) { if let Some(cs) = self.inner() { - cs.borrow_mut().finalize() + cs.borrow_mut().optimize() } } @@ -942,6 +943,32 @@ impl ConstraintSystemRef { self.inner().and_then(|cs| cs.borrow().to_matrices()) } + /// This step must be called after constraint generation has completed, and + /// after all symbolic LCs have been inlined into the places that they + /// are used. + #[inline] + pub fn instance_assignment(&self) -> Option> { + self.inner() + .map(|cs| { + // Drop the leading 1 before returning the assignment. + let mut inst = cs.borrow().instance_assignment.clone(); + inst.remove(0); + assert_eq!(inst.len(), cs.borrow().num_instance_variables - 1); + inst + }) + .map(Instance) + } + + /// This step must be called after constraint generation has completed, and + /// after all symbolic LCs have been inlined into the places that they + /// are used. + #[inline] + pub fn witness_assignment(&self) -> Option> { + self.inner() + .map(|cs| cs.borrow().witness_assignment.clone()) + .map(Witness) + } + /// If `self` is satisfied, outputs `Ok(true)`. /// If `self` is unsatisfied, outputs `Ok(false)`. /// If `self.is_in_setup_mode()` or if `self == None`, outputs `Err(())`. diff --git a/relations/src/r1cs/mod.rs b/relations/src/r1cs/mod.rs index 69adfa3f5..8b6edfd2f 100644 --- a/relations/src/r1cs/mod.rs +++ b/relations/src/r1cs/mod.rs @@ -1,9 +1,115 @@ -//! Core interface for working with Rank-1 Constraint Systems (R1CS). +//! Core interfaces for working with Rank-1 Constraint Systems (R1CS), which is +//! an indexed NP relation. + +use crate::{NPRelation, Relation}; +use ark_std::{cmp::Ordering, marker::PhantomData, vec::Vec}; + +/// R1CS is an *indexed NP relation*. +/// An index consists of three matrices (A, B, C), +/// while the instance *x* and witness *w* are vectors of field elements +/// such that, for z := (1 || x || w), Az ○ Bz = Cz. +pub struct R1CS { + f: PhantomData, +} -use ark_std::vec::Vec; +/// An R1CS index consists of three matrices, as well as the number of instance variables +/// and number of witness variables. The "one" variable is treated as a defacto instance +/// variable for the purposes of counting. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstraintMatrices { + /// The number of variables that are "public instances" to the constraint + /// system. This includes the one variable. + pub num_instance_variables: usize, + /// The number of variables that are "private witnesses" to the constraint + /// system. + pub num_witness_variables: usize, + /// The number of constraints in the constraint system. + pub num_constraints: usize, + /// The number of non_zero entries in the A matrix. + pub a_num_non_zero: usize, + /// The number of non_zero entries in the B matrix. + pub b_num_non_zero: usize, + /// The number of non_zero entries in the C matrix. + pub c_num_non_zero: usize, + + /// The A constraint matrix. + pub a: Matrix, + /// The B constraint matrix. + pub b: Matrix, + /// The C constraint matrix. + pub c: Matrix, +} -/// A result type specialized to `SynthesisError`. -pub type Result = core::result::Result; +/// An R1CS instance consists of variable assignments to the instance variables. +#[derive(Eq, PartialEq, Debug, Hash, Clone)] +pub struct Instance(pub Vec); + +/// An R1CS instance consists of variable assignments to the witness variables. +#[derive(Eq, PartialEq, Debug, Hash, Clone)] +pub struct Witness(pub Vec); + +impl Relation for R1CS { + type Index = ConstraintMatrices; + type Instance = Instance; + type Witness = Witness; +} + +impl NPRelation for R1CS { + fn check_membership( + index: &Self::Index, + instance: &Self::Instance, + witness: &Self::Witness, + ) -> bool { + let mut instance = instance.0.to_vec(); + instance.insert(0, F::one()); + // The number of instance variables does not match. + if instance.len() != index.num_instance_variables + 1 { + return false; + } + // The number of witness variables does not match. + if witness.0.len() != index.num_witness_variables { + return false; + } + + // Let z = instance || witness. Check that Az ○ Bz = Cz, where ○ is the Hadamard product. + // The Hadamard product of two vectors is the product of them entry-wise, so + // [ 2 ] [ 3 ] [ 6 ] + // [ 3 ] ○ [ 6 ] = [ 18 ] + // [ 4 ] [ 7 ] [ 28 ] + cfg_iter!(&index.a) + .zip(&index.b) + .zip(&index.c) + .all(|((a_row, b_row), c_row)| { + let a = inner_product(&a_row, index.num_instance_variables, &instance, &witness.0); + let b = inner_product(&b_row, index.num_instance_variables, &instance, &witness.0); + let c = inner_product(&c_row, index.num_instance_variables, &instance, &witness.0); + a * b == c + }) + } +} + +// Compute the inner product of `row` with `instance.concat(witness)`. +fn inner_product( + row: &[(F, usize)], + num_instance_variables: usize, + instance: &[F], + witness: &[F], +) -> F { + let mut acc = F::zero(); + for &(ref coeff, i) in row { + let tmp = if i < num_instance_variables { + instance[i] + } else { + witness[i - num_instance_variables] + }; + acc += &(if coeff.is_one() { tmp } else { tmp * coeff }); + } + acc +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// #[macro_use] mod impl_lc; @@ -19,12 +125,12 @@ pub use tracing::info_span; pub use ark_ff::{Field, ToConstraintField}; pub use constraint_system::{ - ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, - OptimizationGoal, SynthesisMode, + ConstraintGenerator, ConstraintSystem, ConstraintSystemRef, Namespace, OptimizationGoal, + SynthesisMode, }; pub use error::SynthesisError; - -use core::cmp::Ordering; +/// A result type specialized to `SynthesisError`. +pub type Result = core::result::Result; /// A sparse representation of constraint matrices. pub type Matrix = Vec>; diff --git a/snark/src/lib.rs b/snark/src/lib.rs index 7832f3e81..36a7641c9 100644 --- a/snark/src/lib.rs +++ b/snark/src/lib.rs @@ -10,13 +10,16 @@ )] #![forbid(unsafe_code)] -use ark_ff::{PrimeField, ToBytes}; -use ark_relations::r1cs::ConstraintSynthesizer; +use ark_ff::ToBytes; +use ark_relations::NPRelation; use ark_std::rand::{CryptoRng, RngCore}; use core::fmt::Debug; +/// Specialized interface for R1CS-based SNARKs. +pub mod r1cs; + /// The basic functionality for a SNARK. -pub trait SNARK { +pub trait SNARK { /// The information required by the prover to produce a proof for a specific /// circuit *C*. type ProvingKey: Clone; @@ -35,63 +38,53 @@ pub trait SNARK { /// Errors encountered during setup, proving, or verification. type Error: 'static + ark_std::error::Error; - /// Takes in a description of a computation (specified in R1CS constraints), - /// and samples proving and verification keys for that circuit. - fn circuit_specific_setup, R: RngCore + CryptoRng>( - circuit: C, - rng: &mut R, - ) -> Result<(Self::ProvingKey, Self::VerifyingKey), Self::Error>; - /// Generates a proof of satisfaction of the arithmetic circuit C (specified /// as R1CS constraints). - fn prove, R: RngCore + CryptoRng>( - circuit_pk: &Self::ProvingKey, - circuit: C, - rng: &mut R, + fn prove( + pk: &Self::ProvingKey, + index: &Option, + instance: &R::Instance, + witness: &R::Witness, + rng: &mut Rng, ) -> Result; /// Checks that `proof` is a valid proof of the satisfaction of circuit - /// encoded in `circuit_vk`, with respect to the public input `public_input`, + /// encoded in `vk`, with respect to the public input `public_input`, /// specified as R1CS constraints. fn verify( - circuit_vk: &Self::VerifyingKey, - public_input: &[F], + vk: &Self::VerifyingKey, + instance: &R::Instance, proof: &Self::Proof, ) -> Result { - let pvk = Self::process_vk(circuit_vk)?; - Self::verify_with_processed_vk(&pvk, public_input, proof) + let pvk = Self::process_vk(vk)?; + Self::verify_with_processed_vk(&pvk, instance, proof) } - /// Preprocesses `circuit_vk` to enable faster verification. - fn process_vk( - circuit_vk: &Self::VerifyingKey, - ) -> Result; + /// Preprocesses `vk` to enable faster verification. + fn process_vk(vk: &Self::VerifyingKey) -> Result; /// Checks that `proof` is a valid proof of the satisfaction of circuit - /// encoded in `circuit_pvk`, with respect to the public input `public_input`, + /// encoded in `pvk`, with respect to the public input `public_input`, /// specified as R1CS constraints. fn verify_with_processed_vk( - circuit_pvk: &Self::ProcessedVerifyingKey, - public_input: &[F], + pvk: &Self::ProcessedVerifyingKey, + instance: &R::Instance, proof: &Self::Proof, ) -> Result; } /// A SNARK with (only) circuit-specific setup. -pub trait CircuitSpecificSetupSNARK: SNARK { - /// The setup algorithm for circuit-specific SNARKs. By default, this - /// just invokes `>::circuit_specific_setup(...)`. - fn setup, R: RngCore + CryptoRng>( - circuit: C, - rng: &mut R, - ) -> Result<(Self::ProvingKey, Self::VerifyingKey), Self::Error> { - >::circuit_specific_setup(circuit, rng) - } +pub trait CircuitSpecificSetupSNARK: SNARK { + /// The setup algorithm for circuit-specific SNARKs. + fn circuit_specific_setup( + index: &R::Index, + rng: &mut Rng, + ) -> Result<(Self::ProvingKey, Self::VerifyingKey), Self::Error>; } /// A helper type for universal-setup SNARKs, which must infer their computation /// size bounds. -pub enum UniversalSetupIndexError { +pub enum IndexingError { /// The provided universal public parameters were insufficient to encode /// the given circuit. NeedLargerBound(Bound), @@ -101,30 +94,57 @@ pub enum UniversalSetupIndexError { /// A SNARK with universal setup. That is, a SNARK where the trusted setup is /// circuit-independent. -pub trait UniversalSetupSNARK: SNARK { +pub trait UniversalSetupSNARK: SNARK { /// Specifies how to bound the size of public parameters required to /// generate the index proving and verification keys for a given /// circuit. - type ComputationBound: Clone + Default + Debug; + /// + /// For example, for SNARKs that rely on polynomial commitments, this would + /// be the maximum degree of polynomials required to prove a given + /// instance. + type IndexBound: Clone + Default + Debug; /// Specifies the type of universal public parameters. type PublicParameters: Clone + Debug; + /// Specifies the bound size that is necessary and sufficient to + /// generate public parameters for `index`. + fn bound_for_index(index: &R::Index) -> Self::IndexBound; + /// Specifies how to bound the size of public parameters required to /// generate the index proving and verification keys for a given /// circuit. - fn universal_setup( - compute_bound: &Self::ComputationBound, - rng: &mut R, + fn universal_setup( + compute_bound: &Self::IndexBound, + rng: &mut Rng, ) -> Result; /// Indexes the public parameters according to the circuit `circuit`, and /// outputs circuit-specific proving and verification keys. - fn index, R: RngCore + CryptoRng>( + /// + /// This is a *deterministic* method. + fn index( pp: &Self::PublicParameters, - circuit: C, - rng: &mut R, - ) -> Result< - (Self::ProvingKey, Self::VerifyingKey), - UniversalSetupIndexError, - >; + index: &R::Index, + ) -> Result<(Self::ProvingKey, Self::VerifyingKey), IndexingError>; +} + +impl CircuitSpecificSetupSNARK for S +where + S: UniversalSetupSNARK, +{ + /// The setup algorithm for circuit-specific SNARKs. + fn circuit_specific_setup( + index: &R::Index, + rng: &mut Rng, + ) -> Result<(Self::ProvingKey, Self::VerifyingKey), Self::Error> { + let bound = Self::bound_for_index(index); + let pp = Self::universal_setup(&bound, rng)?; + Self::index(&pp, index).map_err(|e| { + if let IndexingError::Other(e) = e { + e + } else { + panic!("`bound_for_index` returned bound that is insufficient for indexing") + } + }) + } } diff --git a/snark/src/r1cs.rs b/snark/src/r1cs.rs new file mode 100644 index 000000000..b690837f0 --- /dev/null +++ b/snark/src/r1cs.rs @@ -0,0 +1,89 @@ +use crate::{CircuitSpecificSetupSNARK, IndexingError, UniversalSetupSNARK, SNARK}; +use ark_ff::Field; +use ark_relations::r1cs::{ConstraintGenerator, ConstraintMatrices, Instance, Witness, R1CS}; +use ark_std::rand::{CryptoRng, RngCore}; + +/// A [`SNARKForR1CS`] is a [`SNARK`] for the [`R1CS`] relation. +pub trait SNARKForR1CS: SNARK> { + /// Does the proving algorithm explicitly require the matrices, or is it stored + /// in the proving key? + const PROVING_REQUIRES_MATRICES: bool; + + /// Generate inputs for the SNARK indexer from [`cs`]. + /// These inputs consist of the constraint matrices. + fn indexer_inputs>( + cs: &CG, + ) -> Result, Self::Error>; + + /// Generate inputs for the SNARK prover from [`cs`]. + /// These inputs consist of the instance and witness. Additionally, + /// if `Self::PROVING_REQUIRES_MATRICES == true`, then this method returns + /// `Some(index)` as well. + fn prover_inputs>( + cs: &CG, + ) -> Result<(Option>, Instance, Witness), Self::Error>; + + /// Generate inputs for the SNARK verifier from [`cs`]. + /// This input consists of the instance. + fn verifier_inputs>(cs: &CG) -> Result, Self::Error>; + + /// Indexes the public parameters according to the circuit `circuit`, and + /// outputs circuit-specific proving and verification keys. + fn circuit_specific_setup_with_cs, Rng: RngCore + CryptoRng>( + c: &CG, + rng: &mut Rng, + ) -> Result<(Self::ProvingKey, Self::VerifyingKey), Self::Error> + where + Self: CircuitSpecificSetupSNARK>, + { + let index = Self::indexer_inputs(c)?; + Self::circuit_specific_setup(&index, rng) + } + + /// Indexes the public parameters according to the circuit `circuit`, and + /// outputs circuit-specific proving and verification keys. + /// + /// This is a *deterministic* method. + fn index_with_cs>( + pp: &Self::PublicParameters, + c: &CG, + ) -> Result<(Self::ProvingKey, Self::VerifyingKey), IndexingError> + where + Self: UniversalSetupSNARK>, + { + let index = Self::indexer_inputs(c).map_err(IndexingError::Other)?; + Self::index(pp, &index) + } + + /// Generates a proof of satisfaction of the constraint system induced by [`c`]. + fn prove_with_cs, Rng: RngCore + CryptoRng>( + pk: &Self::ProvingKey, + c: &CG, + rng: &mut Rng, + ) -> Result { + let (index, instance, witness) = Self::prover_inputs(c)?; + Self::prove(pk, &index, &instance, &witness, rng) + } + + /// Verify that [`proof`] is a valid proof with respect to [`vk`] and to the + /// instance induced by [`c`]. + fn verify_with_cs>( + vk: &Self::VerifyingKey, + c: &CG, + proof: &Self::Proof, + ) -> Result { + let instance = Self::verifier_inputs(c)?; + Self::verify(vk, &instance, proof) + } + + /// Checks that [`proof`] is a valid proof of with respect to [`pvk`] and + /// the instance induced by [`cs`]. + fn verify_with_cs_and_processed_vk>( + pvk: &Self::ProcessedVerifyingKey, + c: &CG, + proof: &Self::Proof, + ) -> Result { + let instance = Self::verifier_inputs(c)?; + Self::verify_with_processed_vk(pvk, &instance, proof) + } +}