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 "rpcserver.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"
34 using namespace libzcash;
35 // This method is based on Shutdown from init.cpp
36 void pre_wallet_load()
38 LogPrintf("%s: In progress...\n", __func__);
39 if (ShutdownRequested())
40 throw new std::runtime_error("The node is shutting down");
43 pwalletMain->Flush(false);
45 GenerateBitcoins(false, NULL, 0);
47 UnregisterNodeSignals(GetNodeSignals());
49 pwalletMain->Flush(true);
51 UnregisterValidationInterface(pwalletMain);
55 RegisterNodeSignals(GetNodeSignals());
56 LogPrintf("%s: done\n", __func__);
59 void post_wallet_load(){
60 RegisterValidationInterface(pwalletMain);
62 // Generate coins in the background
63 if (pwalletMain || !GetArg("-mineraddress", "").empty())
64 GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1));
69 void timer_start(timeval &tv_start)
71 gettimeofday(&tv_start, 0);
74 double timer_stop(timeval &tv_start)
77 struct timeval tv_end;
78 gettimeofday(&tv_end, 0);
79 elapsed = double(tv_end.tv_sec-tv_start.tv_sec) +
80 (tv_end.tv_usec-tv_start.tv_usec)/double(1000000);
84 double benchmark_sleep()
86 struct timeval tv_start;
87 timer_start(tv_start);
89 return timer_stop(tv_start);
92 double benchmark_parameter_loading()
94 // FIXME: this is duplicated with the actual loading code
95 boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
96 boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
98 struct timeval tv_start;
99 timer_start(tv_start);
101 auto newParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
103 double ret = timer_stop(tv_start);
110 double benchmark_create_joinsplit()
114 /* Get the anchor of an empty commitment tree. */
115 uint256 anchor = ZCIncrementalMerkleTree().root();
117 struct timeval tv_start;
118 timer_start(tv_start);
119 JSDescription jsdesc(*pzcashParams,
122 {JSInput(), JSInput()},
123 {JSOutput(), JSOutput()},
126 double ret = timer_stop(tv_start);
128 auto verifier = libzcash::ProofVerifier::Strict();
129 assert(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash));
133 std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
135 std::vector<double> ret;
136 std::vector<std::future<double>> tasks;
137 std::vector<std::thread> threads;
138 for (int i = 0; i < nThreads; i++) {
139 std::packaged_task<double(void)> task(&benchmark_create_joinsplit);
140 tasks.emplace_back(task.get_future());
141 threads.emplace_back(std::move(task));
143 std::future_status status;
144 for (auto it = tasks.begin(); it != tasks.end(); it++) {
146 ret.push_back(it->get());
148 for (auto it = threads.begin(); it != threads.end(); it++) {
154 double benchmark_verify_joinsplit(const JSDescription &joinsplit)
156 struct timeval tv_start;
157 timer_start(tv_start);
159 auto verifier = libzcash::ProofVerifier::Strict();
160 joinsplit.Verify(*pzcashParams, verifier, pubKeyHash);
161 return timer_stop(tv_start);
165 double benchmark_solve_equihash()
168 CEquihashInput I{pblock};
169 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
172 unsigned int n = Params(CBaseChainParams::MAIN).EquihashN();
173 unsigned int k = Params(CBaseChainParams::MAIN).EquihashK();
174 crypto_generichash_blake2b_state eh_state;
175 EhInitialiseState(n, k, eh_state);
176 crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size());
179 randombytes_buf(nonce.begin(), 32);
180 crypto_generichash_blake2b_update(&eh_state,
184 struct timeval tv_start;
185 timer_start(tv_start);
186 std::set<std::vector<unsigned int>> solns;
187 EhOptimisedSolveUncancellable(n, k, eh_state,
188 [](std::vector<unsigned char> soln) { return false; });
189 return timer_stop(tv_start);
192 std::vector<double> benchmark_solve_equihash_threaded(int nThreads)
194 std::vector<double> ret;
195 std::vector<std::future<double>> tasks;
196 std::vector<std::thread> threads;
197 for (int i = 0; i < nThreads; i++) {
198 std::packaged_task<double(void)> task(&benchmark_solve_equihash);
199 tasks.emplace_back(task.get_future());
200 threads.emplace_back(std::move(task));
202 std::future_status status;
203 for (auto it = tasks.begin(); it != tasks.end(); it++) {
205 ret.push_back(it->get());
207 for (auto it = threads.begin(); it != threads.end(); it++) {
212 #endif // ENABLE_MINING
214 double benchmark_verify_equihash()
216 CChainParams params = Params(CBaseChainParams::MAIN);
217 CBlock genesis = Params(CBaseChainParams::MAIN).GenesisBlock();
218 CBlockHeader genesis_header = genesis.GetBlockHeader();
219 struct timeval tv_start;
220 timer_start(tv_start);
221 CheckEquihashSolution(&genesis_header, params);
222 return timer_stop(tv_start);
225 double benchmark_large_tx(size_t nInputs)
227 // Create priv/pub key
229 priv.MakeNewKey(false);
230 auto pub = priv.GetPubKey();
231 CBasicKeyStore tempKeystore;
232 tempKeystore.AddKey(priv);
234 // The "original" transaction that the spending transaction will spend
236 CMutableTransaction m_orig_tx;
237 m_orig_tx.vout.resize(1);
238 m_orig_tx.vout[0].nValue = 1000000;
239 CScript prevPubKey = GetScriptForDestination(pub.GetID());
240 m_orig_tx.vout[0].scriptPubKey = prevPubKey;
242 auto orig_tx = CTransaction(m_orig_tx);
244 CMutableTransaction spending_tx;
245 spending_tx.fOverwintered = true;
246 spending_tx.nVersion = 3;
247 spending_tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
249 auto input_hash = orig_tx.GetHash();
250 // Add nInputs inputs
251 for (size_t i = 0; i < nInputs; i++) {
252 spending_tx.vin.emplace_back(input_hash, 0);
255 // Sign for all the inputs
256 auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId;
257 for (size_t i = 0; i < nInputs; i++) {
258 SignSignature(tempKeystore, prevPubKey, spending_tx, i, 1000000, SIGHASH_ALL, consensusBranchId);
261 // Spending tx has all its inputs signed and does not need to be mutated anymore
262 CTransaction final_spending_tx(spending_tx);
264 // Benchmark signature verification costs:
265 struct timeval tv_start;
266 timer_start(tv_start);
267 PrecomputedTransactionData txdata(final_spending_tx);
268 for (size_t i = 0; i < nInputs; i++) {
269 ScriptError serror = SCRIPT_ERR_OK;
270 assert(VerifyScript(final_spending_tx.vin[i].scriptSig,
272 STANDARD_SCRIPT_VERIFY_FLAGS,
273 TransactionSignatureChecker(&final_spending_tx, i, 1000000, txdata),
277 return timer_stop(tv_start);
280 double benchmark_try_decrypt_notes(size_t nAddrs)
283 for (int i = 0; i < nAddrs; i++) {
284 auto sk = libzcash::SpendingKey::random();
285 wallet.AddSpendingKey(sk);
288 auto sk = libzcash::SpendingKey::random();
289 auto tx = GetValidReceive(*pzcashParams, sk, 10, true);
291 struct timeval tv_start;
292 timer_start(tv_start);
293 auto nd = wallet.FindMyNotes(tx);
294 return timer_stop(tv_start);
297 double benchmark_increment_note_witnesses(size_t nTxs)
300 ZCIncrementalMerkleTree tree;
302 auto sk = libzcash::SpendingKey::random();
303 wallet.AddSpendingKey(sk);
307 for (int i = 0; i < nTxs; i++) {
308 auto wtx = GetValidReceive(*pzcashParams, sk, 10, true);
309 auto note = GetNote(*pzcashParams, sk, wtx, 0, 1);
310 auto nullifier = note.nullifier(sk);
312 mapNoteData_t noteData;
313 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
314 CNoteData nd {sk.address(), nullifier};
315 noteData[jsoutpt] = nd;
317 wtx.SetNoteData(noteData);
318 wallet.AddToWallet(wtx, true, NULL);
319 block1.vtx.push_back(wtx);
321 CBlockIndex index1(block1);
324 // Increment to get transactions witnessed
325 wallet.ChainTip(&index1, &block1, tree, true);
329 block2.hashPrevBlock = block1.GetHash();
331 auto wtx = GetValidReceive(*pzcashParams, sk, 10, true);
332 auto note = GetNote(*pzcashParams, sk, wtx, 0, 1);
333 auto nullifier = note.nullifier(sk);
335 mapNoteData_t noteData;
336 JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
337 CNoteData nd {sk.address(), nullifier};
338 noteData[jsoutpt] = nd;
340 wtx.SetNoteData(noteData);
341 wallet.AddToWallet(wtx, true, NULL);
342 block2.vtx.push_back(wtx);
344 CBlockIndex index2(block2);
347 struct timeval tv_start;
348 timer_start(tv_start);
349 wallet.ChainTip(&index2, &block2, tree, true);
350 return timer_stop(tv_start);
353 // Fake the input of a given block
354 class FakeCoinsViewDB : public CCoinsViewDB {
356 ZCIncrementalMerkleTree t;
359 FakeCoinsViewDB(std::string dbName, uint256& hash) : CCoinsViewDB(dbName, 100, false, false), hash(hash) {}
361 bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
362 if (rt == t.root()) {
369 bool GetNullifier(const uint256 &nf) const {
373 uint256 GetBestBlock() const {
377 uint256 GetBestAnchor() const {
381 bool BatchWrite(CCoinsMap &mapCoins,
382 const uint256 &hashBlock,
383 const uint256 &hashAnchor,
384 CAnchorsMap &mapAnchors,
385 CNullifiersMap &mapNullifiers) {
389 bool GetStats(CCoinsStats &stats) const {
394 double benchmark_connectblock_slow()
396 // Test for issue 2017-05-01.a
397 SelectParams(CBaseChainParams::MAIN);
399 FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb");
400 if (!fp) throw new std::runtime_error("Failed to open block data file");
401 CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION);
406 auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef");
407 FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev);
408 CCoinsViewCache view(&fakeDB);
411 CBlockIndex index(block);
412 index.nHeight = 107134;
413 CBlockIndex indexPrev;
414 indexPrev.phashBlock = &hashPrev;
415 indexPrev.nHeight = index.nHeight - 1;
416 index.pprev = &indexPrev;
417 mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev));
419 CValidationState state;
420 struct timeval tv_start;
421 timer_start(tv_start);
422 assert(ConnectBlock(block, state, &index, view, true));
423 auto duration = timer_stop(tv_start);
425 // Undo alterations to global state
426 mapBlockIndex.erase(hashPrev);
427 SelectParamsFromCommandLine();
432 double benchmark_sendtoaddress(CAmount amount)
434 UniValue params(UniValue::VARR);
435 auto addr = getnewaddress(params, false);
437 params.push_back(addr);
438 params.push_back(ValueFromAmount(amount));
440 struct timeval tv_start;
441 timer_start(tv_start);
442 auto txid = sendtoaddress(params, false);
443 return timer_stop(tv_start);
446 double benchmark_loadwallet()
449 struct timeval tv_start;
450 bool fFirstRunRet=true;
451 timer_start(tv_start);
452 pwalletMain = new CWallet("wallet.dat");
453 DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRunRet);
454 auto res = timer_stop(tv_start);
459 double benchmark_listunspent()
461 UniValue params(UniValue::VARR);
462 struct timeval tv_start;
463 timer_start(tv_start);
464 auto unspent = listunspent(params, false);
465 return timer_stop(tv_start);