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