6 #include <boost/filesystem.hpp>
11 #include "primitives/transaction.h"
13 #include "crypto/equihash.h"
15 #include "chainparams.h"
16 #include "consensus/upgrades.h"
17 #include "consensus/validation.h"
21 #include "rpc/server.h"
22 #include "script/sign.h"
27 #include "wallet/wallet.h"
29 #include "zcbenchmarks.h"
31 #include "zcash/Zcash.h"
32 #include "zcash/IncrementalMerkleTree.hpp"
33 #include "zcash/Note.hpp"
34 #include "librustzcash.h"
36 using namespace libzcash;
37 // This method is based on Shutdown from init.cpp
38 void pre_wallet_load()
40 LogPrintf("%s: In progress...\n", __func__);
41 if (ShutdownRequested())
42 throw new std::runtime_error("The node is shutting down");
45 pwalletMain->Flush(false);
48 GenerateBitcoins(false, NULL, 0);
50 GenerateBitcoins(false, 0);
53 UnregisterNodeSignals(GetNodeSignals());
55 pwalletMain->Flush(true);
57 UnregisterValidationInterface(pwalletMain);
61 RegisterNodeSignals(GetNodeSignals());
62 LogPrintf("%s: done\n", __func__);
65 void post_wallet_load(){
66 RegisterValidationInterface(pwalletMain);
68 // Generate coins in the background
69 if (pwalletMain || !GetArg("-mineraddress", "").empty())
72 GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 0));
74 GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 0));
81 void timer_start(timeval &tv_start)
83 gettimeofday(&tv_start, 0);
86 double timer_stop(timeval &tv_start)
89 struct timeval tv_end;
90 gettimeofday(&tv_end, 0);
91 elapsed = double(tv_end.tv_sec-tv_start.tv_sec) +
92 (tv_end.tv_usec-tv_start.tv_usec)/double(1000000);
96 double benchmark_sleep()
98 struct timeval tv_start;
99 timer_start(tv_start);
101 return timer_stop(tv_start);
104 double benchmark_parameter_loading()
106 // FIXME: this is duplicated with the actual loading code
107 boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
108 boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
110 struct timeval tv_start;
111 timer_start(tv_start);
113 auto newParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
115 double ret = timer_stop(tv_start);
122 double benchmark_create_joinsplit()
124 uint256 joinSplitPubKey;
126 /* Get the anchor of an empty commitment tree. */
127 uint256 anchor = SproutMerkleTree().root();
129 struct timeval tv_start;
130 timer_start(tv_start);
131 JSDescription jsdesc(true,
135 {JSInput(), JSInput()},
136 {JSOutput(), JSOutput()},
139 double ret = timer_stop(tv_start);
141 auto verifier = libzcash::ProofVerifier::Strict();
142 assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey));
146 std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
148 std::vector<double> ret;
149 std::vector<std::future<double>> tasks;
150 std::vector<std::thread> threads;
151 for (int i = 0; i < nThreads; i++) {
152 std::packaged_task<double(void)> task(&benchmark_create_joinsplit);
153 tasks.emplace_back(task.get_future());
154 threads.emplace_back(std::move(task));
156 std::future_status status;
157 for (auto it = tasks.begin(); it != tasks.end(); it++) {
159 ret.push_back(it->get());
161 for (auto it = threads.begin(); it != threads.end(); it++) {
167 double benchmark_verify_joinsplit(const JSDescription &joinsplit)
169 struct timeval tv_start;
170 timer_start(tv_start);
171 uint256 joinSplitPubKey;
172 auto verifier = libzcash::ProofVerifier::Strict();
173 joinsplit.Verify(*pzcashParams, verifier, joinSplitPubKey);
174 return timer_stop(tv_start);
178 double benchmark_solve_equihash()
181 CEquihashInput I{pblock};
182 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
185 auto params = Params(CBaseChainParams::MAIN).GetConsensus();
186 unsigned int n = params.nEquihashN;
187 unsigned int k = params.nEquihashK;
188 crypto_generichash_blake2b_state eh_state;
189 EhInitialiseState(n, k, eh_state);
190 crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size());
193 randombytes_buf(nonce.begin(), 32);
194 crypto_generichash_blake2b_update(&eh_state,
198 struct timeval tv_start;
199 timer_start(tv_start);
200 std::set<std::vector<unsigned int>> solns;
201 EhOptimisedSolveUncancellable(n, k, eh_state,
202 [](std::vector<unsigned char> soln) { return false; });
203 return timer_stop(tv_start);
206 std::vector<double> benchmark_solve_equihash_threaded(int nThreads)
208 std::vector<double> ret;
209 std::vector<std::future<double>> tasks;
210 std::vector<std::thread> threads;
211 for (int i = 0; i < nThreads; i++) {
212 std::packaged_task<double(void)> task(&benchmark_solve_equihash);
213 tasks.emplace_back(task.get_future());
214 threads.emplace_back(std::move(task));
216 std::future_status status;
217 for (auto it = tasks.begin(); it != tasks.end(); it++) {
219 ret.push_back(it->get());
221 for (auto it = threads.begin(); it != threads.end(); it++) {
226 #endif // ENABLE_MINING
228 double benchmark_verify_equihash()
230 CChainParams params = Params(CBaseChainParams::MAIN);
231 CBlock genesis = params.GenesisBlock();
232 CBlockHeader genesis_header = genesis.GetBlockHeader();
233 struct timeval tv_start;
234 timer_start(tv_start);
235 CheckEquihashSolution(&genesis_header, params.GetConsensus());
236 return timer_stop(tv_start);
239 double benchmark_large_tx(size_t nInputs)
241 // Create priv/pub key
243 priv.MakeNewKey(false);
244 auto pub = priv.GetPubKey();
245 CBasicKeyStore tempKeystore;
246 tempKeystore.AddKey(priv);
248 // The "original" transaction that the spending transaction will spend
250 CMutableTransaction m_orig_tx;
251 m_orig_tx.vout.resize(1);
252 m_orig_tx.vout[0].nValue = 1000000;
253 CScript prevPubKey = GetScriptForDestination(pub.GetID());
254 m_orig_tx.vout[0].scriptPubKey = prevPubKey;
256 auto orig_tx = CTransaction(m_orig_tx);
258 CMutableTransaction spending_tx;
259 spending_tx.fOverwintered = true;
260 spending_tx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
261 spending_tx.nVersion = SAPLING_TX_VERSION;
263 auto input_hash = orig_tx.GetHash();
264 // Add nInputs inputs
265 for (size_t i = 0; i < nInputs; i++) {
266 spending_tx.vin.emplace_back(input_hash, 0);
269 // Sign for all the inputs
270 auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
271 for (size_t i = 0; i < nInputs; i++) {
272 SignSignature(tempKeystore, prevPubKey, spending_tx, i, 1000000, SIGHASH_ALL, consensusBranchId);
275 // Spending tx has all its inputs signed and does not need to be mutated anymore
276 CTransaction final_spending_tx(spending_tx);
278 // Benchmark signature verification costs:
279 struct timeval tv_start;
280 timer_start(tv_start);
281 PrecomputedTransactionData txdata(final_spending_tx);
282 for (size_t i = 0; i < nInputs; i++) {
283 ScriptError serror = SCRIPT_ERR_OK;
284 assert(VerifyScript(final_spending_tx.vin[i].scriptSig,
286 STANDARD_SCRIPT_VERIFY_FLAGS,
287 TransactionSignatureChecker(&final_spending_tx, i, 1000000, txdata),
291 return timer_stop(tv_start);
294 // The two benchmarks, try_decrypt_sprout_notes and try_decrypt_sapling_notes,
295 // are checking worst-case scenarios. In both we add n keys to a wallet,
296 // create a transaction using a key not in our original list of n, and then
297 // check that the transaction is not associated with any of the keys in our
298 // wallet. We call assert(...) to ensure that this is true.
299 double benchmark_try_decrypt_sprout_notes(size_t nKeys)
302 for (int i = 0; i < nKeys; i++) {
303 auto sk = libzcash::SproutSpendingKey::random();
304 wallet.AddSproutSpendingKey(sk);
307 auto sk = libzcash::SproutSpendingKey::random();
308 auto tx = GetValidSproutReceive(*pzcashParams, sk, 10, true);
310 struct timeval tv_start;
311 timer_start(tv_start);
312 auto noteDataMap = wallet.FindMySproutNotes(tx);
314 assert(noteDataMap.empty());
315 return timer_stop(tv_start);
318 double benchmark_try_decrypt_sapling_notes(size_t nKeys)
321 auto consensusParams = Params().GetConsensus();
323 auto masterKey = GetTestMasterSaplingSpendingKey();
327 for (int i = 0; i < nKeys; i++) {
328 auto sk = masterKey.Derive(i);
329 wallet.AddSaplingSpendingKey(sk, sk.DefaultAddress());
332 // Generate a key that has not been added to the wallet
333 auto sk = masterKey.Derive(nKeys);
334 auto tx = GetValidSaplingReceive(consensusParams, wallet, sk, 10);
336 struct timeval tv_start;
337 timer_start(tv_start);
338 auto noteDataMapAndAddressesToAdd = wallet.FindMySaplingNotes(tx);
339 assert(noteDataMapAndAddressesToAdd.first.empty());
340 return timer_stop(tv_start);
343 CWalletTx CreateSproutTxWithNoteData(const libzcash::SproutSpendingKey& sk) {
344 auto wtx = GetValidSproutReceive(*pzcashParams, sk, 10, true);
345 auto note = GetSproutNote(*pzcashParams, sk, wtx, 0, 1);
346 auto nullifier = note.nullifier(sk);
348 mapSproutNoteData_t noteDataMap;
349 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
350 SproutNoteData nd {sk.address(), nullifier};
351 noteDataMap[jsoutpt] = nd;
353 wtx.SetSproutNoteData(noteDataMap);
358 double benchmark_increment_sprout_note_witnesses(size_t nTxs)
360 auto consensusParams = Params().GetConsensus();
363 SproutMerkleTree sproutTree;
364 SaplingMerkleTree saplingTree;
366 auto sproutSpendingKey = libzcash::SproutSpendingKey::random();
367 wallet.AddSproutSpendingKey(sproutSpendingKey);
371 for (int i = 0; i < nTxs; ++i) {
372 auto wtx = CreateSproutTxWithNoteData(sproutSpendingKey);
373 wallet.AddToWallet(wtx, true, NULL);
374 block1.vtx.push_back(wtx);
377 CBlockIndex index1(block1);
380 // Increment to get transactions witnessed
381 wallet.ChainTip(&index1, &block1, sproutTree, saplingTree, true);
385 block2.hashPrevBlock = block1.GetHash();
387 auto sproutTx = CreateSproutTxWithNoteData(sproutSpendingKey);
388 wallet.AddToWallet(sproutTx, true, NULL);
389 block2.vtx.push_back(sproutTx);
392 CBlockIndex index2(block2);
395 struct timeval tv_start;
396 timer_start(tv_start);
397 wallet.ChainTip(&index2, &block2, sproutTree, saplingTree, true);
398 return timer_stop(tv_start);
401 CWalletTx CreateSaplingTxWithNoteData(const Consensus::Params& consensusParams,
402 CBasicKeyStore& keyStore,
403 const libzcash::SaplingExtendedSpendingKey &sk) {
404 auto wtx = GetValidSaplingReceive(consensusParams, keyStore, sk, 10);
405 auto testNote = GetTestSaplingNote(sk.DefaultAddress(), 10);
406 auto fvk = sk.expsk.full_viewing_key();
407 auto nullifier = testNote.note.nullifier(fvk, testNote.tree.witness().position()).get();
409 mapSaplingNoteData_t noteDataMap;
410 SaplingOutPoint outPoint {wtx.GetHash(), 0};
411 auto ivk = fvk.in_viewing_key();
412 SaplingNoteData noteData {ivk, nullifier};
413 noteDataMap[outPoint] = noteData;
415 wtx.SetSaplingNoteData(noteDataMap);
420 double benchmark_increment_sapling_note_witnesses(size_t nTxs)
422 auto consensusParams = Params().GetConsensus();
425 SproutMerkleTree sproutTree;
426 SaplingMerkleTree saplingTree;
428 auto saplingSpendingKey = GetTestMasterSaplingSpendingKey();
429 wallet.AddSaplingSpendingKey(saplingSpendingKey, saplingSpendingKey.DefaultAddress());
433 for (int i = 0; i < nTxs; ++i) {
434 auto wtx = CreateSaplingTxWithNoteData(consensusParams, wallet, saplingSpendingKey);
435 wallet.AddToWallet(wtx, true, NULL);
436 block1.vtx.push_back(wtx);
439 CBlockIndex index1(block1);
442 // Increment to get transactions witnessed
443 wallet.ChainTip(&index1, &block1, sproutTree, saplingTree, true);
447 block2.hashPrevBlock = block1.GetHash();
449 auto saplingTx = CreateSaplingTxWithNoteData(consensusParams, wallet, saplingSpendingKey);
450 wallet.AddToWallet(saplingTx, true, NULL);
451 block1.vtx.push_back(saplingTx);
454 CBlockIndex index2(block2);
457 struct timeval tv_start;
458 timer_start(tv_start);
459 wallet.ChainTip(&index2, &block2, sproutTree, saplingTree, true);
460 return timer_stop(tv_start);
463 // Fake the input of a given block
464 // This class is based on the class CCoinsViewDB, but with limited functionality.
465 // The construtor and the functions `GetCoins` and `HaveCoins` come directly from
466 // CCoinsViewDB, but the rest are either mocks and/or don't really do anything.
468 // The following constant is a duplicate of the one found in txdb.cpp
469 static const char DB_COINS = 'c';
471 class FakeCoinsViewDB : public CCoinsView {
476 SproutMerkleTree sproutTree;
477 SaplingMerkleTree saplingTree;
480 FakeCoinsViewDB(std::string dbName, uint256& hash) : db(GetDataDir() / dbName, 100, false, false), hash(hash) {}
482 bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
483 if (rt == sproutTree.root()) {
490 bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
491 if (rt == saplingTree.root()) {
498 bool GetNullifier(const uint256 &nf, ShieldedType type) const {
502 bool GetCoins(const uint256 &txid, CCoins &coins) const {
503 return db.Read(std::make_pair(DB_COINS, txid), coins);
506 bool HaveCoins(const uint256 &txid) const {
507 return db.Exists(std::make_pair(DB_COINS, txid));
510 uint256 GetBestBlock() const {
514 uint256 GetBestAnchor(ShieldedType type) const {
517 return sproutTree.root();
519 return saplingTree.root();
521 throw new std::runtime_error("Unknown shielded type");
525 bool BatchWrite(CCoinsMap &mapCoins,
526 const uint256 &hashBlock,
527 const uint256 &hashSproutAnchor,
528 const uint256 &hashSaplingAnchor,
529 CAnchorsSproutMap &mapSproutAnchors,
530 CAnchorsSaplingMap &mapSaplingAnchors,
531 CNullifiersMap &mapSproutNullifiers,
532 CNullifiersMap &mapSaplingNullifiers) {
536 bool GetStats(CCoinsStats &stats) const {
541 double benchmark_connectblock_slow()
543 // Test for issue 2017-05-01.a
544 SelectParams(CBaseChainParams::MAIN);
546 FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb");
547 if (!fp) throw new std::runtime_error("Failed to open block data file");
548 CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION);
553 auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef");
554 FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev);
555 CCoinsViewCache view(&fakeDB);
558 CBlockIndex index(block);
559 index.SetHeight(107134);
560 CBlockIndex indexPrev;
561 indexPrev.phashBlock = &hashPrev;
562 indexPrev.SetHeight(index.GetHeight() - 1);
563 index.pprev = &indexPrev;
564 mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev));
566 CValidationState state;
567 struct timeval tv_start;
568 timer_start(tv_start);
569 assert(ConnectBlock(block, state, &index, view, Params(), true));
570 auto duration = timer_stop(tv_start);
572 // Undo alterations to global state
573 mapBlockIndex.erase(hashPrev);
574 SelectParamsFromCommandLine();
579 extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
580 extern UniValue sendtoaddress(const UniValue& params, bool fHelp);
582 double benchmark_sendtoaddress(CAmount amount)
584 UniValue params(UniValue::VARR);
585 auto addr = getnewaddress(params, false);
587 params.push_back(addr);
588 params.push_back(ValueFromAmount(amount));
590 struct timeval tv_start;
591 timer_start(tv_start);
592 auto txid = sendtoaddress(params, false);
593 return timer_stop(tv_start);
596 double benchmark_loadwallet()
599 struct timeval tv_start;
600 bool fFirstRunRet=true;
601 timer_start(tv_start);
602 pwalletMain = new CWallet("wallet.dat");
603 DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRunRet);
604 auto res = timer_stop(tv_start);
609 extern UniValue listunspent(const UniValue& params, bool fHelp);
611 double benchmark_listunspent()
613 UniValue params(UniValue::VARR);
614 struct timeval tv_start;
615 timer_start(tv_start);
616 auto unspent = listunspent(params, false);
617 return timer_stop(tv_start);
620 double benchmark_create_sapling_spend()
622 auto sk = libzcash::SaplingSpendingKey::random();
623 auto expsk = sk.expanded_spending_key();
624 auto address = sk.default_address();
625 SaplingNote note(address, GetRand(MAX_MONEY));
626 SaplingMerkleTree tree;
627 auto maybe_cm = note.cm();
628 tree.append(maybe_cm.get());
629 auto anchor = tree.root();
630 auto witness = tree.witness();
631 auto maybe_nf = note.nullifier(expsk.full_viewing_key(), witness.position());
632 if (!(maybe_cm && maybe_nf)) {
633 throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not create note commitment and nullifier");
636 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
637 ss << witness.path();
638 std::vector<unsigned char> witnessChars(ss.begin(), ss.end());
641 librustzcash_sapling_generate_r(alpha.begin());
643 auto ctx = librustzcash_sapling_proving_ctx_init();
645 struct timeval tv_start;
646 timer_start(tv_start);
648 SpendDescription sdesc;
649 bool result = librustzcash_sapling_spend_proof(
651 expsk.full_viewing_key().ak.begin(),
661 sdesc.zkproof.data());
663 double t = timer_stop(tv_start);
664 librustzcash_sapling_proving_ctx_free(ctx);
666 throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_spend_proof() should return true");
671 double benchmark_create_sapling_output()
673 auto sk = libzcash::SaplingSpendingKey::random();
674 auto address = sk.default_address();
676 std::array<unsigned char, ZC_MEMO_SIZE> memo;
677 SaplingNote note(address, GetRand(MAX_MONEY));
679 libzcash::SaplingNotePlaintext notePlaintext(note, memo);
680 auto res = notePlaintext.encrypt(note.pk_d);
682 throw JSONRPCError(RPC_INTERNAL_ERROR, "SaplingNotePlaintext::encrypt() failed");
685 auto enc = res.get();
686 auto encryptor = enc.second;
688 auto ctx = librustzcash_sapling_proving_ctx_init();
690 struct timeval tv_start;
691 timer_start(tv_start);
693 OutputDescription odesc;
694 bool result = librustzcash_sapling_output_proof(
696 encryptor.get_esk().begin(),
702 odesc.zkproof.begin());
704 double t = timer_stop(tv_start);
705 librustzcash_sapling_proving_ctx_free(ctx);
707 throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_output_proof() should return true");
712 // Verify Sapling spend from testnet
713 // txid: abbd823cbd3d4e3b52023599d81a96b74817e95ce5bb58354f979156bd22ecc8
715 double benchmark_verify_sapling_spend()
717 SpendDescription spend;
718 CDataStream ss(ParseHex("8c6cf86bbb83bf0d075e5bd9bb4b5cd56141577be69f032880b11e26aa32aa5ef09fd00899e4b469fb11f38e9d09dc0379f0b11c23b5fe541765f76695120a03f0261d32af5d2a2b1e5c9a04200cd87d574dc42349de9790012ce560406a8a876a1e54cfcdc0eb74998abec2a9778330eeb2a0ac0e41d0c9ed5824fbd0dbf7da930ab299966ce333fd7bc1321dada0817aac5444e02c754069e218746bf879d5f2a20a8b028324fb2c73171e63336686aa5ec2e6e9a08eb18b87c14758c572f4531ccf6b55d09f44beb8b47563be4eff7a52598d80959dd9c9fee5ac4783d8370cb7d55d460053d3e067b5f9fe75ff2722623fb1825fcba5e9593d4205b38d1f502ff03035463043bd393a5ee039ce75a5d54f21b395255df6627ef96751566326f7d4a77d828aa21b1827282829fcbc42aad59cdb521e1a3aaa08b99ea8fe7fff0a04da31a52260fc6daeccd79bb877bdd8506614282258e15b3fe74bf71a93f4be3b770119edf99a317b205eea7d5ab800362b97384273888106c77d633600"), SER_NETWORK, PROTOCOL_VERSION);
720 uint256 dataToBeSigned = uint256S("0x2dbf83fe7b88a7cbd80fac0c719483906bb9a0c4fc69071e4780d5f2c76e592c");
722 auto ctx = librustzcash_sapling_verification_ctx_init();
724 struct timeval tv_start;
725 timer_start(tv_start);
727 bool result = librustzcash_sapling_check_spend(
730 spend.anchor.begin(),
731 spend.nullifier.begin(),
733 spend.zkproof.begin(),
734 spend.spendAuthSig.begin(),
735 dataToBeSigned.begin()
738 double t = timer_stop(tv_start);
739 librustzcash_sapling_verification_ctx_free(ctx);
741 throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_check_spend() should return true");
746 // Verify Sapling output from testnet
747 // txid: abbd823cbd3d4e3b52023599d81a96b74817e95ce5bb58354f979156bd22ecc8
749 double benchmark_verify_sapling_output()
751 OutputDescription output;
752 CDataStream ss(ParseHex("edd742af18857e5ec2d71d346a7fe2ac97c137339bd5268eea86d32e0ff4f38f76213fa8cfed3347ac4e8572dd88aff395c0c10a59f8b3f49d2bc539ed6c726667e29d4763f914ddd0abf1cdfa84e44de87c233434c7e69b8b5b8f4623c8aa444163425bae5cef842972fed66046c1c6ce65c866ad894d02e6e6dcaae7a962d9f2ef95757a09c486928e61f0f7aed90ad0a542b0d3dc5fe140dfa7626b9315c77e03b055f19cbacd21a866e46f06c00e0c7792b2a590a611439b510a9aaffcf1073bad23e712a9268b36888e3727033eee2ab4d869f54a843f93b36ef489fb177bf74b41a9644e5d2a0a417c6ac1c8869bc9b83273d453f878ed6fd96b82a5939903f7b64ecaf68ea16e255a7fb7cc0b6d8b5608a1c6b0ed3024cc62c2f0f9c5cfc7b431ae6e9d40815557aa1d010523f9e1960de77b2274cb6710d229d475c87ae900183206ba90cb5bbc8ec0df98341b82726c705e0308ca5dc08db4db609993a1046dfb43dfd8c760be506c0bed799bb2205fc29dc2e654dce731034a23b0aaf6da0199248702ee0523c159f41f4cbfff6c35ace4dd9ae834e44e09c76a0cbdda1d3f6a2c75ad71212daf9575ab5f09ca148718e667f29ddf18c8a330a86ace18a86e89454653902aa393c84c6b694f27d0d42e24e7ac9fe34733de5ec15f5066081ce912c62c1a804a2bb4dedcef7cc80274f6bb9e89e2fce91dc50d6a73c8aefb9872f1cf3524a92626a0b8f39bbf7bf7d96ca2f770fc04d7f457021c536a506a187a93b2245471ddbfb254a71bc4a0d72c8d639a31c7b1920087ffca05c24214157e2e7b28184e91989ef0b14f9b34c3dc3cc0ac64226b9e337095870cb0885737992e120346e630a416a9b217679ce5a778fb15779c136bcecca5efe79012013d77d90b4e99dd22c8f35bc77121716e160d05bd30d288ee8886390ee436f85bdc9029df888a3a3326d9d4ddba5cb5318b3274928829d662e96fea1d601f7a306251ed8c6cc4e5a3a7a98c35a3650482a0eee08f3b4c2da9b22947c96138f1505c2f081f8972d429f3871f32bef4aaa51aa6945df8e9c9760531ac6f627d17c1518202818a91ca304fb4037875c666060597976144fcbbc48a776a2c61beb9515fa8f3ae6d3a041d320a38a8ac75cb47bb9c866ee497fc3cd13299970c4b369c1c2ceb4220af082fbecdd8114492a8e4d713b5a73396fd224b36c1185bd5e20d683e6c8db35346c47ae7401988255da7cfffdced5801067d4d296688ee8fe424b4a8a69309ce257eefb9345ebfda3f6de46bb11ec94133e1f72cd7ac54934d6cf17b3440800e70b80ebc7c7bfc6fb0fc2c"), SER_NETWORK, PROTOCOL_VERSION);
755 auto ctx = librustzcash_sapling_verification_ctx_init();
757 struct timeval tv_start;
758 timer_start(tv_start);
760 bool result = librustzcash_sapling_check_output(
764 output.ephemeralKey.begin(),
765 output.zkproof.begin()
768 double t = timer_stop(tv_start);
769 librustzcash_sapling_verification_ctx_free(ctx);
771 throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_check_output() should return true");
773 return timer_stop(tv_start);