Auto merge of #1262 - ebfull:compressed-proofs, r=ebfull
Implement zkSNARK compression Implement zkSNARK compression. The serialization format of zkSNARKs has now been specified in our protocol specification. G1/G2 elements are now compressed into 33 byte and 65 byte formats, repsectively. `MONTGOMERY_OUTPUT` is enabled everywhere to make proving/verifying keys load faster and be consistent with the MPC. Proofs are now 296 bytes. This PR also updates the proving and verifying keys. Closes #1103. Closes #471. See also https://github.com/zcash/libsnark/pull/3 and https://github.com/zcash/libsnark/pull/4
This commit is contained in:
@@ -3,8 +3,8 @@ $(package)_version=0.1
|
||||
$(package)_download_path=https://github.com/zcash/$(package)/archive/
|
||||
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
|
||||
$(package)_download_file=$($(package)_git_commit).tar.gz
|
||||
$(package)_sha256_hash=369fe9f5a8018eb46cce3cf50bfbf21419d8e963b25067ffd00cb9ac5c39f649
|
||||
$(package)_git_commit=11242d8afe304c8bcc252baec483f04a110d1dd4
|
||||
$(package)_sha256_hash=cf002c50711654f0a4abb76b63f5b7d3679403822025772acc334bc7992e9494
|
||||
$(package)_git_commit=d65904ac6f1c0f7676035e62f84d6352b386ba45
|
||||
|
||||
$(package)_dependencies=libgmp libsodium
|
||||
|
||||
|
||||
@@ -411,12 +411,15 @@ libzcash_a_SOURCES = \
|
||||
zcash/NoteEncryption.cpp \
|
||||
zcash/Address.cpp \
|
||||
zcash/JoinSplit.cpp \
|
||||
zcash/Proof.cpp \
|
||||
zcash/Note.cpp \
|
||||
zcash/prf.cpp \
|
||||
zcash/util.cpp
|
||||
|
||||
libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
||||
|
||||
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
||||
|
||||
# bitcoinconsensus library #
|
||||
if BUILD_BITCOIN_LIBS
|
||||
include_HEADERS = script/bitcoinconsensus.h
|
||||
|
||||
@@ -4,6 +4,7 @@ bin_PROGRAMS += zcash-gtest
|
||||
# tool for generating our public parameters
|
||||
zcash_gtest_SOURCES = \
|
||||
gtest/main.cpp \
|
||||
gtest/json_test_vectors.cpp \
|
||||
gtest/test_tautology.cpp \
|
||||
gtest/test_checktransaction.cpp \
|
||||
gtest/test_equihash.cpp \
|
||||
@@ -13,7 +14,8 @@ zcash_gtest_SOURCES = \
|
||||
gtest/test_merkletree.cpp \
|
||||
gtest/test_circuit.cpp \
|
||||
gtest/test_txid.cpp \
|
||||
gtest/test_libzcash_utils.cpp
|
||||
gtest/test_libzcash_utils.cpp \
|
||||
gtest/test_proofs.cpp
|
||||
|
||||
zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ JSON_TEST_FILES = \
|
||||
test/data/merkle_roots_empty.json \
|
||||
test/data/merkle_serialization.json \
|
||||
test/data/merkle_witness_serialization.json \
|
||||
test/data/merkle_path.json
|
||||
test/data/merkle_path.json \
|
||||
test/data/g1_compressed.json \
|
||||
test/data/g2_compressed.json
|
||||
|
||||
RAW_TEST_FILES = test/data/alertTests.raw
|
||||
|
||||
|
||||
14
src/gtest/json_test_vectors.cpp
Normal file
14
src/gtest/json_test_vectors.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "json_test_vectors.h"
|
||||
|
||||
Array
|
||||
read_json(const std::string& jsondata)
|
||||
{
|
||||
Value v;
|
||||
|
||||
if (!read_string(jsondata, v) || v.type() != array_type)
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Array();
|
||||
}
|
||||
return v.get_array();
|
||||
}
|
||||
54
src/gtest/json_test_vectors.h
Normal file
54
src/gtest/json_test_vectors.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "utilstrencodings.h"
|
||||
#include "version.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
|
||||
#include "json/json_spirit_reader_template.h"
|
||||
#include "json/json_spirit_utils.h"
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
using namespace json_spirit;
|
||||
Array
|
||||
read_json(const std::string& jsondata);
|
||||
|
||||
// #define PRINT_JSON 1
|
||||
|
||||
template<typename T>
|
||||
void expect_deser_same(const T& expected)
|
||||
{
|
||||
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss1 << expected;
|
||||
|
||||
auto serialized_size = ss1.size();
|
||||
|
||||
T object;
|
||||
ss1 >> object;
|
||||
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << object;
|
||||
|
||||
ASSERT_TRUE(serialized_size == ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), serialized_size) == 0);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void expect_test_vector(T& it, const U& expected)
|
||||
{
|
||||
expect_deser_same(expected);
|
||||
|
||||
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss1 << expected;
|
||||
|
||||
#ifdef PRINT_JSON
|
||||
std::cout << "\t\"" ;
|
||||
std::cout << HexStr(ss1.begin(), ss1.end()) << "\",\n";
|
||||
#else
|
||||
std::string raw = (it++)->get_str();
|
||||
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
ASSERT_TRUE(ss1.size() == ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), ss1.size()) == 0);
|
||||
#endif
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "crypto/common.h"
|
||||
|
||||
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
|
||||
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
assert(init_and_check_sodium() != -1);
|
||||
libsnark::default_r1cs_ppzksnark_pp::init_public_params();
|
||||
libsnark::inhibit_profiling_info = true;
|
||||
libsnark::inhibit_profiling_counters = true;
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ void test_value_equals(uint64_t i) {
|
||||
|
||||
TEST(circuit, values)
|
||||
{
|
||||
default_r1cs_ppzksnark_pp::init_public_params();
|
||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
||||
test_value_equals<FieldT>(0);
|
||||
test_value_equals<FieldT>(1);
|
||||
@@ -162,7 +161,6 @@ TEST(circuit, merkle_tree_gadget_weirdness)
|
||||
behavior.
|
||||
*/
|
||||
|
||||
default_r1cs_ppzksnark_pp::init_public_params();
|
||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
||||
|
||||
// Test the normal case
|
||||
|
||||
@@ -33,7 +33,7 @@ void test_full_api(ZCJoinSplit* js)
|
||||
boost::array<uint256, 2> commitments;
|
||||
uint256 rt = tree.root();
|
||||
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> proof;
|
||||
ZCProof proof;
|
||||
|
||||
{
|
||||
boost::array<JSInput, 2> inputs = {
|
||||
|
||||
@@ -25,48 +25,11 @@
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "json/json_spirit_reader_template.h"
|
||||
#include "json/json_spirit_utils.h"
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
using namespace json_spirit;
|
||||
Array
|
||||
read_json(const std::string& jsondata)
|
||||
{
|
||||
Value v;
|
||||
|
||||
if (!read_string(jsondata, v) || v.type() != array_type)
|
||||
{
|
||||
ADD_FAILURE();
|
||||
return Array();
|
||||
}
|
||||
return v.get_array();
|
||||
}
|
||||
|
||||
//#define PRINT_JSON 1
|
||||
#include "json_test_vectors.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace libsnark;
|
||||
|
||||
|
||||
template<typename T>
|
||||
void expect_deser_same(const T& expected)
|
||||
{
|
||||
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss1 << expected;
|
||||
|
||||
auto serialized_size = ss1.size();
|
||||
|
||||
T object;
|
||||
ss1 >> object;
|
||||
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << object;
|
||||
|
||||
ASSERT_TRUE(serialized_size == ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), serialized_size) == 0);
|
||||
}
|
||||
|
||||
template<>
|
||||
void expect_deser_same(const ZCTestingIncrementalWitness& expected)
|
||||
{
|
||||
@@ -86,26 +49,6 @@ void expect_deser_same(const libzcash::MerklePath& expected)
|
||||
// deserialized by Bitcoin's serialization code.
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void expect_test_vector(T& it, const U& expected)
|
||||
{
|
||||
expect_deser_same(expected);
|
||||
|
||||
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss1 << expected;
|
||||
|
||||
#ifdef PRINT_JSON
|
||||
std::cout << "\t\"" ;
|
||||
std::cout << HexStr(ss1.begin(), ss1.end()) << "\",\n";
|
||||
#else
|
||||
std::string raw = (it++)->get_str();
|
||||
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
ASSERT_TRUE(ss1.size() == ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), ss1.size()) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
||||
expect_test_vector<B, C>(b, c);
|
||||
@@ -227,8 +170,6 @@ void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array
|
||||
}
|
||||
|
||||
TEST(merkletree, vectors) {
|
||||
libsnark::default_r1cs_ppzksnark_pp::init_public_params();
|
||||
|
||||
Array root_tests = read_json(std::string(json_tests::merkle_roots, json_tests::merkle_roots + sizeof(json_tests::merkle_roots)));
|
||||
Array ser_tests = read_json(std::string(json_tests::merkle_serialization, json_tests::merkle_serialization + sizeof(json_tests::merkle_serialization)));
|
||||
Array witness_ser_tests = read_json(std::string(json_tests::merkle_witness_serialization, json_tests::merkle_witness_serialization + sizeof(json_tests::merkle_witness_serialization)));
|
||||
|
||||
553
src/gtest/test_proofs.cpp
Normal file
553
src/gtest/test_proofs.cpp
Normal file
@@ -0,0 +1,553 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "zcash/Proof.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
|
||||
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
|
||||
#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
|
||||
#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp"
|
||||
|
||||
using namespace libzcash;
|
||||
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp curve_pp;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::G1_type curve_G1;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::G2_type curve_G2;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::GT_type curve_GT;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::Fp_type curve_Fr;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::Fq_type curve_Fq;
|
||||
typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2;
|
||||
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
TEST(proofs, sqrt_fq)
|
||||
{
|
||||
// Poor man's PRNG
|
||||
curve_Fq acc = curve_Fq("348957923485290374852379485") ^ 1000;
|
||||
|
||||
size_t quadratic_residues = 0;
|
||||
size_t quadratic_nonresidues = 0;
|
||||
|
||||
for (size_t i = 1; i < 1000; i++) {
|
||||
try {
|
||||
acc += curve_Fq("45634563456") ^ i;
|
||||
|
||||
curve_Fq x = acc.sqrt();
|
||||
ASSERT_TRUE((x*x) == acc);
|
||||
quadratic_residues += 1;
|
||||
} catch (std::runtime_error &e) {
|
||||
quadratic_nonresidues += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Half of all nonzero elements in Fp are quadratic residues
|
||||
ASSERT_TRUE(quadratic_residues == 511);
|
||||
ASSERT_TRUE(quadratic_nonresidues == 488);
|
||||
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_Fq x = curve_Fq::random_element();
|
||||
curve_Fq x2 = x * x;
|
||||
|
||||
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x));
|
||||
}
|
||||
|
||||
// Test vectors
|
||||
ASSERT_TRUE(
|
||||
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601")
|
||||
==
|
||||
curve_Fq("348579348568").sqrt()
|
||||
);
|
||||
ASSERT_THROW(curve_Fq("348579348569").sqrt(), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(proofs, sqrt_fq2)
|
||||
{
|
||||
curve_Fq2 acc = curve_Fq2(
|
||||
curve_Fq("3456293840592348059238409578239048769348760238476029347885092384059238459834") ^ 1000,
|
||||
curve_Fq("2394578084760439457823945729347502374590283479582739485723945729384759823745") ^ 1000
|
||||
);
|
||||
|
||||
size_t quadratic_residues = 0;
|
||||
size_t quadratic_nonresidues = 0;
|
||||
|
||||
for (size_t i = 1; i < 1000; i++) {
|
||||
try {
|
||||
acc = acc + curve_Fq2(
|
||||
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601") ^ i,
|
||||
curve_Fq("348957923485290374852379485348957923485290374852379485348957923485290374852") ^ i
|
||||
);
|
||||
|
||||
curve_Fq2 x = acc.sqrt();
|
||||
ASSERT_TRUE((x*x) == acc);
|
||||
quadratic_residues += 1;
|
||||
} catch (std::runtime_error &e) {
|
||||
quadratic_nonresidues += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Half of all nonzero elements in Fp^k are quadratic residues as long
|
||||
// as p != 2
|
||||
ASSERT_TRUE(quadratic_residues == 505);
|
||||
ASSERT_TRUE(quadratic_nonresidues == 494);
|
||||
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_Fq2 x = curve_Fq2::random_element();
|
||||
curve_Fq2 x2 = x * x;
|
||||
|
||||
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x));
|
||||
}
|
||||
|
||||
// Test vectors
|
||||
ASSERT_THROW(curve_Fq2(
|
||||
curve_Fq("2"),
|
||||
curve_Fq("1")
|
||||
).sqrt(), std::runtime_error);
|
||||
|
||||
ASSERT_THROW(curve_Fq2(
|
||||
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589473"),
|
||||
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676")
|
||||
).sqrt(), std::runtime_error);
|
||||
|
||||
curve_Fq2 x = curve_Fq2(
|
||||
curve_Fq("12844195307879678418043983815760255909500142247603239203345049921980497041944"),
|
||||
curve_Fq("7476417578426924565731404322659619974551724117137577781074613937423560117731")
|
||||
);
|
||||
|
||||
curve_Fq2 nx = -x;
|
||||
|
||||
curve_Fq2 x2 = curve_Fq2(
|
||||
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589474"),
|
||||
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676")
|
||||
);
|
||||
|
||||
ASSERT_TRUE(x == x2.sqrt());
|
||||
ASSERT_TRUE(nx == -x2.sqrt());
|
||||
ASSERT_TRUE(x*x == x2);
|
||||
ASSERT_TRUE(nx*nx == x2);
|
||||
}
|
||||
|
||||
TEST(proofs, size_is_expected)
|
||||
{
|
||||
ZCProof p;
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << p;
|
||||
|
||||
ASSERT_EQ(ss.size(), 296);
|
||||
}
|
||||
|
||||
TEST(proofs, fq_serializes_properly)
|
||||
{
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_Fq e = curve_Fq::random_element();
|
||||
|
||||
Fq e2(e);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << e2;
|
||||
|
||||
Fq e3;
|
||||
ss >> e3;
|
||||
|
||||
curve_Fq e4 = e3.to_libsnark_fq<curve_Fq>();
|
||||
|
||||
ASSERT_TRUE(e == e4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(proofs, fq2_serializes_properly)
|
||||
{
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_Fq2 e = curve_Fq2::random_element();
|
||||
|
||||
Fq2 e2(e);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << e2;
|
||||
|
||||
Fq2 e3;
|
||||
ss >> e3;
|
||||
|
||||
curve_Fq2 e4 = e3.to_libsnark_fq2<curve_Fq2>();
|
||||
|
||||
ASSERT_TRUE(e == e4);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T deserialize_tv(std::string s)
|
||||
{
|
||||
T e;
|
||||
CDataStream ss(ParseHex(s), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> e;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
curve_Fq deserialize_fq(std::string s)
|
||||
{
|
||||
return deserialize_tv<Fq>(s).to_libsnark_fq<curve_Fq>();
|
||||
}
|
||||
|
||||
curve_Fq2 deserialize_fq2(std::string s)
|
||||
{
|
||||
return deserialize_tv<Fq2>(s).to_libsnark_fq2<curve_Fq2>();
|
||||
}
|
||||
|
||||
TEST(proofs, fq_valid)
|
||||
{
|
||||
curve_Fq e = deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46");
|
||||
|
||||
ASSERT_TRUE(e == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
|
||||
ASSERT_TRUE(e != curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208581"));
|
||||
|
||||
curve_Fq e2 = deserialize_fq("30644e72e131a029b75045b68181585d97816a916871ca8d3c208c16d87cfd46");
|
||||
|
||||
ASSERT_TRUE(e2 == curve_Fq("21888242871839275222221885816603420866962577604863418715751138068690288573766"));
|
||||
}
|
||||
|
||||
TEST(proofs, fq_invalid)
|
||||
{
|
||||
// Should not be able to deserialize the modulus
|
||||
ASSERT_THROW(
|
||||
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"),
|
||||
std::logic_error
|
||||
);
|
||||
|
||||
// Should not be able to deserialize the modulus plus one
|
||||
ASSERT_THROW(
|
||||
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd48"),
|
||||
std::logic_error
|
||||
);
|
||||
|
||||
// Should not be able to deserialize a ridiculously out of bound int
|
||||
ASSERT_THROW(
|
||||
deserialize_fq("ff644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"),
|
||||
std::logic_error
|
||||
);
|
||||
}
|
||||
|
||||
TEST(proofs, fq2_valid)
|
||||
{
|
||||
// (q - 1) * q + q
|
||||
curve_Fq2 e = deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0");
|
||||
ASSERT_TRUE(e.c0 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
|
||||
ASSERT_TRUE(e.c1 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
|
||||
|
||||
curve_Fq2 e2 = deserialize_fq2("000000000000000000000000000000000000000000000000010245be1c91e3186bbbe1c430a93fcfc5aada4ab10c3492f70eea97a91c7b29554db55acffa34d2");
|
||||
ASSERT_TRUE(e2.c0 == curve_Fq("238769481237490823"));
|
||||
ASSERT_TRUE(e2.c1 == curve_Fq("384579238459723485"));
|
||||
|
||||
curve_Fq2 e3 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
ASSERT_TRUE(e3.c0 == curve_Fq("0"));
|
||||
ASSERT_TRUE(e3.c1 == curve_Fq("0"));
|
||||
|
||||
curve_Fq2 e4 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
|
||||
ASSERT_TRUE(e4.c0 == curve_Fq("1"));
|
||||
ASSERT_TRUE(e4.c1 == curve_Fq("0"));
|
||||
}
|
||||
|
||||
TEST(proofs, fq2_invalid)
|
||||
{
|
||||
// (q - 1) * q + q is invalid
|
||||
ASSERT_THROW(
|
||||
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b1"),
|
||||
std::logic_error
|
||||
);
|
||||
|
||||
// q * q + (q - 1) is invalid
|
||||
ASSERT_THROW(
|
||||
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d34cced085b43e2f202a05e52ef18233a3d8371be725c8b8e7774e4b8ffda66f7"),
|
||||
std::logic_error
|
||||
);
|
||||
|
||||
// Ridiculously out of bounds
|
||||
ASSERT_THROW(
|
||||
deserialize_fq2("0fffc4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"),
|
||||
std::logic_error
|
||||
);
|
||||
ASSERT_THROW(
|
||||
deserialize_fq2("ffffffff763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"),
|
||||
std::logic_error
|
||||
);
|
||||
}
|
||||
|
||||
TEST(proofs, g1_serializes_properly)
|
||||
{
|
||||
// Cannot serialize zero
|
||||
{
|
||||
ASSERT_THROW({CompressedG1 g = CompressedG1(curve_G1::zero());}, std::domain_error);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_G1 e = curve_G1::random_element();
|
||||
|
||||
CompressedG1 e2(e);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << e2;
|
||||
|
||||
CompressedG1 e3;
|
||||
ss >> e3;
|
||||
|
||||
ASSERT_TRUE(e2 == e3);
|
||||
|
||||
curve_G1 e4 = e3.to_libsnark_g1<curve_G1>();
|
||||
|
||||
ASSERT_TRUE(e == e4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(proofs, g2_serializes_properly)
|
||||
{
|
||||
// Cannot serialize zero
|
||||
{
|
||||
ASSERT_THROW({CompressedG2 g = CompressedG2(curve_G2::zero());}, std::domain_error);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
curve_G2 e = curve_G2::random_element();
|
||||
|
||||
CompressedG2 e2(e);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << e2;
|
||||
|
||||
CompressedG2 e3;
|
||||
ss >> e3;
|
||||
|
||||
ASSERT_TRUE(e2 == e3);
|
||||
|
||||
curve_G2 e4 = e3.to_libsnark_g2<curve_G2>();
|
||||
|
||||
ASSERT_TRUE(e == e4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(proofs, zksnark_serializes_properly)
|
||||
{
|
||||
auto example = libsnark::generate_r1cs_example_with_field_input<curve_Fr>(250, 4);
|
||||
example.constraint_system.swap_AB_if_beneficial();
|
||||
auto kp = libsnark::r1cs_ppzksnark_generator<curve_pp>(example.constraint_system);
|
||||
|
||||
for (size_t i = 0; i < 20; i++) {
|
||||
auto proof = libsnark::r1cs_ppzksnark_prover<curve_pp>(
|
||||
kp.pk,
|
||||
example.primary_input,
|
||||
example.auxiliary_input,
|
||||
example.constraint_system
|
||||
);
|
||||
|
||||
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>(
|
||||
kp.vk,
|
||||
example.primary_input,
|
||||
proof
|
||||
));
|
||||
|
||||
ZCProof compressed_proof_0(proof);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << compressed_proof_0;
|
||||
|
||||
ZCProof compressed_proof_1;
|
||||
ss >> compressed_proof_1;
|
||||
|
||||
ASSERT_TRUE(compressed_proof_0 == compressed_proof_1);
|
||||
|
||||
auto newproof = compressed_proof_1.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>();
|
||||
|
||||
ASSERT_TRUE(proof == newproof);
|
||||
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>(
|
||||
kp.vk,
|
||||
example.primary_input,
|
||||
newproof
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(proofs, g1_deserialization)
|
||||
{
|
||||
CompressedG1 g;
|
||||
curve_G1 expected;
|
||||
|
||||
// Valid G1 element.
|
||||
{
|
||||
CDataStream ss(ParseHex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
|
||||
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776");
|
||||
expected.Z = curve_Fq::one();
|
||||
|
||||
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == expected);
|
||||
}
|
||||
|
||||
// Its negation.
|
||||
{
|
||||
CDataStream ss(ParseHex("0330644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
|
||||
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776");
|
||||
expected.Z = curve_Fq::one();
|
||||
|
||||
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == -expected);
|
||||
}
|
||||
|
||||
// Invalid leading bytes
|
||||
{
|
||||
CDataStream ss(ParseHex("ff30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
ASSERT_THROW(ss >> g, std::ios_base::failure);
|
||||
}
|
||||
|
||||
// Invalid point
|
||||
{
|
||||
CDataStream ss(ParseHex("0208c6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::runtime_error);
|
||||
}
|
||||
|
||||
// Point with out of bounds Fq
|
||||
{
|
||||
CDataStream ss(ParseHex("02ffc6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::logic_error);
|
||||
}
|
||||
|
||||
// Randomly produce valid G1 representations and fail/succeed to
|
||||
// turn them into G1 points based on whether they are valid.
|
||||
for (size_t i = 0; i < 5000; i++) {
|
||||
curve_Fq e = curve_Fq::random_element();
|
||||
CDataStream ss(ParseHex("02"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << Fq(e);
|
||||
CompressedG1 g;
|
||||
ss >> g;
|
||||
|
||||
try {
|
||||
curve_G1 g_real = g.to_libsnark_g1<curve_G1>();
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(proofs, g2_deserialization)
|
||||
{
|
||||
CompressedG2 g;
|
||||
curve_G2 expected = curve_G2::random_element();
|
||||
|
||||
// Valid G2 point
|
||||
{
|
||||
CDataStream ss(ParseHex("0a023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
expected.X = curve_Fq2(
|
||||
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"),
|
||||
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570")
|
||||
);
|
||||
expected.Y = curve_Fq2(
|
||||
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"),
|
||||
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932")
|
||||
);
|
||||
expected.Z = curve_Fq2::one();
|
||||
|
||||
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == expected);
|
||||
}
|
||||
|
||||
// Its negation
|
||||
{
|
||||
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
expected.X = curve_Fq2(
|
||||
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"),
|
||||
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570")
|
||||
);
|
||||
expected.Y = curve_Fq2(
|
||||
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"),
|
||||
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932")
|
||||
);
|
||||
expected.Z = curve_Fq2::one();
|
||||
|
||||
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == -expected);
|
||||
}
|
||||
|
||||
// Invalid leading bytes
|
||||
{
|
||||
CDataStream ss(ParseHex("ff023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
ASSERT_THROW(ss >> g, std::ios_base::failure);
|
||||
}
|
||||
|
||||
|
||||
// Invalid point
|
||||
{
|
||||
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984b"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::runtime_error);
|
||||
}
|
||||
|
||||
// Point with out of bounds Fq2
|
||||
{
|
||||
CDataStream ss(ParseHex("0a0f3aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss >> g;
|
||||
|
||||
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::logic_error);
|
||||
}
|
||||
|
||||
// Randomly produce valid G2 representations and fail/succeed to
|
||||
// turn them into G2 points based on whether they are valid.
|
||||
for (size_t i = 0; i < 5000; i++) {
|
||||
curve_Fq2 e = curve_Fq2::random_element();
|
||||
CDataStream ss(ParseHex("0a"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << Fq2(e);
|
||||
CompressedG2 g;
|
||||
ss >> g;
|
||||
|
||||
try {
|
||||
curve_G2 g_real = g.to_libsnark_g2<curve_G2>();
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "json_test_vectors.h"
|
||||
#include "test/data/g1_compressed.json.h"
|
||||
|
||||
TEST(proofs, g1_test_vectors)
|
||||
{
|
||||
Array v = read_json(std::string(json_tests::g1_compressed, json_tests::g1_compressed + sizeof(json_tests::g1_compressed)));
|
||||
Array::iterator v_iterator = v.begin();
|
||||
|
||||
curve_G1 e = curve_Fr("34958239045823") * curve_G1::one();
|
||||
for (size_t i = 0; i < 10000; i++) {
|
||||
e = (curve_Fr("34958239045823") ^ i) * e;
|
||||
auto expected = CompressedG1(e);
|
||||
|
||||
expect_test_vector(v_iterator, expected);
|
||||
ASSERT_TRUE(expected.to_libsnark_g1<curve_G1>() == e);
|
||||
}
|
||||
}
|
||||
|
||||
#include "test/data/g2_compressed.json.h"
|
||||
|
||||
TEST(proofs, g2_test_vectors)
|
||||
{
|
||||
Array v = read_json(std::string(json_tests::g2_compressed, json_tests::g2_compressed + sizeof(json_tests::g2_compressed)));
|
||||
Array::iterator v_iterator = v.begin();
|
||||
|
||||
curve_G2 e = curve_Fr("34958239045823") * curve_G2::one();
|
||||
for (size_t i = 0; i < 10000; i++) {
|
||||
e = (curve_Fr("34958239045823") ^ i) * e;
|
||||
auto expected = CompressedG2(e);
|
||||
|
||||
expect_test_vector(v_iterator, expected);
|
||||
ASSERT_TRUE(expected.to_libsnark_g2<curve_G2>() == e);
|
||||
}
|
||||
}
|
||||
@@ -603,8 +603,8 @@ static void ZC_LoadParams()
|
||||
struct timeval tv_start, tv_end;
|
||||
float elapsed;
|
||||
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z7-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z7-verifying.key";
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z9-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z9-verifying.key";
|
||||
|
||||
pzcashParams = ZCJoinSplit::Unopened();
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/Zcash.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
#include "zcash/Proof.hpp"
|
||||
|
||||
class JSDescription
|
||||
{
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
|
||||
// JoinSplit proof
|
||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> proof;
|
||||
libzcash::ZCProof proof;
|
||||
|
||||
JSDescription(): vpub_old(0), vpub_new(0) { }
|
||||
|
||||
|
||||
10002
src/test/data/g1_compressed.json
Normal file
10002
src/test/data/g1_compressed.json
Normal file
File diff suppressed because it is too large
Load Diff
10002
src/test/data/g2_compressed.json
Normal file
10002
src/test/data/g2_compressed.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -136,7 +136,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
||||
jsdesc.randomSeed = GetRandHash();
|
||||
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
||||
randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size());
|
||||
randombytes_buf(jsdesc.proof.begin(), jsdesc.proof.size());
|
||||
jsdesc.proof = libzcash::ZCProof::random_invalid();
|
||||
jsdesc.macs[0] = GetRandHash();
|
||||
jsdesc.macs[1] = GetRandHash();
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ BasicTestingSetup::BasicTestingSetup()
|
||||
{
|
||||
assert(init_and_check_sodium() != -1);
|
||||
ECC_Start();
|
||||
pzcashParams = ZCJoinSplit::Unopened();
|
||||
SetupEnvironment();
|
||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||
fCheckBlockIndex = true;
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
JoinSplitCircuit() {}
|
||||
|
||||
bool verify(
|
||||
const boost::array<unsigned char, ZKSNARK_PROOF_SIZE>& proof,
|
||||
const ZCProof& proof,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& macs,
|
||||
@@ -140,11 +140,7 @@ public:
|
||||
}
|
||||
|
||||
try {
|
||||
r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
|
||||
std::stringstream ss;
|
||||
std::string proof_str(proof.begin(), proof.end());
|
||||
ss.str(proof_str);
|
||||
ss >> r1cs_proof;
|
||||
auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();
|
||||
|
||||
uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||
|
||||
@@ -164,7 +160,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> prove(
|
||||
ZCProof prove(
|
||||
const boost::array<JSInput, NumInputs>& inputs,
|
||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||
boost::array<Note, NumOutputs>& out_notes,
|
||||
@@ -264,23 +260,12 @@ public:
|
||||
// estimate that it doesn't matter if we check every time.
|
||||
pb.constraint_system.swap_AB_if_beneficial();
|
||||
|
||||
auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
|
||||
return ZCProof(r1cs_ppzksnark_prover<ppzksnark_ppT>(
|
||||
*pk,
|
||||
primary_input,
|
||||
aux_input,
|
||||
pb.constraint_system
|
||||
);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << proof;
|
||||
std::string serialized_proof = ss.str();
|
||||
|
||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> result_proof;
|
||||
//std::cout << "proof size in bytes when serialized: " << serialized_proof.size() << std::endl;
|
||||
assert(serialized_proof.size() == ZKSNARK_PROOF_SIZE);
|
||||
memcpy(&result_proof[0], &serialized_proof[0], ZKSNARK_PROOF_SIZE);
|
||||
|
||||
return result_proof;
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _ZCJOINSPLIT_H_
|
||||
|
||||
#include "Zcash.h"
|
||||
#include "Proof.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Note.hpp"
|
||||
#include "IncrementalMerkleTree.hpp"
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
virtual void loadVerifyingKey(std::string path) = 0;
|
||||
virtual void saveVerifyingKey(std::string path) = 0;
|
||||
|
||||
virtual boost::array<unsigned char, ZKSNARK_PROOF_SIZE> prove(
|
||||
virtual ZCProof prove(
|
||||
const boost::array<JSInput, NumInputs>& inputs,
|
||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||
boost::array<Note, NumOutputs>& out_notes,
|
||||
@@ -76,7 +77,7 @@ public:
|
||||
) = 0;
|
||||
|
||||
virtual bool verify(
|
||||
const boost::array<unsigned char, ZKSNARK_PROOF_SIZE>& proof,
|
||||
const ZCProof& proof,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& hmacs,
|
||||
@@ -96,4 +97,4 @@ protected:
|
||||
typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS,
|
||||
ZC_NUM_JS_OUTPUTS> ZCJoinSplit;
|
||||
|
||||
#endif // _ZCJOINSPLIT_H_
|
||||
#endif // _ZCJOINSPLIT_H_
|
||||
|
||||
214
src/zcash/Proof.cpp
Normal file
214
src/zcash/Proof.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "Proof.hpp"
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#include "crypto/common.h"
|
||||
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
|
||||
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
typedef alt_bn128_pp curve_pp;
|
||||
typedef alt_bn128_pp::G1_type curve_G1;
|
||||
typedef alt_bn128_pp::G2_type curve_G2;
|
||||
typedef alt_bn128_pp::GT_type curve_GT;
|
||||
typedef alt_bn128_pp::Fp_type curve_Fr;
|
||||
typedef alt_bn128_pp::Fq_type curve_Fq;
|
||||
typedef alt_bn128_pp::Fqe_type curve_Fq2;
|
||||
|
||||
BOOST_STATIC_ASSERT(sizeof(mp_limb_t) == 8);
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
// FE2IP as defined in the protocol spec and IEEE Std 1363a-2004.
|
||||
bigint<8> fq2_to_bigint(const curve_Fq2 &e)
|
||||
{
|
||||
auto modq = curve_Fq::field_char();
|
||||
auto c0 = e.c0.as_bigint();
|
||||
auto c1 = e.c1.as_bigint();
|
||||
|
||||
bigint<8> temp = c1 * modq;
|
||||
temp += c0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// Writes a bigint in big endian
|
||||
template<mp_size_t LIMBS>
|
||||
void write_bigint(base_blob<8 * LIMBS * sizeof(mp_limb_t)> &blob, const bigint<LIMBS> &val)
|
||||
{
|
||||
auto ptr = blob.begin();
|
||||
for (ssize_t i = LIMBS-1; i >= 0; i--, ptr += 8) {
|
||||
WriteBE64(ptr, val.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a bigint from big endian
|
||||
template<mp_size_t LIMBS>
|
||||
bigint<LIMBS> read_bigint(const base_blob<8 * LIMBS * sizeof(mp_limb_t)> &blob)
|
||||
{
|
||||
bigint<LIMBS> ret;
|
||||
|
||||
auto ptr = blob.begin();
|
||||
|
||||
for (ssize_t i = LIMBS-1; i >= 0; i--, ptr += 8) {
|
||||
ret.data[i] = ReadBE64(ptr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
Fq::Fq(curve_Fq element) : data()
|
||||
{
|
||||
write_bigint<4>(data, element.as_bigint());
|
||||
}
|
||||
|
||||
template<>
|
||||
curve_Fq Fq::to_libsnark_fq() const
|
||||
{
|
||||
auto element_bigint = read_bigint<4>(data);
|
||||
|
||||
// Check that the integer is smaller than the modulus
|
||||
auto modq = curve_Fq::field_char();
|
||||
element_bigint.limit(modq, "element is not in Fq");
|
||||
|
||||
return curve_Fq(element_bigint);
|
||||
}
|
||||
|
||||
template<>
|
||||
Fq2::Fq2(curve_Fq2 element) : data()
|
||||
{
|
||||
write_bigint<8>(data, fq2_to_bigint(element));
|
||||
}
|
||||
|
||||
template<>
|
||||
curve_Fq2 Fq2::to_libsnark_fq2() const
|
||||
{
|
||||
bigint<4> modq = curve_Fq::field_char();
|
||||
bigint<8> combined = read_bigint<8>(data);
|
||||
bigint<5> res;
|
||||
bigint<4> c0;
|
||||
bigint<8>::div_qr(res, c0, combined, modq);
|
||||
bigint<4> c1 = res.shorten(modq, "element is not in Fq2");
|
||||
|
||||
return curve_Fq2(curve_Fq(c0), curve_Fq(c1));
|
||||
}
|
||||
|
||||
template<>
|
||||
CompressedG1::CompressedG1(curve_G1 point)
|
||||
{
|
||||
if (point.is_zero()) {
|
||||
throw std::domain_error("curve point is zero");
|
||||
}
|
||||
|
||||
point.to_affine_coordinates();
|
||||
|
||||
x = Fq(point.X);
|
||||
y_lsb = point.Y.as_bigint().data[0] & 1;
|
||||
}
|
||||
|
||||
template<>
|
||||
curve_G1 CompressedG1::to_libsnark_g1() const
|
||||
{
|
||||
curve_Fq x_coordinate = x.to_libsnark_fq<curve_Fq>();
|
||||
|
||||
// y = +/- sqrt(x^3 + b)
|
||||
auto y_coordinate = ((x_coordinate.squared() * x_coordinate) + alt_bn128_coeff_b).sqrt();
|
||||
|
||||
if ((y_coordinate.as_bigint().data[0] & 1) != y_lsb) {
|
||||
y_coordinate = -y_coordinate;
|
||||
}
|
||||
|
||||
curve_G1 r = curve_G1::one();
|
||||
r.X = x_coordinate;
|
||||
r.Y = y_coordinate;
|
||||
r.Z = curve_Fq::one();
|
||||
|
||||
assert(r.is_well_formed());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<>
|
||||
CompressedG2::CompressedG2(curve_G2 point)
|
||||
{
|
||||
if (point.is_zero()) {
|
||||
throw std::domain_error("curve point is zero");
|
||||
}
|
||||
|
||||
point.to_affine_coordinates();
|
||||
|
||||
x = Fq2(point.X);
|
||||
y_gt = fq2_to_bigint(point.Y) > fq2_to_bigint(-(point.Y));
|
||||
}
|
||||
|
||||
template<>
|
||||
curve_G2 CompressedG2::to_libsnark_g2() const
|
||||
{
|
||||
auto x_coordinate = x.to_libsnark_fq2<curve_Fq2>();
|
||||
|
||||
// y = +/- sqrt(x^3 + b)
|
||||
auto y_coordinate = ((x_coordinate.squared() * x_coordinate) + alt_bn128_twist_coeff_b).sqrt();
|
||||
auto y_coordinate_neg = -y_coordinate;
|
||||
|
||||
if ((fq2_to_bigint(y_coordinate) > fq2_to_bigint(y_coordinate_neg)) != y_gt) {
|
||||
y_coordinate = y_coordinate_neg;
|
||||
}
|
||||
|
||||
curve_G2 r = curve_G2::one();
|
||||
r.X = x_coordinate;
|
||||
r.Y = y_coordinate;
|
||||
r.Z = curve_Fq2::one();
|
||||
|
||||
assert(r.is_well_formed());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<>
|
||||
ZCProof::ZCProof(const r1cs_ppzksnark_proof<curve_pp> &proof)
|
||||
{
|
||||
g_A = CompressedG1(proof.g_A.g);
|
||||
g_A_prime = CompressedG1(proof.g_A.h);
|
||||
g_B = CompressedG2(proof.g_B.g);
|
||||
g_B_prime = CompressedG1(proof.g_B.h);
|
||||
g_C = CompressedG1(proof.g_C.g);
|
||||
g_C_prime = CompressedG1(proof.g_C.h);
|
||||
g_K = CompressedG1(proof.g_K);
|
||||
g_H = CompressedG1(proof.g_H);
|
||||
}
|
||||
|
||||
template<>
|
||||
r1cs_ppzksnark_proof<curve_pp> ZCProof::to_libsnark_proof() const
|
||||
{
|
||||
r1cs_ppzksnark_proof<curve_pp> proof;
|
||||
|
||||
proof.g_A.g = g_A.to_libsnark_g1<curve_G1>();
|
||||
proof.g_A.h = g_A_prime.to_libsnark_g1<curve_G1>();
|
||||
proof.g_B.g = g_B.to_libsnark_g2<curve_G2>();
|
||||
proof.g_B.h = g_B_prime.to_libsnark_g1<curve_G1>();
|
||||
proof.g_C.g = g_C.to_libsnark_g1<curve_G1>();
|
||||
proof.g_C.h = g_C_prime.to_libsnark_g1<curve_G1>();
|
||||
proof.g_K = g_K.to_libsnark_g1<curve_G1>();
|
||||
proof.g_H = g_H.to_libsnark_g1<curve_G1>();
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
ZCProof ZCProof::random_invalid()
|
||||
{
|
||||
ZCProof p;
|
||||
p.g_A = curve_G1::random_element();
|
||||
p.g_A_prime = curve_G1::random_element();
|
||||
p.g_B = curve_G2::random_element();
|
||||
p.g_B_prime = curve_G1::random_element();
|
||||
p.g_C = curve_G1::random_element();
|
||||
p.g_C_prime = curve_G1::random_element();
|
||||
|
||||
p.g_K = curve_G1::random_element();
|
||||
p.g_H = curve_G1::random_element();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
241
src/zcash/Proof.hpp
Normal file
241
src/zcash/Proof.hpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#ifndef _ZCPROOF_H_
|
||||
#define _ZCPROOF_H_
|
||||
|
||||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
const unsigned char G1_PREFIX_MASK = 0x02;
|
||||
const unsigned char G2_PREFIX_MASK = 0x0a;
|
||||
|
||||
// Element in the base field
|
||||
class Fq {
|
||||
private:
|
||||
base_blob<256> data;
|
||||
public:
|
||||
Fq() : data() { }
|
||||
|
||||
template<typename libsnark_Fq>
|
||||
Fq(libsnark_Fq element);
|
||||
|
||||
template<typename libsnark_Fq>
|
||||
libsnark_Fq to_libsnark_fq() const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
friend bool operator==(const Fq& a, const Fq& b)
|
||||
{
|
||||
return (
|
||||
a.data == b.data
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const Fq& a, const Fq& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
// Element in the extension field
|
||||
class Fq2 {
|
||||
private:
|
||||
base_blob<512> data;
|
||||
public:
|
||||
Fq2() : data() { }
|
||||
|
||||
template<typename libsnark_Fq2>
|
||||
Fq2(libsnark_Fq2 element);
|
||||
|
||||
template<typename libsnark_Fq2>
|
||||
libsnark_Fq2 to_libsnark_fq2() const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
friend bool operator==(const Fq2& a, const Fq2& b)
|
||||
{
|
||||
return (
|
||||
a.data == b.data
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const Fq2& a, const Fq2& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
// Compressed point in G1
|
||||
class CompressedG1 {
|
||||
private:
|
||||
bool y_lsb;
|
||||
Fq x;
|
||||
|
||||
public:
|
||||
CompressedG1() : y_lsb(false), x() { }
|
||||
|
||||
template<typename libsnark_G1>
|
||||
CompressedG1(libsnark_G1 point);
|
||||
|
||||
template<typename libsnark_G1>
|
||||
libsnark_G1 to_libsnark_g1() const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
unsigned char leadingByte = G1_PREFIX_MASK;
|
||||
|
||||
if (y_lsb) {
|
||||
leadingByte |= 1;
|
||||
}
|
||||
|
||||
READWRITE(leadingByte);
|
||||
|
||||
if ((leadingByte & (~1)) != G1_PREFIX_MASK) {
|
||||
throw std::ios_base::failure("lead byte of G1 point not recognized");
|
||||
}
|
||||
|
||||
y_lsb = leadingByte & 1;
|
||||
|
||||
READWRITE(x);
|
||||
}
|
||||
|
||||
friend bool operator==(const CompressedG1& a, const CompressedG1& b)
|
||||
{
|
||||
return (
|
||||
a.y_lsb == b.y_lsb &&
|
||||
a.x == b.x
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CompressedG1& a, const CompressedG1& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
// Compressed point in G2
|
||||
class CompressedG2 {
|
||||
private:
|
||||
bool y_gt;
|
||||
Fq2 x;
|
||||
|
||||
public:
|
||||
CompressedG2() : y_gt(false), x() { }
|
||||
|
||||
template<typename libsnark_G2>
|
||||
CompressedG2(libsnark_G2 point);
|
||||
|
||||
template<typename libsnark_G2>
|
||||
libsnark_G2 to_libsnark_g2() const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
unsigned char leadingByte = G2_PREFIX_MASK;
|
||||
|
||||
if (y_gt) {
|
||||
leadingByte |= 1;
|
||||
}
|
||||
|
||||
READWRITE(leadingByte);
|
||||
|
||||
if ((leadingByte & (~1)) != G2_PREFIX_MASK) {
|
||||
throw std::ios_base::failure("lead byte of G2 point not recognized");
|
||||
}
|
||||
|
||||
y_gt = leadingByte & 1;
|
||||
|
||||
READWRITE(x);
|
||||
}
|
||||
|
||||
friend bool operator==(const CompressedG2& a, const CompressedG2& b)
|
||||
{
|
||||
return (
|
||||
a.y_gt == b.y_gt &&
|
||||
a.x == b.x
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CompressedG2& a, const CompressedG2& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
// Compressed zkSNARK proof
|
||||
class ZCProof {
|
||||
private:
|
||||
CompressedG1 g_A;
|
||||
CompressedG1 g_A_prime;
|
||||
CompressedG2 g_B;
|
||||
CompressedG1 g_B_prime;
|
||||
CompressedG1 g_C;
|
||||
CompressedG1 g_C_prime;
|
||||
CompressedG1 g_K;
|
||||
CompressedG1 g_H;
|
||||
|
||||
public:
|
||||
ZCProof() : g_A(), g_A_prime(), g_B(), g_B_prime(), g_C(), g_C_prime(), g_K(), g_H() { }
|
||||
|
||||
// Produces a compressed proof using a libsnark zkSNARK proof
|
||||
template<typename libsnark_proof>
|
||||
ZCProof(const libsnark_proof& proof);
|
||||
|
||||
// Produces a libsnark zkSNARK proof out of this proof,
|
||||
// or throws an exception if it is invalid.
|
||||
template<typename libsnark_proof>
|
||||
libsnark_proof to_libsnark_proof() const;
|
||||
|
||||
static ZCProof random_invalid();
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(g_A);
|
||||
READWRITE(g_A_prime);
|
||||
READWRITE(g_B);
|
||||
READWRITE(g_B_prime);
|
||||
READWRITE(g_C);
|
||||
READWRITE(g_C_prime);
|
||||
READWRITE(g_K);
|
||||
READWRITE(g_H);
|
||||
}
|
||||
|
||||
friend bool operator==(const ZCProof& a, const ZCProof& b)
|
||||
{
|
||||
return (
|
||||
a.g_A == b.g_A &&
|
||||
a.g_A_prime == b.g_A_prime &&
|
||||
a.g_B == b.g_B &&
|
||||
a.g_B_prime == b.g_B_prime &&
|
||||
a.g_C == b.g_C &&
|
||||
a.g_C_prime == b.g_C_prime &&
|
||||
a.g_K == b.g_K &&
|
||||
a.g_H == b.g_H
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const ZCProof& a, const ZCProof& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // _ZCPROOF_H_
|
||||
@@ -14,6 +14,4 @@
|
||||
|
||||
#define ZC_NOTEPLAINTEXT_SIZE (ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE)
|
||||
|
||||
#define ZKSNARK_PROOF_SIZE 584
|
||||
|
||||
#endif // _ZCCONSTANTS_H_
|
||||
|
||||
@@ -51,8 +51,8 @@ double benchmark_sleep()
|
||||
double benchmark_parameter_loading()
|
||||
{
|
||||
// FIXME: this is duplicated with the actual loading code
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z7-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z7-verifying.key";
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z9-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z9-verifying.key";
|
||||
|
||||
timer_start();
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ set -eu
|
||||
|
||||
PARAMS_DIR="$HOME/.zcash-params"
|
||||
|
||||
REGTEST_PKEY_NAME='z7-proving.key'
|
||||
REGTEST_VKEY_NAME='z7-verifying.key'
|
||||
REGTEST_PKEY_NAME='z9-proving.key'
|
||||
REGTEST_VKEY_NAME='z9-verifying.key'
|
||||
REGTEST_PKEY_URL="https://z.cash/downloads/$REGTEST_PKEY_NAME"
|
||||
REGTEST_VKEY_URL="https://z.cash/downloads/$REGTEST_VKEY_NAME"
|
||||
REGTEST_DIR="$PARAMS_DIR/regtest"
|
||||
@@ -86,9 +86,9 @@ cd "$PARAMS_DIR"
|
||||
# Now verify their hashes:
|
||||
echo 'Verifying parameter file integrity via sha256sum...'
|
||||
shasum -a 256 --check <<EOF
|
||||
18bf2599787a2ed6ee4218d433983d15195944f8a40e09ef9cb80c186c142bc3 regtest/$REGTEST_PKEY_NAME
|
||||
18bf2599787a2ed6ee4218d433983d15195944f8a40e09ef9cb80c186c142bc3 testnet3/$REGTEST_PKEY_NAME
|
||||
239ddba9249bdd1f4ba7654b17a960bd319c1cefcd3cd79883b422d0f4a806da regtest/$REGTEST_VKEY_NAME
|
||||
239ddba9249bdd1f4ba7654b17a960bd319c1cefcd3cd79883b422d0f4a806da testnet3/$REGTEST_VKEY_NAME
|
||||
226913bbdc48b70834f8e044d194ddb61c8e15329f67cdc6014f4e5ac11a82ab regtest/$REGTEST_PKEY_NAME
|
||||
226913bbdc48b70834f8e044d194ddb61c8e15329f67cdc6014f4e5ac11a82ab testnet3/$REGTEST_PKEY_NAME
|
||||
4c151c562fce2cdee55ac0a0f8bd9454eb69e6a0db9a8443b58b770ec29b37f5 regtest/$REGTEST_VKEY_NAME
|
||||
4c151c562fce2cdee55ac0a0f8bd9454eb69e6a0db9a8443b58b770ec29b37f5 testnet3/$REGTEST_VKEY_NAME
|
||||
EOF
|
||||
|
||||
|
||||
Reference in New Issue
Block a user