p2p_feefilter.py raw

   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()
 136