From 9f178f36604a386056405f03c1d5d31202f7935e Mon Sep 17 00:00:00 2001 From: Sam Mayo Date: Wed, 27 Jun 2018 16:21:24 -0400 Subject: [PATCH] add serialized keys/proofs --- CMakeLists.txt | 2 +- src/ethereum/contracts/Migrations.sol | 23 ++ src/ethereum/contracts/Verifier.sol | 239 ++++++++++++++++++ .../migrations/1_initial_migration.js | 5 + src/ethereum/migrations/2_deploy_contracts.js | 5 + src/ethereum/test/TestVerifier.js | 141 +++++++++++ src/ethereum/truffle.js | 9 + src/test-gadget.cpp | 31 +-- src/test.cpp | 28 +- src/util.hpp | 122 +++++++++ 10 files changed, 570 insertions(+), 35 deletions(-) create mode 100644 src/ethereum/contracts/Migrations.sol create mode 100644 src/ethereum/contracts/Verifier.sol create mode 100644 src/ethereum/migrations/1_initial_migration.js create mode 100644 src/ethereum/migrations/2_deploy_contracts.js create mode 100644 src/ethereum/test/TestVerifier.js create mode 100644 src/ethereum/truffle.js create mode 100644 src/util.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dba6a0..0db7a82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(libsnark-tutorial) set( CURVE - "BN128" + "ALT_BN128" CACHE STRING "Default curve: one of ALT_BN128, BN128, EDWARDS, MNT4, MNT6" diff --git a/src/ethereum/contracts/Migrations.sol b/src/ethereum/contracts/Migrations.sol new file mode 100644 index 0000000..c4efb65 --- /dev/null +++ b/src/ethereum/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.23; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/src/ethereum/contracts/Verifier.sol b/src/ethereum/contracts/Verifier.sol new file mode 100644 index 0000000..7e81a40 --- /dev/null +++ b/src/ethereum/contracts/Verifier.sol @@ -0,0 +1,239 @@ +pragma solidity ^0.4.14; +library Pairing { + struct G1Point { + uint X; + uint Y; + } + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint[2] X; + uint[2] Y; + } + /// @return the generator of G1 + function P1() internal returns (G1Point) { + return G1Point(1, 2); + } + /// @return the generator of G2 + function P2() internal returns (G2Point) { + return G2Point( + [11559732032986387107991004021392285783925812861821192530917403151452391805634, + 10857046999023057135944570762232829481370756359578518086990519993285655852781], + [4082367875863433681332203403145435568316851327593401208105741076214120093531, + 8495653923123431417604973247489272438418190587263600148770280649306958101930] + ); + } + /// @return the negation of p, i.e. p.add(p.negate()) should be zero. + function negate(G1Point p) internal returns (G1Point) { + // The prime q in the base field F_q for G1 + uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + if (p.X == 0 && p.Y == 0) + return G1Point(0, 0); + return G1Point(p.X, q - (p.Y % q)); + } + /// @return the sum of two points of G1 + function add(G1Point p1, G1Point p2) internal returns (G1Point r) { + uint[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + assembly { + success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid } + } + require(success); + } + /// @return the product of a point on G1 and a scalar, i.e. + /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. + function mul(G1Point p, uint s) internal returns (G1Point r) { + uint[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + assembly { + success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid } + } + require (success); + } + /// @return the result of computing the pairing check + /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should + /// return true. + function pairing(G1Point[] p1, G2Point[] p2) internal returns (bool) { + require(p1.length == p2.length); + uint elements = p1.length; + uint inputSize = elements * 6; + uint[] memory input = new uint[](inputSize); + for (uint i = 0; i < elements; i++) + { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[0]; + input[i * 6 + 3] = p2[i].X[1]; + input[i * 6 + 4] = p2[i].Y[0]; + input[i * 6 + 5] = p2[i].Y[1]; + } + uint[1] memory out; + bool success; + assembly { + success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid } + } + require(success); + return out[0] != 0; + } + /// Convenience method for a pairing check for two pairs. + function pairingProd2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) internal returns (bool) { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for three pairs. + function pairingProd3( + G1Point a1, G2Point a2, + G1Point b1, G2Point b2, + G1Point c1, G2Point c2 + ) internal returns (bool) { + G1Point[] memory p1 = new G1Point[](3); + G2Point[] memory p2 = new G2Point[](3); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for four pairs. + function pairingProd4( + G1Point a1, G2Point a2, + G1Point b1, G2Point b2, + G1Point c1, G2Point c2, + G1Point d1, G2Point d2 + ) internal returns (bool) { + G1Point[] memory p1 = new G1Point[](4); + G2Point[] memory p2 = new G2Point[](4); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p1[3] = d1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + p2[3] = d2; + return pairing(p1, p2); + } +} +contract Verifier { + using Pairing for *; + bool public verifyingKeySet = false; + struct VerifyingKey { + Pairing.G2Point A; + Pairing.G1Point B; + Pairing.G2Point C; + Pairing.G2Point gamma; + Pairing.G1Point gammaBeta1; + Pairing.G2Point gammaBeta2; + Pairing.G2Point Z; + Pairing.G1Point[5] IC; + uint IC_length; + } + VerifyingKey vk; + struct Proof { + Pairing.G1Point A; + Pairing.G1Point A_p; + Pairing.G2Point B; + Pairing.G1Point B_p; + Pairing.G1Point C; + Pairing.G1Point C_p; + Pairing.G1Point K; + Pairing.G1Point H; + } + function verify(uint[] input, Proof proof) internal returns (uint) { + require(input.length + 1 == vk.IC_length); + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint i = 0; i < input.length; i++) + vk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[i + 1], input[i])); + vk_x = Pairing.add(vk_x, vk.IC[0]); + if (!Pairing.pairingProd2(proof.A, vk.A, Pairing.negate(proof.A_p), Pairing.P2())) return 1; + if (!Pairing.pairingProd2(vk.B, proof.B, Pairing.negate(proof.B_p), Pairing.P2())) return 2; + if (!Pairing.pairingProd2(proof.C, vk.C, Pairing.negate(proof.C_p), Pairing.P2())) return 3; + if (!Pairing.pairingProd3( + proof.K, vk.gamma, + Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, + Pairing.negate(vk.gammaBeta1), proof.B + )) return 4; + if (!Pairing.pairingProd3( + Pairing.add(vk_x, proof.A), proof.B, + Pairing.negate(proof.H), vk.Z, + Pairing.negate(proof.C), Pairing.P2() + )) return 5; + return 0; + } + function setVerifyingKey( + uint[2][2] A, + uint[2] B, + uint[2][2] C, + uint[2][2] gamma, + uint[2] gamma_beta_1, + uint[2][2] gamma_beta_2, + uint[2][2] Z, + uint[2][] IC) public { + require(IC.length <= 5); + vk.A = Pairing.G2Point([A[0][0], A[0][1]], [A[1][0], A[1][1]]); + vk.B = Pairing.G1Point(B[0], B[1]); + vk.C = Pairing.G2Point([C[0][0], C[0][1]], [C[1][0], C[1][1]]); + vk.gamma = Pairing.G2Point([gamma[0][0], gamma[0][1]], [gamma[1][0], gamma[1][1]]); + vk.gammaBeta1 = Pairing.G1Point(gamma_beta_1[0], gamma_beta_1[1]); + vk.gammaBeta2 = Pairing.G2Point([gamma_beta_2[0][0], gamma_beta_2[0][1]], [gamma_beta_2[1][0], gamma_beta_2[1][1]]); + vk.Z = Pairing.G2Point([Z[0][0], Z[0][1]], [Z[1][0], Z[1][1]]); + vk.IC_length = IC.length; + for(uint i=0; i FieldT; @@ -35,10 +37,10 @@ int main() pb.set_input_sizes(1); + // Initialize gadget + test_gadget g(pb, out, x); g.generate_r1cs_constraints(); - - cout << "Number of variables: " << pb.num_variables() << endl; // Add witness values @@ -46,31 +48,24 @@ int main() pb.val(x) = 3; g.generate_r1cs_witness(); - - if (pb.is_satisfied()) { - cout << "Constraint system is satisfied." << endl; - } - else { - cout << "Constraint system is not satisfied." << endl; - } - - cout << "primary (public) input: " << pb.primary_input() << endl; - cout << "auxiliary (private) input: " << pb.auxiliary_input() << endl; const r1cs_constraint_system constraint_system = pb.get_constraint_system(); - - cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; - r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); + const r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); - r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input()); + const r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input()); bool verified = r1cs_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof); + cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; cout << "Primary (public) input: " << pb.primary_input() << endl; cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl; - cout << "Verification status: " << verified << endl; + const r1cs_ppzksnark_verification_key vk = keypair.vk; + + print_vk_to_file(vk, "../build/vk_data"); + print_proof_to_file(proof, "../build/proof_data"); + return 0; } diff --git a/src/test.cpp b/src/test.cpp index 14ca6a5..b773d5d 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -6,15 +6,17 @@ #include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" #include "libsnark/gadgetlib1/pb_variable.hpp" +#include "util.hpp" + using namespace libsnark; using namespace std; int main() { - typedef libff::Fr FieldT; - // Initialize the curve parameters. + // Initialize the curve parameters + default_r1cs_ppzksnark_pp::init_public_params(); // Create protoboard @@ -43,8 +45,6 @@ int main() // input and the rest is private input pb.set_input_sizes(1); - cout << "Number of variables: " << pb.num_variables() << endl; - // Add R1CS constraints to protoboard // x*x = sym_1 @@ -67,27 +67,23 @@ int main() pb.val(y) = 27; pb.val(sym_2) = 30; - if (pb.is_satisfied()) { - cout << "Constraint system is satisfied." << endl; - } - else { - cout << "Constraint system is not satisfied." << endl; - } - const r1cs_constraint_system constraint_system = pb.get_constraint_system(); - - cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; - r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); + const r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); - r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input()); + const r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input()); bool verified = r1cs_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof); + cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; cout << "Primary (public) input: " << pb.primary_input() << endl; cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl; - cout << "Verification status: " << verified << endl; + const r1cs_ppzksnark_verification_key vk = keypair.vk; + + print_vk_to_file(vk, "../build/vk_data"); + print_proof_to_file(proof, "../build/proof_data"); + return 0; } diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 0000000..e83c754 --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,122 @@ +#include + +#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" +#include "libff/algebra/curves/public_params.hpp" + +using namespace libsnark; +using namespace libff; +using namespace std; + +template +void print_vk_to_file(r1cs_ppzksnark_verification_key vk, string pathToFile) +{ + ofstream vk_data; + vk_data.open(pathToFile); + + G2 A(vk.alphaA_g2); + A.to_affine_coordinates(); + G1 B(vk.alphaB_g1); + B.to_affine_coordinates(); + G2 C(vk.alphaC_g2); + C.to_affine_coordinates(); + + G2 gamma(vk.gamma_g2); + gamma.to_affine_coordinates(); + G1 gamma_beta_1(vk.gamma_beta_g1); + gamma_beta_1.to_affine_coordinates(); + G2 gamma_beta_2(vk.gamma_beta_g2); + gamma_beta_2.to_affine_coordinates(); + + G2 Z(vk.rC_Z_g2); + Z.to_affine_coordinates(); + + accumulation_vector> IC(vk.encoded_IC_query); + G1 IC_0(IC.first); + IC_0.to_affine_coordinates(); + + vk_data << A.X << endl; + vk_data << A.Y << endl; + + vk_data << B.X << endl; + vk_data << B.Y << endl; + + vk_data << C.X << endl; + vk_data << C.Y << endl; + + vk_data << gamma.X << endl; + vk_data << gamma.Y << endl; + + vk_data << gamma_beta_1.X << endl; + vk_data << gamma_beta_1.Y << endl; + + vk_data << gamma_beta_2.X << endl; + vk_data << gamma_beta_2.Y << endl; + + vk_data << Z.X << endl; + vk_data << Z.Y << endl; + + vk_data << IC_0.X << endl; + vk_data << IC_0.Y << endl; + + for(size_t i=0; i IC_N(IC.rest[i]); + IC_N.to_affine_coordinates(); + vk_data << IC_N.X << endl; + vk_data << IC_N.Y << endl; + } + + vk_data.close(); +} + +template +void print_proof_to_file(r1cs_ppzksnark_proof proof, string pathToFile) +{ + ofstream proof_data; + proof_data.open(pathToFile); + + G1 A_g(proof.g_A.g); + A_g.to_affine_coordinates(); + G1 A_h(proof.g_A.h); + A_h.to_affine_coordinates(); + + G2 B_g(proof.g_B.g); + B_g.to_affine_coordinates(); + G1 B_h(proof.g_B.h); + B_h.to_affine_coordinates(); + + G1 C_g(proof.g_C.g); + C_g.to_affine_coordinates(); + G1 C_h(proof.g_C.h); + C_h.to_affine_coordinates(); + + G1 H(proof.g_H); + H.to_affine_coordinates(); + G1 K(proof.g_K); + K.to_affine_coordinates(); + + proof_data << A_g.X << endl; + proof_data << A_g.Y << endl; + + proof_data << A_h.X << endl; + proof_data << A_h.Y << endl; + + proof_data << B_g.X << endl; + proof_data << B_g.Y << endl; + + proof_data << B_h.X << endl; + proof_data << B_h.Y << endl; + + proof_data << C_g.X << endl; + proof_data << C_g.Y << endl; + + proof_data << C_h.X << endl; + proof_data << C_h.Y << endl; + + proof_data << H.X << endl; + proof_data << H.Y << endl; + + proof_data << K.X << endl; + proof_data << K.Y << endl; + + proof_data.close(); +} \ No newline at end of file