p2p_1p1c_network.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2024-present The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 """ 6 Test that 1p1c package submission allows a 1p1c package to propagate in a "network" of nodes. Send 7 various packages from different nodes on a network in which some nodes have already received some of 8 the transactions (and submitted them to mempool, kept them as orphans or rejected them as 9 too-low-feerate transactions). The packages should be received and accepted by all nodes. 10 """ 11 12 from decimal import Decimal 13 14 from test_framework.mempool_util import ( 15 DEFAULT_MIN_RELAY_TX_FEE, 16 ) 17 from test_framework.messages import ( 18 COIN, 19 msg_tx, 20 ) 21 from test_framework.p2p import ( 22 P2PInterface, 23 ) 24 from test_framework.test_framework import BitcoinTestFramework 25 from test_framework.util import ( 26 assert_equal, 27 ) 28 from test_framework.wallet import ( 29 MiniWallet, 30 MiniWalletMode, 31 ) 32 33 class PackageRelayTest(BitcoinTestFramework): 34 def set_test_params(self): 35 self.setup_clean_chain = True 36 self.num_nodes = 4 37 # hugely speeds up the test, as it involves multiple hops of tx relay. 38 self.noban_tx_relay = True 39 40 def create_basic_1p1c(self, wallet): 41 low_fee_parent = wallet.create_self_transfer(fee_rate=0, confirmed_only=True) 42 high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*Decimal(DEFAULT_MIN_RELAY_TX_FEE)/ COIN) 43 package_hex_basic = [low_fee_parent["hex"], high_fee_child["hex"]] 44 return package_hex_basic, low_fee_parent["tx"], high_fee_child["tx"] 45 46 def create_package_2outs(self, wallet): 47 # First create a tester tx to see the vsize, and then adjust the fees 48 utxo_for_2outs = wallet.get_utxo(confirmed_only=True) 49 50 low_fee_parent_2outs = wallet.create_self_transfer_multi( 51 utxos_to_spend=[utxo_for_2outs], 52 num_outputs=2, 53 fee_per_output=0, 54 ) 55 56 # Now create the child 57 high_fee_child_2outs = wallet.create_self_transfer_multi( 58 utxos_to_spend=low_fee_parent_2outs["new_utxos"][::-1], 59 fee_per_output=10_000, 60 ) 61 return [low_fee_parent_2outs["hex"], high_fee_child_2outs["hex"]], low_fee_parent_2outs["tx"], high_fee_child_2outs["tx"] 62 63 def create_package_2p1c(self, wallet): 64 parent1 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 10, confirmed_only=True) 65 parent2 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 20, confirmed_only=True) 66 child = wallet.create_self_transfer_multi( 67 utxos_to_spend=[parent1["new_utxo"], parent2["new_utxo"]], 68 fee_per_output=999*parent1["tx"].get_vsize(), 69 ) 70 return [parent1["hex"], parent2["hex"], child["hex"]], parent1["tx"], parent2["tx"], child["tx"] 71 72 def create_packages(self): 73 # 1: Basic 1-parent-1-child package, parent 0sat/vB, child 999sat/vB 74 package_hex_1, parent_1, child_1 = self.create_basic_1p1c(self.wallet) 75 76 # 2: same as 1, parent's txid is the same as its wtxid. 77 package_hex_2, parent_2, child_2 = self.create_basic_1p1c(self.wallet_nonsegwit) 78 79 # 3: 2-parent-1-child package. Both parents are above mempool min feerate. No package submission happens. 80 # We require packages to be child-with-parents and only allow 1-parent-1-child packages. 81 package_hex_3, parent_31, _parent_32, child_3 = self.create_package_2p1c(self.wallet) 82 83 # 4: parent + child package where the child spends 2 different outputs from the parent. 84 package_hex_4, parent_4, child_4 = self.create_package_2outs(self.wallet) 85 86 # Assemble return results 87 packages_to_submit = [package_hex_1, package_hex_2, package_hex_3, package_hex_4] 88 # node0: sender 89 # node1: pre-received the children (orphans, will be dropped on peer disconnect) 90 # node3: pre-received the parents (too low fee) 91 # All nodes receive parent_31 ahead of time. 92 txns_to_send = [ 93 [], 94 [child_1, child_2, parent_31, child_3, child_4], 95 [parent_31], 96 [parent_1, parent_2, parent_31, parent_4] 97 ] 98 99 return packages_to_submit, txns_to_send 100 101 def run_test(self): 102 self.wallet = MiniWallet(self.nodes[1]) 103 self.wallet_nonsegwit = MiniWallet(self.nodes[2], mode=MiniWalletMode.RAW_P2PK) 104 self.generate(self.wallet_nonsegwit, 10) 105 self.generate(self.wallet, 120) 106 107 # Create the transactions. 108 self.wallet.rescan_utxos(include_mempool=True) 109 packages_to_submit, transactions_to_presend = self.create_packages() 110 111 self.peers = [self.nodes[i].add_p2p_connection(P2PInterface()) for i in range(self.num_nodes)] 112 113 self.log.info("Pre-send some transactions to nodes") 114 for (i, peer) in enumerate(self.peers): 115 for tx in transactions_to_presend[i]: 116 peer.send_and_ping(msg_tx(tx)) 117 118 # The fee-having parent should be the only thing in mempools 119 self.sync_mempools() 120 sufficient_parent = transactions_to_presend[2][0].txid_hex 121 for i, node in enumerate(self.nodes): 122 # node1 has non-empty orphanage as well 123 if i == 1: 124 assert_equal(len(self.nodes[i].getorphantxs()), 4) 125 else: 126 assert_equal(self.nodes[i].getorphantxs(), []) 127 128 assert_equal(node.getrawmempool(), [sufficient_parent]) 129 130 # Disconnect python peers to clear outstanding orphan requests with them, avoiding timeouts. 131 # We are only interested in the syncing behavior between real nodes. 132 for i in range(self.num_nodes): 133 self.nodes[i].disconnect_p2ps() 134 self.wait_until(lambda: len(self.nodes[i].getorphantxs()) == 0) 135 136 self.log.info("Submit full packages to node0") 137 for package_hex in packages_to_submit: 138 submitpackage_result = self.nodes[0].submitpackage(package_hex) 139 assert_equal(submitpackage_result["package_msg"], "success") 140 141 self.log.info("Wait for mempools to sync") 142 self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == 9) 143 self.sync_mempools() 144 145 146 if __name__ == '__main__': 147 PackageRelayTest(__file__).main()