Merge #10169: [tests] Remove func test code duplication

2a52ae6 Remove duplicate method definitions in NodeConnCB subclasses (John Newbery)
52e15aa Adds helper functions to NodeConnCB (John Newbery)

Tree-SHA512: 2d7909eb85b3bde0fc3ebf133798eca21e561f4b2a2880937750820a42856cfb61fc94e30591c14ac13218bcfae0ebe7c5e8662a7b10f5b02470325c44a86cf1

remove duplicate code 10169

Signed-off-by: Pasta <pasta@dashboost.org>
This commit is contained in:
Wladimir J. van der Laan 2017-05-02 19:10:23 +02:00 committed by Pasta
parent 146cae9569
commit 2d771c95a1
10 changed files with 213 additions and 426 deletions

View File

@ -10,63 +10,24 @@ if uploadtarget has been reached.
if uploadtarget has been reached. if uploadtarget has been reached.
* Verify that the upload counters are reset after 24 hours. * Verify that the upload counters are reset after 24 hours.
""" """
from collections import defaultdict
import time
from test_framework.mininode import * from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
import time
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB): class TestNode(NodeConnCB):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.connection = None self.block_receive_map = defaultdict(int)
self.ping_counter = 1
self.last_pong = msg_pong()
self.block_receive_map = {}
def add_connection(self, conn):
self.connection = conn
self.peer_disconnected = False
def on_inv(self, conn, message): def on_inv(self, conn, message):
pass pass
# Track the last getdata message we receive (used in the test)
def on_getdata(self, conn, message):
self.last_getdata = message
def on_block(self, conn, message): def on_block(self, conn, message):
message.block.calc_sha256() message.block.calc_sha256()
try: self.block_receive_map[message.block.sha256] += 1
self.block_receive_map[message.block.sha256] += 1
except KeyError as e:
self.block_receive_map[message.block.sha256] = 1
# Spin until verack message is received from the node.
# We use this to signal that our test can begin. This
# is called from the testing thread, so it needs to acquire
# the global lock.
def wait_for_verack(self):
def veracked():
return self.verack_received
return wait_until(veracked, timeout=10)
def wait_for_disconnect(self):
def disconnected():
return self.peer_disconnected
return wait_until(disconnected, timeout=10)
# Wrapper for the NodeConn's send_message function
def send_message(self, message):
self.connection.send_message(message)
def on_pong(self, conn, message):
self.last_pong = message
def on_close(self, conn):
self.peer_disconnected = True
class MaxUploadTest(BitcoinTestFramework): class MaxUploadTest(BitcoinTestFramework):
@ -197,33 +158,26 @@ class MaxUploadTest(BitcoinTestFramework):
stop_node(self.nodes[0], 0) stop_node(self.nodes[0], 0)
self.nodes[0] = start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-maxtipage="+str(2*60*60*24*7)]) self.nodes[0] = start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-maxtipage="+str(2*60*60*24*7)])
#recreate/reconnect 3 test nodes #recreate/reconnect a test node
test_nodes = [] test_nodes = [TestNode()]
connections = [] connections = [NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[0])]
test_nodes[0].add_connection(connections[0])
for i in range(3):
test_nodes.append(TestNode())
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
test_nodes[i].add_connection(connections[i])
NetworkThread().start() # Start up network handling in another thread NetworkThread().start() # Start up network handling in another thread
[x.wait_for_verack() for x in test_nodes] test_nodes[0].wait_for_verack()
#retrieve 20 blocks which should be enough to break the 1MB limit #retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(2, big_new_block)] getdata_request.inv = [CInv(2, big_new_block)]
for i in range(20): for i in range(20):
test_nodes[1].send_message(getdata_request) test_nodes[0].send_message(getdata_request)
test_nodes[1].sync_with_ping() test_nodes[0].sync_with_ping()
assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) assert_equal(test_nodes[0].block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(2, big_old_block)] getdata_request.inv = [CInv(2, big_old_block)]
test_nodes[1].send_message(getdata_request) test_nodes[0].send_and_ping(getdata_request)
test_nodes[1].wait_for_disconnect() assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist
assert_equal(len(self.nodes[0].getpeerinfo()), 3) #node is still connected because of the whitelist
self.log.info("Peer 1 still connected after trying to download old block (whitelisted)") self.log.info("Peer still connected after trying to download old block (whitelisted)")
[c.disconnect_node() for c in connections]
if __name__ == '__main__': if __name__ == '__main__':
MaxUploadTest().main() MaxUploadTest().main()

View File

