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