Browse Source

Auto merge of #2670 - arielgabizon:lowmemprover, r=daira

Low memory prover

This PR integrates @ebfull 's low memory changes:  https://github.com/zcash/zcash/pull/2243/commits
on top of @str4d 's work bringing in libsnark as a subtree
4699d0eb36
metaverse
Homu 7 years ago
parent
commit
6f9f09dfb0
  1. 7
      src/gtest/main.cpp
  2. 52
      src/gtest/test_joinsplit.cpp
  3. 4
      src/gtest/utils.cpp
  4. 6
      src/init.cpp
  5. 3
      src/primitives/transaction.cpp
  6. 6
      src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp
  7. 204
      src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc
  8. 4
      src/test/rpc_wallet_tests.cpp
  9. 28
      src/test/test_bitcoin.cpp
  10. 8
      src/test/test_bitcoin.h
  11. 20
      src/test/transaction_tests.cpp
  12. 6
      src/wallet/rpcwallet.cpp
  13. 8
      src/zcash/CreateJoinSplit.cpp
  14. 8
      src/zcash/GenerateParams.cpp
  15. 120
      src/zcash/JoinSplit.cpp
  16. 17
      src/zcash/JoinSplit.hpp
  17. 6
      src/zcbenchmarks.cpp

7
src/gtest/main.cpp

