diff --git a/src/gtest/main.cpp b/src/gtest/main.cpp index d1ffa628a..a0a2e0042 100644 --- a/src/gtest/main.cpp +++ b/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(); diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp index 17ac7ecb2..986592e89 100644 --- a/src/gtest/test_joinsplit.cpp +++ b/src/gtest/test_joinsplit.cpp @@ -5,6 +5,7 @@ #include #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& 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 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) diff --git a/src/gtest/utils.cpp b/src/gtest/utils.cpp index cf025162c..527e613e2 100644 --- a/src/gtest/utils.cpp +++ b/src/gtest/utils.cpp @@ -1,7 +1,3 @@ -#include "zcash/JoinSplit.hpp" - -ZCJoinSplit* params = ZCJoinSplit::Unopened(); - int GenZero(int n) { return 0; diff --git a/src/init.cpp b/src/init.cpp index 59a59524d..e8ab677b0 100644 --- a/src/init.cpp +++ b/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) diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index f6236a2f8..6aa7949b2 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -20,9 +20,6 @@ JSDescription::JSDescription(ZCJoinSplit& params, { boost::array notes; - if (computeProof) { - params.loadProvingKey(); - } proof = params.prove( inputs, outputs, diff --git a/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index 36f6c1499..96bc36504 100644 --- a/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -397,6 +397,12 @@ r1cs_ppzksnark_proof r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key const r1cs_ppzksnark_auxiliary_input &auxiliary_input, const r1cs_ppzksnark_constraint_system &constraint_system); +template +r1cs_ppzksnark_proof r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system); + /* Below are four variants of verifier algorithm for the R1CS ppzkSNARK. diff --git a/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc index aeb2bbb85..84db9fc1a 100644 --- a/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc +++ b/src/snark/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -431,6 +431,92 @@ r1cs_ppzksnark_keypair r1cs_ppzksnark_generator( return r1cs_ppzksnark_keypair(std::move(pk), std::move(vk)); } +template +knowledge_commitment r1cs_compute_proof_kc(const qap_witness > &qap_wit, + const knowledge_commitment_vector &kcv, + const Fr &zk_shift) +{ + knowledge_commitment 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 >( + 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 +G1 r1cs_compute_proof_K(const qap_witness> &qap_wit, const G1_vector &K_query, const G1 &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 g_K = K_query[0] + zk_shift; + g_K = g_K + multi_exp_with_mixed_addition, Fr >( + 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 +G1 r1cs_compute_proof_H(const qap_witness > &qap_wit, const G1_vector &H_query) +{ + G1 g_H = G1::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, Fr >( + 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 r1cs_ppzksnark_proof r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key &pk, const r1cs_ppzksnark_primary_input &primary_input, @@ -457,67 +543,36 @@ r1cs_ppzksnark_proof r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key assert(qap_inst.is_satisfied(qap_wit)); #endif - knowledge_commitment, G1 > g_A = pk.A_query[0] + qap_wit.d1*pk.A_query[qap_wit.num_variables()+1]; - knowledge_commitment, G1 > g_B = pk.B_query[0] + qap_wit.d2*pk.B_query[qap_wit.num_variables()+1]; - knowledge_commitment, G1 > g_C = pk.C_query[0] + qap_wit.d3*pk.C_query[qap_wit.num_variables()+1]; - - G1 g_H = G1::zero(); - G1 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::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, Fr >(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, G1 >(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, G1, Fr >(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, G1 >(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, Fr >(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, G1 >(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, Fr >(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(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, Fr >(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 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 g_K = r1cs_compute_proof_K(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 r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key leave_block("Call to r1cs_ppzksnark_prover"); r1cs_ppzksnark_proof proof = r1cs_ppzksnark_proof(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 +r1cs_ppzksnark_proof r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system) +{ + enter_block("Call to r1cs_ppzksnark_prover_streaming"); + + const Fr d1 = Fr::random_element(), + d2 = Fr::random_element(), + d3 = Fr::random_element(); + + enter_block("Compute the polynomial H"); + const qap_witness > 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 proof; + + enter_block("Compute answer to A-query", false); + { + knowledge_commitment_vector, G1 > A_query; + proving_key_file >> A_query; + proof.g_A = r1cs_compute_proof_kc, G1 >(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, G1 > B_query; + proving_key_file >> B_query; + proof.g_B = r1cs_compute_proof_kc, G1 >(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 > C_query; + proving_key_file >> C_query; + proof.g_C = r1cs_compute_proof_kc, G1 >(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 H_query; + proving_key_file >> H_query; + proof.g_H = r1cs_compute_proof_H(qap_wit, H_query); + } + leave_block("Compute answer to H-query", false); + + enter_block("Compute answer to K-query", false); + { + G1_vector K_query; + proving_key_file >> K_query; + G1 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(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; } diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 6017333b4..e444eb2a5 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/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); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 5c701c5cc..0a39f2944 100644 --- a/src/test/test_bitcoin.cpp +++ b/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() diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 205e5b5c4..d1c426373 100644 --- a/src/test/test_bitcoin.h +++ b/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; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 3fa691a94..6b207f643 100644 --- a/src/test/transaction_tests.cpp +++ b/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)); } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 843af53bd..034147f42 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2543,12 +2543,6 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) std::vector 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") { diff --git a/src/zcash/CreateJoinSplit.cpp b/src/zcash/CreateJoinSplit.cpp index ea2d59c9e..7253e9568 100644 --- a/src/zcash/CreateJoinSplit.cpp +++ b/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 } diff --git a/src/zcash/GenerateParams.cpp b/src/zcash/GenerateParams.cpp index eadfe7b18..8ca2d7814 100644 --- a/src/zcash/GenerateParams.cpp +++ b/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; } diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 590700cd9..d71dbd0ee 100644 --- a/src/zcash/JoinSplit.cpp +++ b/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 -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 -void loadFromFile(std::string path, boost::optional& 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 FieldT; - boost::optional> pk; - boost::optional> vk; - boost::optional> vk_precomp; - boost::optional pkPath; + r1cs_ppzksnark_verification_key vk; + r1cs_ppzksnark_processed_verification_key vk_precomp; + std::string pkPath; - JoinSplitCircuit() {} + JoinSplitCircuit(const std::string vkPath, const std::string pkPath) : pkPath(pkPath) { + loadFromFile(vkPath, vk); + vk_precomp = r1cs_ppzksnark_verifier_process_vk(vk); + } ~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(); - - saveToFile(path, r1cs); - } - - r1cs_constraint_system generate_r1cs() { + static void generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath) + { protoboard pb; joinsplit_gadget 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 constraint_system = generate_r1cs(); - r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); + r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(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>(); @@ -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( - *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( + fh, primary_input, aux_input, pb.constraint_system @@ -355,20 +309,20 @@ public: }; template -JoinSplit* JoinSplit::Generate() +void JoinSplit::Generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath) { initialize_curve_params(); - auto js = new JoinSplitCircuit(); - js->generate(); - - return js; + JoinSplitCircuit::generate(r1csPath, vkPath, pkPath); } template -JoinSplit* JoinSplit::Unopened() +JoinSplit* JoinSplit::Prepared(const std::string vkPath, + const std::string pkPath) { initialize_curve_params(); - return new JoinSplitCircuit(); + return new JoinSplitCircuit(vkPath, pkPath); } template diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index a8c08d21b..2d9921ac8 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -48,22 +48,17 @@ class JoinSplit { public: virtual ~JoinSplit() {} - static JoinSplit* Generate(); - static JoinSplit* Unopened(); + static void Generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath); + static JoinSplit* Prepared(const std::string vkPath, + const std::string pkPath); + static uint256 h_sig(const uint256& randomSeed, const boost::array& 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& inputs, const boost::array& outputs, diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 1cb880ea1..bb51cdd6c 100644 --- a/src/zcbenchmarks.cpp +++ b/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);