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