@ -4,6 +4,8 @@
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "zcash/JoinSplit.hpp"
#include "util.h"
struct ECCryptoClosure
{
@ -12,11 +14,16 @@ struct ECCryptoClosure
ECCryptoClosure instance_of_eccryptoclosure;
ZCJoinSplit* params;
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;
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
params = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();

52
src/gtest/test_joinsplit.cpp

@ -5,6 +5,7 @@
#include <boost/foreach.hpp>
#include "zcash/prf.h"
#include "util.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
@ -13,6 +14,8 @@
using namespace libzcash;
extern ZCJoinSplit* params;
void test_full_api(ZCJoinSplit* js)
{
// Create verification context.
@ -219,8 +222,6 @@ void invokeAPIFailure(
TEST(joinsplit, h_sig)
{
auto js = ZCJoinSplit::Unopened();
/*
// by Taylor Hornby
@ -284,7 +285,7 @@ for test_input in TEST_VECTORS:
};
BOOST_FOREACH(std::vector<std::string>& v, tests) {
auto expected = js->h_sig(
auto expected = ZCJoinSplit::h_sig(
uint256S(v[0]),
{uint256S(v[1]), uint256S(v[2])},
uint256S(v[3])
@ -292,8 +293,6 @@ for test_input in TEST_VECTORS:
EXPECT_EQ(expected, uint256S(v[4]));
}
delete js;
}
void increment_note_witnesses(
@ -311,8 +310,6 @@ void increment_note_witnesses(
TEST(joinsplit, full_api_test)
{
auto js = ZCJoinSplit::Generate();
{
std::vector<ZCIncrementalWitness> witnesses;
ZCIncrementalMerkleTree tree;
@ -331,7 +328,7 @@ TEST(joinsplit, full_api_test)
increment_note_witnesses(note5.cm(), witnesses, tree);
// Should work
invokeAPI(js,
invokeAPI(params,
{
JSInput(),
JSInput()
@ -345,7 +342,7 @@ TEST(joinsplit, full_api_test)
tree.root());
// lhs > MAX_MONEY
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(),
JSInput()
@ -360,7 +357,7 @@ TEST(joinsplit, full_api_test)
"nonsensical vpub_old value");
// rhs > MAX_MONEY
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(),
JSInput()
@ -375,7 +372,7 @@ TEST(joinsplit, full_api_test)
"nonsensical vpub_new value");
// input witness for the wrong element
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(witnesses[0], note1, sk),
JSInput()
@ -391,7 +388,7 @@ TEST(joinsplit, full_api_test)
// input witness doesn't match up with
// real root
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(witnesses[1], note1, sk),
JSInput()
@ -406,7 +403,7 @@ TEST(joinsplit, full_api_test)
"joinsplit not anchored to the correct root");
// input is in the tree now! this should work
invokeAPI(js,
invokeAPI(params,
{
JSInput(witnesses[1], note1, sk),
JSInput()
@ -420,7 +417,7 @@ TEST(joinsplit, full_api_test)
tree.root());
// Wrong secret key
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(witnesses[1], note1, SpendingKey::random()),
JSInput()
@ -435,7 +432,7 @@ TEST(joinsplit, full_api_test)
"input note not authorized to spend with given key");
// Absurd input value
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(witnesses[3], note3, sk),
JSInput()
@ -450,7 +447,7 @@ TEST(joinsplit, full_api_test)
"nonsensical input note value");
// Absurd total input value
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(witnesses[4], note4, sk),
JSInput(witnesses[5], note5, sk)
@ -465,7 +462,7 @@ TEST(joinsplit, full_api_test)
"nonsensical left hand size of joinsplit balance");
// Absurd output value
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(),
JSInput()
@ -480,7 +477,7 @@ TEST(joinsplit, full_api_test)
"nonsensical output value");
// Absurd total output value
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(),
JSInput()
@ -495,7 +492,7 @@ TEST(joinsplit, full_api_test)
"nonsensical right hand side of joinsplit balance");
// Absurd total output value
invokeAPIFailure(js,
invokeAPIFailure(params,
{
JSInput(),
JSInput()
@ -510,22 +507,7 @@ TEST(joinsplit, full_api_test)
"invalid joinsplit balance");
}
test_full_api(js);
js->saveProvingKey("./zcashTest.pk");
js->saveVerifyingKey("./zcashTest.vk");
delete js;
js = ZCJoinSplit::Unopened();
js->setProvingKeyPath("./zcashTest.pk");
js->loadProvingKey();
js->loadVerifyingKey("./zcashTest.vk");
test_full_api(js);
delete js;
test_full_api(params);
}
TEST(joinsplit, note_plaintexts)

4
src/gtest/utils.cpp

@ -1,7 +1,3 @@
#include "zcash/JoinSplit.hpp"
ZCJoinSplit* params = ZCJoinSplit::Unopened();
int GenZero(int n)
{
return 0;

6
src/init.cpp

@ -688,18 +688,14 @@ static void ZC_LoadParams()
return;
}
pzcashParams = ZCJoinSplit::Unopened();
LogPrintf("Loading verifying key from %s\n", vk_path.string().c_str());
gettimeofday(&tv_start, 0);
pzcashParams->loadVerifyingKey(vk_path.string());
pzcashParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
gettimeofday(&tv_end, 0);
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
LogPrintf("Loaded verifying key in %fs seconds.\n", elapsed);
pzcashParams->setProvingKeyPath(pk_path.string());
}
bool AppInitServers(boost::thread_group& threadGroup)

3
src/primitives/transaction.cpp

@ -20,9 +20,6 @@ JSDescription::JSDescription(ZCJoinSplit& params,
{
boost::array<libzcash::Note, ZC_NUM_JS_OUTPUTS> notes;
if (computeProof) {
params.loadProvingKey();
}
proof = params.prove(
inputs,
outputs,

6
src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp

@ -397,6 +397,12 @@ r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key
const r1cs_ppzksnark_auxiliary_input<ppT> &auxiliary_input,
const r1cs_ppzksnark_constraint_system<ppT> &constraint_system);
template<typename ppT>
r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file,
const r1cs_ppzksnark_primary_input<ppT> &primary_input,
const r1cs_ppzksnark_auxiliary_input<ppT> &auxiliary_input,
const r1cs_ppzksnark_constraint_system<ppT> &constraint_system);
/*
Below are four variants of verifier algorithm for the R1CS ppzkSNARK.

204
src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc

@ -431,6 +431,92 @@ r1cs_ppzksnark_keypair<ppT> r1cs_ppzksnark_generator(
return r1cs_ppzksnark_keypair<ppT>(std::move(pk), std::move(vk));
}
template <typename ppT, typename T1, typename T2>
knowledge_commitment<T1, T2> r1cs_compute_proof_kc(const qap_witness<Fr<ppT> > &qap_wit,
const knowledge_commitment_vector<T1, T2> &kcv,
const Fr<ppT> &zk_shift)
{
knowledge_commitment<T1, T2> returnval = kcv[0] + (zk_shift * kcv[qap_wit.num_variables()+1]);
#ifdef DEBUG
assert(kcv.domain_size() == qap_wit.num_variables()+2);
#endif
#ifdef MULTICORE
const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads()
#else
const size_t chunks = 1;
#endif
returnval = returnval + kc_multi_exp_with_mixed_addition<T1, T2, Fr<ppT> >(
kcv,
1,
1 + qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(),
qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks,
true
);
return returnval;
}
template <typename ppT>
G1<ppT> r1cs_compute_proof_K(const qap_witness<Fr<ppT>> &qap_wit, const G1_vector<ppT> &K_query, const G1<ppT> &zk_shift)
{
#ifdef DEBUG
assert(K_query.size() == qap_wit.num_variables()+4);
#endif
#ifdef MULTICORE
const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads()
#else
const size_t chunks = 1;
#endif
G1<ppT> g_K = K_query[0] + zk_shift;
g_K = g_K + multi_exp_with_mixed_addition<G1<ppT>, Fr<ppT> >(
K_query.begin()+1,
K_query.begin()+1+qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(),
qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks,
true
);
return g_K;
}
template <typename ppT>
G1<ppT> r1cs_compute_proof_H(const qap_witness<Fr<ppT> > &qap_wit, const G1_vector<ppT> &H_query)
{
G1<ppT> g_H = G1<ppT>::zero();
#ifdef DEBUG
assert(H_query.size() == qap_wit.degree()+1);
#endif
#ifdef MULTICORE
const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads()
#else
const size_t chunks = 1;
#endif
g_H = g_H + multi_exp<G1<ppT>, Fr<ppT> >(
H_query.begin(),
H_query.begin()+qap_wit.degree()+1,
qap_wit.coefficients_for_H.begin(),
qap_wit.coefficients_for_H.begin()+qap_wit.degree()+1,
chunks,
true
);
return g_H;
}
template <typename ppT>
r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key<ppT> &pk,
const r1cs_ppzksnark_primary_input<ppT> &primary_input,
@ -457,67 +543,36 @@ r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key
assert(qap_inst.is_satisfied(qap_wit));
#endif
knowledge_commitment<G1<ppT>, G1<ppT> > g_A = pk.A_query[0] + qap_wit.d1*pk.A_query[qap_wit.num_variables()+1];
knowledge_commitment<G2<ppT>, G1<ppT> > g_B = pk.B_query[0] + qap_wit.d2*pk.B_query[qap_wit.num_variables()+1];
knowledge_commitment<G1<ppT>, G1<ppT> > g_C = pk.C_query[0] + qap_wit.d3*pk.C_query[qap_wit.num_variables()+1];
G1<ppT> g_H = G1<ppT>::zero();
G1<ppT> g_K = (pk.K_query[0] +
qap_wit.d1*pk.K_query[qap_wit.num_variables()+1] +
qap_wit.d2*pk.K_query[qap_wit.num_variables()+2] +
qap_wit.d3*pk.K_query[qap_wit.num_variables()+3]);
#ifdef DEBUG
for (size_t i = 0; i < qap_wit.num_inputs() + 1; ++i)
{
assert(pk.A_query[i].g == G1<ppT>::zero());
}
assert(pk.A_query.domain_size() == qap_wit.num_variables()+2);
assert(pk.B_query.domain_size() == qap_wit.num_variables()+2);
assert(pk.C_query.domain_size() == qap_wit.num_variables()+2);
assert(pk.H_query.size() == qap_wit.degree()+1);
assert(pk.K_query.size() == qap_wit.num_variables()+4);
#endif
#ifdef MULTICORE
const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads()
#else
const size_t chunks = 1;
#endif
enter_block("Compute the proof");
enter_block("Compute answer to A-query", false);
g_A = g_A + kc_multi_exp_with_mixed_addition<G1<ppT>, G1<ppT>, Fr<ppT> >(pk.A_query,
1, 1+qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(), qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks, true);
auto g_A = r1cs_compute_proof_kc<ppT, G1<ppT>, G1<ppT> >(qap_wit, pk.A_query, qap_wit.d1);
leave_block("Compute answer to A-query", false);
enter_block("Compute answer to B-query", false);
g_B = g_B + kc_multi_exp_with_mixed_addition<G2<ppT>, G1<ppT>, Fr<ppT> >(pk.B_query,
1, 1+qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(), qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks, true);
auto g_B = r1cs_compute_proof_kc<ppT, G2<ppT>, G1<ppT> >(qap_wit, pk.B_query, qap_wit.d2);
leave_block("Compute answer to B-query", false);
enter_block("Compute answer to C-query", false);
g_C = g_C + kc_multi_exp_with_mixed_addition<G1<ppT>, G1<ppT>, Fr<ppT> >(pk.C_query,
1, 1+qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(), qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks, true);
auto g_C = r1cs_compute_proof_kc<ppT, G1<ppT>, G1<ppT> >(qap_wit, pk.C_query, qap_wit.d3);
leave_block("Compute answer to C-query", false);
enter_block("Compute answer to H-query", false);
g_H = g_H + multi_exp<G1<ppT>, Fr<ppT> >(pk.H_query.begin(), pk.H_query.begin()+qap_wit.degree()+1,
qap_wit.coefficients_for_H.begin(), qap_wit.coefficients_for_H.begin()+qap_wit.degree()+1,
chunks, true);
auto g_H = r1cs_compute_proof_H<ppT>(qap_wit, pk.H_query);
leave_block("Compute answer to H-query", false);
enter_block("Compute answer to K-query", false);
g_K = g_K + multi_exp_with_mixed_addition<G1<ppT>, Fr<ppT> >(pk.K_query.begin()+1, pk.K_query.begin()+1+qap_wit.num_variables(),
qap_wit.coefficients_for_ABCs.begin(), qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(),
chunks, true);
G1<ppT> zk_shift = qap_wit.d1*pk.K_query[qap_wit.num_variables()+1] +
qap_wit.d2*pk.K_query[qap_wit.num_variables()+2] +
qap_wit.d3*pk.K_query[qap_wit.num_variables()+3];
G1<ppT> g_K = r1cs_compute_proof_K<ppT>(qap_wit, pk.K_query, zk_shift);
leave_block("Compute answer to K-query", false);
leave_block("Compute the proof");
@ -525,7 +580,76 @@ r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key
leave_block("Call to r1cs_ppzksnark_prover");
r1cs_ppzksnark_proof<ppT> proof = r1cs_ppzksnark_proof<ppT>(std::move(g_A), std::move(g_B), std::move(g_C), std::move(g_H), std::move(g_K));
//proof.print_size();
return proof;
}
template <typename ppT>
r1cs_ppzksnark_proof<ppT> r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file,
const r1cs_ppzksnark_primary_input<ppT> &primary_input,
const r1cs_ppzksnark_auxiliary_input<ppT> &auxiliary_input,
const r1cs_ppzksnark_constraint_system<ppT> &constraint_system)
{
enter_block("Call to r1cs_ppzksnark_prover_streaming");
const Fr<ppT> d1 = Fr<ppT>::random_element(),
d2 = Fr<ppT>::random_element(),
d3 = Fr<ppT>::random_element();
enter_block("Compute the polynomial H");
const qap_witness<Fr<ppT> > qap_wit = r1cs_to_qap_witness_map(constraint_system, primary_input, auxiliary_input, d1, d2, d3);
leave_block("Compute the polynomial H");
enter_block("Compute the proof");
r1cs_ppzksnark_proof<ppT> proof;
enter_block("Compute answer to A-query", false);
{
knowledge_commitment_vector<G1<ppT>, G1<ppT> > A_query;
proving_key_file >> A_query;
proof.g_A = r1cs_compute_proof_kc<ppT, G1<ppT>, G1<ppT> >(qap_wit, A_query, qap_wit.d1);
}
leave_block("Compute answer to A-query", false);
enter_block("Compute answer to B-query", false);
{
knowledge_commitment_vector<G2<ppT>, G1<ppT> > B_query;
proving_key_file >> B_query;
proof.g_B = r1cs_compute_proof_kc<ppT, G2<ppT>, G1<ppT> >(qap_wit, B_query, qap_wit.d2);
}
leave_block("Compute answer to B-query", false);
enter_block("Compute answer to C-query", false);
{
knowledge_commitment_vector<G1<ppT>, G1<ppT> > C_query;
proving_key_file >> C_query;
proof.g_C = r1cs_compute_proof_kc<ppT, G1<ppT>, G1<ppT> >(qap_wit, C_query, qap_wit.d3);
}
leave_block("Compute answer to C-query", false);
enter_block("Compute answer to H-query", false);
{
G1_vector<ppT> H_query;
proving_key_file >> H_query;
proof.g_H = r1cs_compute_proof_H<ppT>(qap_wit, H_query);
}
leave_block("Compute answer to H-query", false);
enter_block("Compute answer to K-query", false);
{
G1_vector<ppT> K_query;
proving_key_file >> K_query;
G1<ppT> zk_shift = qap_wit.d1*K_query[qap_wit.num_variables()+1] +
qap_wit.d2*K_query[qap_wit.num_variables()+2] +
qap_wit.d3*K_query[qap_wit.num_variables()+3];
proof.g_K = r1cs_compute_proof_K<ppT>(qap_wit, K_query, zk_shift);
}
leave_block("Compute answer to K-query", false);
leave_block("Compute the proof");
leave_block("Call to r1cs_ppzksnark_prover_streaming");
return proof;
}

4
src/test/rpc_wallet_tests.cpp

@ -1175,7 +1175,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
try {
proxy.perform_joinsplit(info);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("JoinSplit verifying key not loaded")!= string::npos);
BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos);
}
}
@ -1372,7 +1372,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
try {
proxy.perform_joinsplit(info);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("JoinSplit verifying key not loaded")!= string::npos);
BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos);
}
}

28
src/test/test_bitcoin.cpp

@ -30,20 +30,30 @@ ZCJoinSplit *pzcashParams;
extern bool fPrintToConsole;
extern void noui_connect();
JoinSplitTestingSetup::JoinSplitTestingSetup()
{
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
pzcashParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
}
JoinSplitTestingSetup::~JoinSplitTestingSetup()
{
delete pzcashParams;
}
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;
SelectParams(CBaseChainParams::MAIN);
assert(init_and_check_sodium() != -1);
ECC_Start();
SetupEnvironment();
fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(CBaseChainParams::MAIN);
}
BasicTestingSetup::~BasicTestingSetup()
{
ECC_Stop();
delete pzcashParams;
ECC_Stop();
}
TestingSetup::TestingSetup()

8
src/test/test_bitcoin.h

@ -17,11 +17,17 @@ struct BasicTestingSetup {
~BasicTestingSetup();
};
// Setup w.r.t. zk-SNARK API
struct JoinSplitTestingSetup: public BasicTestingSetup {
JoinSplitTestingSetup();
~JoinSplitTestingSetup();
};
/** Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads
* and wallet (if enabled) setup.
*/
struct TestingSetup: public BasicTestingSetup {
struct TestingSetup: public JoinSplitTestingSetup {
CCoinsViewDB *pcoinsdbview;
boost::filesystem::path pathTemp;
boost::thread_group threadGroup;

20
src/test/transaction_tests.cpp

@ -6,6 +6,7 @@
#include "data/tx_valid.json.h"
#include "test/test_bitcoin.h"
#include "init.h"
#include "clientversion.h"
#include "consensus/validation.h"
#include "core_io.h"
@ -85,7 +86,7 @@ string FormatScriptFlags(unsigned int flags)
return ret.substr(0, ret.size() - 1);
}
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
BOOST_FIXTURE_TEST_SUITE(transaction_tests, JoinSplitTestingSetup)
BOOST_AUTO_TEST_CASE(tx_valid)
{
@ -326,9 +327,6 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
// Also, it's generally libzcash's job to ensure the
// integrity of the scheme through its own tests.
// construct the r1cs keypair
auto p = ZCJoinSplit::Generate();
// construct a merkle tree
ZCIncrementalMerkleTree merkleTree;
@ -362,8 +360,8 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
auto verifier = libzcash::ProofVerifier::Strict();
{
JSDescription jsdesc(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
BOOST_CHECK(jsdesc.Verify(*p, verifier, pubKeyHash));
JSDescription jsdesc(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0);
BOOST_CHECK(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash));
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << jsdesc;
@ -372,20 +370,20 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
ss >> jsdesc_deserialized;
BOOST_CHECK(jsdesc_deserialized == jsdesc);
BOOST_CHECK(jsdesc_deserialized.Verify(*p, verifier, pubKeyHash));
BOOST_CHECK(jsdesc_deserialized.Verify(*pzcashParams, verifier, pubKeyHash));
}
{
// Ensure that the balance equation is working.
BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument);
BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument);
}
{
// Ensure that it won't verify if the root is changed.
auto test = JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
auto test = JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0);
test.anchor = GetRandHash();
BOOST_CHECK(!test.Verify(*p, verifier, pubKeyHash));
BOOST_CHECK(!test.Verify(*pzcashParams, verifier, pubKeyHash));
}
}

6
src/wallet/rpcwallet.cpp

@ -2543,12 +2543,6 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp)
std::vector<double> sample_times;
if (benchmarktype == "createjoinsplit") {
/* Load the proving now key so that it doesn't happen as part of the
* first joinsplit. */
pzcashParams->loadProvingKey();
}
JSDescription samplejoinsplit;
if (benchmarktype == "verifyjoinsplit") {

8
src/zcash/CreateJoinSplit.cpp

@ -13,10 +13,8 @@ int main(int argc, char **argv)
{
libsnark::start_profiling();
auto p = ZCJoinSplit::Unopened();
p->loadVerifyingKey((ZC_GetParamsDir() / "sprout-verifying.key").string());
p->setProvingKeyPath((ZC_GetParamsDir() / "sprout-proving.key").string());
p->loadProvingKey();
auto p = ZCJoinSplit::Prepared((ZC_GetParamsDir() / "sprout-verifying.key").string(),
(ZC_GetParamsDir() / "sprout-proving.key").string());
// construct a proof.
@ -32,4 +30,6 @@ int main(int argc, char **argv)
0,
0);
}
delete p; // not that it matters
}

