mempool_unbroadcast.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2017-2022 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 """Test that the mempool ensures transaction delivery by periodically sending 6 to peers until a GETDATA is received.""" 7 8 from test_framework.p2p import P2PTxInvStore 9 from test_framework.test_framework import BitcoinTestFramework 10 from test_framework.util import ( 11 assert_equal, 12 ensure_for, 13 ) 14 from test_framework.wallet import MiniWallet 15 16 MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds 17 18 class MempoolUnbroadcastTest(BitcoinTestFramework): 19 def set_test_params(self): 20 self.num_nodes = 2 21 self.uses_wallet = None 22 23 def run_test(self): 24 self.wallet = MiniWallet(self.nodes[0]) 25 self.test_broadcast() 26 self.test_txn_removal() 27 28 def test_broadcast(self): 29 self.log.info("Test that mempool reattempts delivery of locally submitted transaction") 30 node = self.nodes[0] 31 32 self.disconnect_nodes(0, 1) 33 34 self.log.info("Generate transactions that only node 0 knows about") 35 36 if self.is_wallet_compiled(): 37 self.import_deterministic_coinbase_privkeys() 38 # generate a wallet txn 39 addr = node.getnewaddress() 40 wallet_tx_hsh = node.sendtoaddress(addr, 0.0001) 41 42 # generate a txn using sendrawtransaction 43 txFS = self.wallet.create_self_transfer() 44 rpc_tx_hsh = node.sendrawtransaction(txFS["hex"]) 45 46 # check transactions are in unbroadcast using rpc 47 mempoolinfo = self.nodes[0].getmempoolinfo() 48 unbroadcast_count = 1 49 if self.is_wallet_compiled(): 50 unbroadcast_count += 1 51 assert_equal(mempoolinfo['unbroadcastcount'], unbroadcast_count) 52 mempool = self.nodes[0].getrawmempool(True) 53 for tx in mempool: 54 assert_equal(mempool[tx]['unbroadcast'], True) 55 56 # check that second node doesn't have these two txns 57 mempool = self.nodes[1].getrawmempool() 58 assert rpc_tx_hsh not in mempool 59 if self.is_wallet_compiled(): 60 assert wallet_tx_hsh not in mempool 61 62 # ensure that unbroadcast txs are persisted to mempool.dat 63 self.restart_node(0) 64 65 self.log.info("Reconnect nodes & check if they are sent to node 1") 66 self.connect_nodes(0, 1) 67 68 # fast forward into the future & ensure that the second node has the txns 69 node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY) 70 self.sync_mempools(timeout=30) 71 mempool = self.nodes[1].getrawmempool() 72 assert rpc_tx_hsh in mempool 73 if self.is_wallet_compiled(): 74 assert wallet_tx_hsh in mempool 75 76 # check that transactions are no longer in first node's unbroadcast set 77 mempool = self.nodes[0].getrawmempool(True) 78 for tx in mempool: 79 assert_equal(mempool[tx]['unbroadcast'], False) 80 81 self.log.info("Add another connection & ensure transactions aren't broadcast again") 82 83 conn = node.add_p2p_connection(P2PTxInvStore()) 84 node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY) 85 # allow sufficient time for possibility of broadcast 86 ensure_for(duration=2, f=lambda: len(conn.get_invs()) == 0) 87 88 self.disconnect_nodes(0, 1) 89 node.disconnect_p2ps() 90 91 self.log.info("Rebroadcast transaction and ensure it is not added to unbroadcast set when already in mempool") 92 rpc_tx_hsh = node.sendrawtransaction(txFS["hex"]) 93 assert not node.getmempoolentry(rpc_tx_hsh)['unbroadcast'] 94 95 def test_txn_removal(self): 96 self.log.info("Test that transactions removed from mempool are removed from unbroadcast set") 97 node = self.nodes[0] 98 99 # since the node doesn't have any connections, it will not receive 100 # any GETDATAs & thus the transaction will remain in the unbroadcast set. 101 txhsh = self.wallet.send_self_transfer(from_node=node)["txid"] 102 103 # check transaction was removed from unbroadcast set due to presence in 104 # a block 105 removal_reason = "Removed {} from set of unbroadcast txns before confirmation that txn was sent out".format(txhsh) 106 with node.assert_debug_log([removal_reason]): 107 self.generate(node, 1, sync_fun=self.no_op) 108 109 110 if __name__ == "__main__": 111 MempoolUnbroadcastTest(__file__).main()