@@ -105,6 +105,10 @@ def clear_block_announcement(self):
105105 self .last_message .pop ("headers" , None )
106106 self .last_message .pop ("cmpctblock" , None )
107107
108+ def clear_getblocktxn (self ):
109+ with p2p_lock :
110+ self .last_message .pop ("getblocktxn" , None )
111+
108112 def get_headers (self , locator , hashstop ):
109113 msg = msg_getheaders ()
110114 msg .locator .vHave = locator
@@ -745,7 +749,7 @@ def request_cb_announcements(self, peer):
745749 peer .get_headers (locator = [int (tip , 16 )], hashstop = 0 )
746750 peer .send_and_ping (msg_sendcmpct (announce = True , version = 2 ))
747751
748- def test_compactblock_reconstruction_multiple_peers (self , stalling_peer , delivery_peer ):
752+ def test_compactblock_reconstruction_stalling_peer (self , stalling_peer , delivery_peer ):
749753 node = self .nodes [0 ]
750754 assert len (self .utxos )
751755
@@ -823,12 +827,85 @@ def assert_highbandwidth_states(node, hb_to, hb_from):
823827 hb_test_node .send_and_ping (msg_sendcmpct (announce = False , version = 2 ))
824828 assert_highbandwidth_states (self .nodes [0 ], hb_to = True , hb_from = False )
825829
830+ def test_compactblock_reconstruction_parallel_reconstruction (self , stalling_peer , delivery_peer , inbound_peer , outbound_peer ):
831+ """ All p2p connections are inbound except outbound_peer. We test that ultimate parallel slot
832+ can only be taken by an outbound node unless prior attempts were done by an outbound
833+ """
834+ node = self .nodes [0 ]
835+ assert len (self .utxos )
836+
837+ def announce_cmpct_block (node , peer , txn_count ):
838+ utxo = self .utxos .pop (0 )
839+ block = self .build_block_with_transactions (node , utxo , txn_count )
840+
841+ cmpct_block = HeaderAndShortIDs ()
842+ cmpct_block .initialize_from_block (block )
843+ msg = msg_cmpctblock (cmpct_block .to_p2p ())
844+ peer .send_and_ping (msg )
845+ with p2p_lock :
846+ assert "getblocktxn" in peer .last_message
847+ return block , cmpct_block
848+
849+ for name , peer in [("delivery" , delivery_peer ), ("inbound" , inbound_peer ), ("outbound" , outbound_peer )]:
850+ self .log .info (f"Setting { name } as high bandwidth peer" )
851+ block , cmpct_block = announce_cmpct_block (node , peer , 1 )
852+ msg = msg_blocktxn ()
853+ msg .block_transactions .blockhash = block .sha256
854+ msg .block_transactions .transactions = block .vtx [1 :]
855+ peer .send_and_ping (msg )
856+ assert_equal (int (node .getbestblockhash (), 16 ), block .sha256 )
857+ peer .clear_getblocktxn ()
858+
859+ # Test the simple parallel download case...
860+ for num_missing in [1 , 5 , 20 ]:
861+
862+ # Remaining low-bandwidth peer is stalling_peer, who announces first
863+ assert_equal ([peer ['bip152_hb_to' ] for peer in node .getpeerinfo ()], [False , True , True , True ])
864+
865+ block , cmpct_block = announce_cmpct_block (node , stalling_peer , num_missing )
866+
867+ delivery_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
868+ with p2p_lock :
869+ # The second peer to announce should still get a getblocktxn
870+ assert "getblocktxn" in delivery_peer .last_message
871+ assert int (node .getbestblockhash (), 16 ) != block .sha256
872+
873+ inbound_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
874+ with p2p_lock :
875+ # The third inbound peer to announce should *not* get a getblocktxn
876+ assert "getblocktxn" not in inbound_peer .last_message
877+ assert int (node .getbestblockhash (), 16 ) != block .sha256
878+
879+ outbound_peer .send_and_ping (msg_cmpctblock (cmpct_block .to_p2p ()))
880+ with p2p_lock :
881+ # The third peer to announce should get a getblocktxn if outbound
882+ assert "getblocktxn" in outbound_peer .last_message
883+ assert int (node .getbestblockhash (), 16 ) != block .sha256
884+
885+ # Second peer completes the compact block first
886+ msg = msg_blocktxn ()
887+ msg .block_transactions .blockhash = block .sha256
888+ msg .block_transactions .transactions = block .vtx [1 :]
889+ delivery_peer .send_and_ping (msg )
890+ assert_equal (int (node .getbestblockhash (), 16 ), block .sha256 )
891+
892+ # Nothing bad should happen if we get a late fill from the first peer...
893+ stalling_peer .send_and_ping (msg )
894+ self .utxos .append ([block .vtx [- 1 ].sha256 , 0 , block .vtx [- 1 ].vout [0 ].nValue ])
895+
896+ delivery_peer .clear_getblocktxn ()
897+ inbound_peer .clear_getblocktxn ()
898+ outbound_peer .clear_getblocktxn ()
899+
900+
826901 def run_test (self ):
827902 self .wallet = MiniWallet (self .nodes [0 ])
828903
829904 # Setup the p2p connections
830905 self .segwit_node = self .nodes [0 ].add_p2p_connection (TestP2PConn ())
831906 self .additional_segwit_node = self .nodes [0 ].add_p2p_connection (TestP2PConn ())
907+ self .onemore_inbound_node = self .nodes [0 ].add_p2p_connection (TestP2PConn ())
908+ self .outbound_node = self .nodes [0 ].add_outbound_p2p_connection (TestP2PConn (), p2p_idx = 3 , connection_type = "outbound-full-relay" )
832909
833910 # We will need UTXOs to construct transactions in later tests.
834911 self .make_utxos ()
@@ -838,6 +915,8 @@ def run_test(self):
838915 self .log .info ("Testing SENDCMPCT p2p message... " )
839916 self .test_sendcmpct (self .segwit_node )
840917 self .test_sendcmpct (self .additional_segwit_node )
918+ self .test_sendcmpct (self .onemore_inbound_node )
919+ self .test_sendcmpct (self .outbound_node )
841920
842921 self .log .info ("Testing compactblock construction..." )
843922 self .test_compactblock_construction (self .segwit_node )
@@ -860,8 +939,11 @@ def run_test(self):
860939 self .log .info ("Testing handling of incorrect blocktxn responses..." )
861940 self .test_incorrect_blocktxn_response (self .segwit_node )
862941
863- self .log .info ("Testing reconstructing compact blocks from all peers..." )
864- self .test_compactblock_reconstruction_multiple_peers (self .segwit_node , self .additional_segwit_node )
942+ self .log .info ("Testing reconstructing compact blocks with a stalling peer..." )
943+ self .test_compactblock_reconstruction_stalling_peer (self .segwit_node , self .additional_segwit_node )
944+
945+ self .log .info ("Testing reconstructing compact blocks from multiple peers..." )
946+ 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 )
865947
866948 # Test that if we submitblock to node1, we'll get a compact block
867949 # announcement to all peers.
0 commit comments