Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/comptool.py
Show All 35 Lines | def __init__(self, code, reason=b''): | ||||
self.reason = reason | self.reason = reason | ||||
def match(self, other): | def match(self, other): | ||||
if self.code != other.code: | if self.code != other.code: | ||||
return False | return False | ||||
return other.reason.startswith(self.reason) | return other.reason.startswith(self.reason) | ||||
def __repr__(self): | def __repr__(self): | ||||
return '%i:%s' % (self.code, self.reason or '*') | return '{}:{}'.format(self.code, self.reason or '*') | ||||
class TestNode(P2PInterface): | class TestNode(P2PInterface): | ||||
def __init__(self, block_store, tx_store): | def __init__(self, block_store, tx_store): | ||||
super().__init__() | super().__init__() | ||||
self.bestblockhash = None | self.bestblockhash = None | ||||
self.block_store = block_store | self.block_store = block_store | ||||
Show All 39 Lines | class TestNode(P2PInterface): | ||||
def on_inv(self, message): | def on_inv(self, message): | ||||
self.lastInv = [x.hash for x in message.inv] | self.lastInv = [x.hash for x in message.inv] | ||||
def on_pong(self, message): | def on_pong(self, message): | ||||
try: | try: | ||||
del self.pingMap[message.nonce] | del self.pingMap[message.nonce] | ||||
except KeyError: | except KeyError: | ||||
raise AssertionError( | raise AssertionError( | ||||
"Got pong for unknown ping [%s]" % repr(message)) | "Got pong for unknown ping [{}]".format(repr(message))) | ||||
def on_reject(self, message): | def on_reject(self, message): | ||||
if message.message == b'tx': | if message.message == b'tx': | ||||
self.tx_reject_map[message.data] = RejectResult( | self.tx_reject_map[message.data] = RejectResult( | ||||
message.code, message.reason) | message.code, message.reason) | ||||
if message.message == b'block': | if message.message == b'block': | ||||
self.block_reject_map[message.data] = RejectResult( | self.block_reject_map[message.data] = RejectResult( | ||||
message.code, message.reason) | message.code, message.reason) | ||||
▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | def check_results(self, blockhash, outcome): | ||||
if c.bestblockhash != self.p2p_connections[0].bestblockhash: | if c.bestblockhash != self.p2p_connections[0].bestblockhash: | ||||
return False | return False | ||||
# Check that block was rejected w/ code | # Check that block was rejected w/ code | ||||
elif isinstance(outcome, RejectResult): | elif isinstance(outcome, RejectResult): | ||||
if c.bestblockhash == blockhash: | if c.bestblockhash == blockhash: | ||||
return False | return False | ||||
if blockhash not in c.block_reject_map: | if blockhash not in c.block_reject_map: | ||||
logger.error( | logger.error( | ||||
'Block not in reject map: %064x' % (blockhash)) | 'Block not in reject map: {:064x}'.format(blockhash)) | ||||
return False | return False | ||||
if not outcome.match(c.block_reject_map[blockhash]): | if not outcome.match(c.block_reject_map[blockhash]): | ||||
logger.error('Block rejected with %s instead of expected %s: %064x' % ( | logger.error('Block rejected with {} instead of expected {}: {:064x}'.format( | ||||
c.block_reject_map[blockhash], outcome, blockhash)) | c.block_reject_map[blockhash], outcome, blockhash)) | ||||
return False | return False | ||||
elif ((c.bestblockhash == blockhash) != outcome): | elif ((c.bestblockhash == blockhash) != outcome): | ||||
return False | return False | ||||
return True | return True | ||||
# Either check that the mempools all agree with each other, or that | # Either check that the mempools all agree with each other, or that | ||||
# txhash's presence in the mempool matches the outcome specified. | # txhash's presence in the mempool matches the outcome specified. | ||||
# This is somewhat of a strange comparison, in that we're either comparing | # This is somewhat of a strange comparison, in that we're either comparing | ||||
# a particular tx to an outcome, or the entire mempools altogether; | # a particular tx to an outcome, or the entire mempools altogether; | ||||
# perhaps it would be useful to add the ability to check explicitly that | # perhaps it would be useful to add the ability to check explicitly that | ||||
# a particular tx's existence in the mempool is the same across all nodes. | # a particular tx's existence in the mempool is the same across all nodes. | ||||
def check_mempool(self, txhash, outcome): | def check_mempool(self, txhash, outcome): | ||||
with mininode_lock: | with mininode_lock: | ||||
for c in self.p2p_connections: | for c in self.p2p_connections: | ||||
if outcome is None: | if outcome is None: | ||||
# Make sure the mempools agree with each other | # Make sure the mempools agree with each other | ||||
if c.lastInv != self.p2p_connections[0].lastInv: | if c.lastInv != self.p2p_connections[0].lastInv: | ||||
return False | return False | ||||
# Check that tx was rejected w/ code | # Check that tx was rejected w/ code | ||||
elif isinstance(outcome, RejectResult): | elif isinstance(outcome, RejectResult): | ||||
if txhash in c.lastInv: | if txhash in c.lastInv: | ||||
return False | return False | ||||
if txhash not in c.tx_reject_map: | if txhash not in c.tx_reject_map: | ||||
logger.error('Tx not in reject map: %064x' % (txhash)) | logger.error( | ||||
'Tx not in reject map: {:064x}'.format(txhash)) | |||||
return False | return False | ||||
if not outcome.match(c.tx_reject_map[txhash]): | if not outcome.match(c.tx_reject_map[txhash]): | ||||
logger.error('Tx rejected with %s instead of expected %s: %064x' % ( | logger.error('Tx rejected with {} instead of expected {}: {:064x}'.format( | ||||
c.tx_reject_map[txhash], outcome, txhash)) | c.tx_reject_map[txhash], outcome, txhash)) | ||||
return False | return False | ||||
elif ((txhash in c.lastInv) != outcome): | elif ((txhash in c.lastInv) != outcome): | ||||
return False | return False | ||||
return True | return True | ||||
def run(self): | def run(self): | ||||
# Wait until verack is received | # Wait until verack is received | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | def run(self): | ||||
[c.send_message(msg_block(block)) | [c.send_message(msg_block(block)) | ||||
for c in self.p2p_connections] | for c in self.p2p_connections] | ||||
[c.send_ping(self.ping_counter) | [c.send_ping(self.ping_counter) | ||||
for c in self.p2p_connections] | for c in self.p2p_connections] | ||||
self.wait_for_pings(self.ping_counter) | self.wait_for_pings(self.ping_counter) | ||||
self.ping_counter += 1 | self.ping_counter += 1 | ||||
if (not self.check_results(tip, outcome)): | if (not self.check_results(tip, outcome)): | ||||
raise AssertionError( | raise AssertionError( | ||||
"Test failed at test %d" % test_number) | "Test failed at test {}".format(test_number)) | ||||
else: | else: | ||||
invqueue.append(CInv(2, block.sha256)) | invqueue.append(CInv(2, block.sha256)) | ||||
elif isinstance(b_or_t, CBlockHeader): | elif isinstance(b_or_t, CBlockHeader): | ||||
block_header = b_or_t | block_header = b_or_t | ||||
self.block_store.add_header(block_header) | self.block_store.add_header(block_header) | ||||
[c.send_header(block_header) for c in self.p2p_connections] | [c.send_header(block_header) for c in self.p2p_connections] | ||||
else: # Tx test runner | else: # Tx test runner | ||||
assert(isinstance(b_or_t, CTransaction)) | assert(isinstance(b_or_t, CTransaction)) | ||||
tx = b_or_t | tx = b_or_t | ||||
tx_outcome = outcome | tx_outcome = outcome | ||||
# Add to shared tx store and clear map entry | # Add to shared tx store and clear map entry | ||||
with mininode_lock: | with mininode_lock: | ||||
self.tx_store.add_transaction(tx) | self.tx_store.add_transaction(tx) | ||||
for c in self.p2p_connections: | for c in self.p2p_connections: | ||||
c.tx_request_map[tx.sha256] = False | c.tx_request_map[tx.sha256] = False | ||||
# Again, either inv to all nodes or save for later | # Again, either inv to all nodes or save for later | ||||
if (test_instance.sync_every_tx): | if (test_instance.sync_every_tx): | ||||
[c.send_inv(tx) for c in self.p2p_connections] | [c.send_inv(tx) for c in self.p2p_connections] | ||||
self.sync_transaction(tx.sha256, 1) | self.sync_transaction(tx.sha256, 1) | ||||
if (not self.check_mempool(tx.sha256, outcome)): | if (not self.check_mempool(tx.sha256, outcome)): | ||||
raise AssertionError( | raise AssertionError( | ||||
"Test failed at test %d" % test_number) | "Test failed at test {}".format(test_number)) | ||||
else: | else: | ||||
invqueue.append(CInv(1, tx.sha256)) | invqueue.append(CInv(1, tx.sha256)) | ||||
# Ensure we're not overflowing the inv queue | # Ensure we're not overflowing the inv queue | ||||
if len(invqueue) == MAX_INV_SZ: | if len(invqueue) == MAX_INV_SZ: | ||||
[c.send_message(msg_inv(invqueue)) | [c.send_message(msg_inv(invqueue)) | ||||
for c in self.p2p_connections] | for c in self.p2p_connections] | ||||
invqueue = [] | invqueue = [] | ||||
# Do final sync if we weren't syncing on every block or every tx. | # Do final sync if we weren't syncing on every block or every tx. | ||||
if (not test_instance.sync_every_block and block is not None): | if (not test_instance.sync_every_block and block is not None): | ||||
if len(invqueue) > 0: | if len(invqueue) > 0: | ||||
[c.send_message(msg_inv(invqueue)) | [c.send_message(msg_inv(invqueue)) | ||||
for c in self.p2p_connections] | for c in self.p2p_connections] | ||||
invqueue = [] | invqueue = [] | ||||
self.sync_blocks(block.sha256, len( | self.sync_blocks(block.sha256, len( | ||||
test_instance.blocks_and_transactions)) | test_instance.blocks_and_transactions)) | ||||
if (not self.check_results(tip, block_outcome)): | if (not self.check_results(tip, block_outcome)): | ||||
raise AssertionError( | raise AssertionError( | ||||
"Block test failed at test %d" % test_number) | "Block test failed at test {}".format(test_number)) | ||||
if (not test_instance.sync_every_tx and tx is not None): | if (not test_instance.sync_every_tx and tx is not None): | ||||
if len(invqueue) > 0: | if len(invqueue) > 0: | ||||
[c.send_message(msg_inv(invqueue)) | [c.send_message(msg_inv(invqueue)) | ||||
for c in self.p2p_connections] | for c in self.p2p_connections] | ||||
invqueue = [] | invqueue = [] | ||||
self.sync_transaction(tx.sha256, len( | self.sync_transaction(tx.sha256, len( | ||||
test_instance.blocks_and_transactions)) | test_instance.blocks_and_transactions)) | ||||
if (not self.check_mempool(tx.sha256, tx_outcome)): | if (not self.check_mempool(tx.sha256, tx_outcome)): | ||||
raise AssertionError( | raise AssertionError( | ||||
"Mempool test failed at test %d" % test_number) | "Mempool test failed at test {}".format(test_number)) | ||||
logger.info("Test %d: PASS" % test_number) | logger.info("Test {}: PASS".format(test_number)) | ||||
test_number += 1 | test_number += 1 | ||||
[c.disconnect_node() for c in self.p2p_connections] | [c.disconnect_node() for c in self.p2p_connections] | ||||
self.wait_for_disconnections() | self.wait_for_disconnections() | ||||
self.block_store.close() | self.block_store.close() | ||||
self.tx_store.close() | self.tx_store.close() |