Browse Source

Add APIs to specify an R1CS Instance (#24)

master
Srinath Setty 4 years ago
committed by GitHub
parent
commit
2d22bff71f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 398 additions and 77 deletions
  1. +151
    -12
      README.md
  2. +2
    -2
      benches/nizk.rs
  3. +3
    -3
      benches/snark.rs
  4. +3
    -1
      profiler/nizk.rs
  5. +3
    -1
      profiler/snark.rs
  6. +25
    -0
      src/errors.rs
  7. +139
    -18
      src/lib.rs
  8. +1
    -1
      src/nizk/bullet.rs
  9. +53
    -12
      src/r1csinstance.rs
  10. +15
    -25
      src/r1csproof.rs
  11. +1
    -1
      src/scalar/ristretto255.rs
  12. +2
    -1
      src/sparse_mlpoly.rs

+ 151
- 12
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. * **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`. * **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:** * **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 ### 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. `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); let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries);
// ask the library to produce a synthentic R1CS instance // 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 // create a commitment to the R1CS instance
let (comm, decomm) = SNARK::encode(&inst, &gens); 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); let gens = NIZKGens::new(num_cons, num_vars);
// ask the library to produce a synthentic R1CS instance // 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 // produce a proof of satisfiability
let mut prover_transcript = Transcript::new(b"nizk_example"); 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` ## Building `libspartan`
Install [`rustup`](https://rustup.rs/) Install [`rustup`](https://rustup.rs/)

+ 2
- 2
benches/nizk.rs

@ -22,7 +22,7 @@ fn nizk_prove_benchmark(c: &mut Criterion) {
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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); 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_vars = (2 as usize).pow(s as u32);
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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); let gens = NIZKGens::new(num_cons, num_vars);

+ 3
- 3
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_vars = (2 as usize).pow(s as u32);
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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 // produce public parameters
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); 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_cons = num_vars;
let num_inputs = 10; 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 // produce public parameters
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); 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_vars = (2 as usize).pow(s as u32);
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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 // produce public parameters
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons);

+ 3
- 1
profiler/nizk.rs

@ -22,7 +22,9 @@ pub fn main() {
let num_vars = (2 as usize).pow(s as u32); let num_vars = (2 as usize).pow(s as u32);
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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 // produce public generators
let gens = NIZKGens::new(num_cons, num_vars); let gens = NIZKGens::new(num_cons, num_vars);

+ 3
- 1
profiler/snark.rs

@ -21,7 +21,9 @@ pub fn main() {
let num_vars = (2 as usize).pow(s as u32); let num_vars = (2 as usize).pow(s as u32);
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; 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 // produce public generators
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons);

+ 25
- 0
src/errors.rs

@ -13,3 +13,28 @@ impl fmt::Debug for ProofVerifyError {
write!(f, "{{ file: {}, line: {} }}", file!(), line!()) 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!())
}
}

+ 139
- 18
src/lib.rs

@ -31,7 +31,7 @@ mod timer;
mod transcript; mod transcript;
mod unipoly; mod unipoly;
use errors::ProofVerifyError;
use errors::{ProofVerifyError, R1CSError};
use merlin::Transcript; use merlin::Transcript;
use r1csinstance::{ use r1csinstance::{
R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance,
@ -53,6 +53,47 @@ pub struct ComputationDecommitment {
decomm: R1CSDecommitment, decomm: R1CSDecommitment,
} }
/// `Assignment` holds an assignment of values to either the inputs or variables in an `Instance`
#[derive(Clone)]
pub struct Assignment {
assignment: Vec<Scalar>,
}
impl Assignment {
/// Constructs a new `Assignment` from a vector
pub fn new(assignment: &Vec<[u8; 32]>) -> Result<Assignment, R1CSError> {
let bytes_to_scalar = |vec: &Vec<[u8; 32]>| -> Result<Vec<Scalar>, R1CSError> {
let mut vec_scalar: Vec<Scalar> = 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 /// `Instance` holds the description of R1CS matrices
pub struct Instance { pub struct Instance {
inst: R1CSInstance, inst: R1CSInstance,
@ -64,9 +105,87 @@ impl Instance {
num_cons: usize, num_cons: usize,
num_vars: usize, num_vars: usize,
num_inputs: usize, num_inputs: usize,
) -> (Self, Vec<Scalar>, Vec<Scalar>) {
A: &Vec<(usize, usize, [u8; 32])>,
B: &Vec<(usize, usize, [u8; 32])>,
C: &Vec<(usize, usize, [u8; 32])>,
) -> Result<Instance, R1CSError> {
// 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<Vec<(usize, usize, Scalar)>, 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<bool, R1CSError> {
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); 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( pub fn prove(
inst: &Instance, inst: &Instance,
decomm: &ComputationDecommitment, decomm: &ComputationDecommitment,
vars: Vec<Scalar>,
input: &[Scalar],
vars: VarsAssignment,
inputs: &InputsAssignment,
gens: &SNARKGens, gens: &SNARKGens,
transcript: &mut Transcript, transcript: &mut Transcript,
) -> Self { ) -> Self {
@ -139,8 +258,8 @@ impl SNARK {
let (r1cs_sat_proof, rx, ry) = { let (r1cs_sat_proof, rx, ry) = {
let (proof, rx, ry) = R1CSProof::prove( let (proof, rx, ry) = R1CSProof::prove(
&inst.inst, &inst.inst,
vars,
input,
vars.assignment,
&inputs.assignment,
&gens.gens_r1cs_sat, &gens.gens_r1cs_sat,
transcript, transcript,
&mut random_tape, &mut random_tape,
@ -191,7 +310,7 @@ impl SNARK {
pub fn verify( pub fn verify(
&self, &self,
comm: &ComputationCommitment, comm: &ComputationCommitment,
input: &[Scalar],
input: &InputsAssignment,
transcript: &mut Transcript, transcript: &mut Transcript,
gens: &SNARKGens, gens: &SNARKGens,
) -> Result<(), ProofVerifyError> { ) -> Result<(), ProofVerifyError> {
@ -199,13 +318,13 @@ impl SNARK {
transcript.append_protocol_name(SNARK::protocol_name()); transcript.append_protocol_name(SNARK::protocol_name());
let timer_sat_proof = Timer::new("verify_sat_proof"); 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 let (rx, ry) = self
.r1cs_sat_proof .r1cs_sat_proof
.verify( .verify(
comm.comm.get_num_vars(), comm.comm.get_num_vars(),
comm.comm.get_num_cons(), comm.comm.get_num_cons(),
input,
&input.assignment,
&self.inst_evals, &self.inst_evals,
transcript, transcript,
&gens.gens_r1cs_sat, &gens.gens_r1cs_sat,
@ -263,8 +382,8 @@ impl NIZK {
/// A method to produce a NIZK proof of the satisfiability of an R1CS instance /// A method to produce a NIZK proof of the satisfiability of an R1CS instance
pub fn prove( pub fn prove(
inst: &Instance, inst: &Instance,
vars: Vec<Scalar>,
input: &[Scalar],
vars: VarsAssignment,
input: &InputsAssignment,
gens: &NIZKGens, gens: &NIZKGens,
transcript: &mut Transcript, transcript: &mut Transcript,
) -> Self { ) -> Self {
@ -276,8 +395,8 @@ impl NIZK {
let (r1cs_sat_proof, rx, ry) = { let (r1cs_sat_proof, rx, ry) = {
let (proof, rx, ry) = R1CSProof::prove( let (proof, rx, ry) = R1CSProof::prove(
&inst.inst, &inst.inst,
vars,
input,
vars.assignment,
&input.assignment,
&gens.gens_r1cs_sat, &gens.gens_r1cs_sat,
transcript, transcript,
&mut random_tape, &mut random_tape,
@ -298,7 +417,7 @@ impl NIZK {
pub fn verify( pub fn verify(
&self, &self,
inst: &Instance, inst: &Instance,
input: &[Scalar],
input: &InputsAssignment,
transcript: &mut Transcript, transcript: &mut Transcript,
gens: &NIZKGens, gens: &NIZKGens,
) -> Result<(), ProofVerifyError> { ) -> Result<(), ProofVerifyError> {
@ -314,13 +433,13 @@ impl NIZK {
timer_eval.stop(); timer_eval.stop();
let timer_sat_proof = Timer::new("verify_sat_proof"); 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 let (rx, ry) = self
.r1cs_sat_proof .r1cs_sat_proof
.verify( .verify(
inst.inst.get_num_vars(), inst.inst.get_num_vars(),
inst.inst.get_num_cons(), inst.inst.get_num_cons(),
input,
&input.assignment,
&inst_evals, &inst_evals,
transcript, transcript,
&gens.gens_r1cs_sat, &gens.gens_r1cs_sat,
@ -346,11 +465,13 @@ mod tests {
let num_vars = 256; let num_vars = 256;
let num_cons = num_vars; let num_cons = num_vars;
let num_inputs = 10; let num_inputs = 10;
let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs);
// produce public generators // produce public generators
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); 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 // create a commitment to R1CSInstance
let (comm, decomm) = SNARK::encode(&inst, &gens); let (comm, decomm) = SNARK::encode(&inst, &gens);

+ 1
- 1
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 //! See NOTICE.md for more details
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]

+ 53
- 12
src/r1csinstance.rs

@ -73,18 +73,54 @@ impl R1CSInstance {
num_cons: usize, num_cons: usize,
num_vars: usize, num_vars: usize,
num_inputs: 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::<Vec<SparseMatEntry>>();
let mat_B = (0..B.len())
.map(|i| SparseMatEntry::new(B[i].0, B[i].1, B[i].2))
.collect::<Vec<SparseMatEntry>>();
let mat_C = (0..C.len())
.map(|i| SparseMatEntry::new(C[i].0, C[i].1, C[i].2))
.collect::<Vec<SparseMatEntry>>();
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_cons,
num_vars, num_vars,
num_inputs, num_inputs,
A,
B,
C,
}
A: poly_A,
B: poly_B,
C: poly_C,
};
inst
} }
pub fn get_num_vars(&self) -> usize { 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_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 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!( assert_eq!(
inst.is_sat(&Z[0..num_vars].to_vec(), &Z[num_vars + 1..].to_vec()), 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) { 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 (comm, dense) = SparseMatPolynomial::multi_commit(&[&self.A, &self.B, &self.C], &gens.gens);
let r1cs_comm = R1CSCommitment { let r1cs_comm = R1CSCommitment {
num_cons: self.num_cons, num_cons: self.num_cons,

+ 15
- 25
src/r1csproof.rs

@ -18,9 +18,6 @@ use core::iter;
use merlin::Transcript; use merlin::Transcript;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
use super::sparse_mlpoly::{SparseMatEntry, SparseMatPolynomial};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct R1CSProof { pub struct R1CSProof {
comm_vars: PolyCommitment, comm_vars: PolyCommitment,
@ -520,37 +517,30 @@ mod tests {
let num_inputs = 2; let num_inputs = 2;
// encode the above constraints into three matrices // encode the above constraints into three matrices
let mut A: Vec<SparseMatEntry> = Vec::new();
let mut B: Vec<SparseMatEntry> = Vec::new();
let mut C: Vec<SparseMatEntry> = 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(); let one = Scalar::one();
// constraint 0 entries // constraint 0 entries
// (Z1 + Z2) * I0 - Z3 = 0; // (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 // constraint 1 entries
// (Z1 + I1) * (Z3) - Z4 = 0 // (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 // constraint 3 entries
// Z5 * 1 - 0 = 0 // 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 // compute a satisfying assignment
let mut csprng: OsRng = OsRng; let mut csprng: OsRng = OsRng;
@ -611,7 +601,7 @@ mod tests {
); );
let inst_evals = inst.evaluate(&rx, &ry); let inst_evals = inst.evaluate(&rx, &ry);
let mut verifier_transcript = Transcript::new(b"example"); let mut verifier_transcript = Transcript::new(b"example");
assert!(proof assert!(proof
.verify( .verify(

+ 1
- 1
src/scalar/ristretto255.rs

@ -1,6 +1,6 @@
//! This module provides an implementation of the Curve25519's scalar field $\mathbb{F}_q$ //! This module provides an implementation of the Curve25519's scalar field $\mathbb{F}_q$
//! where `q = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed` //! 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 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. //! We borrow the `invert` method from the curve25519-dalek crate.
//! See NOTICE.md for more details //! See NOTICE.md for more details

+ 2
- 1
src/sparse_mlpoly.rs

@ -371,7 +371,8 @@ impl SparseMatPolynomial {
let N = (0..sparse_polys.len()) let N = (0..sparse_polys.len())
.map(|i| sparse_polys[i].get_num_nz_entries()) .map(|i| sparse_polys[i].get_num_nz_entries())
.max() .max()
.unwrap();
.unwrap()
.next_power_of_two();
let mut ops_row_vec: Vec<Vec<usize>> = Vec::new(); let mut ops_row_vec: Vec<Vec<usize>> = Vec::new();
let mut ops_col_vec: Vec<Vec<usize>> = Vec::new(); let mut ops_col_vec: Vec<Vec<usize>> = Vec::new();

Loading…
Cancel
Save