diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -2,4 +2,5 @@ - +This release includes the following features and fixes: + - Fixes and issue with `getblocktemplate` returning fees and sigops in non-CTOR ordering after Nov 15th CTOR activation. \ No newline at end of file diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -24,10 +24,19 @@ static const bool DEFAULT_PRINTPRIORITY = false; +struct CBlockTemplateEntry { + CTransactionRef tx; + Amount fees; + int64_t sigOpCount; + + CBlockTemplateEntry(CTransactionRef tx, Amount fees, int64_t sigOpCount) + : tx(tx), fees(fees), sigOpCount(sigOpCount){}; +}; + struct CBlockTemplate { CBlock block; - std::vector vTxFees; - std::vector vTxSigOpsCount; + + std::vector entries; }; // Container for tracking updates to ancestor feerate as we include (parent) diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -32,9 +32,6 @@ #include #include -#include -#include - ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner @@ -141,12 +138,8 @@ // Pointer for convenience. pblock = &pblocktemplate->block; - // Add dummy coinbase tx as first transaction. - pblock->vtx.emplace_back(); - // updated at end - pblocktemplate->vTxFees.push_back(-SATOSHI); - // updated at end - pblocktemplate->vTxSigOpsCount.push_back(-1); + // Add dummy coinbase tx as first transaction. It is updated at the end. + pblocktemplate->entries.emplace_back(CTransactionRef(), -SATOSHI, -1); LOCK2(cs_main, mempool.cs); CBlockIndex *pindexPrev = chainActive.Tip(); @@ -178,11 +171,11 @@ if (IsMagneticAnomalyEnabled(*config, pindexPrev)) { // If magnetic anomaly is enabled, we make sure transaction are // canonically ordered. - std::sort(std::begin(pblock->vtx) + 1, std::end(pblock->vtx), - [](const std::shared_ptr &a, - const std::shared_ptr &b) -> bool { - return a->GetId() < b->GetId(); - }); + // FIXME: Use a zipped list. See T479 + std::sort(std::begin(pblocktemplate->entries) + 1, + std::end(pblocktemplate->entries), + [](const CBlockTemplateEntry &a, const CBlockTemplateEntry &b) + -> bool { return a.tx->GetId() < b.tx->GetId(); }); } int64_t nTime1 = GetTimeMicros(); @@ -208,8 +201,8 @@ << std::vector(MIN_TX_SIZE - coinbaseSize - 1); } - pblock->vtx[0] = MakeTransactionRef(coinbaseTx); - pblocktemplate->vTxFees[0] = -1 * nFees; + pblocktemplate->entries[0].tx = MakeTransactionRef(coinbaseTx); + pblocktemplate->entries[0].fees = -1 * nFees; uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION); @@ -222,8 +215,15 @@ UpdateTime(pblock, *config, pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, *config); pblock->nNonce = 0; - pblocktemplate->vTxSigOpsCount[0] = GetSigOpCountWithoutP2SH( - *pblock->vtx[0], STANDARD_CHECKDATASIG_VERIFY_FLAGS); + pblocktemplate->entries[0].sigOpCount = GetSigOpCountWithoutP2SH( + *pblocktemplate->entries[0].tx, STANDARD_CHECKDATASIG_VERIFY_FLAGS); + + // Copy all the transactions into the block + // FIXME: This should be removed as it is significant overhead. + // See T479 + for (const CBlockTemplateEntry &tx : pblocktemplate->entries) { + pblock->vtx.push_back(tx.tx); + } CValidationState state; BlockValidationOptions validationOptions(false, false); @@ -350,9 +350,8 @@ } void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { - pblock->vtx.emplace_back(iter->GetSharedTx()); - pblocktemplate->vTxFees.push_back(iter->GetFee()); - pblocktemplate->vTxSigOpsCount.push_back(iter->GetSigOpCount()); + pblocktemplate->entries.emplace_back(iter->GetSharedTx(), iter->GetFee(), + iter->GetSigOpCount()); nBlockSize += iter->GetTxSize(); ++nBlockTx; nBlockSigOps += iter->GetSigOpCount(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -326,7 +326,7 @@ // Message from normal bitcoincash:URI // (bitcoincash:123...?message=example) newTx->vOrderForm.push_back( - make_pair("Message", rcp.message.toStdString())); + std::make_pair("Message", rcp.message.toStdString())); } } diff --git a/src/rest.cpp b/src/rest.cpp --- a/src/rest.cpp +++ b/src/rest.cpp @@ -628,12 +628,10 @@ // pack in some essentials // use more or less the same output as mentioned in Bip64 - objGetUTXOResponse.push_back( - Pair("chainHeight", chainActive.Height())); + objGetUTXOResponse.pushKV("chainHeight", chainActive.Height()); objGetUTXOResponse.push_back(Pair( "chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); - objGetUTXOResponse.push_back( - Pair("bitmap", bitmapStringRepresentation)); + objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); UniValue utxos(UniValue::VARR); for (const CCoin &coin : outs) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -78,12 +78,10 @@ result.pushKV("confirmations", confirmations); result.pushKV("height", blockindex->nHeight); result.pushKV("version", blockindex->nVersion); - result.push_back( - Pair("versionHex", strprintf("%08x", blockindex->nVersion))); + result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion)); result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); result.pushKV("time", int64_t(blockindex->nTime)); - result.push_back( - Pair("mediantime", int64_t(blockindex->GetMedianTimePast()))); + result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(blockindex->nNonce)); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); @@ -128,8 +126,7 @@ } result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); - result.push_back( - Pair("mediantime", int64_t(blockindex->GetMedianTimePast()))); + result.pushKV("mediantime", int64_t(blockindex->GetMedianTimePast())); result.pushKV("nonce", uint64_t(block.nNonce)); result.pushKV("bits", strprintf("%08x", block.nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); @@ -407,12 +404,10 @@ info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); - info.push_back( - Pair("currentpriority", e.GetPriority(chainActive.Height()))); + info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); - info.push_back( - Pair("descendantfees", e.GetModFeesWithDescendants() / SATOSHI)); + info.pushKV("descendantfees", e.GetModFeesWithDescendants() / SATOSHI); info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors()); info.pushKV("ancestorfees", e.GetModFeesWithAncestors() / SATOSHI); @@ -1040,8 +1035,7 @@ ret.pushKV("bogosize", int64_t(stats.nBogoSize)); ret.pushKV("hash_serialized", stats.hashSerialized.GetHex()); ret.pushKV("disk_size", stats.nDiskSize); - ret.push_back( - Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } @@ -1201,8 +1195,7 @@ UniValue rv(UniValue::VOBJ); rv.pushKV("id", name); rv.pushKV("version", version); - rv.push_back( - Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams))); + rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); return rv; } @@ -1257,13 +1250,10 @@ UniValue obj(UniValue::VOBJ); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); obj.pushKV("blocks", int(chainActive.Height())); - obj.push_back( - Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); - obj.push_back( - Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); + obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); + obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()); obj.pushKV("difficulty", double(GetDifficulty(chainActive.Tip()))); - obj.push_back( - Pair("mediantime", int64_t(chainActive.Tip()->GetMedianTimePast()))); + obj.pushKV("mediantime", int64_t(chainActive.Tip()->GetMedianTimePast())); obj.push_back( Pair("verificationprogress", GuessVerificationProgress(config.GetChainParams().TxData(), diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -632,17 +632,17 @@ entry.pushKV("depends", deps); int index_in_template = i - 1; - entry.push_back( - Pair("fee", pblocktemplate->vTxFees[index_in_template] / SATOSHI)); - int64_t nTxSigOps = pblocktemplate->vTxSigOpsCount[index_in_template]; + entry.pushKV("fee", + pblocktemplate->entries[index_in_template].fees / SATOSHI); + int64_t nTxSigOps = + pblocktemplate->entries[index_in_template].sigOpCount; entry.pushKV("sigops", nTxSigOps); transactions.push_back(entry); } UniValue aux(UniValue::VOBJ); - aux.push_back( - Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); @@ -665,13 +665,11 @@ chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.pushKV("target", hashTarget.GetHex()); - result.push_back( - Pair("mintime", int64_t(pindexPrev->GetMedianTimePast()) + 1)); + result.pushKV("mintime", int64_t(pindexPrev->GetMedianTimePast()) + 1); result.pushKV("mutable", aMutable); result.pushKV("noncerange", "00000000ffffffff"); // FIXME: Allow for mining block greater than 1M. - result.push_back( - Pair("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE))); + result.pushKV("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE)); result.pushKV("sizelimit", DEFAULT_MAX_BLOCK_SIZE); result.pushKV("curtime", pblock->GetBlockTime()); result.pushKV("bits", strprintf("%08x", pblock->nBits)); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -167,8 +167,7 @@ int nRequired; ExtractDestinations(subscript, whichType, addresses, nRequired); obj.pushKV("script", GetTxnOutputType(whichType)); - obj.push_back( - Pair("hex", HexStr(subscript.begin(), subscript.end()))); + obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); UniValue a(UniValue::VARR); for (const CTxDestination &addr : addresses) { a.push_back(EncodeDestination(addr)); @@ -252,8 +251,7 @@ #ifdef ENABLE_WALLET isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO; ret.pushKV("ismine", (mine & ISMINE_SPENDABLE) ? true : false); - ret.push_back( - Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true : false)); + ret.pushKV("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true : false); UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest); ret.pushKVs(detail); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -409,8 +409,7 @@ if (info.fConnected) { UniValue address(UniValue::VOBJ); address.pushKV("address", info.resolvedAddress.ToString()); - address.push_back( - Pair("connected", info.fInbound ? "inbound" : "outbound")); + address.pushKV("connected", info.fInbound ? "inbound" : "outbound"); addresses.push_back(address); } obj.pushKV("addresses", addresses); @@ -467,17 +466,16 @@ obj.pushKV("timemillis", GetTimeMillis()); UniValue outboundLimit(UniValue::VOBJ); - outboundLimit.push_back( - Pair("timeframe", g_connman->GetMaxOutboundTimeframe())); + outboundLimit.pushKV("timeframe", g_connman->GetMaxOutboundTimeframe()); outboundLimit.pushKV("target", g_connman->GetMaxOutboundTarget()); - outboundLimit.push_back( - Pair("target_reached", g_connman->OutboundTargetReached(false))); + outboundLimit.pushKV("target_reached", + g_connman->OutboundTargetReached(false)); outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true))); - outboundLimit.push_back( - Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft())); - outboundLimit.push_back( - Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle())); + outboundLimit.pushKV("bytes_left_in_cycle", + g_connman->GetOutboundTargetBytesLeft()); + outboundLimit.pushKV("time_left_in_cycle", + g_connman->GetMaxOutboundTimeLeftInCycle()); obj.pushKV("uploadtarget", outboundLimit); return obj; } @@ -498,8 +496,7 @@ obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string())); - obj.push_back( - Pair("proxy_randomize_credentials", proxy.randomize_credentials)); + obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); } return networks; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -44,8 +44,7 @@ out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); if (fIncludeHex) { - out.push_back( - Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())); } if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -115,8 +115,7 @@ if (confirms > 0) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); - entry.push_back( - Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } @@ -1694,8 +1693,8 @@ entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); if (pwallet->mapAddressBook.count(s.destination)) { - entry.push_back( - Pair("label", pwallet->mapAddressBook[s.destination].name)); + entry.pushKV("label", + pwallet->mapAddressBook[s.destination].name); } entry.pushKV("vout", s.vout); entry.pushKV("fee", ValueFromAmount(-1 * nFee)); @@ -2046,8 +2045,8 @@ UniValue ret(UniValue::VOBJ); for (const std::pair &accountBalance : mapAccountBalances) { - ret.push_back( - Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + ret.pushKV(accountBalance.first, + ValueFromAmount(accountBalance.second)); } return ret; } @@ -3280,8 +3279,7 @@ entry.pushKV("address", EncodeDestination(address)); if (pwallet->mapAddressBook.count(address)) { - entry.push_back( - Pair("account", pwallet->mapAddressBook[address].name)); + entry.pushKV("account", pwallet->mapAddressBook[address].name); } if (scriptPubKey.IsPayToScriptHash()) { @@ -3297,8 +3295,7 @@ entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back( - Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)); entry.pushKV("confirmations", out.nDepth); entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); diff --git a/test/functional/abc-magnetic-anomaly-mining.py b/test/functional/abc-magnetic-anomaly-mining.py new file mode 100755 --- /dev/null +++ b/test/functional/abc-magnetic-anomaly-mining.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +""" +Test that mining RPC continues to supply correct transaction metadata after +the Nov 2018 protocol upgrade which engages canonical transaction ordering +""" + +import time +import random +import decimal + +from test_framework.blocktools import create_coinbase +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + + +class CTORMiningTest(BitcoinTestFramework): + def set_test_params(self): + # Setup two nodes so we can getblocktemplate + # it errors out if it is not connected to other nodes + self.num_nodes = 2 + self.setup_clean_chain = True + self.block_heights = {} + self.tip = None + self.blocks = {} + self.mocktime = int(time.time()) - 600 * 100 + + extra_arg = ['-spendzeroconfchange=0', '-whitelist=127.0.0.1', + "-magneticanomalyactivationtime=%d" % self.mocktime, + "-replayprotectionactivationtime=%d" % (10 * self.mocktime)] + self.extra_args = [extra_arg, extra_arg] + + def run_test(self): + mining_node = self.nodes[0] + + # Helper for updating the times + def update_time(): + mining_node.setmocktime(self.mocktime) + self.mocktime = self.mocktime + 600 + + mining_node.getnewaddress() + + # Generate some unspent utxos and also + # activate magnetic anomaly + for x in range(150): + update_time() + mining_node.generate(1) + + update_time() + unspent = mining_node.listunspent() + + transactions = {} + # Spend all our coinbases + while len(unspent): + inputs = [] + # Grab a random number of inputs + for _ in range(random.randrange(1, 5)): + txin = unspent.pop() + inputs.append({ + 'txid': txin['txid'], + 'vout': 0 # This is a coinbase + }) + if len(unspent) == 0: + break + + outputs = {} + # Calculate a unique fee for this transaction + fee = decimal.Decimal(random.randint( + 1000, 2000)) / decimal.Decimal(1e8) + # Spend to the same number of outputs as inputs, so we can leave + # the amounts unchanged and avoid rounding errors. + # + # NOTE: There will be 1 sigop per output (which equals the number + # of inputs now). We need this randomization to ensure the + # numbers are properly following the transactions in the block + # template metadata + addr = "" + for _ in range(len(inputs)): + addr = mining_node.getnewaddress() + output = { + # 50 BCH per coinbase + addr: decimal.Decimal(50) + } + outputs.update(output) + + # Take the fee off the last output to avoid rounding errors we + # need the exact fee later for assertions + outputs[addr] -= fee + + rawtx = mining_node.createrawtransaction(inputs, outputs) + signedtx = mining_node.signrawtransaction(rawtx) + txid = mining_node.sendrawtransaction(signedtx['hex']) + # number of outputs is the same as the number of sigops in this + # case + transactions.update({txid: {'fee': fee, 'sigops': len(outputs)}}) + + tmpl = mining_node.getblocktemplate() + assert 'proposal' in tmpl['capabilities'] + assert 'coinbasetxn' not in tmpl + + # Check the template transaction metadata and ordering + last_txid = 0 + for txn in tmpl['transactions'][1:]: + txid = txn['txid'] + txnMetadata = transactions[txid] + expectedFeeSats = int(txnMetadata['fee'] * 10**8) + expectedSigOps = txnMetadata['sigops'] + + txid_decoded = int(txid, 16) + + # Assert we got the expected metadata + assert(expectedFeeSats == txn['fee']) + assert(expectedSigOps == txn['sigops']) + # Assert transaction ids are in order + assert(last_txid == 0 or last_txid < txid_decoded) + last_txid = txid_decoded + + +if __name__ == '__main__': + CTORMiningTest().main() diff --git a/test/functional/timing.json b/test/functional/timing.json --- a/test/functional/timing.json +++ b/test/functional/timing.json @@ -1,7 +1,7 @@ [ { "name": "abandonconflict.py", - "time": 13 + "time": 16 }, { "name": "abc-checkdatasig-activation.py", @@ -9,31 +9,35 @@ }, { "name": "abc-cmdline.py", - "time": 9 + "time": 8 }, { "name": "abc-high_priority_transaction.py", - "time": 12 + "time": 10 }, { "name": "abc-magnetic-anomaly-activation.py", - "time": 7 + "time": 6 + }, + { + "name": "abc-magnetic-anomaly-mining.py", + "time": 15 }, { "name": "abc-mempool-accept-txn.py", - "time": 4 + "time": 5 }, { "name": "abc-p2p-compactblocks.py", - "time": 167 + "time": 104 }, { "name": "abc-p2p-fullblocktest.py", - "time": 50 + "time": 81 }, { "name": "abc-parkedchain.py", - "time": 3 + "time": 4 }, { "name": "abc-replay-protection.py", @@ -45,47 +49,47 @@ }, { "name": "abc-transaction-ordering.py", - "time": 15 + "time": 6 }, { "name": "assumevalid.py", - "time": 13 + "time": 20 }, { "name": "bip65-cltv-p2p.py", - "time": 19 + "time": 5 }, { "name": "bip68-112-113-p2p.py", - "time": 34 + "time": 18 }, { "name": "bip68-sequence.py", + "time": 18 + }, + { + "name": "bip68-sequence.py --magnetic-anomaly-time=1", "time": 26 }, { "name": "bipdersig-p2p.py", - "time": 5 + "time": 7 }, { "name": "bitcoin_cli.py", - "time": 13 + "time": 4 }, { "name": "blockchain.py", - "time": 10 + "time": 11 }, { "name": "dbcrash.py", - "time": 1257 + "time": 756 }, { "name": "decodescript.py", - "time": 2 - }, - { - "name": "deprecated_rpc.py", - "time": 18 + "time": 3 }, { "name": "disablewallet.py", @@ -93,15 +97,15 @@ }, { "name": "disconnect_ban.py", - "time": 9 + "time": 8 }, { "name": "example_test.py", - "time": 5 + "time": 4 }, { "name": "fundrawtransaction.py", - "time": 50 + "time": 42 }, { "name": "getblocktemplate_longpoll.py", @@ -109,11 +113,11 @@ }, { "name": "getchaintips.py", - "time": 5 + "time": 4 }, { "name": "httpbasics.py", - "time": 4 + "time": 3 }, { "name": "import-rescan.py", @@ -121,163 +125,163 @@ }, { "name": "importmulti.py", - "time": 24 + "time": 12 }, { "name": "importprunedfunds.py", - "time": 8 + "time": 4 }, { "name": "invalidateblock.py", - "time": 10 + "time": 9 }, { "name": "invalidblockrequest.py", - "time": 14 + "time": 4 }, { "name": "invalidtxrequest.py", - "time": 5 + "time": 4 }, { "name": "keypool-topup.py", - "time": 45 + "time": 19 }, { "name": "keypool.py", - "time": 9 + "time": 8 }, { "name": "listsinceblock.py", - "time": 4 + "time": 5 }, { "name": "listtransactions.py", - "time": 10 + "time": 11 }, { "name": "maxuploadtarget.py", - "time": 49 + "time": 37 }, { "name": "mempool_limit.py", - "time": 7 + "time": 5 }, { "name": "mempool_packages.py", - "time": 34 + "time": 28 }, { "name": "mempool_persist.py", - "time": 21 + "time": 16 }, { "name": "mempool_reorg.py", - "time": 28 + "time": 4 }, { "name": "mempool_resurrect_test.py", - "time": 18 + "time": 3 }, { "name": "mempool_spendcoinbase.py", - "time": 17 + "time": 3 }, { "name": "merkle_blocks.py", - "time": 4 + "time": 5 }, { "name": "minchainwork.py", - "time": 16 + "time": 6 }, { "name": "mining.py", - "time": 18 + "time": 3 }, { "name": "multi_rpc.py", - "time": 5 + "time": 4 }, { "name": "multiwallet.py", - "time": 8 + "time": 9 }, { "name": "net.py", - "time": 17 + "time": 4 }, { "name": "notifications.py", - "time": 7 + "time": 6 }, { "name": "nulldummy.py", - "time": 17 + "time": 4 }, { "name": "p2p-acceptblock.py", - "time": 8 + "time": 7 }, { "name": "p2p-compactblocks.py", - "time": 42 + "time": 23 }, { "name": "p2p-feefilter.py", - "time": 25 + "time": 63 }, { "name": "p2p-fullblocktest.py", - "time": 154 + "time": 184 }, { "name": "p2p-leaktests.py", - "time": 24 + "time": 7 }, { "name": "p2p-mempool.py", - "time": 16 + "time": 3 }, { "name": "p2p-timeouts.py", - "time": 65 + "time": 64 }, { "name": "preciousblock.py", - "time": 5 + "time": 4 }, { "name": "prioritise_transaction.py", - "time": 10 + "time": 6 }, { "name": "proxy_test.py", - "time": 5 + "time": 4 }, { "name": "pruning.py", - "time": 1491 + "time": 1040 }, { "name": "rawtransactions.py", - "time": 37 + "time": 17 }, { "name": "receivedby.py", - "time": 13 + "time": 6 }, { "name": "reindex.py", - "time": 26 + "time": 14 }, { "name": "resendwallettransactions.py", - "time": 5 + "time": 6 }, { "name": "rest.py", - "time": 9 + "time": 11 }, { "name": "rpcbind_test.py", @@ -289,11 +293,11 @@ }, { "name": "sendheaders.py", - "time": 30 + "time": 22 }, { "name": "signmessages.py", - "time": 2 + "time": 3 }, { "name": "signrawtransactions.py", @@ -301,27 +305,27 @@ }, { "name": "txn_clone.py", - "time": 7 + "time": 5 }, { "name": "txn_clone.py --mineblock", - "time": 8 + "time": 6 }, { "name": "txn_doublespend.py", - "time": 6 + "time": 5 }, { "name": "txn_doublespend.py --mineblock", - "time": 5 + "time": 6 }, { "name": "uptime.py", - "time": 3 + "time": 2 }, { "name": "wallet-accounts.py", - "time": 40 + "time": 13 }, { "name": "wallet-dump.py", @@ -329,19 +333,19 @@ }, { "name": "wallet-encryption.py", - "time": 20 + "time": 9 }, { "name": "wallet-hd.py", - "time": 126 + "time": 111 }, { "name": "wallet.py", - "time": 44 + "time": 56 }, { "name": "walletbackup.py", - "time": 101 + "time": 137 }, { "name": "zapwallettxes.py",