1 #!/usr/bin/env python3
2 # Copyright (c) 2015-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 p2p permission message.
6 7 Test that permissions are correctly calculated and applied
8 """
9 10 from test_framework.messages import (
11 SEQUENCE_FINAL,
12 )
13 from test_framework.p2p import P2PDataStore
14 from test_framework.test_node import ErrorMatch
15 from test_framework.test_framework import BitcoinTestFramework
16 from test_framework.util import (
17 append_config,
18 assert_equal,
19 p2p_port,
20 tor_port,
21 )
22 from test_framework.wallet import MiniWallet
23 24 25 class P2PPermissionsTests(BitcoinTestFramework):
26 def set_test_params(self):
27 self.num_nodes = 2
28 29 def run_test(self):
30 self.wallet = MiniWallet(self.nodes[0])
31 32 self.check_tx_relay()
33 34 self.checkpermission(
35 # default permissions (no specific permissions)
36 ["-whitelist=127.0.0.1"],
37 # Make sure the default values in the command line documentation match the ones here
38 ["relay", "noban", "mempool", "download"])
39 40 self.checkpermission(
41 # no permission (even with forcerelay)
42 ["-whitelist=@127.0.0.1", "-whitelistforcerelay=1"],
43 [])
44 45 self.checkpermission(
46 # relay permission removed (no specific permissions)
47 ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
48 ["noban", "mempool", "download"])
49 50 self.checkpermission(
51 # forcerelay and relay permission added
52 # Legacy parameter interaction which set whitelistrelay to true
53 # if whitelistforcerelay is true
54 ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
55 ["forcerelay", "relay", "noban", "mempool", "download"])
56 57 # Let's make sure permissions are merged correctly
58 # For this, we need to use whitebind instead of bind
59 # by modifying the configuration file.
60 ip_port = "127.0.0.1:{}".format(p2p_port(1))
61 self.nodes[1].replace_in_config([("bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)])
62 # Explicitly bind the tor port to prevent collisions with the default tor port
63 append_config(self.nodes[1].datadir_path, [f"bind=127.0.0.1:{tor_port(self.nodes[1].index)}=onion"])
64 self.checkpermission(
65 ["-whitelist=noban@127.0.0.1"],
66 # Check parameter interaction forcerelay should activate relay
67 ["noban", "bloomfilter", "forcerelay", "relay", "download"])
68 self.nodes[1].replace_in_config([("whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")])
69 self.nodes[1].replace_in_config([(f"bind=127.0.0.1:{tor_port(self.nodes[1].index)}=onion", "")])
70 71 self.checkpermission(
72 # legacy whitelistrelay should be ignored
73 ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
74 ["noban", "mempool", "download"])
75 76 self.checkpermission(
77 # legacy whitelistforcerelay should be ignored
78 ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
79 ["noban", "mempool", "download"])
80 81 self.checkpermission(
82 # missing mempool permission to be considered legacy whitelisted
83 ["-whitelist=noban@127.0.0.1"],
84 ["noban", "download"])
85 86 self.checkpermission(
87 # all permission added
88 ["-whitelist=all@127.0.0.1"],
89 ["forcerelay", "noban", "mempool", "bloomfilter", "relay", "download", "addr"])
90 91 for flag, permissions in [(["-whitelist=noban,out@127.0.0.1"], ["noban", "download"]), (["-whitelist=noban@127.0.0.1"], [])]:
92 self.restart_node(0, flag)
93 self.connect_nodes(0, 1)
94 peerinfo = self.nodes[0].getpeerinfo()[0]
95 assert_equal(peerinfo['permissions'], permissions)
96 97 self.stop_node(1)
98 self.nodes[1].assert_start_raises_init_error(["-whitelist=in,out@127.0.0.1"], "Only direction was set, no permissions", match=ErrorMatch.PARTIAL_REGEX)
99 self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
100 self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
101 self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
102 self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1", "-bind=127.0.0.1", "-listen=0"], "Cannot set -bind or -whitebind together with -listen=0", match=ErrorMatch.PARTIAL_REGEX)
103 104 def check_tx_relay(self):
105 self.log.debug("Create a connection from a forcerelay peer that rebroadcasts raw txs")
106 # A test framework p2p connection is needed to send the raw transaction directly. If a full node was used, it could only
107 # rebroadcast via the inv-getdata mechanism. However, even for forcerelay connections, a full node would
108 # currently not request a txid that is already in the mempool.
109 self.restart_node(1, extra_args=["-whitelist=forcerelay@127.0.0.1"])
110 p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore())
111 112 self.log.debug("Send a tx from the wallet initially")
113 tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
114 txid = tx.txid_hex
115 116 self.log.debug("Wait until tx is in node[1]'s mempool")
117 p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
118 119 self.log.debug("Check that node[1] will send the tx to node[0] even though it is already in the mempool")
120 self.connect_nodes(1, 0)
121 with self.nodes[1].assert_debug_log(["Force relaying tx {} (wtxid={}) from peer=0".format(txid, tx.wtxid_hex)]):
122 p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
123 self.wait_until(lambda: txid in self.nodes[0].getrawmempool())
124 125 self.log.debug("Check that node[1] will not send an invalid tx to node[0]")
126 tx.vout[0].nValue += 1
127 # add dust to cause policy rejection but no disconnection
128 tx.vout.append(tx.vout[0])
129 tx.vout[-1].nValue = 0
130 txid = tx.txid_hex
131 # Send the transaction twice. The first time, it'll be rejected by ATMP because it conflicts
132 # with a mempool transaction. The second time, it'll be in the m_lazy_recent_rejects filter.
133 p2p_rebroadcast_wallet.send_txs_and_test(
134 [tx],
135 self.nodes[1],
136 success=False,
137 reject_reason='{} (wtxid={}) from peer=0 was not accepted: dust'.format(txid, tx.wtxid_hex)
138 )
139 140 p2p_rebroadcast_wallet.send_txs_and_test(
141 [tx],
142 self.nodes[1],
143 success=False,
144 reject_reason='Not relaying non-mempool transaction {} (wtxid={}) from forcerelay peer=0'.format(txid, tx.wtxid_hex)
145 )
146 147 def checkpermission(self, args, expectedPermissions):
148 self.restart_node(1, args)
149 self.connect_nodes(0, 1)
150 peerinfo = self.nodes[1].getpeerinfo()[0]
151 assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
152 for p in expectedPermissions:
153 if p not in peerinfo['permissions']:
154 raise AssertionError("Expected permissions %r is not granted." % p)
155 156 157 if __name__ == '__main__':
158 P2PPermissionsTests(__file__).main()
159