#![allow(non_snake_case)]
|
|
#![feature(test)]
|
|
#![deny(missing_docs)]
|
|
#![feature(external_doc)]
|
|
#![doc(include = "../README.md")]
|
|
|
|
extern crate byteorder;
|
|
extern crate core;
|
|
extern crate curve25519_dalek;
|
|
extern crate digest;
|
|
extern crate merlin;
|
|
extern crate rand;
|
|
extern crate rayon;
|
|
extern crate sha3;
|
|
extern crate test;
|
|
|
|
mod commitments;
|
|
mod dense_mlpoly;
|
|
mod errors;
|
|
mod group;
|
|
mod math;
|
|
mod nizk;
|
|
mod product_tree;
|
|
mod r1csinstance;
|
|
mod r1csproof;
|
|
mod random;
|
|
mod scalar;
|
|
mod sparse_mlpoly;
|
|
mod sumcheck;
|
|
mod timer;
|
|
mod transcript;
|
|
mod unipoly;
|
|
|
|
use errors::ProofVerifyError;
|
|
use merlin::Transcript;
|
|
use r1csinstance::{
|
|
R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance,
|
|
};
|
|
use r1csproof::{R1CSGens, R1CSProof};
|
|
use random::RandomTape;
|
|
use scalar::Scalar;
|
|
use serde::{Deserialize, Serialize};
|
|
use timer::Timer;
|
|
use transcript::{AppendToTranscript, ProofTranscript};
|
|
|
|
/// `ComputationCommitment` holds a public preprocessed NP statement (e.g., R1CS)
|
|
pub struct ComputationCommitment {
|
|
comm: R1CSCommitment,
|
|
}
|
|
|
|
/// `ComputationDecommitment` holds information to decommit `ComputationCommitment`
|
|
pub struct ComputationDecommitment {
|
|
decomm: R1CSDecommitment,
|
|
}
|
|
|
|
/// `Instance` holds the description of R1CS matrices
|
|
pub struct Instance {
|
|
inst: R1CSInstance,
|
|
}
|
|
|
|
impl Instance {
|
|
/// Constructs a new `Instance` and an associated satisfying assignment
|
|
pub fn new(
|
|
num_cons: usize,
|
|
num_vars: usize,
|
|
num_inputs: usize,
|
|
) -> (Self, Vec<Scalar>, Vec<Scalar>) {
|
|
let (inst, vars, inputs) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
|
|
(Instance { inst }, vars, inputs)
|
|
}
|
|
}
|
|
|
|
/// `SNARKGens` holds public parameters for producing and verifying proofs with the Spartan SNARK
|
|
pub struct SNARKGens {
|
|
gens_r1cs_sat: R1CSGens,
|
|
gens_r1cs_eval: R1CSCommitmentGens,
|
|
}
|
|
|
|
impl SNARKGens {
|
|
/// Constructs a new `SNARKGens` given the size of the R1CS statement
|
|
pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize, num_nz_entries: usize) -> Self {
|
|
let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars);
|
|
let gens_r1cs_eval = R1CSCommitmentGens::new(
|
|
b"gens_r1cs_eval",
|
|
num_cons,
|
|
num_vars,
|
|
num_inputs,
|
|
num_nz_entries,
|
|
);
|
|
SNARKGens {
|
|
gens_r1cs_sat,
|
|
gens_r1cs_eval,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// `SNARK` holds a proof produced by Spartan SNARK
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct SNARK {
|
|
r1cs_sat_proof: R1CSProof,
|
|
inst_evals: (Scalar, Scalar, Scalar),
|
|
r1cs_eval_proof: R1CSEvalProof,
|
|
}
|
|
|
|
impl SNARK {
|
|
fn protocol_name() -> &'static [u8] {
|
|
b"Spartan SNARK proof"
|
|
}
|
|
|
|
/// A public computation to create a commitment to an R1CS instance
|
|
pub fn encode(
|
|
inst: &Instance,
|
|
gens: &SNARKGens,
|
|
) -> (ComputationCommitment, ComputationDecommitment) {
|
|
let timer_encode = Timer::new("SNARK::encode");
|
|
let (comm, decomm) = inst.inst.commit(&gens.gens_r1cs_eval);
|
|
timer_encode.stop();
|
|
(
|
|
ComputationCommitment { comm },
|
|
ComputationDecommitment { decomm },
|
|
)
|
|
}
|
|
|
|
/// A method to produce a SNARK proof of the satisfiability of an R1CS instance
|
|
pub fn prove(
|
|
inst: &Instance,
|
|
decomm: &ComputationDecommitment,
|
|
vars: Vec<Scalar>,
|
|
input: &[Scalar],
|
|
gens: &SNARKGens,
|
|
transcript: &mut Transcript,
|
|
) -> Self {
|
|
let timer_prove = Timer::new("SNARK::prove");
|
|
|
|
// we create a Transcript object seeded with a random Scalar
|
|
// to aid the prover produce its randomness
|
|
let mut random_tape = RandomTape::new(b"proof");
|
|
transcript.append_protocol_name(SNARK::protocol_name());
|
|
let (r1cs_sat_proof, rx, ry) = {
|
|
let (proof, rx, ry) = R1CSProof::prove(
|
|
&inst.inst,
|
|
vars,
|
|
input,
|
|
&gens.gens_r1cs_sat,
|
|
transcript,
|
|
&mut random_tape,
|
|
);
|
|
let proof_encoded: Vec<u8> = bincode::serialize(&proof).unwrap();
|
|
Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len()));
|
|
|
|
(proof, rx, ry)
|
|
};
|
|
|
|
// We send evaluations of A, B, C at r = (rx, ry) as claims
|
|
// to enable the verifier complete the first sum-check
|
|
let timer_eval = Timer::new("eval_sparse_polys");
|
|
let inst_evals = {
|
|
let (Ar, Br, Cr) = inst.inst.evaluate(&rx, &ry);
|
|
Ar.append_to_transcript(b"Ar_claim", transcript);
|
|
Br.append_to_transcript(b"Br_claim", transcript);
|
|
Cr.append_to_transcript(b"Cr_claim", transcript);
|
|
(Ar, Br, Cr)
|
|
};
|
|
timer_eval.stop();
|
|
|
|
let r1cs_eval_proof = {
|
|
let proof = R1CSEvalProof::prove(
|
|
&decomm.decomm,
|
|
&rx,
|
|
&ry,
|
|
&inst_evals,
|
|
&gens.gens_r1cs_eval,
|
|
transcript,
|
|
&mut random_tape,
|
|
);
|
|
|
|
let proof_encoded: Vec<u8> = bincode::serialize(&proof).unwrap();
|
|
Timer::print(&format!("len_r1cs_eval_proof {:?}", proof_encoded.len()));
|
|
proof
|
|
};
|
|
|
|
timer_prove.stop();
|
|
SNARK {
|
|
r1cs_sat_proof,
|
|
inst_evals,
|
|
r1cs_eval_proof,
|
|
}
|
|
}
|
|
|
|
/// A method to verify the SNARK proof of the satisfiability of an R1CS instance
|
|
pub fn verify(
|
|
&self,
|
|
comm: &ComputationCommitment,
|
|
input: &[Scalar],
|
|
transcript: &mut Transcript,
|
|
gens: &SNARKGens,
|
|
) -> Result<(), ProofVerifyError> {
|
|
let timer_verify = Timer::new("SNARK::verify");
|
|
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());
|
|
let (rx, ry) = self
|
|
.r1cs_sat_proof
|
|
.verify(
|
|
comm.comm.get_num_vars(),
|
|
comm.comm.get_num_cons(),
|
|
input,
|
|
&self.inst_evals,
|
|
transcript,
|
|
&gens.gens_r1cs_sat,
|
|
)
|
|
.unwrap();
|
|
timer_sat_proof.stop();
|
|
|
|
let timer_eval_proof = Timer::new("verify_eval_proof");
|
|
let (Ar, Br, Cr) = &self.inst_evals;
|
|
Ar.append_to_transcript(b"Ar_claim", transcript);
|
|
Br.append_to_transcript(b"Br_claim", transcript);
|
|
Cr.append_to_transcript(b"Cr_claim", transcript);
|
|
assert!(self
|
|
.r1cs_eval_proof
|
|
.verify(
|
|
&comm.comm,
|
|
&rx,
|
|
&ry,
|
|
&self.inst_evals,
|
|
&gens.gens_r1cs_eval,
|
|
transcript
|
|
)
|
|
.is_ok());
|
|
timer_eval_proof.stop();
|
|
timer_verify.stop();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// `NIZKGens` holds public parameters for producing and verifying proofs with the Spartan NIZK
|
|
pub struct NIZKGens {
|
|
gens_r1cs_sat: R1CSGens,
|
|
}
|
|
|
|
impl NIZKGens {
|
|
/// Constructs a new `NIZKGens` given the size of the R1CS statement
|
|
pub fn new(num_cons: usize, num_vars: usize) -> Self {
|
|
let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars);
|
|
NIZKGens { gens_r1cs_sat }
|
|
}
|
|
}
|
|
|
|
/// `NIZK` holds a proof produced by Spartan NIZK
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct NIZK {
|
|
r1cs_sat_proof: R1CSProof,
|
|
r: (Vec<Scalar>, Vec<Scalar>),
|
|
}
|
|
|
|
impl NIZK {
|
|
fn protocol_name() -> &'static [u8] {
|
|
b"Spartan NIZK proof"
|
|
}
|
|
|
|
/// A method to produce a NIZK proof of the satisfiability of an R1CS instance
|
|
pub fn prove(
|
|
inst: &Instance,
|
|
vars: Vec<Scalar>,
|
|
input: &[Scalar],
|
|
gens: &NIZKGens,
|
|
transcript: &mut Transcript,
|
|
) -> Self {
|
|
let timer_prove = Timer::new("NIZK::prove");
|
|
// we create a Transcript object seeded with a random Scalar
|
|
// to aid the prover produce its randomness
|
|
let mut random_tape = RandomTape::new(b"proof");
|
|
transcript.append_protocol_name(NIZK::protocol_name());
|
|
let (r1cs_sat_proof, rx, ry) = {
|
|
let (proof, rx, ry) = R1CSProof::prove(
|
|
&inst.inst,
|
|
vars,
|
|
input,
|
|
&gens.gens_r1cs_sat,
|
|
transcript,
|
|
&mut random_tape,
|
|
);
|
|
let proof_encoded: Vec<u8> = bincode::serialize(&proof).unwrap();
|
|
Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len()));
|
|
(proof, rx, ry)
|
|
};
|
|
|
|
timer_prove.stop();
|
|
NIZK {
|
|
r1cs_sat_proof,
|
|
r: (rx, ry),
|
|
}
|
|
}
|
|
|
|
/// A method to verify a NIZK proof of the satisfiability of an R1CS instance
|
|
pub fn verify(
|
|
&self,
|
|
inst: &Instance,
|
|
input: &[Scalar],
|
|
transcript: &mut Transcript,
|
|
gens: &NIZKGens,
|
|
) -> Result<(), ProofVerifyError> {
|
|
let timer_verify = Timer::new("NIZK::verify");
|
|
|
|
transcript.append_protocol_name(NIZK::protocol_name());
|
|
|
|
// We send evaluations of A, B, C at r = (rx, ry) as claims
|
|
// to enable the verifier complete the first sum-check
|
|
let timer_eval = Timer::new("eval_sparse_polys");
|
|
let (claimed_rx, claimed_ry) = &self.r;
|
|
let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry);
|
|
timer_eval.stop();
|
|
|
|
let timer_sat_proof = Timer::new("verify_sat_proof");
|
|
assert_eq!(input.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,
|
|
&inst_evals,
|
|
transcript,
|
|
&gens.gens_r1cs_sat,
|
|
)
|
|
.unwrap();
|
|
|
|
// verify if claimed rx and ry are correct
|
|
assert_eq!(rx, *claimed_rx);
|
|
assert_eq!(ry, *claimed_ry);
|
|
timer_sat_proof.stop();
|
|
timer_verify.stop();
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
pub fn check_snark() {
|
|
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);
|
|
|
|
// create a commitment to R1CSInstance
|
|
let (comm, decomm) = SNARK::encode(&inst, &gens);
|
|
|
|
// produce a proof
|
|
let mut prover_transcript = Transcript::new(b"example");
|
|
let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript);
|
|
|
|
// verify the proof
|
|
let mut verifier_transcript = Transcript::new(b"example");
|
|
assert!(proof
|
|
.verify(&comm, &inputs, &mut verifier_transcript, &gens)
|
|
.is_ok());
|
|
}
|
|
}
|