/ test / functional / p2p_feefilter.py
p2p_feefilter.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2016-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 processing of feefilter messages."""
  6  
  7  from decimal import Decimal
  8  
  9  from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
 10  from test_framework.p2p import P2PInterface, p2p_lock
 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  
 16  class FeefilterConn(P2PInterface):
 17      feefilter_received = False
 18  
 19      def on_feefilter(self, message):
 20          self.feefilter_received = True
 21  
 22      def assert_feefilter_received(self, recv: bool):
 23          with p2p_lock:
 24              assert_equal(self.feefilter_received, recv)
 25  
 26  
 27  class TestP2PConn(P2PInterface):
 28      def __init__(self):
 29          super().__init__()
 30          self.txinvs = []
 31  
 32      def on_inv(self, message):
 33          for i in message.inv:
 34              if (i.type == MSG_TX) or (i.type == MSG_WTX):
 35                  self.txinvs.append('{:064x}'.format(i.hash))
 36  
 37      def wait_for_invs_to_match(self, invs_expected):
 38          invs_expected.sort()
 39          self.wait_until(lambda: invs_expected == sorted(self.txinvs))
 40  
 41      def clear_invs(self):
 42          with p2p_lock:
 43              self.txinvs = []
 44  
 45  
 46  class FeeFilterTest(BitcoinTestFramework):
 47      def set_test_params(self):
 48          self.num_nodes = 2
 49          # whitelist peers to speed up tx relay / mempool sync
 50          self.noban_tx_relay = True
 51          # We lower the various required feerates for this test
 52          # to catch a corner-case where feefilter used to slightly undercut
 53          # mempool and wallet feerate calculation based on GetFee
 54          # rounding down 3 places, leading to stranded transactions.
 55          # See issue #16499
 56          self.extra_args = [[
 57              "-minrelaytxfee=0.00000100",
 58              "-mintxfee=0.00000100"
 59          ]] * self.num_nodes
 60  
 61      def run_test(self):
 62          self.test_feefilter_forcerelay()
 63          self.test_feefilter()
 64          self.test_feefilter_blocksonly()
 65  
 66      def test_feefilter_forcerelay(self):
 67          self.log.info('Check that peers without forcerelay permission (default) get a feefilter message')
 68          self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received(True)
 69  
 70          self.log.info('Check that peers with forcerelay permission do not get a feefilter message')
 71          self.restart_node(0, extra_args=['-whitelist=forcerelay@127.0.0.1'])
 72          self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received(False)
 73  
 74          # Restart to disconnect peers and load default extra_args
 75          self.restart_node(0)
 76          self.connect_nodes(1, 0)
 77  
 78      def test_feefilter(self):
 79          node1 = self.nodes[1]
 80          node0 = self.nodes[0]
 81          miniwallet = MiniWallet(node1)
 82  
 83          conn = self.nodes[0].add_p2p_connection(TestP2PConn())
 84  
 85          self.log.info("Test txs paying 0.2 sat/byte are received by test connection")
 86          txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000200'), from_node=node1)['wtxid'] for _ in range(3)]
 87          conn.wait_for_invs_to_match(txids)
 88          conn.clear_invs()
 89  
 90          # Set a fee filter of 0.15 sat/byte on test connection
 91          conn.send_and_ping(msg_feefilter(150))
 92  
 93          self.log.info("Test txs paying 0.15 sat/byte are received by test connection")
 94          txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000150'), from_node=node1)['wtxid'] for _ in range(3)]
 95          conn.wait_for_invs_to_match(txids)
 96          conn.clear_invs()
 97  
 98          self.log.info("Test txs paying 0.1 sat/byte are no longer received by test connection")
 99          txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000100'), from_node=node1)['wtxid'] for _ in range(3)]
100          self.sync_mempools()  # must be sure node 0 has received all txs
101  
102          # Send one transaction from node0 that should be received, so that we
103          # we can sync the test on receipt (if node1's txs were relayed, they'd
104          # be received by the time this node0 tx is received). This is
105          # unfortunately reliant on the current relay behavior where we batch up
106          # to 35 entries in an inv, which means that when this next transaction
107          # is eligible for relay, the prior transactions from node1 are eligible
108          # as well.
109          txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node0)['wtxid'] for _ in range(1)]
110          conn.wait_for_invs_to_match(txids)
111          conn.clear_invs()
112          self.sync_mempools()  # must be sure node 1 has received all txs
113  
114          self.log.info("Remove fee filter and check txs are received again")
115          conn.send_and_ping(msg_feefilter(0))
116          txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node1)['wtxid'] for _ in range(3)]
117          conn.wait_for_invs_to_match(txids)
118          conn.clear_invs()
119  
120      def test_feefilter_blocksonly(self):
121          """Test that we don't send fee filters to block-relay-only peers and when we're in blocksonly mode."""
122          self.log.info("Check that we don't send fee filters to block-relay-only peers.")
123          feefilter_peer = self.nodes[0].add_outbound_p2p_connection(FeefilterConn(), p2p_idx=0, connection_type="block-relay-only")
124          feefilter_peer.sync_with_ping()
125          feefilter_peer.assert_feefilter_received(False)
126  
127          self.log.info("Check that we don't send fee filters when in blocksonly mode.")
128          self.restart_node(0, ["-blocksonly"])
129          feefilter_peer = self.nodes[0].add_p2p_connection(FeefilterConn())
130          feefilter_peer.sync_with_ping()
131          feefilter_peer.assert_feefilter_received(False)
132  
133  
134  if __name__ == '__main__':
135      FeeFilterTest(__file__).main()