Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/mininode.py
Show First 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | def __init__(self): | ||||
# Should only call methods on this from the NetworkThread, c.f. | # Should only call methods on this from the NetworkThread, c.f. | ||||
# call_soon_threadsafe | # call_soon_threadsafe | ||||
self._transport = None | self._transport = None | ||||
@property | @property | ||||
def is_connected(self): | def is_connected(self): | ||||
return self._transport is not None | return self._transport is not None | ||||
def peer_connect(self, dstaddr, dstport, *, net): | def peer_connect(self, dstaddr, dstport, *, net, factor): | ||||
assert not self.is_connected | assert not self.is_connected | ||||
self.factor = factor | |||||
self.dstaddr = dstaddr | self.dstaddr = dstaddr | ||||
self.dstport = dstport | self.dstport = dstport | ||||
# The initial message to send after the connection was made: | # The initial message to send after the connection was made: | ||||
self.on_connection_send_msg = None | self.on_connection_send_msg = None | ||||
self.on_connection_send_msg_is_raw = False | self.on_connection_send_msg_is_raw = False | ||||
self.recvbuf = b"" | self.recvbuf = b"" | ||||
self.magic_bytes = MAGIC_BYTES[net] | self.magic_bytes = MAGIC_BYTES[net] | ||||
logger.debug('Connecting to Bitcoin Node: {}:{}'.format( | logger.debug('Connecting to Bitcoin Node: {}:{}'.format( | ||||
▲ Show 20 Lines • Show All 298 Lines • ▼ Show 20 Lines | class P2PInterface(P2PConnection): | ||||
def on_version(self, message): | def on_version(self, message): | ||||
assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format( | assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format( | ||||
message.nVersion, MIN_VERSION_SUPPORTED) | message.nVersion, MIN_VERSION_SUPPORTED) | ||||
self.send_message(msg_verack()) | self.send_message(msg_verack()) | ||||
self.nServices = message.nServices | self.nServices = message.nServices | ||||
# Connection helper methods | # Connection helper methods | ||||
def wait_until(self, test_function, timeout): | |||||
wait_until(test_function, timeout=timeout, | |||||
lock=mininode_lock, factor=self.factor) | |||||
def wait_for_disconnect(self, timeout=60): | def wait_for_disconnect(self, timeout=60): | ||||
def test_function(): return not self.is_connected | def test_function(): return not self.is_connected | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
# Message receiving helper methods | # Message receiving helper methods | ||||
def wait_for_tx(self, txid, timeout=60): | def wait_for_tx(self, txid, timeout=60): | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
if not self.last_message.get('tx'): | if not self.last_message.get('tx'): | ||||
return False | return False | ||||
return self.last_message['tx'].tx.rehash() == txid | return self.last_message['tx'].tx.rehash() == txid | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_block(self, blockhash, timeout=60): | def wait_for_block(self, blockhash, timeout=60): | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
return self.last_message.get( | return self.last_message.get( | ||||
"block") and self.last_message["block"].block.rehash() == blockhash | "block") and self.last_message["block"].block.rehash() == blockhash | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_header(self, blockhash, timeout=60): | def wait_for_header(self, blockhash, timeout=60): | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
last_headers = self.last_message.get('headers') | last_headers = self.last_message.get('headers') | ||||
if not last_headers: | if not last_headers: | ||||
return False | return False | ||||
return last_headers.headers[0].rehash() == int(blockhash, 16) | return last_headers.headers[0].rehash() == int(blockhash, 16) | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_merkleblock(self, blockhash, timeout=60): | def wait_for_merkleblock(self, blockhash, timeout=60): | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
last_filtered_block = self.last_message.get('merkleblock') | last_filtered_block = self.last_message.get('merkleblock') | ||||
if not last_filtered_block: | if not last_filtered_block: | ||||
return False | return False | ||||
return last_filtered_block.merkleblock.header.rehash() == int(blockhash, 16) | return last_filtered_block.merkleblock.header.rehash() == int(blockhash, 16) | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_getdata(self, hash_list, timeout=60): | def wait_for_getdata(self, hash_list, timeout=60): | ||||
"""Waits for a getdata message. | """Waits for a getdata message. | ||||
The object hashes in the inventory vector must match the provided hash_list.""" | The object hashes in the inventory vector must match the provided hash_list.""" | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
last_data = self.last_message.get("getdata") | last_data = self.last_message.get("getdata") | ||||
if not last_data: | if not last_data: | ||||
return False | return False | ||||
return [x.hash for x in last_data.inv] == hash_list | return [x.hash for x in last_data.inv] == hash_list | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_getheaders(self, timeout=60): | def wait_for_getheaders(self, timeout=60): | ||||
"""Waits for a getheaders message. | """Waits for a getheaders message. | ||||
Receiving any getheaders message will satisfy the predicate. the last_message["getheaders"] | Receiving any getheaders message will satisfy the predicate. the last_message["getheaders"] | ||||
value must be explicitly cleared before calling this method, or this will return | value must be explicitly cleared before calling this method, or this will return | ||||
immediately with success. TODO: change this method to take a hash value and only | immediately with success. TODO: change this method to take a hash value and only | ||||
return true if the correct block header has been requested.""" | return true if the correct block header has been requested.""" | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
return self.last_message.get("getheaders") | return self.last_message.get("getheaders") | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_inv(self, expected_inv, timeout=60): | def wait_for_inv(self, expected_inv, timeout=60): | ||||
"""Waits for an INV message and checks that the first inv object in the message was as expected.""" | """Waits for an INV message and checks that the first inv object in the message was as expected.""" | ||||
if len(expected_inv) > 1: | if len(expected_inv) > 1: | ||||
raise NotImplementedError( | raise NotImplementedError( | ||||
"wait_for_inv() will only verify the first inv object") | "wait_for_inv() will only verify the first inv object") | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
return self.last_message.get("inv") and \ | return self.last_message.get("inv") and \ | ||||
self.last_message["inv"].inv[0].type == expected_inv[0].type and \ | self.last_message["inv"].inv[0].type == expected_inv[0].type and \ | ||||
self.last_message["inv"].inv[0].hash == expected_inv[0].hash | self.last_message["inv"].inv[0].hash == expected_inv[0].hash | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
def wait_for_verack(self, timeout=60): | def wait_for_verack(self, timeout=60): | ||||
def test_function(): | def test_function(): | ||||
return self.message_count["verack"] | return self.message_count["verack"] | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
# Message sending helper functions | # Message sending helper functions | ||||
def send_and_ping(self, message, timeout=60): | def send_and_ping(self, message, timeout=60): | ||||
self.send_message(message) | self.send_message(message) | ||||
self.sync_with_ping(timeout=timeout) | self.sync_with_ping(timeout=timeout) | ||||
# Sync up with the node | # Sync up with the node | ||||
def sync_with_ping(self, timeout=60): | def sync_with_ping(self, timeout=60): | ||||
self.send_message(msg_ping(nonce=self.ping_counter)) | self.send_message(msg_ping(nonce=self.ping_counter)) | ||||
def test_function(): | def test_function(): | ||||
assert self.is_connected | assert self.is_connected | ||||
return self.last_message.get( | return self.last_message.get( | ||||
"pong") and self.last_message["pong"].nonce == self.ping_counter | "pong") and self.last_message["pong"].nonce == self.ping_counter | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | self.wait_until(test_function, timeout=timeout) | ||||
self.ping_counter += 1 | self.ping_counter += 1 | ||||
# One lock for synchronizing all data access between the networking thread (see | # One lock for synchronizing all data access between the networking thread (see | ||||
# NetworkThread below) and the thread running the test logic. For simplicity, | # NetworkThread below) and the thread running the test logic. For simplicity, | ||||
# P2PConnection acquires this lock whenever delivering a message to a P2PInterface. | # P2PConnection acquires this lock whenever delivering a message to a P2PInterface. | ||||
# This lock should be acquired in the thread running the test logic to synchronize | # This lock should be acquired in the thread running the test logic to synchronize | ||||
# access to any data shared with the P2PInterface or P2PConnection. | # access to any data shared with the P2PInterface or P2PConnection. | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | def send_blocks_and_test(self, blocks, node, *, success=True, force_send=False, | ||||
def test(): | def test(): | ||||
if force_send: | if force_send: | ||||
for b in blocks: | for b in blocks: | ||||
self.send_message(msg_block(block=b)) | self.send_message(msg_block(block=b)) | ||||
else: | else: | ||||
self.send_message( | self.send_message( | ||||
msg_headers([CBlockHeader(block) for block in blocks])) | msg_headers([CBlockHeader(block) for block in blocks])) | ||||
wait_until( | self.wait_until( | ||||
lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock) | lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout) | ||||
if expect_disconnect: | if expect_disconnect: | ||||
self.wait_for_disconnect(timeout=timeout) | self.wait_for_disconnect(timeout=timeout) | ||||
else: | else: | ||||
self.sync_with_ping(timeout=timeout) | self.sync_with_ping(timeout=timeout) | ||||
if success: | if success: | ||||
wait_until(lambda: node.getbestblockhash() == | self.wait_until(lambda: node.getbestblockhash() == | ||||
blocks[-1].hash, timeout=timeout) | blocks[-1].hash, timeout=timeout) | ||||
else: | else: | ||||
assert node.getbestblockhash() != blocks[-1].hash | assert node.getbestblockhash() != blocks[-1].hash | ||||
if reject_reason: | if reject_reason: | ||||
with node.assert_debug_log(expected_msgs=[reject_reason]): | with node.assert_debug_log(expected_msgs=[reject_reason]): | ||||
test() | test() | ||||
else: | else: | ||||
test() | test() | ||||
▲ Show 20 Lines • Show All 60 Lines • Show Last 20 Lines |