@ -54,40 +54,6 @@ from test_framework.util import *
import time import time
from test_framework.blocktools import create_block, create_coinbase from test_framework.blocktools import create_block, create_coinbase
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
def add_connection(self, conn):
self.connection = conn
# Track the last getdata message we receive (used in the test)
def on_getdata(self, conn, message):
self.last_getdata = message
# Spin until verack message is received from the node.
# We use this to signal that our test can begin. This
# is called from the testing thread, so it needs to acquire
# the global lock.
def wait_for_verack(self):
while True:
with mininode_lock:
if self.verack_received:
return
time.sleep(0.05)
# Wrapper for the NodeConn's send_message function
def send_message(self, message):
self.connection.send_message(message)
def on_pong(self, conn, message):
self.last_pong = message
class AcceptBlockTest(BitcoinTestFramework): class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser): def add_options(self, parser):
parser.add_option("--testbinary", dest="testbinary", parser.add_option("--testbinary", dest="testbinary",
@ -112,8 +78,8 @@ class AcceptBlockTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
# Setup the p2p connections and start up the network thread. # Setup the p2p connections and start up the network thread.
test_node = TestNode() # connects to node0 (not whitelisted) test_node = NodeConnCB() # connects to node0 (not whitelisted)
white_node = TestNode() # connects to node1 (whitelisted) white_node = NodeConnCB() # connects to node1 (whitelisted)
connections = [] connections = []
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
@ -239,12 +205,12 @@ class AcceptBlockTest(BitcoinTestFramework):
# triggers a getdata on block 2 (it should if block 2 is missing). # triggers a getdata on block 2 (it should if block 2 is missing).
with mininode_lock: with mininode_lock:
# Clear state so we can check the getdata request # Clear state so we can check the getdata request
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
test_node.sync_with_ping() test_node.sync_with_ping()
with mininode_lock: with mininode_lock:
getdata = test_node.last_getdata getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block # Check that the getdata includes the right block
assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)

View File

@ -16,64 +16,31 @@ class TestNode(NodeConnCB):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.last_sendcmpct = [] self.last_sendcmpct = []
self.last_headers = None
self.last_inv = None
self.last_cmpctblock = None
self.block_announced = False self.block_announced = False
self.last_getdata = None
self.last_getheaders = None
self.last_getblocktxn = None
self.last_block = None
self.last_blocktxn = None
# Store the hashes of blocks we've seen announced. # Store the hashes of blocks we've seen announced.
# This is for synchronizing the p2p message traffic, # This is for synchronizing the p2p message traffic,
# so we can eg wait until a particular block is announced. # so we can eg wait until a particular block is announced.
self.set_announced_blockhashes = set() self.announced_blockhashes = set()
self.connected = False
def on_open(self, conn):
self.connected = True
def on_close(self, conn):
self.connected = False
def on_sendcmpct(self, conn, message): def on_sendcmpct(self, conn, message):
self.last_sendcmpct.append(message) self.last_sendcmpct.append(message)
def on_block(self, conn, message):
self.last_block = message
def on_cmpctblock(self, conn, message): def on_cmpctblock(self, conn, message):
self.last_cmpctblock = message
self.block_announced = True self.block_announced = True
self.last_cmpctblock.header_and_shortids.header.calc_sha256() self.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
self.set_announced_blockhashes.add(self.last_cmpctblock.header_and_shortids.header.sha256) self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.sha256)
def on_headers(self, conn, message): def on_headers(self, conn, message):
self.last_headers = message
self.block_announced = True self.block_announced = True
for x in self.last_headers.headers: for x in self.last_message["headers"].headers:
x.calc_sha256() x.calc_sha256()
self.set_announced_blockhashes.add(x.sha256) self.announced_blockhashes.add(x.sha256)
def on_inv(self, conn, message): def on_inv(self, conn, message):
self.last_inv = message for x in self.last_message["inv"].inv:
for x in self.last_inv.inv:
if x.type == 2: if x.type == 2:
self.block_announced = True self.block_announced = True
self.set_announced_blockhashes.add(x.hash) self.announced_blockhashes.add(x.hash)
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
def on_blocktxn(self, conn, message):
self.last_blocktxn = message
# Requires caller to hold mininode_lock # Requires caller to hold mininode_lock
def received_block_announcement(self): def received_block_announcement(self):
@ -82,9 +49,9 @@ class TestNode(NodeConnCB):
def clear_block_announcement(self): def clear_block_announcement(self):
with mininode_lock: with mininode_lock:
self.block_announced = False self.block_announced = False
self.last_inv = None self.last_message.pop("inv", None)
self.last_headers = None self.last_message.pop("headers", None)
self.last_cmpctblock = None self.last_message.pop("cmpctblock", None)
def get_headers(self, locator, hashstop): def get_headers(self, locator, hashstop):
msg = msg_getheaders() msg = msg_getheaders()
@ -100,15 +67,14 @@ class TestNode(NodeConnCB):
def request_headers_and_sync(self, locator, hashstop=0): def request_headers_and_sync(self, locator, hashstop=0):
self.clear_block_announcement() self.clear_block_announcement()
self.get_headers(locator, hashstop) self.get_headers(locator, hashstop)
assert(wait_until(self.received_block_announcement, timeout=30)) assert wait_until(self.received_block_announcement, timeout=30)
assert(self.received_block_announcement())
self.clear_block_announcement() self.clear_block_announcement()
# Block until a block announcement for a particular block hash is # Block until a block announcement for a particular block hash is
# received. # received.
def wait_for_block_announcement(self, block_hash, timeout=30): def wait_for_block_announcement(self, block_hash, timeout=30):
def received_hash(): def received_hash():
return (block_hash in self.set_announced_blockhashes) return (block_hash in self.announced_blockhashes)
return wait_until(received_hash, timeout=timeout) return wait_until(received_hash, timeout=timeout)
def send_await_disconnect(self, message, timeout=30): def send_await_disconnect(self, message, timeout=30):
@ -205,14 +171,14 @@ class CompactBlocksTest(BitcoinTestFramework):
with mininode_lock: with mininode_lock:
assert predicate(peer), ( assert predicate(peer), (
"block_hash={!r}, cmpctblock={!r}, inv={!r}".format( "block_hash={!r}, cmpctblock={!r}, inv={!r}".format(
block_hash, peer.last_cmpctblock, peer.last_inv)) block_hash, peer.last_message.get("cmpctblock", None), peer.last_message.get("inv", None)))
# We shouldn't get any block announcements via cmpctblock yet. # 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) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Try one more time, this time after requesting headers. # Try one more time, this time after requesting headers.
test_node.request_headers_and_sync(locator=[tip]) test_node.request_headers_and_sync(locator=[tip])
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_inv is not None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "inv" in p.last_message)
# Test a few ways of using sendcmpct that should NOT # Test a few ways of using sendcmpct that should NOT
# result in compact block announcements. # result in compact block announcements.
@ -224,7 +190,7 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version+1 sendcmpct.version = preferred_version+1
sendcmpct.announce = True sendcmpct.announce = True
test_node.send_and_ping(sendcmpct) test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test. # Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip]) test_node.request_headers_and_sync(locator=[tip])
@ -233,7 +199,7 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version sendcmpct.version = preferred_version
sendcmpct.announce = False sendcmpct.announce = False
test_node.send_and_ping(sendcmpct) test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test. # Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip]) test_node.request_headers_and_sync(locator=[tip])
@ -242,20 +208,20 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version sendcmpct.version = preferred_version
sendcmpct.announce = True sendcmpct.announce = True
test_node.send_and_ping(sendcmpct) test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time (no headers sync should be needed!) # Try one more time (no headers sync should be needed!)
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after turning on sendheaders # Try one more time, after turning on sendheaders
test_node.send_and_ping(msg_sendheaders()) test_node.send_and_ping(msg_sendheaders())
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Now turn off announcements # Now turn off announcements
sendcmpct.version = preferred_version sendcmpct.version = preferred_version
sendcmpct.announce = False sendcmpct.announce = False
test_node.send_and_ping(sendcmpct) test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None) check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
# This code should be enabled after increasing cmctblk version # This code should be enabled after increasing cmctblk version
#if old_node is not None: #if old_node is not None:
@ -266,7 +232,7 @@ class CompactBlocksTest(BitcoinTestFramework):
#old_node.send_and_ping(sendcmpct) #old_node.send_and_ping(sendcmpct)
# Header sync # Header sync
#old_node.request_headers_and_sync(locator=[tip]) #old_node.request_headers_and_sync(locator=[tip])
#check_announcement_of_new_block(node, old_node, lambda p: p.last_cmpctblock is not None) #check_announcement_of_new_block(node, old_node, lambda p: "cmpctblock" in p.last_message)
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last. # This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self): def test_invalid_cmpctblock_message(self):
@ -318,9 +284,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block # Now fetch and check the compact block
header_and_shortids = None header_and_shortids = None
with mininode_lock: with mininode_lock:
assert(test_node.last_cmpctblock is not None) assert("cmpctblock" in test_node.last_message)
# Convert the on-the-wire representation to absolute indexes # Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
# Now fetch the compact block using a normal non-announce getdata # Now fetch the compact block using a normal non-announce getdata
@ -335,9 +301,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block # Now fetch and check the compact block
header_and_shortids = None header_and_shortids = None
with mininode_lock: with mininode_lock:
assert(test_node.last_cmpctblock is not None) assert("cmpctblock" in test_node.last_message)
# Convert the on-the-wire representation to absolute indexes # Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block): def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block):
@ -384,20 +350,20 @@ class CompactBlocksTest(BitcoinTestFramework):
for announce in ["inv", "header"]: for announce in ["inv", "header"]:
block = self.build_block_on_tip(node) block = self.build_block_on_tip(node)
with mininode_lock: with mininode_lock:
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
if announce == "inv": if announce == "inv":
test_node.send_message(msg_inv([CInv(2, block.sha256)])) test_node.send_message(msg_inv([CInv(2, block.sha256)]))
success = wait_until(lambda: test_node.last_getheaders is not None, timeout=30) success = wait_until(lambda: "getheaders" in test_node.last_message, timeout=30)
assert(success) assert(success)
test_node.send_header_for_blocks([block]) test_node.send_header_for_blocks([block])
else: else:
test_node.send_header_for_blocks([block]) test_node.send_header_for_blocks([block])
success = wait_until(lambda: test_node.last_getdata is not None, timeout=30) success = wait_until(lambda: "getdata" in test_node.last_message, timeout=30)
assert(success) assert(success)
assert_equal(len(test_node.last_getdata.inv), 1) assert_equal(len(test_node.last_message["getdata"].inv), 1)
assert_equal(test_node.last_getdata.inv[0].type, 20) assert_equal(test_node.last_message["getdata"].inv[0].type, 20)
assert_equal(test_node.last_getdata.inv[0].hash, block.sha256) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Send back a compactblock message that omits the coinbase # Send back a compactblock message that omits the coinbase
comp_block = HeaderAndShortIDs() comp_block = HeaderAndShortIDs()
@ -410,8 +376,8 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock) assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message. # Expect a getblocktxn message.
with mininode_lock: with mininode_lock:
assert(test_node.last_getblocktxn is not None) assert("getblocktxn" in test_node.last_message)
absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute() absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [0]) # should be a coinbase request assert_equal(absolute_indexes, [0]) # should be a coinbase request
# Send the coinbase, and verify that the tip advances. # Send the coinbase, and verify that the tip advances.
@ -446,8 +412,8 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_cmpctblock(compact_block.to_p2p()) msg = msg_cmpctblock(compact_block.to_p2p())
peer.send_and_ping(msg) peer.send_and_ping(msg)
with mininode_lock: with mininode_lock:
assert(peer.last_getblocktxn is not None) assert("getblocktxn" in peer.last_message)
absolute_indexes = peer.last_getblocktxn.block_txn_request.to_absolute() absolute_indexes = peer.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, expected_result) assert_equal(absolute_indexes, expected_result)
def test_tip_after_message(node, peer, msg, tip): def test_tip_after_message(node, peer, msg, tip):
@ -509,14 +475,14 @@ class CompactBlocksTest(BitcoinTestFramework):
# Clear out last request. # Clear out last request.
with mininode_lock: with mininode_lock:
test_node.last_getblocktxn = None test_node.last_message.pop("getblocktxn", None)
# Send compact block # Send compact block
comp_block.initialize_from_block(block, prefill_list=[0]) comp_block.initialize_from_block(block, prefill_list=[0])
test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256) test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
with mininode_lock: with mininode_lock:
# Shouldn't have gotten a request for any transaction # Shouldn't have gotten a request for any transaction
assert(test_node.last_getblocktxn is None) assert("getblocktxn" not in test_node.last_message)
# Incorrectly responding to a getblocktxn shouldn't cause the block to be # Incorrectly responding to a getblocktxn shouldn't cause the block to be
# permanently failed. # permanently failed.
@ -542,8 +508,8 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = [] absolute_indexes = []
with mininode_lock: with mininode_lock:
assert(test_node.last_getblocktxn is not None) assert("getblocktxn" in test_node.last_message)
absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute() absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [6, 7, 8, 9, 10]) assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
# Now give an incorrect response. # Now give an incorrect response.
@ -562,11 +528,11 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock) assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# We should receive a getdata request # We should receive a getdata request
success = wait_until(lambda: test_node.last_getdata is not None, timeout=10) success = wait_until(lambda: "getdata" in test_node.last_message, timeout=10)
assert(success) assert(success)
assert_equal(len(test_node.last_getdata.inv), 1) assert_equal(len(test_node.last_message["getdata"].inv), 1)
assert(test_node.last_getdata.inv[0].type == 2) assert(test_node.last_message["getdata"].inv[0].type == 2)
assert_equal(test_node.last_getdata.inv[0].hash, block.sha256) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Deliver the block # Deliver the block
test_node.send_and_ping(msg_block(block)) test_node.send_and_ping(msg_block(block))
@ -587,18 +553,18 @@ class CompactBlocksTest(BitcoinTestFramework):
num_to_request = random.randint(1, len(block.vtx)) num_to_request = random.randint(1, len(block.vtx))
msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request))) msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
test_node.send_message(msg) test_node.send_message(msg)
success = wait_until(lambda: test_node.last_blocktxn is not None, timeout=10) success = wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10)
assert(success) assert(success)
[tx.calc_sha256() for tx in block.vtx] [tx.calc_sha256() for tx in block.vtx]
with mininode_lock: with mininode_lock:
assert_equal(test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16)) assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int(block_hash, 16))
all_indices = msg.block_txn_request.to_absolute() all_indices = msg.block_txn_request.to_absolute()
for index in all_indices: for index in all_indices:
tx = test_node.last_blocktxn.block_transactions.transactions.pop(0) tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop(0)
tx.calc_sha256() tx.calc_sha256()
assert_equal(tx.sha256, block.vtx[index].sha256) assert_equal(tx.sha256, block.vtx[index].sha256)
test_node.last_blocktxn = None test_node.last_message.pop("blocktxn", None)
current_height -= 1 current_height -= 1
# Next request should send a full block response, as we're past the # Next request should send a full block response, as we're past the
@ -606,13 +572,13 @@ class CompactBlocksTest(BitcoinTestFramework):
block_hash = node.getblockhash(current_height) block_hash = node.getblockhash(current_height)
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0]) msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
with mininode_lock: with mininode_lock:
test_node.last_block = None test_node.last_message.pop("block", None)
test_node.last_blocktxn = None test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg) test_node.send_and_ping(msg)
with mininode_lock: with mininode_lock:
test_node.last_block.block.calc_sha256() test_node.last_message["block"].block.calc_sha256()
assert_equal(test_node.last_block.block.sha256, int(block_hash, 16)) assert_equal(test_node.last_message["block"].block.sha256, int(block_hash, 16))
assert_equal(test_node.last_blocktxn, None) assert "blocktxn" not in test_node.last_message
def test_compactblocks_not_at_tip(self, node, test_node): def test_compactblocks_not_at_tip(self, node, test_node):
# Test that requesting old compactblocks doesn't work. # Test that requesting old compactblocks doesn't work.
@ -625,7 +591,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.clear_block_announcement() test_node.clear_block_announcement()
test_node.send_message(msg_getdata([CInv(20, int(new_blocks[0], 16))])) test_node.send_message(msg_getdata([CInv(20, int(new_blocks[0], 16))]))
success = wait_until(lambda: test_node.last_cmpctblock is not None, timeout=30) success = wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
assert(success) assert(success)
test_node.clear_block_announcement() test_node.clear_block_announcement()
@ -633,13 +599,13 @@ class CompactBlocksTest(BitcoinTestFramework):
wait_until(test_node.received_block_announcement, timeout=30) wait_until(test_node.received_block_announcement, timeout=30)
test_node.clear_block_announcement() test_node.clear_block_announcement()
with mininode_lock: with mininode_lock:
test_node.last_block = None test_node.last_message.pop("block", None)
test_node.send_message(msg_getdata([CInv(20, int(new_blocks[0], 16))])) test_node.send_message(msg_getdata([CInv(20, int(new_blocks[0], 16))]))
success = wait_until(lambda: test_node.last_block is not None, timeout=30) success = wait_until(lambda: "block" in test_node.last_message, timeout=30)
assert(success) assert(success)
with mininode_lock: with mininode_lock:
test_node.last_block.block.calc_sha256() test_node.last_message["block"].block.calc_sha256()
assert_equal(test_node.last_block.block.sha256, int(new_blocks[0], 16)) assert_equal(test_node.last_message["block"].block.sha256, int(new_blocks[0], 16))
# Generate an old compactblock, and verify that it's not accepted. # Generate an old compactblock, and verify that it's not accepted.
cur_height = node.getblockcount() cur_height = node.getblockcount()
@ -666,10 +632,10 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_getblocktxn() msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0]) msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
with mininode_lock: with mininode_lock:
test_node.last_blocktxn = None test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg) test_node.send_and_ping(msg)
with mininode_lock: with mininode_lock:
assert(test_node.last_blocktxn is None) assert "blocktxn" not in test_node.last_message
def test_end_to_end_block_relay(self, node, listeners): def test_end_to_end_block_relay(self, node, listeners):
utxo = self.utxos.pop(0) utxo = self.utxos.pop(0)
@ -684,9 +650,9 @@ class CompactBlocksTest(BitcoinTestFramework):
wait_until(lambda: l.received_block_announcement(), timeout=30) wait_until(lambda: l.received_block_announcement(), timeout=30)
with mininode_lock: with mininode_lock:
for l in listeners: for l in listeners:
assert(l.last_cmpctblock is not None) assert "cmpctblock" in l.last_message
l.last_cmpctblock.header_and_shortids.header.calc_sha256() l.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
assert_equal(l.last_cmpctblock.header_and_shortids.header.sha256, block.sha256) assert_equal(l.last_message["cmpctblock"].header_and_shortids.header.sha256, block.sha256)
# Test that we don't get disconnected if we relay a compact block with valid header, # Test that we don't get disconnected if we relay a compact block with valid header,
# but invalid transactions. # but invalid transactions.
@ -733,7 +699,7 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_cmpctblock(cmpct_block.to_p2p()) msg = msg_cmpctblock(cmpct_block.to_p2p())
peer.send_and_ping(msg) peer.send_and_ping(msg)
with mininode_lock: with mininode_lock:
assert(peer.last_getblocktxn is not None) assert "getblocktxn" in peer.last_message
return block, cmpct_block return block, cmpct_block
block, cmpct_block = announce_cmpct_block(node, stalling_peer) block, cmpct_block = announce_cmpct_block(node, stalling_peer)

