diff --git a/test/functional/p2p_ibd_stalling.py b/test/functional/p2p_ibd_stalling.py index fe14d979e5..779e652b46 100755 --- a/test/functional/p2p_ibd_stalling.py +++ b/test/functional/p2p_ibd_stalling.py @@ -13,14 +13,26 @@ from test_framework.blocktools import ( create_coinbase ) from test_framework.messages import ( + COutPoint, + CTransaction, + CTxIn, + CTxOut, + HeaderAndShortIDs, MSG_BLOCK, MSG_TYPE_MASK, + msg_cmpctblock, + msg_sendcmpct, +) +from test_framework.script import ( + CScript, + OP_TRUE, ) from test_framework.p2p import ( CBlockHeader, msg_block, msg_headers, P2PDataStore, + p2p_lock, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -50,7 +62,7 @@ class P2PStaller(P2PDataStore): class P2PIBDStallingTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 2 + self.num_nodes = 3 def setup_network(self): self.setup_nodes() @@ -65,7 +77,7 @@ class P2PIBDStallingTest(BitcoinTestFramework): node = self.nodes[0] tip = int(node.getbestblockhash(), 16) height = 1 - block_time = node.getblock(node.getbestblockhash())['time'] + 1 + block_time = int(time.time()) for _ in range(self.NUM_BLOCKS): self.blocks.append(create_block(tip, create_coinbase(height), block_time)) self.blocks[-1].solve() @@ -212,6 +224,59 @@ class P2PIBDStallingTest(BitcoinTestFramework): self.wait_until(lambda: node.getblockcount() == self.NUM_BLOCKS - 1) node.disconnect_p2ps() + def at_tip_stalling(self): + self.log.info("Test stalling and interaction with compact blocks when at tip") + node = self.nodes[2] + peers = [] + # Create a block with a tx (would be invalid, but this doesn't matter since we will only ever send the header) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.blocks[1].vtx[0].sha256, 0), scriptSig=b"")) + tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE]))) + tx.calc_sha256() + block_time = self.blocks[1].nTime + 1 + block = create_block(self.blocks[1].sha256, create_coinbase(3), block_time, txlist=[tx]) + block.solve() + + for id in range(3): + peers.append(node.add_outbound_p2p_connection(P2PStaller(block.sha256), p2p_idx=id, connection_type="outbound-full-relay")) + + # First Peer is a high-bw compact block peer + peers[0].send_and_ping(msg_sendcmpct(announce=True, version=2)) + peers[0].block_store = self.block_dict + headers_message = msg_headers() + headers_message.headers = [CBlockHeader(b) for b in self.blocks[:2]] + peers[0].send_message(headers_message) + self.wait_until(lambda: node.getblockcount() == 2) + + self.log.info("First peer announces via cmpctblock") + cmpct_block = HeaderAndShortIDs() + cmpct_block.initialize_from_block(block) + peers[0].send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) + with p2p_lock: + assert "getblocktxn" in peers[0].last_message + + self.log.info("Also announce block from other peers by header") + headers_message = msg_headers() + headers_message.headers = [CBlockHeader(block)] + for peer in peers[1:4]: + peer.send_and_ping(headers_message) + + self.log.info("Check that block is requested from two more header-announcing peers") + self.wait_until(lambda: sum(peer.stall_block_requested for peer in peers) == 0) + + self.mocktime = int(time.time()) + 31 + node.setmocktime(self.mocktime) + self.wait_until(lambda: sum(peer.stall_block_requested for peer in peers) == 1) + + self.mocktime += 31 + node.setmocktime(self.mocktime) + self.wait_until(lambda: sum(peer.stall_block_requested for peer in peers) == 2) + + self.log.info("Check that block is not requested from a third header-announcing peer") + self.mocktime += 31 + node.setmocktime(self.mocktime) + self.wait_until(lambda: sum(peer.stall_block_requested for peer in peers) == 2) + def all_sync_send_with_ping(self, peers): for p in peers: if p.is_connected: @@ -227,6 +292,7 @@ class P2PIBDStallingTest(BitcoinTestFramework): self.prepare_blocks() self.ibd_stalling() self.near_tip_stalling() + self.at_tip_stalling() if __name__ == '__main__':