8
src/zcash/GenerateParams.cpp

@ -18,13 +18,7 @@ int main(int argc, char **argv)
std::string vkFile = argv[2];
std::string r1csFile = argv[3];
auto p = ZCJoinSplit::Generate();
p->saveProvingKey(pkFile);
p->saveVerifyingKey(vkFile);
p->saveR1CS(r1csFile);
delete p;
ZCJoinSplit::Generate(r1csFile, vkFile, pkFile);
return 0;
}

120
src/zcash/JoinSplit.cpp

@ -14,7 +14,7 @@
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
#include "tinyformat.h"
#include "sync.h"
#include "amount.h"
@ -28,7 +28,7 @@ CCriticalSection cs_ParamsIO;
CCriticalSection cs_LoadKeys;
template<typename T>
void saveToFile(std::string path, T& obj) {
void saveToFile(const std::string path, T& obj) {
LOCK(cs_ParamsIO);
std::stringstream ss;
@ -42,14 +42,14 @@ void saveToFile(std::string path, T& obj) {
}
template<typename T>
void loadFromFile(std::string path, boost::optional<T>& objIn) {
void loadFromFile(const std::string path, T& objIn) {
LOCK(cs_ParamsIO);
std::stringstream ss;
std::ifstream fh(path, std::ios::binary);
if(!fh.is_open()) {
throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
throw std::runtime_error(strprintf("could not load param file at %s", path));
}
ss << fh.rdbuf();
@ -69,77 +69,33 @@ public:
typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
typedef Fr<ppzksnark_ppT> FieldT;
boost::optional<r1cs_ppzksnark_proving_key<ppzksnark_ppT>> pk;
boost::optional<r1cs_ppzksnark_verification_key<ppzksnark_ppT>> vk;
boost::optional<r1cs_ppzksnark_processed_verification_key<ppzksnark_ppT>> vk_precomp;
boost::optional<std::string> pkPath;
JoinSplitCircuit() {}
~JoinSplitCircuit() {}
void setProvingKeyPath(std::string path) {
pkPath = path;
}
void loadProvingKey() {
LOCK(cs_LoadKeys);
if (!pk) {
if (!pkPath) {
throw std::runtime_error("proving key path unknown");
}
loadFromFile(*pkPath, pk);
}
}
void saveProvingKey(std::string path) {
if (pk) {
saveToFile(path, *pk);
} else {
throw std::runtime_error("cannot save proving key; key doesn't exist");
}
}
void loadVerifyingKey(std::string path) {
LOCK(cs_LoadKeys);
loadFromFile(path, vk);
processVerifyingKey();
}
void processVerifyingKey() {
vk_precomp = r1cs_ppzksnark_verifier_process_vk(*vk);
}
void saveVerifyingKey(std::string path) {
if (vk) {
saveToFile(path, *vk);
} else {
throw std::runtime_error("cannot save verifying key; key doesn't exist");
}
}
void saveR1CS(std::string path) {
auto r1cs = generate_r1cs();
r1cs_ppzksnark_verification_key<ppzksnark_ppT> vk;
r1cs_ppzksnark_processed_verification_key<ppzksnark_ppT> vk_precomp;
std::string pkPath;
saveToFile(path, r1cs);
JoinSplitCircuit(const std::string vkPath, const std::string pkPath) : pkPath(pkPath) {
loadFromFile(vkPath, vk);
vk_precomp = r1cs_ppzksnark_verifier_process_vk(vk);
}
~JoinSplitCircuit() {}
r1cs_constraint_system<FieldT> generate_r1cs() {
static void generate(const std::string r1csPath,
const std::string vkPath,
const std::string pkPath)
{
protoboard<FieldT> pb;
joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
g.generate_r1cs_constraints();
return pb.get_constraint_system();
}
auto r1cs = pb.get_constraint_system();
void generate() {
LOCK(cs_LoadKeys);
saveToFile(r1csPath, r1cs);
const r1cs_constraint_system<FieldT> constraint_system = generate_r1cs();
r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(r1cs);
pk = keypair.pk;
vk = keypair.vk;
processVerifyingKey();
saveToFile(vkPath, keypair.vk);
saveToFile(pkPath, keypair.pk);
}
bool verify(
@ -154,10 +110,6 @@ public:
uint64_t vpub_new,
const uint256& rt
) {
if (!vk || !vk_precomp) {
throw std::runtime_error("JoinSplit verifying key not loaded");
}
try {
auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();
@ -174,8 +126,8 @@ public:
);
return verifier.check(
*vk,
*vk_precomp,
vk,
vk_precomp,
witness,
r1cs_proof
);
@ -200,10 +152,6 @@ public:
const uint256& rt,
bool computeProof
) {
if (computeProof && !pk) {
throw std::runtime_error("JoinSplit proving key not loaded");
}
if (vpub_old > MAX_MONEY) {
throw std::invalid_argument("nonsensical vpub_old value");
}
@ -345,8 +293,14 @@ public:
// estimate that it doesn't matter if we check every time.
pb.constraint_system.swap_AB_if_beneficial();
return ZCProof(r1cs_ppzksnark_prover<ppzksnark_ppT>(
*pk,
std::ifstream fh(pkPath, std::ios::binary);
if(!fh.is_open()) {
throw std::runtime_error(strprintf("could not load param file at %s", pkPath));
}
return ZCProof(r1cs_ppzksnark_prover_streaming<ppzksnark_ppT>(
fh,
primary_input,
aux_input,
pb.constraint_system
@ -355,20 +309,20 @@ public:
};
template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
void JoinSplit<NumInputs, NumOutputs>::Generate(const std::string r1csPath,
const std::string vkPath,
const std::string pkPath)
{
initialize_curve_params();
auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
js->generate();
return js;
JoinSplitCircuit<NumInputs, NumOutputs>::generate(r1csPath, vkPath, pkPath);
}
template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Prepared(const std::string vkPath,
const std::string pkPath)
{
initialize_curve_params();
return new JoinSplitCircuit<NumInputs, NumOutputs>();
return new JoinSplitCircuit<NumInputs, NumOutputs>(vkPath, pkPath);
}
template<size_t NumInputs, size_t NumOutputs>

17
src/zcash/JoinSplit.hpp

@ -48,22 +48,17 @@ class JoinSplit {
public:
virtual ~JoinSplit() {}
static JoinSplit<NumInputs, NumOutputs>* Generate();
static JoinSplit<NumInputs, NumOutputs>* Unopened();
static void Generate(const std::string r1csPath,
const std::string vkPath,
const std::string pkPath);
static JoinSplit<NumInputs, NumOutputs>* Prepared(const std::string vkPath,
const std::string pkPath);
static uint256 h_sig(const uint256& randomSeed,
const boost::array<uint256, NumInputs>& nullifiers,
const uint256& pubKeyHash
);
// TODO: #789
virtual void setProvingKeyPath(std::string) = 0;
virtual void loadProvingKey() = 0;
virtual void saveProvingKey(std::string path) = 0;
virtual void loadVerifyingKey(std::string path) = 0;
virtual void saveVerifyingKey(std::string path) = 0;
virtual void saveR1CS(std::string path) = 0;
virtual ZCProof prove(
const boost::array<JSInput, NumInputs>& inputs,
const boost::array<JSOutput, NumOutputs>& outputs,

6
src/zcbenchmarks.cpp

@ -97,11 +97,7 @@ double benchmark_parameter_loading()
struct timeval tv_start;
timer_start(tv_start);
auto newParams = ZCJoinSplit::Unopened();
newParams->loadVerifyingKey(vk_path.string());
newParams->setProvingKeyPath(pk_path.string());
newParams->loadProvingKey();
auto newParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
double ret = timer_stop(tv_start);

Loading…
Cancel
Save