Add APIs to specify an R1CS Instance (#24)

This commit is contained in:
Srinath Setty
2020-08-31 14:11:42 -07:00
committed by GitHub
parent 131fad938c
commit 2d22bff71f
12 changed files with 398 additions and 77 deletions

View File

@@ -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!())
}
}

View File

@@ -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);

View File

@@ -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)]

View File

@@ -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,

View File

@@ -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));
A.push((2, 4, one));
B.push((2, num_vars, one));
let num_vars_x = num_cons.log2();
let num_vars_y = (2 * num_vars).log2();
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(

View File

@@ -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

View File

@@ -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();