View File

@ -20,15 +20,8 @@ banscore = 10
class CLazyNode(NodeConnCB): class CLazyNode(NodeConnCB):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.connection = None
self.unexpected_msg = False self.unexpected_msg = False
self.connected = False self.ever_connected = False
def add_connection(self, conn):
self.connection = conn
def send_message(self, message):
self.connection.send_message(message)
def bad_message(self, message): def bad_message(self, message):
self.unexpected_msg = True self.unexpected_msg = True
@ -36,6 +29,7 @@ class CLazyNode(NodeConnCB):
def on_open(self, conn): def on_open(self, conn):
self.connected = True self.connected = True
self.ever_connected = True
def on_version(self, conn, message): self.bad_message(message) def on_version(self, conn, message): self.bad_message(message)
def on_verack(self, conn, message): self.bad_message(message) def on_verack(self, conn, message): self.bad_message(message)
@ -62,9 +56,6 @@ class CLazyNode(NodeConnCB):
# Node that never sends a version. We'll use this to send a bunch of messages # Node that never sends a version. We'll use this to send a bunch of messages
# anyway, and eventually get disconnected. # anyway, and eventually get disconnected.
class CNodeNoVersionBan(CLazyNode): class CNodeNoVersionBan(CLazyNode):
def __init__(self):
super().__init__()
# send a bunch of veracks without sending a message. This should get us disconnected. # send a bunch of veracks without sending a message. This should get us disconnected.
# NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes # NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
def on_open(self, conn): def on_open(self, conn):
@ -120,7 +111,9 @@ class P2PLeakTest(BitcoinTestFramework):
NetworkThread().start() # Start up network handling in another thread NetworkThread().start() # Start up network handling in another thread
assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10)) assert wait_until(lambda: no_version_bannode.ever_connected, timeout=10)
assert wait_until(lambda: no_version_idlenode.ever_connected, timeout=10)
assert wait_until(lambda: no_verack_idlenode.version_received, timeout=10)
# Mine a block and make sure that it's not sent to the connected nodes # Mine a block and make sure that it's not sent to the connected nodes
self.nodes[0].generate(1) self.nodes[0].generate(1)
@ -129,7 +122,7 @@ class P2PLeakTest(BitcoinTestFramework):
time.sleep(5) time.sleep(5)
#This node should have been banned #This node should have been banned
assert(no_version_bannode.connection.state == "closed") assert not no_version_bannode.connected
[conn.disconnect_node() for conn in connections] [conn.disconnect_node() for conn in connections]

