From f5edc37f3fb00055842c331d514dfd2c73930ecc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 8 Jun 2016 11:28:12 +1200 Subject: [PATCH] Add performance tests for creating and validating a MAX_BLOCK_SIZE transaction --- qa/zcash/performance-measurements.sh | 66 +++++++++++++- src/wallet/rpcwallet.cpp | 4 + src/zcbenchmarks.cpp | 124 +++++++++++++++++++++++++++ src/zcbenchmarks.h | 1 + 4 files changed, 193 insertions(+), 2 deletions(-) diff --git a/qa/zcash/performance-measurements.sh b/qa/zcash/performance-measurements.sh index cd8602a54..25b79b4bc 100755 --- a/qa/zcash/performance-measurements.sh +++ b/qa/zcash/performance-measurements.sh @@ -8,6 +8,10 @@ function zcash_rpc { ./src/zcash-cli -rpcwait -rpcuser=user -rpcpassword=password -rpcport=5983 "$@" } +function zcashd_generate { + zcash_rpc generate 101 > /dev/null +} + function zcashd_start { rm -rf "$DATADIR" mkdir -p "$DATADIR" @@ -28,6 +32,19 @@ function zcashd_massif_start { ZCASHD_PID=$! } +function zcashd_massif_start_chain { + rm -rf "$DATADIR" + mkdir -p "$DATADIR" + rm -f massif.out + ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 & + ZCASHD_PID=$! + zcashd_generate + zcash_rpc stop > /dev/null + wait $ZCASHD_PID + valgrind --tool=massif --time-unit=ms --massif-out-file=massif.out ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 & + ZCASHD_PID=$! +} + function zcashd_massif_stop { zcash_rpc stop > /dev/null wait $ZCASHD_PID @@ -42,6 +59,19 @@ function zcashd_valgrind_start { ZCASHD_PID=$! } +function zcashd_valgrind_start_chain { + rm -rf "$DATADIR" + mkdir -p "$DATADIR" + rm -f valgrind.out + ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 & + ZCASHD_PID=$! + zcashd_generate + zcash_rpc stop > /dev/null + wait $ZCASHD_PID + valgrind --leak-check=yes -v --error-limit=no --log-file="valgrind.out" ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 & + ZCASHD_PID=$! +} + function zcashd_valgrind_stop { zcash_rpc stop > /dev/null wait $ZCASHD_PID @@ -72,6 +102,14 @@ case "$1" in verifyequihash) zcash_rpc zcbenchmark verifyequihash 1000 ;; + createlargetx) + zcashd_generate + zcash_rpc zcbenchmark createlargetx 10 + ;; + validatelargetx) + zcashd_generate + zcash_rpc zcbenchmark validatelargetx 10 + ;; *) zcashd_stop echo "Bad arguments." @@ -80,7 +118,13 @@ case "$1" in zcashd_stop ;; memory) - zcashd_massif_start + case "$2" in + createlargetx|validatelargetx) + zcashd_massif_start_chain + ;; + *) + zcashd_massif_start + esac case "$2" in sleep) zcash_rpc zcbenchmark sleep 1 @@ -100,6 +144,12 @@ case "$1" in verifyequihash) zcash_rpc zcbenchmark verifyequihash 1 ;; + createlargetx) + zcash_rpc zcbenchmark validatelargetx 1 + ;; + validatelargetx) + zcash_rpc zcbenchmark validatelargetx 1 + ;; *) zcashd_massif_stop echo "Bad arguments." @@ -109,7 +159,13 @@ case "$1" in rm -f massif.out ;; valgrind) - zcashd_valgrind_start + case "$2" in + createlargetx|validatelargetx) + zcashd_valgrind_start_chain + ;; + *) + zcashd_valgrind_start + esac case "$2" in sleep) zcash_rpc zcbenchmark sleep 1 @@ -129,6 +185,12 @@ case "$1" in verifyequihash) zcash_rpc zcbenchmark verifyequihash 1 ;; + createlargetx) + zcash_rpc zcbenchmark validatelargetx 1 + ;; + validatelargetx) + zcash_rpc zcbenchmark validatelargetx 1 + ;; *) zcashd_valgrind_stop echo "Bad arguments." diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae3354c2c..14b217caa 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2419,6 +2419,10 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp) sample_times.push_back(benchmark_solve_equihash()); } else if (benchmarktype == "verifyequihash") { sample_times.push_back(benchmark_verify_equihash()); + } else if (benchmarktype == "createlargetx") { + sample_times.push_back(benchmark_large_tx(false)); + } else if (benchmarktype == "validatelargetx") { + sample_times.push_back(benchmark_large_tx(true)); } else { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 095f80cfc..6f68adac6 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -4,11 +4,17 @@ #include "util.h" #include "init.h" #include "primitives/transaction.h" +#include "base58.h" #include "crypto/equihash.h" #include "chainparams.h" +#include "consensus/validation.h" +#include "main.h" +#include "miner.h" #include "pow.h" +#include "script/sign.h" #include "sodium.h" #include "streams.h" +#include "wallet/wallet.h" #include "zcbenchmarks.h" @@ -126,3 +132,121 @@ double benchmark_verify_equihash() return timer_stop(); } +double benchmark_large_tx(bool testValidate) +{ + // Note that the transaction size is checked against the splitting + // transaction, not the merging one. Thus we are assuming that transactions + // with 1 input and N outputs are about the same size as transactions with + // N inputs and 1 output. + mapArgs["-blockmaxsize"] = itostr(MAX_BLOCK_SIZE); + int nBlockSizeRemaining = MAX_BLOCK_SIZE-1000; + + std::vector vCoins; + pwalletMain->AvailableCoins(vCoins, true); + + // 1) Create a transaction splitting the first coinbase to many outputs + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.hash = vCoins[0].tx->GetHash(); + mtx.vin[0].prevout.n = 0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = vCoins[0].tx->vout[0].scriptPubKey; + mtx.vout[0].nValue = 1000; + SignSignature(*pwalletMain, *vCoins[0].tx, mtx, 0); + + // 1a) While the transaction is smaller than the maximum: + CTransaction tx {mtx}; + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + while (nTxSize < nBlockSizeRemaining) { + // 1b) Add another output + size_t oldSize = mtx.vout.size(); + mtx.vout.resize(oldSize+1); + mtx.vout[oldSize].scriptPubKey = vCoins[0].tx->vout[0].scriptPubKey; + mtx.vout[oldSize].nValue = 1000; + + tx = mtx; + nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + } + + // 1c) Sign the splitting transaction + SignSignature(*pwalletMain, *vCoins[0].tx, mtx, 0); + uint256 hash = mtx.GetHash(); + mempool.addUnchecked(hash, CTxMemPoolEntry(mtx, 11, GetTime(), 111.0, 11)); + + // 2) Mine the splitting transaction into a block + CScript scriptDummy = CScript() << OP_TRUE; + CBlockTemplate* pblocktemplate = CreateNewBlock(scriptDummy); + CBlock *pblock = &pblocktemplate->block; + CBlockIndex* pindexPrev = chainActive.Tip(); + unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + unsigned int n = Params().EquihashN(); + unsigned int k = Params().EquihashK(); + crypto_generichash_blake2b_state eh_state; + EhInitialiseState(n, k, eh_state); + CEquihashInput I{*pblock}; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << I; + crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size()); + while (true) { + crypto_generichash_blake2b_state curr_state; + curr_state = eh_state; + crypto_generichash_blake2b_update(&curr_state, + pblock->nNonce.begin(), + pblock->nNonce.size()); + std::set> solns; + EhOptimisedSolve(n, k, curr_state, solns); + for (auto soln : solns) { + pblock->nSolution = soln; + if (UintToArith256(pblock->GetHash()) > hashTarget) { + continue; + } + goto processblock; + } + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + } +processblock: + CValidationState state; + assert(ProcessNewBlock(state, NULL, pblock, true, NULL)); + + timer_start(); + + // 3) Create a transaction that merges all of the inputs + CMutableTransaction mtx2; + mtx2.vin.resize(mtx.vout.size()); + mtx2.vout.resize(1); + mtx2.vout[0].scriptPubKey = vCoins[0].tx->vout[0].scriptPubKey; + mtx2.vout[0].nValue = 0; + + for (int i = 0; i < mtx2.vin.size(); i++) { + mtx2.vin[i].prevout.hash = hash; + mtx2.vin[i].prevout.n = i; + } + + for (int i = 0; i < mtx2.vin.size(); i++) { + SignSignature(*pwalletMain, mtx, mtx2, i); + } + + double ret = timer_stop(); + if (!testValidate) + return ret; + + hash = mtx2.GetHash(); + mempool.addUnchecked(hash, CTxMemPoolEntry(mtx2, 11, GetTime(), 111.0, 11)); + + // 4) Call CreateNewBlock (which itself calls TestBlockValidity) + delete pblocktemplate; + pblocktemplate = CreateNewBlock(scriptDummy); + pblock = &pblocktemplate->block; + + // 5) Call TestBlockValidity again under timing + timer_start(); + assert(TestBlockValidity(state, *pblock, pindexPrev, false, false)); + ret = timer_stop(); + delete pblocktemplate; + mempool.clear(); + return ret; +} + diff --git a/src/zcbenchmarks.h b/src/zcbenchmarks.h index 5ea0faa68..4540c1f12 100644 --- a/src/zcbenchmarks.h +++ b/src/zcbenchmarks.h @@ -10,5 +10,6 @@ extern double benchmark_create_joinsplit(); extern double benchmark_solve_equihash(); extern double benchmark_verify_joinsplit(const CPourTx &joinsplit); extern double benchmark_verify_equihash(); +extern double benchmark_large_tx(bool testValidate); #endif