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