View File

@ -12,60 +12,6 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
self.block_receive_map = {}
def add_connection(self, conn):
self.connection = conn
self.peer_disconnected = False
def on_inv(self, conn, message):
pass
# Track the last getdata message we receive (used in the test)
def on_getdata(self, conn, message):
self.last_getdata = message
def on_block(self, conn, message):
message.block.calc_sha256()
try:
self.block_receive_map[message.block.sha256] += 1
except KeyError as e:
self.block_receive_map[message.block.sha256] = 1
# Spin until verack message is received from the node.
# We use this to signal that our test can begin. This
# is called from the testing thread, so it needs to acquire
# the global lock.
def wait_for_verack(self):
def veracked():
return self.verack_received
return wait_until(veracked, timeout=10)
def wait_for_disconnect(self):
def disconnected():
return self.peer_disconnected
return wait_until(disconnected, timeout=10)
# Wrapper for the NodeConn's send_message function
def send_message(self, message):
self.connection.send_message(message)
def on_pong(self, conn, message):
self.last_pong = message
def on_close(self, conn):
self.peer_disconnected = True
def send_mempool(self):
self.lastInv = []
self.send_message(msg_mempool())
class P2PMempoolTests(BitcoinTestFramework): class P2PMempoolTests(BitcoinTestFramework):
def __init__(self): def __init__(self):
@ -74,20 +20,18 @@ class P2PMempoolTests(BitcoinTestFramework):
self.num_nodes = 2 self.num_nodes = 2
def setup_network(self): def setup_network(self):
# Start a node with maxuploadtarget of 200 MB (/24h) self.nodes = [start_node(0, self.options.tmpdir, ["-peerbloomfilters=0"])]
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-peerbloomfilters=0"]))
def run_test(self): def run_test(self):
#connect a mininode #connect a mininode
aTestNode = TestNode() aTestNode = NodeConnCB()
node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode) node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
aTestNode.add_connection(node) aTestNode.add_connection(node)
NetworkThread().start() NetworkThread().start()
aTestNode.wait_for_verack() aTestNode.wait_for_verack()
#request mempool #request mempool
aTestNode.send_mempool() aTestNode.send_message(msg_mempool())
aTestNode.wait_for_disconnect() aTestNode.wait_for_disconnect()
#mininode must be disconnected at this point #mininode must be disconnected at this point

