]>
Commit | Line | Data |
---|---|---|
c66c731a | 1 | #include <cstdio> |
9e52ca32 | 2 | #include <future> |
c66c731a | 3 | #include <map> |
9e52ca32 | 4 | #include <thread> |
6962bb3d TH |
5 | #include <unistd.h> |
6 | #include <boost/filesystem.hpp> | |
9e52ca32 | 7 | |
6962bb3d TH |
8 | #include "coins.h" |
9 | #include "util.h" | |
10 | #include "init.h" | |
11 | #include "primitives/transaction.h" | |
f5edc37f | 12 | #include "base58.h" |
bf8def97 | 13 | #include "crypto/equihash.h" |
c66c731a | 14 | #include "chain.h" |
bf8def97 | 15 | #include "chainparams.h" |
f5edc37f JG |
16 | #include "consensus/validation.h" |
17 | #include "main.h" | |
18 | #include "miner.h" | |
a1cd1a27 | 19 | #include "pow.h" |
a76174b7 | 20 | #include "rpcserver.h" |
f5edc37f | 21 | #include "script/sign.h" |
722b0117 TH |
22 | #include "sodium.h" |
23 | #include "streams.h" | |
c66c731a | 24 | #include "txdb.h" |
0bb3d40f | 25 | #include "utiltest.h" |
f5edc37f | 26 | #include "wallet/wallet.h" |
6962bb3d TH |
27 | |
28 | #include "zcbenchmarks.h" | |
29 | ||
2dc35992 SB |
30 | #include "zcash/Zcash.h" |
31 | #include "zcash/IncrementalMerkleTree.hpp" | |
32 | ||
33 | using namespace libzcash; | |
2e8aefdc AG |
34 | // This method is based on Shutdown from init.cpp |
35 | void pre_wallet_load() | |
36 | { | |
37 | LogPrintf("%s: In progress...\n", __func__); | |
38 | if (ShutdownRequested()) | |
39 | throw new std::runtime_error("The node is shutting down"); | |
40 | ||
41 | if (pwalletMain) | |
42 | pwalletMain->Flush(false); | |
43 | #ifdef ENABLE_MINING | |
44 | GenerateBitcoins(false, NULL, 0); | |
45 | #endif | |
46 | UnregisterNodeSignals(GetNodeSignals()); | |
47 | if (pwalletMain) | |
48 | pwalletMain->Flush(true); | |
49 | ||
50 | UnregisterValidationInterface(pwalletMain); | |
51 | delete pwalletMain; | |
52 | pwalletMain = NULL; | |
53 | bitdb.Reset(); | |
54 | RegisterNodeSignals(GetNodeSignals()); | |
55 | LogPrintf("%s: done\n", __func__); | |
56 | } | |
57 | ||
58 | void post_wallet_load(){ | |
59 | RegisterValidationInterface(pwalletMain); | |
60 | #ifdef ENABLE_MINING | |
61 | // Generate coins in the background | |
62 | if (pwalletMain || !GetArg("-mineraddress", "").empty()) | |
63 | GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); | |
64 | #endif | |
65 | } | |
66 | ||
2dc35992 | 67 | |
9e52ca32 | 68 | void timer_start(timeval &tv_start) |
6962bb3d TH |
69 | { |
70 | gettimeofday(&tv_start, 0); | |
71 | } | |
72 | ||
9e52ca32 | 73 | double timer_stop(timeval &tv_start) |
6962bb3d TH |
74 | { |
75 | double elapsed; | |
76 | struct timeval tv_end; | |
77 | gettimeofday(&tv_end, 0); | |
78 | elapsed = double(tv_end.tv_sec-tv_start.tv_sec) + | |
79 | (tv_end.tv_usec-tv_start.tv_usec)/double(1000000); | |
80 | return elapsed; | |
81 | } | |
82 | ||
83 | double benchmark_sleep() | |
84 | { | |
9e52ca32 JG |
85 | struct timeval tv_start; |
86 | timer_start(tv_start); | |
6962bb3d | 87 | sleep(1); |
9e52ca32 | 88 | return timer_stop(tv_start); |
6962bb3d TH |
89 | } |
90 | ||
91 | double benchmark_parameter_loading() | |
92 | { | |
93 | // FIXME: this is duplicated with the actual loading code | |
27e3f362 SB |
94 | boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key"; |
95 | boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key"; | |
6962bb3d | 96 | |
9e52ca32 JG |
97 | struct timeval tv_start; |
98 | timer_start(tv_start); | |
2dc35992 | 99 | |
1a9543d0 | 100 | auto newParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string()); |
2dc35992 | 101 | |
9e52ca32 | 102 | double ret = timer_stop(tv_start); |
2dc35992 SB |
103 | |
104 | delete newParams; | |
105 | ||
106 | return ret; | |
6962bb3d TH |
107 | } |
108 | ||
109 | double benchmark_create_joinsplit() | |
110 | { | |
21406393 | 111 | uint256 pubKeyHash; |
6962bb3d | 112 | |
6962bb3d | 113 | /* Get the anchor of an empty commitment tree. */ |
5961dcb6 | 114 | uint256 anchor = ZCIncrementalMerkleTree().root(); |
6962bb3d | 115 | |
9e52ca32 JG |
116 | struct timeval tv_start; |
117 | timer_start(tv_start); | |
22de1602 SB |
118 | JSDescription jsdesc(*pzcashParams, |
119 | pubKeyHash, | |
120 | anchor, | |
121 | {JSInput(), JSInput()}, | |
122 | {JSOutput(), JSOutput()}, | |
123 | 0, | |
124 | 0); | |
9e52ca32 | 125 | double ret = timer_stop(tv_start); |
21406393 | 126 | |
bc59f537 SB |
127 | auto verifier = libzcash::ProofVerifier::Strict(); |
128 | assert(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash)); | |
6962bb3d TH |
129 | return ret; |
130 | } | |
bf8def97 | 131 | |
4082dcb1 JG |
132 | std::vector<double> benchmark_create_joinsplit_threaded(int nThreads) |
133 | { | |
134 | std::vector<double> ret; | |
135 | std::vector<std::future<double>> tasks; | |
136 | std::vector<std::thread> threads; | |
137 | for (int i = 0; i < nThreads; i++) { | |
138 | std::packaged_task<double(void)> task(&benchmark_create_joinsplit); | |
139 | tasks.emplace_back(task.get_future()); | |
140 | threads.emplace_back(std::move(task)); | |
141 | } | |
142 | std::future_status status; | |
143 | for (auto it = tasks.begin(); it != tasks.end(); it++) { | |
144 | it->wait(); | |
145 | ret.push_back(it->get()); | |
146 | } | |
147 | for (auto it = threads.begin(); it != threads.end(); it++) { | |
148 | it->join(); | |
149 | } | |
150 | return ret; | |
151 | } | |
152 | ||
a8c68ffe | 153 | double benchmark_verify_joinsplit(const JSDescription &joinsplit) |
a1cd1a27 | 154 | { |
9e52ca32 JG |
155 | struct timeval tv_start; |
156 | timer_start(tv_start); | |
21406393 | 157 | uint256 pubKeyHash; |
bc59f537 SB |
158 | auto verifier = libzcash::ProofVerifier::Strict(); |
159 | joinsplit.Verify(*pzcashParams, verifier, pubKeyHash); | |
9e52ca32 | 160 | return timer_stop(tv_start); |
a1cd1a27 TH |
161 | } |
162 | ||
2cc0a252 | 163 | #ifdef ENABLE_MINING |
9e52ca32 | 164 | double benchmark_solve_equihash() |
bf8def97 | 165 | { |
722b0117 TH |
166 | CBlock pblock; |
167 | CEquihashInput I{pblock}; | |
168 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
169 | ss << I; | |
170 | ||
e9574728 JG |
171 | unsigned int n = Params(CBaseChainParams::MAIN).EquihashN(); |
172 | unsigned int k = Params(CBaseChainParams::MAIN).EquihashK(); | |
bf8def97 | 173 | crypto_generichash_blake2b_state eh_state; |
e9574728 | 174 | EhInitialiseState(n, k, eh_state); |
722b0117 TH |
175 | crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size()); |
176 | ||
177 | uint256 nonce; | |
178 | randombytes_buf(nonce.begin(), 32); | |
179 | crypto_generichash_blake2b_update(&eh_state, | |
180 | nonce.begin(), | |
181 | nonce.size()); | |
182 | ||
9e52ca32 JG |
183 | struct timeval tv_start; |
184 | timer_start(tv_start); | |
e9574728 | 185 | std::set<std::vector<unsigned int>> solns; |
51eb5273 | 186 | EhOptimisedSolveUncancellable(n, k, eh_state, |
5be6abbf | 187 | [](std::vector<unsigned char> soln) { return false; }); |
9e52ca32 | 188 | return timer_stop(tv_start); |
f7478de6 JG |
189 | } |
190 | ||
9e52ca32 | 191 | std::vector<double> benchmark_solve_equihash_threaded(int nThreads) |
f7478de6 | 192 | { |
9e52ca32 JG |
193 | std::vector<double> ret; |
194 | std::vector<std::future<double>> tasks; | |
195 | std::vector<std::thread> threads; | |
196 | for (int i = 0; i < nThreads; i++) { | |
197 | std::packaged_task<double(void)> task(&benchmark_solve_equihash); | |
198 | tasks.emplace_back(task.get_future()); | |
199 | threads.emplace_back(std::move(task)); | |
200 | } | |
201 | std::future_status status; | |
202 | for (auto it = tasks.begin(); it != tasks.end(); it++) { | |
203 | it->wait(); | |
204 | ret.push_back(it->get()); | |
205 | } | |
206 | for (auto it = threads.begin(); it != threads.end(); it++) { | |
207 | it->join(); | |
208 | } | |
209 | return ret; | |
bf8def97 | 210 | } |
2cc0a252 | 211 | #endif // ENABLE_MINING |
d44feea4 | 212 | |
a1cd1a27 | 213 | double benchmark_verify_equihash() |
d44feea4 | 214 | { |
a1cd1a27 TH |
215 | CChainParams params = Params(CBaseChainParams::MAIN); |
216 | CBlock genesis = Params(CBaseChainParams::MAIN).GenesisBlock(); | |
217 | CBlockHeader genesis_header = genesis.GetBlockHeader(); | |
9e52ca32 JG |
218 | struct timeval tv_start; |
219 | timer_start(tv_start); | |
a1cd1a27 | 220 | CheckEquihashSolution(&genesis_header, params); |
9e52ca32 | 221 | return timer_stop(tv_start); |
d44feea4 | 222 | } |
a1cd1a27 | 223 | |
9c45b501 | 224 | double benchmark_large_tx() |
f5edc37f | 225 | { |
9c45b501 | 226 | // Number of inputs in the spending transaction that we will simulate |
74f15a73 | 227 | const size_t NUM_INPUTS = 555; |
9c45b501 SB |
228 | |
229 | // Create priv/pub key | |
230 | CKey priv; | |
231 | priv.MakeNewKey(false); | |
232 | auto pub = priv.GetPubKey(); | |
233 | CBasicKeyStore tempKeystore; | |
234 | tempKeystore.AddKey(priv); | |
235 | ||
236 | // The "original" transaction that the spending transaction will spend | |
237 | // from. | |
238 | CMutableTransaction m_orig_tx; | |
239 | m_orig_tx.vout.resize(1); | |
240 | m_orig_tx.vout[0].nValue = 1000000; | |
241 | CScript prevPubKey = GetScriptForDestination(pub.GetID()); | |
242 | m_orig_tx.vout[0].scriptPubKey = prevPubKey; | |
243 | ||
244 | auto orig_tx = CTransaction(m_orig_tx); | |
245 | ||
246 | CMutableTransaction spending_tx; | |
805344dc | 247 | auto input_hash = orig_tx.GetHash(); |
9c45b501 SB |
248 | // Add NUM_INPUTS inputs |
249 | for (size_t i = 0; i < NUM_INPUTS; i++) { | |
250 | spending_tx.vin.emplace_back(input_hash, 0); | |
f5edc37f JG |
251 | } |
252 | ||
9c45b501 SB |
253 | // Sign for all the inputs |
254 | for (size_t i = 0; i < NUM_INPUTS; i++) { | |
157a5d0d | 255 | SignSignature(tempKeystore, prevPubKey, spending_tx, i, 1000000, SIGHASH_ALL); |
f5edc37f | 256 | } |
f5edc37f | 257 | |
9c45b501 SB |
258 | // Serialize: |
259 | { | |
260 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
261 | ss << spending_tx; | |
262 | //std::cout << "SIZE OF SPENDING TX: " << ss.size() << std::endl; | |
f5edc37f | 263 | |
74f15a73 SB |
264 | auto error = MAX_TX_SIZE / 20; // 5% error |
265 | assert(ss.size() < MAX_TX_SIZE + error); | |
266 | assert(ss.size() > MAX_TX_SIZE - error); | |
f5edc37f JG |
267 | } |
268 | ||
822b84b6 S |
269 | // Spending tx has all its inputs signed and does not need to be mutated anymore |
270 | CTransaction final_spending_tx(spending_tx); | |
271 | ||
9c45b501 | 272 | // Benchmark signature verification costs: |
9e52ca32 JG |
273 | struct timeval tv_start; |
274 | timer_start(tv_start); | |
9c45b501 SB |
275 | for (size_t i = 0; i < NUM_INPUTS; i++) { |
276 | ScriptError serror = SCRIPT_ERR_OK; | |
75c2f268 | 277 | assert(VerifyScript(final_spending_tx.vin[i].scriptSig, |
9c45b501 SB |
278 | prevPubKey, |
279 | STANDARD_SCRIPT_VERIFY_FLAGS, | |
2d42e1a9 | 280 | TransactionSignatureChecker(&final_spending_tx, i, 1000000), |
9c45b501 SB |
281 | &serror)); |
282 | } | |
9e52ca32 | 283 | return timer_stop(tv_start); |
f5edc37f JG |
284 | } |
285 | ||
88b7f3c2 | 286 | double benchmark_try_decrypt_notes(size_t nAddrs) |
0fbab55b | 287 | { |
0fbab55b | 288 | CWallet wallet; |
88b7f3c2 | 289 | for (int i = 0; i < nAddrs; i++) { |
0fbab55b JG |
290 | auto sk = libzcash::SpendingKey::random(); |
291 | wallet.AddSpendingKey(sk); | |
292 | } | |
293 | ||
88b7f3c2 JG |
294 | auto sk = libzcash::SpendingKey::random(); |
295 | auto tx = GetValidReceive(*pzcashParams, sk, 10, true); | |
0fbab55b JG |
296 | |
297 | struct timeval tv_start; | |
298 | timer_start(tv_start); | |
299 | auto nd = wallet.FindMyNotes(tx); | |
300 | return timer_stop(tv_start); | |
301 | } | |
302 | ||
0bb3d40f JG |
303 | double benchmark_increment_note_witnesses(size_t nTxs) |
304 | { | |
305 | CWallet wallet; | |
306 | ZCIncrementalMerkleTree tree; | |
307 | ||
308 | auto sk = libzcash::SpendingKey::random(); | |
309 | wallet.AddSpendingKey(sk); | |
310 | ||
311 | // First block | |
312 | CBlock block1; | |
313 | for (int i = 0; i < nTxs; i++) { | |
314 | auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); | |
315 | auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); | |
316 | auto nullifier = note.nullifier(sk); | |
317 | ||
318 | mapNoteData_t noteData; | |
319 | JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; | |
320 | CNoteData nd {sk.address(), nullifier}; | |
321 | noteData[jsoutpt] = nd; | |
322 | ||
323 | wtx.SetNoteData(noteData); | |
324 | wallet.AddToWallet(wtx, true, NULL); | |
325 | block1.vtx.push_back(wtx); | |
326 | } | |
327 | CBlockIndex index1(block1); | |
328 | index1.nHeight = 1; | |
329 | ||
330 | // Increment to get transactions witnessed | |
331 | wallet.ChainTip(&index1, &block1, tree, true); | |
332 | ||
333 | // Second block | |
334 | CBlock block2; | |
335 | block2.hashPrevBlock = block1.GetHash(); | |
9755eb82 JG |
336 | { |
337 | auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); | |
338 | auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); | |
339 | auto nullifier = note.nullifier(sk); | |
340 | ||
341 | mapNoteData_t noteData; | |
342 | JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; | |
343 | CNoteData nd {sk.address(), nullifier}; | |
344 | noteData[jsoutpt] = nd; | |
345 | ||
346 | wtx.SetNoteData(noteData); | |
347 | wallet.AddToWallet(wtx, true, NULL); | |
348 | block2.vtx.push_back(wtx); | |
349 | } | |
0bb3d40f JG |
350 | CBlockIndex index2(block2); |
351 | index2.nHeight = 2; | |
352 | ||
353 | struct timeval tv_start; | |
354 | timer_start(tv_start); | |
355 | wallet.ChainTip(&index2, &block2, tree, true); | |
356 | return timer_stop(tv_start); | |
357 | } | |
358 | ||
c66c731a JG |
359 | // Fake the input of a given block |
360 | class FakeCoinsViewDB : public CCoinsViewDB { | |
361 | uint256 hash; | |
362 | ZCIncrementalMerkleTree t; | |
363 | ||
364 | public: | |
365 | FakeCoinsViewDB(std::string dbName, uint256& hash) : CCoinsViewDB(dbName, 100, false, false), hash(hash) {} | |
366 | ||
367 | bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { | |
368 | if (rt == t.root()) { | |
369 | tree = t; | |
370 | return true; | |
371 | } | |
372 | return false; | |
373 | } | |
374 | ||
375 | bool GetNullifier(const uint256 &nf) const { | |
376 | return false; | |
377 | } | |
378 | ||
379 | uint256 GetBestBlock() const { | |
380 | return hash; | |
381 | } | |
382 | ||
383 | uint256 GetBestAnchor() const { | |
384 | return t.root(); | |
385 | } | |
386 | ||
387 | bool BatchWrite(CCoinsMap &mapCoins, | |
388 | const uint256 &hashBlock, | |
389 | const uint256 &hashAnchor, | |
390 | CAnchorsMap &mapAnchors, | |
391 | CNullifiersMap &mapNullifiers) { | |
392 | return false; | |
393 | } | |
394 | ||
395 | bool GetStats(CCoinsStats &stats) const { | |
396 | return false; | |
397 | } | |
398 | }; | |
399 | ||
400 | double benchmark_connectblock_slow() | |
401 | { | |
402 | // Test for issue 2017-05-01.a | |
403 | SelectParams(CBaseChainParams::MAIN); | |
404 | CBlock block; | |
405 | FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb"); | |
406 | if (!fp) throw new std::runtime_error("Failed to open block data file"); | |
407 | CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION); | |
408 | blkFile >> block; | |
409 | blkFile.fclose(); | |
410 | ||
411 | // Fake its inputs | |
412 | auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef"); | |
413 | FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev); | |
414 | CCoinsViewCache view(&fakeDB); | |
415 | ||
416 | // Fake the chain | |
417 | CBlockIndex index(block); | |
418 | index.nHeight = 107134; | |
419 | CBlockIndex indexPrev; | |
420 | indexPrev.phashBlock = &hashPrev; | |
421 | indexPrev.nHeight = index.nHeight - 1; | |
422 | index.pprev = &indexPrev; | |
423 | mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev)); | |
424 | ||
425 | CValidationState state; | |
426 | struct timeval tv_start; | |
427 | timer_start(tv_start); | |
428 | assert(ConnectBlock(block, state, &index, view, true)); | |
429 | auto duration = timer_stop(tv_start); | |
430 | ||
431 | // Undo alterations to global state | |
432 | mapBlockIndex.erase(hashPrev); | |
433 | SelectParamsFromCommandLine(); | |
434 | ||
435 | return duration; | |
436 | } | |
437 | ||
a76174b7 JG |
438 | double benchmark_sendtoaddress(CAmount amount) |
439 | { | |
440 | UniValue params(UniValue::VARR); | |
441 | auto addr = getnewaddress(params, false); | |
442 | ||
443 | params.push_back(addr); | |
444 | params.push_back(ValueFromAmount(amount)); | |
445 | ||
446 | struct timeval tv_start; | |
447 | timer_start(tv_start); | |
448 | auto txid = sendtoaddress(params, false); | |
449 | return timer_stop(tv_start); | |
450 | } | |
451 | ||
2e8aefdc AG |
452 | double benchmark_loadwallet() |
453 | { | |
454 | pre_wallet_load(); | |
455 | struct timeval tv_start; | |
456 | bool fFirstRunRet=true; | |
457 | timer_start(tv_start); | |
458 | pwalletMain = new CWallet("wallet.dat"); | |
459 | DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRunRet); | |
460 | auto res = timer_stop(tv_start); | |
461 | post_wallet_load(); | |
462 | return res; | |
463 | } | |
99dd50c3 JG |
464 | |
465 | double benchmark_listunspent() | |
466 | { | |
467 | UniValue params(UniValue::VARR); | |
468 | struct timeval tv_start; | |
469 | timer_start(tv_start); | |
470 | auto unspent = listunspent(params, false); | |
471 | return timer_stop(tv_start); | |
472 | } |