mirror of
https://github.com/arnaucube/testudo.git
synced 2026-01-13 01:01:28 +01:00
Add APIs to specify an R1CS Instance (#24)
This commit is contained in:
@@ -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!())
|
||||
}
|
||||
}
|
||||
|
||||
157
src/lib.rs
157
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,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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user