View File

@ -28,20 +28,9 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
class TestNode(NodeConnCB): class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
self.connected = False
self.received_version = False
def on_open(self, conn):
self.connected = True
def on_close(self, conn):
self.connected = False
def on_version(self, conn, message): def on_version(self, conn, message):
# Don't send a verack in response # Don't send a verack in response
self.received_version = True pass
class TimeoutsTest(BitcoinTestFramework): class TimeoutsTest(BitcoinTestFramework):
def __init__(self): def __init__(self):
@ -83,7 +72,7 @@ class TimeoutsTest(BitcoinTestFramework):
sleep(30) sleep(30)
assert(self.no_verack_node.received_version) assert "version" in self.no_verack_node.last_message
assert(self.no_verack_node.connected) assert(self.no_verack_node.connected)
assert(self.no_version_node.connected) assert(self.no_version_node.connected)

View File

@ -24,28 +24,10 @@ WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible un
WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT) WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
VB_PATTERN = re.compile("^Warning.*versionbit") VB_PATTERN = re.compile("^Warning.*versionbit")
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB): class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
def add_connection(self, conn):
self.connection = conn
def on_inv(self, conn, message): def on_inv(self, conn, message):
pass pass
# Wrapper for the NodeConn's send_message function
def send_message(self, message):
self.connection.send_message(message)
def on_pong(self, conn, message):
self.last_pong = message
class VersionBitsWarningTest(BitcoinTestFramework): class VersionBitsWarningTest(BitcoinTestFramework):
def __init__(self): def __init__(self):
super().__init__() super().__init__()

