From 2d22bff71f27dbb523b8520f5eecdff57f90da34 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Mon, 31 Aug 2020 14:11:42 -0700 Subject: [PATCH] Add APIs to specify an R1CS Instance (#24) --- README.md | 163 ++++++++++++++++++++++++++++++++++--- benches/nizk.rs | 4 +- benches/snark.rs | 6 +- profiler/nizk.rs | 4 +- profiler/snark.rs | 4 +- src/errors.rs | 25 ++++++ src/lib.rs | 157 +++++++++++++++++++++++++++++++---- src/nizk/bullet.rs | 2 +- src/r1csinstance.rs | 65 ++++++++++++--- src/r1csproof.rs | 40 ++++----- src/scalar/ristretto255.rs | 2 +- src/sparse_mlpoly.rs | 3 +- 12 files changed, 398 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 03bb8f1..ee65edf 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,12 @@ We now highlight Spartan's distinctive features. * **General-purpose:** Spartan produces proofs for arbitrary NP statements. `libspartan` supports NP statements expressed as rank-1 constraint satisfiability (R1CS) instances, a popular language for which there exists efficient transformations and compiler toolchains from high-level programs of interest. -* **Sub-linear verification costs and linear-time proving costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS). Spartan also features a time-optimal prover, a property that has remained elusive for nearly all zkSNARKs in the literature. +* **Sub-linear verification costs and linear-time proving costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS). Spartan also features a linear-time prover, a property that has remained elusive for nearly all zkSNARKs in the literature. * **Standardized security:** Spartan's security relies on the hardness of computing discrete logarithms (a standard cryptographic assumption) in the random oracle model. `libspartan` uses `ristretto255`, a prime-order group abstraction atop `curve25519` (a high-speed elliptic curve). We use [`curve25519-dalek`](https://docs.rs/curve25519-dalek) for arithmetic over `ristretto255`. * **State-of-the-art performance:** -Among transparent SNARKs, Spartan -offers the fastest prover with speedups of 36–152× depending on the baseline, -produces proofs that are shorter by 1.2–416×, and incurs the lowest verification -times with speedups of 3.6–1326×. When compared to the state-of-the-art zkSNARK -with trusted setup, Spartan’s prover is 2× faster for arbitrary R1CS instances and -16× faster for data-parallel workloads. - -### Status -Development is ongoing (PRs are welcome!). For example, the library does not yet offer APIs to specify an NP statement, but we will in the future offer standardized APIs and also integrate with a compiler that produces R1CS instances from high level programs. +Among transparent SNARKs, Spartan offers the fastest prover with speedups of 36–152× depending on the baseline, produces proofs that are shorter by 1.2–416×, and incurs the lowest verification times with speedups of 3.6–1326×. The only exception is proof sizes under Bulletproofs, but Bulletproofs incurs slower verification both asymptotically and concretely. When compared to the state-of-the-art zkSNARK with trusted setup, Spartan’s prover is 2× faster for arbitrary R1CS instances and 16× faster for data-parallel workloads. ### Implementation details `libspartan` uses [`merlin`](https://docs.rs/merlin/) to automate the Fiat-Shamir transform. We also introduce a new type called `RandomTape` that extends a `Transcript` in `merlin` to allow the prover's internal methods to produce private randomness using its private transcript without having to create `OsRng` objects throughout the code. An object of type `RandomTape` is initialized with a new random seed from `OsRng` for each proof produced by the library. @@ -53,7 +45,7 @@ Some of our public APIs' style is inspired by the underlying crates we use. let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); // ask the library to produce a synthentic R1CS instance - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // create a commitment to the R1CS instance let (comm, decomm) = SNARK::encode(&inst, &gens); @@ -86,7 +78,7 @@ Here is another example to use the NIZK variant of the Spartan proof system: let gens = NIZKGens::new(num_cons, num_vars); // ask the library to produce a synthentic R1CS instance - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce a proof of satisfiability let mut prover_transcript = Transcript::new(b"nizk_example"); @@ -100,6 +92,153 @@ Here is another example to use the NIZK variant of the Spartan proof system: # } ``` +Finally, we provide an example that specifies a custom R1CS instance instead of using a synthetic instance +```rust +# extern crate curve25519_dalek; +# extern crate libspartan; +# extern crate merlin; +# use curve25519_dalek::scalar::Scalar; +# use libspartan::{InputsAssignment, Instance, SNARKGens, VarsAssignment, SNARK}; +# use merlin::Transcript; +# use rand::rngs::OsRng; +# fn main() { + // produce a tiny instance + let ( + num_cons, + num_vars, + num_inputs, + num_non_zero_entries, + inst, + assignment_vars, + assignment_inputs, + ) = produce_tiny_r1cs(); + + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); + + // create a commitment to the R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + // produce a proof of satisfiability + let mut prover_transcript = Transcript::new(b"snark_example"); + let proof = SNARK::prove( + &inst, + &decomm, + assignment_vars, + &assignment_inputs, + &gens, + &mut prover_transcript, + ); + + // verify the proof of satisfiability + let mut verifier_transcript = Transcript::new(b"snark_example"); + assert!(proof + .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens) + .is_ok()); +# } + +# fn produce_tiny_r1cs() -> ( +# usize, +# usize, +# usize, +# usize, +# Instance, +# VarsAssignment, +# InputsAssignment, +# ) { + // We will use the following example, but one could construct any R1CS instance. + // Our R1CS instance is three constraints over five variables and two public inputs + // (Z0 + Z1) * I0 - Z2 = 0 + // (Z0 + I1) * Z2 - Z3 = 0 + // Z4 * 1 - 0 = 0 + + // parameters of the R1CS instance rounded to the nearest power of two + let num_cons = 4; + let num_vars = 8; + let num_inputs = 2; + let num_non_zero_entries = 8; + + // We will encode the above constraints into three matrices, where + // the coefficients in the matrix are in the little-endian byte order + let mut A: Vec<(usize, usize, [u8; 32])> = Vec::new(); + let mut B: Vec<(usize, usize, [u8; 32])> = Vec::new(); + let mut C: Vec<(usize, usize, [u8; 32])> = Vec::new(); + + // The constraint system is defined over a finite field, which in our case is + // the scalar field of ristreeto255/curve25519 i.e., p = 2^{252}+27742317777372353535851937790883648493 + // To construct these matrices, we will use `curve25519-dalek` but one can use any other method. + + // a variable that holds a byte representation of 1 + let one = Scalar::one().to_bytes(); + + // R1CS is a set of three sparse matrices A B C, where is a row for every + // constraint and a column for every entry in z = (vars, 1, inputs) + // An R1CS instance is satisfiable iff: + // Az \circ Bz = Cz, where z = (vars, 1, inputs) + + // constraint 0 entries in (A,B,C) + // constraint 0 is (Z0 + Z1) * I0 - Z2 = 0. + // We set 1 in matrix A for columns that correspond to Z0 and Z1 + // We set 1 in matrix B for column that corresponds to I0 + // We set 1 in matrix C for column that corresponds to Z2 + A.push((0, 0, one)); + A.push((0, 1, one)); + B.push((0, num_vars + 1, one)); + C.push((0, 2, one)); + + // constraint 1 entries in (A,B,C) + A.push((1, 0, one)); + A.push((1, num_vars + 2, one)); + B.push((1, 2, one)); + C.push((1, 3, one)); + + // constraint 3 entries in (A,B,C) + A.push((2, 4, one)); + B.push((2, num_vars, one)); + + let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap(); + + // compute a satisfying assignment + let mut csprng: OsRng = OsRng; + let i0 = Scalar::random(&mut csprng); + let i1 = Scalar::random(&mut csprng); + let z0 = Scalar::random(&mut csprng); + let z1 = Scalar::random(&mut csprng); + let z2 = (z0 + z1) * i0; // constraint 0 + let z3 = (z0 + i1) * z2; // constraint 1 + let z4 = Scalar::zero(); //constraint 2 + + // create a VarsAssignment + let mut vars = vec![Scalar::zero().to_bytes(); num_vars]; + vars[0] = z0.to_bytes(); + vars[1] = z1.to_bytes(); + vars[2] = z2.to_bytes(); + vars[3] = z3.to_bytes(); + vars[4] = z4.to_bytes(); + let assignment_vars = VarsAssignment::new(&vars).unwrap(); + + // create an InputsAssignment + let mut inputs = vec![Scalar::zero().to_bytes(); num_inputs]; + inputs[0] = i0.to_bytes(); + inputs[1] = i1.to_bytes(); + let assignment_inputs = InputsAssignment::new(&inputs).unwrap(); + + // check if the instance we created is satisfiable + let res = inst.is_sat(&assignment_vars, &assignment_inputs); + assert_eq!(res.unwrap(), true); + + ( + num_cons, + num_vars, + num_inputs, + num_non_zero_entries, + inst, + assignment_vars, + assignment_inputs, + ) +# } +``` + ## Building `libspartan` Install [`rustup`](https://rustup.rs/) diff --git a/benches/nizk.rs b/benches/nizk.rs index aec0740..42f2634 100644 --- a/benches/nizk.rs +++ b/benches/nizk.rs @@ -22,7 +22,7 @@ fn nizk_prove_benchmark(c: &mut Criterion) { let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); let gens = NIZKGens::new(num_cons, num_vars); @@ -52,7 +52,7 @@ fn nizk_verify_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); let gens = NIZKGens::new(num_cons, num_vars); diff --git a/benches/snark.rs b/benches/snark.rs index 47a91de..b0c7431 100644 --- a/benches/snark.rs +++ b/benches/snark.rs @@ -15,7 +15,7 @@ fn snark_encode_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, _vars, _inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, _vars, _inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce public parameters let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); @@ -41,7 +41,7 @@ fn snark_prove_benchmark(c: &mut Criterion) { let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce public parameters let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); @@ -77,7 +77,7 @@ fn snark_verify_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce public parameters let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); diff --git a/profiler/nizk.rs b/profiler/nizk.rs index 033327a..cb3f72d 100644 --- a/profiler/nizk.rs +++ b/profiler/nizk.rs @@ -22,7 +22,9 @@ pub fn main() { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce public generators let gens = NIZKGens::new(num_cons, num_vars); diff --git a/profiler/snark.rs b/profiler/snark.rs index 165ade4..6c1518c 100644 --- a/profiler/snark.rs +++ b/profiler/snark.rs @@ -21,7 +21,9 @@ pub fn main() { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); // produce public generators let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); diff --git a/src/errors.rs b/src/errors.rs index 69ca340..8b04c9a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -13,3 +13,28 @@ impl fmt::Debug for ProofVerifyError { write!(f, "{{ file: {}, line: {} }}", file!(), line!()) } } + +pub enum R1CSError { + /// returned if the number of constraints is not a power of 2 + NonPowerOfTwoCons, + /// returned if the number of variables is not a power of 2 + NonPowerOfTwoVars, + /// returned if a wrong number of inputs in an assignment are supplied + InvalidNumberOfInputs, + /// returned if a wrong number of variables in an assignment are supplied + InvalidNumberOfVars, + /// returned if a [u8;32] does not parse into a valid Scalar in the field of ristretto255 + InvalidScalar, +} + +impl fmt::Display for R1CSError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "R1CSError") + } +} + +impl fmt::Debug for R1CSError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{{ file: {}, line: {} }}", file!(), line!()) + } +} diff --git a/src/lib.rs b/src/lib.rs index e36ea53..a090d38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ mod timer; mod transcript; mod unipoly; -use errors::ProofVerifyError; +use errors::{ProofVerifyError, R1CSError}; use merlin::Transcript; use r1csinstance::{ R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, @@ -53,6 +53,47 @@ pub struct ComputationDecommitment { decomm: R1CSDecommitment, } +/// `Assignment` holds an assignment of values to either the inputs or variables in an `Instance` +#[derive(Clone)] +pub struct Assignment { + assignment: Vec, +} + +impl Assignment { + /// Constructs a new `Assignment` from a vector + pub fn new(assignment: &Vec<[u8; 32]>) -> Result { + let bytes_to_scalar = |vec: &Vec<[u8; 32]>| -> Result, R1CSError> { + let mut vec_scalar: Vec = Vec::new(); + for i in 0..vec.len() { + let val = Scalar::from_bytes(&vec[i]); + if val.is_some().unwrap_u8() == 1 { + vec_scalar.push(val.unwrap()); + } else { + return Err(R1CSError::InvalidScalar); + } + } + Ok(vec_scalar) + }; + + let assignment_scalar = bytes_to_scalar(assignment); + + // check for any parsing errors + if assignment_scalar.is_err() { + return Err(R1CSError::InvalidScalar); + } + + Ok(Assignment { + assignment: assignment_scalar.unwrap(), + }) + } +} + +/// `VarsAssignment` holds an assignment of values to variables in an `Instance` +pub type VarsAssignment = Assignment; + +/// `VarsAssignment` holds an assignment of values to variables in an `Instance` +pub type InputsAssignment = Assignment; + /// `Instance` holds the description of R1CS matrices pub struct Instance { inst: R1CSInstance, @@ -64,9 +105,87 @@ impl Instance { num_cons: usize, num_vars: usize, num_inputs: usize, - ) -> (Self, Vec, Vec) { + A: &Vec<(usize, usize, [u8; 32])>, + B: &Vec<(usize, usize, [u8; 32])>, + C: &Vec<(usize, usize, [u8; 32])>, + ) -> Result { + // check that num_cons is power of 2 + if num_cons.next_power_of_two() != num_cons { + return Err(R1CSError::NonPowerOfTwoCons); + } + + // check that the number of variables is a power of 2 + if num_vars.next_power_of_two() != num_vars { + return Err(R1CSError::NonPowerOfTwoVars); + } + + // check that num_inputs + 1 <= num_vars + if num_inputs >= num_vars { + return Err(R1CSError::InvalidNumberOfInputs); + } + + let bytes_to_scalar = + |tups: &Vec<(usize, usize, [u8; 32])>| -> Result, R1CSError> { + let mut mat: Vec<(usize, usize, Scalar)> = Vec::new(); + for i in 0..tups.len() { + let (row, col, val_bytes) = tups[i]; + let val = Scalar::from_bytes(&val_bytes); + if val.is_some().unwrap_u8() == 1 { + mat.push((row, col, val.unwrap())); + } else { + return Err(R1CSError::InvalidScalar); + } + } + Ok(mat) + }; + + let A_scalar = bytes_to_scalar(A); + let B_scalar = bytes_to_scalar(B); + let C_scalar = bytes_to_scalar(C); + + // check for any parsing errors + if A_scalar.is_err() || B_scalar.is_err() || C_scalar.is_err() { + return Err(R1CSError::InvalidScalar); + } + + let inst = R1CSInstance::new( + num_cons, + num_vars, + num_inputs, + &A_scalar.unwrap(), + &B_scalar.unwrap(), + &C_scalar.unwrap(), + ); + + Ok(Instance { inst }) + } + + /// Checks if a given R1CSInstance is satisfiable with a given variables and inputs assignments + pub fn is_sat(&self, vars: &VarsAssignment, inputs: &InputsAssignment) -> Result { + + if vars.assignment.len() != self.inst.get_num_vars() { + return Err(R1CSError::InvalidNumberOfVars) + } + + if inputs.assignment.len() != self.inst.get_num_inputs() { + return Err(R1CSError::InvalidNumberOfInputs) + } + + Ok(self.inst.is_sat(&vars.assignment, &inputs.assignment)) + } + + /// Constructs a new synthetic R1CS `Instance` and an associated satisfying assignment + pub fn produce_synthetic_r1cs( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + ) -> (Instance, VarsAssignment, InputsAssignment) { let (inst, vars, inputs) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - (Instance { inst }, vars, inputs) + ( + Instance { inst }, + VarsAssignment { assignment: vars }, + InputsAssignment { assignment: inputs }, + ) } } @@ -125,8 +244,8 @@ impl SNARK { pub fn prove( inst: &Instance, decomm: &ComputationDecommitment, - vars: Vec, - input: &[Scalar], + vars: VarsAssignment, + inputs: &InputsAssignment, gens: &SNARKGens, transcript: &mut Transcript, ) -> Self { @@ -139,8 +258,8 @@ impl SNARK { let (r1cs_sat_proof, rx, ry) = { let (proof, rx, ry) = R1CSProof::prove( &inst.inst, - vars, - input, + vars.assignment, + &inputs.assignment, &gens.gens_r1cs_sat, transcript, &mut random_tape, @@ -191,7 +310,7 @@ impl SNARK { pub fn verify( &self, comm: &ComputationCommitment, - input: &[Scalar], + input: &InputsAssignment, transcript: &mut Transcript, gens: &SNARKGens, ) -> Result<(), ProofVerifyError> { @@ -199,13 +318,13 @@ impl SNARK { transcript.append_protocol_name(SNARK::protocol_name()); let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.len(), comm.comm.get_num_inputs()); + assert_eq!(input.assignment.len(), comm.comm.get_num_inputs()); let (rx, ry) = self .r1cs_sat_proof .verify( comm.comm.get_num_vars(), comm.comm.get_num_cons(), - input, + &input.assignment, &self.inst_evals, transcript, &gens.gens_r1cs_sat, @@ -263,8 +382,8 @@ impl NIZK { /// A method to produce a NIZK proof of the satisfiability of an R1CS instance pub fn prove( inst: &Instance, - vars: Vec, - input: &[Scalar], + vars: VarsAssignment, + input: &InputsAssignment, gens: &NIZKGens, transcript: &mut Transcript, ) -> Self { @@ -276,8 +395,8 @@ impl NIZK { let (r1cs_sat_proof, rx, ry) = { let (proof, rx, ry) = R1CSProof::prove( &inst.inst, - vars, - input, + vars.assignment, + &input.assignment, &gens.gens_r1cs_sat, transcript, &mut random_tape, @@ -298,7 +417,7 @@ impl NIZK { pub fn verify( &self, inst: &Instance, - input: &[Scalar], + input: &InputsAssignment, transcript: &mut Transcript, gens: &NIZKGens, ) -> Result<(), ProofVerifyError> { @@ -314,13 +433,13 @@ impl NIZK { timer_eval.stop(); let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.len(), inst.inst.get_num_inputs()); + assert_eq!(input.assignment.len(), inst.inst.get_num_inputs()); let (rx, ry) = self .r1cs_sat_proof .verify( inst.inst.get_num_vars(), inst.inst.get_num_cons(), - input, + &input.assignment, &inst_evals, transcript, &gens.gens_r1cs_sat, @@ -346,11 +465,13 @@ mod tests { let num_vars = 256; let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public generators let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + // produce a synthetic R1CSInstance + let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + // create a commitment to R1CSInstance let (comm, decomm) = SNARK::encode(&inst, &gens); diff --git a/src/nizk/bullet.rs b/src/nizk/bullet.rs index 0224932..d33f878 100644 --- a/src/nizk/bullet.rs +++ b/src/nizk/bullet.rs @@ -1,4 +1,4 @@ -//! This module is an adaptation of code from the bulletproofs crate. +//! This module is an adaptation of code from the bulletproofs crate. //! See NOTICE.md for more details #![allow(non_snake_case)] #![allow(clippy::type_complexity)] diff --git a/src/r1csinstance.rs b/src/r1csinstance.rs index 40b6f59..c0bd21c 100644 --- a/src/r1csinstance.rs +++ b/src/r1csinstance.rs @@ -73,18 +73,54 @@ impl R1CSInstance { num_cons: usize, num_vars: usize, num_inputs: usize, - A: SparseMatPolynomial, - B: SparseMatPolynomial, - C: SparseMatPolynomial, - ) -> Self { - R1CSInstance { + A: &Vec<(usize, usize, Scalar)>, + B: &Vec<(usize, usize, Scalar)>, + C: &Vec<(usize, usize, Scalar)>, + ) -> R1CSInstance { + Timer::print(&format!("number_of_constraints {}", num_cons)); + Timer::print(&format!("number_of_variables {}", num_vars)); + Timer::print(&format!("number_of_inputs {}", num_inputs)); + Timer::print(&format!("number_non-zero_entries_A {}", A.len())); + Timer::print(&format!("number_non-zero_entries_B {}", B.len())); + Timer::print(&format!("number_non-zero_entries_C {}", C.len())); + + // check that num_cons is a power of 2 + assert_eq!(num_cons.next_power_of_two(), num_cons); + + // check that num_vars is a power of 2 + assert_eq!(num_vars.next_power_of_two(), num_vars); + + // check that number_inputs + 1 <= num_vars + assert!(num_inputs < num_vars); + + // no errors, so create polynomials + let num_poly_vars_x = num_cons.log2(); + let num_poly_vars_y = (2 * num_vars).log2(); + + let mat_A = (0..A.len()) + .map(|i| SparseMatEntry::new(A[i].0, A[i].1, A[i].2)) + .collect::>(); + let mat_B = (0..B.len()) + .map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2)) + .collect::>(); + let mat_C = (0..C.len()) + .map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2)) + .collect::>(); + + let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_A); + let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_B); + let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, mat_C); + + let inst = R1CSInstance { num_cons, num_vars, num_inputs, - A, - B, - C, - } + A: poly_A, + B: poly_B, + C: poly_C, + }; + + inst } pub fn get_num_vars(&self) -> usize { @@ -165,7 +201,14 @@ impl R1CSInstance { let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, B); let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, C); - let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, poly_A, poly_B, poly_C); + let inst = R1CSInstance { + num_cons, + num_vars, + num_inputs, + A: poly_A, + B: poly_B, + C: poly_C, + }; assert_eq!( inst.is_sat(&Z[0..num_vars].to_vec(), &Z[num_vars + 1..].to_vec()), @@ -245,8 +288,6 @@ impl R1CSInstance { } pub fn commit(&self, gens: &R1CSCommitmentGens) -> (R1CSCommitment, R1CSDecommitment) { - assert_eq!(self.A.get_num_nz_entries(), self.B.get_num_nz_entries()); - assert_eq!(self.A.get_num_nz_entries(), self.C.get_num_nz_entries()); let (comm, dense) = SparseMatPolynomial::multi_commit(&[&self.A, &self.B, &self.C], &gens.gens); let r1cs_comm = R1CSCommitment { num_cons: self.num_cons, diff --git a/src/r1csproof.rs b/src/r1csproof.rs index 71c1c43..1fbecef 100644 --- a/src/r1csproof.rs +++ b/src/r1csproof.rs @@ -18,9 +18,6 @@ use core::iter; use merlin::Transcript; use serde::{Deserialize, Serialize}; -#[cfg(test)] -use super::sparse_mlpoly::{SparseMatEntry, SparseMatPolynomial}; - #[derive(Serialize, Deserialize, Debug)] pub struct R1CSProof { comm_vars: PolyCommitment, @@ -520,37 +517,30 @@ mod tests { let num_inputs = 2; // encode the above constraints into three matrices - let mut A: Vec = Vec::new(); - let mut B: Vec = Vec::new(); - let mut C: Vec = Vec::new(); + let mut A: Vec<(usize, usize, Scalar)> = Vec::new(); + let mut B: Vec<(usize, usize, Scalar)> = Vec::new(); + let mut C: Vec<(usize, usize, Scalar)> = Vec::new(); let one = Scalar::one(); // constraint 0 entries // (Z1 + Z2) * I0 - Z3 = 0; - A.push(SparseMatEntry::new(0, 0, one)); - A.push(SparseMatEntry::new(0, 1, one)); - B.push(SparseMatEntry::new(0, num_vars + 1, one)); - C.push(SparseMatEntry::new(0, 2, one)); + A.push((0, 0, one)); + A.push((0, 1, one)); + B.push((0, num_vars + 1, one)); + C.push((0, 2, one)); // constraint 1 entries // (Z1 + I1) * (Z3) - Z4 = 0 - A.push(SparseMatEntry::new(1, 0, one)); - A.push(SparseMatEntry::new(1, num_vars + 2, one)); - B.push(SparseMatEntry::new(1, 2, one)); - C.push(SparseMatEntry::new(1, 3, one)); + A.push((1, 0, one)); + A.push((1, num_vars + 2, one)); + B.push((1, 2, one)); + C.push((1, 3, one)); // constraint 3 entries // Z5 * 1 - 0 = 0 - A.push(SparseMatEntry::new(2, 4, one)); - B.push(SparseMatEntry::new(2, num_vars, one)); - - let num_vars_x = num_cons.log2(); - let num_vars_y = (2 * num_vars).log2(); + A.push((2, 4, one)); + B.push((2, num_vars, one)); - let poly_A = SparseMatPolynomial::new(num_vars_x, num_vars_y, A); - let poly_B = SparseMatPolynomial::new(num_vars_x, num_vars_y, B); - let poly_C = SparseMatPolynomial::new(num_vars_x, num_vars_y, C); - - let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, poly_A, poly_B, poly_C); + let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, &A, &B, &C); // compute a satisfying assignment let mut csprng: OsRng = OsRng; @@ -611,7 +601,7 @@ mod tests { ); let inst_evals = inst.evaluate(&rx, &ry); - + let mut verifier_transcript = Transcript::new(b"example"); assert!(proof .verify( diff --git a/src/scalar/ristretto255.rs b/src/scalar/ristretto255.rs index 8a20b02..5696917 100755 --- a/src/scalar/ristretto255.rs +++ b/src/scalar/ristretto255.rs @@ -1,6 +1,6 @@ //! This module provides an implementation of the Curve25519's scalar field $\mathbb{F}_q$ //! where `q = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed` -//! This module is an adaptation of code from the bls12-381 crate. +//! This module is an adaptation of code from the bls12-381 crate. //! We modify various constants (MODULUS, R, R2, etc.) to appropriate values for Curve25519 and update tests //! We borrow the `invert` method from the curve25519-dalek crate. //! See NOTICE.md for more details diff --git a/src/sparse_mlpoly.rs b/src/sparse_mlpoly.rs index 3c96f0a..d242757 100644 --- a/src/sparse_mlpoly.rs +++ b/src/sparse_mlpoly.rs @@ -371,7 +371,8 @@ impl SparseMatPolynomial { let N = (0..sparse_polys.len()) .map(|i| sparse_polys[i].get_num_nz_entries()) .max() - .unwrap(); + .unwrap() + .next_power_of_two(); let mut ops_row_vec: Vec> = Vec::new(); let mut ops_col_vec: Vec> = Vec::new();