diff --git a/qa/zcash/performance-measurements.sh b/qa/zcash/performance-measurements.sh index 84d3e4cf3..b65974078 100755 --- a/qa/zcash/performance-measurements.sh +++ b/qa/zcash/performance-measurements.sh @@ -91,6 +91,9 @@ case "$1" in trydecryptnotes) zcash_rpc zcbenchmark trydecryptnotes 1000 "\"$RAWJOINSPLIT\"" ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 100 "${@:3}" + ;; *) zcashd_stop echo "Bad arguments." @@ -122,6 +125,9 @@ case "$1" in trydecryptnotes) zcash_rpc zcbenchmark trydecryptnotes 1 "\"$RAWJOINSPLIT\"" ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}" + ;; *) zcashd_massif_stop echo "Bad arguments." @@ -154,6 +160,9 @@ case "$1" in trydecryptnotes) zcash_rpc zcbenchmark trydecryptnotes 1 "\"$RAWJOINSPLIT\"" ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}" + ;; *) zcashd_valgrind_stop echo "Bad arguments." diff --git a/src/Makefile.am b/src/Makefile.am index 8ded268cf..e37510ae4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -232,6 +232,8 @@ libbitcoin_server_a_SOURCES = \ # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_wallet_a_SOURCES = \ + utiltest.cpp \ + utiltest.h \ zcbenchmarks.cpp \ zcbenchmarks.h \ wallet/asyncrpcoperation_sendmany.cpp \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 3941192b5..728d0a7f3 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -27,8 +27,11 @@ zcash_gtest_SOURCES = \ gtest/test_txid.cpp \ gtest/test_libzcash_utils.cpp \ gtest/test_proofs.cpp \ - gtest/test_checkblock.cpp \ + gtest/test_checkblock.cpp +if ENABLE_WALLET +zcash_gtest_SOURCES += \ wallet/gtest/test_wallet.cpp +endif zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC diff --git a/src/utiltest.cpp b/src/utiltest.cpp new file mode 100644 index 000000000..3ebbcf128 --- /dev/null +++ b/src/utiltest.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "utiltest.h" + +CWalletTx GetValidReceive(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, CAmount value, + bool randomInputs) { + CMutableTransaction mtx; + mtx.nVersion = 2; // Enable JoinSplits + mtx.vin.resize(2); + if (randomInputs) { + mtx.vin[0].prevout.hash = GetRandHash(); + mtx.vin[1].prevout.hash = GetRandHash(); + } else { + mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); + mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); + } + mtx.vin[0].prevout.n = 0; + mtx.vin[1].prevout.n = 0; + + // Generate an ephemeral keypair. + uint256 joinSplitPubKey; + unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; + crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); + mtx.joinSplitPubKey = joinSplitPubKey; + + boost::array inputs = { + libzcash::JSInput(), // dummy input + libzcash::JSInput() // dummy input + }; + + boost::array outputs = { + libzcash::JSOutput(sk.address(), value), + libzcash::JSOutput(sk.address(), value) + }; + + boost::array output_notes; + + // Prepare JoinSplits + uint256 rt; + JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + inputs, outputs, 2*value, 0, false}; + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + + // Add the signature + assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey + ) == 0); + + CTransaction tx {mtx}; + CWalletTx wtx {NULL, tx}; + return wtx; +} + +libzcash::Note GetNote(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const CTransaction& tx, size_t js, size_t n) { + ZCNoteDecryption decryptor {sk.viewing_key()}; + auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey); + auto note_pt = libzcash::NotePlaintext::decrypt( + decryptor, + tx.vjoinsplit[js].ciphertexts[n], + tx.vjoinsplit[js].ephemeralKey, + hSig, + (unsigned char) n); + return note_pt.note(sk.address()); +} + +CWalletTx GetValidSpend(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const libzcash::Note& note, CAmount value) { + CMutableTransaction mtx; + mtx.vout.resize(2); + mtx.vout[0].nValue = value; + mtx.vout[1].nValue = 0; + + // Generate an ephemeral keypair. + uint256 joinSplitPubKey; + unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; + crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); + mtx.joinSplitPubKey = joinSplitPubKey; + + // Fake tree for the unused witness + ZCIncrementalMerkleTree tree; + + libzcash::JSOutput dummyout; + libzcash::JSInput dummyin; + + { + if (note.value > value) { + libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); + libzcash::PaymentAddress dummyaddr = dummykey.address(); + dummyout = libzcash::JSOutput(dummyaddr, note.value - value); + } else if (note.value < value) { + libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); + libzcash::PaymentAddress dummyaddr = dummykey.address(); + libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256()); + tree.append(dummynote.cm()); + dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); + } + } + + tree.append(note.cm()); + + boost::array inputs = { + libzcash::JSInput(tree.witness(), note, sk), + dummyin + }; + + boost::array outputs = { + dummyout, // dummy output + libzcash::JSOutput() // dummy output + }; + + boost::array output_notes; + + // Prepare JoinSplits + uint256 rt = tree.root(); + JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + inputs, outputs, 0, value, false}; + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + + // Add the signature + assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey + ) == 0); + CTransaction tx {mtx}; + CWalletTx wtx {NULL, tx}; + return wtx; +} diff --git a/src/utiltest.h b/src/utiltest.h new file mode 100644 index 000000000..8cfa60d06 --- /dev/null +++ b/src/utiltest.h @@ -0,0 +1,18 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/wallet.h" +#include "zcash/JoinSplit.hpp" +#include "zcash/Note.hpp" +#include "zcash/NoteEncryption.hpp" + +CWalletTx GetValidReceive(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, CAmount value, + bool randomInputs); +libzcash::Note GetNote(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const CTransaction& tx, size_t js, size_t n); +CWalletTx GetValidSpend(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const libzcash::Note& note, CAmount value); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index d30944954..9fdec6bd3 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -6,6 +6,7 @@ #include "chainparams.h" #include "main.h" #include "random.h" +#include "utiltest.h" #include "wallet/wallet.h" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" @@ -65,138 +66,17 @@ public: }; CWalletTx GetValidReceive(const libzcash::SpendingKey& sk, CAmount value, bool randomInputs) { - CMutableTransaction mtx; - mtx.nVersion = 2; // Enable JoinSplits - mtx.vin.resize(2); - if (randomInputs) { - mtx.vin[0].prevout.hash = GetRandHash(); - mtx.vin[1].prevout.hash = GetRandHash(); - } else { - mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); - mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); - } - mtx.vin[0].prevout.n = 0; - mtx.vin[1].prevout.n = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - boost::array inputs = { - libzcash::JSInput(), // dummy input - libzcash::JSInput() // dummy input - }; - - boost::array outputs = { - libzcash::JSOutput(sk.address(), value), - libzcash::JSOutput(sk.address(), value) - }; - - boost::array output_notes; - - // Prepare JoinSplits - uint256 rt; - JSDescription jsdesc {*params, mtx.joinSplitPubKey, rt, - inputs, outputs, 2*value, 0, false}; - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; + return GetValidReceive(*params, sk, value, randomInputs); } libzcash::Note GetNote(const libzcash::SpendingKey& sk, const CTransaction& tx, size_t js, size_t n) { - ZCNoteDecryption decryptor {sk.viewing_key()}; - auto hSig = tx.vjoinsplit[js].h_sig(*params, tx.joinSplitPubKey); - auto note_pt = libzcash::NotePlaintext::decrypt( - decryptor, - tx.vjoinsplit[js].ciphertexts[n], - tx.vjoinsplit[js].ephemeralKey, - hSig, - (unsigned char) n); - return note_pt.note(sk.address()); + return GetNote(*params, sk, tx, js, n); } CWalletTx GetValidSpend(const libzcash::SpendingKey& sk, const libzcash::Note& note, CAmount value) { - CMutableTransaction mtx; - mtx.vout.resize(2); - mtx.vout[0].nValue = value; - mtx.vout[1].nValue = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - // Fake tree for the unused witness - ZCIncrementalMerkleTree tree; - - libzcash::JSOutput dummyout; - libzcash::JSInput dummyin; - - { - if (note.value > value) { - libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); - libzcash::PaymentAddress dummyaddr = dummykey.address(); - dummyout = libzcash::JSOutput(dummyaddr, note.value - value); - } else if (note.value < value) { - libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); - libzcash::PaymentAddress dummyaddr = dummykey.address(); - libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256()); - tree.append(dummynote.cm()); - dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); - } - } - - tree.append(note.cm()); - - boost::array inputs = { - libzcash::JSInput(tree.witness(), note, sk), - dummyin - }; - - boost::array outputs = { - dummyout, // dummy output - libzcash::JSOutput() // dummy output - }; - - boost::array output_notes; - - // Prepare JoinSplits - uint256 rt = tree.root(); - JSDescription jsdesc {*params, mtx.joinSplitPubKey, rt, - inputs, outputs, 0, value, false}; - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; + return GetValidSpend(*params, sk, note, value); } TEST(wallet_tests, setup_datadir_location_run_as_first_test) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index d33c35781..6c1ce2d11 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2483,6 +2483,9 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp) sample_times.push_back(benchmark_large_tx()); } else if (benchmarktype == "trydecryptnotes") { sample_times.push_back(benchmark_try_decrypt_notes(samplejoinsplit)); + } else if (benchmarktype == "incnotewitnesses") { + int nTxs = params[2].get_int(); + sample_times.push_back(benchmark_increment_note_witnesses(nTxs)); } else { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 6374141b8..61afb535d 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -17,6 +17,7 @@ #include "script/sign.h" #include "sodium.h" #include "streams.h" +#include "utiltest.h" #include "wallet/wallet.h" #include "zcbenchmarks.h" @@ -243,3 +244,45 @@ double benchmark_try_decrypt_notes(const JSDescription &joinsplit) return timer_stop(tv_start); } +double benchmark_increment_note_witnesses(size_t nTxs) +{ + CWallet wallet; + ZCIncrementalMerkleTree tree; + + auto sk = libzcash::SpendingKey::random(); + wallet.AddSpendingKey(sk); + + // First block + CBlock block1; + for (int i = 0; i < nTxs; i++) { + auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); + auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); + auto nullifier = note.nullifier(sk); + + mapNoteData_t noteData; + JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; + CNoteData nd {sk.address(), nullifier}; + noteData[jsoutpt] = nd; + + wtx.SetNoteData(noteData); + wallet.AddToWallet(wtx, true, NULL); + block1.vtx.push_back(wtx); + } + CBlockIndex index1(block1); + index1.nHeight = 1; + + // Increment to get transactions witnessed + wallet.ChainTip(&index1, &block1, tree, true); + + // Second block + CBlock block2; + block2.hashPrevBlock = block1.GetHash(); + CBlockIndex index2(block2); + index2.nHeight = 2; + + struct timeval tv_start; + timer_start(tv_start); + wallet.ChainTip(&index2, &block2, tree, true); + return timer_stop(tv_start); +} + diff --git a/src/zcbenchmarks.h b/src/zcbenchmarks.h index 9a99783ad..a1575e500 100644 --- a/src/zcbenchmarks.h +++ b/src/zcbenchmarks.h @@ -13,5 +13,6 @@ extern double benchmark_verify_joinsplit(const JSDescription &joinsplit); extern double benchmark_verify_equihash(); extern double benchmark_large_tx(); extern double benchmark_try_decrypt_notes(const JSDescription &joinsplit); +extern double benchmark_increment_note_witnesses(size_t nTxs); #endif