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 multiple RPC users."""7from test_framework.test_framework import BitcoinTestFramework8from test_framework.util import (9assert_equal,10str_to_b64str,11)13import http.client14import os15import platform16import urllib.parse17import subprocess18from random import SystemRandom19import string20import sys21from typing import Optional24def call_with_auth(node, user, password, method="getbestblockhash"):25url = urllib.parse.urlparse(node.url)26headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))}28conn = http.client.HTTPConnection(url.hostname, url.port)29conn.connect()30conn.request('POST', '/', f'{{"method": "{method}"}}', headers)31resp = conn.getresponse()32conn.close()33return resp36class HTTPBasicsTest(BitcoinTestFramework):37def set_test_params(self):38self.num_nodes = 240def conf_setup(self):41#Append rpcauth to bitcoin.conf before initialization42self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="43rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"45self.rpcuser = "rpcuser💻"46self.rpcpassword = "rpcpassword🔑"48gen_rpcauth = self.config["environment"]["RPCAUTH"]50# Generate RPCAUTH with specified password51self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="52p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, text=True)53lines = p.stdout.read().splitlines()54rpcauth2 = lines[1]56# Generate RPCAUTH without specifying password57self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))58p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, text=True)59lines = p.stdout.read().splitlines()60rpcauth3 = lines[1]61self.password = lines[3]63self.stop_nodes()64with open(self.nodes[0].datadir_path / "bitcoin.conf", "a") as f:65f.write(rpcauth + "\n")66f.write(rpcauth2 + "\n")67f.write(rpcauth3 + "\n")68with open(self.nodes[1].datadir_path / "bitcoin.conf", "a") as f:69f.write("rpcuser={}\n".format(self.rpcuser))70f.write("rpcpassword={}\n".format(self.rpcpassword))71self.start_nodes()73def test_auth(self, node, user, password):74self.log.info('Correct...')75assert_equal(200, call_with_auth(node, user, password).status)77self.log.info('Wrong...')78assert_equal(401, call_with_auth(node, user, password + 'wrong').status)80self.log.info('Wrong...')81assert_equal(401, call_with_auth(node, user + 'wrong', password).status)83self.log.info('Wrong...')84assert_equal(401, call_with_auth(node, user + 'wrong', password + 'wrong').status)86def test_rpccookieperms(self):87p = {"owner": 0o600, "group": 0o640, "all": 0o644}89if platform.system() == 'Windows':90self.log.info(f"Skip cookie file permissions checks as OS detected as: {platform.system()=}")91return93self.log.info('Check cookie file permissions can be set using -rpccookieperms')95cookie_file_path = self.nodes[1].chain_path / '.cookie'96PERM_BITS_UMASK = 0o77798def test_perm(perm: Optional[str]):99if not perm:100perm = 'owner'101self.restart_node(1)102else:103self.restart_node(1, extra_args=[f"-rpccookieperms={perm}"])105file_stat = os.stat(cookie_file_path)106actual_perms = file_stat.st_mode & PERM_BITS_UMASK107expected_perms = p[perm]108assert_equal(expected_perms, actual_perms)110# Remove any leftover rpc{user|password} config options from previous tests111self.stop_node(1)112self.nodes[1].replace_in_config([("rpcuser", "#rpcuser"), ("rpcpassword", "#rpcpassword")])114self.log.info('Check default cookie permission')115test_perm(None)117self.log.info('Check custom cookie permissions')118for perm in ["owner", "group", "all"]:119test_perm(perm)121def test_norpccookiefile(self, node0_cookie_path):122assert self.nodes[0].is_node_stopped(), "We expect previous test to stopped the node"123assert not node0_cookie_path.exists()125self.log.info('Starting with -norpccookiefile')126# Start, but don't wait for RPC connection as TestNode.wait_for_rpc_connection() requires the cookie.127with self.nodes[0].busy_wait_for_debug_log([b'init message: Done loading']):128self.nodes[0].start(extra_args=["-norpccookiefile"])129assert not node0_cookie_path.exists()131self.log.info('Testing user/password authentication still works without cookie file')132assert_equal(200, call_with_auth(self.nodes[0], "rt", self.rtpassword).status)133# After confirming that we could log in, check that cookie file does not exist.134assert not node0_cookie_path.exists()136# Need to shut down in slightly unorthodox way since cookie auth can't be used137assert_equal(200, call_with_auth(self.nodes[0], "rt", self.rtpassword, method="stop").status)138self.nodes[0].wait_until_stopped()140def run_test(self):141self.conf_setup()142self.log.info('Check correctness of the rpcauth config option')143url = urllib.parse.urlparse(self.nodes[0].url)145self.test_auth(self.nodes[0], url.username, url.password)146self.test_auth(self.nodes[0], 'rt', self.rtpassword)147self.test_auth(self.nodes[0], 'rt2', self.rt2password)148self.test_auth(self.nodes[0], self.user, self.password)150self.log.info('Check correctness of the rpcuser/rpcpassword config options')151url = urllib.parse.urlparse(self.nodes[1].url)153self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword)155init_error = 'Error: Unable to start HTTP server. See debug log for details.'157self.log.info('Check -rpcauth are validated')158self.log.info('Empty -rpcauth are treated as error')159self.stop_node(0)160self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth'])161self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth='])162self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=""'])163self.log.info('Check malformed -rpcauth')164self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo'])165self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo:bar'])166self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo:bar:baz'])167self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo$bar:baz'])168self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo$bar$baz'])170self.log.info('Check interactions between blank and non-blank rpcauth')171# pw = bitcoin172rpcauth_user1 = '-rpcauth=user1:6dd184e5e69271fdd69103464630014f$eb3d7ce67c4d1ff3564270519b03b636c0291012692a5fa3dd1d2075daedd07b'173rpcauth_user2 = '-rpcauth=user2:57b2f77c919eece63cfa46c2f06e46ae$266b63902f99f97eeaab882d4a87f8667ab84435c3799f2ce042ef5a994d620b'174self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=[rpcauth_user1, rpcauth_user2, '-rpcauth='])175self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=[rpcauth_user1, '-rpcauth=', rpcauth_user2])176self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=', rpcauth_user1, rpcauth_user2])178self.log.info('Check -norpcauth disables previous -rpcauth params')179self.restart_node(0, extra_args=[rpcauth_user1, rpcauth_user2, '-norpcauth'])180assert_equal(401, call_with_auth(self.nodes[0], 'user1', 'bitcoin').status)181assert_equal(401, call_with_auth(self.nodes[0], 'rt', self.rtpassword).status)182self.stop_node(0)184self.log.info('Check that failure to write cookie file will abort the node gracefully')185cookie_path = self.nodes[0].chain_path / ".cookie"186cookie_path_tmp = self.nodes[0].chain_path / ".cookie.tmp"187cookie_path_tmp.mkdir()188self.nodes[0].assert_start_raises_init_error(expected_msg=init_error)189cookie_path_tmp.rmdir()190assert not cookie_path.exists()191self.restart_node(0)192assert cookie_path.exists()193self.stop_node(0)195self.test_rpccookieperms()197self.test_norpccookiefile(cookie_path)199if __name__ == '__main__':200HTTPBasicsTest(__file__).main()
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.