@@ -109,6 +109,10 @@ def clear_block_announcement(self):
109109 self .last_message .pop ("headers" , None )
110110 self .last_message .pop ("cmpctblock" , None )
111111
112+ def clear_getblocktxn (self ):
113+ with p2p_lock :
114+ self .last_message .pop ("getblocktxn" , None )
115+
112116 def get_headers (self , locator , hashstop ):
113117 msg = msg_getheaders ()
114118 msg .locator .vHave = locator
@@ -764,7 +768,7 @@ def request_cb_announcements(self, peer):
764768 peer .get_headers (locator = [int (tip , 16 )], hashstop = 0 )
765769 peer .send_and_ping (msg_sendcmpct (announce = True , version = peer .cmpct_version ))
766770
767- def test_compactblock_reconstruction_multiple_peers (self , stalling_peer , delivery_peer ):
771+ def test_compactblock_reconstruction_stalling_peer (self , stalling_peer , delivery_peer ):
768772 node = self .nodes [0 ]
769773 assert len (self .utxos )
770774
@@ -842,6 +846,77 @@ def assert_highbandwidth_states(node, hb_to, hb_from):
842846 hb_test_node .send_and_ping (msg_sendcmpct (announce = False , version = 2 ))
843847 assert_highbandwidth_states (self .nodes [0 ], hb_to = True , hb_from = False )
844848
849+ def test_compactblock_reconstruction_parallel_reconstruction (self , stalling_peer , delivery_peer , inbound_peer , outbound_peer ):
850+ """ All p2p connections are inbound except outbound_peer. We test that ultimate parallel slot
851+ can only be taken by an outbound node unless prior attempts were done by an outbound
852+ """
853+ node = self .nodes [0 ]
854+ assert len (self .utxos )
855+
856+ def announce_cmpct_block (node , peer , txn_count ):
857+ utxo = self .utxos .pop (0 )
858+ block = self .build_block_with_transactions (node , utxo , txn_count )
859+
860+ cmpct_block = HeaderAndShortIDs ()
861+ cmpct_block .initialize_from_block (block )
862+ msg = msg_cmpctblock (cmpct_block .to_p2p ())
863+ peer .send_and_ping (msg )
864+ with p2p_lock :
865+ assert "getblocktxn" in peer .last_message
866+ return block , cmpct_block
867+
868+ for name , peer in [("delivery" , delivery_peer ), ("inbound" , inbound_peer ), ("outbound" , outbound_peer )]:
869+ self .log .info (f"Setting { name } as high bandwidth peer" )
870+ block , cmpct_block = announce_cmpct_block (node , peer , 1 )
871+ msg = msg_blocktxn ()
872+ msg .block_transactions .blockhash = block .sha256
873+ msg .block_transactions .transactions = block .vtx [1 :]
874+ peer .send_and_ping (msg )
875+ assert_equal (int (node .getbestblockhash (), 16 ), block .sha256 )
876+ peer .clear_getblocktxn ()
877+
878+ # Test the simple parallel download case...
879+ for num_missing in [1 , 5 , 20 ]:
880+
881+ # Remaining low-bandwidth peer is stalling_peer, who announces first
882+ assert_equal ([peer ['bip152_hb_to' ] for peer in node .getpeerinfo ()], [False , True , True , True ])
883+
884+ block , cmpct_block = announce_cmpct_block (node , stalling_peer , num_missing )
885+
886+ delivery_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
887+ with p2p_lock :
888+ # The second peer to announce should still get a getblocktxn
889+ assert "getblocktxn" in delivery_peer .last_message
890+ assert int (node .getbestblockhash (), 16 ) != block .sha256
891+
892+ inbound_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
893+ with p2p_lock :
894+ # The third inbound peer to announce should *not* get a getblocktxn
895+ assert "getblocktxn" not in inbound_peer .last_message
896+ assert int (node .getbestblockhash (), 16 ) != block .sha256
897+
898+ outbound_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
899+ with p2p_lock :
900+ # The third peer to announce should get a getblocktxn if outbound
901+ assert "getblocktxn" in outbound_peer .last_message
902+ assert int (node .getbestblockhash (), 16 ) != block .sha256
903+
904+ # Second peer completes the compact block first
905+ msg = msg_blocktxn ()
906+ msg .block_transactions .blockhash = block .sha256
907+ msg .block_transactions .transactions = block .vtx [1 :]
908+ delivery_peer .send_and_ping (msg )
909+ assert_equal (int (node .getbestblockhash (), 16 ), block .sha256 )
910+
911+ # Nothing bad should happen if we get a late fill from the first peer...
912+ stalling_peer .send_and_ping (msg )
913+ self .utxos .append ([block .vtx [- 1 ].sha256 , 0 , block .vtx [- 1 ].vout [0 ].nValue ])
914+
915+ delivery_peer .clear_getblocktxn ()
916+ inbound_peer .clear_getblocktxn ()
917+ outbound_peer .clear_getblocktxn ()
918+
919+
845920 def run_test (self ):
846921 # Get the nodes out of IBD
847922 self .nodes [0 ].generate (1 )
@@ -850,6 +925,7 @@ def run_test(self):
850925 self .segwit_node = self .nodes [0 ].add_p2p_connection (TestP2PConn (cmpct_version = 2 ))
851926 self .old_node = self .nodes [0 ].add_p2p_connection (TestP2PConn (cmpct_version = 1 ), services = NODE_NETWORK )
852927 self .additional_segwit_node = self .nodes [0 ].add_p2p_connection (TestP2PConn (cmpct_version = 2 ))
928+ self .outbound_node = self .nodes [0 ].add_outbound_p2p_connection (TestP2PConn (), p2p_idx = 3 , connection_type = "outbound-full-relay" )
853929
854930 # We will need UTXOs to construct transactions in later tests.
855931 self .make_utxos ()
@@ -859,6 +935,8 @@ def run_test(self):
859935 self .log .info ("Testing SENDCMPCT p2p message... " )
860936 self .test_sendcmpct (self .segwit_node , old_node = self .old_node )
861937 self .test_sendcmpct (self .additional_segwit_node )
938+ self .test_sendcmpct (self .onemore_inbound_node )
939+ self .test_sendcmpct (self .outbound_node )
862940
863941 self .log .info ("Testing compactblock construction..." )
864942 self .test_compactblock_construction (self .old_node )
@@ -881,8 +959,11 @@ def run_test(self):
881959 self .log .info ("Testing handling of incorrect blocktxn responses..." )
882960 self .test_incorrect_blocktxn_response (self .segwit_node )
883961
884- self .log .info ("Testing reconstructing compact blocks from all peers..." )
885- self .test_compactblock_reconstruction_multiple_peers (self .segwit_node , self .additional_segwit_node )
962+ self .log .info ("Testing reconstructing compact blocks with a stalling peer..." )
963+ self .test_compactblock_reconstruction_stalling_peer (self .segwit_node , self .additional_segwit_node )
964+
965+ self .log .info ("Testing reconstructing compact blocks from multiple peers..." )
966+ self .test_compactblock_reconstruction_parallel_reconstruction (stalling_peer = self .segwit_node , inbound_peer = self .onemore_inbound_node , delivery_peer = self .additional_segwit_node , outbound_peer = self .outbound_node )
886967
887968 # Test that if we submitblock to node1, we'll get a compact block
888969 # announcement to all peers.
0 commit comments