Skip to content

Commit da8b109

Browse files
committed
qa: Add feature_onion.py
1 parent a855538 commit da8b109

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

test/functional/feature_onion.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2020 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 bitcoind with 'onion'-tagged peers.
6+
7+
Tested inbound and outbound peers.
8+
"""
9+
10+
import os
11+
12+
from test_framework.p2p import P2PInterface
13+
from test_framework.socks5 import Socks5Configuration, Socks5Server
14+
from test_framework.test_framework import BitcoinTestFramework
15+
from test_framework.util import (
16+
PORT_MIN,
17+
PORT_RANGE,
18+
assert_equal,
19+
)
20+
21+
22+
class OnionTest(BitcoinTestFramework):
23+
def set_test_params(self):
24+
self.num_nodes = 2
25+
self.setup_clean_chain = True
26+
27+
def setup_network(self):
28+
port = PORT_MIN + 2 * PORT_RANGE + (os.getpid() % 1000)
29+
self.bind_onion = '127.0.0.1:{}'.format(port)
30+
31+
proxy_conf = Socks5Configuration()
32+
port += 1
33+
proxy_conf.addr = ('127.0.0.1', port)
34+
proxy_conf.unauth = True
35+
Socks5Server(proxy_conf).start()
36+
37+
args = [
38+
['-bind={}=onion'.format(self.bind_onion), '-onion={0[0]}:{0[1]}'.format(proxy_conf.addr)],
39+
[],
40+
]
41+
self.add_nodes(self.num_nodes, extra_args=args)
42+
self.start_nodes()
43+
44+
def reset_connections(self):
45+
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
46+
self.nodes[0].setnetworkactive(state=False)
47+
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
48+
self.wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0)
49+
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
50+
self.nodes[0].setnetworkactive(state=True)
51+
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
52+
53+
def test_connections(self, in_clearnet=0, in_onion=0, out_onion=0):
54+
self.reset_connections()
55+
56+
for i in range(in_clearnet):
57+
self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
58+
self.wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == in_clearnet)
59+
60+
if in_onion > 0:
61+
if in_onion > 1:
62+
self.log.warning("Maximum 1 inbound 'onion'-tagged connection is supported.")
63+
in_onion = 1
64+
self.nodes[1].addnode(self.bind_onion, 'onetry')
65+
self.wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == in_clearnet + in_onion)
66+
67+
# Create a connection to a dummy Socks5 server just before
68+
# the getnetworkinfo RPC, as such connections are short-lived.
69+
if out_onion > 0:
70+
if out_onion > 1:
71+
self.log.warning("Maximum 1 outbound onion connection is supported.")
72+
out_onion = 1
73+
self.nodes[0].addnode('bitcoinostk4e4re.onion:18444', 'onetry')
74+
75+
network_info = self.nodes[0].getnetworkinfo()
76+
assert_equal(network_info["connections"], in_clearnet + in_onion + out_onion)
77+
assert_equal(network_info["connections_in"], in_clearnet + in_onion)
78+
assert_equal(network_info["connections_out"], out_onion)
79+
assert_equal(network_info["connections_onion_only"], in_clearnet == 0 and (in_onion + out_onion) > 0)
80+
81+
def run_test(self):
82+
self.log.info("Checking a node without any peers...")
83+
self.test_connections(in_clearnet=0, in_onion=0, out_onion=0)
84+
85+
self.log.info("Checking onion only connections...")
86+
self.test_connections(in_clearnet=0, in_onion=0, out_onion=1)
87+
self.test_connections(in_clearnet=0, in_onion=1, out_onion=0)
88+
self.test_connections(in_clearnet=0, in_onion=1, out_onion=1)
89+
90+
self.log.info("Checking clearnet only connections...")
91+
self.test_connections(in_clearnet=1, in_onion=0, out_onion=0)
92+
self.test_connections(in_clearnet=2, in_onion=0, out_onion=0)
93+
94+
self.log.info("Checking mixed type connections...")
95+
self.test_connections(in_clearnet=1, in_onion=0, out_onion=1)
96+
self.test_connections(in_clearnet=1, in_onion=1, out_onion=0)
97+
self.test_connections(in_clearnet=1, in_onion=1, out_onion=1)
98+
self.test_connections(in_clearnet=2, in_onion=0, out_onion=1)
99+
self.test_connections(in_clearnet=2, in_onion=1, out_onion=0)
100+
self.test_connections(in_clearnet=2, in_onion=1, out_onion=1)
101+
102+
103+
if __name__ == '__main__':
104+
OnionTest().main()

test/functional/test_framework/socks5.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import socket
88
import threading
9+
import time
910
import queue
1011
import logging
1112

@@ -116,6 +117,8 @@ def handle(self):
116117
cmdin = Socks5Command(cmd, atyp, addr, port, username, password)
117118
self.serv.queue.put(cmdin)
118119
logger.info('Proxy: %s', cmdin)
120+
# Some tests, e.g., feature_onion.py, require prolonged lifetime of a connection.
121+
time.sleep(0.25)
119122
# Fall through to disconnect
120123
except Exception as e:
121124
logger.exception("socks5 request handling failed.")

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225
'feature_dersig.py',
226226
'feature_cltv.py',
227227
'rpc_uptime.py',
228+
'feature_onion.py',
228229
'wallet_resendwallettransactions.py',
229230
'wallet_fallbackfee.py',
230231
'rpc_dumptxoutset.py',

0 commit comments

Comments
 (0)