diff options
Diffstat (limited to 'qa/rpc-tests')
-rwxr-xr-x | qa/rpc-tests/p2p-compactblocks.py | 71 | ||||
-rwxr-xr-x | qa/rpc-tests/p2p-segwit.py | 14 | ||||
-rwxr-xr-x | qa/rpc-tests/preciousblock.py | 65 | ||||
-rwxr-xr-x | qa/rpc-tests/proxy_test.py | 16 | ||||
-rwxr-xr-x | qa/rpc-tests/sendheaders.py | 11 | ||||
-rwxr-xr-x | qa/rpc-tests/smartfees.py | 6 | ||||
-rw-r--r-- | qa/rpc-tests/test_framework/authproxy.py | 13 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/comptool.py | 19 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/mininode.py | 26 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/test_framework.py | 7 | ||||
-rw-r--r-- | qa/rpc-tests/test_framework/util.py | 57 | ||||
-rwxr-xr-x | qa/rpc-tests/wallet-dump.py | 6 | ||||
-rwxr-xr-x | qa/rpc-tests/wallet.py | 2 |
13 files changed, 219 insertions, 94 deletions
diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py index 6b5d477131..ab4b809ded 100755 --- a/qa/rpc-tests/p2p-compactblocks.py +++ b/qa/rpc-tests/p2p-compactblocks.py @@ -27,6 +27,7 @@ class TestNode(SingleNodeConnCB): self.last_cmpctblock = None self.block_announced = False self.last_getdata = None + self.last_getheaders = None self.last_getblocktxn = None self.last_block = None self.last_blocktxn = None @@ -64,6 +65,9 @@ class TestNode(SingleNodeConnCB): def on_getdata(self, conn, message): self.last_getdata = message + def on_getheaders(self, conn, message): + self.last_getheaders = message + def on_getblocktxn(self, conn, message): self.last_getblocktxn = message @@ -186,12 +190,15 @@ class CompactBlocksTest(BitcoinTestFramework): def check_announcement_of_new_block(node, peer, predicate): peer.clear_block_announcement() - node.generate(1) - got_message = wait_until(lambda: peer.block_announced, timeout=30) + block_hash = int(node.generate(1)[0], 16) + peer.wait_for_block_announcement(block_hash, timeout=30) assert(peer.block_announced) assert(got_message) + with mininode_lock: - assert(predicate(peer)) + assert predicate(peer), ( + "block_hash={!r}, cmpctblock={!r}, inv={!r}".format( + block_hash, peer.last_cmpctblock, peer.last_inv)) # We shouldn't get any block announcements via cmpctblock yet. check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None) @@ -300,8 +307,8 @@ class CompactBlocksTest(BitcoinTestFramework): assert(segwit_tx_generated) # check that our test is not broken # Wait until we've seen the block announcement for the resulting tip - tip = int(self.nodes[0].getbestblockhash(), 16) - assert(self.test_node.wait_for_block_announcement(tip)) + tip = int(node.getbestblockhash(), 16) + assert(test_node.wait_for_block_announcement(tip)) # Now mine a block, and look at the resulting compact block. test_node.clear_block_announcement() @@ -393,6 +400,9 @@ class CompactBlocksTest(BitcoinTestFramework): if announce == "inv": test_node.send_message(msg_inv([CInv(2, block.sha256)])) + success = wait_until(lambda: test_node.last_getheaders is not None, timeout=30) + assert(success) + test_node.send_header_for_blocks([block]) else: test_node.send_header_for_blocks([block]) success = wait_until(lambda: test_node.last_getdata is not None, timeout=30) @@ -589,8 +599,8 @@ class CompactBlocksTest(BitcoinTestFramework): assert_equal(int(node.getbestblockhash(), 16), block.sha256) def test_getblocktxn_handler(self, node, test_node, version): - # bitcoind won't respond for blocks whose height is more than 15 blocks - # deep. + # bitcoind will not send blocktxn responses for blocks whose height is + # more than 10 blocks deep. MAX_GETBLOCKTXN_DEPTH = 10 chain_height = node.getblockcount() current_height = chain_height @@ -623,11 +633,17 @@ class CompactBlocksTest(BitcoinTestFramework): test_node.last_blocktxn = None current_height -= 1 - # Next request should be ignored, as we're past the allowed depth. + # Next request should send a full block response, as we're past the + # allowed depth for a blocktxn response. block_hash = node.getblockhash(current_height) msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0]) + with mininode_lock: + test_node.last_block = None + test_node.last_blocktxn = None test_node.send_and_ping(msg) with mininode_lock: + test_node.last_block.block.calc_sha256() + assert_equal(test_node.last_block.block.sha256, int(block_hash, 16)) assert_equal(test_node.last_blocktxn, None) def test_compactblocks_not_at_tip(self, node, test_node): @@ -648,6 +664,8 @@ class CompactBlocksTest(BitcoinTestFramework): node.generate(1) wait_until(test_node.received_block_announcement, timeout=30) test_node.clear_block_announcement() + with mininode_lock: + test_node.last_block = None test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) success = wait_until(lambda: test_node.last_block is not None, timeout=30) assert(success) @@ -708,6 +726,33 @@ class CompactBlocksTest(BitcoinTestFramework): l.last_cmpctblock.header_and_shortids.header.calc_sha256() assert_equal(l.last_cmpctblock.header_and_shortids.header.sha256, block.sha256) + # Test that we don't get disconnected if we relay a compact block with valid header, + # but invalid transactions. + def test_invalid_tx_in_compactblock(self, node, test_node, use_segwit): + assert(len(self.utxos)) + utxo = self.utxos[0] + + block = self.build_block_with_transactions(node, utxo, 5) + del block.vtx[3] + block.hashMerkleRoot = block.calc_merkle_root() + if use_segwit: + # If we're testing with segwit, also drop the coinbase witness, + # but include the witness commitment. + add_witness_commitment(block) + block.vtx[0].wit.vtxinwit = [] + block.solve() + + # Now send the compact block with all transactions prefilled, and + # verify that we don't get disconnected. + comp_block = HeaderAndShortIDs() + comp_block.initialize_from_block(block, prefill_list=[0, 1, 2, 3, 4], use_witness=use_segwit) + msg = msg_cmpctblock(comp_block.to_p2p()) + test_node.send_and_ping(msg) + + # Check that the tip didn't advance + assert(int(node.getbestblockhash(), 16) is not block.sha256) + test_node.sync_with_ping() + # Helper for enabling cb announcements # Send the sendcmpct request and sync headers def request_cb_announcements(self, peer, node, version): @@ -798,6 +843,11 @@ class CompactBlocksTest(BitcoinTestFramework): self.test_end_to_end_block_relay(self.nodes[0], [self.segwit_node, self.test_node, self.old_node]) self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node]) + print("\tTesting handling of invalid compact blocks...") + self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False) + self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False) + self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False) + # Advance to segwit activation print ("\nAdvancing to segwit activation\n") self.activate_segwit(self.nodes[1]) @@ -844,6 +894,11 @@ class CompactBlocksTest(BitcoinTestFramework): self.request_cb_announcements(self.segwit_node, self.nodes[1], 2) self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node]) + print("\tTesting handling of invalid compact blocks...") + self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False) + self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, True) + self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, True) + print("\tTesting invalid index in cmpctblock message...") self.test_invalid_cmpctblock_message() diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py index 09ab1b80fc..6ecd84c3f0 100755 --- a/qa/rpc-tests/p2p-segwit.py +++ b/qa/rpc-tests/p2p-segwit.py @@ -64,6 +64,9 @@ class TestNode(NodeConnCB): self.getdataset.add(inv.hash) self.last_getdata = message + def on_getheaders(self, conn, message): + self.last_getheaders = message + def on_pong(self, conn, message): self.last_pong = message @@ -97,6 +100,10 @@ class TestNode(NodeConnCB): test_function = lambda: self.last_getdata != None self.sync(test_function, timeout) + def wait_for_getheaders(self, timeout=60): + test_function = lambda: self.last_getheaders != None + self.sync(test_function, timeout) + def wait_for_inv(self, expected_inv, timeout=60): test_function = lambda: self.last_inv != expected_inv self.sync(test_function, timeout) @@ -111,12 +118,15 @@ class TestNode(NodeConnCB): def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60): with mininode_lock: self.last_getdata = None + self.last_getheaders = None + msg = msg_headers() + msg.headers = [ CBlockHeader(block) ] if use_header: - msg = msg_headers() - msg.headers = [ CBlockHeader(block) ] self.send_message(msg) else: self.send_message(msg_inv(inv=[CInv(2, block.sha256)])) + self.wait_for_getheaders() + self.send_message(msg) self.wait_for_getdata() return diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py index 854dcc7251..3cefa51c0a 100755 --- a/qa/rpc-tests/preciousblock.py +++ b/qa/rpc-tests/preciousblock.py @@ -8,7 +8,12 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + connect_nodes_bi, + sync_chain, + sync_blocks, +) def unidirectional_node_sync_via_rpc(node_src, node_dest): blocks_to_copy = [] @@ -33,84 +38,82 @@ def node_sync_via_rpc(nodes): unidirectional_node_sync_via_rpc(node_src, node_dest) class PreciousTest(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 + self.extra_args = [["-debug"]] * self.num_nodes def setup_network(self): - self.nodes = [] - self.is_network_split = False - self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"])) - self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"])) + self.nodes = self.setup_nodes() def run_test(self): print("Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) - assert(self.nodes[0].getblockcount() == 1) + assert_equal(self.nodes[0].getblockcount(), 1) (hashY, hashZ) = self.nodes[1].generate(2) - assert(self.nodes[1].getblockcount() == 2) + assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) - assert(self.nodes[0].getbestblockhash() == hashZ) + assert_equal(self.nodes[0].getbestblockhash(), hashZ) print("Mine blocks A-B-C on Node 0") (hashA, hashB, hashC) = self.nodes[0].generate(3) - assert(self.nodes[0].getblockcount() == 5) + assert_equal(self.nodes[0].getblockcount(), 5) print("Mine competing blocks E-F-G on Node 1") (hashE, hashF, hashG) = self.nodes[1].generate(3) - assert(self.nodes[1].getblockcount() == 5) + assert_equal(self.nodes[1].getblockcount(), 5) assert(hashC != hashG) print("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) connect_nodes_bi(self.nodes,0,1) - assert(self.nodes[0].getbestblockhash() == hashC) - assert(self.nodes[1].getbestblockhash() == hashG) + assert_equal(self.nodes[0].getbestblockhash(), hashC) + assert_equal(self.nodes[1].getbestblockhash(), hashG) print("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) - assert(self.nodes[0].getbestblockhash() == hashG) + assert_equal(self.nodes[0].getbestblockhash(), hashG) print("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) - assert(self.nodes[0].getbestblockhash() == hashC) + assert_equal(self.nodes[0].getbestblockhash(), hashC) print("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC - assert(self.nodes[1].getbestblockhash() == hashC) + assert_equal(self.nodes[1].getbestblockhash(), hashC) print("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) - assert(self.nodes[1].getbestblockhash() == hashG) + assert_equal(self.nodes[1].getbestblockhash(), hashG) print("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) - assert(self.nodes[0].getbestblockhash() == hashG) + assert_equal(self.nodes[0].getbestblockhash(), hashG) print("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) - assert(self.nodes[1].getbestblockhash() == hashC) + assert_equal(self.nodes[1].getbestblockhash(), hashC) print("Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.nodes[0].generate(1) - assert(self.nodes[0].getblockcount() == 6) + assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() - assert(self.nodes[1].getbestblockhash() == hashH) + assert_equal(self.nodes[1].getbestblockhash(), hashH) print("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) - assert(self.nodes[1].getbestblockhash() == hashH) + assert_equal(self.nodes[1].getbestblockhash(), hashH) print("Mine competing blocks I-J-K-L on Node 2") self.nodes[2].generate(4) - assert(self.nodes[2].getblockcount() == 6) + assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() print("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[0:3]) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - assert(self.nodes[0].getbestblockhash() == hashH) - assert(self.nodes[1].getbestblockhash() == hashH) - assert(self.nodes[2].getbestblockhash() == hashL) + assert_equal(self.nodes[0].getbestblockhash(), hashH) + assert_equal(self.nodes[1].getbestblockhash(), hashH) + assert_equal(self.nodes[2].getbestblockhash(), hashL) print("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) - assert(self.nodes[1].getbestblockhash() == hashL) + assert_equal(self.nodes[1].getbestblockhash(), hashL) print("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) - assert(self.nodes[2].getbestblockhash() == hashH) + assert_equal(self.nodes[2].getbestblockhash(), hashH) if __name__ == '__main__': PreciousTest().main() diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index 27160cae07..9ccc0ffbb0 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -4,10 +4,16 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. import socket +import os from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + PORT_MIN, + PORT_RANGE, + start_nodes, + assert_equal, +) from test_framework.netutil import test_ipv6_local ''' Test plan: @@ -33,6 +39,8 @@ addnode connect to onion addnode connect to generic DNS name ''' +RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports + class ProxyTest(BitcoinTestFramework): def __init__(self): @@ -44,19 +52,19 @@ class ProxyTest(BitcoinTestFramework): # Create two proxies on different ports # ... one unauthenticated self.conf1 = Socks5Configuration() - self.conf1.addr = ('127.0.0.1', 13000 + (os.getpid() % 1000)) + self.conf1.addr = ('127.0.0.1', RANGE_BEGIN + (os.getpid() % 1000)) self.conf1.unauth = True self.conf1.auth = False # ... one supporting authenticated and unauthenticated (Tor) self.conf2 = Socks5Configuration() - self.conf2.addr = ('127.0.0.1', 14000 + (os.getpid() % 1000)) + self.conf2.addr = ('127.0.0.1', RANGE_BEGIN + 1000 + (os.getpid() % 1000)) self.conf2.unauth = True self.conf2.auth = True if self.have_ipv6: # ... one on IPv6 with similar configuration self.conf3 = Socks5Configuration() self.conf3.af = socket.AF_INET6 - self.conf3.addr = ('::1', 15000 + (os.getpid() % 1000)) + self.conf3.addr = ('::1', RANGE_BEGIN + 2000 + (os.getpid() % 1000)) self.conf3.unauth = True self.conf3.auth = True else: diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index 81b2442e6a..37b98c576e 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -348,14 +348,13 @@ class SendHeadersTest(BitcoinTestFramework): if j == 0: # Announce via inv test_node.send_block_inv(tip) - test_node.wait_for_getdata([tip], timeout=5) + test_node.wait_for_getheaders(timeout=5) + # Should have received a getheaders now + test_node.send_header_for_blocks(blocks) # Test that duplicate inv's won't result in duplicate # getdata requests, or duplicate headers announcements - inv_node.send_block_inv(tip) - # Should have received a getheaders as well! - test_node.send_header_for_blocks(blocks) - test_node.wait_for_getdata([x.sha256 for x in blocks[0:-1]], timeout=5) - [ inv_node.send_block_inv(x.sha256) for x in blocks[0:-1] ] + [ inv_node.send_block_inv(x.sha256) for x in blocks ] + test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) inv_node.sync_with_ping() else: # Announce via headers diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index d76fba4b07..74a74f679a 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -225,9 +225,9 @@ class EstimateFeeTest(BitcoinTestFramework): self.memutxo, Decimal("0.005"), min_fee, min_fee) tx_kbytes = (len(txhex) // 2) / 1000.0 self.fees_per_kb.append(float(fee)/tx_kbytes) - sync_mempools(self.nodes[0:3],.1) + sync_mempools(self.nodes[0:3], wait=.1) mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"] - sync_blocks(self.nodes[0:3],.1) + sync_blocks(self.nodes[0:3], wait=.1) # update which txouts are confirmed newmem = [] for utx in self.memutxo: @@ -259,7 +259,7 @@ class EstimateFeeTest(BitcoinTestFramework): while len(self.nodes[1].getrawmempool()) > 0: self.nodes[1].generate(1) - sync_blocks(self.nodes[0:3],.1) + sync_blocks(self.nodes[0:3], wait=.1) print("Final estimates after emptying mempools") check_estimates(self.nodes[1], self.fees_per_kb, 2) diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py index fd7f32b5c6..9bee1962e2 100644 --- a/qa/rpc-tests/test_framework/authproxy.py +++ b/qa/rpc-tests/test_framework/authproxy.py @@ -1,6 +1,6 @@ """ - Copyright 2011 Jeff Garzik + Copyright (c) 2011 Jeff Garzik AuthServiceProxy has the following improvements over python-jsonrpc's ServiceProxy class: @@ -42,6 +42,7 @@ import base64 import decimal import json import logging +import socket try: import urllib.parse as urlparse except ImportError: @@ -161,7 +162,15 @@ class AuthServiceProxy(object): return self._request('POST', self.__url.path, postdata.encode('utf-8')) def _get_response(self): - http_response = self.__conn.getresponse() + try: + http_response = self.__conn.getresponse() + except socket.timeout as e: + raise JSONRPCException({ + 'code': -344, + 'message': '%r RPC took longer than %f seconds. Consider ' + 'using larger timeout for calls that take ' + 'longer to return.' % (self._service_name, + self.__conn.timeout)}) if http_response is None: raise JSONRPCException({ 'code': -342, 'message': 'missing HTTP response from server'}) diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 7c92d3f828..17679fc7e1 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -111,6 +111,11 @@ class TestNode(NodeConnCB): m.locator = self.block_store.get_locator(self.bestblockhash) self.conn.send_message(m) + def send_header(self, header): + m = msg_headers() + m.headers.append(header) + self.conn.send_message(m) + # This assumes BIP31 def send_ping(self, nonce): self.pingMap[nonce] = True @@ -345,8 +350,16 @@ class TestManager(object): # 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 we expect success, send inv and sync every block + # if we expect failure, just push the block and see what happens. + if outcome == True: + [ c.cb.send_inv(block) for c in self.connections ] + self.sync_blocks(block.sha256, 1) + else: + [ c.send_message(msg_block(block)) for c in self.connections ] + [ c.cb.send_ping(self.ping_counter) for c in self.connections ] + self.wait_for_pings(self.ping_counter) + self.ping_counter += 1 if (not self.check_results(tip, outcome)): raise AssertionError("Test failed at test %d" % test_number) else: @@ -354,6 +367,8 @@ class TestManager(object): elif isinstance(b_or_t, CBlockHeader): block_header = b_or_t self.block_store.add_header(block_header) + [ c.cb.send_header(block_header) for c in self.connections ] + else: # Tx test runner assert(isinstance(b_or_t, CTransaction)) tx = b_or_t diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 4d238c08d9..495c6bdf35 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -41,6 +41,7 @@ from test_framework.siphash import siphash256 BIP0031_VERSION = 60000 MY_VERSION = 70014 # past bip-31 for ping/pong MY_SUBVERSION = b"/python-mininode-tester:0.0.3/" +MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37) MAX_INV_SZ = 50000 MAX_BLOCK_SIZE = 1000000 @@ -951,6 +952,7 @@ class msg_version(object): self.nNonce = random.getrandbits(64) self.strSubVer = MY_SUBVERSION self.nStartingHeight = -1 + self.nRelay = MY_RELAY def deserialize(self, f): self.nVersion = struct.unpack("<i", f.read(4))[0] @@ -960,21 +962,32 @@ class msg_version(object): self.nTime = struct.unpack("<q", f.read(8))[0] self.addrTo = CAddress() self.addrTo.deserialize(f) + if self.nVersion >= 106: self.addrFrom = CAddress() self.addrFrom.deserialize(f) self.nNonce = struct.unpack("<Q", f.read(8))[0] self.strSubVer = deser_string(f) - if self.nVersion >= 209: - self.nStartingHeight = struct.unpack("<i", f.read(4))[0] - else: - self.nStartingHeight = None else: self.addrFrom = None self.nNonce = None self.strSubVer = None self.nStartingHeight = None + if self.nVersion >= 209: + self.nStartingHeight = struct.unpack("<i", f.read(4))[0] + else: + self.nStartingHeight = None + + if self.nVersion >= 70001: + # Relay field is optional for version 70001 onwards + try: + self.nRelay = struct.unpack("<b", f.read(1))[0] + except: + self.nRelay = 0 + else: + self.nRelay = 0 + def serialize(self): r = b"" r += struct.pack("<i", self.nVersion) @@ -985,13 +998,14 @@ class msg_version(object): r += struct.pack("<Q", self.nNonce) r += ser_string(self.strSubVer) r += struct.pack("<i", self.nStartingHeight) + r += struct.pack("<b", self.nRelay) return r def __repr__(self): - return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i)' \ + return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \ % (self.nVersion, self.nServices, time.ctime(self.nTime), repr(self.addrTo), repr(self.addrFrom), self.nNonce, - self.strSubVer, self.nStartingHeight) + self.strSubVer, self.nStartingHeight, self.nRelay) class msg_verack(object): diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index e6fc5fd8a2..e6d3e9ab9a 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -139,16 +139,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]) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c818af4bd7..0b3585c6b2 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -121,33 +121,42 @@ 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): +def sync_blocks(rpc_connections, *, wait=1, timeout=60): """ - Wait until everybody has the same tip + Wait until everybody has the same tip. + + sync_blocks needs to be called with an rpc_connections set that has least + one node already synced to the latest, stable tip, otherwise there's a + chance it might return before all nodes are stably synced. """ maxheight = 0 - while timeout > 0: - tips = [ x.waitforblockheight(maxheight, int(wait * 1000)) for x in rpc_connections ] - heights = [ x["height"] for x in tips ] - if tips == [ tips[0] ]*len(tips): - return True - if heights == [ heights[0] ]*len(heights): #heights are the same but hashes are not - raise AssertionError("Block sync failed") - timeout -= wait + start_time = cur_time = time.time() + while cur_time <= start_time + timeout: + tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections] + heights = [t["height"] for t in tips] + if all(t == tips[0] for t in tips): + return + if all(h == heights[0] for h in heights): + raise AssertionError("Block sync failed, mismatched block hashes:{}".format( + "".join("\n {!r}".format(tip) for tip in tips))) maxheight = max(heights) - raise AssertionError("Block sync failed") + cur_time = time.time() + raise AssertionError("Block sync to height {} timed out:{}".format( + maxheight, "".join("\n {!r}".format(tip) for tip in tips))) -def sync_chain(rpc_connections, wait=1): +def sync_chain(rpc_connections, *, wait=1, timeout=60): """ Wait until everybody has the same best block """ - while True: - counts = [ x.getbestblockhash() for x in rpc_connections ] - if counts == [ counts[0] ]*len(counts): - break + while timeout > 0: + best_hash = [x.getbestblockhash() for x in rpc_connections] + if best_hash == [best_hash[0]]*len(best_hash): + return time.sleep(wait) + timeout -= wait + raise AssertionError("Chain sync failed: Best block hashes don't match") -def sync_mempools(rpc_connections, wait=1, timeout=60): +def sync_mempools(rpc_connections, *, wait=1, timeout=60): """ Wait until everybody has the same transactions in their memory pools @@ -159,7 +168,7 @@ def sync_mempools(rpc_connections, wait=1, timeout=60): if set(rpc_connections[i].getrawmempool()) == pool: num_match = num_match+1 if num_match == len(rpc_connections): - return True + return time.sleep(wait) timeout -= wait raise AssertionError("Mempool sync failed") @@ -341,7 +350,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary= return proxy -def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None): +def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """ Start multiple bitcoinds, return RPC connections to them """ @@ -350,7 +359,7 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None): rpcs = [] try: for i in range(num_nodes): - rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i])) + rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i])) except: # If one node failed to start, stop the others stop_nodes(rpcs) raise @@ -522,10 +531,14 @@ def assert_greater_than(thing1, thing2): raise AssertionError("%s <= %s"%(str(thing1),str(thing2))) def assert_raises(exc, fun, *args, **kwds): + assert_raises_message(exc, None, fun, *args, **kwds) + +def assert_raises_message(exc, message, fun, *args, **kwds): try: fun(*args, **kwds) - except exc: - pass + except exc as e: + if message is not None and message not in e.error['message']: + raise AssertionError("Expected substring not found:"+e.error['message']) except Exception as e: raise AssertionError("Unexpected exception raised: "+type(e).__name__) else: diff --git a/qa/rpc-tests/wallet-dump.py b/qa/rpc-tests/wallet-dump.py index a37096a40c..c6dc2e3d10 100755 --- a/qa/rpc-tests/wallet-dump.py +++ b/qa/rpc-tests/wallet-dump.py @@ -61,7 +61,11 @@ class WalletDumpTest(BitcoinTestFramework): self.extra_args = [["-keypool=90"]] def setup_network(self, split=False): - self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + # Use 1 minute timeout because the initial getnewaddress RPC can take + # longer than the default 30 seconds due to an expensive + # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in + # the test often takes even longer. + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) def run_test (self): tmpdir = self.options.tmpdir diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index e43f6ea5d2..3c0dc0f4ea 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -71,7 +71,7 @@ class WalletTest (BitcoinTestFramework): unspent_0 = self.nodes[2].listunspent()[0] unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} self.nodes[2].lockunspent(False, [unspent_0]) - assert_raises(JSONRPCException, self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) + assert_raises_message(JSONRPCException, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) |