1 #!/usr/bin/env python3
2 # Copyright (c) 2017-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 """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()
112