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