aboutsummaryrefslogtreecommitdiff
path: root/test/functional/p2p_feefilter.py
blob: f939ea965cf62f8e6d15e3fccb980964f558bd4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python3
# Copyright (c) 2016-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test processing of feefilter messages."""

from decimal import Decimal
import time

from test_framework.messages import MSG_TX, msg_feefilter
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal


def hashToHex(hash):
    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 x in range(60):
        with mininode_lock:
            if (sorted(invsExpected) == sorted(testnode.txinvs)):
                return True
        time.sleep(1)
    return False


class FeefilterConn(P2PInterface):
    feefilter_received = False

    def on_feefilter(self, message):
        self.feefilter_received = True

    def assert_feefilter_received(self, recv: bool):
        with mininode_lock:
            assert_equal(self.feefilter_received, recv)


class TestP2PConn(P2PInterface):
    def __init__(self):
        super().__init__()
        self.txinvs = []

    def on_inv(self, message):
        for i in message.inv:
            if (i.type == MSG_TX):
                self.txinvs.append(hashToHex(i.hash))

    def clear_invs(self):
        with mininode_lock:
            self.txinvs = []


class FeeFilterTest(BitcoinTestFramework):
    def set_test_params(self):
        self.num_nodes = 2
        # We lower the various required feerates for this test
        # to catch a corner-case where feefilter used to slightly undercut
        # mempool and wallet feerate calculation based on GetFee
        # rounding down 3 places, leading to stranded transactions.
        # See issue #16499
        self.extra_args = [["-minrelaytxfee=0.00000100", "-mintxfee=0.00000100"]] * self.num_nodes

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()

    def run_test(self):
        self.test_feefilter_forcerelay()
        self.test_feefilter()

    def test_feefilter_forcerelay(self):
        self.log.info('Check that peers without forcerelay permission (default) get a feefilter message')
        self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received(True)

        self.log.info('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(FeefilterConn()).assert_feefilter_received(False)

        # Restart to disconnect peers and load default extra_args
        self.restart_node(0)
        self.connect_nodes(1, 0)

    def test_feefilter(self):
        node1 = self.nodes[1]
        node0 = self.nodes[0]

        conn = self.nodes[0].add_p2p_connection(TestP2PConn())

        # Test that invs are received by test connection for all txs at
        # feerate of .2 sat/byte
        node1.settxfee(Decimal("0.00000200"))
        txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
        assert allInvsMatch(txids, conn)
        conn.clear_invs()

        # Set a filter of .15 sat/byte on test connection
        conn.send_and_ping(msg_feefilter(150))

        # Test that txs are still being received by test connection (paying .15 sat/byte)
        node1.settxfee(Decimal("0.00000150"))
        txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
        assert allInvsMatch(txids, conn)
        conn.clear_invs()

        # Change tx fee rate to .1 sat/byte and test they are no longer received
        # by the test connection
        node1.settxfee(Decimal("0.00000100"))
        [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
        self.sync_mempools()  # must be sure node 0 has received all txs

        # 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
        # be received by the time this node0 tx is received). This is
        # unfortunately reliant on the current relay behavior where we batch up
        # to 35 entries in an inv, which means that when this next transaction
        # is eligible for relay, the prior transactions from node1 are eligible
        # as well.
        node0.settxfee(Decimal("0.00020000"))
        txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
        assert allInvsMatch(txids, conn)
        conn.clear_invs()

        # Remove fee filter and check that txs are received again
        conn.send_and_ping(msg_feefilter(0))
        txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
        assert allInvsMatch(txids, conn)
        conn.clear_invs()


if __name__ == '__main__':
    FeeFilterTest().main()