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) 2014-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"""Helpful routines for regression testing."""7from base64 import b64encode8from copy import copy9from decimal import Decimal10from subprocess import CalledProcessError11import hashlib12import inspect13import json14import logging15import os16import pathlib17import platform18import random19import re20import shlex21import time22import types24from .descriptors import descsum_create25from collections.abc import Callable26from typing import Optional, Union28SATOSHI_PRECISION = Decimal('0.00000001')30logger = logging.getLogger("TestFramework.utils")32class JSONRPCException(Exception):33def __init__(self, rpc_error, http_status=None):34self.error = rpc_error35self.http_status = http_status37# throw KeyError if any required fields are missing38copied_error = copy(rpc_error)39message = copied_error.pop("message")40code = copied_error.pop("code")41extra = f'{copied_error}' if copied_error else ''42super().__init__(f"{message} ({code}) {extra} [http_status={http_status}]")44# Assert functions45##################48def assert_approx(v, vexp, vspan=0.00001):49"""Assert that `v` is within `vspan` of `vexp`"""50if isinstance(v, Decimal) or isinstance(vexp, Decimal):51v=Decimal(v)52vexp=Decimal(vexp)53vspan=Decimal(vspan)54if v < vexp - vspan:55raise AssertionError("%s < [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))56if v > vexp + vspan:57raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))60def assert_fee_amount(fee, tx_size, feerate_BTC_kvB):61"""Assert the fee is in range."""62assert isinstance(tx_size, int)63target_fee = get_fee(tx_size, feerate_BTC_kvB)64if fee < target_fee:65raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))66# allow the wallet's estimation to be at most 2 bytes off67high_fee = get_fee(tx_size + 2, feerate_BTC_kvB)68if fee > high_fee:69raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))72def summarise_dict_differences(thing1, thing2):73if not isinstance(thing1, dict) or not isinstance(thing2, dict):74return thing1, thing275d1, d2 = {}, {}76for k in sorted(thing1.keys()):77if k not in thing2:78d1[k] = thing1[k]79elif thing1[k] != thing2[k]:80d1[k], d2[k] = summarise_dict_differences(thing1[k], thing2[k])81for k in sorted(thing2.keys()):82if k not in thing1:83d2[k] = thing2[k]84return d1, d286def assert_equal(thing1, thing2, *args):87if thing1 != thing2 and not args and isinstance(thing1, dict) and isinstance(thing2, dict):88d1,d2 = summarise_dict_differences(thing1, thing2)89if d1 != thing1 or d2 != thing2:90raise AssertionError(f"not({thing1!s} == {thing2!s})\n in particular not({d1!s} == {d2!s})")91else:92raise AssertionError(f"not({thing1!s} == {thing2!s})")93if thing1 != thing2 or any(thing1 != arg for arg in args):94raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))96def assert_not_equal(thing1, thing2, *, error_message=""):97if thing1 == thing2:98raise AssertionError(f"Both values are {thing1}{f', {error_message}' if error_message else ''}")101def assert_greater_than(thing1, thing2):102if thing1 <= thing2:103raise AssertionError("%s <= %s" % (str(thing1), str(thing2)))106def assert_greater_than_or_equal(thing1, thing2):107if thing1 < thing2:108raise AssertionError("%s < %s" % (str(thing1), str(thing2)))111def assert_raises(exc, fun, *args, **kwds):112assert_raises_message(exc, None, fun, *args, **kwds)115def assert_raises_message(exc, message, fun, *args, **kwds):116try:117fun(*args, **kwds)118except JSONRPCException:119raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")120except exc as e:121if message is not None and message not in str(e):122raise AssertionError("Expected substring not found in exception:\n"123f"substring: '{message}'\nexception: {e!r}.")124except Exception as e:125raise AssertionError("Unexpected exception raised: " + type(e).__name__)126else:127raise AssertionError("No exception raised")130def assert_raises_process_error(returncode: int, output: str, fun: Callable, *args, **kwds):131"""Execute a process and asserts the process return code and output.133Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError134and verifies that the return code and output are as expected. Throws AssertionError if135no CalledProcessError was raised or if the return code and output are not as expected.137Args:138returncode: the process return code.139output: [a substring of] the process output.140fun: the function to call. This should execute a process.141args*: positional arguments for the function.142kwds**: named arguments for the function.143"""144try:145fun(*args, **kwds)146except CalledProcessError as e:147if returncode != e.returncode:148raise AssertionError("Unexpected returncode %i" % e.returncode)149if output not in e.output:150raise AssertionError(f"Expected substring not found in: {e.output!r}")151else:152raise AssertionError("No exception raised")155def assert_raises_rpc_error(code: Optional[int], message: Optional[str], fun: Callable, *args, **kwds):156"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.158Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException159and verifies that the error code and message are as expected. Throws AssertionError if160no JSONRPCException was raised or if the error code/message are not as expected.162Args:163code: the error code returned by the RPC call (defined in src/rpc/protocol.h).164Set to None if checking the error code is not required.165message: [a substring of] the error string returned by the RPC call.166Set to None if checking the error string is not required.167fun: the function to call. This should be the name of an RPC.168args*: positional arguments for the function.169kwds**: named arguments for the function.170"""171assert try_rpc(code, message, fun, *args, **kwds), "No exception raised"174def try_rpc(code, message, fun, *args, **kwds):175"""Tries to run an rpc command.177Test against error code and message if the rpc fails.178Returns whether a JSONRPCException was raised."""179try:180fun(*args, **kwds)181except JSONRPCException as e:182# JSONRPCException was thrown as expected. Check the message and code values are correct.183if (message is not None) and (message not in e.error['message']):184raise AssertionError(185"Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(186message, e.error['message']))187if (code is not None) and (code != e.error["code"]):188raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])189return True190except Exception as e:191raise AssertionError("Unexpected exception raised: " + type(e).__name__)192else:193return False196def assert_is_hex_string(string):197try:198int(string, 16)199except Exception as e:200raise AssertionError("Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))203def assert_is_hash_string(string, length=64):204if not isinstance(string, str):205raise AssertionError("Expected a string, got type %r" % type(string))206elif length and len(string) != length:207raise AssertionError("String of length %d expected; got %d" % (length, len(string)))208elif not re.match('[abcdef0-9]+$', string):209raise AssertionError("String %r contains invalid characters for a hash." % string)212def assert_array_result(object_array, to_match, expected, should_not_find=False):213"""214Pass in array of JSON objects, a dictionary with key/value pairs215to match against, and another dictionary with expected key/value216pairs.217If the should_not_find flag is true, to_match should not be found218in object_array219"""220if should_not_find:221assert_equal(expected, {})222num_matched = 0223for item in object_array:224all_match = True225for key, value in to_match.items():226if item[key] != value:227all_match = False228if not all_match:229continue230elif should_not_find:231num_matched = num_matched + 1232for key, value in expected.items():233if item[key] != value:234raise AssertionError("%s : expected %s=%s" % (str(item), str(key), str(value)))235num_matched = num_matched + 1236if num_matched == 0 and not should_not_find:237raise AssertionError("No objects matched %s" % (str(to_match)))238if num_matched > 0 and should_not_find:239raise AssertionError("Objects were found %s" % (str(to_match)))242# Utility functions243###################246def check_json_precision():247"""Make sure json library being used does not lose precision converting BTC values"""248n = Decimal("20000000.00000003")249satoshis = int(json.loads(json.dumps(float(n))) * 1.0e8)250if satoshis != 2000000000000003:
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.