Changeset View
Changeset View
Standalone View
Standalone View
test/functional/p2p_feefilter.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2016-2019 The Bitcoin Core developers | # Copyright (c) 2016-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test processing of feefilter messages.""" | """Test processing of feefilter messages.""" | ||||
from decimal import Decimal | from decimal import Decimal | ||||
import time | |||||
from test_framework.messages import MSG_TX, msg_feefilter | from test_framework.messages import MSG_TX, msg_feefilter | ||||
from test_framework.p2p import ( | from test_framework.p2p import ( | ||||
P2PInterface, | P2PInterface, | ||||
p2p_lock, | p2p_lock, | ||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import assert_equal | from test_framework.util import assert_equal | ||||
def hashToHex(hash): | def hashToHex(hash): | ||||
return format(hash, '064x') | return format(hash, '064x') | ||||
# Wait up to 60 secs to see if the testnode has received all the expected invs | |||||
def allInvsMatch(invsExpected, testnode): | |||||
for _ in range(60): | |||||
with p2p_lock: | |||||
if (sorted(invsExpected) == sorted(testnode.txinvs)): | |||||
return True | |||||
time.sleep(1) | |||||
return False | |||||
class FeefilterConn(P2PInterface): | class FeefilterConn(P2PInterface): | ||||
feefilter_received = False | feefilter_received = False | ||||
def on_feefilter(self, message): | def on_feefilter(self, message): | ||||
self.feefilter_received = True | self.feefilter_received = True | ||||
def assert_feefilter_received(self, recv: bool): | def assert_feefilter_received(self, recv: bool): | ||||
with p2p_lock: | with p2p_lock: | ||||
assert_equal(self.feefilter_received, recv) | assert_equal(self.feefilter_received, recv) | ||||
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(hashToHex(i.hash)) | self.txinvs.append(hashToHex(i.hash)) | ||||
def wait_for_invs_to_match(self, invs_expected): | |||||
invs_expected.sort() | |||||
self.wait_until(lambda: invs_expected == sorted(self.txinvs), | |||||
timeout=60) | |||||
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 | ||||
self.extra_args = [["-minrelaytxfee=1", | # grant noban permission to all peers to speed up tx relay / mempool | ||||
"-mintxfee=1"]] * self.num_nodes | # sync | ||||
self.extra_args = [[ | |||||
"-minrelaytxfee=1", | |||||
"-mintxfee=1", | |||||
"-whitelist=noban@127.0.0.1", | |||||
]] * self.num_nodes | |||||
def skip_test_if_missing_module(self): | def skip_test_if_missing_module(self): | ||||
self.skip_if_no_wallet() | self.skip_if_no_wallet() | ||||
def run_test(self): | def run_test(self): | ||||
self.test_feefilter_forcerelay() | self.test_feefilter_forcerelay() | ||||
self.test_feefilter() | self.test_feefilter() | ||||
Show All 14 Lines | def test_feefilter_forcerelay(self): | ||||
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] | ||||
conn = self.nodes[0].add_p2p_connection(TestP2PConn()) | conn = self.nodes[0].add_p2p_connection(TestP2PConn()) | ||||
# Test that invs are received by test connection for all txs at | self.log.info( | ||||
# feerate of .2 sat/byte | "Test txs paying 0.2 sat/byte are received by test connection") | ||||
node1.settxfee(Decimal("2")) | node1.settxfee(Decimal("2")) | ||||
txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | ||||
for _ in range(3)] | for _ in range(3)] | ||||
assert allInvsMatch(txids, conn) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
# Set a filter of .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)) | ||||
# Test that txs are still being received by test connection | self.log.info( | ||||
# (paying .15 sat/byte) | "Test txs paying 0.15 sat/byte are received by test connection") | ||||
node1.settxfee(Decimal("1.5")) | node1.settxfee(Decimal("1.5")) | ||||
txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | ||||
for _ in range(3)] | for _ in range(3)] | ||||
assert allInvsMatch(txids, conn) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
# Change tx fee rate to .1 sat/byte and test they are no longer received | self.log.info( | ||||
# by the test connection | "Test txs paying 0.1 sat/byte are no longer received by test connection") | ||||
node1.settxfee(Decimal("1")) | node1.settxfee(Decimal("1")) | ||||
[node1.sendtoaddress(node1.getnewaddress(), 1000000) for _ in range(3)] | [node1.sendtoaddress(node1.getnewaddress(), 1000000) 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. | ||||
node0.settxfee(Decimal("200.00")) | node0.settxfee(Decimal("200.00")) | ||||
txids = [node0.sendtoaddress(node0.getnewaddress(), 1000000)] | txids = [node0.sendtoaddress(node0.getnewaddress(), 1000000)] | ||||
assert allInvsMatch(txids, conn) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
# Remove fee filter and check that 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 = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) | ||||
for _ in range(3)] | for _ in range(3)] | ||||
assert allInvsMatch(txids, conn) | conn.wait_for_invs_to_match(txids) | ||||
conn.clear_invs() | conn.clear_invs() | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
FeeFilterTest().main() | FeeFilterTest().main() |