View File

@ -92,23 +92,17 @@ from test_framework.blocktools import create_block, create_coinbase
direct_fetch_response_time = 0.05 direct_fetch_response_time = 0.05
class BaseNode(NodeConnCB): class TestNode(NodeConnCB):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.last_inv = None
self.last_headers = None
self.last_block = None
self.last_getdata = None
self.block_announced = False self.block_announced = False
self.last_getheaders = None
self.disconnected = False
self.last_blockhash_announced = None self.last_blockhash_announced = None
def clear_last_announcement(self): def clear_last_announcement(self):
with mininode_lock: with mininode_lock:
self.block_announced = False self.block_announced = False
self.last_inv = None self.last_message.pop("inv", None)
self.last_headers = None self.last_message.pop("headers", None)
# Request data for a list of block hashes # Request data for a list of block hashes
def get_data(self, block_hashes): def get_data(self, block_hashes):
@ -129,29 +123,17 @@ class BaseNode(NodeConnCB):
self.connection.send_message(msg) self.connection.send_message(msg)
def on_inv(self, conn, message): def on_inv(self, conn, message):
self.last_inv = message
self.block_announced = True self.block_announced = True
self.last_blockhash_announced = message.inv[-1].hash self.last_blockhash_announced = message.inv[-1].hash
def on_headers(self, conn, message): def on_headers(self, conn, message):
self.last_headers = message
if len(message.headers): if len(message.headers):
self.block_announced = True self.block_announced = True
message.headers[-1].calc_sha256() message.headers[-1].calc_sha256()
self.last_blockhash_announced = message.headers[-1].sha256 self.last_blockhash_announced = message.headers[-1].sha256
def on_block(self, conn, message): def on_block(self, conn, message):
self.last_block = message.block self.last_message["block"].calc_sha256()
self.last_block.calc_sha256()
def on_getdata(self, conn, message):
self.last_getdata = message
def on_getheaders(self, conn, message):
self.last_getheaders = message
def on_close(self, conn):
self.disconnected = True
# Test whether the last announcement we received had the # Test whether the last announcement we received had the
# right header or the right inv # right header or the right inv
@ -166,43 +148,27 @@ class BaseNode(NodeConnCB):
success = True success = True
compare_inv = [] compare_inv = []
if self.last_inv != None: if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_inv.inv] compare_inv = [x.hash for x in self.last_message["inv"].inv]
if compare_inv != expect_inv: if compare_inv != expect_inv:
success = False success = False
hash_headers = [] hash_headers = []
if self.last_headers != None: if "headers" in self.last_message:
# treat headers as a list of block hashes # treat headers as a list of block hashes
hash_headers = [ x.sha256 for x in self.last_headers.headers ] hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ]
if hash_headers != expect_headers: if hash_headers != expect_headers:
success = False success = False
self.last_inv = None self.last_message.pop("inv", None)
self.last_headers = None self.last_message.pop("headers", None)
return success return success
# Syncing helpers
def wait_for_block(self, blockhash, timeout=60):
test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getheaders(self, timeout=60):
test_function = lambda: self.last_getheaders != None
assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getdata(self, hash_list, timeout=60): def wait_for_getdata(self, hash_list, timeout=60):
if hash_list == []: if hash_list == []:
return return
test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
assert(wait_until(test_function, timeout=timeout))
return
def wait_for_disconnect(self, timeout=60):
test_function = lambda: self.disconnected
assert(wait_until(test_function, timeout=timeout)) assert(wait_until(test_function, timeout=timeout))
return return
@ -221,17 +187,6 @@ class BaseNode(NodeConnCB):
getblocks_message.locator.vHave = locator getblocks_message.locator.vHave = locator
self.send_message(getblocks_message) self.send_message(getblocks_message)
# InvNode: This peer should only ever receive inv's, because it doesn't ever send a
# "sendheaders" message.
class InvNode(BaseNode):
def __init__(self):
BaseNode.__init__(self)
# TestNode: This peer is the one we use for most of the testing.
class TestNode(BaseNode):
def __init__(self):
BaseNode.__init__(self)
class SendHeadersTest(BitcoinTestFramework): class SendHeadersTest(BitcoinTestFramework):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -271,7 +226,7 @@ class SendHeadersTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
# Setup the p2p connections and start up the network thread. # Setup the p2p connections and start up the network thread.
inv_node = InvNode() inv_node = TestNode()
test_node = TestNode() test_node = TestNode()
self.p2p_connections = [inv_node, test_node] self.p2p_connections = [inv_node, test_node]
@ -407,8 +362,8 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node.sync_with_ping() inv_node.sync_with_ping()
# This block should not be announced to the inv node (since it also # This block should not be announced to the inv node (since it also
# broadcast it) # broadcast it)
assert_equal(inv_node.last_inv, None) assert "inv" not in inv_node.last_message
assert_equal(inv_node.last_headers, None) assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
assert_equal(test_node.check_last_announcement(headers=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True)
@ -498,12 +453,12 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node.send_message(msg_block(blocks[-1])) inv_node.send_message(msg_block(blocks[-1]))
inv_node.sync_with_ping() # Make sure blocks are processed inv_node.sync_with_ping() # Make sure blocks are processed
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks) test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping() test_node.sync_with_ping()
# should not have received any getdata messages # should not have received any getdata messages
with mininode_lock: with mininode_lock:
assert_equal(test_node.last_getdata, None) assert "getdata" not in test_node.last_message
# This time, direct fetch should work # This time, direct fetch should work
blocks = [] blocks = []
@ -537,11 +492,11 @@ class SendHeadersTest(BitcoinTestFramework):
# Announcing one block on fork should not trigger direct fetch # Announcing one block on fork should not trigger direct fetch
# (less work than tip) # (less work than tip)
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[0:1]) test_node.send_header_for_blocks(blocks[0:1])
test_node.sync_with_ping() test_node.sync_with_ping()
with mininode_lock: with mininode_lock:
assert_equal(test_node.last_getdata, None) assert "getdata" not in test_node.last_message
# Announcing one more block on fork should trigger direct fetch for # Announcing one more block on fork should trigger direct fetch for
# both blocks (same work as tip) # both blocks (same work as tip)
@ -556,11 +511,11 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time) test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
# Announcing 1 more header should not trigger any response # Announcing 1 more header should not trigger any response
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[18:19]) test_node.send_header_for_blocks(blocks[18:19])
test_node.sync_with_ping() test_node.sync_with_ping()
with mininode_lock: with mininode_lock:
assert_equal(test_node.last_getdata, None) assert "getdata" not in test_node.last_message
self.log.info("Part 4: success!") self.log.info("Part 4: success!")
@ -571,7 +526,7 @@ class SendHeadersTest(BitcoinTestFramework):
# First we test that receipt of an unconnecting header doesn't prevent # First we test that receipt of an unconnecting header doesn't prevent
# chain sync. # chain sync.
for i in range(10): for i in range(10):
test_node.last_getdata = None test_node.last_message.pop("getdata", None)
blocks = [] blocks = []
# Create two more blocks. # Create two more blocks.
for j in range(2): for j in range(2):
@ -582,7 +537,7 @@ class SendHeadersTest(BitcoinTestFramework):
height += 1 height += 1
# Send the header of the second block -> this won't connect. # Send the header of the second block -> this won't connect.
with mininode_lock: with mininode_lock:
test_node.last_getheaders = None test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[1]]) test_node.send_header_for_blocks([blocks[1]])
test_node.wait_for_getheaders() test_node.wait_for_getheaders()
test_node.send_header_for_blocks(blocks) test_node.send_header_for_blocks(blocks)
@ -605,7 +560,7 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(1, MAX_UNCONNECTING_HEADERS): for i in range(1, MAX_UNCONNECTING_HEADERS):
# Send a header that doesn't connect, check that we get a getheaders. # Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock: with mininode_lock:
test_node.last_getheaders = None test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i]]) test_node.send_header_for_blocks([blocks[i]])
test_node.wait_for_getheaders() test_node.wait_for_getheaders()
@ -620,25 +575,21 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(5*MAX_UNCONNECTING_HEADERS - 1): for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
# Send a header that doesn't connect, check that we get a getheaders. # Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock: with mininode_lock:
test_node.last_getheaders = None test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i%len(blocks)]]) test_node.send_header_for_blocks([blocks[i%len(blocks)]])
test_node.wait_for_getheaders() test_node.wait_for_getheaders()
# Eventually this stops working. # Eventually this stops working.
with mininode_lock:
self.last_getheaders = None
test_node.send_header_for_blocks([blocks[-1]]) test_node.send_header_for_blocks([blocks[-1]])
# Should get disconnected # Should get disconnected
test_node.wait_for_disconnect() test_node.wait_for_disconnect()
with mininode_lock:
self.last_getheaders = True
self.log.info("Part 5: success!") self.log.info("Part 5: success!")
# Finally, check that the inv node never received a getdata request, # Finally, check that the inv node never received a getdata request,
# throughout the test # throughout the test
assert_equal(inv_node.last_getdata, None) assert "getdata" not in inv_node.last_message
if __name__ == '__main__': if __name__ == '__main__':
SendHeadersTest().main() SendHeadersTest().main()

