diff --git a/Cargo.toml b/Cargo.toml index 19bb13c..eeeb7ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ name = "bellman" repository = "https://github.com/ebfull/bellman" version = "0.0.1" +[dependencies] +rand = "0.3.12" + [dependencies.tinysnark] path = "tinysnark" version = "0.0.1" diff --git a/src/main.rs b/src/main.rs index 9447469..fbea99e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,16 @@ extern crate tinysnark; +extern crate rand; + +use tinysnark::{Proof, Keypair, FieldT, LinearTerm, ConstraintSystem}; fn main() { tinysnark::init(); - tinysnark::test(); + let mut cs = ConstraintSystem::new(1, 2); + cs.add_constraint( + &[LinearTerm{coeff: FieldT::one(), index: 2}], + &[LinearTerm{coeff: FieldT::one(), index: 3}], + &[LinearTerm{coeff: FieldT::one(), index: 1}] + ); + assert!(cs.test(&[100.into()], &[10.into(), 10.into()])); } diff --git a/tinysnark/src/lib.rs b/tinysnark/src/lib.rs index eec1ae7..a8c6bbc 100644 --- a/tinysnark/src/lib.rs +++ b/tinysnark/src/lib.rs @@ -1,10 +1,13 @@ #![feature(box_syntax)] +#![allow(improper_ctypes)] //#![cfg_attr(test, feature(test))] extern crate libc; mod arith; +mod r1cs; pub use self::arith::*; +pub use self::r1cs::*; use std::sync::{Once, ONCE_INIT}; @@ -13,7 +16,6 @@ static mut INITIALIZED: bool = false; extern "C" { fn tinysnark_init_public_params(); - fn tinysnark_test(); } pub fn init() { @@ -27,16 +29,48 @@ pub fn is_initialized() -> bool { unsafe { INITIALIZED } } -pub fn test() { - unsafe { tinysnark_test(); } -} - #[cfg(test)] mod tests { //extern crate test; - use super::{init, FieldT}; + use super::{init, FieldT, Proof, Keypair, LinearTerm, ConstraintSystem}; //use self::test::Bencher; + #[test] + fn test_zk() { + init(); + { + let mut cs = ConstraintSystem::new(1, 2); + // zkpok { (a, b) c = a * b } + cs.add_constraint( + &[LinearTerm{coeff: FieldT::one(), index: 2}], + &[LinearTerm{coeff: FieldT::one(), index: 3}], + &[LinearTerm{coeff: FieldT::one(), index: 1}] + ); + assert!(cs.test(&[10.into()], &[5.into(), 2.into()])); + assert!(!cs.test(&[10.into()], &[6.into(), 2.into()])); + + let kp = Keypair::new(&cs); + let proof = Proof::new(&kp, &cs, &[10.into()], &[5.into(), 2.into()]); + assert!(proof.verify(&kp, &cs, &[10.into()])); + } + { + let mut cs = ConstraintSystem::new(0, 1); + // simple boolean constraint + cs.add_constraint( + &[LinearTerm{coeff: FieldT::one(), index: 0}, LinearTerm{coeff: -FieldT::one(), index: 1}], + &[LinearTerm{coeff: FieldT::one(), index: 1}], + &[LinearTerm{coeff: FieldT::zero(), index: 0}] + ); + assert!(cs.test(&[], &[1.into()])); + assert!(cs.test(&[], &[0.into()])); + assert!(!cs.test(&[], &[2.into()])); + + let kp = Keypair::new(&cs); + let proof = Proof::new(&kp, &cs, &[], &[1.into()]); + assert!(proof.verify(&kp, &cs, &[])); + } + } + #[test] fn test_one() { init(); diff --git a/tinysnark/src/r1cs.rs b/tinysnark/src/r1cs.rs new file mode 100644 index 0000000..e589924 --- /dev/null +++ b/tinysnark/src/r1cs.rs @@ -0,0 +1,144 @@ +use libc::{size_t}; +use super::arith::FieldT; + +#[repr(C)] +struct R1ConstraintSystem; + +#[repr(C)] +pub struct LinearTerm { + pub coeff: FieldT, + pub index: size_t +} + +extern "C" { + fn tinysnark_new_r1cs(primary: size_t, aux: size_t) -> *mut R1ConstraintSystem; + fn tinysnark_drop_r1cs(cs: *mut R1ConstraintSystem); + fn tinysnark_satisfy_test(cs: *mut R1ConstraintSystem, primary: *const FieldT, aux: *const FieldT) -> bool; + fn tinysnark_add_constraint(cs: *mut R1ConstraintSystem, + a: *const LinearTerm, + a_len: size_t, + b: *const LinearTerm, + b_len: size_t, + c: *const LinearTerm, + c_len: size_t + ); +} + +pub struct ConstraintSystem { + cs: *mut R1ConstraintSystem, + primary_size: usize, + aux_size: usize +} + +impl Drop for ConstraintSystem { + fn drop(&mut self) { + unsafe { tinysnark_drop_r1cs(self.cs) } + } +} + +impl ConstraintSystem { + pub fn new(primary_size: usize, aux_size: usize) -> ConstraintSystem { + ConstraintSystem { + cs: unsafe { tinysnark_new_r1cs(primary_size, aux_size) }, + primary_size: primary_size, + aux_size: aux_size + } + } + + pub fn add_constraint(&mut self, a: &[LinearTerm], b: &[LinearTerm], c: &[LinearTerm]) + { + unsafe { + tinysnark_add_constraint( + self.cs, + a.get_unchecked(0), + a.len(), + b.get_unchecked(0), + b.len(), + c.get_unchecked(0), + c.len() + ); + } + } + + pub fn test(&mut self, primary: &[FieldT], aux: &[FieldT]) -> bool + { + assert_eq!(primary.len(), self.primary_size); + assert_eq!(aux.len(), self.aux_size); + + unsafe { + tinysnark_satisfy_test(self.cs, primary.get_unchecked(0), aux.get_unchecked(0)) + } + } +} + +#[repr(C)] +struct R1CSKeypair; + +pub struct Keypair { + kp: *mut R1CSKeypair +} + +impl Keypair { + pub fn new(constraint_system: &ConstraintSystem) -> Keypair { + Keypair { + kp: unsafe { tinysnark_gen_keypair(constraint_system.cs) } + } + } +} + +impl Drop for Keypair { + fn drop(&mut self) { + unsafe { tinysnark_drop_keypair(self.kp) } + } +} + +extern "C" { + fn tinysnark_gen_keypair(cs: *mut R1ConstraintSystem) -> *mut R1CSKeypair; + fn tinysnark_drop_keypair(cs: *mut R1CSKeypair); +} + +#[repr(C)] +struct R1CSProof; + +pub struct Proof { + proof: *mut R1CSProof +} + +impl Proof { + pub fn new(keypair: &Keypair, constraint_system: &ConstraintSystem, primary: &[FieldT], aux: &[FieldT]) + -> Proof + { + assert_eq!(primary.len(), constraint_system.primary_size); + assert_eq!(aux.len(), constraint_system.aux_size); + + unsafe { + Proof { + proof: tinysnark_gen_proof(keypair.kp, constraint_system.cs, primary.get_unchecked(0), aux.get_unchecked(0)) + } + } + } + + pub fn verify(&self, keypair: &Keypair, constraint_system: &ConstraintSystem, primary: &[FieldT]) -> bool { + unsafe { + tinysnark_verify_proof(self.proof, keypair.kp, constraint_system.cs, primary.get_unchecked(0)) + } + } +} + +impl Drop for Proof { + fn drop(&mut self) { + unsafe { tinysnark_drop_proof(self.proof) } + } +} + +extern "C" { + fn tinysnark_gen_proof(keypair: *mut R1CSKeypair, + cs: *mut R1ConstraintSystem, + primary: *const FieldT, + aux: *const FieldT) -> *mut R1CSProof; + fn tinysnark_verify_proof(proof: *mut R1CSProof, + keypair: *mut R1CSKeypair, + cs: *mut R1ConstraintSystem, + primary: *const FieldT) -> bool; + fn tinysnark_drop_proof(proof: *mut R1CSProof); +} \ No newline at end of file diff --git a/tinysnark/tinysnark.cpp b/tinysnark/tinysnark.cpp index f716777..a25ba52 100644 --- a/tinysnark/tinysnark.cpp +++ b/tinysnark/tinysnark.cpp @@ -13,6 +13,126 @@ using namespace std; typedef Fr FieldT; +struct tinysnark_linear_term { + FieldT coeff; + size_t index; +}; + +extern "C" void * tinysnark_gen_proof(void * kp, void * ics, FieldT* primary, FieldT* aux) { + r1cs_constraint_system* cs = static_cast*>(ics); + r1cs_ppzksnark_keypair* keypair = static_cast*>(kp); + + r1cs_primary_input primary_input(primary, primary+(cs->primary_input_size)); + r1cs_auxiliary_input aux_input(aux, aux+(cs->auxiliary_input_size)); + + auto proof = new r1cs_ppzksnark_proof( + r1cs_ppzksnark_prover(keypair->pk, primary_input, aux_input) + ); + + return static_cast(std::move(proof)); +} + +extern "C" bool tinysnark_verify_proof(void * iproof, void * kp, void * ics, FieldT* primary) { + r1cs_ppzksnark_proof* proof = static_cast*>(iproof); + r1cs_constraint_system* cs = static_cast*>(ics); + r1cs_ppzksnark_keypair* keypair = static_cast*>(kp); + + r1cs_primary_input primary_input(primary, primary+(cs->primary_input_size)); + + return r1cs_ppzksnark_verifier_strong_IC(keypair->vk, primary_input, *proof); +} + +extern "C" void * tinysnark_drop_proof(void * proof) { + r1cs_ppzksnark_proof* p = static_cast*>(proof); + + delete p; +} + +extern "C" void * tinysnark_gen_keypair(void * ics) { + r1cs_constraint_system* cs = static_cast*>(ics); + + auto keypair = new r1cs_ppzksnark_keypair( + r1cs_ppzksnark_generator(*cs) + ); + + return static_cast(std::move(keypair)); +} + +extern "C" void * tinysnark_drop_keypair(void * kp) { + r1cs_ppzksnark_keypair* k = static_cast*>(kp); + + delete k; +} + +extern "C" void * tinysnark_new_r1cs(size_t primary_size, size_t aux_size) { + auto cs = new r1cs_constraint_system(); + + cs->primary_input_size = primary_size; + cs->auxiliary_input_size = aux_size; + + return static_cast(std::move(cs)); +} + +extern "C" void tinysnark_drop_r1cs(void * ics) { + r1cs_constraint_system* cs = static_cast*>(ics); + + delete cs; +} + +extern "C" bool tinysnark_satisfy_test(void * ics, FieldT* primary, FieldT* aux) { + r1cs_constraint_system* cs = static_cast*>(ics); + + r1cs_primary_input primary_input(primary, primary+(cs->primary_input_size)); + r1cs_auxiliary_input aux_input(aux, aux+(cs->auxiliary_input_size)); + + return cs->is_valid() && cs->is_satisfied(primary_input, aux_input); +} + +extern "C" void * tinysnark_add_constraint( + void * ics, + tinysnark_linear_term * a_terms, + size_t a_terms_len, + tinysnark_linear_term * b_terms, + size_t b_terms_len, + tinysnark_linear_term * c_terms, + size_t c_terms_len +) { + r1cs_constraint_system* cs = static_cast*>(ics); + + std::vector> a; + std::vector> b; + std::vector> c; + + for (size_t i = 0; i < a_terms_len; i++) { + FieldT coeff = a_terms[i].coeff; + size_t index = a_terms[i].index; + + a.push_back(linear_term(variable(index), coeff)); + } + + for (size_t i = 0; i < b_terms_len; i++) { + FieldT coeff = b_terms[i].coeff; + size_t index = b_terms[i].index; + + b.push_back(linear_term(variable(index), coeff)); + } + + for (size_t i = 0; i < c_terms_len; i++) { + FieldT coeff = c_terms[i].coeff; + size_t index = c_terms[i].index; + + c.push_back(linear_term(variable(index), coeff)); + } + + linear_combination a_lc(a); + linear_combination b_lc(b); + linear_combination c_lc(c); + + r1cs_constraint constraint(a_lc, b_lc, c_lc); + + cs->add_constraint(constraint); +} + extern "C" FieldT tinysnark_fieldt_mul(FieldT a, FieldT b) { return a * b; } @@ -52,27 +172,3 @@ extern "C" void tinysnark_init_public_params() { assert(sizeof(p) == 32); } } - -extern "C" void tinysnark_test() { - protoboard pb; - - linear_combination sum; - - sum = sum + 1; - - pb.add_r1cs_constraint(r1cs_constraint(1, sum, 1), "testing"); - - assert(pb.is_satisfied()); - - const r1cs_constraint_system constraint_system = pb.get_constraint_system(); - - cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; - - auto keypair = r1cs_ppzksnark_generator(constraint_system); - - auto proof = r1cs_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input()); - - r1cs_primary_input input; - - assert(r1cs_ppzksnark_verifier_strong_IC(keypair.vk, input, proof)); -} \ No newline at end of file