Changeset View
Changeset View
Standalone View
Standalone View
test/functional/p2p-compactblocks.py
Show First 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | class TestNode(NodeConnCB): | ||||
def send_header_for_blocks(self, new_blocks): | def send_header_for_blocks(self, new_blocks): | ||||
headers_message = msg_headers() | headers_message = msg_headers() | ||||
headers_message.headers = [CBlockHeader(b) for b in new_blocks] | headers_message.headers = [CBlockHeader(b) for b in new_blocks] | ||||
self.send_message(headers_message) | self.send_message(headers_message) | ||||
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) | wait_until(self.received_block_announcement, | ||||
timeout=30, lock=mininode_lock) | |||||
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.announced_blockhashes) | return (block_hash in self.announced_blockhashes) | ||||
return wait_until(received_hash, timeout=timeout) | wait_until(received_hash, timeout=timeout, lock=mininode_lock) | ||||
def send_await_disconnect(self, message, timeout=30): | def send_await_disconnect(self, message, timeout=30): | ||||
"""Sends a message to the node and wait for disconnect. | """Sends a message to the node and wait for disconnect. | ||||
This is used when we want to send a message into the node that we expect | This is used when we want to send a message into the node that we expect | ||||
will get us disconnected, eg an invalid block.""" | will get us disconnected, eg an invalid block.""" | ||||
self.send_message(message) | self.send_message(message) | ||||
success = wait_until(lambda: not self.connected, timeout=timeout) | wait_until(lambda: not self.connected, | ||||
if not success: | timeout=timeout, lock=mininode_lock) | ||||
logger.error("send_await_disconnect failed!") | |||||
raise AssertionError("send_await_disconnect failed!") | |||||
return success | |||||
class CompactBlocksTest(BitcoinTestFramework): | class CompactBlocksTest(BitcoinTestFramework): | ||||
def __init__(self): | def __init__(self): | ||||
super().__init__() | super().__init__() | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | class CompactBlocksTest(BitcoinTestFramework): | ||||
# - If sendcmpct is then sent with boolean 1, then new block announcements | # - If sendcmpct is then sent with boolean 1, then new block announcements | ||||
# are made with compact blocks. | # are made with compact blocks. | ||||
# If old_node is passed in, request compact blocks with version=preferred-1 | # If old_node is passed in, request compact blocks with version=preferred-1 | ||||
# and verify that it receives block announcements via compact block. | # and verify that it receives block announcements via compact block. | ||||
def test_sendcmpct(self, node, test_node, preferred_version, old_node=None): | def test_sendcmpct(self, node, test_node, preferred_version, old_node=None): | ||||
# Make sure we get a SENDCMPCT message from our peer | # Make sure we get a SENDCMPCT message from our peer | ||||
def received_sendcmpct(): | def received_sendcmpct(): | ||||
return (len(test_node.last_sendcmpct) > 0) | return (len(test_node.last_sendcmpct) > 0) | ||||
got_message = wait_until(received_sendcmpct, timeout=30) | wait_until(received_sendcmpct, timeout=30, lock=mininode_lock) | ||||
assert(received_sendcmpct()) | |||||
assert(got_message) | |||||
with mininode_lock: | with mininode_lock: | ||||
# Check that the first version received is the preferred one | # Check that the first version received is the preferred one | ||||
assert_equal( | assert_equal( | ||||
test_node.last_sendcmpct[0].version, preferred_version) | test_node.last_sendcmpct[0].version, preferred_version) | ||||
# And that we receive versions down to 1. | # And that we receive versions down to 1. | ||||
assert_equal(test_node.last_sendcmpct[-1].version, 1) | assert_equal(test_node.last_sendcmpct[-1].version, 1) | ||||
test_node.last_sendcmpct = [] | test_node.last_sendcmpct = [] | ||||
tip = int(node.getbestblockhash(), 16) | tip = int(node.getbestblockhash(), 16) | ||||
def check_announcement_of_new_block(node, peer, predicate): | def check_announcement_of_new_block(node, peer, predicate): | ||||
peer.clear_block_announcement() | peer.clear_block_announcement() | ||||
block_hash = int(node.generate(1)[0], 16) | block_hash = int(node.generate(1)[0], 16) | ||||
peer.wait_for_block_announcement(block_hash, timeout=30) | peer.wait_for_block_announcement(block_hash, timeout=30) | ||||
assert(peer.block_announced) | assert(peer.block_announced) | ||||
assert(got_message) | |||||
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_message.get("cmpctblock", None), peer.last_message.get("inv", None))) | 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( | check_announcement_of_new_block( | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | def test_compactblock_construction(self, node, test_node): | ||||
for i in range(num_transactions): | for i in range(num_transactions): | ||||
txid = node.sendtoaddress(address, 0.1) | txid = node.sendtoaddress(address, 0.1) | ||||
hex_tx = node.gettransaction(txid)["hex"] | hex_tx = node.gettransaction(txid)["hex"] | ||||
tx = FromHex(CTransaction(), hex_tx) | tx = FromHex(CTransaction(), hex_tx) | ||||
assert(tx.wit.is_null()) | assert(tx.wit.is_null()) | ||||
# Wait until we've seen the block announcement for the resulting tip | # Wait until we've seen the block announcement for the resulting tip | ||||
tip = int(node.getbestblockhash(), 16) | tip = int(node.getbestblockhash(), 16) | ||||
assert(test_node.wait_for_block_announcement(tip)) | test_node.wait_for_block_announcement(tip) | ||||
# Make sure we will receive a fast-announce compact block | # Make sure we will receive a fast-announce compact block | ||||
self.request_cb_announcements(test_node, node) | self.request_cb_announcements(test_node, node) | ||||
# Now mine a block, and look at the resulting compact block. | # Now mine a block, and look at the resulting compact block. | ||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
block_hash = int(node.generate(1)[0], 16) | block_hash = int(node.generate(1)[0], 16) | ||||
# Store the raw block in our internal format. | # Store the raw block in our internal format. | ||||
block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False)) | block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False)) | ||||
[tx.calc_sha256() for tx in block.vtx] | [tx.calc_sha256() for tx in block.vtx] | ||||
block.rehash() | block.rehash() | ||||
# Wait until the block was announced (via compact blocks) | # Wait until the block was announced (via compact blocks) | ||||
wait_until(test_node.received_block_announcement, timeout=30) | wait_until(test_node.received_block_announcement, | ||||
assert(test_node.received_block_announcement()) | timeout=30, lock=mininode_lock) | ||||
# 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("cmpctblock" in test_node.last_message) | 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( | header_and_shortids = HeaderAndShortIDs( | ||||
test_node.last_message["cmpctblock"].header_and_shortids) | test_node.last_message["cmpctblock"].header_and_shortids) | ||||
self.check_compactblock_construction_from_block( | self.check_compactblock_construction_from_block( | ||||
header_and_shortids, block_hash, block) | 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 | ||||
with mininode_lock: | with mininode_lock: | ||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
inv = CInv(4, block_hash) # 4 == "CompactBlock" | inv = CInv(4, block_hash) # 4 == "CompactBlock" | ||||
test_node.send_message(msg_getdata([inv])) | test_node.send_message(msg_getdata([inv])) | ||||
wait_until(test_node.received_block_announcement, timeout=30) | wait_until(test_node.received_block_announcement, | ||||
assert(test_node.received_block_announcement()) | timeout=30, lock=mininode_lock) | ||||
# 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("cmpctblock" in test_node.last_message) | 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( | header_and_shortids = HeaderAndShortIDs( | ||||
test_node.last_message["cmpctblock"].header_and_shortids) | test_node.last_message["cmpctblock"].header_and_shortids) | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | def test_compactblock_requests(self, node, test_node, version): | ||||
# request | # request | ||||
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_message.pop("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( | wait_until(lambda: "getheaders" in test_node.last_message, | ||||
lambda: "getheaders" in test_node.last_message, timeout=30) | timeout=30, lock=mininode_lock) | ||||
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( | wait_until(lambda: "getdata" in test_node.last_message, | ||||
lambda: "getdata" in test_node.last_message, timeout=30) | timeout=30, lock=mininode_lock) | ||||
assert(success) | |||||
assert_equal(len(test_node.last_message["getdata"].inv), 1) | assert_equal(len(test_node.last_message["getdata"].inv), 1) | ||||
assert_equal(test_node.last_message["getdata"].inv[0].type, 4) | assert_equal(test_node.last_message["getdata"].inv[0].type, 4) | ||||
assert_equal( | assert_equal( | ||||
test_node.last_message["getdata"].inv[0].hash, block.sha256) | 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() | ||||
comp_block.header = CBlockHeader(block) | comp_block.header = CBlockHeader(block) | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | def test_incorrect_blocktxn_response(self, node, test_node, version): | ||||
msg.block_transactions = BlockTransactions( | msg.block_transactions = BlockTransactions( | ||||
block.sha256, [block.vtx[5]] + block.vtx[7:]) | block.sha256, [block.vtx[5]] + block.vtx[7:]) | ||||
test_node.send_and_ping(msg) | test_node.send_and_ping(msg) | ||||
# Tip should not have updated | # Tip should not have updated | ||||
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( | wait_until(lambda: "getdata" in test_node.last_message, | ||||
lambda: "getdata" in test_node.last_message, timeout=10) | timeout=10, lock=mininode_lock) | ||||
assert(success) | |||||
assert_equal(len(test_node.last_message["getdata"].inv), 1) | assert_equal(len(test_node.last_message["getdata"].inv), 1) | ||||
assert(test_node.last_message["getdata"].inv[0].type == | assert(test_node.last_message["getdata"].inv[0].type == | ||||
2 or test_node.last_message["getdata"].inv[0].type == 2 | MSG_WITNESS_FLAG) | 2 or test_node.last_message["getdata"].inv[0].type == 2 | MSG_WITNESS_FLAG) | ||||
assert_equal( | assert_equal( | ||||
test_node.last_message["getdata"].inv[0].hash, block.sha256) | test_node.last_message["getdata"].inv[0].hash, block.sha256) | ||||
# Deliver the block | # Deliver the block | ||||
if version == 2: | if version == 2: | ||||
Show All 14 Lines | def test_getblocktxn_handler(self, node, test_node, version): | ||||
msg = msg_getblocktxn() | msg = msg_getblocktxn() | ||||
msg.block_txn_request = BlockTransactionsRequest( | msg.block_txn_request = BlockTransactionsRequest( | ||||
int(block_hash, 16), []) | int(block_hash, 16), []) | ||||
num_to_request = random.randint(1, len(block.vtx)) | num_to_request = random.randint(1, len(block.vtx)) | ||||
msg.block_txn_request.from_absolute( | msg.block_txn_request.from_absolute( | ||||
sorted(random.sample(range(len(block.vtx)), num_to_request))) | sorted(random.sample(range(len(block.vtx)), num_to_request))) | ||||
test_node.send_message(msg) | test_node.send_message(msg) | ||||
success = wait_until( | wait_until(lambda: "blocktxn" in test_node.last_message, | ||||
lambda: "blocktxn" in test_node.last_message, timeout=10) | timeout=10, lock=mininode_lock) | ||||
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_message["blocktxn"].block_transactions.blockhash, int( | assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int( | ||||
block_hash, 16)) | 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_message["blocktxn"].block_transactions.transactions.pop( | tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop( | ||||
Show All 27 Lines | class CompactBlocksTest(BitcoinTestFramework): | ||||
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. | ||||
MAX_CMPCTBLOCK_DEPTH = 5 | MAX_CMPCTBLOCK_DEPTH = 5 | ||||
new_blocks = [] | new_blocks = [] | ||||
for i in range(MAX_CMPCTBLOCK_DEPTH + 1): | for i in range(MAX_CMPCTBLOCK_DEPTH + 1): | ||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
new_blocks.append(node.generate(1)[0]) | new_blocks.append(node.generate(1)[0]) | ||||
wait_until(test_node.received_block_announcement, timeout=30) | wait_until(test_node.received_block_announcement, | ||||
timeout=30, lock=mininode_lock) | |||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) | test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) | ||||
success = wait_until( | wait_until(lambda: "cmpctblock" in test_node.last_message, | ||||
lambda: "cmpctblock" in test_node.last_message, timeout=30) | timeout=30, lock=mininode_lock) | ||||
assert(success) | |||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
node.generate(1) | node.generate(1) | ||||
wait_until(test_node.received_block_announcement, timeout=30) | wait_until(test_node.received_block_announcement, | ||||
timeout=30, lock=mininode_lock) | |||||
test_node.clear_block_announcement() | test_node.clear_block_announcement() | ||||
with mininode_lock: | with mininode_lock: | ||||
test_node.last_message.pop("block", None) | test_node.last_message.pop("block", None) | ||||
test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) | test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) | ||||
success = wait_until( | wait_until(lambda: "block" in test_node.last_message, | ||||
lambda: "block" in test_node.last_message, timeout=30) | timeout=30, lock=mininode_lock) | ||||
assert(success) | |||||
with mininode_lock: | with mininode_lock: | ||||
test_node.last_message["block"].block.calc_sha256() | test_node.last_message["block"].block.calc_sha256() | ||||
assert_equal( | assert_equal( | ||||
test_node.last_message["block"].block.sha256, int(new_blocks[0], 16)) | 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() | ||||
hashPrevBlock = int(node.getblockhash(cur_height - 5), 16) | hashPrevBlock = int(node.getblockhash(cur_height - 5), 16) | ||||
Show All 29 Lines | def test_end_to_end_block_relay(self, node, listeners): | ||||
block = self.build_block_with_transactions(node, utxo, 10) | block = self.build_block_with_transactions(node, utxo, 10) | ||||
[l.clear_block_announcement() for l in listeners] | [l.clear_block_announcement() for l in listeners] | ||||
node.submitblock(ToHex(block)) | node.submitblock(ToHex(block)) | ||||
for l in listeners: | for l in listeners: | ||||
wait_until(lambda: l.received_block_announcement(), timeout=30) | wait_until(lambda: l.received_block_announcement(), | ||||
timeout=30, lock=mininode_lock) | |||||
with mininode_lock: | with mininode_lock: | ||||
for l in listeners: | for l in listeners: | ||||
assert "cmpctblock" in l.last_message | assert "cmpctblock" in l.last_message | ||||
l.last_message["cmpctblock"].header_and_shortids.header.calc_sha256( | l.last_message["cmpctblock"].header_and_shortids.header.calc_sha256( | ||||
) | ) | ||||
assert_equal( | assert_equal( | ||||
l.last_message["cmpctblock"].header_and_shortids.header.sha256, block.sha256) | l.last_message["cmpctblock"].header_and_shortids.header.sha256, block.sha256) | ||||
▲ Show 20 Lines • Show All 191 Lines • Show Last 20 Lines |