|
| 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 |
| 6 | +to peers until a GETDATA is received.""" |
| 7 | + |
| 8 | +import time |
| 9 | + |
| 10 | +from test_framework.mininode import P2PTxInvStore |
| 11 | +from test_framework.test_framework import BitcoinTestFramework |
| 12 | +from test_framework.util import ( |
| 13 | + assert_equal, |
| 14 | + connect_nodes, |
| 15 | + create_confirmed_utxos, |
| 16 | + disconnect_nodes, |
| 17 | +) |
| 18 | + |
| 19 | + |
| 20 | +class MempoolUnbroadcastTest(BitcoinTestFramework): |
| 21 | + def set_test_params(self): |
| 22 | + self.num_nodes = 2 |
| 23 | + |
| 24 | + def skip_test_if_missing_module(self): |
| 25 | + self.skip_if_no_wallet() |
| 26 | + |
| 27 | + def run_test(self): |
| 28 | + self.test_broadcast() |
| 29 | + self.test_txn_removal() |
| 30 | + |
| 31 | + def test_broadcast(self): |
| 32 | + self.log.info("Test that mempool reattempts delivery of locally submitted transaction") |
| 33 | + node = self.nodes[0] |
| 34 | + |
| 35 | + min_relay_fee = node.getnetworkinfo()["relayfee"] |
| 36 | + utxos = create_confirmed_utxos(min_relay_fee, node, 10) |
| 37 | + |
| 38 | + disconnect_nodes(node, 1) |
| 39 | + |
| 40 | + self.log.info("Generate transactions that only node 0 knows about") |
| 41 | + |
| 42 | + # generate a wallet txn |
| 43 | + addr = node.getnewaddress() |
| 44 | + wallet_tx_hsh = node.sendtoaddress(addr, 0.0001) |
| 45 | + |
| 46 | + # generate a txn using sendrawtransaction |
| 47 | + us0 = utxos.pop() |
| 48 | + inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] |
| 49 | + outputs = {addr: 0.0001} |
| 50 | + tx = node.createrawtransaction(inputs, outputs) |
| 51 | + node.settxfee(min_relay_fee) |
| 52 | + txF = node.fundrawtransaction(tx) |
| 53 | + txFS = node.signrawtransactionwithwallet(txF["hex"]) |
| 54 | + rpc_tx_hsh = node.sendrawtransaction(txFS["hex"]) |
| 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 | + assert wallet_tx_hsh not in mempool |
| 60 | + |
| 61 | + self.log.info("Reconnect nodes & check if they are sent to node 1") |
| 62 | + connect_nodes(node, 1) |
| 63 | + |
| 64 | + # fast forward into the future & ensure that the second node has the txns |
| 65 | + node.mockscheduler(15 * 60) # 15 min in seconds |
| 66 | + self.sync_mempools(timeout=30) |
| 67 | + mempool = self.nodes[1].getrawmempool() |
| 68 | + assert rpc_tx_hsh in mempool |
| 69 | + assert wallet_tx_hsh in mempool |
| 70 | + |
| 71 | + self.log.info("Add another connection & ensure transactions aren't broadcast again") |
| 72 | + |
| 73 | + conn = node.add_p2p_connection(P2PTxInvStore()) |
| 74 | + node.mockscheduler(15 * 60) |
| 75 | + time.sleep(5) |
| 76 | + assert_equal(len(conn.get_invs()), 0) |
| 77 | + |
| 78 | + def test_txn_removal(self): |
| 79 | + self.log.info("Test that transactions removed from mempool are removed from unbroadcast set") |
| 80 | + node = self.nodes[0] |
| 81 | + disconnect_nodes(node, 1) |
| 82 | + node.disconnect_p2ps |
| 83 | + |
| 84 | + # since the node doesn't have any connections, it will not receive |
| 85 | + # any GETDATAs & thus the transaction will remain in the unbroadcast set. |
| 86 | + addr = node.getnewaddress() |
| 87 | + txhsh = node.sendtoaddress(addr, 0.0001) |
| 88 | + |
| 89 | + # check transaction was removed from unbroadcast set due to presence in |
| 90 | + # a block |
| 91 | + removal_reason = "Removed {} from set of unbroadcast txns before confirmation that txn was sent out".format(txhsh) |
| 92 | + with node.assert_debug_log([removal_reason]): |
| 93 | + node.generate(1) |
| 94 | + |
| 95 | +if __name__ == "__main__": |
| 96 | + MempoolUnbroadcastTest().main() |
0 commit comments