Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2015-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test bitcoind with different proxy configuration.7Test plan:8- Start bitcoind's with different proxy configurations9- Use addnode to initiate connections10- Verify that proxies are connected to, and the right connection command is given11- Proxy configurations to test on bitcoind side:12- `-proxy` (proxy everything)13- `-onion` (proxy just onions)14- `-proxyrandomize` Circuit randomization15- `-cjdnsreachable`16- Proxy configurations to test on proxy side,17- support no authentication (other proxy)18- support no authentication + user/pass authentication (Tor)19- proxy on IPv620- proxy over unix domain sockets22- Create various proxies (as threads)23- Create nodes that connect to them24- Manipulate the peer connections using addnode (onetry) and observe effects25- Test the getpeerinfo `network` field for the peer27addnode connect to IPv428addnode connect to IPv629addnode connect to onion30addnode connect to generic DNS name31addnode connect to a CJDNS address33- Test getnetworkinfo for each node35- Test passing invalid -proxy36- Test passing invalid -onion37- Test passing invalid -i2psam38- Test passing -onlynet=onion without -proxy or -onion39- Test passing -onlynet=onion with -onion=0 and with -noonion40- Test passing unknown -onlynet41"""43import os44import socket45import tempfile47from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType48from test_framework.test_framework import BitcoinTestFramework49from test_framework.util import assert_equal50from test_framework.netutil import test_ipv6_local, test_unix_socket52# Networks returned by RPC getpeerinfo.53NET_UNROUTABLE = "not_publicly_routable"54NET_IPV4 = "ipv4"55NET_IPV6 = "ipv6"56NET_ONION = "onion"57NET_I2P = "i2p"58NET_CJDNS = "cjdns"60# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()61NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS})63# Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit64socket_path = tempfile.NamedTemporaryFile().name66class ProxyTest(BitcoinTestFramework):67def set_test_params(self):68self.num_nodes = 769self.setup_clean_chain = True71def setup_nodes(self):72self.have_ipv6 = test_ipv6_local()73self.have_unix_sockets = test_unix_socket()74# Create two proxies on different ports.75# Use port=0 to let the OS assign available ports, avoiding76# "address already in use" errors from concurrent tests.77# ... one unauthenticated78self.conf1 = Socks5Configuration()79self.conf1.addr = ('127.0.0.1', 0)80self.conf1.unauth = True81self.conf1.auth = False82# ... one supporting authenticated and unauthenticated (Tor)83self.conf2 = Socks5Configuration()84self.conf2.addr = ('127.0.0.1', 0)85self.conf2.unauth = True86self.conf2.auth = True87if self.have_ipv6:88# ... one on IPv6 with similar configuration89self.conf3 = Socks5Configuration()90self.conf3.af = socket.AF_INET691self.conf3.addr = ('::1', 0)92self.conf3.unauth = True93self.conf3.auth = True94else:95self.log.warning("Testing without local IPv6 support")97if self.have_unix_sockets:98self.conf4 = Socks5Configuration()99self.conf4.af = socket.AF_UNIX100self.conf4.addr = socket_path101self.conf4.unauth = True102self.conf4.auth = True103else:104self.log.warning("Testing without local unix domain sockets support")106self.serv1 = Socks5Server(self.conf1)107self.serv1.start()108self.serv2 = Socks5Server(self.conf2)109self.serv2.start()110if self.have_ipv6:111self.serv3 = Socks5Server(self.conf3)112self.serv3.start()113if self.have_unix_sockets:114self.serv4 = Socks5Server(self.conf4)115self.serv4.start()117# We will not try to connect to this.118self.i2p_sam = ('127.0.0.1', 7656)120# Note: proxies are not used to connect to local nodes. This is because the proxy to121# use is based on CService.GetNetwork(), which returns NET_UNROUTABLE for localhost.122args = [123['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1'],124['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}',f'-onion={self.conf2.addr[0]}:{self.conf2.addr[1]}',125f'-i2psam={self.i2p_sam[0]}:{self.i2p_sam[1]}', '-i2pacceptincoming=0', '-proxyrandomize=0'],126['-listen', f'-proxy={self.conf2.addr[0]}:{self.conf2.addr[1]}','-proxyrandomize=1'],127[],128['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1',129'-cjdnsreachable'],130[],131[]132]133if self.have_ipv6:134args[3] = ['-listen', f'-proxy=[{self.conf3.addr[0]}]:{self.conf3.addr[1]}','-proxyrandomize=0', '-noonion']135if self.have_unix_sockets:136args[5] = ['-listen', f'-proxy=unix:{socket_path}']137args[6] = ['-listen', f'-onion=unix:{socket_path}']138# This test launches many nodes; disable prevout prefetching so we don't spin up a139# thread pool for each one.140args = [a + ['-prevoutfetchthreads=0'] for a in args]141self.add_nodes(self.num_nodes, extra_args=args)142self.start_nodes()144def network_test(self, node, addr, network):145for peer in node.getpeerinfo():146if peer["addr"] == addr:147assert_equal(peer["network"], network)149def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):150rv = []151addr = "15.61.23.23:1234"152self.log.debug(f"Test: outgoing IPv4 connection through node {node.index} for address {addr}")153# v2transport=False is used to avoid reconnections with v1 being scheduled. These could interfere with later checks.154node.addnode(addr, "onetry", v2transport=False)155cmd = proxies[0].queue.get()156assert isinstance(cmd, Socks5Command)157# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6158assert_equal(cmd.atyp, AddressType.DOMAINNAME)159assert_equal(cmd.addr, b"15.61.23.23")160assert_equal(cmd.port, 1234)161if not auth:162assert_equal(cmd.username, None)163assert_equal(cmd.password, None)164rv.append(cmd)165self.network_test(node, addr, network=NET_IPV4)167if self.have_ipv6:168addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"169self.log.debug(f"Test: outgoing IPv6 connection through node {node.index} for address {addr}")170node.addnode(addr, "onetry", v2transport=False)171cmd = proxies[1].queue.get()172assert isinstance(cmd, Socks5Command)173# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6174assert_equal(cmd.atyp, AddressType.DOMAINNAME)175assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534")176assert_equal(cmd.port, 5443)177if not auth:178assert_equal(cmd.username, None)179assert_equal(cmd.password, None)180rv.append(cmd)181self.network_test(node, addr, network=NET_IPV6)183if test_onion:184addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"185self.log.debug(f"Test: outgoing onion connection through node {node.index} for address {addr}")186node.addnode(addr, "onetry", v2transport=False)187cmd = proxies[2].queue.get()188assert isinstance(cmd, Socks5Command)189assert_equal(cmd.atyp, AddressType.DOMAINNAME)190assert_equal(cmd.addr, b"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")191assert_equal(cmd.port, 8333)192if not auth:193assert_equal(cmd.username, None)194assert_equal(cmd.password, None)195rv.append(cmd)196self.network_test(node, addr, network=NET_ONION)198if test_cjdns:199addr = "[fc00:1:2:3:4:5:6:7]:8888"200self.log.debug(f"Test: outgoing CJDNS connection through node {node.index} for address {addr}")201node.addnode(addr, "onetry", v2transport=False)202cmd = proxies[1].queue.get()203assert isinstance(cmd, Socks5Command)204assert_equal(cmd.atyp, AddressType.DOMAINNAME)205assert_equal(cmd.addr, b"fc00:1:2:3:4:5:6:7")206assert_equal(cmd.port, 8888)207if not auth:208assert_equal(cmd.username, None)209assert_equal(cmd.password, None)210rv.append(cmd)211self.network_test(node, addr, network=NET_CJDNS)213addr = "node.noumenon:8333"214self.log.debug(f"Test: outgoing DNS name connection through node {node.index} for address {addr}")215node.addnode(addr, "onetry", v2transport=False)216cmd = proxies[3].queue.get()217assert isinstance(cmd, Socks5Command)218assert_equal(cmd.atyp, AddressType.DOMAINNAME)219assert_equal(cmd.addr, b"node.noumenon")220assert_equal(cmd.port, 8333)221if not auth:222assert_equal(cmd.username, None)223assert_equal(cmd.password, None)224rv.append(cmd)225self.network_test(node, addr, network=NET_UNROUTABLE)227return rv229def run_test(self):230# basic -proxy231self.node_test(self.nodes[0],232proxies=[self.serv1, self.serv1, self.serv1, self.serv1],233auth=False, test_onion=True, test_cjdns=False)235# -proxy plus -onion236self.node_test(self.nodes[1],237proxies=[self.serv1, self.serv1, self.serv2, self.serv1],238auth=False, test_onion=True, test_cjdns=False)240# -proxy plus -onion, -proxyrandomize241rv = self.node_test(self.nodes[2],242proxies=[self.serv2, self.serv2, self.serv2, self.serv2],243auth=True, test_onion=True, test_cjdns=False)244# Check that credentials as used for -proxyrandomize connections are unique245credentials = set((x.username,x.password) for x in rv)246assert_equal(len(credentials), len(rv))248if self.have_ipv6:249# proxy on IPv6 localhost250self.node_test(self.nodes[3],
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.