Implement zkSNARK compression.
This commit is contained in:
@@ -411,6 +411,7 @@ libzcash_a_SOURCES = \
|
|||||||
zcash/NoteEncryption.cpp \
|
zcash/NoteEncryption.cpp \
|
||||||
zcash/Address.cpp \
|
zcash/Address.cpp \
|
||||||
zcash/JoinSplit.cpp \
|
zcash/JoinSplit.cpp \
|
||||||
|
zcash/Proof.cpp \
|
||||||
zcash/Note.cpp \
|
zcash/Note.cpp \
|
||||||
zcash/prf.cpp \
|
zcash/prf.cpp \
|
||||||
zcash/util.cpp
|
zcash/util.cpp
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ bin_PROGRAMS += zcash-gtest
|
|||||||
# tool for generating our public parameters
|
# tool for generating our public parameters
|
||||||
zcash_gtest_SOURCES = \
|
zcash_gtest_SOURCES = \
|
||||||
gtest/main.cpp \
|
gtest/main.cpp \
|
||||||
|
gtest/json_test_vectors.cpp \
|
||||||
gtest/test_tautology.cpp \
|
gtest/test_tautology.cpp \
|
||||||
gtest/test_checktransaction.cpp \
|
gtest/test_checktransaction.cpp \
|
||||||
gtest/test_equihash.cpp \
|
gtest/test_equihash.cpp \
|
||||||
@@ -13,7 +14,8 @@ zcash_gtest_SOURCES = \
|
|||||||
gtest/test_merkletree.cpp \
|
gtest/test_merkletree.cpp \
|
||||||
gtest/test_circuit.cpp \
|
gtest/test_circuit.cpp \
|
||||||
gtest/test_txid.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
|
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_roots_empty.json \
|
||||||
test/data/merkle_serialization.json \
|
test/data/merkle_serialization.json \
|
||||||
test/data/merkle_witness_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
|
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
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ void test_full_api(ZCJoinSplit* js)
|
|||||||
boost::array<uint256, 2> commitments;
|
boost::array<uint256, 2> commitments;
|
||||||
uint256 rt = tree.root();
|
uint256 rt = tree.root();
|
||||||
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> proof;
|
ZCProof proof;
|
||||||
|
|
||||||
{
|
{
|
||||||
boost::array<JSInput, 2> inputs = {
|
boost::array<JSInput, 2> inputs = {
|
||||||
|
|||||||
@@ -25,48 +25,11 @@
|
|||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include "json/json_spirit_reader_template.h"
|
#include "json_test_vectors.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
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace libsnark;
|
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<>
|
template<>
|
||||||
void expect_deser_same(const ZCTestingIncrementalWitness& expected)
|
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.
|
// 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>
|
template<typename A, typename B, typename C>
|
||||||
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
||||||
expect_test_vector<B, C>(b, c);
|
expect_test_vector<B, C>(b, c);
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "zcash/NoteEncryption.hpp"
|
#include "zcash/NoteEncryption.hpp"
|
||||||
#include "zcash/Zcash.h"
|
#include "zcash/Zcash.h"
|
||||||
#include "zcash/JoinSplit.hpp"
|
#include "zcash/JoinSplit.hpp"
|
||||||
|
#include "zcash/Proof.hpp"
|
||||||
|
|
||||||
class JSDescription
|
class JSDescription
|
||||||
{
|
{
|
||||||
@@ -63,7 +64,7 @@ public:
|
|||||||
|
|
||||||
// JoinSplit proof
|
// JoinSplit proof
|
||||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
// 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) { }
|
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();
|
jsdesc.randomSeed = GetRandHash();
|
||||||
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
||||||
randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].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[0] = GetRandHash();
|
||||||
jsdesc.macs[1] = GetRandHash();
|
jsdesc.macs[1] = GetRandHash();
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ BasicTestingSetup::BasicTestingSetup()
|
|||||||
{
|
{
|
||||||
assert(init_and_check_sodium() != -1);
|
assert(init_and_check_sodium() != -1);
|
||||||
ECC_Start();
|
ECC_Start();
|
||||||
|
pzcashParams = ZCJoinSplit::Unopened();
|
||||||
SetupEnvironment();
|
SetupEnvironment();
|
||||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||||
fCheckBlockIndex = true;
|
fCheckBlockIndex = true;
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public:
|
|||||||
JoinSplitCircuit() {}
|
JoinSplitCircuit() {}
|
||||||
|
|
||||||
bool verify(
|
bool verify(
|
||||||
const boost::array<unsigned char, ZKSNARK_PROOF_SIZE>& proof,
|
const ZCProof& proof,
|
||||||
const uint256& pubKeyHash,
|
const uint256& pubKeyHash,
|
||||||
const uint256& randomSeed,
|
const uint256& randomSeed,
|
||||||
const boost::array<uint256, NumInputs>& macs,
|
const boost::array<uint256, NumInputs>& macs,
|
||||||
@@ -140,11 +140,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
|
auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();
|
||||||
std::stringstream ss;
|
|
||||||
std::string proof_str(proof.begin(), proof.end());
|
|
||||||
ss.str(proof_str);
|
|
||||||
ss >> r1cs_proof;
|
|
||||||
|
|
||||||
uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
|
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<JSInput, NumInputs>& inputs,
|
||||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||||
boost::array<Note, NumOutputs>& out_notes,
|
boost::array<Note, NumOutputs>& out_notes,
|
||||||
@@ -264,23 +260,12 @@ public:
|
|||||||
// estimate that it doesn't matter if we check every time.
|
// estimate that it doesn't matter if we check every time.
|
||||||
pb.constraint_system.swap_AB_if_beneficial();
|
pb.constraint_system.swap_AB_if_beneficial();
|
||||||
|
|
||||||
auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
|
return ZCProof(r1cs_ppzksnark_prover<ppzksnark_ppT>(
|
||||||
*pk,
|
*pk,
|
||||||
primary_input,
|
primary_input,
|
||||||
aux_input,
|
aux_input,
|
||||||
pb.constraint_system
|
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_
|
#define _ZCJOINSPLIT_H_
|
||||||
|
|
||||||
#include "Zcash.h"
|
#include "Zcash.h"
|
||||||
|
#include "Proof.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Note.hpp"
|
#include "Note.hpp"
|
||||||
#include "IncrementalMerkleTree.hpp"
|
#include "IncrementalMerkleTree.hpp"
|
||||||
@@ -59,7 +60,7 @@ public:
|
|||||||
virtual void loadVerifyingKey(std::string path) = 0;
|
virtual void loadVerifyingKey(std::string path) = 0;
|
||||||
virtual void saveVerifyingKey(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<JSInput, NumInputs>& inputs,
|
||||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||||
boost::array<Note, NumOutputs>& out_notes,
|
boost::array<Note, NumOutputs>& out_notes,
|
||||||
@@ -76,7 +77,7 @@ public:
|
|||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
virtual bool verify(
|
virtual bool verify(
|
||||||
const boost::array<unsigned char, ZKSNARK_PROOF_SIZE>& proof,
|
const ZCProof& proof,
|
||||||
const uint256& pubKeyHash,
|
const uint256& pubKeyHash,
|
||||||
const uint256& randomSeed,
|
const uint256& randomSeed,
|
||||||
const boost::array<uint256, NumInputs>& hmacs,
|
const boost::array<uint256, NumInputs>& hmacs,
|
||||||
@@ -96,4 +97,4 @@ protected:
|
|||||||
typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS,
|
typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS,
|
||||||
ZC_NUM_JS_OUTPUTS> ZCJoinSplit;
|
ZC_NUM_JS_OUTPUTS> ZCJoinSplit;
|
||||||
|
|
||||||
#endif // _ZCJOINSPLIT_H_
|
#endif // _ZCJOINSPLIT_H_
|
||||||
|
|||||||
258
src/zcash/Proof.cpp
Normal file
258
src/zcash/Proof.cpp
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
#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 {
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// TODO: It should be possible to use libsnark's bigint
|
||||||
|
// to do this stuff.
|
||||||
|
|
||||||
|
bigint<8> res;
|
||||||
|
// Multiply c1 by modq
|
||||||
|
mpn_mul(res.data, c1.data, 4, modq.data, 4);
|
||||||
|
// Add c0
|
||||||
|
mpn_add(res.data, res.data, 8, c0.data, 4);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares two bigints, returning 0 if equal, 1 if a > b, and -1 if a < b
|
||||||
|
template<mp_size_t LIMBS>
|
||||||
|
int cmp_bigint(const bigint<LIMBS> &a, const bigint<LIMBS> &b)
|
||||||
|
{
|
||||||
|
for (ssize_t i = LIMBS-1; i >= 0; i--) {
|
||||||
|
if (a.data[i] < b.data[i]) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.data[i] > b.data[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether a > b
|
||||||
|
bool cmp_fq2(const curve_Fq2 &a, const curve_Fq2 &b)
|
||||||
|
{
|
||||||
|
return cmp_bigint(fq2_to_bigint(a), fq2_to_bigint(b)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(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();
|
||||||
|
if (cmp_bigint(element_bigint, modq) != -1) {
|
||||||
|
throw std::logic_error("element is not in Fq");
|
||||||
|
}
|
||||||
|
|
||||||
|
return curve_Fq(element_bigint);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Fq2::Fq2(curve_Fq2 element) : data()
|
||||||
|
{
|
||||||
|
write_bigint(data, fq2_to_bigint(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
curve_Fq2 Fq2::to_libsnark_fq2() const
|
||||||
|
{
|
||||||
|
auto modq = curve_Fq::field_char();
|
||||||
|
auto combined = read_bigint<8>(data);
|
||||||
|
|
||||||
|
// TODO: It should be possible to use libsnark's bigint
|
||||||
|
// to do this stuff.
|
||||||
|
|
||||||
|
bigint<5> res;
|
||||||
|
bigint<4> c0;
|
||||||
|
|
||||||
|
mpn_tdiv_qr(res.data, c0.data, 0, combined.data, 8, modq.data, 4);
|
||||||
|
|
||||||
|
if (res.data[4] != 0) {
|
||||||
|
throw std::logic_error("element is not in Fq2");
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint<4> c1;
|
||||||
|
memcpy(c1.data, res.data, 4 * sizeof(mp_limb_t));
|
||||||
|
|
||||||
|
if (cmp_bigint(c1, modq) != -1) {
|
||||||
|
throw std::logic_error("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 = cmp_fq2(point.Y, -(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 (cmp_fq2(y_coordinate, 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 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_
|
#endif // _ZCCONSTANTS_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user