View File

@ -192,9 +192,7 @@ class TestManager(object):
return wait_until(disconnected, timeout=10) return wait_until(disconnected, timeout=10)
def wait_for_verack(self): def wait_for_verack(self):
def veracked(): return all(node.wait_for_verack() for node in self.test_nodes)
return all(node.verack_received for node in self.test_nodes)
return wait_until(veracked, timeout=10)
def wait_for_pings(self, counter, timeout=float('inf')): def wait_for_pings(self, counter, timeout=float('inf')):
def received_pongs(): def received_pongs():

View File

@ -20,23 +20,24 @@ msg_block, msg_tx, msg_headers, etc.:
ser_*, deser_*: functions that handle serialization/deserialization ser_*, deser_*: functions that handle serialization/deserialization
""" """
import struct
import socket
import asyncore import asyncore
from collections import namedtuple from collections import namedtuple
import time
import sys
import random
from .util import hex_str_to_bytes, bytes_to_hex_str
from io import BytesIO
from codecs import encode from codecs import encode
import hashlib from collections import defaultdict
from threading import RLock
from threading import Thread
import logging
import copy import copy
import hashlib
from io import BytesIO
import logging
import random
import socket
import struct
import sys
import time
from threading import RLock, Thread
from test_framework.siphash import siphash256 from test_framework.siphash import siphash256
from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
import dash_hash import dash_hash
@ -1595,30 +1596,57 @@ class msg_islock(object):
return "msg_islock(inputs=%s, txid=%064x)" % (repr(self.inputs), self.txid) return "msg_islock(inputs=%s, txid=%064x)" % (repr(self.inputs), self.txid)
# This is what a callback should look like for NodeConn
# Reimplement the on_* functions to provide handling for events
class NodeConnCB(object): class NodeConnCB(object):
"""Callback and helper functions for P2P connection to a bitcoind node.
Individual testcases should subclass this and override the on_* methods
if they want to alter message handling behaviour.
"""
def __init__(self): def __init__(self):
self.verack_received = False # Track whether we have a P2P connection open to the node
self.connected = False
self.connection = None
# Track number of messages of each type received and the most recent
# message of each type
self.message_count = defaultdict(int)
self.last_message = {}
# A count of the number of ping messages we've sent to the node
self.ping_counter = 1
# deliver_sleep_time is helpful for debugging race conditions in p2p # deliver_sleep_time is helpful for debugging race conditions in p2p
# tests; it causes message delivery to sleep for the specified time # tests; it causes message delivery to sleep for the specified time
# before acquiring the global lock and delivering the next message. # before acquiring the global lock and delivering the next message.
self.deliver_sleep_time = None self.deliver_sleep_time = None
# Remember the services our peer has advertised # Remember the services our peer has advertised
self.peer_services = None self.peer_services = None
self.connection = None
self.ping_counter = 1 # Message receiving methods
self.last_pong = msg_pong()
def deliver(self, conn, message): def deliver(self, conn, message):
"""Receive message and dispatch message to appropriate callback.
We keep a count of how many of each message type has been received
and the most recent message of each type.
Optionally waits for deliver_sleep_time before dispatching message.
"""
deliver_sleep = self.get_deliver_sleep_time() deliver_sleep = self.get_deliver_sleep_time()
if deliver_sleep is not None: if deliver_sleep is not None:
time.sleep(deliver_sleep) time.sleep(deliver_sleep)
with mininode_lock: with mininode_lock:
try: try:
getattr(self, 'on_' + message.command.decode('ascii'))(conn, message) command = message.command.decode('ascii')
self.message_count[command] += 1
self.last_message[command] = message
getattr(self, 'on_' + command)(conn, message)
except: except:
logger.exception("ERROR delivering %s" % repr(message)) print("ERROR delivering %s (%s)" % (repr(message),
sys.exc_info()[0]))
def set_deliver_sleep_time(self, value): def set_deliver_sleep_time(self, value):
with mininode_lock: with mininode_lock:
@ -1628,14 +1656,20 @@ class NodeConnCB(object):
with mininode_lock: with mininode_lock:
return self.deliver_sleep_time return self.deliver_sleep_time
# Callbacks which can be overridden by subclasses # Callback methods. Can be overridden by subclasses in individual test
################################################# # cases to provide custom message handling behaviour.
def on_open(self, conn):
self.connected = True
def on_close(self, conn):
self.connected = False
self.connection = None
def on_addr(self, conn, message): pass def on_addr(self, conn, message): pass
def on_alert(self, conn, message): pass def on_alert(self, conn, message): pass
def on_block(self, conn, message): pass def on_block(self, conn, message): pass
def on_blocktxn(self, conn, message): pass def on_blocktxn(self, conn, message): pass
def on_close(self, conn): pass
def on_cmpctblock(self, conn, message): pass def on_cmpctblock(self, conn, message): pass
def on_feefilter(self, conn, message): pass def on_feefilter(self, conn, message): pass
def on_getaddr(self, conn, message): pass def on_getaddr(self, conn, message): pass
@ -1645,7 +1679,7 @@ class NodeConnCB(object):
def on_getheaders(self, conn, message): pass def on_getheaders(self, conn, message): pass
def on_headers(self, conn, message): pass def on_headers(self, conn, message): pass
def on_mempool(self, conn): pass def on_mempool(self, conn): pass
def on_open(self, conn): pass def on_pong(self, conn, message): pass
def on_reject(self, conn, message): pass def on_reject(self, conn, message): pass
def on_sendcmpct(self, conn, message): pass def on_sendcmpct(self, conn, message): pass
def on_sendheaders(self, conn, message): pass def on_sendheaders(self, conn, message): pass
@ -1663,9 +1697,6 @@ class NodeConnCB(object):
if conn.ver_send > BIP0031_VERSION: if conn.ver_send > BIP0031_VERSION:
conn.send_message(msg_pong(message.nonce)) conn.send_message(msg_pong(message.nonce))
def on_pong(self, conn, message):
self.last_pong = message
def on_mnlistdiff(self, conn, message): pass def on_mnlistdiff(self, conn, message): pass
def on_clsig(self, conn, message): pass def on_clsig(self, conn, message): pass
def on_islock(self, conn, message): pass def on_islock(self, conn, message): pass
@ -1682,15 +1713,44 @@ class NodeConnCB(object):
conn.ver_recv = conn.ver_send conn.ver_recv = conn.ver_send
conn.nServices = message.nServices conn.nServices = message.nServices
# Helper functions # Connection helper methods
##################
def add_connection(self, conn): def add_connection(self, conn):
self.connection = conn self.connection = conn
# Wrapper for the NodeConn's send_message function def wait_for_disconnect(self, timeout=60):
test_function = lambda: not self.connected
assert wait_until(test_function, timeout=timeout)
# Message receiving helper methods
def wait_for_block(self, blockhash, timeout=60):
test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
assert wait_until(test_function, timeout=timeout)
def wait_for_getdata(self, timeout=60):
test_function = lambda: self.last_message.get("getdata")
assert wait_until(test_function, timeout=timeout)
def wait_for_getheaders(self, timeout=60):
test_function = lambda: self.last_message.get("getheaders")
assert wait_until(test_function, timeout=timeout)
def wait_for_inv(self, expected_inv, timeout=60):
test_function = lambda: self.last_message.get("inv") and self.last_message["inv"] != expected_inv
assert wait_until(test_function, timeout=timeout)
def wait_for_verack(self, timeout=60):
test_function = lambda: self.message_count["verack"]
assert wait_until(test_function, timeout=timeout)
# Message sending helper functions
def send_message(self, message): def send_message(self, message):
self.connection.send_message(message) if self.connection:
self.connection.send_message(message)
else:
logger.error("Cannot send message. No connection to node!")
def send_and_ping(self, message): def send_and_ping(self, message):
self.send_message(message) self.send_message(message)
@ -1698,27 +1758,11 @@ class NodeConnCB(object):
# Sync up with the node # Sync up with the node
def sync_with_ping(self, timeout=60): def sync_with_ping(self, timeout=60):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.send_message(msg_ping(nonce=self.ping_counter)) self.send_message(msg_ping(nonce=self.ping_counter))
success = wait_until(received_pong, timeout=timeout) test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
if not success: assert wait_until(test_function, timeout=timeout)
logger.error("sync_with_ping failed!")
raise AssertionError("sync_with_ping failed!")
self.ping_counter += 1 self.ping_counter += 1
return True
return success
# Spin until verack message is received from the node.
# Tests may want to use this as a signal that the test can begin.
# This can be called from the testing thread, so it needs to acquire the
# global lock.
def wait_for_verack(self):
while True:
with mininode_lock:
if self.verack_received:
return
time.sleep(0.05)
# The actual NodeConn class # The actual NodeConn class
# This class provides an interface for a p2p connection to a specified node # This class provides an interface for a p2p connection to a specified node