From 95d1f887ca66feb1d573affc761d1fa957ac88e9 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 18 Aug 2015 09:07:33 +0200 Subject: [PATCH 01/66] Fix crash when mining with empty keypool. Since the introduction of the ScriptForMining callback, the mining functions (setgenerate and generate) crash with an assertion failure (due to a NULL pointer script returned) if the keypool is empty. Fix this by giving a proper error. Zcash: Adapted to our MinerAddress type. Co-authored-by: Jack Grigg --- qa/rpc-tests/keypool.py | 17 ++++++++++++++++- src/miner.cpp | 6 +----- src/miner.h | 19 ++++++++++++++++++- src/rpc/mining.cpp | 10 ++++++++-- src/wallet/wallet.cpp | 6 +++++- 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 945f29776..ac4b92a9a 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -8,7 +8,7 @@ # Add python-bitcoinrpc to module search path: from test_framework.authproxy import JSONRPCException -from test_framework.util import check_json_precision, initialize_chain, \ +from test_framework.util import assert_equal, check_json_precision, initialize_chain, \ start_nodes, start_node, stop_nodes, wait_bitcoinds, bitcoind_processes import os @@ -72,6 +72,21 @@ def run_test(nodes, tmpdir): except JSONRPCException as e: assert(e.error['code']==-12) + # refill keypool with three new addresses + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() + + # drain them by mining + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + try: + nodes[0].generate(1) + raise AssertionError('Keypool should be exhausted after three addesses') + except JSONRPCException as e: + assert_equal(e.error['code'], -12) def main(): import optparse diff --git a/src/miner.cpp b/src/miner.cpp index 7c599ed88..b3cf0f1f0 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -116,10 +116,6 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, } } -bool IsValidMinerAddress(const MinerAddress& minerAddr) { - return minerAddr.which() != 0; -} - class AddFundingStreamValueToTx : public boost::static_visitor { private: @@ -742,7 +738,7 @@ void static BitcoinMiner(const CChainParams& chainparams) try { // Throw an error if no address valid for mining was provided. - if (!IsValidMinerAddress(minerAddress)) { + if (!boost::apply_visitor(IsValidMinerAddress(), minerAddress)) { throw std::runtime_error("No miner address available (mining requires a wallet or -mineraddress)"); } diff --git a/src/miner.h b/src/miner.h index 0fa2dbb33..f7852c69c 100644 --- a/src/miner.h +++ b/src/miner.h @@ -41,7 +41,24 @@ public: } }; -bool IsValidMinerAddress(const MinerAddress& minerAddr); +class IsValidMinerAddress : public boost::static_visitor +{ +public: + IsValidMinerAddress() {} + + bool operator()(const InvalidMinerAddress &invalid) const { + return false; + } + bool operator()(const libzcash::SaplingPaymentAddress &pa) const { + return true; + } + bool operator()(const boost::shared_ptr &coinbaseScript) const { + // Return false if no script was provided. This can happen + // due to some internal error but also if the keypool is empty. + // In the latter case, already the pointer is NULL. + return coinbaseScript.get() && !coinbaseScript->reserveScript.empty(); + } +}; struct CBlockTemplate { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 74ebcccd6..486590110 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -188,8 +188,14 @@ UniValue generate(const UniValue& params, bool fHelp) MinerAddress minerAddress; GetMainSignals().AddressForMining(minerAddress); + // If the keypool is exhausted, no script is returned at all. Catch this. + auto resv = boost::get>(&minerAddress); + if (resv && !resv->get()) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + } + // Throw an error if no address valid for mining was provided. - if (!IsValidMinerAddress(minerAddress)) { + if (!boost::apply_visitor(IsValidMinerAddress(), minerAddress)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)"); } @@ -622,7 +628,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) GetMainSignals().AddressForMining(minerAddress); // Throw an error if no address valid for mining was provided. - if (!IsValidMinerAddress(minerAddress)) { + if (!boost::apply_visitor(IsValidMinerAddress(), minerAddress)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)"); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6915f9ea3..ee17d9e69 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4418,8 +4418,12 @@ void CWallet::GetAddressForMining(MinerAddress &minerAddress) boost::shared_ptr rKey(new CReserveKey(this)); CPubKey pubkey; - if (!rKey->GetReservedKey(pubkey)) + if (!rKey->GetReservedKey(pubkey)) { + // Explicitly return nullptr to indicate that the keypool is empty. + rKey = nullptr; + minerAddress = rKey; return; + } rKey->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; minerAddress = rKey; From 4761c9f2bc1ec1cb52d7f61fceb463131ddd187a Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 5 Aug 2015 17:47:34 -0400 Subject: [PATCH 02/66] Add p2p-fullblocktest.py Zcash: Includes pyflakes changes. --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/README.md | 105 +++++++- qa/rpc-tests/bip65-cltv-p2p.py | 3 +- qa/rpc-tests/bipdersig-p2p.py | 3 +- qa/rpc-tests/invalidblockrequest.py | 12 +- qa/rpc-tests/p2p-acceptblock.py | 8 +- qa/rpc-tests/p2p-fullblocktest.py | 284 ++++++++++++++++++++++ qa/rpc-tests/script_test.py | 20 +- qa/rpc-tests/test_framework/blockstore.py | 23 +- qa/rpc-tests/test_framework/blocktools.py | 20 +- qa/rpc-tests/test_framework/comptool.py | 69 +++++- qa/rpc-tests/test_framework/key.py | 215 ++++++++++++++++ qa/rpc-tests/test_framework/script.py | 4 +- 13 files changed, 718 insertions(+), 49 deletions(-) create mode 100755 qa/rpc-tests/p2p-fullblocktest.py create mode 100644 qa/rpc-tests/test_framework/key.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index b0dc2ec88..9dadf4833 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -63,6 +63,7 @@ testScripts=( 'spentindex.py' 'timestampindex.py' 'decodescript.py' + 'p2p-fullblocktest.py' 'blockchain.py' 'disablewallet.py' 'zcjoinsplit.py' diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 3affd50fc..43421684e 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -1,5 +1,5 @@ -Regression tests of RPC interface -================================= +Regression tests +================ ### [test_framework/test_framework.py](test_framework/test_framework.py) Base class for RPC regression tests. @@ -7,6 +7,28 @@ Base class for RPC regression tests. ### [test_framework/util.py](test_framework/util.py) Generally useful functions. +### [test_framework/mininode.py](test_framework/mininode.py) +Basic code to support p2p connectivity to a bitcoind. + +### [test_framework/comptool.py](test_framework/comptool.py) +Framework for comparison-tool style, p2p tests. + +### [test_framework/script.py](test_framework/script.py) +Utilities for manipulating transaction scripts (originally from python-bitcoinlib) + +### [test_framework/blockstore.py](test_framework/blockstore.py) +Implements disk-backed block and tx storage. + +### [test_framework/key.py](test_framework/key.py) +Wrapper around OpenSSL EC_Key (originally from python-bitcoinlib) + +### [test_framework/bignum.py](test_framework/bignum.py) +Helpers for script.py + +### [test_framework/blocktools.py](test_framework/blocktools.py) +Helper functions for creating blocks and transactions. + + Notes ===== @@ -44,3 +66,82 @@ to recover with: rm -rf cache killall zcashd ``` + +P2P test design notes +--------------------- + +## Mininode + +* ```mininode.py``` contains all the definitions for objects that pass +over the network (```CBlock```, ```CTransaction```, etc, along with the network-level +wrappers for them, ```msg_block```, ```msg_tx```, etc). + +* P2P tests have two threads. One thread handles all network communication +with the bitcoind(s) being tested (using python's asyncore package); the other +implements the test logic. + +* ```NodeConn``` is the class used to connect to a bitcoind. If you implement +a callback class that derives from ```NodeConnCB``` and pass that to the +```NodeConn``` object, your code will receive the appropriate callbacks when +events of interest arrive. NOTE: be sure to call +```self.create_callback_map()``` in your derived classes' ```__init__``` +function, so that the correct mappings are set up between p2p messages and your +callback functions. + +* You can pass the same handler to multiple ```NodeConn```'s if you like, or pass +different ones to each -- whatever makes the most sense for your test. + +* Call ```NetworkThread.start()``` after all ```NodeConn``` objects are created to +start the networking thread. (Continue with the test logic in your existing +thread.) + +* RPC calls are available in p2p tests. + +* Can be used to write free-form tests, where specific p2p-protocol behavior +is tested. Examples: ```p2p-accept-block.py```, ```maxblocksinflight.py```. + +## Comptool + +* Testing framework for writing tests that compare the block/tx acceptance +behavior of a bitcoind against 1 or more other bitcoind instances, or against +known outcomes, or both. + +* Set the ```num_nodes``` variable (defined in ```ComparisonTestFramework```) to start up +1 or more nodes. If using 1 node, then ```--testbinary``` can be used as a command line +option to change the bitcoind binary used by the test. If using 2 or more nodes, +then ```--refbinary``` can be optionally used to change the bitcoind that will be used +on nodes 2 and up. + +* Implement a (generator) function called ```get_tests()``` which yields ```TestInstance```s. +Each ```TestInstance``` consists of: + - a list of ```[object, outcome, hash]``` entries + * ```object``` is a ```CBlock```, ```CTransaction```, or + ```CBlockHeader```. ```CBlock```'s and ```CTransaction```'s are tested for + acceptance. ```CBlockHeader```s can be used so that the test runner can deliver + complete headers-chains when requested from the bitcoind, to allow writing + tests where blocks can be delivered out of order but still processed by + headers-first bitcoind's. + * ```outcome``` is ```True```, ```False```, or ```None```. If ```True``` + or ```False```, the tip is compared with the expected tip -- either the + block passed in, or the hash specified as the optional 3rd entry. If + ```None``` is specified, then the test will compare all the bitcoind's + being tested to see if they all agree on what the best tip is. + * ```hash``` is the block hash of the tip to compare against. Optional to + specify; if left out then the hash of the block passed in will be used as + the expected tip. This allows for specifying an expected tip while testing + the handling of either invalid blocks or blocks delivered out of order, + which complete a longer chain. + - ```sync_every_block```: ```True/False```. If ```False```, then all blocks + are inv'ed together, and the test runner waits until the node receives the + last one, and tests only the last block for tip acceptance using the + outcome and specified tip. If ```True```, then each block is tested in + sequence and synced (this is slower when processing many blocks). + - ```sync_every_transaction```: ```True/False```. Analogous to + ```sync_every_block```, except if the outcome on the last tx is "None", + then the contents of the entire mempool are compared across all bitcoind + connections. If ```True``` or ```False```, then only the last tx's + acceptance is tested against the given outcome. + +* For examples of tests written in this framework, see + ```invalidblockrequest.py``` and ```p2p-fullblocktest.py```. + diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 613538d8f..506b9fd50 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -63,6 +63,7 @@ class BIP65Test(ComparisonTestFramework): def get_tests(self): self.coinbase_blocks = self.nodes[0].generate(1) self.nodes[0].generate(100) + height = 102 # height of the next block to build hashTip = self.nodes[0].getbestblockhash() hashFinalSaplingRoot = int("0x" + self.nodes[0].getblock(hashTip)['finalsaplingroot'] , 0) self.tip = int ("0x" + hashTip , 0) @@ -81,7 +82,7 @@ class BIP65Test(ComparisonTestFramework): self.block_time = gbt["mintime"] + 1 self.block_bits = int("0x" + gbt["bits"], 0) - block = create_block(self.tip, create_coinbase(101), + block = create_block(self.tip, create_coinbase(height), self.block_time, self.block_bits, hashFinalSaplingRoot) block.nVersion = 4 diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index 130768fff..b25872982 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -71,6 +71,7 @@ class BIP66Test(ComparisonTestFramework): def get_tests(self): self.coinbase_blocks = self.nodes[0].generate(1) self.nodes[0].generate(100) + height = 102 # height of the next block to build hashTip = self.nodes[0].getbestblockhash() hashFinalSaplingRoot = int("0x" + self.nodes[0].getblock(hashTip)['finalsaplingroot'], 0) self.tip = int ("0x"+hashTip , 0) @@ -89,7 +90,7 @@ class BIP66Test(ComparisonTestFramework): self.block_time = gbt["mintime"] + 1 self.block_bits = int("0x" + gbt["bits"], 0) - block = create_block(self.tip, create_coinbase(101), + block = create_block(self.tip, create_coinbase(height), self.block_time, self.block_bits, hashFinalSaplingRoot) block.nVersion = 4 diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index b8afa5f10..02833704a 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -46,12 +46,14 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' Create a new block with an anyone-can-spend coinbase ''' - block = create_block(self.tip, create_coinbase(), self.block_time) + height = 1 + block = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 + height += 1 yield TestInstance([[block, True]]) ''' @@ -59,11 +61,12 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' test = TestInstance(sync_every_block=False) for i in range(100): - block = create_block(self.tip, create_coinbase(), self.block_time) + block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.tip = block.sha256 self.block_time += 1 test.blocks_and_transactions.append([block, True]) + height += 1 yield test ''' @@ -73,7 +76,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework): coinbase, spend of that spend). Duplicate the 3rd transaction to leave merkle root and blockheader unchanged but invalidate the block. ''' - block2 = create_block(self.tip, create_coinbase(), self.block_time) + block2 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 # chr(81) is OP_TRUE @@ -95,11 +98,12 @@ class InvalidBlockRequestTest(ComparisonTestFramework): self.tip = block2.sha256 yield TestInstance([[block2, False], [block2_orig, True]]) + height += 1 ''' Make sure that a totally screwed up block is not valid. ''' - block3 = create_block(self.tip, create_coinbase(), self.block_time) + block3 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block3.vtx[0].vout[0].nValue = 100*100000000 # Too high! block3.vtx[0].sha256=None diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 00f3f3289..b24508eee 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -158,7 +158,7 @@ class AcceptBlockTest(BitcoinTestFramework): blocks_h2 = [] # the height 2 blocks on each node's chain block_time = time.time() + 1 for i in range(2): - blocks_h2.append(create_block(tips[i], create_coinbase(), block_time)) + blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) @@ -172,7 +172,7 @@ class AcceptBlockTest(BitcoinTestFramework): # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain for i in range(2): - blocks_h2f.append(create_block(tips[i], create_coinbase(), blocks_h2[i].nTime+1)) + blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1)) blocks_h2f[i].solve() test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) @@ -191,7 +191,7 @@ class AcceptBlockTest(BitcoinTestFramework): # 4. Now send another block that builds on the forking chain. blocks_h3 = [] for i in range(2): - blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(), blocks_h2f[i].nTime+1)) + blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1)) blocks_h3[i].solve() test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) @@ -222,7 +222,7 @@ class AcceptBlockTest(BitcoinTestFramework): all_blocks = [] # node0's blocks for j in range(2): for i in range(288): - next_block = create_block(tips[j].sha256, create_coinbase(), tips[j].nTime+1) + next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1) next_block.solve() if j==0: test_node.send_message(msg_block(next_block)) diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py new file mode 100755 index 000000000..b79a1cd73 --- /dev/null +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python2 + +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.test_framework import ComparisonTestFramework +from test_framework.blocktools import (create_block, create_coinbase) +from test_framework.comptool import TestManager, TestInstance +from test_framework.key import CECKey +from test_framework.mininode import ( + CBlockHeader, + COutPoint, + CTransaction, + CTxIn, + CTxOut, + NetworkThread, +) +from test_framework.script import CScript, SignatureHash, SIGHASH_ALL, OP_CHECKSIG, OP_TRUE +from test_framework.util import SAPLING_BRANCH_ID +import random +import time + +class PreviousSpendableOutput(object): + def __init__(self, tx = CTransaction(), n = -1): + self.tx = tx + self.n = n # the output we're spending + +''' +This reimplements tests from the bitcoinj/FullBlockTestGenerator used +by the pull-tester. + +We use the testing framework in which we expect a particular answer from +each test. +''' + +class FullBlockTest(ComparisonTestFramework): + + ''' Can either run this test as 1 node with expected answers, or two and compare them. + Change the "outcome" variable from each TestInstance object to only do the comparison. ''' + def __init__(self): + self.num_nodes = 1 + self.block_heights = {} + self.coinbase_key = CECKey() + self.coinbase_key.set_secretbytes(b"horsebattery") + self.coinbase_pubkey = self.coinbase_key.get_pubkey() + self.block_time = int(time.time())+1 + self.tip = None + self.blocks = {} + + def run_test(self): + test = TestManager(self, self.options.tmpdir) + test.add_all_connections(self.nodes) + NetworkThread().start() # Start up network handling in another thread + test.run() + + def add_transactions_to_block(self, block, tx_list): + [ tx.rehash() for tx in tx_list ] + block.vtx.extend(tx_list) + block.hashMerkleRoot = block.calc_merkle_root() + block.rehash() + return block + + # Create a block on top of self.tip, and advance self.tip to point to the new block + # if spend is specified, then 1 satoshi will be spent from that to an anyone-can-spend output, + # and rest will go to fees. + def next_block(self, number, spend=None, additional_coinbase_value=0, script=None): + if self.tip == None: + base_block_hash = self.genesis_hash + else: + base_block_hash = self.tip.sha256 + # First create the coinbase + height = self.block_heights[base_block_hash] + 1 + coinbase = create_coinbase(height, self.coinbase_pubkey) + coinbase.vout[0].nValue += additional_coinbase_value + if (spend != None): + coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees + coinbase.rehash() + block = create_block(base_block_hash, coinbase, self.block_time) + if (spend != None): + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n), "", 0xffffffff)) # no signature yet + # This copies the java comparison tool testing behavior: the first + # txout has a garbage scriptPubKey, "to make sure we're not + # pre-verifying too much" (?) + tx.vout.append(CTxOut(0, CScript([random.randint(0,255), height & 255]))) + if script == None: + tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) + else: + tx.vout.append(CTxOut(1, script)) + # Now sign it if necessary + scriptSig = "" + scriptPubKey = bytearray(spend.tx.vout[spend.n].scriptPubKey) + if (scriptPubKey[0] == OP_TRUE): # looks like an anyone-can-spend + scriptSig = CScript([OP_TRUE]) + else: + # We have to actually sign it + (sighash, err) = SignatureHash( + spend.tx.vout[spend.n].scriptPubKey, + tx, + 0, + SIGHASH_ALL, + spend.tx.vout[spend.n].nValue, + SAPLING_BRANCH_ID, + ) + scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))]) + tx.vin[0].scriptSig = scriptSig + # Now add the transaction to the block + block = self.add_transactions_to_block(block, [tx]) + block.solve() + self.tip = block + self.block_heights[block.sha256] = height + self.block_time += 1 + assert number not in self.blocks + self.blocks[number] = block + return block + + def get_tests(self): + self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) + self.block_heights[self.genesis_hash] = 0 + spendable_outputs = [] + + # save the current tip so it can be spent by a later block + def save_spendable_output(): + spendable_outputs.append(self.tip) + + # get an output that we previous marked as spendable + def get_spendable_output(): + return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) + + # returns a test case that asserts that the current tip was accepted + def accepted(): + return TestInstance([[self.tip, True]]) + + # returns a test case that asserts that the current tip was rejected + def rejected(): + return TestInstance([[self.tip, False]]) + + # move the tip back to a previous block + def tip(number): + self.tip = self.blocks[number] + + # creates a new block and advances the tip to that block + block = self.next_block + + + # Create a new block + block(0) + save_spendable_output() + yield accepted() + + + # Now we need that block to mature so we can spend the coinbase. + test = TestInstance(sync_every_block=False) + for i in range(100): + block(1000 + i) + test.blocks_and_transactions.append([self.tip, True]) + save_spendable_output() + yield test + + + # Start by bulding a couple of blocks on top (which output is spent is in parentheses): + # genesis -> b1 (0) -> b2 (1) + out0 = get_spendable_output() + block(1, spend=out0) + save_spendable_output() + yield accepted() + + out1 = get_spendable_output() + block(2, spend=out1) + # Inv again, then deliver twice (shouldn't break anything). + yield accepted() + + + # so fork like this: + # + # genesis -> b1 (0) -> b2 (1) + # \-> b3 (1) + # + # Nothing should happen at this point. We saw b2 first so it takes priority. + tip(1) + block(3, spend=out1) + # Deliver twice (should still not break anything) + yield rejected() + + + # Now we add another block to make the alternative chain longer. + # + # genesis -> b1 (0) -> b2 (1) + # \-> b3 (1) -> b4 (2) + out2 = get_spendable_output() + block(4, spend=out2) + yield accepted() + + + # ... and back to the first chain. + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b3 (1) -> b4 (2) + tip(2) + block(5, spend=out2) + save_spendable_output() + yield rejected() + + out3 = get_spendable_output() + block(6, spend=out3) + yield accepted() + + + # Try to create a fork that double-spends + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b7 (2) -> b8 (4) + # \-> b3 (1) -> b4 (2) + tip(5) + block(7, spend=out2) + yield rejected() + + out4 = get_spendable_output() + block(8, spend=out4) + yield rejected() + + + # Try to create a block that has too much fee + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b9 (4) + # \-> b3 (1) -> b4 (2) + tip(6) + block(9, spend=out4, additional_coinbase_value=1) + yield rejected() + + + # Create a fork that ends in a block with too much fee (the one that causes the reorg) + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b10 (3) -> b11 (4) + # \-> b3 (1) -> b4 (2) + tip(5) + block(10, spend=out3) + yield rejected() + + block(11, spend=out4, additional_coinbase_value=1) + yield rejected() + + + # Try again, but with a valid fork first + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b14 (5) + # (b12 added last) + # \-> b3 (1) -> b4 (2) + tip(5) + b12 = block(12, spend=out3) + save_spendable_output() + #yield TestInstance([[b12, False]]) + b13 = block(13, spend=out4) + # Deliver the block header for b12, and the block b13. + # b13 should be accepted but the tip won't advance until b12 is delivered. + yield TestInstance([[CBlockHeader(b12), None], [b13, False]]) + + save_spendable_output() + out5 = get_spendable_output() + # b14 is invalid, but the node won't know that until it tries to connect + # Tip still can't advance because b12 is missing + block(14, spend=out5, additional_coinbase_value=1) + yield rejected() + + yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. + + + # Test that a block with a lot of checksigs is okay + lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50 - 1)) + tip(13) + block(15, spend=out5, script=lots_of_checksigs) + yield accepted() + + + # Test that a block with too many checksigs is rejected + out6 = get_spendable_output() + too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50)) + block(16, spend=out6, script=too_many_checksigs) + yield rejected() + + + +if __name__ == '__main__': + FullBlockTest().main() diff --git a/qa/rpc-tests/script_test.py b/qa/rpc-tests/script_test.py index f556f10c9..f242ff08c 100755 --- a/qa/rpc-tests/script_test.py +++ b/qa/rpc-tests/script_test.py @@ -121,10 +121,10 @@ def ParseScript(json_script): return parsed_script class TestBuilder(object): - def create_credit_tx(self, scriptPubKey): + def create_credit_tx(self, scriptPubKey, height): # self.tx1 is a coinbase transaction, modeled after the one created by script_tests.cpp # This allows us to reuse signatures created in the unit test framework. - self.tx1 = create_coinbase() # this has a bip34 scriptsig, + self.tx1 = create_coinbase(height) # this has a bip34 scriptsig, self.tx1.vin[0].scriptSig = CScript([0, 0]) # but this matches the unit tests self.tx1.vout[0].nValue = 0 self.tx1.vout[0].scriptPubKey = scriptPubKey @@ -165,7 +165,7 @@ class ScriptTest(ComparisonTestFramework): test = TestInstance(sync_every_block=False) test_build = TestBuilder() - test_build.create_credit_tx(scriptpubkey) + test_build.create_credit_tx(scriptpubkey, self.height) test_build.create_spend_tx(scriptsig) test_build.rehash() @@ -173,16 +173,18 @@ class ScriptTest(ComparisonTestFramework): self.block_time += 1 block.solve() self.tip = block.sha256 + self.height += 1 test.blocks_and_transactions = [[block, True]] for i in range(100): - block = create_block(self.tip, create_coinbase(), self.block_time) + block = create_block(self.tip, create_coinbase(self.height), self.block_time) self.block_time += 1 block.solve() self.tip = block.sha256 + self.height += 1 test.blocks_and_transactions.append([block, True]) - block = create_block(self.tip, create_coinbase(), self.block_time) + block = create_block(self.tip, create_coinbase(self.height), self.block_time) self.block_time += 1 block.vtx.append(test_build.tx2) block.hashMerkleRoot = block.calc_merkle_root() @@ -195,14 +197,16 @@ class ScriptTest(ComparisonTestFramework): def get_tests(self): self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) self.block_time = 1333230000 # before the BIP16 switchover + self.height = 1 ''' Create a new block with an anyone-can-spend coinbase ''' - block = create_block(self.tip, create_coinbase(), self.block_time) + block = create_block(self.tip, create_coinbase(self.height), self.block_time) self.block_time += 1 block.solve() self.tip = block.sha256 + self.height += 1 yield TestInstance(objects=[[block, True]]) ''' @@ -210,11 +214,12 @@ class ScriptTest(ComparisonTestFramework): ''' test = TestInstance(objects=[], sync_every_block=False, sync_every_tx=False) for i in range(100): - b = create_block(self.tip, create_coinbase(), self.block_time) + b = create_block(self.tip, create_coinbase(self.height), self.block_time) b.solve() test.blocks_and_transactions.append([b, True]) self.tip = b.sha256 self.block_time += 1 + self.height += 1 yield test ''' Iterate through script tests. ''' @@ -226,6 +231,7 @@ class ScriptTest(ComparisonTestFramework): self.nodes[1].invalidateblock(self.nodes[1].getblockhash(102)) self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) + self.height = 102 [scriptsig, scriptpubkey, flags] = script_test[0:3] flags = ParseScriptFlags(flags) diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py index 7f59250bb..1288521e9 100644 --- a/qa/rpc-tests/test_framework/blockstore.py +++ b/qa/rpc-tests/test_framework/blockstore.py @@ -13,6 +13,7 @@ class BlockStore(): def __init__(self, datadir): self.blockDB = dbm.open(datadir + "/blocks", 'c') self.currentBlock = 0 + self.headers_map = dict() def close(self): self.blockDB.close() @@ -29,24 +30,30 @@ class BlockStore(): ret.calc_sha256() return ret + def get_header(self, blockhash): + try: + return self.headers_map[blockhash] + except KeyError: + return None + # Note: this pulls full blocks out of the database just to retrieve # the headers -- perhaps we could keep a separate data structure # to avoid this overhead. def headers_for(self, locator, hash_stop, current_tip=None): if current_tip is None: current_tip = self.currentBlock - current_block = self.get(current_tip) - if current_block is None: + current_block_header = self.get_header(current_tip) + if current_block_header is None: return None response = msg_headers() - headersList = [ CBlockHeader(current_block) ] + headersList = [ current_block_header ] maxheaders = 2000 while (headersList[0].sha256 not in locator.vHave): prevBlockHash = headersList[0].hashPrevBlock - prevBlock = self.get(prevBlockHash) - if prevBlock is not None: - headersList.insert(0, CBlockHeader(prevBlock)) + prevBlockHeader = self.get_header(prevBlockHash) + if prevBlockHeader is not None: + headersList.insert(0, prevBlockHeader) else: break headersList = headersList[:maxheaders] # truncate if we have too many @@ -64,6 +71,10 @@ class BlockStore(): except TypeError as e: print("Unexpected error: ", sys.exc_info()[0], e.args) self.currentBlock = block.sha256 + self.headers_map[block.sha256] = CBlockHeader(block) + + def add_header(self, header): + self.headers_map[header.sha256] = header def get_blocks(self, inv): responses = [] diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index d3f06f479..1cd48232d 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -5,7 +5,7 @@ # from .mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint -from .script import CScript, OP_0, OP_EQUAL, OP_HASH160 +from .script import CScript, OP_0, OP_EQUAL, OP_HASH160, OP_TRUE, OP_CHECKSIG # Create a block (with regtest difficulty) def create_block(hashprev, coinbase, nTime=None, nBits=None, hashFinalSaplingRoot=None): @@ -44,19 +44,21 @@ def serialize_script_num(value): r[-1] |= 0x80 return r -counter=1 -# Create an anyone-can-spend coinbase transaction, assuming no miner fees -def create_coinbase(heightAdjust = 0): - global counter +# Create a coinbase transaction, assuming no miner fees. +# If pubkey is passed in, the coinbase output will be a P2PK output; +# otherwise an anyone-can-spend output. +def create_coinbase(height, pubkey = None): coinbase = CTransaction() coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), - CScript([counter+heightAdjust, OP_0]), 0xffffffff)) - counter += 1 + CScript([height, OP_0]), 0xffffffff)) coinbaseoutput = CTxOut() coinbaseoutput.nValue = int(12.5*100000000) - halvings = int((counter+heightAdjust)/150) # regtest + halvings = int(height/150) # regtest coinbaseoutput.nValue >>= halvings - coinbaseoutput.scriptPubKey = b"" + if (pubkey != None): + coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG]) + else: + coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [ coinbaseoutput ] if halvings == 0: # regtest froutput = CTxOut() diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 70eb15d5d..42dbd8cf3 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -4,9 +4,22 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . # -from .mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \ - msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ from .blockstore import BlockStore, TxStore +from .mininode import ( + CBlock, + CBlockHeader, + CTransaction, + CInv, + msg_block, + msg_getheaders, + msg_inv, + msg_mempool, + msg_ping, + mininode_lock, + MAX_INV_SZ, + NodeConn, + NodeConnCB, +) from .util import p2p_port import time @@ -123,12 +136,19 @@ class TestNode(NodeConnCB): # Instances of these are generated by the test generator, and fed into the # comptool. # -# "blocks_and_transactions" should be an array of [obj, True/False/None]: -# - obj is either a CBlock or a CTransaction, and +# "blocks_and_transactions" should be an array of +# [obj, True/False/None, hash/None]: +# - obj is either a CBlock, CBlockHeader, or a CTransaction, and # - the second value indicates whether the object should be accepted # into the blockchain or mempool (for tests where we expect a certain # answer), or "None" if we don't expect a certain answer and are just # comparing the behavior of the nodes being tested. +# - the third value is the hash to test the tip against (if None or omitted, +# use the hash of the block) +# - NOTE: if a block header, no test is performed; instead the header is +# just added to the block_store. This is to facilitate block delivery +# when communicating with headers-first clients (when withholding an +# intermediate block). # sync_every_block: if True, then each block will be inv'ed, synced, and # nodes will be tested based on the outcome for the block. If False, # then inv's accumulate until all blocks are processed (or max inv size @@ -195,7 +215,6 @@ class TestManager(object): if not wait_until(blocks_requested, attempts=20*num_blocks): # print [ c.cb.block_request_map for c in self.connections ] raise AssertionError("Not all nodes requested block") - # --> Answer request (we did this inline!) # Send getheaders message [ c.cb.send_getheaders() for c in self.connections ] @@ -218,7 +237,6 @@ class TestManager(object): if not wait_until(transaction_requested, attempts=20*num_events): # print [ c.cb.tx_request_map for c in self.connections ] raise AssertionError("Not all nodes requested transaction") - # --> Answer request (we did this inline!) # Get the mempool [ c.cb.send_mempool() for c in self.connections ] @@ -272,29 +290,55 @@ class TestManager(object): # We use these variables to keep track of the last block # and last transaction in the tests, which are used # if we're not syncing on every block or every tx. - [ block, block_outcome ] = [ None, None ] + [ block, block_outcome, tip ] = [ None, None, None ] [ tx, tx_outcome ] = [ None, None ] invqueue = [] - for b_or_t, outcome in test_instance.blocks_and_transactions: + for test_obj in test_instance.blocks_and_transactions: + b_or_t = test_obj[0] + outcome = test_obj[1] # Determine if we're dealing with a block or tx if isinstance(b_or_t, CBlock): # Block test runner block = b_or_t block_outcome = outcome + tip = block.sha256 + # each test_obj can have an optional third argument + # to specify the tip we should compare with + # (default is to use the block being tested) + if len(test_obj) >= 3: + tip = test_obj[2] + # Add to shared block_store, set as current block + # If there was an open getdata request for the block + # previously, and we didn't have an entry in the + # block_store, then immediately deliver, because the + # node wouldn't send another getdata request while + # the earlier one is outstanding. + first_block_with_hash = True + if self.block_store.get(block.sha256) is not None: + first_block_with_hash = False with mininode_lock: self.block_store.add_block(block) for c in self.connections: - c.cb.block_request_map[block.sha256] = False + if first_block_with_hash and block.sha256 in c.cb.block_request_map and c.cb.block_request_map[block.sha256] == True: + # There was a previous request for this block hash + # Most likely, we delivered a header for this block + # but never had the block to respond to the getdata + c.send_message(msg_block(block)) + else: + c.cb.block_request_map[block.sha256] = False # Either send inv's to each node and sync, or add # to invqueue for later inv'ing. if (test_instance.sync_every_block): [ c.cb.send_inv(block) for c in self.connections ] self.sync_blocks(block.sha256, 1) - if (not self.check_results(block.sha256, outcome)): + if (not self.check_results(tip, outcome)): raise AssertionError("Test failed at test %d" % test_number) else: invqueue.append(CInv(2, block.sha256)) + elif isinstance(b_or_t, CBlockHeader): + block_header = b_or_t + self.block_store.add_header(block_header) else: # Tx test runner assert(isinstance(b_or_t, CTransaction)) tx = b_or_t @@ -322,9 +366,8 @@ class TestManager(object): if len(invqueue) > 0: [ c.send_message(msg_inv(invqueue)) for c in self.connections ] invqueue = [] - self.sync_blocks(block.sha256, - len(test_instance.blocks_and_transactions)) - if (not self.check_results(block.sha256, block_outcome)): + self.sync_blocks(block.sha256, len(test_instance.blocks_and_transactions)) + if (not self.check_results(tip, block_outcome)): raise AssertionError("Block test failed at test %d" % test_number) if (not test_instance.sync_every_tx and tx is not None): if len(invqueue) > 0: diff --git a/qa/rpc-tests/test_framework/key.py b/qa/rpc-tests/test_framework/key.py new file mode 100644 index 000000000..ba3038fe0 --- /dev/null +++ b/qa/rpc-tests/test_framework/key.py @@ -0,0 +1,215 @@ +# Copyright (c) 2011 Sam Rushing +# +# key.py - OpenSSL wrapper +# +# This file is modified from python-bitcoinlib. +# + +"""ECC secp256k1 crypto routines + +WARNING: This module does not mlock() secrets; your private keys may end up on +disk in swap! Use with caution! +""" + +import ctypes +import ctypes.util +import hashlib +import sys + +ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32') + +ssl.BN_new.restype = ctypes.c_void_p +ssl.BN_new.argtypes = [] + +ssl.BN_bin2bn.restype = ctypes.c_void_p +ssl.BN_bin2bn.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p] + +ssl.BN_CTX_free.restype = None +ssl.BN_CTX_free.argtypes = [ctypes.c_void_p] + +ssl.BN_CTX_new.restype = ctypes.c_void_p +ssl.BN_CTX_new.argtypes = [] + +ssl.ECDH_compute_key.restype = ctypes.c_int +ssl.ECDH_compute_key.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p] + +ssl.ECDSA_sign.restype = ctypes.c_int +ssl.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +ssl.ECDSA_verify.restype = ctypes.c_int +ssl.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p] + +ssl.EC_KEY_free.restype = None +ssl.EC_KEY_free.argtypes = [ctypes.c_void_p] + +ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p +ssl.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int] + +ssl.EC_KEY_get0_group.restype = ctypes.c_void_p +ssl.EC_KEY_get0_group.argtypes = [ctypes.c_void_p] + +ssl.EC_KEY_get0_public_key.restype = ctypes.c_void_p +ssl.EC_KEY_get0_public_key.argtypes = [ctypes.c_void_p] + +ssl.EC_KEY_set_private_key.restype = ctypes.c_int +ssl.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + +ssl.EC_KEY_set_conv_form.restype = None +ssl.EC_KEY_set_conv_form.argtypes = [ctypes.c_void_p, ctypes.c_int] + +ssl.EC_KEY_set_public_key.restype = ctypes.c_int +ssl.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + +ssl.i2o_ECPublicKey.restype = ctypes.c_void_p +ssl.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + +ssl.EC_POINT_new.restype = ctypes.c_void_p +ssl.EC_POINT_new.argtypes = [ctypes.c_void_p] + +ssl.EC_POINT_free.restype = None +ssl.EC_POINT_free.argtypes = [ctypes.c_void_p] + +ssl.EC_POINT_mul.restype = ctypes.c_int +ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +# this specifies the curve used with ECDSA. +NID_secp256k1 = 714 # from openssl/obj_mac.h + +# Thx to Sam Devlin for the ctypes magic 64-bit fix. +def _check_result(val, func, args): + if val == 0: + raise ValueError + else: + return ctypes.c_void_p (val) + +ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p +ssl.EC_KEY_new_by_curve_name.errcheck = _check_result + +class CECKey(object): + """Wrapper around OpenSSL's EC_KEY""" + + POINT_CONVERSION_COMPRESSED = 2 + POINT_CONVERSION_UNCOMPRESSED = 4 + + def __init__(self): + self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1) + + def __del__(self): + if ssl: + ssl.EC_KEY_free(self.k) + self.k = None + + def set_secretbytes(self, secret): + priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new()) + group = ssl.EC_KEY_get0_group(self.k) + pub_key = ssl.EC_POINT_new(group) + ctx = ssl.BN_CTX_new() + if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx): + raise ValueError("Could not derive public key from the supplied secret.") + ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) + ssl.EC_KEY_set_private_key(self.k, priv_key) + ssl.EC_KEY_set_public_key(self.k, pub_key) + ssl.EC_POINT_free(pub_key) + ssl.BN_CTX_free(ctx) + return self.k + + def set_privkey(self, key): + self.mb = ctypes.create_string_buffer(key) + return ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) + + def set_pubkey(self, key): + self.mb = ctypes.create_string_buffer(key) + return ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) + + def get_privkey(self): + size = ssl.i2d_ECPrivateKey(self.k, 0) + mb_pri = ctypes.create_string_buffer(size) + ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri))) + return mb_pri.raw + + def get_pubkey(self): + size = ssl.i2o_ECPublicKey(self.k, 0) + mb = ctypes.create_string_buffer(size) + ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb))) + return mb.raw + + def get_raw_ecdh_key(self, other_pubkey): + ecdh_keybuffer = ctypes.create_string_buffer(32) + r = ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32, + ssl.EC_KEY_get0_public_key(other_pubkey.k), + self.k, 0) + if r != 32: + raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed') + return ecdh_keybuffer.raw + + def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()): + # FIXME: be warned it's not clear what the kdf should be as a default + r = self.get_raw_ecdh_key(other_pubkey) + return kdf(r) + + def sign(self, hash): + # FIXME: need unit tests for below cases + if not isinstance(hash, bytes): + raise TypeError('Hash must be bytes instance; got %r' % hash.__class__) + if len(hash) != 32: + raise ValueError('Hash must be exactly 32 bytes long') + + sig_size0 = ctypes.c_uint32() + sig_size0.value = ssl.ECDSA_size(self.k) + mb_sig = ctypes.create_string_buffer(sig_size0.value) + result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k) + assert 1 == result + return mb_sig.raw[:sig_size0.value] + + def verify(self, hash, sig): + """Verify a DER signature""" + return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1 + + def set_compressed(self, compressed): + if compressed: + form = self.POINT_CONVERSION_COMPRESSED + else: + form = self.POINT_CONVERSION_UNCOMPRESSED + ssl.EC_KEY_set_conv_form(self.k, form) + + +class CPubKey(bytes): + """An encapsulated public key + + Attributes: + + is_valid - Corresponds to CPubKey.IsValid() + is_fullyvalid - Corresponds to CPubKey.IsFullyValid() + is_compressed - Corresponds to CPubKey.IsCompressed() + """ + + def __new__(cls, buf, _cec_key=None): + self = super(CPubKey, cls).__new__(cls, buf) + if _cec_key is None: + _cec_key = CECKey() + self._cec_key = _cec_key + self.is_fullyvalid = _cec_key.set_pubkey(self) != 0 + return self + + @property + def is_valid(self): + return len(self) > 0 + + @property + def is_compressed(self): + return len(self) == 33 + + def verify(self, hash, sig): + return self._cec_key.verify(hash, sig) + + def __str__(self): + return repr(self) + + def __repr__(self): + # Always have represent as b'' so test cases don't have to + # change for py2/3 + if sys.version > '3': + return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + else: + return '%s(b%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index 9397dbb8f..767d49fad 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -22,7 +22,7 @@ if sys.version > '3': from pyblake2 import blake2b import struct -from test_framework import bignum +from test_framework.bignum import bn2vch from test_framework.mininode import (CTransaction, CTxOut, hash256, ser_string, ser_uint256) MAX_SCRIPT_SIZE = 10000 @@ -659,7 +659,7 @@ class CScript(bytes): elif other == -1: other = bytes([OP_1NEGATE]) else: - other = CScriptOp.encode_op_pushdata(bignum.bn2vch(other)) + other = CScriptOp.encode_op_pushdata(bn2vch(other)) elif isinstance(other, (bytes, bytearray)): other = bytes(CScriptOp.encode_op_pushdata(other)) return other From 772d7807306b6a79f908011c88885da0d30c21b0 Mon Sep 17 00:00:00 2001 From: ptschip Date: Wed, 26 Aug 2015 03:05:36 -0700 Subject: [PATCH 03/66] Migrated rpc-tests.sh to all python rpc-tests.py 1) created rpc-tests.py 2) deleted rpc-tests.sh 3) travis.yml points to rpc-tests.py 4) Modified Makefile.am 5) Updated README.md 6) Added tests_config.py and deleted tests-config.sh 7) Modified configure.ac with script to set correct path in tests_config.py Zcash: Migrated our test list over, and other necessary modifications. The UI changes were not migrated. --- Makefile.am | 2 +- configure.ac | 12 +- qa/pull-tester/rpc-tests.py | 176 +++++++++++++++++ qa/pull-tester/rpc-tests.sh | 180 ------------------ ...{tests-config.sh.in => tests_config.py.in} | 4 +- qa/rpc-tests/README.md | 10 +- 6 files changed, 196 insertions(+), 188 deletions(-) create mode 100644 qa/pull-tester/rpc-tests.py delete mode 100755 qa/pull-tester/rpc-tests.sh rename qa/pull-tester/{tests-config.sh.in => tests_config.py.in} (80%) mode change 100755 => 100644 diff --git a/Makefile.am b/Makefile.am index 82c06c641..5784fef5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -147,7 +147,7 @@ dist_noinst_SCRIPTS = autogen.sh zcutil/build-debian-package.sh zcutil/build.sh RUST_DIST = $(top_srcdir)/.cargo $(top_srcdir)/Cargo.toml $(top_srcdir)/Cargo.lock rust-toolchain -EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS) $(RUST_DIST) +EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/pull-tester/tests_config.py qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS) $(RUST_DIST) install-exec-hook: mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/zcash-fetch-params diff --git a/configure.ac b/configure.ac index 78d8f65b6..8b60e58db 100644 --- a/configure.ac +++ b/configure.ac @@ -969,7 +969,8 @@ AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) -AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) +AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) +chmod +x qa/pull-tester/rpc-tests.py dnl boost's m4 checks do something really nasty: they export these vars. As a dnl result, they leak into secp256k1's configure and crazy things happen. @@ -1010,6 +1011,15 @@ case $host in ;; esac +dnl Replace the BUILDDIR path with the correct Windows path if compiling on Native Windows +case ${OS} in + *Windows*) + sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' qa/pull-tester/tests_config.py > qa/pull-tester/tests_config-2.py + mv qa/pull-tester/tests_config-2.py qa/pull-tester/tests_config.py + chmod +x qa/pull-tester/tests_config.py + ;; +esac + echo echo "Options used to compile and link:" echo " with wallet = $enable_wallet" diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py new file mode 100644 index 000000000..a8a197219 --- /dev/null +++ b/qa/pull-tester/rpc-tests.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python2 + +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Run Regression Test Suite +# + +import os +import sys +import subprocess +import re +from tests_config import * +from sets import Set + +#If imported values are not defined then set to zero (or disabled) +if not vars().has_key('ENABLE_WALLET'): + ENABLE_WALLET=0 +if not vars().has_key('ENABLE_BITCOIND'): + ENABLE_BITCOIND=0 +if not vars().has_key('ENABLE_UTILS'): + ENABLE_UTILS=0 +if not vars().has_key('ENABLE_ZMQ'): + ENABLE_ZMQ=0 + +#Create a set to store arguments and create the passOn string +opts = Set() +passOn = "" +p = re.compile("^--") +for i in range(1,len(sys.argv)): + if (p.match(sys.argv[i]) or sys.argv[i] == "-h"): + passOn += " " + sys.argv[i] + else: + opts.add(sys.argv[i]) + +#Set env vars +buildDir = BUILDDIR +os.environ["BITCOIND"] = buildDir + '/src/zcashd' + EXEEXT +os.environ["BITCOINCLI"] = buildDir + '/src/zcash-cli' + EXEEXT + +#Disable Windows tests by default +if EXEEXT == ".exe" and "-win" not in opts: + print "Win tests currently disabled. Use -win option to enable" + sys.exit(0) + +#Tests +testScripts = [ + 'paymentdisclosure.py', + 'prioritisetransaction.py', + 'wallet_treestate.py', + 'wallet_anchorfork.py', + 'wallet_changeaddresses.py', + 'wallet_changeindicator.py', + 'wallet_import_export.py', + 'wallet_sendmany_any_taddr.py', + 'wallet_shieldingcoinbase.py', + 'wallet_shieldcoinbase_sprout.py', + 'wallet_shieldcoinbase_sapling.py', + 'wallet_listreceived.py', + 'wallet.py', + 'wallet_overwintertx.py', + 'wallet_persistence.py', + 'wallet_nullifiers.py', + 'wallet_1941.py', + 'wallet_addresses.py', + 'wallet_sapling.py', + 'wallet_listnotes.py', + 'mergetoaddress_sprout.py', + 'mergetoaddress_sapling.py', + 'mergetoaddress_mixednotes.py', + 'listtransactions.py', + 'mempool_resurrect_test.py', + 'txn_doublespend.py', + 'txn_doublespend.py --mineblock', + 'getchaintips.py', + 'rawtransactions.py', + 'getrawtransaction_insight.py', + 'rest.py', + 'mempool_limit.py', + 'mempool_spendcoinbase.py', + 'mempool_reorg.py', + 'mempool_nu_activation.py', + 'mempool_tx_expiry.py', + 'httpbasics.py', + 'multi_rpc.py', + 'zapwallettxes.py', + 'proxy_test.py', + 'merkle_blocks.py', + 'fundrawtransaction.py', + 'signrawtransactions.py', + 'signrawtransaction_offline.py', + 'walletbackup.py', + 'key_import_export.py', + 'nodehandling.py', + 'reindex.py', + 'addressindex.py', + 'spentindex.py', + 'timestampindex.py', + 'decodescript.py', + 'p2p-fullblocktest.py', + 'blockchain.py', + 'disablewallet.py', + 'zcjoinsplit.py', + 'zcjoinsplitdoublespend.py', + 'zkey_import_export.py', + 'reorg_limit.py', + 'getblocktemplate.py', + 'bip65-cltv-p2p.py', + 'bipdersig-p2p.py', + 'p2p_nu_peer_management.py', + 'rewind_index.py', + 'p2p_txexpiry_dos.py', + 'p2p_txexpiringsoon.py', + 'p2p_node_bloom.py', + 'regtest_signrawtransaction.py', + 'finalsaplingroot.py', + 'shorter_block_times.py', + 'sprout_sapling_migration.py', + 'turnstile.py', + 'mining_shielded_coinbase.py', + 'coinbase_funding_streams.py', + 'framework.py', + 'sapling_rewind_check.py', + 'feature_zip221.py', + 'upgrade_golden.py', + 'post_heartwood_rollback.py', + 'feature_logging.py', + 'remove_sprout_shielding.py', + 'feature_walletfile.py', +] +testScriptsExt = [ + 'getblocktemplate_longpoll.py', + 'getblocktemplate_proposals.py', + 'pruning.py', + 'forknotify.py', + 'hardforkdetection.py', + 'invalidateblock.py', + 'keypool.py', + 'receivedby.py', + 'rpcbind_test.py', +# 'script_test.py', + 'smartfees.py', + 'maxblocksinflight.py', + 'invalidblockrequest.py', +# 'forknotify.py', + 'p2p-acceptblock.py', + 'wallet_db_flush.py', +] + +#Enable ZMQ tests +if ENABLE_ZMQ == 1: + testScripts.append('zmq_test.py') + +if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): + rpcTestDir = buildDir + '/qa/rpc-tests/' + #Run Tests + for i in range(len(testScripts)): + if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts + or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): + print "Running testscript " + testScripts[i] + "..." + subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) + #exit if help is called so we print just one set of instructions + p = re.compile(" -h| --help") + if p.match(passOn): + sys.exit(0) + + #Run Extended Tests + for i in range(len(testScriptsExt)): + if ('-extended' in opts or testScriptsExt[i] in opts + or re.sub(".py$", "", testScriptsExt[i]) in opts): + print "Running 2nd level testscript " + testScriptsExt[i] + "..." + subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) +else: + print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh deleted file mode 100755 index 9dadf4833..000000000 --- a/qa/pull-tester/rpc-tests.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/bash -set -e -o pipefail - -CURDIR=$(cd $(dirname "$0"); pwd) -# Get BUILDDIR and REAL_BITCOIND -. "${CURDIR}/tests-config.sh" - -export BITCOIND=${REAL_BITCOIND} -export BITCOINCLI=${REAL_BITCOINCLI} - -#Run the tests - -testScripts=( - 'paymentdisclosure.py' - 'prioritisetransaction.py' - 'wallet_treestate.py' - 'wallet_anchorfork.py' - 'wallet_changeaddresses.py' - 'wallet_changeindicator.py' - 'wallet_import_export.py' - 'wallet_sendmany_any_taddr.py' - 'wallet_shieldingcoinbase.py' - 'wallet_shieldcoinbase_sprout.py' - 'wallet_shieldcoinbase_sapling.py' - 'wallet_listreceived.py' - 'wallet.py' - 'wallet_overwintertx.py' - 'wallet_persistence.py' - 'wallet_nullifiers.py' - 'wallet_1941.py' - 'wallet_addresses.py' - 'wallet_sapling.py' - 'wallet_listnotes.py' - 'mergetoaddress_sprout.py' - 'mergetoaddress_sapling.py' - 'mergetoaddress_mixednotes.py' - 'listtransactions.py' - 'mempool_resurrect_test.py' - 'txn_doublespend.py' - 'txn_doublespend.py --mineblock' - 'getchaintips.py' - 'rawtransactions.py' - 'getrawtransaction_insight.py' - 'rest.py' - 'mempool_limit.py' - 'mempool_spendcoinbase.py' - 'mempool_reorg.py' - 'mempool_nu_activation.py' - 'mempool_tx_expiry.py' - 'httpbasics.py' - 'multi_rpc.py' - 'zapwallettxes.py' - 'proxy_test.py' - 'merkle_blocks.py' - 'fundrawtransaction.py' - 'signrawtransactions.py' - 'signrawtransaction_offline.py' - 'walletbackup.py' - 'key_import_export.py' - 'nodehandling.py' - 'reindex.py' - 'addressindex.py' - 'spentindex.py' - 'timestampindex.py' - 'decodescript.py' - 'p2p-fullblocktest.py' - 'blockchain.py' - 'disablewallet.py' - 'zcjoinsplit.py' - 'zcjoinsplitdoublespend.py' - 'zkey_import_export.py' - 'reorg_limit.py' - 'getblocktemplate.py' - 'bip65-cltv-p2p.py' - 'bipdersig-p2p.py' - 'p2p_nu_peer_management.py' - 'rewind_index.py' - 'p2p_txexpiry_dos.py' - 'p2p_txexpiringsoon.py' - 'p2p_node_bloom.py' - 'regtest_signrawtransaction.py' - 'finalsaplingroot.py' - 'shorter_block_times.py' - 'sprout_sapling_migration.py' - 'turnstile.py' - 'mining_shielded_coinbase.py' - 'coinbase_funding_streams.py' - 'framework.py' - 'sapling_rewind_check.py' - 'feature_zip221.py' - 'upgrade_golden.py' - 'post_heartwood_rollback.py' - 'feature_logging.py' - 'remove_sprout_shielding.py' - 'feature_walletfile.py' -); -testScriptsExt=( - 'getblocktemplate_longpoll.py' - 'getblocktemplate_proposals.py' - 'pruning.py' - 'forknotify.py' - 'hardforkdetection.py' - 'invalidateblock.py' - 'keypool.py' - 'receivedby.py' - 'rpcbind_test.py' -# 'script_test.py' - 'smartfees.py' - 'maxblocksinflight.py' - 'invalidblockrequest.py' -# 'forknotify.py' - 'p2p-acceptblock.py' - 'wallet_db_flush.py' -); - -if [ "x$ENABLE_ZMQ" = "x1" ]; then - testScripts+=('zmq_test.py') -fi - -extArg="-extended" -passOn=${@#$extArg} - -successCount=0 -declare -a failures - -function runTestScript -{ - local testName="$1" - shift - - echo -e "=== Running testscript ${testName} ===" - - local startTime=$(date +%s) - if eval "$@" - then - successCount=$(expr $successCount + 1) - echo "--- Success: ${testName} ($(($(date +%s) - $startTime))s) ---" - else - failures[${#failures[@]}]="$testName" - echo "!!! FAIL: ${testName} ($(($(date +%s) - $startTime))s) !!!" - fi - - echo -} - -if [ "x${ENABLE_BITCOIND}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then - for (( i = 0; i < ${#testScripts[@]}; i++ )) - do - if [ -z "$1" ] || [ "${1:0:1}" == "-" ] || [ "$1" == "${testScripts[$i]}" ] || [ "$1.py" == "${testScripts[$i]}" ] - then - runTestScript \ - "${testScripts[$i]}" \ - "${BUILDDIR}/qa/rpc-tests/${testScripts[$i]}" \ - --srcdir "${BUILDDIR}/src" ${passOn} - fi - done - for (( i = 0; i < ${#testScriptsExt[@]}; i++ )) - do - if [ "$1" == $extArg ] || [ "$1" == "${testScriptsExt[$i]}" ] || [ "$1.py" == "${testScriptsExt[$i]}" ] - then - runTestScript \ - "${testScriptsExt[$i]}" \ - "${BUILDDIR}/qa/rpc-tests/${testScriptsExt[$i]}" \ - --srcdir "${BUILDDIR}/src" ${passOn} - fi - done - - echo -e "\n\nTests completed: $(expr $successCount + ${#failures[@]})" - echo "successes $successCount; failures: ${#failures[@]}" - - if [ ${#failures[@]} -gt 0 ] - then - echo -e "\nFailing tests: ${failures[*]}" - exit 1 - else - exit 0 - fi -else - echo "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" -fi diff --git a/qa/pull-tester/tests-config.sh.in b/qa/pull-tester/tests_config.py.in old mode 100755 new mode 100644 similarity index 80% rename from qa/pull-tester/tests-config.sh.in rename to qa/pull-tester/tests_config.py.in index 6386a8c48..d2c667eaf --- a/qa/pull-tester/tests-config.sh.in +++ b/qa/pull-tester/tests_config.py.in @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env python2 # Copyright (c) 2013-2014 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -12,6 +12,4 @@ EXEEXT="@EXEEXT@" @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1 @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1 -REAL_BITCOIND="$BUILDDIR/src/zcashd${EXEEXT}" -REAL_BITCOINCLI="$BUILDDIR/src/zcash-cli${EXEEXT}" diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 43421684e..e74ff1b48 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -32,9 +32,13 @@ Helper functions for creating blocks and transactions. Notes ===== -You can run a single test by calling `qa/pull-tester/rpc-tests.sh `. +You can run any single test by calling qa/pull-tester/rpc-tests.py -Run all possible tests with `qa/pull-tester/rpc-tests.sh -extended`. +Or you can run any combination of tests by calling `qa/pull-tester/rpc-tests.py ...` + +Run the regression test suite with `qa/pull-tester/rpc-tests.py' + +Run all possible tests with `qa/pull-tester/rpc-tests.py -extended` Possible options: @@ -48,7 +52,7 @@ Possible options: --tracerpc Print out all RPC calls as they are made ``` -If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.sh wallet`). +If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`). A 200-block -regtest blockchain and wallets for four nodes is created the first time a regression test is run and From 66185801949d3be874169ac98db467ec3fefe092 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sun, 4 Oct 2015 15:08:18 -0400 Subject: [PATCH 04/66] qa/pull-tester/rpc-tests.py: chmod 0755 Fix file mode to be executable. Include dummy whitespace change to force git to change mode. --- qa/pull-tester/rpc-tests.py | 1 - 1 file changed, 1 deletion(-) mode change 100644 => 100755 qa/pull-tester/rpc-tests.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py old mode 100644 new mode 100755 index a8a197219..87c9694f8 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -1,5 +1,4 @@ #!/usr/bin/env python2 - # Copyright (c) 2014 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. From 8e58eddab110dcf580521083ab650422f0693842 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 8 Oct 2015 14:54:22 +0200 Subject: [PATCH 05/66] [doc] trivial: fix markdown syntax in qa/rpc-tests/README.md --- qa/rpc-tests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index e74ff1b48..9cbff33ec 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -32,11 +32,11 @@ Helper functions for creating blocks and transactions. Notes ===== -You can run any single test by calling qa/pull-tester/rpc-tests.py +You can run any single test by calling `qa/pull-tester/rpc-tests.py `. Or you can run any combination of tests by calling `qa/pull-tester/rpc-tests.py ...` -Run the regression test suite with `qa/pull-tester/rpc-tests.py' +Run the regression test suite with `qa/pull-tester/rpc-tests.py` Run all possible tests with `qa/pull-tester/rpc-tests.py -extended` From 480f1bf43e7e47ac25cc8beee1bab8999f5b1b37 Mon Sep 17 00:00:00 2001 From: James O'Beirne Date: Thu, 8 Oct 2015 01:22:50 -0700 Subject: [PATCH 06/66] Add tests for gettxoutsetinfo, CLevelDBBatch, CLevelDBIterator Thanks @dexX7. Zcash: Rest of this commit was cherry-picked in zcash/zcash#2598. --- qa/pull-tester/rpc-tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 87c9694f8..cc7826526 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -156,10 +156,10 @@ if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): rpcTestDir = buildDir + '/qa/rpc-tests/' #Run Tests for i in range(len(testScripts)): - if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts + if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): - print "Running testscript " + testScripts[i] + "..." - subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) + print "Running testscript " + testScripts[i] + "..." + subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) #exit if help is called so we print just one set of instructions p = re.compile(" -h| --help") if p.match(passOn): @@ -167,9 +167,9 @@ if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): #Run Extended Tests for i in range(len(testScriptsExt)): - if ('-extended' in opts or testScriptsExt[i] in opts + if ('-extended' in opts or testScriptsExt[i] in opts or re.sub(".py$", "", testScriptsExt[i]) in opts): - print "Running 2nd level testscript " + testScriptsExt[i] + "..." - subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) + print "Running 2nd level testscript " + testScriptsExt[i] + "..." + subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) else: print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" From 6aa4f19e87c9d27c33dfc60390d008746f180ac5 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 14 Oct 2015 14:27:03 +0200 Subject: [PATCH 07/66] [rpc-tests] fundrawtransaction: Update fee after minRelayTxFee increase Zcash: Added floor, since our minRelayTxFee is actually lower. --- qa/rpc-tests/fundrawtransaction.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 03aa5a113..4445d0ed7 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -32,7 +32,15 @@ class RawTransactionsTest(BitcoinTestFramework): def run_test(self): print("Mining blocks...") - feeTolerance = Decimal("0.00000002") #if the fee's positive delta is higher than this value tests will fail, neg. delta always fail the tests + + min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee'] + # if the fee's positive delta is higher than this value tests will fail, + # neg. delta always fail the tests. + # The size of the signature of every input may be at most 2 bytes larger + # than a minimum sized signature. + + # = 2 bytes * minRelayTxFeePerByte + feeTolerance = max(2 * min_relay_tx_fee/1000, Decimal("0.00000001")) self.nodes[2].generate(1) self.sync_all() From 760e5008110643a333078a841fdf989aadaa1e52 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 14 Oct 2015 13:15:41 +0200 Subject: [PATCH 08/66] [rpc-tests] Check return code --- qa/pull-tester/rpc-tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index cc7826526..146a8ddb5 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -159,7 +159,7 @@ if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): print "Running testscript " + testScripts[i] + "..." - subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) + subprocess.check_call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) #exit if help is called so we print just one set of instructions p = re.compile(" -h| --help") if p.match(passOn): @@ -170,6 +170,6 @@ if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): if ('-extended' in opts or testScriptsExt[i] in opts or re.sub(".py$", "", testScriptsExt[i]) in opts): print "Running 2nd level testscript " + testScriptsExt[i] + "..." - subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) + subprocess.check_call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) else: print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" From 5832635532d9bedf542b7a969c1661675f0aa445 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 2 Nov 2015 04:15:58 +0100 Subject: [PATCH 09/66] build: don't distribute tests_config.py This file is dynamically generated by configure based on the platform, it doesn't belong in the distribution archive. Fixes #6929. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 5784fef5d..e14204c10 100644 --- a/Makefile.am +++ b/Makefile.am @@ -147,7 +147,7 @@ dist_noinst_SCRIPTS = autogen.sh zcutil/build-debian-package.sh zcutil/build.sh RUST_DIST = $(top_srcdir)/.cargo $(top_srcdir)/Cargo.toml $(top_srcdir)/Cargo.lock rust-toolchain -EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/pull-tester/tests_config.py qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS) $(RUST_DIST) +EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS) $(RUST_DIST) install-exec-hook: mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/zcash-fetch-params From eaf4dfe2801d39d823616b69ff02f1fa99735463 Mon Sep 17 00:00:00 2001 From: James O'Beirne Date: Sat, 10 Oct 2015 22:41:19 -0700 Subject: [PATCH 10/66] Add basic coverage reporting for RPC tests Thanks to @MarcoFalke @dexX7 @laanwj for review. Zcash: Just the --coverage flag for rpc-tests.py, as we backported the rest of the coverage backend in zcash/zcash#4411. --- qa/pull-tester/rpc-tests.py | 180 ++++++++++++++++++++++++++++++------ 1 file changed, 151 insertions(+), 29 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 146a8ddb5..603b6d9ed 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -3,16 +3,32 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Run Regression Test Suite -# +""" +Run Regression Test Suite + +This module calls down into individual test cases via subprocess. It will +forward all unrecognized arguments onto the individual test scripts, other +than: + + - `-extended`: run the "extended" test suite in addition to the basic one. + - `-win`: signal that this is running in a Windows environment, and we + should run the tests. + - `--coverage`: this generates a basic coverage report for the RPC + interface. + +For a description of arguments recognized by test scripts, see +`qa/pull-tester/test_framework/test_framework.py:BitcoinTestFramework.main`. + +""" import os +import shutil import sys import subprocess +import tempfile import re + from tests_config import * -from sets import Set #If imported values are not defined then set to zero (or disabled) if not vars().has_key('ENABLE_WALLET'): @@ -24,15 +40,20 @@ if not vars().has_key('ENABLE_UTILS'): if not vars().has_key('ENABLE_ZMQ'): ENABLE_ZMQ=0 +ENABLE_COVERAGE=0 + #Create a set to store arguments and create the passOn string -opts = Set() +opts = set() passOn = "" p = re.compile("^--") -for i in range(1,len(sys.argv)): - if (p.match(sys.argv[i]) or sys.argv[i] == "-h"): - passOn += " " + sys.argv[i] + +for arg in sys.argv[1:]: + if arg == '--coverage': + ENABLE_COVERAGE = 1 + elif (p.match(arg) or arg == "-h"): + passOn += " " + arg else: - opts.add(sys.argv[i]) + opts.add(arg) #Set env vars buildDir = BUILDDIR @@ -152,24 +173,125 @@ testScriptsExt = [ if ENABLE_ZMQ == 1: testScripts.append('zmq_test.py') -if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): - rpcTestDir = buildDir + '/qa/rpc-tests/' - #Run Tests - for i in range(len(testScripts)): - if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts - or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): - print "Running testscript " + testScripts[i] + "..." - subprocess.check_call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) - #exit if help is called so we print just one set of instructions - p = re.compile(" -h| --help") - if p.match(passOn): - sys.exit(0) - #Run Extended Tests - for i in range(len(testScriptsExt)): - if ('-extended' in opts or testScriptsExt[i] in opts - or re.sub(".py$", "", testScriptsExt[i]) in opts): - print "Running 2nd level testscript " + testScriptsExt[i] + "..." - subprocess.check_call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) -else: - print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" +def runtests(): + coverage = None + + if ENABLE_COVERAGE: + coverage = RPCCoverage() + print("Initializing coverage directory at %s" % coverage.dir) + + if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): + rpcTestDir = buildDir + '/qa/rpc-tests/' + run_extended = '-extended' in opts + cov_flag = coverage.flag if coverage else '' + flags = " --srcdir %s/src %s %s" % (buildDir, cov_flag, passOn) + + #Run Tests + for i in range(len(testScripts)): + if (len(opts) == 0 + or (len(opts) == 1 and "-win" in opts ) + or run_extended + or testScripts[i] in opts + or re.sub(".py$", "", testScripts[i]) in opts ): + print("Running testscript " + testScripts[i] + "...") + + subprocess.check_call( + rpcTestDir + testScripts[i] + flags, shell=True) + + # exit if help is called so we print just one set of + # instructions + p = re.compile(" -h| --help") + if p.match(passOn): + sys.exit(0) + + # Run Extended Tests + for i in range(len(testScriptsExt)): + if (run_extended or testScriptsExt[i] in opts + or re.sub(".py$", "", testScriptsExt[i]) in opts): + print( + "Running 2nd level testscript " + + testScriptsExt[i] + "...") + + subprocess.check_call( + rpcTestDir + testScriptsExt[i] + flags, shell=True) + + if coverage: + coverage.report_rpc_coverage() + + print("Cleaning up coverage data") + coverage.cleanup() + + else: + print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" + + +class RPCCoverage(object): + """ + Coverage reporting utilities for pull-tester. + + Coverage calculation works by having each test script subprocess write + coverage files into a particular directory. These files contain the RPC + commands invoked during testing, as well as a complete listing of RPC + commands per `bitcoin-cli help` (`rpc_interface.txt`). + + After all tests complete, the commands run are combined and diff'd against + the complete list to calculate uncovered RPC commands. + + See also: qa/rpc-tests/test_framework/coverage.py + + """ + def __init__(self): + self.dir = tempfile.mkdtemp(prefix="coverage") + self.flag = '--coveragedir %s' % self.dir + + def report_rpc_coverage(self): + """ + Print out RPC commands that were unexercised by tests. + + """ + uncovered = self._get_uncovered_rpc_commands() + + if uncovered: + print("Uncovered RPC commands:") + print("".join((" - %s\n" % i) for i in sorted(uncovered))) + else: + print("All RPC commands covered.") + + def cleanup(self): + return shutil.rmtree(self.dir) + + def _get_uncovered_rpc_commands(self): + """ + Return a set of currently untested RPC commands. + + """ + # This is shared from `qa/rpc-tests/test-framework/coverage.py` + REFERENCE_FILENAME = 'rpc_interface.txt' + COVERAGE_FILE_PREFIX = 'coverage.' + + coverage_ref_filename = os.path.join(self.dir, REFERENCE_FILENAME) + coverage_filenames = set() + all_cmds = set() + covered_cmds = set() + + if not os.path.isfile(coverage_ref_filename): + raise RuntimeError("No coverage reference found") + + with open(coverage_ref_filename, 'r') as f: + all_cmds.update([i.strip() for i in f.readlines()]) + + for root, dirs, files in os.walk(self.dir): + for filename in files: + if filename.startswith(COVERAGE_FILE_PREFIX): + coverage_filenames.add(os.path.join(root, filename)) + + for filename in coverage_filenames: + with open(filename, 'r') as f: + covered_cmds.update([i.strip() for i in f.readlines()]) + + return all_cmds - covered_cmds + + +if __name__ == '__main__': + runtests() From 800bf0a45fd83acf1dd65700bdae4596583b904e Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 16 Nov 2015 09:45:57 -0500 Subject: [PATCH 11/66] Remove unmaintained example test script_test.py --- qa/pull-tester/rpc-tests.py | 1 - qa/rpc-tests/script_test.py | 256 ------------------------------------ 2 files changed, 257 deletions(-) delete mode 100755 qa/rpc-tests/script_test.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 603b6d9ed..a81235bae 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -160,7 +160,6 @@ testScriptsExt = [ 'keypool.py', 'receivedby.py', 'rpcbind_test.py', -# 'script_test.py', 'smartfees.py', 'maxblocksinflight.py', 'invalidblockrequest.py', diff --git a/qa/rpc-tests/script_test.py b/qa/rpc-tests/script_test.py deleted file mode 100755 index f242ff08c..000000000 --- a/qa/rpc-tests/script_test.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python3 -# -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . -# - -''' -Test notes: -This test uses the script_valid and script_invalid tests from the unittest -framework to do end-to-end testing where we compare that two nodes agree on -whether blocks containing a given test script are valid. - -We generally ignore the script flags associated with each test (since we lack -the precision to test each script using those flags in this framework), but -for tests with SCRIPT_VERIFY_P2SH, we can use a block time after the BIP16 -switchover date to try to test with that flag enabled (and for tests without -that flag, we use a block time before the switchover date). - -NOTE: This test is very slow and may take more than 40 minutes to run. -''' - -from test_framework.test_framework import ComparisonTestFramework -from test_framework.comptool import TestInstance, TestManager -from test_framework.mininode import NetworkThread -from test_framework.blocktools import create_block, create_coinbase, create_transaction -from test_framework.script import CScript, CScriptOp, CScriptNum, OPCODES_BY_NAME - -import os -import json - -script_valid_file = "../../src/test/data/script_valid.json" -script_invalid_file = "../../src/test/data/script_invalid.json" - -# Pass in a set of json files to open. -class ScriptTestFile(object): - - def __init__(self, files): - self.files = files - self.index = -1 - self.data = [] - - def load_files(self): - for f in self.files: - self.data.extend(json.loads(open(os.path.dirname(os.path.abspath(__file__))+"/"+f).read())) - - # Skip over records that are not long enough to be tests - def get_records(self): - while (self.index < len(self.data)): - if len(self.data[self.index]) >= 3: - yield self.data[self.index] - self.index += 1 - - -# Helper for parsing the flags specified in the .json files -SCRIPT_VERIFY_NONE = 0 -SCRIPT_VERIFY_P2SH = 1 -SCRIPT_VERIFY_STRICTENC = 1 << 1 -SCRIPT_VERIFY_LOW_S = 1 << 3 -SCRIPT_VERIFY_NULLDUMMY = 1 << 4 -SCRIPT_VERIFY_SIGPUSHONLY = 1 << 5 -SCRIPT_VERIFY_MINIMALDATA = 1 << 6 -SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = 1 << 7 -SCRIPT_VERIFY_CLEANSTACK = 1 << 8 - -flag_map = { - "": SCRIPT_VERIFY_NONE, - "NONE": SCRIPT_VERIFY_NONE, - "P2SH": SCRIPT_VERIFY_P2SH, - "STRICTENC": SCRIPT_VERIFY_STRICTENC, - "LOW_S": SCRIPT_VERIFY_LOW_S, - "NULLDUMMY": SCRIPT_VERIFY_NULLDUMMY, - "SIGPUSHONLY": SCRIPT_VERIFY_SIGPUSHONLY, - "MINIMALDATA": SCRIPT_VERIFY_MINIMALDATA, - "DISCOURAGE_UPGRADABLE_NOPS": SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS, - "CLEANSTACK": SCRIPT_VERIFY_CLEANSTACK, -} - -def ParseScriptFlags(flag_string): - flags = 0 - for x in flag_string.split(","): - if x in flag_map: - flags |= flag_map[x] - else: - print("Error: unrecognized script flag: ", x) - return flags - -''' -Given a string that is a scriptsig or scriptpubkey from the .json files above, -convert it to a CScript() -''' -# Replicates behavior from core_read.cpp -def ParseScript(json_script): - script = json_script.split(" ") - parsed_script = CScript() - for x in script: - if len(x) == 0: - # Empty string, ignore. - pass - elif x.isdigit() or (len(x) >= 1 and x[0] == "-" and x[1:].isdigit()): - # Number - n = int(x, 0) - if (n == -1) or (n >= 1 and n <= 16): - parsed_script = CScript(bytes(parsed_script) + bytes(CScript([n]))) - else: - parsed_script += CScriptNum(int(x, 0)) - elif x.startswith("0x"): - # Raw hex data, inserted NOT pushed onto stack: - for i in range(2, len(x), 2): - parsed_script = CScript(bytes(parsed_script) + bytes(chr(int(x[i:i+2],16)))) - elif x.startswith("'") and x.endswith("'") and len(x) >= 2: - # Single-quoted string, pushed as data. - parsed_script += CScript([x[1:-1]]) - else: - # opcode, e.g. OP_ADD or ADD: - tryopname = "OP_" + x - if tryopname in OPCODES_BY_NAME: - parsed_script += CScriptOp(OPCODES_BY_NAME["OP_" + x]) - else: - print("ParseScript: error parsing '%s'" % x) - return "" - return parsed_script - -class TestBuilder(object): - def create_credit_tx(self, scriptPubKey, height): - # self.tx1 is a coinbase transaction, modeled after the one created by script_tests.cpp - # This allows us to reuse signatures created in the unit test framework. - self.tx1 = create_coinbase(height) # this has a bip34 scriptsig, - self.tx1.vin[0].scriptSig = CScript([0, 0]) # but this matches the unit tests - self.tx1.vout[0].nValue = 0 - self.tx1.vout[0].scriptPubKey = scriptPubKey - self.tx1.rehash() - def create_spend_tx(self, scriptSig): - self.tx2 = create_transaction(self.tx1, 0, CScript(), 0) - self.tx2.vin[0].scriptSig = scriptSig - self.tx2.vout[0].scriptPubKey = CScript() - self.tx2.rehash() - def rehash(self): - self.tx1.rehash() - self.tx2.rehash() - -# This test uses the (default) two nodes provided by ComparisonTestFramework, -# specified on the command line with --testbinary and --refbinary. -# See comptool.py -class ScriptTest(ComparisonTestFramework): - - def run_test(self): - # Set up the comparison tool TestManager - test = TestManager(self, self.options.tmpdir) - test.add_all_connections(self.nodes) - - # Load scripts - self.scripts = ScriptTestFile([script_valid_file, script_invalid_file]) - self.scripts.load_files() - - # Some variables we re-use between test instances (to build blocks) - self.tip = None - self.block_time = None - - NetworkThread().start() # Start up network handling in another thread - test.run() - - def generate_test_instance(self, pubkeystring, scriptsigstring): - scriptpubkey = ParseScript(pubkeystring) - scriptsig = ParseScript(scriptsigstring) - - test = TestInstance(sync_every_block=False) - test_build = TestBuilder() - test_build.create_credit_tx(scriptpubkey, self.height) - test_build.create_spend_tx(scriptsig) - test_build.rehash() - - block = create_block(self.tip, test_build.tx1, self.block_time) - self.block_time += 1 - block.solve() - self.tip = block.sha256 - self.height += 1 - test.blocks_and_transactions = [[block, True]] - - for i in range(100): - block = create_block(self.tip, create_coinbase(self.height), self.block_time) - self.block_time += 1 - block.solve() - self.tip = block.sha256 - self.height += 1 - test.blocks_and_transactions.append([block, True]) - - block = create_block(self.tip, create_coinbase(self.height), self.block_time) - self.block_time += 1 - block.vtx.append(test_build.tx2) - block.hashMerkleRoot = block.calc_merkle_root() - block.rehash() - block.solve() - test.blocks_and_transactions.append([block, None]) - return test - - # This generates the tests for TestManager. - def get_tests(self): - self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) - self.block_time = 1333230000 # before the BIP16 switchover - self.height = 1 - - ''' - Create a new block with an anyone-can-spend coinbase - ''' - block = create_block(self.tip, create_coinbase(self.height), self.block_time) - self.block_time += 1 - block.solve() - self.tip = block.sha256 - self.height += 1 - yield TestInstance(objects=[[block, True]]) - - ''' - Build out to 100 blocks total, maturing the coinbase. - ''' - test = TestInstance(objects=[], sync_every_block=False, sync_every_tx=False) - for i in range(100): - b = create_block(self.tip, create_coinbase(self.height), self.block_time) - b.solve() - test.blocks_and_transactions.append([b, True]) - self.tip = b.sha256 - self.block_time += 1 - self.height += 1 - yield test - - ''' Iterate through script tests. ''' - counter = 0 - for script_test in self.scripts.get_records(): - ''' Reset the blockchain to genesis block + 100 blocks. ''' - if self.nodes[0].getblockcount() > 101: - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(102)) - self.nodes[1].invalidateblock(self.nodes[1].getblockhash(102)) - - self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) - self.height = 102 - - [scriptsig, scriptpubkey, flags] = script_test[0:3] - flags = ParseScriptFlags(flags) - - # We can use block time to determine whether the nodes should be - # enforcing BIP16. - # - # We intentionally let the block time grow by 1 each time. - # This forces the block hashes to differ between tests, so that - # a call to invalidateblock doesn't interfere with a later test. - if (flags & SCRIPT_VERIFY_P2SH): - self.block_time = 1333238400 + counter # Advance to enforcing BIP16 - else: - self.block_time = 1333230000 + counter # Before the BIP16 switchover - - print("Script test: [%s]" % script_test) - - yield self.generate_test_instance(scriptpubkey, scriptsig) - counter += 1 - -if __name__ == '__main__': - ScriptTest().main() From 80b2274ddffecaf77f624554ad8ae4083b7f37df Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 16 Nov 2015 15:33:40 +0100 Subject: [PATCH 12/66] [qa] Split README.md to /qa and /qa/rpc-tests + Update with new -help message --- qa/README.md | 46 ++++++++++++++++++++++++++++++++++++++++++ qa/rpc-tests/README.md | 43 --------------------------------------- 2 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 qa/README.md diff --git a/qa/README.md b/qa/README.md new file mode 100644 index 000000000..44ae40b3b --- /dev/null +++ b/qa/README.md @@ -0,0 +1,46 @@ +Notes +===== + +You can run any single test by calling `qa/pull-tester/rpc-tests.py `. + +Or you can run any combination of tests by calling `qa/pull-tester/rpc-tests.py ...` + +Run the regression test suite with `qa/pull-tester/rpc-tests.py` + +Run all possible tests with `qa/pull-tester/rpc-tests.py -extended` + +Possible options: + +``` + -h, --help show this help message and exit + --nocleanup Leave zcashds and test.* datadir on exit or error + --noshutdown Don't stop zcashds after the test execution + --srcdir=SRCDIR Source directory containing zcashd/zcash-cli + (default: ../../src) + --tmpdir=TMPDIR Root directory for datadirs + --tracerpc Print out all RPC calls as they are made + --coveragedir=COVERAGEDIR + Write tested RPC commands into this directory +``` + +If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`). + +A 200-block -regtest blockchain and wallets for four nodes +is created the first time a regression test is run and +is stored in the cache/ directory. Each node has the miner +subsidy from 25 mature blocks (25*10=250 ZEC) in its wallet. + +After the first run, the cache/ blockchain and wallets are +copied into a temporary directory and used as the initial +test state. + +If you get into a bad state, you should be able +to recover with: + +```bash +rm -rf cache +killall zcashd +``` + +Further information about the test framework and individual rpc +tests is found in [qa/rpc-tests/README.md](/qa/rpc-tests/README.md). diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 9cbff33ec..315feae71 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -28,49 +28,6 @@ Helpers for script.py ### [test_framework/blocktools.py](test_framework/blocktools.py) Helper functions for creating blocks and transactions. - -Notes -===== - -You can run any single test by calling `qa/pull-tester/rpc-tests.py `. - -Or you can run any combination of tests by calling `qa/pull-tester/rpc-tests.py ...` - -Run the regression test suite with `qa/pull-tester/rpc-tests.py` - -Run all possible tests with `qa/pull-tester/rpc-tests.py -extended` - -Possible options: - -``` --h, --help show this help message and exit - --nocleanup Leave zcashds and test.* datadir on exit or error - --noshutdown Don't stop bitcoinds after the test execution - --srcdir=SRCDIR Source directory containing zcashd/zcash-cli (default: - ../../src) - --tmpdir=TMPDIR Root directory for datadirs - --tracerpc Print out all RPC calls as they are made -``` - -If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`). - -A 200-block -regtest blockchain and wallets for four nodes -is created the first time a regression test is run and -is stored in the cache/ directory. Each node has the miner -subsidy from 25 mature blocks (25*10=250 ZEC) in its wallet. - -After the first run, the cache/ blockchain and wallets are -copied into a temporary directory and used as the initial -test state. - -If you get into a bad state, you should be able -to recover with: - -```bash -rm -rf cache -killall zcashd -``` - P2P test design notes --------------------- From 01c36295c489b18ae3f68281106e8d97bf232936 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 18 Nov 2015 00:26:07 +0100 Subject: [PATCH 13/66] [qa] Extend README.md --- qa/README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/qa/README.md b/qa/README.md index 44ae40b3b..0e2b49cdd 100644 --- a/qa/README.md +++ b/qa/README.md @@ -1,5 +1,12 @@ -Notes -===== +The [pull-tester](/qa/pull-tester/) folder contains a script to call +multiple tests from the [rpc-tests](/qa/rpc-tests/) folder. + +Every pull request to the zcash repository is built and run through +the regression test suite. You can also run all or only individual +tests locally. + +Running tests +============= You can run any single test by calling `qa/pull-tester/rpc-tests.py `. @@ -23,7 +30,8 @@ Possible options: Write tested RPC commands into this directory ``` -If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`). +If you set the environment variable `PYTHON_DEBUG=1` you will get some debug +output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`). A 200-block -regtest blockchain and wallets for four nodes is created the first time a regression test is run and @@ -42,5 +50,8 @@ rm -rf cache killall zcashd ``` +Writing tests +============= +You are encouraged to write tests for new or existing features. Further information about the test framework and individual rpc -tests is found in [qa/rpc-tests/README.md](/qa/rpc-tests/README.md). +tests is found in [qa/rpc-tests](/qa/rpc-tests). From fa44509d6c02e659d67dd2214489a4b2f9f6b38d Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 15 Nov 2015 18:48:18 +0100 Subject: [PATCH 14/66] [qa] keypool: Fix white space to prepare transition to test framework --- qa/rpc-tests/keypool.py | 168 ++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index ac4b92a9a..2edaeb05e 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -38,107 +38,107 @@ def check_array_result(object_array, to_match, expected): if num_matched == 0: raise AssertionError("No objects matched %s"%(str(to_match))) -def run_test(nodes, tmpdir): - # Encrypt wallet and wait to terminate - nodes[0].encryptwallet('test') - bitcoind_processes[0].wait() - # Restart node 0 - nodes[0] = start_node(0, tmpdir) - # Keep creating keys - addr = nodes[0].getnewaddress() - try: + def run_test(nodes, tmpdir): + # Encrypt wallet and wait to terminate + nodes[0].encryptwallet('test') + bitcoind_processes[0].wait() + # Restart node 0 + nodes[0] = start_node(0, tmpdir) + # Keep creating keys addr = nodes[0].getnewaddress() - raise AssertionError('Keypool should be exhausted after one address') - except JSONRPCException as e: - assert(e.error['code']==-12) + try: + addr = nodes[0].getnewaddress() + raise AssertionError('Keypool should be exhausted after one address') + except JSONRPCException as e: + assert(e.error['code']==-12) - # put three new keys in the keypool - nodes[0].walletpassphrase('test', 12000) - nodes[0].keypoolrefill(3) - nodes[0].walletlock() + # put three new keys in the keypool + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() - # drain the keys - addr = set() - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - # assert that four unique addresses were returned - assert(len(addr) == 4) - # the next one should fail - try: - addr = nodes[0].getrawchangeaddress() - raise AssertionError('Keypool should be exhausted after three addresses') - except JSONRPCException as e: - assert(e.error['code']==-12) + # drain the keys + addr = set() + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + # assert that four unique addresses were returned + assert(len(addr) == 4) + # the next one should fail + try: + addr = nodes[0].getrawchangeaddress() + raise AssertionError('Keypool should be exhausted after three addresses') + except JSONRPCException as e: + assert(e.error['code']==-12) - # refill keypool with three new addresses - nodes[0].walletpassphrase('test', 12000) - nodes[0].keypoolrefill(3) - nodes[0].walletlock() + # refill keypool with three new addresses + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() - # drain them by mining - nodes[0].generate(1) - nodes[0].generate(1) - nodes[0].generate(1) - nodes[0].generate(1) - try: + # drain them by mining nodes[0].generate(1) - raise AssertionError('Keypool should be exhausted after three addesses') - except JSONRPCException as e: - assert_equal(e.error['code'], -12) + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + try: + nodes[0].generate(1) + raise AssertionError('Keypool should be exhausted after three addesses') + except JSONRPCException as e: + assert_equal(e.error['code'], -12) -def main(): - import optparse + def main(): + import optparse - parser = optparse.OptionParser(usage="%prog [options]") - parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", - help="Leave bitcoinds and test.* datadir on exit or error") - parser.add_option("--srcdir", dest="srcdir", default="../../src", - help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") - parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), - help="Root directory for datadirs") - (options, args) = parser.parse_args() + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", + help="Leave bitcoinds and test.* datadir on exit or error") + parser.add_option("--srcdir", dest="srcdir", default="../../src", + help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") + parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), + help="Root directory for datadirs") + (options, args) = parser.parse_args() - os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] + os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] - check_json_precision() + check_json_precision() - success = False - nodes = [] - try: - print("Initializing test directory "+options.tmpdir) - if not os.path.isdir(options.tmpdir): - os.makedirs(options.tmpdir) - initialize_chain(options.tmpdir) + success = False + nodes = [] + try: + print("Initializing test directory "+options.tmpdir) + if not os.path.isdir(options.tmpdir): + os.makedirs(options.tmpdir) + initialize_chain(options.tmpdir) - nodes = start_nodes(1, options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) + nodes = start_nodes(1, options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) - run_test(nodes, options.tmpdir) + run_test(nodes, options.tmpdir) - success = True + success = True - except AssertionError as e: - print("Assertion failed: "+e.message) - except JSONRPCException as e: - print("JSONRPC error: "+e.error['message']) - traceback.print_tb(sys.exc_info()[2]) - except Exception as e: - print("Unexpected exception caught during testing: ", e.error['message'], str(sys.exc_info()[0])) - traceback.print_tb(sys.exc_info()[2]) + except AssertionError as e: + print("Assertion failed: "+e.message) + except JSONRPCException as e: + print("JSONRPC error: "+e.error['message']) + traceback.print_tb(sys.exc_info()[2]) + except Exception as e: + print("Unexpected exception caught during testing: ", e.error['message'], str(sys.exc_info()[0])) + traceback.print_tb(sys.exc_info()[2]) - if not options.nocleanup: - print("Cleaning up") - stop_nodes(nodes) - wait_bitcoinds() - shutil.rmtree(options.tmpdir) + if not options.nocleanup: + print("Cleaning up") + stop_nodes(nodes) + wait_bitcoinds() + shutil.rmtree(options.tmpdir) - if success: - print("Tests successful") - sys.exit(0) - else: - print("Failed") - sys.exit(1) + if success: + print("Tests successful") + sys.exit(0) + else: + print("Failed") + sys.exit(1) if __name__ == '__main__': main() From db27db2e4358098f15ce9eae13d79c8f260fe083 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 15 Nov 2015 17:58:01 +0100 Subject: [PATCH 15/66] [qa] keypool: DRY: Use test framework --- qa/rpc-tests/keypool.py | 75 ++++++++--------------------------------- 1 file changed, 14 insertions(+), 61 deletions(-) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 2edaeb05e..3291f0516 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -8,14 +8,9 @@ # Add python-bitcoinrpc to module search path: from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, check_json_precision, initialize_chain, \ - start_nodes, start_node, stop_nodes, wait_bitcoinds, bitcoind_processes - -import os -import sys -import shutil -import tempfile -import traceback +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, initialize_chain, \ + start_nodes, start_node, bitcoind_processes def check_array_result(object_array, to_match, expected): """ @@ -38,12 +33,15 @@ def check_array_result(object_array, to_match, expected): if num_matched == 0: raise AssertionError("No objects matched %s"%(str(to_match))) - def run_test(nodes, tmpdir): +class KeyPoolTest(BitcoinTestFramework): + + def run_test(self): + nodes = self.nodes # Encrypt wallet and wait to terminate nodes[0].encryptwallet('test') bitcoind_processes[0].wait() # Restart node 0 - nodes[0] = start_node(0, tmpdir) + nodes[0] = start_node(0, self.options.tmpdir) # Keep creating keys addr = nodes[0].getnewaddress() try: @@ -88,57 +86,12 @@ def check_array_result(object_array, to_match, expected): except JSONRPCException as e: assert_equal(e.error['code'], -12) - def main(): - import optparse + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain(self.options.tmpdir) - parser = optparse.OptionParser(usage="%prog [options]") - parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", - help="Leave bitcoinds and test.* datadir on exit or error") - parser.add_option("--srcdir", dest="srcdir", default="../../src", - help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") - parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), - help="Root directory for datadirs") - (options, args) = parser.parse_args() - - os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] - - check_json_precision() - - success = False - nodes = [] - try: - print("Initializing test directory "+options.tmpdir) - if not os.path.isdir(options.tmpdir): - os.makedirs(options.tmpdir) - initialize_chain(options.tmpdir) - - nodes = start_nodes(1, options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) - - run_test(nodes, options.tmpdir) - - success = True - - except AssertionError as e: - print("Assertion failed: "+e.message) - except JSONRPCException as e: - print("JSONRPC error: "+e.error['message']) - traceback.print_tb(sys.exc_info()[2]) - except Exception as e: - print("Unexpected exception caught during testing: ", e.error['message'], str(sys.exc_info()[0])) - traceback.print_tb(sys.exc_info()[2]) - - if not options.nocleanup: - print("Cleaning up") - stop_nodes(nodes) - wait_bitcoinds() - shutil.rmtree(options.tmpdir) - - if success: - print("Tests successful") - sys.exit(0) - else: - print("Failed") - sys.exit(1) + def setup_network(self): + self.nodes = start_nodes(1, self.options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) if __name__ == '__main__': - main() + KeyPoolTest().main() From fb1f76f330a8ff5220edf5ca49242b991ae9b96f Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 30 Nov 2015 14:53:07 +0100 Subject: [PATCH 16/66] [qa] pull-tester: Cleanup (run keypool, tidy stdout) * Run keypool (takes 6 seconds) * Print duration of each rpc test * Structure output (bold, new lines) --- qa/pull-tester/rpc-tests.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index a81235bae..ff2d7287c 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -22,6 +22,7 @@ For a description of arguments recognized by test scripts, see """ import os +import time import shutil import sys import subprocess @@ -47,6 +48,10 @@ opts = set() passOn = "" p = re.compile("^--") +bold = ("","") +if (os.name == 'posix'): + bold = ('\033[0m', '\033[1m') + for arg in sys.argv[1:]: if arg == '--coverage': ENABLE_COVERAGE = 1 @@ -122,6 +127,7 @@ testScripts = [ 'p2p-fullblocktest.py', 'blockchain.py', 'disablewallet.py', + 'keypool.py', 'zcjoinsplit.py', 'zcjoinsplitdoublespend.py', 'zkey_import_export.py', @@ -157,7 +163,6 @@ testScriptsExt = [ 'forknotify.py', 'hardforkdetection.py', 'invalidateblock.py', - 'keypool.py', 'receivedby.py', 'rpcbind_test.py', 'smartfees.py', @@ -178,7 +183,7 @@ def runtests(): if ENABLE_COVERAGE: coverage = RPCCoverage() - print("Initializing coverage directory at %s" % coverage.dir) + print("Initializing coverage directory at %s\n" % coverage.dir) if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): rpcTestDir = buildDir + '/qa/rpc-tests/' @@ -193,10 +198,12 @@ def runtests(): or run_extended or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): - print("Running testscript " + testScripts[i] + "...") + print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScripts[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) # exit if help is called so we print just one set of # instructions @@ -208,12 +215,14 @@ def runtests(): for i in range(len(testScriptsExt)): if (run_extended or testScriptsExt[i] in opts or re.sub(".py$", "", testScriptsExt[i]) in opts): + print( "Running 2nd level testscript " - + testScriptsExt[i] + "...") - + + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScriptsExt[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) if coverage: coverage.report_rpc_coverage() From 239857d7d9e77ee2ef37786edcc31ec9787f52ec Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 14 Dec 2015 14:18:12 +0100 Subject: [PATCH 17/66] test: don't override BITCOIND and BITCOINCLI if they're set In rpc-tests.py, don't override BITCOIND and BITCOINCLI if they're already set. Makes it possible to run the tests with either another tree or the GUI. --- qa/pull-tester/rpc-tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index ff2d7287c..214967fae 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -62,8 +62,10 @@ for arg in sys.argv[1:]: #Set env vars buildDir = BUILDDIR -os.environ["BITCOIND"] = buildDir + '/src/zcashd' + EXEEXT -os.environ["BITCOINCLI"] = buildDir + '/src/zcash-cli' + EXEEXT +if "BITCOIND" not in os.environ: + os.environ["BITCOIND"] = buildDir + '/src/zcashd' + EXEEXT +if "BITCOINCLI" not in os.environ: + os.environ["BITCOINCLI"] = buildDir + '/src/zcash-cli' + EXEEXT #Disable Windows tests by default if EXEEXT == ".exe" and "-win" not in opts: From 845b31a7603648472ad2b6789d6e27860911fb16 Mon Sep 17 00:00:00 2001 From: Elliot Olds Date: Thu, 10 Mar 2016 03:12:40 -0800 Subject: [PATCH 18/66] Check if zmq is installed in tests, update docs If ZMQ is enabled, check whether it's installed before running ZMQ tests. If it isn't, disable ZMQ and print a warning. Also add dependency info to test docs, so users know ZMQ is required before running tests, and so they know how to install it. When following the build instructions before this change then trying to run the RPC tests, a unix user would get an error when python tried to import zmq. There may be other dependencies that should be added to the docs, particularly ones for non-unix systems. This is the only unlisted dependency I encountered using linux. --- qa/README.md | 11 +++++++++++ qa/pull-tester/rpc-tests.py | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/qa/README.md b/qa/README.md index 0e2b49cdd..168f66a03 100644 --- a/qa/README.md +++ b/qa/README.md @@ -5,6 +5,17 @@ Every pull request to the zcash repository is built and run through the regression test suite. You can also run all or only individual tests locally. +Test dependencies +================= +Before running the tests, the following must be installed. + +Unix +---- +The python-zmq library is required. On Ubuntu or Debian it can be installed via: +``` +sudo apt-get install python-zmq +``` + Running tests ============= diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 214967fae..d78ffefdd 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -40,6 +40,15 @@ if not vars().has_key('ENABLE_UTILS'): ENABLE_UTILS=0 if not vars().has_key('ENABLE_ZMQ'): ENABLE_ZMQ=0 + +# python-zmq may not be installed. Handle this gracefully and with some helpful info +if ENABLE_ZMQ: + try: + import zmq + except ImportError: + print("WARNING: \"import zmq\" failed. Setting ENABLE_ZMQ=0. " \ + "To run zmq tests, see dependency info in /qa/README.md.") + ENABLE_ZMQ=0 ENABLE_COVERAGE=0 From 65a6249d41eb972d341d444e6ebe3d8a61861690 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 19 Mar 2016 21:36:32 +0100 Subject: [PATCH 19/66] [qa] Use python2/3 syntax --- qa/pull-tester/rpc-tests.py | 8 ++++---- qa/rpc-tests/bipdersig-p2p.py | 4 ++-- qa/rpc-tests/fundrawtransaction.py | 2 +- qa/rpc-tests/rest.py | 4 ++-- qa/rpc-tests/smartfees.py | 2 +- qa/rpc-tests/test_framework/blockstore.py | 6 +++--- qa/rpc-tests/test_framework/mininode.py | 3 +-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index d78ffefdd..07ebf587b 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -32,13 +32,13 @@ import re from tests_config import * #If imported values are not defined then set to zero (or disabled) -if not vars().has_key('ENABLE_WALLET'): +if 'ENABLE_WALLET' not in vars(): ENABLE_WALLET=0 -if not vars().has_key('ENABLE_BITCOIND'): +if 'ENABLE_BITCOIND' not in vars(): ENABLE_BITCOIND=0 -if not vars().has_key('ENABLE_UTILS'): +if 'ENABLE_UTILS' not in vars(): ENABLE_UTILS=0 -if not vars().has_key('ENABLE_ZMQ'): +if 'ENABLE_ZMQ' not in vars(): ENABLE_ZMQ=0 # python-zmq may not be installed. Handle this gracefully and with some helpful info diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index b25872982..cca3aefd1 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -11,7 +11,7 @@ from test_framework.blocktools import create_coinbase, create_block from test_framework.comptool import TestInstance, TestManager from test_framework.script import CScript from binascii import unhexlify -import io +from io import BytesIO ''' @@ -47,7 +47,7 @@ class BIP66Test(ComparisonTestFramework): rawtx = node.createrawtransaction(inputs, outputs) signresult = node.signrawtransaction(rawtx) tx = CTransaction() - f = io.BytesIO(unhexlify(signresult['hex'])) + f = BytesIO(unhexlify(signresult['hex'])) tx.deserialize(f) return tx diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 4445d0ed7..938cb0041 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -210,7 +210,7 @@ class RawTransactionsTest(BitcoinTestFramework): matchingOuts = 0 for i, out in enumerate(dec_tx['vout']): totalOut += out['value'] - if out['scriptPubKey']['addresses'][0] in outputs : + if out['scriptPubKey']['addresses'][0] in outputs: matchingOuts+=1 else: assert_equal(i, rawtxfund['changepos']) diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index acc5d943f..f09eda0b9 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -14,7 +14,7 @@ from test_framework.util import assert_equal, assert_greater_than, \ import struct import binascii import json -import io +from io import BytesIO from codecs import encode from decimal import Decimal @@ -145,7 +145,7 @@ class RESTTest (BitcoinTestFramework): binaryRequest += struct.pack("i", 0); bin_response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', binaryRequest) - output = io.BytesIO() + output = BytesIO() output.write(bin_response) output.seek(0) chainHeight = struct.unpack("i", output.read(4))[0] diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 8caab8808..3f632696d 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -112,7 +112,7 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True): print([str(all_estimates[e-1]) for e in [1,2,3,6,15,25]]) delta = 1.0e-6 # account for rounding error last_e = max(fees_seen) - for e in filter(lambda x: x >= 0, all_estimates): + for e in [x for x in all_estimates if x >= 0]: # Estimates should be within the bounds of what transactions fees actually were: if float(e)+delta < min(fees_seen) or float(e)-delta > max(fees_seen): raise AssertionError("Estimated fee (%f) out of range (%f,%f)" diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py index 1288521e9..7e8dd2a5a 100644 --- a/qa/rpc-tests/test_framework/blockstore.py +++ b/qa/rpc-tests/test_framework/blockstore.py @@ -6,7 +6,7 @@ from .mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx import sys -import io +from io import BytesIO import dbm.dumb as dbm class BlockStore(): @@ -24,7 +24,7 @@ class BlockStore(): serialized_block = self.blockDB[repr(blockhash)] except KeyError: return None - f = io.BytesIO(serialized_block) + f = BytesIO(serialized_block) ret = CBlock() ret.deserialize(f) ret.calc_sha256() @@ -118,7 +118,7 @@ class TxStore(object): serialized_tx = self.txDB[repr(txhash)] except KeyError: return None - f = io.BytesIO(serialized_tx) + f = BytesIO(serialized_tx) ret = CTransaction() ret.deserialize(f) ret.calc_sha256() diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 04a7ef773..36f31e431 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -757,9 +757,8 @@ class CTransaction(object): self.calc_sha256() def calc_sha256(self): - serialized = self.serialize() if self.sha256 is None: - self.sha256 = uint256_from_str(hash256(serialized)) + self.sha256 = uint256_from_str(hash256(self.serialize())) self.hash = hash256(self.serialize())[::-1].hex() def is_valid(self): From 7108d6aaa6670f121a3f07962b4362a2fc499adb Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 20 Mar 2016 18:18:32 +0100 Subject: [PATCH 20/66] [qa] rpc-tests: Properly use integers, floats --- qa/rpc-tests/fundrawtransaction.py | 2 +- qa/rpc-tests/p2p-acceptblock.py | 2 +- qa/rpc-tests/pruning.py | 6 +++--- qa/rpc-tests/smartfees.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 938cb0041..e98949abf 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -49,7 +49,7 @@ class RawTransactionsTest(BitcoinTestFramework): watchonly_address = self.nodes[0].getnewaddress() watchonly_pubkey = self.nodes[0].validateaddress(watchonly_address)["pubkey"] - watchonly_amount = 200 + watchonly_amount = Decimal(200) self.nodes[3].importpubkey(watchonly_pubkey, "", True) watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount) self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10) diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index b24508eee..b36c9ccd2 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -156,7 +156,7 @@ class AcceptBlockTest(BitcoinTestFramework): # 2. Send one block that builds on each tip. # This should be accepted. blocks_h2 = [] # the height 2 blocks on each node's chain - block_time = time.time() + 1 + block_time = int(time.time()) + 1 for i in range(2): blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index bcc12fd5f..7b1be9a4f 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -20,7 +20,7 @@ import os.path import time def calc_usage(blockdir): - return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f))/(1024*1024) + return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) class PruneTest(BitcoinTestFramework): @@ -78,7 +78,7 @@ class PruneTest(BitcoinTestFramework): self.nodes[1].generate(200) sync_blocks(self.nodes[0:2]) self.nodes[0].generate(150) - # Then mine enough full blocks to create more than 550MB of data + # Then mine enough full blocks to create more than 550MiB of data for i in range(645): self.mine_full_block(self.nodes[0], self.address[0]) @@ -88,7 +88,7 @@ class PruneTest(BitcoinTestFramework): if not os.path.isfile(self.prunedir+"blk00000.dat"): raise AssertionError("blk00000.dat is missing, pruning too early") print("Success") - print("Though we're already using more than 550MB, current usage:", calc_usage(self.prunedir)) + print("Though we're already using more than 550MiB, current usage:", calc_usage(self.prunedir)) print("Mining 25 more blocks should cause the first block file to be pruned") # Pruning doesn't run until we're allocating another chunk, 20 full blocks past the height cutoff will ensure this for i in range(25): diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 3f632696d..0eaee5ae2 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -215,7 +215,7 @@ class EstimateFeeTest(BitcoinTestFramework): from_index = random.randint(1,2) (txhex, fee) = small_txpuzzle_randfee(self.nodes[from_index], self.confutxo, self.memutxo, Decimal("0.005"), min_fee, min_fee) - tx_kbytes = (len(txhex)/2)/1000.0 + tx_kbytes = (len(txhex) // 2) / 1000.0 self.fees_per_kb.append(float(fee)/tx_kbytes) sync_mempools(self.nodes[0:3],.1) mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"] From db761077e542e3de08d18e56a20831274c42ae60 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 31 Mar 2016 18:33:15 +0200 Subject: [PATCH 21/66] [qa] mininode: Catch exceptions in got_data --- qa/rpc-tests/test_framework/mininode.py | 73 +++++++++++++------------ 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 36f31e431..6df191c90 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1606,43 +1606,46 @@ class NodeConn(asyncore.dispatcher): self.sendbuf = self.sendbuf[sent:] def got_data(self): - while True: - if len(self.recvbuf) < 4: - return - if self.recvbuf[:4] != self.MAGIC_BYTES[self.network]: - raise ValueError("got garbage %r" % (self.recvbuf,)) - if self.ver_recv < 209: - if len(self.recvbuf) < 4 + 12 + 4: + try: + while True: + if len(self.recvbuf) < 4: return - command = self.recvbuf[4:4+12].split(b"\x00", 1)[0] - msglen = struct.unpack(" Date: Sat, 9 Apr 2016 21:14:18 +0200 Subject: [PATCH 22/66] [qa] pull-tester: Don't mute zmq ImportError --- qa/pull-tester/rpc-tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 07ebf587b..10b3f1064 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -45,10 +45,10 @@ if 'ENABLE_ZMQ' not in vars(): if ENABLE_ZMQ: try: import zmq - except ImportError: - print("WARNING: \"import zmq\" failed. Setting ENABLE_ZMQ=0. " \ - "To run zmq tests, see dependency info in /qa/README.md.") - ENABLE_ZMQ=0 + except ImportError as e: + print("WARNING: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ + "to run zmq tests, see dependency info in /qa/README.md.") + raise e ENABLE_COVERAGE=0 From cc4436831504d1d1b0cd0571da205b7ab9e5a394 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 9 Apr 2016 22:17:52 +0200 Subject: [PATCH 23/66] [qa] pull-tester: Exit early when no tests are run --- qa/pull-tester/rpc-tests.py | 110 ++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 10b3f1064..0c2875779 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -40,15 +40,6 @@ if 'ENABLE_UTILS' not in vars(): ENABLE_UTILS=0 if 'ENABLE_ZMQ' not in vars(): ENABLE_ZMQ=0 - -# python-zmq may not be installed. Handle this gracefully and with some helpful info -if ENABLE_ZMQ: - try: - import zmq - except ImportError as e: - print("WARNING: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ - "to run zmq tests, see dependency info in /qa/README.md.") - raise e ENABLE_COVERAGE=0 @@ -76,11 +67,24 @@ if "BITCOIND" not in os.environ: if "BITCOINCLI" not in os.environ: os.environ["BITCOINCLI"] = buildDir + '/src/zcash-cli' + EXEEXT -#Disable Windows tests by default if EXEEXT == ".exe" and "-win" not in opts: - print "Win tests currently disabled. Use -win option to enable" + # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 + print "Win tests currently disabled by default. Use -win option to enable" sys.exit(0) +if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): + print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" + sys.exit(0) + +# python-zmq may not be installed. Handle this gracefully and with some helpful info +if ENABLE_ZMQ: + try: + import zmq + except ImportError as e: + print("WARNING: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ + "to run zmq tests, see dependency info in /qa/README.md.") + raise e + #Tests testScripts = [ 'paymentdisclosure.py', @@ -167,6 +171,9 @@ testScripts = [ 'remove_sprout_shielding.py', 'feature_walletfile.py', ] +if ENABLE_ZMQ: + testScripts.append('zmq_test.py') + testScriptsExt = [ 'getblocktemplate_longpoll.py', 'getblocktemplate_proposals.py', @@ -184,11 +191,6 @@ testScriptsExt = [ 'wallet_db_flush.py', ] -#Enable ZMQ tests -if ENABLE_ZMQ == 1: - testScripts.append('zmq_test.py') - - def runtests(): coverage = None @@ -196,53 +198,49 @@ def runtests(): coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) - if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): - rpcTestDir = buildDir + '/qa/rpc-tests/' - run_extended = '-extended' in opts - cov_flag = coverage.flag if coverage else '' - flags = " --srcdir %s/src %s %s" % (buildDir, cov_flag, passOn) + rpcTestDir = buildDir + '/qa/rpc-tests/' + run_extended = '-extended' in opts + cov_flag = coverage.flag if coverage else '' + flags = " --srcdir %s/src %s %s" % (buildDir, cov_flag, passOn) - #Run Tests - for i in range(len(testScripts)): - if (len(opts) == 0 - or (len(opts) == 1 and "-win" in opts ) - or run_extended - or testScripts[i] in opts - or re.sub(".py$", "", testScripts[i]) in opts ): + #Run Tests + for i in range(len(testScripts)): + if (len(opts) == 0 + or (len(opts) == 1 and "-win" in opts ) + or run_extended + or testScripts[i] in opts + or re.sub(".py$", "", testScripts[i]) in opts ): - print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) - time0 = time.time() - subprocess.check_call( - rpcTestDir + testScripts[i] + flags, shell=True) - print("Duration: %s s\n" % (int(time.time() - time0))) + print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) + time0 = time.time() + subprocess.check_call( + rpcTestDir + testScripts[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) - # exit if help is called so we print just one set of - # instructions - p = re.compile(" -h| --help") - if p.match(passOn): - sys.exit(0) + # exit if help is called so we print just one set of + # instructions + p = re.compile(" -h| --help") + if p.match(passOn): + sys.exit(0) - # Run Extended Tests - for i in range(len(testScriptsExt)): - if (run_extended or testScriptsExt[i] in opts - or re.sub(".py$", "", testScriptsExt[i]) in opts): + # Run Extended Tests + for i in range(len(testScriptsExt)): + if (run_extended or testScriptsExt[i] in opts + or re.sub(".py$", "", testScriptsExt[i]) in opts): - print( - "Running 2nd level testscript " - + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) - time0 = time.time() - subprocess.check_call( - rpcTestDir + testScriptsExt[i] + flags, shell=True) - print("Duration: %s s\n" % (int(time.time() - time0))) + print( + "Running 2nd level testscript " + + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) + time0 = time.time() + subprocess.check_call( + rpcTestDir + testScriptsExt[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) - if coverage: - coverage.report_rpc_coverage() + if coverage: + coverage.report_rpc_coverage() - print("Cleaning up coverage data") - coverage.cleanup() - - else: - print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" + print("Cleaning up coverage data") + coverage.cleanup() class RPCCoverage(object): From d362b632bf979d602e391c0386cfc1ae2e4201b6 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 15 Apr 2016 11:25:19 +0200 Subject: [PATCH 24/66] [qa] rpc-tests: Fix link in comment and label error msg --- qa/pull-tester/rpc-tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 0c2875779..fa4769864 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -69,6 +69,7 @@ if "BITCOINCLI" not in os.environ: if EXEEXT == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 + # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 print "Win tests currently disabled by default. Use -win option to enable" sys.exit(0) @@ -81,7 +82,7 @@ if ENABLE_ZMQ: try: import zmq except ImportError as e: - print("WARNING: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ + print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ "to run zmq tests, see dependency info in /qa/README.md.") raise e From 15721275d0eb66344d5028755c530468a51c2db0 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 19 Mar 2016 20:58:06 +0100 Subject: [PATCH 25/66] [qa] Switch to py3 Zcash: We already did this, so most of the backported changes are ancillary (license header updates and style), but a few are relevant. --- doc/zmq.md | 2 +- qa/README.md | 4 +-- qa/pull-tester/rpc-tests.py | 10 +++---- qa/pull-tester/tests_config.py.in | 6 ++-- qa/rpc-tests/bip65-cltv-p2p.py | 5 ++-- qa/rpc-tests/bipdersig-p2p.py | 5 ++-- qa/rpc-tests/blockchain.py | 2 +- qa/rpc-tests/decodescript.py | 2 +- qa/rpc-tests/disablewallet.py | 2 +- qa/rpc-tests/forknotify.py | 2 +- qa/rpc-tests/fundrawtransaction.py | 4 +-- qa/rpc-tests/getblocktemplate_longpoll.py | 2 +- qa/rpc-tests/getblocktemplate_proposals.py | 10 +++---- qa/rpc-tests/getchaintips.py | 2 +- qa/rpc-tests/httpbasics.py | 30 +++++++++---------- qa/rpc-tests/invalidateblock.py | 2 +- qa/rpc-tests/invalidblockrequest.py | 5 ++-- qa/rpc-tests/keypool.py | 2 +- qa/rpc-tests/listtransactions.py | 2 +- qa/rpc-tests/maxblocksinflight.py | 3 +- qa/rpc-tests/mempool_limit.py | 2 +- qa/rpc-tests/mempool_reorg.py | 2 +- qa/rpc-tests/mempool_resurrect_test.py | 2 +- qa/rpc-tests/mempool_spendcoinbase.py | 2 +- qa/rpc-tests/merkle_blocks.py | 2 +- qa/rpc-tests/multi_rpc.py | 21 ++++++------- qa/rpc-tests/nodehandling.py | 6 ++-- qa/rpc-tests/p2p-acceptblock.py | 5 ++-- qa/rpc-tests/p2p-fullblocktest.py | 8 ++--- qa/rpc-tests/proxy_test.py | 2 +- qa/rpc-tests/pruning.py | 4 +-- qa/rpc-tests/rawtransactions.py | 2 +- qa/rpc-tests/receivedby.py | 2 +- qa/rpc-tests/reindex.py | 2 +- qa/rpc-tests/rest.py | 13 ++++---- qa/rpc-tests/rpcbind_test.py | 2 +- qa/rpc-tests/signrawtransactions.py | 2 +- qa/rpc-tests/smartfees.py | 2 +- qa/rpc-tests/test_framework/bignum.py | 2 +- qa/rpc-tests/test_framework/blockstore.py | 7 +++-- qa/rpc-tests/test_framework/blocktools.py | 4 +-- qa/rpc-tests/test_framework/comptool.py | 3 +- qa/rpc-tests/test_framework/coverage.py | 5 ++++ qa/rpc-tests/test_framework/mininode.py | 15 +++++++--- qa/rpc-tests/test_framework/netutil.py | 2 +- qa/rpc-tests/test_framework/script.py | 9 ++++-- qa/rpc-tests/test_framework/socks5.py | 2 +- qa/rpc-tests/test_framework/test_framework.py | 4 +-- qa/rpc-tests/test_framework/util.py | 3 +- qa/rpc-tests/txn_doublespend.py | 2 +- qa/rpc-tests/wallet.py | 2 +- qa/rpc-tests/walletbackup.py | 2 +- qa/rpc-tests/zapwallettxes.py | 2 +- qa/rpc-tests/zmq_test.py | 2 +- 54 files changed, 128 insertions(+), 121 deletions(-) diff --git a/doc/zmq.md b/doc/zmq.md index e7e5469bf..0db560ac7 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -40,7 +40,7 @@ Typically, it is packaged by distributions as something like *libzmq5-dev*. The C++ wrapper for ZeroMQ is *not* needed. In order to run the example Python client scripts in contrib/ one must -also install *python-zmq*, though this is not necessary for daemon +also install *python3-zmq*, though this is not necessary for daemon operation. ## Security WARNING diff --git a/qa/README.md b/qa/README.md index 168f66a03..fe57ed2dd 100644 --- a/qa/README.md +++ b/qa/README.md @@ -11,9 +11,9 @@ Before running the tests, the following must be installed. Unix ---- -The python-zmq library is required. On Ubuntu or Debian it can be installed via: +The python3-zmq library is required. On Ubuntu or Debian it can be installed via: ``` -sudo apt-get install python-zmq +sudo apt-get install python3-zmq ``` Running tests diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index fa4769864..efbe80f21 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +#!/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. @@ -70,14 +70,14 @@ if "BITCOINCLI" not in os.environ: if EXEEXT == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 - print "Win tests currently disabled by default. Use -win option to enable" + print("Win tests currently disabled by default. Use -win option to enable") sys.exit(0) if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): - print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" + print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") sys.exit(0) -# python-zmq may not be installed. Handle this gracefully and with some helpful info +# python3-zmq may not be installed. Handle this gracefully and with some helpful info if ENABLE_ZMQ: try: import zmq diff --git a/qa/pull-tester/tests_config.py.in b/qa/pull-tester/tests_config.py.in index d2c667eaf..601bbd571 100644 --- a/qa/pull-tester/tests_config.py.in +++ b/qa/pull-tester/tests_config.py.in @@ -1,5 +1,5 @@ -#!/usr/bin/env python2 -# Copyright (c) 2013-2014 The Bitcoin Core developers +#!/usr/bin/env python3 +# Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -11,5 +11,3 @@ EXEEXT="@EXEEXT@" @BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1 @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1 @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1 - - diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 506b9fd50..87796d5e1 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -# +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . -# from test_framework.test_framework import ComparisonTestFramework from test_framework.util import start_nodes @@ -66,7 +65,7 @@ class BIP65Test(ComparisonTestFramework): height = 102 # height of the next block to build hashTip = self.nodes[0].getbestblockhash() hashFinalSaplingRoot = int("0x" + self.nodes[0].getblock(hashTip)['finalsaplingroot'] , 0) - self.tip = int ("0x" + hashTip , 0) + self.tip = int("0x" + hashTip , 0) self.nodeaddress = self.nodes[0].getnewaddress() '''Check that the rules are enforced.''' diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index cca3aefd1..ec585f6e9 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -# +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . -# from test_framework.test_framework import ComparisonTestFramework from test_framework.util import start_nodes @@ -74,7 +73,7 @@ class BIP66Test(ComparisonTestFramework): height = 102 # height of the next block to build hashTip = self.nodes[0].getbestblockhash() hashFinalSaplingRoot = int("0x" + self.nodes[0].getblock(hashTip)['finalsaplingroot'], 0) - self.tip = int ("0x"+hashTip , 0) + self.tip = int("0x"+hashTip , 0) self.nodeaddress = self.nodes[0].getnewaddress() '''Check that the rules are enforced.''' diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py index cea3267e8..7c1bac285 100755 --- a/qa/rpc-tests/blockchain.py +++ b/qa/rpc-tests/blockchain.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index b44ddec7f..60cdaa988 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 The Bitcoin Core developers +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/qa/rpc-tests/disablewallet.py b/qa/rpc-tests/disablewallet.py index 6e2e516b0..3b32b26ae 100755 --- a/qa/rpc-tests/disablewallet.py +++ b/qa/rpc-tests/disablewallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index 195d64103..2374c9dea 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index e98949abf..708c69b27 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -15,7 +15,7 @@ from decimal import Decimal class RawTransactionsTest(BitcoinTestFramework): def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) + print(("Initializing test directory "+self.options.tmpdir)) initialize_chain_clean(self.options.tmpdir, 4) def setup_network(self, split=False): diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index ec0a84053..c2e32b4db 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index 9e07aaa97..bce1b1784 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -68,7 +68,7 @@ def genmrklroot(leaflist): cur = n return cur[0] -def template_to_bytes(tmpl, txlist): +def template_to_bytearray(tmpl, txlist): blkver = pack(' Date: Wed, 27 Apr 2016 22:29:52 +0200 Subject: [PATCH 26/66] [qa] Refactor test_framework and pull tester * log to stdout * increase range for p2p and rpc ports * UPPERCASE_CONSTANTS * Stop nodes on CTRL+C --- qa/pull-tester/rpc-tests.py | 83 +++++++++---------- qa/rpc-tests/test_framework/test_framework.py | 4 +- qa/rpc-tests/test_framework/util.py | 17 +++- qa/rpc-tests/walletbackup.py | 3 +- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index efbe80f21..a817b4c1a 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -31,6 +31,14 @@ import re from tests_config import * +BOLD = ("","") +if os.name == 'posix': + # primitive formatting on supported + # terminal via ANSI escape sequences: + BOLD = ('\033[0m', '\033[1m') + +RPC_TESTS_DIR = BUILDDIR + '/qa/rpc-tests/' + #If imported values are not defined then set to zero (or disabled) if 'ENABLE_WALLET' not in vars(): ENABLE_WALLET=0 @@ -43,29 +51,29 @@ if 'ENABLE_ZMQ' not in vars(): ENABLE_COVERAGE=0 -#Create a set to store arguments and create the passOn string +#Create a set to store arguments and create the passon string opts = set() -passOn = "" -p = re.compile("^--") +passon_args = "" +PASSON_REGEX = re.compile("^--") -bold = ("","") -if (os.name == 'posix'): - bold = ('\033[0m', '\033[1m') +print_help = False for arg in sys.argv[1:]: + if arg == "--help" or arg == "-h" or arg == "-?": + print_help = True + break if arg == '--coverage': ENABLE_COVERAGE = 1 - elif (p.match(arg) or arg == "-h"): - passOn += " " + arg + elif PASSON_REGEX.match(arg): + passon_args += " " + arg else: opts.add(arg) #Set env vars -buildDir = BUILDDIR if "BITCOIND" not in os.environ: - os.environ["BITCOIND"] = buildDir + '/src/zcashd' + EXEEXT + os.environ["BITCOIND"] = BUILDDIR + '/src/zcashd' + EXEEXT if "BITCOINCLI" not in os.environ: - os.environ["BITCOINCLI"] = buildDir + '/src/zcash-cli' + EXEEXT + os.environ["BITCOINCLI"] = BUILDDIR + '/src/zcash-cli' + EXEEXT if EXEEXT == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 @@ -193,48 +201,35 @@ testScriptsExt = [ ] def runtests(): + test_list = [] + if '-extended' in opts: + test_list = testScripts + testScriptsExt + elif len(opts) == 0 or (len(opts) == 1 and "-win" in opts): + test_list = testScripts + else: + for t in testScripts + testScriptsExt: + if t in opts or re.sub(".py$", "", t) in opts: + test_list.append(t) + + if print_help: + # Help should be the same for all scripts, so just + # call the first and exit + subprocess.check_call(RPC_TESTS_DIR + test_list[0] + ' -h', shell=True) + sys.exit(0) + coverage = None if ENABLE_COVERAGE: coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) - - rpcTestDir = buildDir + '/qa/rpc-tests/' - run_extended = '-extended' in opts - cov_flag = coverage.flag if coverage else '' - flags = " --srcdir %s/src %s %s" % (buildDir, cov_flag, passOn) + flags = " --srcdir %s/src %s %s" % (BUILDDIR, coverage.flag if coverage else '', passon_args) #Run Tests - for i in range(len(testScripts)): - if (len(opts) == 0 - or (len(opts) == 1 and "-win" in opts ) - or run_extended - or testScripts[i] in opts - or re.sub(".py$", "", testScripts[i]) in opts ): - - print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) + for t in test_list: + print("Running testscript %s%s%s ..." % (BOLD[1], t, BOLD[0])) time0 = time.time() subprocess.check_call( - rpcTestDir + testScripts[i] + flags, shell=True) - print("Duration: %s s\n" % (int(time.time() - time0))) - - # exit if help is called so we print just one set of - # instructions - p = re.compile(" -h| --help") - if p.match(passOn): - sys.exit(0) - - # Run Extended Tests - for i in range(len(testScriptsExt)): - if (run_extended or testScriptsExt[i] in opts - or re.sub(".py$", "", testScriptsExt[i]) in opts): - - print( - "Running 2nd level testscript " - + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) - time0 = time.time() - subprocess.check_call( - rpcTestDir + testScriptsExt[i] + flags, shell=True) + RPC_TESTS_DIR + t + flags, shell=True) print("Duration: %s s\n" % (int(time.time() - time0))) if coverage: diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 0681ae27b..b1ac8e8fe 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -115,7 +115,7 @@ class BitcoinTestFramework(object): if self.options.trace_rpc: import logging - logging.basicConfig(level=logging.DEBUG) + logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) if self.options.coveragedir: enable_coverage(self.options.coveragedir) @@ -148,6 +148,8 @@ class BitcoinTestFramework(object): except Exception as e: print("Unexpected exception caught during testing: "+str(e)) traceback.print_tb(sys.exc_info()[2]) + except KeyboardInterrupt as e: + print("Exiting after " + repr(e)) if not self.options.noshutdown: print("Stopping nodes") diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 187cffeda..0bf4b6a2d 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -38,6 +38,13 @@ BLOSSOM_BRANCH_ID = 0x2BB40E60 HEARTWOOD_BRANCH_ID = 0xF5B9230B CANOPY_BRANCH_ID = 0xE9FF75A6 +# The maximum number of nodes a single test can spawn +MAX_NODES = 8 +# Don't assign rpc or p2p ports lower than this +PORT_MIN = 11000 +# The number of ports to "reserve" for p2p and rpc, each +PORT_RANGE = 5000 + def enable_coverage(dirname): """Maintain a log of which RPC calls are made during testing.""" global COVERAGE_DIR @@ -71,9 +78,11 @@ def get_rpc_proxy(url, node_number, timeout=None): def p2p_port(n): - return 11000 + n + os.getpid()%999 + assert(n <= MAX_NODES) + return PORT_MIN + n + (MAX_NODES * os.getpid()) % (PORT_RANGE - 1 - MAX_NODES) + def rpc_port(n): - return 12000 + n + os.getpid()%999 + return PORT_MIN + PORT_RANGE + n + (MAX_NODES * os.getpid()) % (PORT_RANGE -1 - MAX_NODES) def check_json_precision(): """Make sure json library being used does not lose precision converting BTC values""" @@ -345,8 +354,8 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None): """ Start multiple bitcoinds, return RPC connections to them """ - if extra_args is None: extra_args = [ None for i in range(num_nodes) ] - if binary is None: binary = [ None for i in range(num_nodes) ] + if extra_args is None: extra_args = [ None for _ in range(num_nodes) ] + if binary is None: binary = [ None for _ in range(num_nodes) ] rpcs = [] try: for i in range(num_nodes): diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index 046b3230b..d22a26088 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -44,8 +44,9 @@ import shutil from random import randint from decimal import Decimal import logging +import sys -logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout) class WalletBackupTest(BitcoinTestFramework): From 065255d3cb05c92d93ca7a7dfc4e54753bf9c942 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 30 Apr 2016 00:03:06 +0200 Subject: [PATCH 27/66] [qa] Update README.md --- qa/README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/qa/README.md b/qa/README.md index fe57ed2dd..0e58365e5 100644 --- a/qa/README.md +++ b/qa/README.md @@ -19,15 +19,25 @@ sudo apt-get install python3-zmq Running tests ============= -You can run any single test by calling `qa/pull-tester/rpc-tests.py `. +You can run any single test by calling -Or you can run any combination of tests by calling `qa/pull-tester/rpc-tests.py ...` + qa/pull-tester/rpc-tests.py -Run the regression test suite with `qa/pull-tester/rpc-tests.py` +Or you can run any combination of tests by calling -Run all possible tests with `qa/pull-tester/rpc-tests.py -extended` + qa/pull-tester/rpc-tests.py ... -Possible options: +Run the regression test suite with + + qa/pull-tester/rpc-tests.py + +Run all possible tests with + + qa/pull-tester/rpc-tests.py -extended + +If you want to create a basic coverage report for the rpc test suite, append `--coverage`. + +Possible options, which apply to each individual test run: ``` -h, --help show this help message and exit From 5f13446dee7ef10c54a2a468048e9c5bb282a651 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 30 Apr 2016 14:55:31 +0200 Subject: [PATCH 28/66] [qa] Stop other nodes, even when one fails to stop --- qa/rpc-tests/test_framework/util.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 0bf4b6a2d..c1785f062 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -16,6 +16,7 @@ from binascii import hexlify, unhexlify from base64 import b64encode from decimal import Decimal, ROUND_DOWN import json +import http.client import random import shutil import subprocess @@ -373,13 +374,19 @@ def check_node(i): return bitcoind_processes[i].returncode def stop_node(node, i): - node.stop() + try: + node.stop() + except http.client.CannotSendRequest as e: + print("WARN: Unable to stop node: " + repr(e)) bitcoind_processes[i].wait() del bitcoind_processes[i] def stop_nodes(nodes): for node in nodes: - node.stop() + try: + node.stop() + except http.client.CannotSendRequest as e: + print("WARN: Unable to stop node: " + repr(e)) del nodes[:] # Emptying array closes connections as a side effect def set_node_times(nodes, t): From 0d8a62925ae26fb4d1290ce6db63c5cbb3d710ff Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 8 May 2016 14:09:49 +0200 Subject: [PATCH 29/66] [qa] pull-tester: Adjust comment --- qa/pull-tester/rpc-tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index a817b4c1a..e27d0cff1 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -212,8 +212,7 @@ def runtests(): test_list.append(t) if print_help: - # Help should be the same for all scripts, so just - # call the first and exit + # Only print help of the first script and exit subprocess.check_call(RPC_TESTS_DIR + test_list[0] + ' -h', shell=True) sys.exit(0) From cbe91ea85529f53bd2616e803bd4fa14726c28ed Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 29 Apr 2016 12:51:15 +0200 Subject: [PATCH 30/66] [qa] pull-tester: Run rpc test in parallel --- qa/README.md | 3 ++ qa/pull-tester/rpc-tests.py | 90 +++++++++++++++++++++++++++++++----- qa/rpc-tests/create_cache.py | 23 +++++++++ 3 files changed, 104 insertions(+), 12 deletions(-) create mode 100755 qa/rpc-tests/create_cache.py diff --git a/qa/README.md b/qa/README.md index 0e58365e5..e5f881f3a 100644 --- a/qa/README.md +++ b/qa/README.md @@ -35,6 +35,9 @@ Run all possible tests with qa/pull-tester/rpc-tests.py -extended +By default, tests will be run in parallel if you want to specify how many +tests should be run in parallel, append `-paralell=n` (default n=4). + If you want to create a basic coverage report for the rpc test suite, append `--coverage`. Possible options, which apply to each individual test run: diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index e27d0cff1..1e630a340 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -53,10 +53,12 @@ ENABLE_COVERAGE=0 #Create a set to store arguments and create the passon string opts = set() -passon_args = "" +passon_args = [] PASSON_REGEX = re.compile("^--") +PARALLEL_REGEX = re.compile('^-parallel=') print_help = False +run_parallel = 4 for arg in sys.argv[1:]: if arg == "--help" or arg == "-h" or arg == "-?": @@ -65,7 +67,9 @@ for arg in sys.argv[1:]: if arg == '--coverage': ENABLE_COVERAGE = 1 elif PASSON_REGEX.match(arg): - passon_args += " " + arg + passon_args.append(arg) + elif PARALLEL_REGEX.match(arg): + run_parallel = int(arg.split(sep='=', maxsplit=1)[1]) else: opts.add(arg) @@ -96,6 +100,7 @@ if ENABLE_ZMQ: #Tests testScripts = [ + 'walletbackup.py', 'paymentdisclosure.py', 'prioritisetransaction.py', 'wallet_treestate.py', @@ -140,7 +145,6 @@ testScripts = [ 'fundrawtransaction.py', 'signrawtransactions.py', 'signrawtransaction_offline.py', - 'walletbackup.py', 'key_import_export.py', 'nodehandling.py', 'reindex.py', @@ -200,6 +204,7 @@ testScriptsExt = [ 'wallet_db_flush.py', ] + def runtests(): test_list = [] if '-extended' in opts: @@ -213,7 +218,7 @@ def runtests(): if print_help: # Only print help of the first script and exit - subprocess.check_call(RPC_TESTS_DIR + test_list[0] + ' -h', shell=True) + subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h']) sys.exit(0) coverage = None @@ -221,15 +226,34 @@ def runtests(): if ENABLE_COVERAGE: coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) - flags = " --srcdir %s/src %s %s" % (BUILDDIR, coverage.flag if coverage else '', passon_args) + flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args + if coverage: + flags.append(coverage.flag) + + if len(test_list) > 1: + # Populate cache + subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags) #Run Tests - for t in test_list: - print("Running testscript %s%s%s ..." % (BOLD[1], t, BOLD[0])) - time0 = time.time() - subprocess.check_call( - RPC_TESTS_DIR + t + flags, shell=True) - print("Duration: %s s\n" % (int(time.time() - time0))) + max_len_name = len(max(test_list, key=len)) + time_sum = 0 + time0 = time.time() + job_queue = RPCTestHandler(run_parallel, test_list, flags) + results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0] + all_passed = True + for _ in range(len(test_list)): + (name, stdout, stderr, passed, duration) = job_queue.get_next() + all_passed = all_passed and passed + time_sum += duration + + print('\n' + BOLD[1] + name + BOLD[0] + ":") + print(stdout) + print('stderr:\n' if not stderr == '' else '', stderr) + results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) + print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration)) + results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0] + print(results) + print("\nRuntime: %s s" % (int(time.time() - time0))) if coverage: coverage.report_rpc_coverage() @@ -237,6 +261,48 @@ def runtests(): print("Cleaning up coverage data") coverage.cleanup() + sys.exit(not all_passed) + + +class RPCTestHandler: + """ + Trigger the testscrips passed in via the list. + """ + + def __init__(self, num_tests_parallel, test_list=None, flags=None): + assert(num_tests_parallel >= 1) + self.num_jobs = num_tests_parallel + self.test_list = test_list + self.flags = flags + self.num_running = 0 + self.jobs = [] + + def get_next(self): + while self.num_running < self.num_jobs and self.test_list: + # Add tests + self.num_running += 1 + t = self.test_list.pop(0) + self.jobs.append((t, + time.time(), + subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE))) + if not self.jobs: + raise IndexError('%s from empty list' % __name__) + while True: + # Return first proc that finishes + time.sleep(.5) + for j in self.jobs: + (name, time0, proc) = j + if proc.poll() is not None: + (stdout, stderr) = proc.communicate(timeout=3) + passed = stderr == "" and proc.returncode == 0 + self.num_running -= 1 + self.jobs.remove(j) + return name, stdout, stderr, passed, int(time.time() - time0) + print('.', end='', flush=True) + class RPCCoverage(object): """ @@ -255,7 +321,7 @@ class RPCCoverage(object): """ def __init__(self): self.dir = tempfile.mkdtemp(prefix="coverage") - self.flag = '--coveragedir %s' % self.dir + self.flag = '--coveragedir=%s' % self.dir def report_rpc_coverage(self): """ diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py new file mode 100755 index 000000000..b6161e091 --- /dev/null +++ b/qa/rpc-tests/create_cache.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Copyright (c) 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. + +# +# Helper script to create the cache +# (see BitcoinTestFramework.setup_chain) +# + +from test_framework.test_framework import BitcoinTestFramework + +class CreateCache(BitcoinTestFramework): + + def setup_network(self): + # Don't setup any test nodes + self.options.noshutdown = True + + def run_test(self): + pass + +if __name__ == '__main__': + CreateCache().main() From 5a12bc9d83202e1e06eab79aa93fbd9e5ec4a480 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 9 May 2016 19:55:49 +0200 Subject: [PATCH 31/66] [qa] Add option --portseed to test_framework --- qa/pull-tester/rpc-tests.py | 3 ++- qa/rpc-tests/test_framework/test_framework.py | 11 +++++++---- qa/rpc-tests/test_framework/util.py | 10 +++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 1e630a340..3f0660981 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -282,9 +282,10 @@ class RPCTestHandler: # Add tests self.num_running += 1 t = self.test_list.pop(0) + port_seed = ["--portseed=%s" % len(self.test_list)] self.jobs.append((t, time.time(), - subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags, + subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE))) diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index b1ac8e8fe..a8a3da5e8 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -5,10 +5,10 @@ # Base class for RPC testing -# Add python-bitcoinrpc to module search path: +import logging +import optparse import os import sys - import shutil import tempfile import traceback @@ -26,6 +26,7 @@ from .util import ( enable_coverage, check_json_precision, initialize_chain_clean, + PortSeed, ) @@ -95,7 +96,6 @@ class BitcoinTestFramework(object): self.setup_network(False) def main(self): - import optparse parser = optparse.OptionParser(usage="%prog [options]") parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", @@ -108,18 +108,21 @@ class BitcoinTestFramework(object): help="Root directory for datadirs") parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true", help="Print out all RPC calls as they are made") + parser.add_option("--portseed", dest="port_seed", default=os.getpid(), type='int', + help="The seed to use for assigning port numbers (default: current process id)") parser.add_option("--coveragedir", dest="coveragedir", help="Write tested RPC commands into this directory") self.add_options(parser) (self.options, self.args) = parser.parse_args() if self.options.trace_rpc: - import logging logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) if self.options.coveragedir: enable_coverage(self.options.coveragedir) + PortSeed.n = self.options.port_seed + os.environ['PATH'] = self.options.srcdir+":"+os.environ['PATH'] check_json_precision() diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c1785f062..f05a2edd5 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -8,7 +8,6 @@ # Helpful routines for regression testing # -# Add python-bitcoinrpc to module search path: import os import sys @@ -46,6 +45,11 @@ PORT_MIN = 11000 # The number of ports to "reserve" for p2p and rpc, each PORT_RANGE = 5000 + +class PortSeed: + # Must be initialized with a unique integer for each process + n = None + def enable_coverage(dirname): """Maintain a log of which RPC calls are made during testing.""" global COVERAGE_DIR @@ -80,10 +84,10 @@ def get_rpc_proxy(url, node_number, timeout=None): def p2p_port(n): assert(n <= MAX_NODES) - return PORT_MIN + n + (MAX_NODES * os.getpid()) % (PORT_RANGE - 1 - MAX_NODES) + return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES) def rpc_port(n): - return PORT_MIN + PORT_RANGE + n + (MAX_NODES * os.getpid()) % (PORT_RANGE -1 - MAX_NODES) + return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES) def check_json_precision(): """Make sure json library being used does not lose precision converting BTC values""" From 818d2de39baa3d76f34c275fc1273147958cd8c2 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 14 May 2016 13:01:31 +0200 Subject: [PATCH 32/66] [qa] Remove hardcoded "4 nodes" from test_framework Zcash: Applied changes to our RPC tests. --- qa/rpc-tests/addressindex.py | 12 +++---- qa/rpc-tests/bip65-cltv-p2p.py | 3 +- qa/rpc-tests/bipdersig-p2p.py | 3 +- qa/rpc-tests/blockchain.py | 10 +++--- qa/rpc-tests/coinbase_funding_streams.py | 8 ++--- qa/rpc-tests/decodescript.py | 11 ++++--- qa/rpc-tests/disablewallet.py | 11 ++++--- qa/rpc-tests/feature_zip221.py | 12 +++---- qa/rpc-tests/finalsaplingroot.py | 12 +++---- qa/rpc-tests/forknotify.py | 5 +++ qa/rpc-tests/framework.py | 8 ++--- qa/rpc-tests/fundrawtransaction.py | 13 ++++---- qa/rpc-tests/getblocktemplate.py | 11 ++++--- qa/rpc-tests/getblocktemplate_longpoll.py | 5 +++ qa/rpc-tests/getblocktemplate_proposals.py | 10 ++++++ qa/rpc-tests/getchaintips.py | 5 ++- qa/rpc-tests/getrawtransaction_insight.py | 12 +++---- qa/rpc-tests/httpbasics.py | 11 +++++-- qa/rpc-tests/invalidateblock.py | 9 ++--- qa/rpc-tests/invalidblockrequest.py | 1 + qa/rpc-tests/key_import_export.py | 11 ++++--- qa/rpc-tests/keypool.py | 13 ++++---- qa/rpc-tests/listtransactions.py | 4 +++ qa/rpc-tests/maxblocksinflight.py | 12 +++---- qa/rpc-tests/mempool_limit.py | 16 ++++----- qa/rpc-tests/mempool_nu_activation.py | 11 ++++--- qa/rpc-tests/mempool_reorg.py | 4 +++ qa/rpc-tests/mempool_resurrect_test.py | 5 +++ qa/rpc-tests/mempool_spendcoinbase.py | 5 +++ qa/rpc-tests/mempool_tx_expiry.py | 4 +-- qa/rpc-tests/merkle_blocks.py | 9 ++--- qa/rpc-tests/mining_shielded_coinbase.py | 8 ++--- qa/rpc-tests/multi_rpc.py | 22 ++++++------- qa/rpc-tests/nodehandling.py | 6 ++++ qa/rpc-tests/p2p-acceptblock.py | 8 +++-- qa/rpc-tests/p2p-fullblocktest.py | 1 + qa/rpc-tests/p2p_txexpiringsoon.py | 11 ++++--- qa/rpc-tests/p2p_txexpiry_dos.py | 12 +++---- qa/rpc-tests/paymentdisclosure.py | 9 ++--- qa/rpc-tests/post_heartwood_rollback.py | 7 +--- qa/rpc-tests/proxy_test.py | 6 +++- qa/rpc-tests/pruning.py | 10 +++--- qa/rpc-tests/rawtransactions.py | 11 ++++--- qa/rpc-tests/receivedby.py | 5 +++ qa/rpc-tests/reindex.py | 9 ++--- qa/rpc-tests/remove_sprout_shielding.py | 7 +--- qa/rpc-tests/rest.py | 11 ++++--- qa/rpc-tests/rewind_index.py | 11 ++++--- qa/rpc-tests/rpcbind_test.py | 4 +-- qa/rpc-tests/sapling_rewind_check.py | 11 ++++--- qa/rpc-tests/shorter_block_times.py | 16 ++++----- qa/rpc-tests/signrawtransactions.py | 12 +++---- qa/rpc-tests/smartfees.py | 5 +++ qa/rpc-tests/sprout_sapling_migration.py | 15 +++++---- qa/rpc-tests/test_framework/test_framework.py | 25 +++++++------- qa/rpc-tests/test_framework/util.py | 33 +++++++++++-------- qa/rpc-tests/turnstile.py | 11 ++++--- qa/rpc-tests/txn_doublespend.py | 5 +++ qa/rpc-tests/wallet.py | 9 ++--- qa/rpc-tests/wallet_nullifiers.py | 4 +-- qa/rpc-tests/wallet_overwintertx.py | 12 +++---- qa/rpc-tests/wallet_sapling.py | 4 --- qa/rpc-tests/walletbackup.py | 11 ++++--- qa/rpc-tests/zapwallettxes.py | 11 ++++--- qa/rpc-tests/zcjoinsplit.py | 4 +++ qa/rpc-tests/zmq_test.py | 6 +++- 66 files changed, 358 insertions(+), 260 deletions(-) diff --git a/qa/rpc-tests/addressindex.py b/qa/rpc-tests/addressindex.py index 0ed542923..e655f9bc6 100755 --- a/qa/rpc-tests/addressindex.py +++ b/qa/rpc-tests/addressindex.py @@ -18,7 +18,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - initialize_chain_clean, start_nodes, stop_nodes, connect_nodes, @@ -44,16 +43,17 @@ from binascii import hexlify, unhexlify class AddressIndexTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self): # -insightexplorer causes addressindex to be enabled (fAddressIndex = true) args_insight = ('-debug', '-txindex', '-experimentalfeatures', '-insightexplorer') # -lightwallet also causes addressindex to be enabled args_lightwallet = ('-debug', '-txindex', '-experimentalfeatures', '-lightwalletd') - self.nodes = start_nodes(4, self.options.tmpdir, [args_insight] * 3 + [args_lightwallet]) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [args_insight] * 3 + [args_lightwallet]) connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[0], 2) @@ -237,7 +237,7 @@ class AddressIndexTest(BitcoinTestFramework): # Test DisconnectBlock() by invalidating the most recent mined block tip = self.nodes[1].getchaintips()[0] - for i in range(3): + for i in range(self.num_nodes): node = self.nodes[i] # the value 4 UTXO is no longer in our balance check_balance(i, addr1, (expected - 4) * COIN, expected * COIN) diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 87796d5e1..61ee6ed95 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -25,10 +25,11 @@ TODO: factor out common code from {bipdersig-p2p,bip65-cltv-p2p}.py. class BIP65Test(ComparisonTestFramework): def __init__(self): + super().__init__() self.num_nodes = 1 def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir, + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-debug', '-whitelist=127.0.0.1']], binary=[self.options.testbinary]) self.is_network_split = False diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index ec585f6e9..7d5f24985 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -25,10 +25,11 @@ TODO: factor out common code from {bipdersig-p2p,bip65-cltv-p2p}.py. class BIP66Test(ComparisonTestFramework): def __init__(self): + super().__init__() self.num_nodes = 1 def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir, + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-debug', '-whitelist=127.0.0.1']], binary=[self.options.testbinary]) self.is_network_split = False diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py index 7c1bac285..7f65063a1 100755 --- a/qa/rpc-tests/blockchain.py +++ b/qa/rpc-tests/blockchain.py @@ -12,7 +12,6 @@ import decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( - initialize_chain, assert_equal, start_nodes, connect_nodes_bi, @@ -26,12 +25,13 @@ class BlockchainTest(BitcoinTestFramework): """ - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain(self.options.tmpdir) + def __init__(self): + super().__init__() + self.setup_clean_chain = False + self.num_nodes = 2 def setup_network(self, split=False): - self.nodes = start_nodes(2, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes, 0, 1) self.is_network_split = False self.sync_all() diff --git a/qa/rpc-tests/coinbase_funding_streams.py b/qa/rpc-tests/coinbase_funding_streams.py index 31c081e90..0fd6e84c8 100755 --- a/qa/rpc-tests/coinbase_funding_streams.py +++ b/qa/rpc-tests/coinbase_funding_streams.py @@ -12,7 +12,6 @@ from test_framework.util import ( assert_equal, bitcoind_processes, connect_nodes, - initialize_chain_clean, start_node, BLOSSOM_BRANCH_ID, HEARTWOOD_BRANCH_ID, @@ -20,9 +19,10 @@ from test_framework.util import ( ) class CoinbaseFundingStreamsTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True def start_node_with(self, index, extra_args=[]): args = [ diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index 60cdaa988..9523c96aa 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -4,7 +4,7 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_nodes, hex_str_to_bytes, bytes_to_hex_str from test_framework.mininode import CTransaction from io import BytesIO @@ -13,12 +13,13 @@ from io import BytesIO class DecodeScriptTest(BitcoinTestFramework): """Tests decoding scripts via RPC command "decodescript".""" - def setup_chain(self): - print('Initializing test directory ' + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 def setup_network(self, split=False): - self.nodes = start_nodes(1, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) self.is_network_split = False def decodescript_script_sig(self): diff --git a/qa/rpc-tests/disablewallet.py b/qa/rpc-tests/disablewallet.py index 3b32b26ae..f29820414 100755 --- a/qa/rpc-tests/disablewallet.py +++ b/qa/rpc-tests/disablewallet.py @@ -8,17 +8,18 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import initialize_chain_clean, start_nodes +from test_framework.util import start_nodes class DisableWalletTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 def setup_network(self, split=False): - self.nodes = start_nodes(1, self.options.tmpdir, [['-disablewallet']]) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [['-disablewallet']]) self.is_network_split = False self.sync_all() diff --git a/qa/rpc-tests/feature_zip221.py b/qa/rpc-tests/feature_zip221.py index bfe0f656b..0539343f5 100755 --- a/qa/rpc-tests/feature_zip221.py +++ b/qa/rpc-tests/feature_zip221.py @@ -12,7 +12,6 @@ from test_framework.util import ( assert_equal, bytes_to_hex_str, hex_str_to_bytes, - initialize_chain_clean, start_nodes, ) @@ -24,16 +23,17 @@ CHAIN_HISTORY_ROOT_VERSION = 2010200 # Verify block header field 'hashLightClientRoot' is set correctly for Heartwood blocks. class Zip221Test(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, extra_args=[[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ '-nuparams=2bb40e60:1', # Blossom '-nuparams=f5b9230b:10', # Heartwood '-nurejectoldversions=false', - ]] * 4) + ]] * self.num_nodes) def node_for_block(self, height): block_header = CBlockHeader() diff --git a/qa/rpc-tests/finalsaplingroot.py b/qa/rpc-tests/finalsaplingroot.py index 25332f141..257d9893d 100755 --- a/qa/rpc-tests/finalsaplingroot.py +++ b/qa/rpc-tests/finalsaplingroot.py @@ -9,7 +9,6 @@ from test_framework.util import ( assert_equal, connect_nodes_bi, get_coinbase_address, - initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, ) @@ -24,14 +23,15 @@ NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000" # is updated when Sapling transactions with outputs (commitments) are mined into a block. class FinalSaplingRootTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[[ + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ '-txindex' # Avoid JSONRPC error: No information available about transaction - ]] * 4 ) + ]] * self.num_nodes) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index 2374c9dea..b97d1fb77 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -14,6 +14,11 @@ import os class ForkNotifyTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = False + alert_filename = None # Set by setup_network def setup_network(self): diff --git a/qa/rpc-tests/framework.py b/qa/rpc-tests/framework.py index ec676eeb2..7a6765160 100755 --- a/qa/rpc-tests/framework.py +++ b/qa/rpc-tests/framework.py @@ -7,16 +7,16 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_raises, connect_nodes, - initialize_chain_clean, start_node, check_node_log, ) class FrameworkTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True def start_node_with(self, index, extra_args=[]): args = [] diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 708c69b27..14e48d0ad 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -6,7 +6,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ - initialize_chain_clean, start_nodes, connect_nodes_bi, stop_nodes, \ + start_nodes, connect_nodes_bi, stop_nodes, \ wait_bitcoinds from decimal import Decimal @@ -14,12 +14,13 @@ from decimal import Decimal # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): - def setup_chain(self): - print(("Initializing test directory "+self.options.tmpdir)) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 def setup_network(self, split=False): - self.nodes = start_nodes(4, self.options.tmpdir, + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4) connect_nodes_bi(self.nodes,0,1) @@ -450,7 +451,7 @@ class RawTransactionsTest(BitcoinTestFramework): stop_nodes(self.nodes) wait_bitcoinds() - self.nodes = start_nodes(4, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index e55e1e9fd..223132da4 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -5,7 +5,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, connect_nodes_bi, \ - initialize_chain_clean, start_nodes + start_nodes class GetBlockTemplateTest(BitcoinTestFramework): @@ -13,12 +13,13 @@ class GetBlockTemplateTest(BitcoinTestFramework): Test getblocktemplate. ''' - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(2, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) self.is_network_split=False self.sync_all() diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index c2e32b4db..89f5394f2 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -49,6 +49,11 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): Test longpolling with getblocktemplate. ''' + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False + def run_test(self): print("Warning: this test will take about 70 seconds in the best case. Be patient.") self.nodes[0].generate(10) diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index bce1b1784..94b1616e2 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -5,6 +5,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException +from test_framework.util import connect_nodes_bi from binascii import a2b_hex, b2a_hex from hashlib import sha256 @@ -94,6 +95,15 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework): Test block proposals with getblocktemplate. ''' + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = False + + def setup_network(self): + self.nodes = self.setup_nodes() + connect_nodes_bi(self.nodes, 0, 1) + def run_test(self): node = self.nodes[0] node.generate(1) # Mine a block to leave initial block download diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py index c75a89f46..63543f48c 100755 --- a/qa/rpc-tests/getchaintips.py +++ b/qa/rpc-tests/getchaintips.py @@ -12,9 +12,12 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False def run_test (self): - BitcoinTestFramework.run_test (self) tips = self.nodes[0].getchaintips () assert_equal (len (tips), 1) diff --git a/qa/rpc-tests/getrawtransaction_insight.py b/qa/rpc-tests/getrawtransaction_insight.py index f32995d68..f54bf85ee 100755 --- a/qa/rpc-tests/getrawtransaction_insight.py +++ b/qa/rpc-tests/getrawtransaction_insight.py @@ -11,7 +11,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal -from test_framework.util import initialize_chain_clean from test_framework.util import start_nodes, stop_nodes, connect_nodes from test_framework.util import wait_bitcoinds @@ -20,15 +19,16 @@ from test_framework.mininode import COIN class GetrawtransactionTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True def setup_network(self): # -insightexplorer causes spentindex to be enabled (fSpentIndex = true) - self.nodes = start_nodes(3, self.options.tmpdir, - [['-debug', '-txindex', '-experimentalfeatures', '-insightexplorer']]*3) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + [['-debug', '-txindex', '-experimentalfeatures', '-insightexplorer']] * self.num_nodes) connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[0], 2) diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index a98fb0098..ba01583dd 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -8,14 +8,19 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, start_nodes, str_to_b64str +from test_framework.util import assert_equal, str_to_b64str import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = False + + def setup_network(self): + self.nodes = self.setup_nodes() def run_test(self): diff --git a/qa/rpc-tests/invalidateblock.py b/qa/rpc-tests/invalidateblock.py index c6b3c5822..6907dc21d 100755 --- a/qa/rpc-tests/invalidateblock.py +++ b/qa/rpc-tests/invalidateblock.py @@ -8,15 +8,16 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import initialize_chain_clean, start_node, \ +from test_framework.util import start_node, \ connect_nodes_bi, sync_blocks import time class InvalidateTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 def setup_network(self): self.nodes = [] diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index b77b2f2ad..958938540 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -27,6 +27,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' def __init__(self): + super().__init__() self.num_nodes = 1 def run_test(self): diff --git a/qa/rpc-tests/key_import_export.py b/qa/rpc-tests/key_import_export.py index d841851a0..43f4c85de 100755 --- a/qa/rpc-tests/key_import_export.py +++ b/qa/rpc-tests/key_import_export.py @@ -6,7 +6,7 @@ from decimal import Decimal from functools import reduce from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_greater_than, start_nodes, initialize_chain_clean, connect_nodes_bi +from test_framework.util import assert_equal, assert_greater_than, start_nodes, connect_nodes_bi import logging @@ -15,12 +15,13 @@ logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) class KeyImportExportTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(4, self.options.tmpdir ) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir ) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 5a8260743..ccba07ea9 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -5,11 +5,9 @@ # Exercise the wallet keypool, and interaction with wallet encryption/locking -# Add python-bitcoinrpc to module search path: - from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain, \ +from test_framework.util import assert_equal, \ start_nodes, start_node, bitcoind_processes def check_array_result(object_array, to_match, expected): @@ -86,12 +84,13 @@ class KeyPoolTest(BitcoinTestFramework): except JSONRPCException as e: assert_equal(e.error['code'], -12) - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain(self.options.tmpdir) + def __init__(self): + super().__init__() + self.setup_clean_chain = False + self.num_nodes = 1 def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-experimentalfeatures', '-developerencryptwallet']]) if __name__ == '__main__': KeyPoolTest().main() diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 35d00a6f1..53871ea59 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -31,6 +31,10 @@ def check_array_result(object_array, to_match, expected): raise AssertionError("No objects matched %s"%(str(to_match))) class ListTransactionsTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False def run_test(self): # Simple send, 0 to 1: diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index f8a67e26e..9211f3ae0 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -6,8 +6,7 @@ from test_framework.mininode import NodeConn, NodeConnCB, NetworkThread, \ EarlyDisconnectError, CInv, msg_inv, mininode_lock from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import initialize_chain_clean, start_nodes, \ - p2p_port +from test_framework.util import start_nodes, p2p_port import os import time @@ -86,12 +85,13 @@ class MaxBlocksInFlightTest(BitcoinTestFramework): default=os.getenv("BITCOIND", "bitcoind"), help="Binary to test max block requests behavior") - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir, + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-debug', '-whitelist=127.0.0.1']], binary=[self.options.testbinary]) diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py index a942b652b..6600376ae 100755 --- a/qa/rpc-tests/mempool_limit.py +++ b/qa/rpc-tests/mempool_limit.py @@ -8,7 +8,6 @@ from test_framework.util import ( assert_equal, get_coinbase_address, fail, - initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, ) @@ -18,10 +17,6 @@ from time import sleep # Test wallet behaviour with Sapling addresses class MempoolLimit(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) - def setup_nodes(self): extra_args = [ ["-debug=mempool", '-mempooltxcostlimit=8000'], # 2 transactions at min cost @@ -30,17 +25,22 @@ class MempoolLimit(BitcoinTestFramework): # Let node 3 hold one more transaction ["-debug=mempool", '-mempooltxcostlimit=12000'], # 3 transactions at min cost ] - return start_nodes(4, self.options.tmpdir, extra_args) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args) def check_mempool_sizes(self, expected_size, check_node3=True): - for i in range(4 if check_node3 else 3): + for i in range(self.num_nodes if check_node3 else self.num_nodes - 1): mempool = self.nodes[i].getrawmempool() if len(mempool) != expected_size: # print all nodes' mempools before failing - for i in range(4): + for i in range(self.num_nodes): print("Mempool for node {}: {}".format(i, mempool)) fail("Fail: Mempool for node {}: size={}, expected={}".format(i, len(mempool), expected_size)) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 + def run_test(self): print("Mining blocks...") self.sync_all() diff --git a/qa/rpc-tests/mempool_nu_activation.py b/qa/rpc-tests/mempool_nu_activation.py index aa402a70a..d7c0bdac6 100755 --- a/qa/rpc-tests/mempool_nu_activation.py +++ b/qa/rpc-tests/mempool_nu_activation.py @@ -5,7 +5,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( - assert_equal, assert_true, initialize_chain_clean, + assert_equal, assert_true, start_node, connect_nodes, wait_and_assert_operationid_status, get_coinbase_address ) @@ -17,6 +17,11 @@ class MempoolUpgradeActivationTest(BitcoinTestFramework): alert_filename = None # Set by setup_network + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True + def setup_network(self): args = ["-checkmempool", "-debug=mempool", "-blockmaxsize=4000", "-nuparams=2bb40e60:200", # Blossom @@ -28,10 +33,6 @@ class MempoolUpgradeActivationTest(BitcoinTestFramework): self.is_network_split = False self.sync_all - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 2) - def run_test(self): self.nodes[1].generate(100) self.sync_all() diff --git a/qa/rpc-tests/mempool_reorg.py b/qa/rpc-tests/mempool_reorg.py index 1dce011bf..e809195dc 100755 --- a/qa/rpc-tests/mempool_reorg.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -15,6 +15,10 @@ from test_framework.util import assert_equal, assert_raises, start_node, connect # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = False alert_filename = None # Set by setup_network diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 0cb207133..ef11b29da 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -15,6 +15,11 @@ from test_framework.util import assert_equal, start_node # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.setup_clean_chain = False + def setup_network(self): # Just need one node for this test args = ["-checkmempool", "-debug=mempool"] diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index 208399332..b692cd513 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -22,6 +22,11 @@ from test_framework.util import assert_equal, assert_greater_than, assert_raises # Create one-input, one-output, no-fee transaction: class MempoolSpendCoinbaseTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.setup_clean_chain = False + def setup_network(self): # Just need one node for this test args = ["-checkmempool", "-debug=mempool"] diff --git a/qa/rpc-tests/mempool_tx_expiry.py b/qa/rpc-tests/mempool_tx_expiry.py index 6d3f28602..f3cff7ba3 100755 --- a/qa/rpc-tests/mempool_tx_expiry.py +++ b/qa/rpc-tests/mempool_tx_expiry.py @@ -21,11 +21,11 @@ TX_EXPIRY_DELTA = 10 class MempoolTxExpiryTest(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, + return start_nodes(self.num_nodes, self.options.tmpdir, [[ "-txexpirydelta=%d" % TX_EXPIRY_DELTA, "-debug=mempool" - ]] * 4) + ]] * self.num_nodes) # Test before, at, and after expiry block # chain is at block height 199 when run_test executes diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index 85ff0924e..4b7f14680 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -11,14 +11,15 @@ import string from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_raises, \ - initialize_chain_clean, start_node, connect_nodes + start_node, connect_nodes class MerkleBlockTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 def setup_network(self): self.nodes = [] diff --git a/qa/rpc-tests/mining_shielded_coinbase.py b/qa/rpc-tests/mining_shielded_coinbase.py index bef3493be..83c344011 100755 --- a/qa/rpc-tests/mining_shielded_coinbase.py +++ b/qa/rpc-tests/mining_shielded_coinbase.py @@ -14,7 +14,6 @@ from test_framework.util import ( assert_raises, bitcoind_processes, connect_nodes, - initialize_chain_clean, start_node, wait_and_assert_operationid_status, check_node_log, @@ -22,9 +21,10 @@ from test_framework.util import ( class ShieldCoinbaseTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def start_node_with(self, index, extra_args=[]): args = [ diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py index bd1b79fd2..74359f553 100755 --- a/qa/rpc-tests/multi_rpc.py +++ b/qa/rpc-tests/multi_rpc.py @@ -8,24 +8,21 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, - initialize_chain, - start_nodes, - str_to_b64str, -) -import os +from test_framework.util import str_to_b64str, assert_equal +import os import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir) + + def __init__(self): + super().__init__() + self.setup_clean_chain = False + self.num_nodes = 1 def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain(self.options.tmpdir) + super().setup_chain() #Append rpcauth to zcash.conf before initialization rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" @@ -33,6 +30,9 @@ class HTTPBasicsTest (BitcoinTestFramework): f.write(rpcauth+"\n") f.write(rpcauth2+"\n") + def setup_network(self): + self.nodes = self.setup_nodes() + def run_test(self): ################################################## diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index fb32cd2cc..8b79c2f46 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -14,6 +14,12 @@ import time import urllib.parse class NodeHandlingTest (BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False + def run_test(self): ########################### # setban/listbanned tests # diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 741c3348e..98ce9ba3d 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -7,7 +7,7 @@ from test_framework.mininode import CBlockHeader, CInv, NodeConn, NodeConnCB, \ NetworkThread, msg_block, msg_headers, msg_inv, msg_ping, msg_pong, \ mininode_lock from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_node, p2p_port from test_framework.blocktools import create_block, create_coinbase @@ -117,8 +117,10 @@ class AcceptBlockTest(BitcoinTestFramework): default=os.getenv("BITCOIND", "bitcoind"), help="bitcoind binary to test") - def setup_chain(self): - initialize_chain_clean(self.options.tmpdir, 2) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 2 def setup_network(self): # Node0 will be used to test behavior of processing unrequested blocks diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 433374850..b8db50487 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -38,6 +38,7 @@ class FullBlockTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' def __init__(self): + super().__init__() self.num_nodes = 1 self.block_heights = {} self.coinbase_key = CECKey() diff --git a/qa/rpc-tests/p2p_txexpiringsoon.py b/qa/rpc-tests/p2p_txexpiringsoon.py index 3d5d10a78..6472b6fba 100755 --- a/qa/rpc-tests/p2p_txexpiringsoon.py +++ b/qa/rpc-tests/p2p_txexpiringsoon.py @@ -8,18 +8,19 @@ from test_framework.mininode import NodeConn, NetworkThread, CInv, \ msg_mempool, msg_getdata, msg_tx, mininode_lock, SAPLING_PROTO_VERSION from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, connect_nodes_bi, fail, \ - initialize_chain_clean, p2p_port, start_nodes, sync_blocks, sync_mempools + p2p_port, start_nodes, sync_blocks, sync_mempools from tx_expiry_helper import TestNode, create_transaction class TxExpiringSoonTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True def setup_network(self): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes, 0, 1) # We don't connect node 2 diff --git a/qa/rpc-tests/p2p_txexpiry_dos.py b/qa/rpc-tests/p2p_txexpiry_dos.py index 0fe5bf698..e1b7e9e1e 100755 --- a/qa/rpc-tests/p2p_txexpiry_dos.py +++ b/qa/rpc-tests/p2p_txexpiry_dos.py @@ -6,8 +6,7 @@ from test_framework.mininode import NodeConn, NetworkThread, \ msg_tx, SAPLING_PROTO_VERSION from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import initialize_chain_clean, start_nodes, \ - p2p_port, assert_equal +from test_framework.util import start_nodes, p2p_port, assert_equal from tx_expiry_helper import TestNode, create_transaction import time @@ -15,12 +14,13 @@ import time class TxExpiryDoSTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.setup_clean_chain = True def setup_network(self): - self.nodes = start_nodes(1, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): test_node = TestNode() diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py index 959de48cf..9330f6797 100755 --- a/qa/rpc-tests/paymentdisclosure.py +++ b/qa/rpc-tests/paymentdisclosure.py @@ -5,7 +5,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_node, connect_nodes_bi, wait_and_assert_operationid_status, \ get_coinbase_address @@ -13,9 +13,10 @@ from decimal import Decimal class PaymentDisclosureTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True def setup_network(self, split=False): args = ['-debug=zrpcunsafe,paymentdisclosure', '-experimentalfeatures', '-paymentdisclosure', '-txindex=1'] diff --git a/qa/rpc-tests/post_heartwood_rollback.py b/qa/rpc-tests/post_heartwood_rollback.py index 0310d7849..4f11545ea 100755 --- a/qa/rpc-tests/post_heartwood_rollback.py +++ b/qa/rpc-tests/post_heartwood_rollback.py @@ -12,7 +12,6 @@ from test_framework.util import ( assert_equal, bitcoind_processes, connect_nodes_bi, - initialize_chain, nuparams, start_node, start_nodes, @@ -29,12 +28,8 @@ NO_CANOPY = [nuparams(BLOSSOM_BRANCH_ID, 205), nuparams(HEARTWOOD_BRANCH_ID, 21 class PostHeartwoodRollbackTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain(self.options.tmpdir) - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, extra_args=[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[ HAS_CANOPY, HAS_CANOPY, NO_CANOPY, diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index af4595d14..d26f90f7c 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -36,6 +36,10 @@ addnode connect to generic DNS name class ProxyTest(BitcoinTestFramework): def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False + self.have_ipv6 = test_ipv6_local() # Create two proxies on different ports # ... one unauthenticated @@ -77,7 +81,7 @@ class ProxyTest(BitcoinTestFramework): ] if self.have_ipv6: args[3] = ['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] - return start_nodes(4, self.options.tmpdir, extra_args=args) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) def node_test(self, node, proxies, auth, test_onion=True): rv = [] diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 7cad82846..cbebeca6b 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -13,7 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import initialize_chain_clean, start_node, \ +from test_framework.util import start_node, \ connect_nodes, stop_node, sync_blocks import os.path @@ -25,6 +25,10 @@ def calc_usage(blockdir): class PruneTest(BitcoinTestFramework): def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 + self.utxo = [] self.address = ["",""] @@ -46,10 +50,6 @@ class PruneTest(BitcoinTestFramework): self.txouts = self.txouts + script_pubkey - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) - def setup_network(self): self.nodes = [] self.is_network_split = False diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 707a8bb17..0c5ae32fb 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -10,7 +10,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_nodes, connect_nodes_bi, assert_raises from decimal import Decimal @@ -18,12 +18,13 @@ from decimal import Decimal # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) #connect to a local machine for debugging #url = "http://bitcoinrpc:DP6DvqZtqXarpeNWyN3LZTFchCCyCUuHwNF7E8pX99x1@%s:%d" % ('127.0.0.1', 18232) diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py index 41db262b4..e124b0fde 100755 --- a/qa/rpc-tests/receivedby.py +++ b/qa/rpc-tests/receivedby.py @@ -53,6 +53,11 @@ def check_array_result(object_array, to_match, expected, should_not_find = False class ReceivedByTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False + def run_test(self): ''' listreceivedbyaddress Test diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py index b66257a51..432d1e002 100755 --- a/qa/rpc-tests/reindex.py +++ b/qa/rpc-tests/reindex.py @@ -8,15 +8,16 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_node, stop_node, wait_bitcoinds class ReindexTest(BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 def setup_network(self): self.nodes = [] diff --git a/qa/rpc-tests/remove_sprout_shielding.py b/qa/rpc-tests/remove_sprout_shielding.py index 2aa5bbcae..29ff2acc9 100755 --- a/qa/rpc-tests/remove_sprout_shielding.py +++ b/qa/rpc-tests/remove_sprout_shielding.py @@ -8,7 +8,6 @@ from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - initialize_chain, start_nodes, get_coinbase_address, wait_and_assert_operationid_status, nuparams, BLOSSOM_BRANCH_ID, HEARTWOOD_BRANCH_ID, CANOPY_BRANCH_ID @@ -23,12 +22,8 @@ HAS_CANOPY = ['-nurejectoldversions=false', ] class RemoveSproutShieldingTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain(self.options.tmpdir) - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, extra_args=[HAS_CANOPY]*4) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[HAS_CANOPY] * self.num_nodes) def run_test (self): diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index c6e266afe..bcc7ef9ad 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -9,7 +9,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_greater_than, \ - initialize_chain_clean, start_nodes, connect_nodes_bi + start_nodes, connect_nodes_bi import struct import binascii @@ -51,12 +51,13 @@ def http_post_call(host, port, path, requestdata = '', response_object = 0): class RESTTest (BitcoinTestFramework): FORMAT_SEPARATOR = "." - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/rewind_index.py b/qa/rpc-tests/rewind_index.py index 27d55891f..00816822d 100755 --- a/qa/rpc-tests/rewind_index.py +++ b/qa/rpc-tests/rewind_index.py @@ -4,7 +4,7 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_nodes, start_node, connect_nodes_bi, bitcoind_processes, \ nuparams, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID @@ -15,15 +15,16 @@ FAKE_OVERWINTER = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_I class RewindBlockIndexTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True def setup_network(self, split=False): # Node 0 - Overwinter, then Sprout, then Overwinter again # Node 1 - Sprout # Node 2 - Overwinter - self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[ + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[ FAKE_OVERWINTER, FAKE_SPROUT, FAKE_OVERWINTER, diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index afb2f04db..8c877464e 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -37,7 +37,7 @@ def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected): if allow_ips: base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] - nodes = start_nodes(1, tmpdir, [base_args + binds], connect_to) + nodes = start_nodes(self.num_nodes, tmpdir, [base_args + binds], connect_to) try: pid = bitcoind_processes[0].pid assert_equal(set(get_bind_addrs(pid)), set(expected)) @@ -51,7 +51,7 @@ def run_allowip_test(tmpdir, allow_ips, rpchost, rpcport): at a non-localhost IP. ''' base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - nodes = start_nodes(1, tmpdir, [base_args]) + nodes = start_nodes(self.num_nodes, tmpdir, [base_args]) try: # connect to node through non-loopback interface url = "http://rt:rt@%s:%d" % (rpchost, rpcport,) diff --git a/qa/rpc-tests/sapling_rewind_check.py b/qa/rpc-tests/sapling_rewind_check.py index 05c0bae72..03065aac0 100755 --- a/qa/rpc-tests/sapling_rewind_check.py +++ b/qa/rpc-tests/sapling_rewind_check.py @@ -28,7 +28,7 @@ length computation (40b5d5e3ea4b602c34c4efaba0b9f6171dddfef5) corrects the issue from test_framework.test_framework import BitcoinTestFramework from test_framework.util import (assert_equal, assert_true, - initialize_chain_clean, start_nodes, start_node, connect_nodes_bi, + start_nodes, start_node, connect_nodes_bi, bitcoind_processes, nuparams, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID) @@ -39,14 +39,15 @@ HAS_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 1 NO_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 150)] class SaplingRewindTest(BitcoinTestFramework): - def setup_chain(self): - logging.info("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True # This mirrors how the network was setup in the bash test def setup_network(self, split=False): logging.info("Initializing the network in "+self.options.tmpdir) - self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[ + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[ HAS_SAPLING, # The first two nodes have a correct view of the network, HAS_SAPLING, # the third will rewind after upgrading. NO_SAPLING diff --git a/qa/rpc-tests/shorter_block_times.py b/qa/rpc-tests/shorter_block_times.py index e57602cc4..d3ff5e079 100755 --- a/qa/rpc-tests/shorter_block_times.py +++ b/qa/rpc-tests/shorter_block_times.py @@ -8,21 +8,21 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, get_coinbase_address, - initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, ) class ShorterBlockTimes(BitcoinTestFramework): - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, [[ - '-nuparams=2bb40e60:106', # Blossom - ]] * 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def setup_nodes(self): + return start_nodes(self.num_nodes, self.options.tmpdir, [[ + '-nuparams=2bb40e60:106', # Blossom + ]] * self.num_nodes) def run_test(self): print("Mining blocks...") diff --git a/qa/rpc-tests/signrawtransactions.py b/qa/rpc-tests/signrawtransactions.py index 97ec39384..2c8e36817 100755 --- a/qa/rpc-tests/signrawtransactions.py +++ b/qa/rpc-tests/signrawtransactions.py @@ -4,19 +4,19 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ - start_nodes +from test_framework.util import assert_equal, start_nodes class SignRawTransactionsTest(BitcoinTestFramework): """Tests transaction signing via RPC command "signrawtransaction".""" - def setup_chain(self): - print('Initializing test directory ' + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 1) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 def setup_network(self, split=False): - self.nodes = start_nodes(1, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) self.is_network_split = False def successful_signing_test(self): diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 6ebaf4355..44086fa4f 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -142,6 +142,11 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True): class EstimateFeeTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = False + def setup_network(self): ''' We'll setup the network to have 3 nodes that all mine with different parameters. diff --git a/qa/rpc-tests/sprout_sapling_migration.py b/qa/rpc-tests/sprout_sapling_migration.py index 56bc28839..ebc96c58f 100755 --- a/qa/rpc-tests/sprout_sapling_migration.py +++ b/qa/rpc-tests/sprout_sapling_migration.py @@ -6,7 +6,7 @@ from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_true, get_coinbase_address, \ - initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, \ + start_nodes, wait_and_assert_operationid_status, \ wait_and_assert_operationid_status_result SAPLING_ADDR = 'zregtestsapling1ssqj3f3majnl270985gqcdqedd9t4nlttjqskccwevj2v20sc25deqspv3masufnwcdy67cydyy' @@ -49,9 +49,14 @@ def check_migration_status(node, destination_address, migration_state): class SproutSaplingMigration(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True + def setup_nodes(self): extra_args = [[ - ]] * 4 + ]] * self.num_nodes # Add migration parameters to nodes[0] extra_args[0] = extra_args[0] + [ '-migration', @@ -60,11 +65,7 @@ class SproutSaplingMigration(BitcoinTestFramework): ] assert_equal(3, len(extra_args[0])) assert_equal(0, len(extra_args[1])) - return start_nodes(4, self.options.tmpdir, extra_args) - - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args) def run_migration_test(self, node, sproutAddr, saplingAddr, target_height): # Make sure we are in a good state to run the test diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index a8a3da5e8..1ab9e3024 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -16,7 +16,6 @@ import traceback from .authproxy import JSONRPCException from .util import ( initialize_chain, - assert_equal, start_nodes, connect_nodes_bi, sync_blocks, @@ -32,21 +31,26 @@ from .util import ( class BitcoinTestFramework(object): - # These may be over-ridden by subclasses: + def __init__(self): + self.num_nodes = 4 + self.setup_clean_chain = False + self.nodes = None + def run_test(self): - for node in self.nodes: - assert_equal(node.getblockcount(), 200) - assert_equal(node.getbalance(), 25*10) + raise NotImplementedError def add_options(self, parser): pass def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) - initialize_chain(self.options.tmpdir) + if self.setup_clean_chain: + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + else: + initialize_chain(self.options.tmpdir, self.num_nodes) def setup_nodes(self): - return start_nodes(4, self.options.tmpdir) + return start_nodes(self.num_nodes, self.options.tmpdir) def setup_network(self, split = False): self.nodes = self.setup_nodes() @@ -181,9 +185,10 @@ class BitcoinTestFramework(object): class ComparisonTestFramework(BitcoinTestFramework): - # Can override the num_nodes variable to indicate how many nodes to run. def __init__(self): + super().__init__() self.num_nodes = 2 + self.setup_clean_chain = True def add_options(self, parser): parser.add_option("--testbinary", dest="testbinary", @@ -193,10 +198,6 @@ class ComparisonTestFramework(BitcoinTestFramework): default=os.getenv("BITCOIND", "bitcoind"), help="bitcoind binary to use for reference nodes (if any)") - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, self.num_nodes) - def setup_network(self): self.nodes = start_nodes( self.num_nodes, self.options.tmpdir, diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index f05a2edd5..c8036a44f 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -186,10 +186,10 @@ def wait_for_bitcoind_start(process, url, i): raise # unknown JSON RPC exception time.sleep(0.25) -def initialize_chain(test_dir): +def initialize_chain(test_dir, num_nodes): """ - Create (or copy from cache) a 200-block-long chain and - 4 wallets. + Create a cache of a 200-block-long chain (with wallet) for MAX_NODES + Afterward, create num_nodes copies from the cache """ # Due to the consensus change fix for the timejacking attack, we need to @@ -213,18 +213,22 @@ def initialize_chain(test_dir): print("initialize_chain(): Removing stale cache") shutil.rmtree("cache") - if (not os.path.isdir(os.path.join("cache","node0")) - or not os.path.isdir(os.path.join("cache","node1")) - or not os.path.isdir(os.path.join("cache","node2")) - or not os.path.isdir(os.path.join("cache","node3"))): + assert num_nodes <= MAX_NODES + create_cache = False + for i in range(MAX_NODES): + if not os.path.isdir(os.path.join('cache', 'node'+str(i))): + create_cache = True + break + + if create_cache: #find and delete old cache directories if any exist - for i in range(4): + for i in range(MAX_NODES): if os.path.isdir(os.path.join("cache","node"+str(i))): shutil.rmtree(os.path.join("cache","node"+str(i))) # Create cache directories, run bitcoinds: - for i in range(4): + for i in range(MAX_NODES): datadir=initialize_datadir("cache", i) args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir="+datadir, "-discover=0" ] args.extend([ @@ -241,15 +245,18 @@ def initialize_chain(test_dir): print("initialize_chain: RPC successfully started") rpcs = [] - for i in range(4): + for i in range(MAX_NODES): try: rpcs.append(get_rpc_proxy(rpc_url(i), i)) except: sys.stderr.write("Error connecting to "+rpc_url(i)+"\n") sys.exit(1) - # Create a 200-block-long chain; each of the 4 nodes + # Create a 200-block-long chain; each of the 4 first nodes # gets 25 mature blocks and 25 immature. + # Note: To preserve compatibility with older versions of + # initialize_chain, only 4 nodes will generate coins. + # # Blocks are created with timestamps 2.5 minutes apart (matching the # chain defaulting above to Sapling active), starting 200 * 2.5 minutes # before the current time. @@ -268,13 +275,13 @@ def initialize_chain(test_dir): # Shut them down, and clean up cache directories: stop_nodes(rpcs) wait_bitcoinds() - for i in range(4): + for i in range(MAX_NODES): os.remove(log_filename("cache", i, "debug.log")) os.remove(log_filename("cache", i, "db.log")) os.remove(log_filename("cache", i, "peers.dat")) os.remove(log_filename("cache", i, "fee_estimates.dat")) - for i in range(4): + for i in range(num_nodes): from_dir = os.path.join("cache", "node"+str(i)) to_dir = os.path.join(test_dir, "node"+str(i)) shutil.copytree(from_dir, to_dir) diff --git a/qa/rpc-tests/turnstile.py b/qa/rpc-tests/turnstile.py index 18cd34629..4b02b9619 100755 --- a/qa/rpc-tests/turnstile.py +++ b/qa/rpc-tests/turnstile.py @@ -32,7 +32,7 @@ from test_framework.util import ( get_coinbase_address, start_node, start_nodes, sync_blocks, sync_mempools, - initialize_chain_clean, connect_nodes_bi, + connect_nodes_bi, wait_and_assert_operationid_status, bitcoind_processes, check_node_log @@ -44,12 +44,13 @@ TURNSTILE_ARGS = ['-experimentalfeatures', class TurnstileTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory " + self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) self.is_network_split=False diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index 2751fc70e..12d7f76dc 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -14,6 +14,11 @@ from test_framework.util import assert_equal, connect_nodes, \ class TxnMallTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = False + def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index b09316716..310d9b44b 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -6,7 +6,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ - initialize_chain_clean, start_nodes, start_node, connect_nodes_bi, \ + start_nodes, start_node, connect_nodes_bi, \ stop_nodes, sync_blocks, sync_mempools, wait_and_assert_operationid_status, \ wait_bitcoinds @@ -14,9 +14,10 @@ from decimal import Decimal class WalletTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 def setup_network(self, split=False): self.nodes = start_nodes(3, self.options.tmpdir) diff --git a/qa/rpc-tests/wallet_nullifiers.py b/qa/rpc-tests/wallet_nullifiers.py index 07c1f1d0d..ee62e5a96 100755 --- a/qa/rpc-tests/wallet_nullifiers.py +++ b/qa/rpc-tests/wallet_nullifiers.py @@ -13,8 +13,8 @@ from decimal import Decimal class WalletNullifiersTest (BitcoinTestFramework): def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, - extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4) + return start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * self.num_nodes) def run_test (self): # add zaddr to node 0 diff --git a/qa/rpc-tests/wallet_overwintertx.py b/qa/rpc-tests/wallet_overwintertx.py index 218de5109..78b7ce1fb 100755 --- a/qa/rpc-tests/wallet_overwintertx.py +++ b/qa/rpc-tests/wallet_overwintertx.py @@ -9,7 +9,6 @@ from test_framework.util import ( assert_greater_than, connect_nodes_bi, get_coinbase_address, - initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, ) @@ -19,16 +18,17 @@ from decimal import Decimal class WalletOverwinterTxTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self, split=False): - self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[[ + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ "-nuparams=2bb40e60:200", "-debug=zrpcunsafe", "-txindex", - ]] * 4 ) + ]] * self.num_nodes) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/wallet_sapling.py b/qa/rpc-tests/wallet_sapling.py index 26a1b68ea..7f9d52b5f 100755 --- a/qa/rpc-tests/wallet_sapling.py +++ b/qa/rpc-tests/wallet_sapling.py @@ -8,7 +8,6 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import ( assert_equal, get_coinbase_address, - start_nodes, wait_and_assert_operationid_status, ) @@ -17,9 +16,6 @@ from decimal import Decimal # Test wallet behaviour with Sapling addresses class WalletSaplingTest(BitcoinTestFramework): - def setup_nodes(self): - return start_nodes(4, self.options.tmpdir) - def run_test(self): # Sanity-check the test harness assert_equal(self.nodes[0].getblockcount(), 200) diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index d22a26088..463fe63b0 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -35,7 +35,7 @@ and confirm again balances are correct. from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_nodes, start_node, connect_nodes, stop_node, \ sync_blocks, sync_mempools @@ -50,9 +50,10 @@ logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, str class WalletBackupTest(BitcoinTestFramework): - def setup_chain(self): - logging.info("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 # This mirrors how the network was setup in the bash test def setup_network(self, split=False): @@ -63,7 +64,7 @@ class WalletBackupTest(BitcoinTestFramework): # nodes 1, 2,3 are spenders, let's give them a keypool=100 extra_args = [["-keypool=100", ed0], ["-keypool=100", ed1], ["-keypool=100", ed2], []] - self.nodes = start_nodes(4, self.options.tmpdir, extra_args) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args) connect_nodes(self.nodes[0], 3) connect_nodes(self.nodes[1], 3) connect_nodes(self.nodes[2], 3) diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index 165b2451f..9985eb89b 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -5,18 +5,19 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ +from test_framework.util import assert_equal, \ start_nodes, start_node, connect_nodes_bi, bitcoind_processes class ZapWalletTXesTest (BitcoinTestFramework): - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) diff --git a/qa/rpc-tests/zcjoinsplit.py b/qa/rpc-tests/zcjoinsplit.py index 821c1256c..85af3c1fb 100755 --- a/qa/rpc-tests/zcjoinsplit.py +++ b/qa/rpc-tests/zcjoinsplit.py @@ -10,6 +10,10 @@ from test_framework.util import assert_equal, start_node, \ class JoinSplitTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 1 + def setup_network(self): self.nodes = [] self.is_network_split = False diff --git a/qa/rpc-tests/zmq_test.py b/qa/rpc-tests/zmq_test.py index 551ffaf09..c679bc8f7 100755 --- a/qa/rpc-tests/zmq_test.py +++ b/qa/rpc-tests/zmq_test.py @@ -15,6 +15,10 @@ import struct class ZMQTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + port = 28332 def setup_nodes(self): @@ -23,7 +27,7 @@ class ZMQTest(BitcoinTestFramework): self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock") self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % self.port) - return start_nodes(4, self.options.tmpdir, extra_args=[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[ ['-zmqpubhashtx=tcp://127.0.0.1:'+str(self.port), '-zmqpubhashblock=tcp://127.0.0.1:'+str(self.port)], [], [], From 17caf1ddd174a972c39d7d64de8b33f463e868f4 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 15 May 2016 11:06:23 +0200 Subject: [PATCH 33/66] [qa] test_framework: Append portseed to tmpdir This makes it possible to specify a tmpdir while running tests in parallel --- qa/rpc-tests/test_framework/test_framework.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 1ab9e3024..48cf43ff5 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -119,6 +119,8 @@ class BitcoinTestFramework(object): self.add_options(parser) (self.options, self.args) = parser.parse_args() + self.options.tmpdir += '/' + str(self.options.port_seed) + if self.options.trace_rpc: logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) From 2399cfc7ba95895895d4eaaa0613bd8c7b9447bf Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 26 May 2016 14:19:07 -0400 Subject: [PATCH 34/66] Tests: add timeout to sync_blocks() and sync_mempools() Previously these functions would infinitely loop if sync failed; now they have a default timeout of 60 seconds, after which an AssertionError is raised. sync_blocks() has also been improved and now compares the tip hash of each node, rather than just using block count. Zcash: Kept block count check for a couple of tests where we use it. --- qa/rpc-tests/rewind_index.py | 4 +-- qa/rpc-tests/test_framework/util.py | 38 ++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/qa/rpc-tests/rewind_index.py b/qa/rpc-tests/rewind_index.py index 00816822d..5c0c013c6 100755 --- a/qa/rpc-tests/rewind_index.py +++ b/qa/rpc-tests/rewind_index.py @@ -6,7 +6,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, \ start_nodes, start_node, connect_nodes_bi, bitcoind_processes, \ - nuparams, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID + nuparams, sync_blocks, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID import time @@ -48,7 +48,7 @@ class RewindBlockIndexTest (BitcoinTestFramework): print("Mining diverging blocks") block10s = self.nodes[1].generate(1)[0] block10o = self.nodes[2].generate(1)[0] - self.sync_all() + sync_blocks(self.nodes, allow_different_tips=True) assert_equal(self.nodes[0].getbestblockhash(), block10o) assert_equal(self.nodes[1].getbestblockhash(), block10s) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c8036a44f..c8666cb04 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -105,31 +105,41 @@ def hex_str_to_bytes(hex_str): def str_to_b64str(string): return b64encode(string.encode('utf-8')).decode('ascii') -def sync_blocks(rpc_connections, wait=1): +def sync_blocks(rpc_connections, wait=1, timeout=60, allow_different_tips=False): """ - Wait until everybody has the same block count, and has notified - all internal listeners of them + Wait until everybody has the same tip, and has notified + all internal listeners of them. + + If allow_different_tips is True, waits until everyone has + the same block count. """ - while True: - counts = [ x.getblockcount() for x in rpc_connections ] - if counts == [ counts[0] ]*len(counts): + while timeout > 0: + if allow_different_tips: + tips = [ x.getblockcount() for x in rpc_connections ] + else: + tips = [ x.getbestblockhash() for x in rpc_connections ] + if tips == [ tips[0] ]*len(tips): break time.sleep(wait) + timeout -= wait # Now that the block counts are in sync, wait for the internal # notifications to finish - while True: + while timeout > 0: notified = [ x.getblockchaininfo()['fullyNotified'] for x in rpc_connections ] if notified == [ True ] * len(notified): - break + return True time.sleep(wait) + timeout -= wait -def sync_mempools(rpc_connections, wait=1): + raise AssertionError("Block sync failed") + +def sync_mempools(rpc_connections, wait=1, timeout=60): """ Wait until everybody has the same transactions in their memory pools, and has notified all internal listeners of them """ - while True: + while timeout > 0: pool = set(rpc_connections[0].getrawmempool()) num_match = 1 for i in range(1, len(rpc_connections)): @@ -138,14 +148,18 @@ def sync_mempools(rpc_connections, wait=1): if num_match == len(rpc_connections): break time.sleep(wait) + timeout -= wait # Now that the mempools are in sync, wait for the internal # notifications to finish - while True: + while timeout > 0: notified = [ x.getmempoolinfo()['fullyNotified'] for x in rpc_connections ] if notified == [ True ] * len(notified): - break + return True time.sleep(wait) + timeout -= wait + + raise AssertionError("Mempool sync failed") bitcoind_processes = {} From ecf9d8eb2b852ec4ab1b6375128cbe0553ae195a Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 1 Jun 2016 18:47:21 -0400 Subject: [PATCH 35/66] build: a few ugly hacks to get the rpc tests working out-of-tree - Link pull-tester/rpc-tests.py to the build dir - Add the build-dir's config to the python path so that tests can find it - The tests themselves are in srcdir - Clean up __pycache__ in 'make clean' --- Makefile.am | 1 + configure.ac | 2 +- qa/pull-tester/rpc-tests.py | 3 ++- qa/pull-tester/tests_config.py.in | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index e14204c10..be3baaf41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,6 +159,7 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ rm -rf afl-temp + rm -rf qa/pull-tester/__pycache__ distclean-local: rm -f zcutil/bin/db_* diff --git a/configure.ac b/configure.ac index 8b60e58db..554e5f363 100644 --- a/configure.ac +++ b/configure.ac @@ -970,7 +970,7 @@ AC_SUBST(LIBZCASH_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) -chmod +x qa/pull-tester/rpc-tests.py +AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py]) dnl boost's m4 checks do something really nasty: they export these vars. As a dnl result, they leak into secp256k1's configure and crazy things happen. diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 3f0660981..414d059d6 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -29,6 +29,7 @@ import subprocess import tempfile import re +sys.path.append("qa/pull-tester/") from tests_config import * BOLD = ("","") @@ -37,7 +38,7 @@ if os.name == 'posix': # terminal via ANSI escape sequences: BOLD = ('\033[0m', '\033[1m') -RPC_TESTS_DIR = BUILDDIR + '/qa/rpc-tests/' +RPC_TESTS_DIR = SRCDIR + '/qa/rpc-tests/' #If imported values are not defined then set to zero (or disabled) if 'ENABLE_WALLET' not in vars(): diff --git a/qa/pull-tester/tests_config.py.in b/qa/pull-tester/tests_config.py.in index 601bbd571..7aa70525d 100644 --- a/qa/pull-tester/tests_config.py.in +++ b/qa/pull-tester/tests_config.py.in @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +SRCDIR="@abs_top_srcdir@" BUILDDIR="@abs_top_builddir@" EXEEXT="@EXEEXT@" From 63a3c9aceba91cb336cd0c71dc03a101e210754f Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 3 Apr 2016 20:36:27 +0200 Subject: [PATCH 36/66] [qa] test_framework: Use different rpc_auth_pair for each node --- qa/rpc-tests/test_framework/util.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c8666cb04..a8b458df5 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -167,18 +167,23 @@ def initialize_datadir(dirname, n): datadir = os.path.join(dirname, "node"+str(n)) if not os.path.isdir(datadir): os.makedirs(datadir) + rpc_u, rpc_p = rpc_auth_pair(n) with open(os.path.join(datadir, "zcash.conf"), 'w', encoding='utf8') as f: f.write("regtest=1\n") f.write("showmetrics=0\n") - f.write("rpcuser=rt\n") - f.write("rpcpassword=rt\n") + f.write("rpcuser=" + rpc_u + "\n") + f.write("rpcpassword=" + rpc_p + "\n") f.write("port="+str(p2p_port(n))+"\n") f.write("rpcport="+str(rpc_port(n))+"\n") f.write("listenonion=0\n") return datadir +def rpc_auth_pair(n): + return 'rpcuser💻' + str(n), 'rpcpass🔑' + str(n) + def rpc_url(i, rpchost=None): - return "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i)) + rpc_u, rpc_p = rpc_auth_pair(i) + return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, rpchost or '127.0.0.1', rpc_port(i)) def wait_for_bitcoind_start(process, url, i): ''' From fd7689ff07b06cef3cc2170f212081ee58359122 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 9 May 2016 21:29:18 +0200 Subject: [PATCH 37/66] [qa] pull-tester: Fix assertion and check for run_parallel --- qa/pull-tester/rpc-tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 414d059d6..b9add6c2d 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -231,7 +231,7 @@ def runtests(): if coverage: flags.append(coverage.flag) - if len(test_list) > 1: + if len(test_list) > 1 and run_parallel > 1: # Populate cache subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags) @@ -291,7 +291,7 @@ class RPCTestHandler: stdout=subprocess.PIPE, stderr=subprocess.PIPE))) if not self.jobs: - raise IndexError('%s from empty list' % __name__) + raise IndexError('pop from empty list') while True: # Return first proc that finishes time.sleep(.5) From 7366e8137599b69df2b1efcf37c7bac086b736e8 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 20 Jun 2016 11:21:00 +0200 Subject: [PATCH 38/66] [qa] pull-tester: Start longest test first --- qa/pull-tester/rpc-tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index b9add6c2d..75dfe4bc8 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -101,6 +101,8 @@ if ENABLE_ZMQ: #Tests testScripts = [ + # longest test should go first, to favor running tests in parallel + 'p2p-fullblocktest.py', 'walletbackup.py', 'paymentdisclosure.py', 'prioritisetransaction.py', @@ -153,7 +155,6 @@ testScripts = [ 'spentindex.py', 'timestampindex.py', 'decodescript.py', - 'p2p-fullblocktest.py', 'blockchain.py', 'disablewallet.py', 'keypool.py', From 2fc450d94e8540b5b8ed2537519416033ca4538b Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 24 Jun 2016 15:32:48 +0800 Subject: [PATCH 39/66] [doc] Add OS X ZMQ requirement to QA readme --- qa/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qa/README.md b/qa/README.md index e5f881f3a..4162f5003 100644 --- a/qa/README.md +++ b/qa/README.md @@ -16,6 +16,12 @@ The python3-zmq library is required. On Ubuntu or Debian it can be installed via sudo apt-get install python3-zmq ``` +OS X +------ +``` +pip3 install pyzmq +``` + Running tests ============= @@ -36,7 +42,7 @@ Run all possible tests with qa/pull-tester/rpc-tests.py -extended By default, tests will be run in parallel if you want to specify how many -tests should be run in parallel, append `-paralell=n` (default n=4). +tests should be run in parallel, append `-parallel=n` (default n=4). If you want to create a basic coverage report for the rpc test suite, append `--coverage`. From 3c7f4ca809e1e438b66f7144ff72f6fffed43517 Mon Sep 17 00:00:00 2001 From: whythat Date: Mon, 25 Jul 2016 01:30:28 +0300 Subject: [PATCH 40/66] [qa]: add parsing for ':' argument form to rpc_url() --- qa/rpc-tests/test_framework/util.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index a8b458df5..08d01765e 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -183,7 +183,15 @@ def rpc_auth_pair(n): def rpc_url(i, rpchost=None): rpc_u, rpc_p = rpc_auth_pair(i) - return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, rpchost or '127.0.0.1', rpc_port(i)) + host = '127.0.0.1' + port = rpc_port(i) + if rpchost: + parts = rpchost.split(':') + if len(parts) == 2: + host, port = parts + else: + host = rpchost + return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port)) def wait_for_bitcoind_start(process, url, i): ''' From 1b3866fd0ece090cb3f7cba118a91833b8a494c8 Mon Sep 17 00:00:00 2001 From: whythat Date: Mon, 25 Jul 2016 01:31:05 +0300 Subject: [PATCH 41/66] [qa]: enable rpcbind_test --- qa/rpc-tests/rpcbind_test.py | 214 +++++++++++++++-------------------- 1 file changed, 89 insertions(+), 125 deletions(-) diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index 8c877464e..33330cc25 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -7,153 +7,117 @@ # Dependency: python-bitcoinrpc +from test_framework.authproxy import JSONRPCException +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, bitcoind_processes, - check_json_precision, get_rpc_proxy, - initialize_chain, rpc_port, + rpc_url, start_nodes, stop_nodes, wait_bitcoinds, ) from test_framework.netutil import addr_to_hex, get_bind_addrs, all_interfaces -import os import sys -import shutil -import tempfile -import traceback -def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected): - ''' - Start a node with requested rpcallowip and rpcbind parameters, - then try to connect, and check if the set of bound addresses - matches the expected set. - ''' - expected = [(addr_to_hex(addr), port) for (addr, port) in expected] - base_args = ['-disablewallet', '-nolisten'] - if allow_ips: - base_args += ['-rpcallowip=' + x for x in allow_ips] - binds = ['-rpcbind='+addr for addr in addresses] - nodes = start_nodes(self.num_nodes, tmpdir, [base_args + binds], connect_to) - try: - pid = bitcoind_processes[0].pid - assert_equal(set(get_bind_addrs(pid)), set(expected)) - finally: - stop_nodes(nodes) - wait_bitcoinds() +class RPCBindTest(BitcoinTestFramework): -def run_allowip_test(tmpdir, allow_ips, rpchost, rpcport): - ''' - Start a node with rpcwallow IP, and request getinfo - at a non-localhost IP. - ''' - base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - nodes = start_nodes(self.num_nodes, tmpdir, [base_args]) - try: - # connect to node through non-loopback interface - url = "http://rt:rt@%s:%d" % (rpchost, rpcport,) - node = get_rpc_proxy(url, 1) - node.getinfo() - finally: - node = None # make sure connection will be garbage collected and closed - stop_nodes(nodes) - wait_bitcoinds() + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 - -def run_test(tmpdir): - assert(sys.platform.startswith('linux')) # due to OS-specific network stats queries, this test works only on Linux - # find the first non-loopback interface for testing - non_loopback_ip = None - for name,ip in all_interfaces(): - if ip != '127.0.0.1': - non_loopback_ip = ip - break - if non_loopback_ip is None: - assert(not 'This test requires at least one non-loopback IPv4 interface') - print("Using interface %s for testing" % non_loopback_ip) - - defaultport = rpc_port(0) - - # check default without rpcallowip (IPv4 and IPv6 localhost) - run_bind_test(tmpdir, None, '127.0.0.1', [], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check default with rpcallowip (IPv6 any) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', [], - [('::0', defaultport)]) - # check only IPv4 localhost (explicit) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', defaultport)]) - # check only IPv4 localhost (explicit) with alternative port - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], - [('127.0.0.1', 32171)]) - # check only IPv4 localhost (explicit) with multiple alternative ports on same host - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], - [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) - # check only IPv6 localhost (explicit) - run_bind_test(tmpdir, ['[::1]'], '[::1]', ['[::1]'], - [('::1', defaultport)]) - # check both IPv4 and IPv6 localhost (explicit) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check only non-loopback interface - run_bind_test(tmpdir, [non_loopback_ip], non_loopback_ip, [non_loopback_ip], - [(non_loopback_ip, defaultport)]) - - # Check that with invalid rpcallowip, we are denied - run_allowip_test(tmpdir, [non_loopback_ip], non_loopback_ip, defaultport) - try: - run_allowip_test(tmpdir, ['1.1.1.1'], non_loopback_ip, defaultport) - assert(not 'Connection not denied by rpcallowip as expected') - except ValueError: + def setup_network(self): pass -def main(): - import optparse + def setup_nodes(self): + pass - parser = optparse.OptionParser(usage="%prog [options]") - parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", - help="Leave bitcoinds and test.* datadir on exit or error") - parser.add_option("--srcdir", dest="srcdir", default="../../src", - help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") - parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), - help="Root directory for datadirs") - (options, args) = parser.parse_args() + def run_bind_test(self, allow_ips, connect_to, addresses, expected): + ''' + Start a node with requested rpcallowip and rpcbind parameters, + then try to connect, and check if the set of bound addresses + matches the expected set. + ''' + expected = [(addr_to_hex(addr), port) for (addr, port) in expected] + base_args = ['-disablewallet', '-nolisten'] + if allow_ips: + base_args += ['-rpcallowip=' + x for x in allow_ips] + binds = ['-rpcbind='+addr for addr in addresses] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) + try: + pid = bitcoind_processes[0].pid + assert_equal(set(get_bind_addrs(pid)), set(expected)) + finally: + stop_nodes(self.nodes) + wait_bitcoinds() - os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] + def run_allowip_test(self, allow_ips, rpchost, rpcport): + ''' + Start a node with rpcwallow IP, and request getinfo + at a non-localhost IP. + ''' + base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) + try: + # connect to node through non-loopback interface + node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0) + node.getinfo() + finally: + node = None # make sure connection will be garbage collected and closed + stop_nodes(self.nodes) + wait_bitcoinds() - check_json_precision() + def run_test(self): + # due to OS-specific network stats queries, this test works only on Linux + assert(sys.platform.startswith('linux')) + # find the first non-loopback interface for testing + non_loopback_ip = None + for name,ip in all_interfaces(): + if ip != '127.0.0.1': + non_loopback_ip = ip + break + if non_loopback_ip is None: + assert(not 'This test requires at least one non-loopback IPv4 interface') + print("Using interface %s for testing" % non_loopback_ip) - success = False - try: - print("Initializing test directory "+options.tmpdir) - if not os.path.isdir(options.tmpdir): - os.makedirs(options.tmpdir) - initialize_chain(options.tmpdir) + defaultport = rpc_port(0) - run_test(options.tmpdir) + # check default without rpcallowip (IPv4 and IPv6 localhost) + self.run_bind_test(None, '127.0.0.1', [], + [('127.0.0.1', defaultport), ('::1', defaultport)]) + # check default with rpcallowip (IPv6 any) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], + [('::0', defaultport)]) + # check only IPv4 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], + [('127.0.0.1', defaultport)]) + # check only IPv4 localhost (explicit) with alternative port + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], + [('127.0.0.1', 32171)]) + # check only IPv4 localhost (explicit) with multiple alternative ports on same host + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], + [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) + # check only IPv6 localhost (explicit) + self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], + [('::1', defaultport)]) + # check both IPv4 and IPv6 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], + [('127.0.0.1', defaultport), ('::1', defaultport)]) + # check only non-loopback interface + self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip], + [(non_loopback_ip, defaultport)]) - success = True - - except AssertionError as e: - print("Assertion failed: "+e.message) - except Exception as e: - print("Unexpected exception caught during testing: "+str(e)) - traceback.print_tb(sys.exc_info()[2]) - - if not options.nocleanup: - print("Cleaning up") - wait_bitcoinds() - shutil.rmtree(options.tmpdir) - - if success: - print("Tests successful") - sys.exit(0) - else: - print("Failed") - sys.exit(1) + # Check that with invalid rpcallowip, we are denied + self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) + try: + self.run_allowip_test(['1.1.1.1'], non_loopback_ip, defaultport) + assert(not 'Connection not denied by rpcallowip as expected') + except JSONRPCException: + pass if __name__ == '__main__': - main() + RPCBindTest ().main () From dbb41d1082b84a325aec159840e3763f5780e53c Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 8 Jul 2016 17:59:06 +0200 Subject: [PATCH 42/66] [qa] Adjust timeouts for micro-optimization of run time --- qa/rpc-tests/test_framework/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 08d01765e..b3610802c 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -105,7 +105,7 @@ def hex_str_to_bytes(hex_str): def str_to_b64str(string): return b64encode(string.encode('utf-8')).decode('ascii') -def sync_blocks(rpc_connections, wait=1, timeout=60, allow_different_tips=False): +def sync_blocks(rpc_connections, wait=0.125, timeout=60, allow_different_tips=False): """ Wait until everybody has the same tip, and has notified all internal listeners of them. @@ -134,7 +134,7 @@ def sync_blocks(rpc_connections, wait=1, timeout=60, allow_different_tips=False) raise AssertionError("Block sync failed") -def sync_mempools(rpc_connections, wait=1, timeout=60): +def sync_mempools(rpc_connections, wait=0.5, timeout=60): """ Wait until everybody has the same transactions in their memory pools, and has notified all internal listeners of them From b25a63593ee1e5e3d15893dba037537e9e9673fd Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 7 Aug 2016 23:06:27 +0200 Subject: [PATCH 43/66] [qa] Use single cache dir for chains --- .gitignore | 5 ++-- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/test_framework/test_framework.py | 4 ++- qa/rpc-tests/test_framework/util.py | 26 +++++++++---------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 7594c69c5..60253a0db 100644 --- a/.gitignore +++ b/.gitignore @@ -105,9 +105,10 @@ linux-coverage-build linux-build win32-build qa/pull-tester/run-bitcoind-for-test.sh -qa/pull-tester/tests-config.sh -qa/pull-tester/cache/* +qa/pull-tester/tests_config.py qa/pull-tester/test.*/* +qa/tmp +qa/cache/* !src/leveldb*/Makefile diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 75dfe4bc8..5df19adde 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -229,6 +229,7 @@ def runtests(): coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args + flags.append("--cachedir=%s/qa/cache" % BUILDDIR) if coverage: flags.append(coverage.flag) diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 48cf43ff5..248dd599e 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -47,7 +47,7 @@ class BitcoinTestFramework(object): if self.setup_clean_chain: initialize_chain_clean(self.options.tmpdir, self.num_nodes) else: - initialize_chain(self.options.tmpdir, self.num_nodes) + initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir) def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir) @@ -108,6 +108,8 @@ class BitcoinTestFramework(object): help="Don't stop bitcoinds after the test execution") parser.add_option("--srcdir", dest="srcdir", default="../../src", help="Source directory containing bitcoind/bitcoin-cli (default: %default)") + parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../cache"), + help="Directory for caching pregenerated datadirs") parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), help="Root directory for datadirs") parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true", diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index b3610802c..0f5f6e121 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -213,7 +213,7 @@ def wait_for_bitcoind_start(process, url, i): raise # unknown JSON RPC exception time.sleep(0.25) -def initialize_chain(test_dir, num_nodes): +def initialize_chain(test_dir, num_nodes, cachedir): """ Create a cache of a 200-block-long chain (with wallet) for MAX_NODES Afterward, create num_nodes copies from the cache @@ -235,15 +235,15 @@ def initialize_chain(test_dir, num_nodes): # default). Therefore, if the logic between the completion of any two # adjacent calls to `generate` within a test takes longer than 2.5 minutes, # the excess will subtract from the slack. - if os.path.isdir(os.path.join("cache", "node0")): - if os.stat("cache").st_mtime + (60 * 60) < time.time(): + if os.path.isdir(os.path.join(cachedir, "node0")): + if os.stat(cachedir).st_mtime + (60 * 60) < time.time(): print("initialize_chain(): Removing stale cache") - shutil.rmtree("cache") + shutil.rmtree(cachedir) assert num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join('cache', 'node'+str(i))): + if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))): create_cache = True break @@ -251,12 +251,12 @@ def initialize_chain(test_dir, num_nodes): #find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join("cache","node"+str(i))): - shutil.rmtree(os.path.join("cache","node"+str(i))) + if os.path.isdir(os.path.join(cachedir,"node"+str(i))): + shutil.rmtree(os.path.join(cachedir,"node"+str(i))) # Create cache directories, run bitcoinds: for i in range(MAX_NODES): - datadir=initialize_datadir("cache", i) + datadir=initialize_datadir(cachedir, i) args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir="+datadir, "-discover=0" ] args.extend([ '-nuparams=5ba81b19:1', # Overwinter @@ -303,13 +303,13 @@ def initialize_chain(test_dir, num_nodes): stop_nodes(rpcs) wait_bitcoinds() for i in range(MAX_NODES): - os.remove(log_filename("cache", i, "debug.log")) - os.remove(log_filename("cache", i, "db.log")) - os.remove(log_filename("cache", i, "peers.dat")) - os.remove(log_filename("cache", i, "fee_estimates.dat")) + os.remove(log_filename(cachedir, i, "debug.log")) + os.remove(log_filename(cachedir, i, "db.log")) + os.remove(log_filename(cachedir, i, "peers.dat")) + os.remove(log_filename(cachedir, i, "fee_estimates.dat")) for i in range(num_nodes): - from_dir = os.path.join("cache", "node"+str(i)) + from_dir = os.path.join(cachedir, "node"+str(i)) to_dir = os.path.join(test_dir, "node"+str(i)) shutil.copytree(from_dir, to_dir) initialize_datadir(test_dir, i) # Overwrite port/rpcport in zcash.conf From e4d1dcc75318c39c4ea844b387dc8d081cc598b6 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 19 Aug 2016 22:41:09 +0200 Subject: [PATCH 44/66] [qa] Remove unused code --- qa/pull-tester/rpc-tests.py | 2 -- qa/rpc-tests/rpcbind_test.py | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 5df19adde..e87c9add5 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -77,8 +77,6 @@ for arg in sys.argv[1:]: #Set env vars if "BITCOIND" not in os.environ: os.environ["BITCOIND"] = BUILDDIR + '/src/zcashd' + EXEEXT -if "BITCOINCLI" not in os.environ: - os.environ["BITCOINCLI"] = BUILDDIR + '/src/zcash-cli' + EXEEXT if EXEEXT == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index 33330cc25..a024cc03b 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -23,6 +23,7 @@ from test_framework.netutil import addr_to_hex, get_bind_addrs, all_interfaces import sys + class RPCBindTest(BitcoinTestFramework): def __init__(self): @@ -120,4 +121,4 @@ class RPCBindTest(BitcoinTestFramework): pass if __name__ == '__main__': - RPCBindTest ().main () + RPCBindTest().main() From 4500a7534614aac259e780009f158b27b2b27528 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 29 Jun 2016 11:53:07 +0200 Subject: [PATCH 45/66] [qa] pull-tester: Don't mute zmq ImportError Zcash: We didn't backport the mute, so this is just conflict removal. --- qa/pull-tester/rpc-tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index e87c9add5..beb6e0ead 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -92,12 +92,12 @@ if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): if ENABLE_ZMQ: try: import zmq - except ImportError as e: - print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " \ - "to run zmq tests, see dependency info in /qa/README.md.") - raise e + except ImportError: + print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " + "to run zmq tests, see dependency info in /qa/README.md.") + # ENABLE_ZMQ=0 + raise -#Tests testScripts = [ # longest test should go first, to favor running tests in parallel 'p2p-fullblocktest.py', From 2d90c8f0d82b73b0c1e6dbe01c6b3b99201d2ad1 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Mon, 29 Aug 2016 21:01:38 -0600 Subject: [PATCH 46/66] [doc] - clarify statement about parallel jobs in rpc-tests.py --- qa/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/README.md b/qa/README.md index 4162f5003..f908b77b7 100644 --- a/qa/README.md +++ b/qa/README.md @@ -41,8 +41,8 @@ Run all possible tests with qa/pull-tester/rpc-tests.py -extended -By default, tests will be run in parallel if you want to specify how many -tests should be run in parallel, append `-parallel=n` (default n=4). +By default, tests will be run in parallel. To specify how many jobs to run, +append `-parallel=n` (default n=4). If you want to create a basic coverage report for the rpc test suite, append `--coverage`. From 352f2dc54bb01182bafa976c1335248463623f4a Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 13 Sep 2016 19:35:35 +0200 Subject: [PATCH 47/66] [qa] create_cache: Delete temp dir when done --- qa/rpc-tests/create_cache.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py index b6161e091..1ace6310d 100755 --- a/qa/rpc-tests/create_cache.py +++ b/qa/rpc-tests/create_cache.py @@ -12,9 +12,15 @@ from test_framework.test_framework import BitcoinTestFramework class CreateCache(BitcoinTestFramework): + def __init__(self): + super().__init__() + + # Test network and test nodes are not required: + self.num_nodes = 0 + self.nodes = [] + def setup_network(self): - # Don't setup any test nodes - self.options.noshutdown = True + pass def run_test(self): pass From bcbf03ebe97ad650157d6fceb3d1ee4b45e28d81 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 17 Sep 2016 11:47:51 +0200 Subject: [PATCH 48/66] [qa] Refactor RPCTestHandler to prevent TimeoutExpired --- qa/pull-tester/rpc-tests.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index beb6e0ead..e0e407ade 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -284,21 +284,27 @@ class RPCTestHandler: self.num_running += 1 t = self.test_list.pop(0) port_seed = ["--portseed=%s" % len(self.test_list)] + log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16) + log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) self.jobs.append((t, time.time(), subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed, universal_newlines=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE))) + stdout=log_stdout, + stderr=log_stderr), + log_stdout, + log_stderr)) if not self.jobs: raise IndexError('pop from empty list') while True: # Return first proc that finishes time.sleep(.5) for j in self.jobs: - (name, time0, proc) = j + (name, time0, proc, log_out, log_err) = j if proc.poll() is not None: - (stdout, stderr) = proc.communicate(timeout=3) + log_out.seek(0), log_err.seek(0) + [stdout, stderr] = [l.read().decode('utf-8') for l in (log_out, log_err)] + log_out.close(), log_err.close() passed = stderr == "" and proc.returncode == 0 self.num_running -= 1 self.jobs.remove(j) From d014a305664c1b3470583b8783359682a0b6f4f5 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 22 Sep 2016 12:41:01 +0200 Subject: [PATCH 49/66] [qa] pull-tester: Only print output when failed --- qa/pull-tester/rpc-tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index e0e407ade..c49f85513 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -248,8 +248,8 @@ def runtests(): time_sum += duration print('\n' + BOLD[1] + name + BOLD[0] + ":") - print(stdout) - print('stderr:\n' if not stderr == '' else '', stderr) + print('' if passed else stdout + '\n', end='') + print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='') results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration)) results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0] From 0a73a0fbd92571a29221177e85658c0396dcc1b2 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 7 Nov 2016 23:00:54 +0100 Subject: [PATCH 50/66] [qa] test_framework: Exit when tmpdir exists --- qa/rpc-tests/test_framework/test_framework.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 248dd599e..4048cf2a2 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -137,16 +137,11 @@ class BitcoinTestFramework(object): success = False try: - if not os.path.isdir(self.options.tmpdir): - os.makedirs(self.options.tmpdir) + os.makedirs(self.options.tmpdir, exist_ok=False) self.setup_chain() - self.setup_network() - self.run_test() - success = True - except JSONRPCException as e: print("JSONRPC error: "+e.error['message']) traceback.print_tb(sys.exc_info()[2]) From 8b3c39ea80bbe34e884a6c3df41ffbad17e2648b Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 7 Nov 2016 22:33:22 +0100 Subject: [PATCH 51/66] [qa] rpc-tests: Apply random offset to portseed This helps to skip over resources, which are blocked by regtest bitcoind zombie nodes --- qa/pull-tester/rpc-tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index c49f85513..691fc7e66 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -276,6 +276,10 @@ class RPCTestHandler: self.test_list = test_list self.flags = flags self.num_running = 0 + # In case there is a graveyard of zombie bitcoinds, we can apply a + # pseudorandom offset to hopefully jump over them. + # (625 is PORT_RANGE/MAX_NODES) + self.portseed_offset = int(time.time() * 1000) % 625 self.jobs = [] def get_next(self): @@ -283,7 +287,7 @@ class RPCTestHandler: # Add tests self.num_running += 1 t = self.test_list.pop(0) - port_seed = ["--portseed=%s" % len(self.test_list)] + port_seed = ["--portseed={}".format(len(self.test_list) + self.portseed_offset)] log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16) log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) self.jobs.append((t, From 3accdb2fc7db8a59e4977a38a8fe9d6deba36c3e Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Sat, 3 Dec 2016 15:46:33 -0500 Subject: [PATCH 52/66] Reorder RPC tests for running time --- qa/pull-tester/rpc-tests.py | 79 +++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 691fc7e66..b62fd4e4f 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -100,31 +100,48 @@ if ENABLE_ZMQ: testScripts = [ # longest test should go first, to favor running tests in parallel - 'p2p-fullblocktest.py', - 'walletbackup.py', - 'paymentdisclosure.py', - 'prioritisetransaction.py', - 'wallet_treestate.py', - 'wallet_anchorfork.py', - 'wallet_changeaddresses.py', - 'wallet_changeindicator.py', - 'wallet_import_export.py', - 'wallet_sendmany_any_taddr.py', - 'wallet_shieldingcoinbase.py', - 'wallet_shieldcoinbase_sprout.py', - 'wallet_shieldcoinbase_sapling.py', - 'wallet_listreceived.py', + # vv Tests less than 5m vv + 'mergetoaddress_sapling.py', 'wallet.py', + 'wallet_shieldcoinbase_sprout.py', + 'sprout_sapling_migration.py', + 'remove_sprout_shielding.py', + 'zcjoinsplitdoublespend.py', + # vv Tests less than 2m vv + 'zcjoinsplit.py', + 'mergetoaddress_mixednotes.py', + 'wallet_shieldcoinbase_sapling.py', + 'turnstile.py', + 'walletbackup.py', + 'zkey_import_export.py', + 'prioritisetransaction.py', + 'wallet_changeaddresses.py', + 'wallet_listreceived.py', + 'mempool_tx_expiry.py', + 'finalsaplingroot.py', 'wallet_overwintertx.py', 'wallet_persistence.py', - 'wallet_nullifiers.py', + 'wallet_listnotes.py', + # v-- This test is down here to spread the CPU load. + 'mergetoaddress_sprout.py', + # vv Tests less than 60s vv + 'fundrawtransaction.py', + 'reorg_limit.py', + 'mempool_limit.py', + 'p2p-fullblocktest.py', + 'paymentdisclosure.py', + # vv Tests less than 30s vv 'wallet_1941.py', 'wallet_addresses.py', + 'wallet_anchorfork.py', + 'wallet_changeindicator.py', + 'wallet_import_export.py', + 'wallet_nullifiers.py', 'wallet_sapling.py', - 'wallet_listnotes.py', - 'mergetoaddress_sprout.py', - 'mergetoaddress_sapling.py', - 'mergetoaddress_mixednotes.py', + 'wallet_sendmany_any_taddr.py', + # v-- This test is down here to spread the CPU load. + 'wallet_shieldingcoinbase.py', + 'wallet_treestate.py', 'listtransactions.py', 'mempool_resurrect_test.py', 'txn_doublespend.py', @@ -133,17 +150,14 @@ testScripts = [ 'rawtransactions.py', 'getrawtransaction_insight.py', 'rest.py', - 'mempool_limit.py', 'mempool_spendcoinbase.py', 'mempool_reorg.py', 'mempool_nu_activation.py', - 'mempool_tx_expiry.py', 'httpbasics.py', 'multi_rpc.py', 'zapwallettxes.py', 'proxy_test.py', 'merkle_blocks.py', - 'fundrawtransaction.py', 'signrawtransactions.py', 'signrawtransaction_offline.py', 'key_import_export.py', @@ -156,10 +170,6 @@ testScripts = [ 'blockchain.py', 'disablewallet.py', 'keypool.py', - 'zcjoinsplit.py', - 'zcjoinsplitdoublespend.py', - 'zkey_import_export.py', - 'reorg_limit.py', 'getblocktemplate.py', 'bip65-cltv-p2p.py', 'bipdersig-p2p.py', @@ -169,10 +179,7 @@ testScripts = [ 'p2p_txexpiringsoon.py', 'p2p_node_bloom.py', 'regtest_signrawtransaction.py', - 'finalsaplingroot.py', 'shorter_block_times.py', - 'sprout_sapling_migration.py', - 'turnstile.py', 'mining_shielded_coinbase.py', 'coinbase_funding_streams.py', 'framework.py', @@ -181,22 +188,26 @@ testScripts = [ 'upgrade_golden.py', 'post_heartwood_rollback.py', 'feature_logging.py', - 'remove_sprout_shielding.py', 'feature_walletfile.py', ] if ENABLE_ZMQ: testScripts.append('zmq_test.py') testScriptsExt = [ - 'getblocktemplate_longpoll.py', - 'getblocktemplate_proposals.py', 'pruning.py', + # vv Tests less than 20m vv + 'smartfees.py', + # vv Tests less than 5m vv + # vv Tests less than 2m vv + 'getblocktemplate_longpoll.py', + # vv Tests less than 60s vv + 'rpcbind_test.py', + # vv Tests less than 30s vv + 'getblocktemplate_proposals.py', 'forknotify.py', 'hardforkdetection.py', 'invalidateblock.py', 'receivedby.py', - 'rpcbind_test.py', - 'smartfees.py', 'maxblocksinflight.py', 'invalidblockrequest.py', # 'forknotify.py', From 2eab4f750388b2665a169d1c816a9050b11f10f5 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Sun, 4 Dec 2016 15:44:41 -0500 Subject: [PATCH 53/66] remove obsolete run-bitcoind-for-test.sh --- .gitignore | 1 - configure.ac | 1 - qa/pull-tester/run-bitcoind-for-test.sh.in | 38 ---------------------- 3 files changed, 40 deletions(-) delete mode 100755 qa/pull-tester/run-bitcoind-for-test.sh.in diff --git a/.gitignore b/.gitignore index 60253a0db..b984933bb 100644 --- a/.gitignore +++ b/.gitignore @@ -104,7 +104,6 @@ afl-temp linux-coverage-build linux-build win32-build -qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/tests_config.py qa/pull-tester/test.*/* qa/tmp diff --git a/configure.ac b/configure.ac index 554e5f363..3b85e97f7 100644 --- a/configure.ac +++ b/configure.ac @@ -968,7 +968,6 @@ AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) -AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py]) diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in deleted file mode 100755 index 122c5523b..000000000 --- a/qa/pull-tester/run-bitcoind-for-test.sh.in +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# Copyright (c) 2013-2014 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . -# -ZCASH_LOAD_TIMEOUT=500 -DATADIR="@abs_top_builddir@/.zcash" -rm -rf "$DATADIR" -mkdir -p "$DATADIR"/regtest -touch "$DATADIR/zcash.conf" -touch "$DATADIR/regtest/debug.log" -tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" & -WAITER=$! -PORT=`expr 10000 + $$ % 55536` -"@abs_top_builddir@/src/zcashd@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -checkmempool=0 -relaypriority=0 -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` & -BITCOIND=$! - -#Install a watchdog. -(sleep "$ZCASH_LOAD_TIMEOUT" && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)& -wait $WAITER - -if [ -n "$TIMEOUT" ]; then - timeout "$TIMEOUT"s "$@" $PORT - RETURN=$? -else - "$@" $PORT - RETURN=$? -fi - -(sleep "$ZCASH_LOAD_TIMEOUT" && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)& -kill $BITCOIND && wait $BITCOIND - -# timeout returns 124 on timeout, otherwise the return value of the child - -# If $RETURN is not 0, the test failed. Dump the tail of the debug log. -if [ $RETURN -ne 0 ]; then tail -n 200 $DATADIR/regtest/debug.log; fi - -exit $RETURN From 41c2bc1e05f5a1fb7fe71e1dace44e5878d9e0b3 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 30 Jan 2017 14:57:27 -0800 Subject: [PATCH 54/66] Use configparser in rpc-tests.py Remove the use of wildcard imports in rpc-tests.py and replace with configparser. --- configure.ac | 2 +- qa/pull-tester/rpc-tests.py | 33 ++++++++++++++---------------- qa/pull-tester/tests_config.ini.in | 25 ++++++++++++++++++++++ qa/pull-tester/tests_config.py.in | 14 ------------- 4 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 qa/pull-tester/tests_config.ini.in delete mode 100644 qa/pull-tester/tests_config.py.in diff --git a/configure.ac b/configure.ac index 3b85e97f7..060b80263 100644 --- a/configure.ac +++ b/configure.ac @@ -968,7 +968,7 @@ AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) -AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) +AC_CONFIG_FILES([qa/pull-tester/tests_config.ini],[chmod +x qa/pull-tester/tests_config.ini]) AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py]) dnl boost's m4 checks do something really nasty: they export these vars. As a diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index b62fd4e4f..ad83226a4 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -21,6 +21,7 @@ For a description of arguments recognized by test scripts, see """ +import configparser import os import time import shutil @@ -29,26 +30,22 @@ import subprocess import tempfile import re -sys.path.append("qa/pull-tester/") -from tests_config import * - BOLD = ("","") if os.name == 'posix': # primitive formatting on supported # terminal via ANSI escape sequences: BOLD = ('\033[0m', '\033[1m') -RPC_TESTS_DIR = SRCDIR + '/qa/rpc-tests/' +# Read config generated by configure. +config = configparser.ConfigParser() +config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) -#If imported values are not defined then set to zero (or disabled) -if 'ENABLE_WALLET' not in vars(): - ENABLE_WALLET=0 -if 'ENABLE_BITCOIND' not in vars(): - ENABLE_BITCOIND=0 -if 'ENABLE_UTILS' not in vars(): - ENABLE_UTILS=0 -if 'ENABLE_ZMQ' not in vars(): - ENABLE_ZMQ=0 +ENABLE_WALLET = config["components"]["ENABLE_WALLET"] == "True" +ENABLE_UTILS = config["components"]["ENABLE_UTILS"] == "True" +ENABLE_BITCOIND = config["components"]["ENABLE_BITCOIND"] == "True" +ENABLE_ZMQ = config["components"]["ENABLE_ZMQ"] == "True" + +RPC_TESTS_DIR = config["environment"]["SRCDIR"] + '/qa/rpc-tests/' ENABLE_COVERAGE=0 @@ -76,15 +73,15 @@ for arg in sys.argv[1:]: #Set env vars if "BITCOIND" not in os.environ: - os.environ["BITCOIND"] = BUILDDIR + '/src/zcashd' + EXEEXT + os.environ["BITCOIND"] = config["environment"]["BUILDDIR"] + '/src/zcashd' + config["environment"]["EXEEXT"] -if EXEEXT == ".exe" and "-win" not in opts: +if config["environment"]["EXEEXT"] == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 print("Win tests currently disabled by default. Use -win option to enable") sys.exit(0) -if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): +if not (ENABLE_WALLET and ENABLE_UTILS and ENABLE_BITCOIND): print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") sys.exit(0) @@ -237,8 +234,8 @@ def runtests(): if ENABLE_COVERAGE: coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) - flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args - flags.append("--cachedir=%s/qa/cache" % BUILDDIR) + flags = ["--srcdir=%s/src" % config["environment"]["BUILDDIR"]] + passon_args + flags.append("--cachedir=%s/qa/cache" % config["environment"]["BUILDDIR"]) if coverage: flags.append(coverage.flag) diff --git a/qa/pull-tester/tests_config.ini.in b/qa/pull-tester/tests_config.ini.in new file mode 100644 index 000000000..8317caaeb --- /dev/null +++ b/qa/pull-tester/tests_config.ini.in @@ -0,0 +1,25 @@ +# Copyright (c) 2013-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. + +# These environment variables are set by the build process and read by +# rpc-tests.py + +[DEFAULT] +# Provides default values for whether different components are enabled +ENABLE_WALLET=False +ENABLE_UTILS=False +ENABLE_BITCOIND=False +ENABLE_ZMQ=False + +[environment] +SRCDIR=@abs_top_srcdir@ +BUILDDIR=@abs_top_builddir@ +EXEEXT=@EXEEXT@ + +[components] +# Which components are enabled. These are commented out by `configure` if they were disabled when running config. +@ENABLE_WALLET_TRUE@ENABLE_WALLET=True +@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=True +@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=True +@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=True diff --git a/qa/pull-tester/tests_config.py.in b/qa/pull-tester/tests_config.py.in deleted file mode 100644 index 7aa70525d..000000000 --- a/qa/pull-tester/tests_config.py.in +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2013-2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . - -SRCDIR="@abs_top_srcdir@" -BUILDDIR="@abs_top_builddir@" -EXEEXT="@EXEEXT@" - -# These will turn into comments if they were disabled when configuring. -@ENABLE_WALLET_TRUE@ENABLE_WALLET=1 -@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1 -@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1 -@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1 From 389b70cafe10154c928d5c752253bcf064e65827 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 31 Jan 2017 10:15:40 -0800 Subject: [PATCH 55/66] Use argparse in rpc_tests.py This commit replaces the roll-your-own argument parsing in rpc_tests.py with Python's standard library argparse. --- qa/pull-tester/rpc-tests.py | 81 ++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index ad83226a4..2a6ec9310 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -21,6 +21,7 @@ For a description of arguments recognized by test scripts, see """ +import argparse import configparser import os import time @@ -30,6 +31,19 @@ import subprocess import tempfile import re +# Parse arguments and pass through unrecognised args +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument('--coverage', action='store_true') +parser.add_argument('-extended', action='store_true') +parser.add_argument('--help', '-h', '-?', action='store_true') +parser.add_argument('--parallel', type=int, default=4) +parser.add_argument('-win', action='store_true') +(args, unknown_args) = parser.parse_known_args() + +#Create a set to store arguments and create the passon string +tests = set(arg for arg in unknown_args if arg[:2] != "--") +passon_args = [arg for arg in unknown_args if arg[:2] == "--"] + BOLD = ("","") if os.name == 'posix': # primitive formatting on supported @@ -47,35 +61,14 @@ ENABLE_ZMQ = config["components"]["ENABLE_ZMQ"] == "True" RPC_TESTS_DIR = config["environment"]["SRCDIR"] + '/qa/rpc-tests/' -ENABLE_COVERAGE=0 - -#Create a set to store arguments and create the passon string -opts = set() -passon_args = [] -PASSON_REGEX = re.compile("^--") -PARALLEL_REGEX = re.compile('^-parallel=') - -print_help = False -run_parallel = 4 - -for arg in sys.argv[1:]: - if arg == "--help" or arg == "-h" or arg == "-?": - print_help = True - break - if arg == '--coverage': - ENABLE_COVERAGE = 1 - elif PASSON_REGEX.match(arg): - passon_args.append(arg) - elif PARALLEL_REGEX.match(arg): - run_parallel = int(arg.split(sep='=', maxsplit=1)[1]) - else: - opts.add(arg) +print_help = args.help +run_parallel = args.parallel #Set env vars if "BITCOIND" not in os.environ: os.environ["BITCOIND"] = config["environment"]["BUILDDIR"] + '/src/zcashd' + config["environment"]["EXEEXT"] -if config["environment"]["EXEEXT"] == ".exe" and "-win" not in opts: +if config["environment"]["EXEEXT"] == ".exe" and not args.win: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 print("Win tests currently disabled by default. Use -win option to enable") @@ -95,7 +88,7 @@ if ENABLE_ZMQ: # ENABLE_ZMQ=0 raise -testScripts = [ +BASE_SCRIPTS= [ # longest test should go first, to favor running tests in parallel # vv Tests less than 5m vv 'mergetoaddress_sapling.py', @@ -187,10 +180,9 @@ testScripts = [ 'feature_logging.py', 'feature_walletfile.py', ] -if ENABLE_ZMQ: - testScripts.append('zmq_test.py') +ZMQ_SCRIPTS = ["zmq_test.py"] -testScriptsExt = [ +EXTENDED_SCRIPTS = [ 'pruning.py', # vv Tests less than 20m vv 'smartfees.py', @@ -212,26 +204,39 @@ testScriptsExt = [ 'wallet_db_flush.py', ] +ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS def runtests(): - test_list = [] - if '-extended' in opts: - test_list = testScripts + testScriptsExt - elif len(opts) == 0 or (len(opts) == 1 and "-win" in opts): - test_list = testScripts + # Build list of tests + if len(tests) != 0: + # Individual tests have been specified. Run specified tests that exist + # in the ALL_SCRIPTS list. Accept the name with or without .py extension. + test_list = [t for t in ALL_SCRIPTS if + (t in tests or re.sub(".py$", "", t) in tests)] + if len(test_list) == 0: + print("No valid test scripts specified. Check that your test is in one " + "of the test lists in rpc-tests.py or run rpc-tests.py with no arguments to run all tests") + sys.exit(0) else: - for t in testScripts + testScriptsExt: - if t in opts or re.sub(".py$", "", t) in opts: - test_list.append(t) + # No individual tests have been specified. Run base tests, and + # optionally ZMQ tests and extended tests. + test_list = BASE_SCRIPTS + if ENABLE_ZMQ: + test_list += ZMQ_SCRIPTS + if args.extended: + test_list += EXTENDED_SCRIPTS + # TODO: BASE_SCRIPTS and EXTENDED_SCRIPTS are sorted by runtime + # (for parallel running efficiency). This combined list will is no + # longer sorted. - if print_help: + if args.help: # Only print help of the first script and exit subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h']) sys.exit(0) coverage = None - if ENABLE_COVERAGE: + if args.coverage: coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) flags = ["--srcdir=%s/src" % config["environment"]["BUILDDIR"]] + passon_args From cc099b3b7b0f52268eb7de2d9eac0954abc7f363 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 31 Jan 2017 11:32:49 -0800 Subject: [PATCH 56/66] Improve rpc-tests.py arguments A few miscellaneous improvements to rpc-tests.py command line arguments: - make all arguments start with double dash for consistency - improve help text and output - add nozmq argument to explicitly exclude the ZMQ tests - change 'parallel' to 'jobs' --- qa/pull-tester/rpc-tests.py | 48 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 2a6ec9310..4a2ca8087 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -2,19 +2,11 @@ # 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. - """ -Run Regression Test Suite +rpc-tests.py - run regression test suite This module calls down into individual test cases via subprocess. It will -forward all unrecognized arguments onto the individual test scripts, other -than: - - - `-extended`: run the "extended" test suite in addition to the basic one. - - `-win`: signal that this is running in a Windows environment, and we - should run the tests. - - `--coverage`: this generates a basic coverage report for the RPC - interface. +forward all unrecognized arguments onto the individual test scripts. For a description of arguments recognized by test scripts, see `qa/pull-tester/test_framework/test_framework.py:BitcoinTestFramework.main`. @@ -32,12 +24,18 @@ import tempfile import re # Parse arguments and pass through unrecognised args -parser = argparse.ArgumentParser(add_help=False) -parser.add_argument('--coverage', action='store_true') -parser.add_argument('-extended', action='store_true') -parser.add_argument('--help', '-h', '-?', action='store_true') -parser.add_argument('--parallel', type=int, default=4) -parser.add_argument('-win', action='store_true') +parser = argparse.ArgumentParser(add_help=False, + usage='%(prog)s [rpc-test.py options] [script options] [scripts]', + description=__doc__, + epilog=''' +Help text and arguments for individual test script:''', + formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') +parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') +parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') +parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.') +parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') +parser.add_argument('--win', action='store_true', help='signal that this is running in a Windows environment and that we should run the tests') (args, unknown_args) = parser.parse_known_args() #Create a set to store arguments and create the passon string @@ -57,12 +55,12 @@ config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) ENABLE_WALLET = config["components"]["ENABLE_WALLET"] == "True" ENABLE_UTILS = config["components"]["ENABLE_UTILS"] == "True" ENABLE_BITCOIND = config["components"]["ENABLE_BITCOIND"] == "True" -ENABLE_ZMQ = config["components"]["ENABLE_ZMQ"] == "True" +ENABLE_ZMQ = config["components"]["ENABLE_ZMQ"] == "True" and not args.nozmq RPC_TESTS_DIR = config["environment"]["SRCDIR"] + '/qa/rpc-tests/' print_help = args.help -run_parallel = args.parallel +jobs = args.jobs #Set env vars if "BITCOIND" not in os.environ: @@ -71,7 +69,7 @@ if "BITCOIND" not in os.environ: if config["environment"]["EXEEXT"] == ".exe" and not args.win: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 - print("Win tests currently disabled by default. Use -win option to enable") + print("Win tests currently disabled by default. Use --win option to enable") sys.exit(0) if not (ENABLE_WALLET and ENABLE_UTILS and ENABLE_BITCOIND): @@ -83,9 +81,8 @@ if ENABLE_ZMQ: try: import zmq except ImportError: - print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " - "to run zmq tests, see dependency info in /qa/README.md.") - # ENABLE_ZMQ=0 + print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests." + "To run zmq tests, see dependency info in /qa/README.md.") raise BASE_SCRIPTS= [ @@ -230,7 +227,8 @@ def runtests(): # longer sorted. if args.help: - # Only print help of the first script and exit + # Print help for rpc-tests.py, then print help of the first script and exit. + parser.print_help() subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h']) sys.exit(0) @@ -244,7 +242,7 @@ def runtests(): if coverage: flags.append(coverage.flag) - if len(test_list) > 1 and run_parallel > 1: + if len(test_list) > 1 and jobs > 1: # Populate cache subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags) @@ -252,7 +250,7 @@ def runtests(): max_len_name = len(max(test_list, key=len)) time_sum = 0 time0 = time.time() - job_queue = RPCTestHandler(run_parallel, test_list, flags) + job_queue = RPCTestHandler(jobs, test_list, flags) results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0] all_passed = True for _ in range(len(test_list)): From 6aad43b63f1968d81f6f6e23d5c91d0cac3095d8 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 6 Feb 2017 09:07:14 -0500 Subject: [PATCH 57/66] Refactor rpc-tests.py - add main() - remove global variables --- qa/pull-tester/rpc-tests.py | 185 +++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 4a2ca8087..b7d66b924 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -23,69 +23,8 @@ import subprocess import tempfile import re -# Parse arguments and pass through unrecognised args -parser = argparse.ArgumentParser(add_help=False, - usage='%(prog)s [rpc-test.py options] [script options] [scripts]', - description=__doc__, - epilog=''' -Help text and arguments for individual test script:''', - formatter_class=argparse.RawTextHelpFormatter) -parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') -parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') -parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') -parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.') -parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') -parser.add_argument('--win', action='store_true', help='signal that this is running in a Windows environment and that we should run the tests') -(args, unknown_args) = parser.parse_known_args() - -#Create a set to store arguments and create the passon string -tests = set(arg for arg in unknown_args if arg[:2] != "--") -passon_args = [arg for arg in unknown_args if arg[:2] == "--"] - -BOLD = ("","") -if os.name == 'posix': - # primitive formatting on supported - # terminal via ANSI escape sequences: - BOLD = ('\033[0m', '\033[1m') - -# Read config generated by configure. -config = configparser.ConfigParser() -config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) - -ENABLE_WALLET = config["components"]["ENABLE_WALLET"] == "True" -ENABLE_UTILS = config["components"]["ENABLE_UTILS"] == "True" -ENABLE_BITCOIND = config["components"]["ENABLE_BITCOIND"] == "True" -ENABLE_ZMQ = config["components"]["ENABLE_ZMQ"] == "True" and not args.nozmq - -RPC_TESTS_DIR = config["environment"]["SRCDIR"] + '/qa/rpc-tests/' - -print_help = args.help -jobs = args.jobs - -#Set env vars -if "BITCOIND" not in os.environ: - os.environ["BITCOIND"] = config["environment"]["BUILDDIR"] + '/src/zcashd' + config["environment"]["EXEEXT"] - -if config["environment"]["EXEEXT"] == ".exe" and not args.win: - # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 - # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 - print("Win tests currently disabled by default. Use --win option to enable") - sys.exit(0) - -if not (ENABLE_WALLET and ENABLE_UTILS and ENABLE_BITCOIND): - print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") - sys.exit(0) - -# python3-zmq may not be installed. Handle this gracefully and with some helpful info -if ENABLE_ZMQ: - try: - import zmq - except ImportError: - print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests." - "To run zmq tests, see dependency info in /qa/README.md.") - raise - BASE_SCRIPTS= [ + # Scripts that are run by the travis build process # longest test should go first, to favor running tests in parallel # vv Tests less than 5m vv 'mergetoaddress_sapling.py', @@ -177,9 +116,15 @@ BASE_SCRIPTS= [ 'feature_logging.py', 'feature_walletfile.py', ] -ZMQ_SCRIPTS = ["zmq_test.py"] + +ZMQ_SCRIPTS = [ + # ZMQ test can only be run if bitcoin was built with zmq-enabled. + # call rpc_tests.py with -nozmq to explicitly exclude these tests. + "zmq_test.py"] EXTENDED_SCRIPTS = [ + # These tests are not run by the travis build process. + # Longest test should go first, to favor running tests in parallel 'pruning.py', # vv Tests less than 20m vv 'smartfees.py', @@ -203,7 +148,55 @@ EXTENDED_SCRIPTS = [ ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS -def runtests(): +def main(): + # Parse arguments and pass through unrecognised args + parser = argparse.ArgumentParser(add_help=False, + usage='%(prog)s [rpc-test.py options] [script options] [scripts]', + description=__doc__, + epilog=''' + Help text and arguments for individual test script:''', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') + parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') + parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') + parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') + parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.') + parser.add_argument('--win', action='store_true', help='signal that this is running in a Windows environment and that we should run the tests') + (args, unknown_args) = parser.parse_known_args() + + # Create a set to store arguments and create the passon string + tests = set(arg for arg in unknown_args if arg[:2] != "--") + passon_args = [arg for arg in unknown_args if arg[:2] == "--"] + + # Read config generated by configure. + config = configparser.ConfigParser() + config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) + + enable_wallet = config["components"]["ENABLE_WALLET"] == "True" + enable_utils = config["components"]["ENABLE_UTILS"] == "True" + enable_bitcoind = config["components"]["ENABLE_BITCOIND"] == "True" + enable_zmq = config["components"]["ENABLE_ZMQ"] == "True" and not args.nozmq + + if config["environment"]["EXEEXT"] == ".exe" and not args.win: + # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 + # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 + print("Win tests currently disabled by default. Use --win option to enable") + sys.exit(0) + + if not (enable_wallet and enable_utils and enable_bitcoind): + print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") + print("Rerun `configure` with -enable-wallet, -with-utils and -with-daemon and rerun make") + sys.exit(0) + + # python3-zmq may not be installed. Handle this gracefully and with some helpful info + if enable_zmq: + try: + import zmq + except ImportError: + print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests." + "To run zmq tests, see dependency info in /qa/README.md.") + raise + # Build list of tests if len(tests) != 0: # Individual tests have been specified. Run specified tests that exist @@ -213,12 +206,15 @@ def runtests(): if len(test_list) == 0: print("No valid test scripts specified. Check that your test is in one " "of the test lists in rpc-tests.py or run rpc-tests.py with no arguments to run all tests") + print("Scripts not found:") + print(tests) sys.exit(0) + else: # No individual tests have been specified. Run base tests, and # optionally ZMQ tests and extended tests. test_list = BASE_SCRIPTS - if ENABLE_ZMQ: + if enable_zmq: test_list += ZMQ_SCRIPTS if args.extended: test_list += EXTENDED_SCRIPTS @@ -229,30 +225,47 @@ def runtests(): if args.help: # Print help for rpc-tests.py, then print help of the first script and exit. parser.print_help() - subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h']) + subprocess.check_call((config["environment"]["SRCDIR"] + '/qa/rpc-tests/' + test_list[0]).split() + ['-h']) sys.exit(0) - coverage = None + runtests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args) - if args.coverage: +def runtests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]): + BOLD = ("","") + if os.name == 'posix': + # primitive formatting on supported + # terminal via ANSI escape sequences: + BOLD = ('\033[0m', '\033[1m') + + #Set env vars + if "BITCOIND" not in os.environ: + os.environ["BITCOIND"] = build_dir + '/src/zcashd' + exeext + + tests_dir = src_dir + '/qa/rpc-tests/' + + flags = ["--srcdir=" + src_dir] + args + flags.append("--cachedir=%s/qa/cache" % build_dir) + + if enable_coverage: coverage = RPCCoverage() - print("Initializing coverage directory at %s\n" % coverage.dir) - flags = ["--srcdir=%s/src" % config["environment"]["BUILDDIR"]] + passon_args - flags.append("--cachedir=%s/qa/cache" % config["environment"]["BUILDDIR"]) - if coverage: flags.append(coverage.flag) + print("Initializing coverage directory at %s\n" % coverage.dir) + else: + coverage = None if len(test_list) > 1 and jobs > 1: # Populate cache - subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags) + subprocess.check_output([tests_dir + 'create_cache.py'] + flags) #Run Tests - max_len_name = len(max(test_list, key=len)) + all_passed = True time_sum = 0 time0 = time.time() - job_queue = RPCTestHandler(jobs, test_list, flags) + + job_queue = RPCTestHandler(jobs, tests_dir, test_list, flags) + + max_len_name = len(max(test_list, key=len)) results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0] - all_passed = True for _ in range(len(test_list)): (name, stdout, stderr, passed, duration) = job_queue.get_next() all_passed = all_passed and passed @@ -261,8 +274,10 @@ def runtests(): print('\n' + BOLD[1] + name + BOLD[0] + ":") print('' if passed else stdout + '\n', end='') print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='') - results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration)) + + results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) + results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0] print(results) print("\nRuntime: %s s" % (int(time.time() - time0))) @@ -275,15 +290,15 @@ def runtests(): sys.exit(not all_passed) - class RPCTestHandler: """ Trigger the testscrips passed in via the list. """ - def __init__(self, num_tests_parallel, test_list=None, flags=None): + def __init__(self, num_tests_parallel, tests_dir, test_list=None, flags=None): assert(num_tests_parallel >= 1) self.num_jobs = num_tests_parallel + self.tests_dir = tests_dir self.test_list = test_list self.flags = flags self.num_running = 0 @@ -303,7 +318,7 @@ class RPCTestHandler: log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) self.jobs.append((t, time.time(), - subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed, + subprocess.Popen((self.tests_dir + t).split() + self.flags + port_seed, universal_newlines=True, stdout=log_stdout, stderr=log_stderr), @@ -368,10 +383,10 @@ class RPCCoverage(object): """ # This is shared from `qa/rpc-tests/test-framework/coverage.py` - REFERENCE_FILENAME = 'rpc_interface.txt' - COVERAGE_FILE_PREFIX = 'coverage.' + reference_filename = 'rpc_interface.txt' + coverage_file_prefix = 'coverage.' - coverage_ref_filename = os.path.join(self.dir, REFERENCE_FILENAME) + coverage_ref_filename = os.path.join(self.dir, reference_filename) coverage_filenames = set() all_cmds = set() covered_cmds = set() @@ -384,7 +399,7 @@ class RPCCoverage(object): for root, dirs, files in os.walk(self.dir): for filename in files: - if filename.startswith(COVERAGE_FILE_PREFIX): + if filename.startswith(coverage_file_prefix): coverage_filenames.add(os.path.join(root, filename)) for filename in coverage_filenames: @@ -395,4 +410,4 @@ class RPCCoverage(object): if __name__ == '__main__': - runtests() + main() From 4f4f8d5ad790f761d443fbcb7ad3a09be89540f6 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 17 Feb 2017 14:22:56 -0500 Subject: [PATCH 58/66] Various review markups for rpc-tests.py improvements --- qa/pull-tester/rpc-tests.py | 30 ++++++++++++++++-------------- qa/pull-tester/tests_config.ini.in | 15 ++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index b7d66b924..67f553659 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -8,6 +8,8 @@ rpc-tests.py - run regression test suite This module calls down into individual test cases via subprocess. It will forward all unrecognized arguments onto the individual test scripts. +RPC tests are disabled on Windows by default. Use --force to run them anyway. + For a description of arguments recognized by test scripts, see `qa/pull-tester/test_framework/test_framework.py:BitcoinTestFramework.main`. @@ -25,7 +27,7 @@ import re BASE_SCRIPTS= [ # Scripts that are run by the travis build process - # longest test should go first, to favor running tests in parallel + # Longest test should go first, to favor running tests in parallel # vv Tests less than 5m vv 'mergetoaddress_sapling.py', 'wallet.py', @@ -158,11 +160,11 @@ def main(): formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') + parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).') parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') - parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.') - parser.add_argument('--win', action='store_true', help='signal that this is running in a Windows environment and that we should run the tests') - (args, unknown_args) = parser.parse_known_args() + parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') + args, unknown_args = parser.parse_known_args() # Create a set to store arguments and create the passon string tests = set(arg for arg in unknown_args if arg[:2] != "--") @@ -172,15 +174,15 @@ def main(): config = configparser.ConfigParser() config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) - enable_wallet = config["components"]["ENABLE_WALLET"] == "True" - enable_utils = config["components"]["ENABLE_UTILS"] == "True" - enable_bitcoind = config["components"]["ENABLE_BITCOIND"] == "True" - enable_zmq = config["components"]["ENABLE_ZMQ"] == "True" and not args.nozmq + enable_wallet = config["components"].getboolean("ENABLE_WALLET") + enable_utils = config["components"].getboolean("ENABLE_UTILS") + enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND") + enable_zmq = config["components"].getboolean("ENABLE_ZMQ") and not args.nozmq - if config["environment"]["EXEEXT"] == ".exe" and not args.win: + if config["environment"]["EXEEXT"] == ".exe" and not args.force: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 - print("Win tests currently disabled by default. Use --win option to enable") + print("Tests currently disabled on Windows by default. Use --force option to enable") sys.exit(0) if not (enable_wallet and enable_utils and enable_bitcoind): @@ -198,12 +200,12 @@ def main(): raise # Build list of tests - if len(tests) != 0: + if tests: # Individual tests have been specified. Run specified tests that exist # in the ALL_SCRIPTS list. Accept the name with or without .py extension. test_list = [t for t in ALL_SCRIPTS if (t in tests or re.sub(".py$", "", t) in tests)] - if len(test_list) == 0: + if not test_list: print("No valid test scripts specified. Check that your test is in one " "of the test lists in rpc-tests.py or run rpc-tests.py with no arguments to run all tests") print("Scripts not found:") @@ -228,9 +230,9 @@ def main(): subprocess.check_call((config["environment"]["SRCDIR"] + '/qa/rpc-tests/' + test_list[0]).split() + ['-h']) sys.exit(0) - runtests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args) + run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args) -def runtests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]): +def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]): BOLD = ("","") if os.name == 'posix': # primitive formatting on supported diff --git a/qa/pull-tester/tests_config.ini.in b/qa/pull-tester/tests_config.ini.in index 8317caaeb..e3e457d0b 100644 --- a/qa/pull-tester/tests_config.ini.in +++ b/qa/pull-tester/tests_config.ini.in @@ -5,13 +5,6 @@ # These environment variables are set by the build process and read by # rpc-tests.py -[DEFAULT] -# Provides default values for whether different components are enabled -ENABLE_WALLET=False -ENABLE_UTILS=False -ENABLE_BITCOIND=False -ENABLE_ZMQ=False - [environment] SRCDIR=@abs_top_srcdir@ BUILDDIR=@abs_top_builddir@ @@ -19,7 +12,7 @@ EXEEXT=@EXEEXT@ [components] # Which components are enabled. These are commented out by `configure` if they were disabled when running config. -@ENABLE_WALLET_TRUE@ENABLE_WALLET=True -@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=True -@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=True -@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=True +@ENABLE_WALLET_TRUE@ENABLE_WALLET=true +@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=true +@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true +@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true From b18c437464cf9f122d3ef1c2ce8d9679178390a1 Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 20 Feb 2017 09:44:35 +0800 Subject: [PATCH 59/66] [trivial] Add tests_config.ini to .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b984933bb..e1ade7a55 100644 --- a/.gitignore +++ b/.gitignore @@ -105,8 +105,7 @@ linux-coverage-build linux-build win32-build qa/pull-tester/tests_config.py -qa/pull-tester/test.*/* -qa/tmp +qa/pull-tester/tests_config.ini qa/cache/* !src/leveldb*/Makefile From 32249fb5b24aac2ef432cb93ccd1a29a52a440a7 Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 20 Feb 2017 09:45:32 +0800 Subject: [PATCH 60/66] [qa][doc] Correct rpc test options in readme --- qa/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qa/README.md b/qa/README.md index f908b77b7..6cca07d2d 100644 --- a/qa/README.md +++ b/qa/README.md @@ -39,12 +39,12 @@ Run the regression test suite with Run all possible tests with - qa/pull-tester/rpc-tests.py -extended + qa/pull-tester/rpc-tests.py --extended By default, tests will be run in parallel. To specify how many jobs to run, -append `-parallel=n` (default n=4). +append `--jobs=n` (default n=4). -If you want to create a basic coverage report for the rpc test suite, append `--coverage`. +If you want to create a basic coverage report for the RPC test suite, append `--coverage`. Possible options, which apply to each individual test run: @@ -83,5 +83,5 @@ killall zcashd Writing tests ============= You are encouraged to write tests for new or existing features. -Further information about the test framework and individual rpc +Further information about the test framework and individual RPC tests is found in [qa/rpc-tests](/qa/rpc-tests). From bc644280bf6b5cd30ce719d79c0abf55ecd86160 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 15 Feb 2017 09:59:19 -0500 Subject: [PATCH 61/66] Add exclude option to rpc-tests.py --- qa/pull-tester/rpc-tests.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 67f553659..8e95642eb 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -159,6 +159,7 @@ def main(): Help text and arguments for individual test script:''', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') + parser.add_argument('--exclude', '-x', help='specify a comma-seperated-list of scripts to exclude. Do not include the .py extension in the name.') parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).') parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') @@ -205,13 +206,6 @@ def main(): # in the ALL_SCRIPTS list. Accept the name with or without .py extension. test_list = [t for t in ALL_SCRIPTS if (t in tests or re.sub(".py$", "", t) in tests)] - if not test_list: - print("No valid test scripts specified. Check that your test is in one " - "of the test lists in rpc-tests.py or run rpc-tests.py with no arguments to run all tests") - print("Scripts not found:") - print(tests) - sys.exit(0) - else: # No individual tests have been specified. Run base tests, and # optionally ZMQ tests and extended tests. @@ -224,6 +218,17 @@ def main(): # (for parallel running efficiency). This combined list will is no # longer sorted. + # Remove the test cases that the user has explicitly asked to exclude. + if args.exclude: + for exclude_test in args.exclude.split(','): + if exclude_test + ".py" in test_list: + test_list.remove(exclude_test + ".py") + + if not test_list: + print("No valid test scripts specified. Check that your test is in one " + "of the test lists in rpc-tests.py, or run rpc-tests.py with no arguments to run all tests") + sys.exit(0) + if args.help: # Print help for rpc-tests.py, then print help of the first script and exit. parser.print_help() From 01449d8a3dd0733c9ec826141b8051746500572b Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 22 Feb 2017 10:50:44 +0100 Subject: [PATCH 62/66] qa: Set correct path for binaries in rpc tests --- qa/pull-tester/rpc-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 8e95642eb..ece7d6896 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -250,7 +250,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal tests_dir = src_dir + '/qa/rpc-tests/' - flags = ["--srcdir=" + src_dir] + args + flags = ["--srcdir={}/src".format(build_dir)] + args flags.append("--cachedir=%s/qa/cache" % build_dir) if enable_coverage: From 891fbff5b9f445a86256ffca8dd977c4d7df35ff Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 21 Nov 2020 02:35:29 +0000 Subject: [PATCH 63/66] test: Adjust some Zcash RPC tests to work with parallel runner --- qa/rpc-tests/key_import_export.py | 3 +- qa/rpc-tests/reorg_limit.py | 45 +++++++++++++++++++++------- qa/rpc-tests/sapling_rewind_check.py | 39 +++++++----------------- qa/rpc-tests/zkey_import_export.py | 3 +- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/qa/rpc-tests/key_import_export.py b/qa/rpc-tests/key_import_export.py index 43f4c85de..186e72261 100755 --- a/qa/rpc-tests/key_import_export.py +++ b/qa/rpc-tests/key_import_export.py @@ -9,8 +9,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_greater_than, start_nodes, connect_nodes_bi import logging +import sys -logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout) class KeyImportExportTest (BitcoinTestFramework): diff --git a/qa/rpc-tests/reorg_limit.py b/qa/rpc-tests/reorg_limit.py index 8c5e28aec..47286db13 100755 --- a/qa/rpc-tests/reorg_limit.py +++ b/qa/rpc-tests/reorg_limit.py @@ -11,8 +11,11 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( check_node, connect_nodes_bi, + start_node, sync_blocks, ) + +import tempfile from time import sleep def check_stopped(i, timeout=10): @@ -28,6 +31,17 @@ def check_stopped(i, timeout=10): class ReorgLimitTest(BitcoinTestFramework): + def setup_nodes(self): + self.log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) + + nodes = [] + nodes.append(start_node(0, self.options.tmpdir, stderr=self.log_stderr)) + nodes.append(start_node(1, self.options.tmpdir)) + nodes.append(start_node(2, self.options.tmpdir)) + nodes.append(start_node(3, self.options.tmpdir)) + + return nodes + def run_test(self): assert(self.nodes[0].getblockcount() == 200) assert(self.nodes[2].getblockcount() == 200) @@ -66,19 +80,28 @@ class ReorgLimitTest(BitcoinTestFramework): assert(self.nodes[0].getblockcount() == 400) assert(self.nodes[2].getblockcount() == 401) - print("Sync nodes to force a reorg") - connect_nodes_bi(self.nodes, 0, 2) - self.is_network_split = False - # sync_blocks uses RPC calls to wait for nodes to be synced, so don't - # call it here, because it will have a non-specific connection error - # when Node 0 stops. Instead, we explicitly check for the process itself - # to stop. + try: + print("Sync nodes to force a reorg") + connect_nodes_bi(self.nodes, 0, 2) + self.is_network_split = False + # sync_blocks uses RPC calls to wait for nodes to be synced, so don't + # call it here, because it will have a non-specific connection error + # when Node 0 stops. Instead, we explicitly check for the process itself + # to stop. - print("Check Node 0 is no longer running") - assert(check_stopped(0)) + print("Check Node 0 is no longer running") + assert(check_stopped(0)) - # Dummy stop to enable the test to tear down - self.nodes[0].stop = lambda: True + # Check that node 0 stopped for the expected reason. + self.log_stderr.seek(0) + stderr = self.log_stderr.read().decode('utf-8') + expected_msg = "A block chain reorganization has been detected that would roll back 100 blocks!" + if expected_msg not in stderr: + raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr) + finally: + self.log_stderr.close() + # Dummy stop to enable the test to tear down + self.nodes[0].stop = lambda: True if __name__ == '__main__': ReorgLimitTest().main() diff --git a/qa/rpc-tests/sapling_rewind_check.py b/qa/rpc-tests/sapling_rewind_check.py index 03065aac0..aa156848e 100755 --- a/qa/rpc-tests/sapling_rewind_check.py +++ b/qa/rpc-tests/sapling_rewind_check.py @@ -28,12 +28,15 @@ length computation (40b5d5e3ea4b602c34c4efaba0b9f6171dddfef5) corrects the issue from test_framework.test_framework import BitcoinTestFramework from test_framework.util import (assert_equal, assert_true, + assert_start_raises_init_error, start_nodes, start_node, connect_nodes_bi, bitcoind_processes, nuparams, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID) -import re import logging +import sys + +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout) HAS_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 15)] NO_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 150)] @@ -92,35 +95,15 @@ class SaplingRewindTest(BitcoinTestFramework): # Restart the nodes, reconnect, and sync the network. This succeeds if "-reindex" is passed. logging.info("Reconnecting the network...") - try: - # expect an exception; the node will refuse to fully start because its last point of - # agreement with the rest of the network was prior to the network upgrade activation - self.nodes[2] = start_node(2, self.options.tmpdir, extra_args=HAS_SAPLING) # + ["-reindex"]) - except: - logpath = self.options.tmpdir + "/node2/regtest/debug.log" - found = False - with open(logpath, 'r', encoding='utf8') as f: - for line in f: - # Search for the rollback message in the debug log, and ensure that it has the - # correct expected rollback length. - m = re.search(r'roll back ([0-9]+)', line) - if m is None: - continue - elif m.group(1) == "120": - found = True - break - else: - raise AssertionError("Incorrect rollback length %s found, expected 120." %(m.group(1))) - if not found: - raise AssertionError("Expected rollback message not found in log file.") + # expect an exception; the node will refuse to fully start because its last point of + # agreement with the rest of the network was prior to the network upgrade activation + assert_start_raises_init_error(2, self.options.tmpdir, HAS_SAPLING, "roll back 120") - # restart the node with -reindex to allow the test to complete gracefully, - # otherwise the node shutdown call in test cleanup will throw an error since - # it can't connect - self.nodes[2] = start_node(2, self.options.tmpdir, extra_args=NO_SAPLING + ["-reindex"]) - else: - raise AssertionError("Expected node to halt due to excessive rewind length.") + # restart the node with -reindex to allow the test to complete gracefully, + # otherwise the node shutdown call in test cleanup will throw an error since + # it can't connect + self.nodes[2] = start_node(2, self.options.tmpdir, extra_args=NO_SAPLING + ["-reindex"]) if __name__ == '__main__': SaplingRewindTest().main() diff --git a/qa/rpc-tests/zkey_import_export.py b/qa/rpc-tests/zkey_import_export.py index f957ccd89..2fff76e1b 100755 --- a/qa/rpc-tests/zkey_import_export.py +++ b/qa/rpc-tests/zkey_import_export.py @@ -9,8 +9,9 @@ from test_framework.util import assert_equal, assert_greater_than, start_nodes,\ initialize_chain_clean, connect_nodes_bi, wait_and_assert_operationid_status from functools import reduce import logging +import sys -logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout) fee = Decimal('0.0001') # constant (but can be changed within reason) From 21a7ec8bfb5de9ef7854ee4c58c4fe6b266af892 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 21 Nov 2020 02:36:02 +0000 Subject: [PATCH 64/66] test: Run rpc-tests.py in full_test_suite.py --- qa/zcash/full_test_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/zcash/full_test_suite.py b/qa/zcash/full_test_suite.py index 461fec5cd..173e07f09 100755 --- a/qa/zcash/full_test_suite.py +++ b/qa/zcash/full_test_suite.py @@ -182,7 +182,7 @@ STAGE_COMMANDS = { 'util-test': util_test, 'secp256k1': ['make', '-C', repofile('src/secp256k1'), 'check'], 'univalue': ['make', '-C', repofile('src/univalue'), 'check'], - 'rpc': [repofile('qa/pull-tester/rpc-tests.sh')], + 'rpc': [repofile('qa/pull-tester/rpc-tests.py')], } From 3a91bcfac54ae6c66f25677284e690afaf5b95cc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 2 Dec 2020 01:01:50 +0000 Subject: [PATCH 65/66] test: Silence pyflakes unused import warning rpc-tests.py trial-imports zmq to determine whether it is available. --- qa/pull-tester/rpc-tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index ece7d6896..d26376862 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -195,6 +195,7 @@ def main(): if enable_zmq: try: import zmq + zmq # Silences pyflakes except ImportError: print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests." "To run zmq tests, see dependency info in /qa/README.md.") From a7370b112e4f0facc7d4915cee14cbb7556a5828 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 2 Dec 2020 13:52:08 +0000 Subject: [PATCH 66/66] test: Run shielding-heavy RPC tests in serial --- qa/pull-tester/rpc-tests.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index d26376862..b6b188722 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -25,11 +25,18 @@ import subprocess import tempfile import re +SERIAL_SCRIPTS = [ + # These tests involve enough shielded spends (consuming all CPU + # cores) that we can't run them in parallel. + 'mergetoaddress_sapling.py', + 'mergetoaddress_sprout.py', + 'wallet_shieldingcoinbase.py', +] + BASE_SCRIPTS= [ # Scripts that are run by the travis build process # Longest test should go first, to favor running tests in parallel # vv Tests less than 5m vv - 'mergetoaddress_sapling.py', 'wallet.py', 'wallet_shieldcoinbase_sprout.py', 'sprout_sapling_migration.py', @@ -50,8 +57,6 @@ BASE_SCRIPTS= [ 'wallet_overwintertx.py', 'wallet_persistence.py', 'wallet_listnotes.py', - # v-- This test is down here to spread the CPU load. - 'mergetoaddress_sprout.py', # vv Tests less than 60s vv 'fundrawtransaction.py', 'reorg_limit.py', @@ -67,8 +72,6 @@ BASE_SCRIPTS= [ 'wallet_nullifiers.py', 'wallet_sapling.py', 'wallet_sendmany_any_taddr.py', - # v-- This test is down here to spread the CPU load. - 'wallet_shieldingcoinbase.py', 'wallet_treestate.py', 'listtransactions.py', 'mempool_resurrect_test.py', @@ -148,7 +151,7 @@ EXTENDED_SCRIPTS = [ 'wallet_db_flush.py', ] -ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS +ALL_SCRIPTS = SERIAL_SCRIPTS + BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS def main(): # Parse arguments and pass through unrecognised args @@ -210,7 +213,7 @@ def main(): else: # No individual tests have been specified. Run base tests, and # optionally ZMQ tests and extended tests. - test_list = BASE_SCRIPTS + test_list = SERIAL_SCRIPTS + BASE_SCRIPTS if enable_zmq: test_list += ZMQ_SCRIPTS if args.extended: @@ -332,6 +335,10 @@ class RPCTestHandler: stderr=log_stderr), log_stdout, log_stderr)) + # Run serial scripts on their own. We always run these first, + # so we won't have added any other jobs yet. + if t in SERIAL_SCRIPTS: + break if not self.jobs: raise IndexError('pop from empty list') while True: