Browse Source

Add APIs to specify an R1CS Instance (#24)

master
Srinath Setty 3 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.
* **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/)

+ 2
- 2
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);

+ 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_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);

+ 3
- 1
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);

+ 3
- 1
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);

+ 25
- 0
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!())
}
}

+ 139
- 18
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<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
pub struct Instance {
inst: R1CSInstance,
@ -64,9 +105,87 @@ impl Instance {
num_cons: usize,
num_vars: 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);
(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<Scalar>,
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<Scalar>,
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);

+ 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
#![allow(non_snake_case)]
#![allow(clippy::type_complexity)]

+ 53
- 12
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::<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_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,

+ 15
- 25
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<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();
// 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(

+ 1
- 1
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

+ 2
- 1
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<usize>> = Vec::new();
let mut ops_col_vec: Vec<Vec<usize>> = Vec::new();

Loading…
Cancel
Save