Changeset View
Changeset View
Standalone View
Standalone View
test/functional/p2p_sendheaders.py
Show First 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | from test_framework.messages import ( | ||||
msg_getblocks, | msg_getblocks, | ||||
msg_getdata, | msg_getdata, | ||||
msg_getheaders, | msg_getheaders, | ||||
msg_headers, | msg_headers, | ||||
msg_inv, | msg_inv, | ||||
msg_sendheaders, | msg_sendheaders, | ||||
) | ) | ||||
from test_framework.mininode import ( | from test_framework.mininode import ( | ||||
mininode_lock, | p2p_lock, | ||||
P2PInterface, | P2PInterface, | ||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
wait_until, | wait_until, | ||||
) | ) | ||||
Show All 33 Lines | class BaseNode(P2PInterface): | ||||
def send_getblocks(self, locator): | def send_getblocks(self, locator): | ||||
getblocks_message = msg_getblocks() | getblocks_message = msg_getblocks() | ||||
getblocks_message.locator.vHave = locator | getblocks_message.locator.vHave = locator | ||||
self.send_message(getblocks_message) | self.send_message(getblocks_message) | ||||
def wait_for_block_announcement(self, block_hash, timeout=60): | def wait_for_block_announcement(self, block_hash, timeout=60): | ||||
def test_function(): return self.last_blockhash_announced == block_hash | def test_function(): return self.last_blockhash_announced == block_hash | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | wait_until(test_function, timeout=timeout, lock=p2p_lock) | ||||
def on_inv(self, message): | def on_inv(self, 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, message): | def on_headers(self, message): | ||||
if len(message.headers): | if len(message.headers): | ||||
self.block_announced = True | self.block_announced = True | ||||
for x in message.headers: | for x in message.headers: | ||||
x.calc_sha256() | x.calc_sha256() | ||||
# append because headers may be announced over multiple | # append because headers may be announced over multiple | ||||
# messages. | # messages. | ||||
self.recent_headers_announced.append(x.sha256) | self.recent_headers_announced.append(x.sha256) | ||||
self.last_blockhash_announced = message.headers[-1].sha256 | self.last_blockhash_announced = message.headers[-1].sha256 | ||||
def clear_block_announcements(self): | def clear_block_announcements(self): | ||||
with mininode_lock: | with p2p_lock: | ||||
self.block_announced = False | self.block_announced = False | ||||
self.last_message.pop("inv", None) | self.last_message.pop("inv", None) | ||||
self.last_message.pop("headers", None) | self.last_message.pop("headers", None) | ||||
self.recent_headers_announced = [] | self.recent_headers_announced = [] | ||||
def check_last_headers_announcement(self, headers): | def check_last_headers_announcement(self, headers): | ||||
"""Test whether the last headers announcements received are right. | """Test whether the last headers announcements received are right. | ||||
Headers may be announced across more than one message.""" | Headers may be announced across more than one message.""" | ||||
def test_function(): return (len(self.recent_headers_announced) >= len(headers)) | def test_function(): return (len(self.recent_headers_announced) >= len(headers)) | ||||
wait_until(test_function, timeout=60, lock=mininode_lock) | wait_until(test_function, timeout=60, lock=p2p_lock) | ||||
with mininode_lock: | with p2p_lock: | ||||
assert_equal(self.recent_headers_announced, headers) | assert_equal(self.recent_headers_announced, headers) | ||||
self.block_announced = False | self.block_announced = False | ||||
self.last_message.pop("headers", None) | self.last_message.pop("headers", None) | ||||
self.recent_headers_announced = [] | self.recent_headers_announced = [] | ||||
def check_last_inv_announcement(self, inv): | def check_last_inv_announcement(self, inv): | ||||
"""Test whether the last announcement received had the right inv. | """Test whether the last announcement received had the right inv. | ||||
inv should be a list of block hashes.""" | inv should be a list of block hashes.""" | ||||
def test_function(): return self.block_announced | def test_function(): return self.block_announced | ||||
wait_until(test_function, timeout=60, lock=mininode_lock) | wait_until(test_function, timeout=60, lock=p2p_lock) | ||||
with mininode_lock: | with p2p_lock: | ||||
compare_inv = [] | compare_inv = [] | ||||
if "inv" in self.last_message: | if "inv" in self.last_message: | ||||
compare_inv = [x.hash for x in self.last_message["inv"].inv] | compare_inv = [x.hash for x in self.last_message["inv"].inv] | ||||
assert_equal(compare_inv, inv) | assert_equal(compare_inv, inv) | ||||
self.block_announced = False | self.block_announced = False | ||||
self.last_message.pop("inv", None) | self.last_message.pop("inv", None) | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | def test_nonnull_locators(self, test_node, inv_node): | ||||
new_block = create_block( | new_block = create_block( | ||||
tip, create_coinbase(height + 1), block_time) | tip, create_coinbase(height + 1), block_time) | ||||
new_block.solve() | new_block.solve() | ||||
test_node.send_header_for_blocks([new_block]) | test_node.send_header_for_blocks([new_block]) | ||||
test_node.wait_for_getdata([new_block.sha256]) | test_node.wait_for_getdata([new_block.sha256]) | ||||
# make sure this block is processed | # make sure this block is processed | ||||
test_node.send_and_ping(msg_block(new_block)) | test_node.send_and_ping(msg_block(new_block)) | ||||
wait_until(lambda: inv_node.block_announced, | wait_until(lambda: inv_node.block_announced, | ||||
timeout=60, lock=mininode_lock) | timeout=60, lock=p2p_lock) | ||||
inv_node.clear_block_announcements() | inv_node.clear_block_announcements() | ||||
test_node.clear_block_announcements() | test_node.clear_block_announcements() | ||||
self.log.info("Part 1: success!") | self.log.info("Part 1: success!") | ||||
self.log.info( | self.log.info( | ||||
"Part 2: announce blocks with headers after sendheaders message...") | "Part 2: announce blocks with headers after sendheaders message...") | ||||
# PART 2 | # PART 2 | ||||
# 2. Send a sendheaders message and test that headers announcements | # 2. Send a sendheaders message and test that headers announcements | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | def test_nonnull_locators(self, test_node, inv_node): | ||||
height += 1 | height += 1 | ||||
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_message.pop("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 p2p_lock: | ||||
assert "getdata" not in test_node.last_message | assert "getdata" not in test_node.last_message | ||||
# This time, direct fetch should work | # This time, direct fetch should work | ||||
blocks = [] | blocks = [] | ||||
for b in range(3): | for b in range(3): | ||||
blocks.append(create_block( | blocks.append(create_block( | ||||
tip, create_coinbase(height), block_time)) | tip, create_coinbase(height), block_time)) | ||||
blocks[-1].solve() | blocks[-1].solve() | ||||
Show All 24 Lines | def test_nonnull_locators(self, test_node, inv_node): | ||||
block_time += 1 | block_time += 1 | ||||
height += 1 | height += 1 | ||||
# 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_message.pop("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 p2p_lock: | ||||
assert "getdata" not in test_node.last_message | 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) | ||||
test_node.send_header_for_blocks(blocks[1:2]) | test_node.send_header_for_blocks(blocks[1:2]) | ||||
test_node.sync_with_ping() | test_node.sync_with_ping() | ||||
test_node.wait_for_getdata( | test_node.wait_for_getdata( | ||||
[x.sha256 for x in blocks[0:2]], timeout=DIRECT_FETCH_RESPONSE_TIME) | [x.sha256 for x in blocks[0:2]], timeout=DIRECT_FETCH_RESPONSE_TIME) | ||||
# Announcing 16 more headers should trigger direct fetch for 14 more | # Announcing 16 more headers should trigger direct fetch for 14 more | ||||
# blocks | # blocks | ||||
test_node.send_header_for_blocks(blocks[2:18]) | test_node.send_header_for_blocks(blocks[2:18]) | ||||
test_node.sync_with_ping() | test_node.sync_with_ping() | ||||
test_node.wait_for_getdata( | test_node.wait_for_getdata( | ||||
[x.sha256 for x in blocks[2:16]], timeout=DIRECT_FETCH_RESPONSE_TIME) | [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_message.pop("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 p2p_lock: | ||||
assert "getdata" not in test_node.last_message | assert "getdata" not in test_node.last_message | ||||
self.log.info("Part 4: success!") | self.log.info("Part 4: success!") | ||||
# Now deliver all those blocks we announced. | # Now deliver all those blocks we announced. | ||||
[test_node.send_message(msg_block(x)) for x in blocks] | [test_node.send_message(msg_block(x)) for x in blocks] | ||||
self.log.info("Part 5: Testing handling of unconnecting headers") | self.log.info("Part 5: Testing handling of unconnecting headers") | ||||
# 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): | ||||
self.log.debug("Part 5.{}: starting...".format(i)) | self.log.debug("Part 5.{}: starting...".format(i)) | ||||
test_node.last_message.pop("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): | ||||
blocks.append(create_block( | blocks.append(create_block( | ||||
tip, create_coinbase(height), block_time)) | tip, create_coinbase(height), block_time)) | ||||
blocks[-1].solve() | blocks[-1].solve() | ||||
tip = blocks[-1].sha256 | tip = blocks[-1].sha256 | ||||
block_time += 1 | block_time += 1 | ||||
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 p2p_lock: | ||||
test_node.last_message.pop("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) | ||||
test_node.wait_for_getdata([x.sha256 for x in blocks]) | test_node.wait_for_getdata([x.sha256 for x in blocks]) | ||||
[test_node.send_message(msg_block(x)) for x in blocks] | [test_node.send_message(msg_block(x)) for x in blocks] | ||||
test_node.sync_with_ping() | test_node.sync_with_ping() | ||||
assert_equal( | assert_equal( | ||||
Show All 9 Lines | def test_nonnull_locators(self, test_node, inv_node): | ||||
blocks[-1].solve() | blocks[-1].solve() | ||||
tip = blocks[-1].sha256 | tip = blocks[-1].sha256 | ||||
block_time += 1 | block_time += 1 | ||||
height += 1 | height += 1 | ||||
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 | # Send a header that doesn't connect, check that we get a | ||||
# getheaders. | # getheaders. | ||||
with mininode_lock: | with p2p_lock: | ||||
test_node.last_message.pop("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() | ||||
# Next header will connect, should re-set our count: | # Next header will connect, should re-set our count: | ||||
test_node.send_header_for_blocks([blocks[0]]) | test_node.send_header_for_blocks([blocks[0]]) | ||||
# Remove the first two entries (blocks[1] would connect): | # Remove the first two entries (blocks[1] would connect): | ||||
blocks = blocks[2:] | blocks = blocks[2:] | ||||
# Now try to see how many unconnecting headers we can send | # Now try to see how many unconnecting headers we can send | ||||
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS | # before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS | ||||
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 | # Send a header that doesn't connect, check that we get a | ||||
# getheaders. | # getheaders. | ||||
with mininode_lock: | with p2p_lock: | ||||
test_node.last_message.pop("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. | ||||
test_node.send_header_for_blocks([blocks[-1]]) | test_node.send_header_for_blocks([blocks[-1]]) | ||||
# Should get disconnected | # Should get disconnected | ||||
Show All 11 Lines |