Changeset View
Changeset View
Standalone View
Standalone View
test/functional/p2p_feefilter.py
Show All 25 Lines | |||||
class TestP2PConn(P2PInterface): | class TestP2PConn(P2PInterface): | ||||
def __init__(self): | def __init__(self): | ||||
super().__init__() | super().__init__() | ||||
self.txinvs = [] | self.txinvs = [] | ||||
def on_inv(self, message): | def on_inv(self, message): | ||||
for i in message.inv: | for i in message.inv: | ||||
if (i.type == MSG_TX): | if i.type == MSG_TX: | ||||
self.txinvs.append(uint256_hex(i.hash)) | self.txinvs.append(uint256_hex(i.hash)) | ||||
def wait_for_invs_to_match(self, invs_expected): | def wait_for_invs_to_match(self, invs_expected): | ||||
invs_expected.sort() | invs_expected.sort() | ||||
self.wait_until(lambda: invs_expected == sorted(self.txinvs)) | self.wait_until(lambda: invs_expected == sorted(self.txinvs)) | ||||
def clear_invs(self): | def clear_invs(self): | ||||
with p2p_lock: | with p2p_lock: | ||||
self.txinvs = [] | self.txinvs = [] | ||||
class FeeFilterTest(BitcoinTestFramework): | class FeeFilterTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
# We lower the various required feerates for this test | # We lower the various required feerates for this test | ||||
# to catch a corner-case where feefilter used to slightly undercut | # to catch a corner-case where feefilter used to slightly undercut | ||||
# mempool and wallet feerate calculation based on GetFee | # mempool and wallet feerate calculation based on GetFee | ||||
# rounding down 3 places, leading to stranded transactions. | # rounding down 3 places, leading to stranded transactions. | ||||
# See issue #16499 | # See issue #16499 | ||||
# grant noban permission to all peers to speed up tx relay / mempool | # grant noban permission to all peers to speed up tx relay / mempool | ||||
# sync | # sync | ||||
self.extra_args = [[ | self.extra_args = [ | ||||
[ | |||||
"-minrelaytxfee=1", | "-minrelaytxfee=1", | ||||
"-mintxfee=1", | "-mintxfee=1", | ||||
"-whitelist=noban@127.0.0.1", | "-whitelist=noban@127.0.0.1", | ||||
]] * self.num_nodes | ] | ||||
] * self.num_nodes | |||||
def run_test(self): | def run_test(self): | ||||
self.test_feefilter_forcerelay() | self.test_feefilter_forcerelay() | ||||
self.test_feefilter() | self.test_feefilter() | ||||
self.test_feefilter_blocksonly() | self.test_feefilter_blocksonly() | ||||
def test_feefilter_forcerelay(self): | def test_feefilter_forcerelay(self): | ||||
self.log.info( | self.log.info( | ||||
'Check that peers without forcerelay permission (default) get a feefilter message') | "Check that peers without forcerelay permission (default) get a feefilter" | ||||
self.nodes[0].add_p2p_connection( | " message" | ||||
FeefilterConn()).assert_feefilter_received(True) | ) | ||||
self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received( | |||||
True | |||||
) | |||||
self.log.info( | self.log.info( | ||||
'Check that peers with forcerelay permission do not get a feefilter message') | "Check that peers with forcerelay permission do not get a feefilter message" | ||||
self.restart_node(0, extra_args=['-whitelist=forcerelay@127.0.0.1']) | ) | ||||
self.nodes[0].add_p2p_connection( | self.restart_node(0, extra_args=["-whitelist=forcerelay@127.0.0.1"]) | ||||
FeefilterConn()).assert_feefilter_received(False) | self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received( | ||||
False | |||||
) | |||||
# Restart to disconnect peers and load default extra_args | # Restart to disconnect peers and load default extra_args | ||||
self.restart_node(0) | self.restart_node(0) | ||||
self.connect_nodes(1, 0) | self.connect_nodes(1, 0) | ||||
def test_feefilter(self): | def test_feefilter(self): | ||||
node1 = self.nodes[1] | node1 = self.nodes[1] | ||||
node0 = self.nodes[0] | node0 = self.nodes[0] | ||||
miniwallet = MiniWallet(node1) | miniwallet = MiniWallet(node1) | ||||
# Add enough mature utxos to the wallet, so that all txs spend | # Add enough mature utxos to the wallet, so that all txs spend | ||||
# confirmed coins | # confirmed coins | ||||
self.generate(miniwallet, 5) | self.generate(miniwallet, 5) | ||||
self.generate(node1, 100) | self.generate(node1, 100) | ||||
conn = self.nodes[0].add_p2p_connection(TestP2PConn()) | conn = self.nodes[0].add_p2p_connection(TestP2PConn()) | ||||
self.log.info( | self.log.info("Test txs paying 0.2 sat/byte are received by test connection") | ||||
"Test txs paying 0.2 sat/byte are received by test connection") | txids = [ | ||||
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('2.00'), | miniwallet.send_self_transfer(fee_rate=Decimal("2.00"), from_node=node1)[ | ||||
from_node=node1)['txid'] | "txid" | ||||
for _ in range(3)] | ] | ||||
for _ in range(3) | |||||
] | |||||
conn.wait_for_invs_to_match(txids) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
# Set a fee filter of 0.15 sat/byte on test connection | # Set a fee filter of 0.15 sat/byte on test connection | ||||
conn.send_and_ping(msg_feefilter(150)) | conn.send_and_ping(msg_feefilter(150)) | ||||
self.log.info( | self.log.info("Test txs paying 0.15 sat/byte are received by test connection") | ||||
"Test txs paying 0.15 sat/byte are received by test connection") | txids = [ | ||||
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('1.50'), | miniwallet.send_self_transfer(fee_rate=Decimal("1.50"), from_node=node1)[ | ||||
from_node=node1)['txid'] | "txid" | ||||
for _ in range(3)] | ] | ||||
for _ in range(3) | |||||
] | |||||
conn.wait_for_invs_to_match(txids) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
self.log.info( | self.log.info( | ||||
"Test txs paying 0.1 sat/byte are no longer received by test connection") | "Test txs paying 0.1 sat/byte are no longer received by test connection" | ||||
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('1.00'), | ) | ||||
from_node=node1)['txid'] | txids = [ | ||||
for _ in range(3)] | miniwallet.send_self_transfer(fee_rate=Decimal("1.00"), from_node=node1)[ | ||||
"txid" | |||||
] | |||||
for _ in range(3) | |||||
] | |||||
self.sync_mempools() # must be sure node 0 has received all txs | self.sync_mempools() # must be sure node 0 has received all txs | ||||
# Send one transaction from node0 that should be received, so that we | # Send one transaction from node0 that should be received, so that we | ||||
# we can sync the test on receipt (if node1's txs were relayed, they'd | # we can sync the test on receipt (if node1's txs were relayed, they'd | ||||
# be received by the time this node0 tx is received). This is | # be received by the time this node0 tx is received). This is | ||||
# unfortunately reliant on the current relay behavior where we batch up | # unfortunately reliant on the current relay behavior where we batch up | ||||
# to 35 entries in an inv, which means that when this next transaction | # to 35 entries in an inv, which means that when this next transaction | ||||
# is eligible for relay, the prior transactions from node1 are eligible | # is eligible for relay, the prior transactions from node1 are eligible | ||||
# as well. | # as well. | ||||
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('200.00'), | txids = [ | ||||
from_node=node0)['txid']] | miniwallet.send_self_transfer(fee_rate=Decimal("200.00"), from_node=node0)[ | ||||
"txid" | |||||
] | |||||
] | |||||
conn.wait_for_invs_to_match(txids) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
# must be sure node 1 has received all txs | # must be sure node 1 has received all txs | ||||
self.sync_mempools() | self.sync_mempools() | ||||
self.log.info("Remove fee filter and check txs are received again") | self.log.info("Remove fee filter and check txs are received again") | ||||
conn.send_and_ping(msg_feefilter(0)) | conn.send_and_ping(msg_feefilter(0)) | ||||
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('200.00'), | txids = [ | ||||
from_node=node1)['txid'] | miniwallet.send_self_transfer(fee_rate=Decimal("200.00"), from_node=node1)[ | ||||
for _ in range(3)] | "txid" | ||||
] | |||||
for _ in range(3) | |||||
] | |||||
conn.wait_for_invs_to_match(txids) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
def test_feefilter_blocksonly(self): | def test_feefilter_blocksonly(self): | ||||
"""Test that we don't send fee filters to block-relay-only peers and | """Test that we don't send fee filters to block-relay-only peers and | ||||
when we're in blocksonly mode.""" | when we're in blocksonly mode.""" | ||||
self.log.info( | self.log.info("Check that we don't send fee filters to block-relay-only peers.") | ||||
"Check that we don't send fee filters to block-relay-only peers.") | |||||
feefilter_peer = self.nodes[0].add_outbound_p2p_connection( | feefilter_peer = self.nodes[0].add_outbound_p2p_connection( | ||||
FeefilterConn(), p2p_idx=0, connection_type="block-relay-only") | FeefilterConn(), p2p_idx=0, connection_type="block-relay-only" | ||||
) | |||||
feefilter_peer.sync_with_ping() | feefilter_peer.sync_with_ping() | ||||
feefilter_peer.assert_feefilter_received(False) | feefilter_peer.assert_feefilter_received(False) | ||||
self.log.info( | self.log.info("Check that we don't send fee filters when in blocksonly mode.") | ||||
"Check that we don't send fee filters when in blocksonly mode.") | |||||
self.restart_node(0, ["-blocksonly"]) | self.restart_node(0, ["-blocksonly"]) | ||||
feefilter_peer = self.nodes[0].add_p2p_connection(FeefilterConn()) | feefilter_peer = self.nodes[0].add_p2p_connection(FeefilterConn()) | ||||
feefilter_peer.sync_with_ping() | feefilter_peer.sync_with_ping() | ||||
feefilter_peer.assert_feefilter_received(False) | feefilter_peer.assert_feefilter_received(False) | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
FeeFilterTest().main() | FeeFilterTest().main() |