From e1d5aeb9f517a00b86ff5fb36af37f0b20f7c249 Mon Sep 17 00:00:00 2001 From: Jacob Eberhardt Date: Wed, 25 Oct 2017 02:02:13 +0200 Subject: [PATCH] Setup phase separated. Keys serialized to files. --- lib/wraplibsnark.cpp | 70 ++++++++++++++++++++++++++++++++------------ lib/wraplibsnark.hpp | 11 +++++++ src/libsnark.rs | 64 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 44 +++++++++++++++++++++------- 4 files changed, 159 insertions(+), 30 deletions(-) diff --git a/lib/wraplibsnark.cpp b/lib/wraplibsnark.cpp index 85ae4b7c..9e4873e1 100644 --- a/lib/wraplibsnark.cpp +++ b/lib/wraplibsnark.cpp @@ -1,33 +1,21 @@ /** * @file wraplibsnark.cpp - * @author Dennis Kuhnert * @author Jacob Eberhardt * @date 2017 */ #include "wraplibsnark.hpp" +#include #include #include #include // contains definition of alt_bn128 ec public parameters #include "libsnark/algebra/curves/alt_bn128/alt_bn128_pp.hpp" - // contains required interfaces and types (keypair, proof, generator, prover, verifier) #include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -// contains usage example for these interfaces -//#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp" - -// How to "zkSNARK from R1CS:" -// libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.tcc - -// How to generate R1CS Example -// libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc - -// Interfaces for R1CS -// libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp - using namespace std; using namespace libsnark; @@ -85,7 +73,7 @@ std::string outputPointG2AffineAsHex(libsnark::alt_bn128_G2 _p) } //takes input and puts it into constraint system -r1cs_ppzksnark_constraint_system createConstraintSystem(const uint8_t* A, const uint8_t* B, const uint8_t* C, const uint8_t* witness, int constraints, int variables, int inputs) +r1cs_ppzksnark_constraint_system createConstraintSystem(const uint8_t* A, const uint8_t* B, const uint8_t* C, int constraints, int variables, int inputs) { r1cs_constraint_system > cs; cs.primary_input_size = inputs; @@ -124,9 +112,6 @@ r1cs_ppzksnark_constraint_system createConstraintSystem(const uint } cs.add_constraint(r1cs_constraint >(lin_comb_A, lin_comb_B, lin_comb_C)); } - for (int idx=0; idx generateKeypair(const r1cs_ppzksnark_constr return r1cs_ppzksnark_generator(cs); } +void serializeProvingKeyToFile(r1cs_ppzksnark_keypair keypair, const char* pk_path){ + writeToFile(pk_path, keypair.pk); +} + +void serializeVerificationKeyToFile(r1cs_ppzksnark_keypair keypair, const char* vk_path){ + writeToFile(vk_path, keypair.vk); +} + // compliant with solidty verification example void exportVerificationKey(r1cs_ppzksnark_keypair keypair){ unsigned icLength = keypair.vk.encoded_IC_query.rest.indices.size() + 1; @@ -196,6 +189,33 @@ void exportProof(r1cs_ppzksnark_proof proof){ } +bool _setup(const uint8_t* A, const uint8_t* B, const uint8_t* C, int constraints, int variables, int inputs, const char* pk_path, const char* vk_path) +{ + libsnark::inhibit_profiling_info = true; + libsnark::inhibit_profiling_counters = true; + + //initialize curve parameters + alt_bn128_pp::init_public_params(); + + r1cs_constraint_system> cs; + cs = createConstraintSystem(A, B ,C , constraints, variables, inputs); + + assert(cs.num_variables() >= inputs); + assert(cs.num_inputs() == inputs); + assert(cs.num_constraints() == constraints); + + // create keypair + r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(cs); + + // Export vk and pk to files + serializeProvingKeyToFile(keypair, pk_path); + serializeVerificationKeyToFile(keypair, vk_path); + + // Print VerificationKey in Solidity compatible format + exportVerificationKey(keypair); + + return true; +} bool _run_libsnark(const uint8_t* A, const uint8_t* B, const uint8_t* C, const uint8_t* witness, int constraints, int variables, int inputs) { @@ -218,7 +238,7 @@ bool _run_libsnark(const uint8_t* A, const uint8_t* B, const uint8_t* C, const u // Setup: // create constraint system r1cs_constraint_system> cs; - cs = createConstraintSystem(A, B ,C , witness, constraints, variables, inputs); + cs = createConstraintSystem(A, B ,C , constraints, variables, inputs); // assign variables based on witness values, excludes ~one r1cs_variable_assignment > full_variable_assignment; @@ -266,3 +286,15 @@ bool _run_libsnark(const uint8_t* A, const uint8_t* B, const uint8_t* C, const u return result; } + +template +void writeToFile(std::string path, T& obj) { + std::stringstream ss; + ss << obj; + std::ofstream fh; + fh.open(path, std::ios::binary); + ss.rdbuf()->pubseekpos(0, std::ios_base::out); + fh << ss.rdbuf(); + fh.flush(); + fh.close(); +} diff --git a/lib/wraplibsnark.hpp b/lib/wraplibsnark.hpp index 28b82eb6..ec4476c3 100644 --- a/lib/wraplibsnark.hpp +++ b/lib/wraplibsnark.hpp @@ -1,5 +1,6 @@ /** * @file wraplibsnark.hpp + * @author Jacob Eberhardt * @date 2017 */ @@ -11,6 +12,16 @@ extern "C" { #include #include +bool _setup(const uint8_t* A, + const uint8_t* B, + const uint8_t* C, + int constraints, + int variables, + int inputs, + const char* pk_path, + const char* vk_path + ); + // entrypoint, wraps the whole process, probably should be removed later bool _run_libsnark(const uint8_t* A, const uint8_t* B, diff --git a/src/libsnark.rs b/src/libsnark.rs index b1a01959..e64251ba 100644 --- a/src/libsnark.rs +++ b/src/libsnark.rs @@ -1,12 +1,16 @@ // // @file libsnark.rs +// @author Jacob Eberhardt // @date 2017 extern crate libc; use self::libc::c_int; +use self::libc::c_char; use self::libc::uint8_t; +use std::ffi::CString; + use field::Field; #[link(name = "snark")] @@ -23,6 +27,65 @@ extern "C" { variables: c_int, inputs: c_int, ) -> bool; + + fn _setup( + A: *const uint8_t, + B: *const uint8_t, + C: *const uint8_t, + constraints: c_int, + variables: c_int, + inputs: c_int, + pk_path: *const c_char, + vk_path: *const c_char, + ) -> bool; +} + +pub fn setup ( + variables: Vec, + a: Vec>, + b: Vec>, + c: Vec>, + num_inputs: usize, + pk_path: &str, + vk_path: &str, + ) -> bool { + + let num_constraints = a.len(); + let num_variables = variables.len(); + + //initialize matrix entries with 0s. + let mut a_arr: Vec<[u8; 32]> = vec![[0u8; 32]; num_constraints * num_variables]; + let mut b_arr: Vec<[u8; 32]> = vec![[0u8; 32]; num_constraints * num_variables]; + let mut c_arr: Vec<[u8; 32]> = vec![[0u8; 32]; num_constraints * num_variables]; + + for row in 0..num_constraints { + for &(idx, ref val) in &a[row] { + a_arr[row * num_variables + idx] = vec_as_u8_32_array(&val.into_byte_vector()); + } + for &(idx, ref val) in &b[row] { + b_arr[row * num_variables + idx] = vec_as_u8_32_array(&val.into_byte_vector()); + } + for &(idx, ref val) in &c[row] { + c_arr[row * num_variables + idx] = vec_as_u8_32_array(&val.into_byte_vector()); + } + } + + // convert String slices to 'CString's + let pk_path_cstring = CString::new(pk_path).unwrap(); + let vk_path_cstring = CString::new(vk_path).unwrap(); + + unsafe { + _setup( + a_arr[0].as_ptr(), + b_arr[0].as_ptr(), + c_arr[0].as_ptr(), + num_constraints as i32, + num_variables as i32, + num_inputs as i32, + pk_path_cstring.as_ptr(), + vk_path_cstring.as_ptr() + ) + } } // assumes that field elements can be represented with 32 bytes @@ -34,6 +97,7 @@ pub fn run_libsnark( witness: Vec, num_inputs: usize, ) -> bool { + let num_constraints = a.len(); let num_variables = variables.len(); diff --git a/src/main.rs b/src/main.rs index 4d06de90..d62eced3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ use flatten::Flattener; use r1cs::r1cs_program; use clap::{App, AppSettings, Arg, SubCommand}; #[cfg(not(feature = "nolibsnark"))] -use libsnark::run_libsnark; +use libsnark::{run_libsnark, setup}; use bincode::{serialize_into, deserialize_from , Infinite}; fn main() { @@ -78,7 +78,26 @@ fn main() { .value_name("FILE") .takes_value(true) .required(false) - .default_value(FLATTENED_CODE_DEFAULT_PATH)) + .default_value(FLATTENED_CODE_DEFAULT_PATH) + ) + .arg(Arg::with_name("proving-key-path") + .short("pk") + .long("proving-key-path") + .help("Path of the generated proving key file.") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(PROVING_KEY_DEFAULT_PATH) + ) + .arg(Arg::with_name("verification-key-path") + .short("vk") + .long("verification-key-path") + .help("Path of the generated verification key file.") + .value_name("FILE") + .takes_value(true) + .required(false) + .default_value(VERIFICATION_KEY_DEFAULT_PATH) + ) ) .subcommand(SubCommand::with_name("export-verifier") .about("Exports a verifier as Solidity smart contract.") @@ -297,7 +316,7 @@ fn main() { // write witness to file let output_path = Path::new(sub_matches.value_of("output").unwrap()); - let mut output_file = match File::create(&output_path) { + let output_file = match File::create(&output_path) { Ok(file) => file, Err(why) => panic!("couldn't create {}: {}", output_path.display(), why), }; @@ -345,16 +364,19 @@ fn main() { // print deserialized flattened program println!("{}", main_flattened); + // transform to R1CS let (variables, a, b, c) = r1cs_program(&program_ast); - // TODO: Setup Operation + // get paths for proving and verification keys + let pk_path = sub_matches.value_of("proving-key-path").unwrap(); + let vk_path = sub_matches.value_of("verification-key-path").unwrap(); - // run libsnark - // #[cfg(not(feature="nolibsnark"))]{ - // // number of inputs in the zkSNARK sense, i.e., input variables + output variables - // let num_inputs = main_flattened.arguments.len() + 1; //currently exactly one output variable - // println!("run_libsnark = {:?}", run_libsnark(variables, a, b, c, witness, num_inputs)); - // } + // run setup phase + #[cfg(not(feature="nolibsnark"))]{ + // number of inputs in the zkSNARK sense, i.e., input variables + output variables + let num_inputs = main_flattened.arguments.len() + 1; //currently exactly one output variable + println!("setup successful: {:?}", setup(variables, a, b, c, num_inputs, pk_path, vk_path)); + } } ("shortcut", Some(sub_matches)) => { @@ -434,7 +456,7 @@ fn main() { // deserialize witness let witness_path = Path::new(sub_matches.value_of("witness").unwrap()); - let mut witness_file = match File::open(&witness_path) { + let witness_file = match File::open(&witness_path) { Ok(file) => file, Err(why) => panic!("couldn't open {}: {}", witness_path.display(), why), };