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) 2017-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 that the wallet can send and receive using all combinations of address types.7There are 5 nodes-under-test:8- node0 uses legacy addresses9- node1 uses p2sh/segwit addresses10- node2 uses p2sh/segwit addresses and bech32 addresses for change11- node3 uses bech32 addresses12- node4 uses a p2sh/segwit addresses for change14node5 exists to generate new blocks.16## Multisig address test18Test that adding a multisig address with:19- an uncompressed pubkey always gives a legacy address20- only compressed pubkeys gives the an `-addresstype` address22## Sending to address types test24A series of tests, iterating over node0-node4. In each iteration of the test, one node sends:25- 10/101th of its balance to itself (using getrawchangeaddress for single key addresses)26- 20/101th to the next node27- 30/101th to the node after that28- 40/101th to the remaining node29- 1/101th remains as fee+change31Iterate over each node for single key addresses, and then over each node for32multisig addresses.34Repeat test, but with explicit address_type parameters passed to getnewaddress35and getrawchangeaddress:36- node0 and node3 send to p2sh.37- node1 sends to bech32.38- node2 sends to legacy.40As every node sends coins after receiving, this also41verifies that spending coins sent to all these address types works.43## Change type test45Test that the nodes generate the correct change address type:46- node0 always uses a legacy change address.47- node1 uses a bech32 addresses for change if any destination address is bech32.48- node2 always uses a bech32 address for change49- node3 always uses a bech32 address for change50- node4 always uses p2sh/segwit output for change.51"""53from decimal import Decimal54import itertools56from test_framework.blocktools import COINBASE_MATURITY57from test_framework.test_framework import BitcoinTestFramework58from test_framework.descriptors import (59descsum_create,60descsum_check,61)62from test_framework.util import (63assert_equal,64assert_greater_than,65assert_raises_rpc_error,66)68class AddressTypeTest(BitcoinTestFramework):69def set_test_params(self):70self.num_nodes = 671self.extra_args = [72["-addresstype=legacy"],73["-addresstype=p2sh-segwit"],74["-addresstype=p2sh-segwit", "-changetype=bech32"],75["-addresstype=bech32"],76["-changetype=p2sh-segwit"],77[],78]79# whitelist peers to speed up tx relay / mempool sync80self.noban_tx_relay = True82def skip_test_if_missing_module(self):83self.skip_if_no_wallet()85def setup_network(self):86self.setup_nodes()88# Fully mesh-connect nodes for faster mempool sync89for i, j in itertools.product(range(self.num_nodes), repeat=2):90if i > j:91self.connect_nodes(i, j)92self.sync_all()94def get_balances(self, key='trusted'):95"""Return a list of balances."""96return [self.nodes[i].getbalances()['mine'][key] for i in range(4)]98def test_address(self, node, address, multisig, typ):99"""Run sanity checks on an address."""100info = self.nodes[node].getaddressinfo(address)101assert self.nodes[node].validateaddress(address)['isvalid']102assert_equal(info.get('solvable'), True)104if not multisig and typ == 'legacy':105# P2PKH106assert not info['isscript']107assert not info['iswitness']108assert 'pubkey' in info109elif not multisig and typ == 'p2sh-segwit':110# P2SH-P2WPKH111assert info['isscript']112assert not info['iswitness']113assert_equal(info['script'], 'witness_v0_keyhash')114assert 'pubkey' in info115elif not multisig and typ == 'bech32':116# P2WPKH117assert not info['isscript']118assert info['iswitness']119assert_equal(info['witness_version'], 0)120assert_equal(len(info['witness_program']), 40)121assert 'pubkey' in info122elif not multisig and typ == "bech32m":123# P2TR single sig124assert info["isscript"]125assert info["iswitness"]126assert_equal(info["witness_version"], 1)127assert_equal(len(info["witness_program"]), 64)128elif typ == 'legacy':129# P2SH-multisig130assert info['isscript']131assert_equal(info['script'], 'multisig')132assert not info['iswitness']133assert 'pubkeys' in info134elif typ == 'p2sh-segwit':135# P2SH-P2WSH-multisig136assert info['isscript']137assert_equal(info['script'], 'witness_v0_scripthash')138assert not info['iswitness']139assert info['embedded']['isscript']140assert_equal(info['embedded']['script'], 'multisig')141assert info['embedded']['iswitness']142assert_equal(info['embedded']['witness_version'], 0)143assert_equal(len(info['embedded']['witness_program']), 64)144assert 'pubkeys' in info['embedded']145elif typ == 'bech32':146# P2WSH-multisig147assert info['isscript']148assert_equal(info['script'], 'multisig')149assert info['iswitness']150assert_equal(info['witness_version'], 0)151assert_equal(len(info['witness_program']), 64)152assert 'pubkeys' in info153else:154# Unknown type155assert False157def test_desc(self, node, address, multisig, typ, utxo):158"""Run sanity checks on a descriptor reported by getaddressinfo."""159info = self.nodes[node].getaddressinfo(address)160assert 'desc' in info161assert_equal(info['desc'], utxo['desc'])162assert self.nodes[node].validateaddress(address)['isvalid']164# Use a ridiculously roundabout way to find the key origin info through165# the PSBT logic. However, this does test consistency between the PSBT reported166# fingerprints/paths and the descriptor logic.167psbt = self.nodes[node].createpsbt([{'txid':utxo['txid'], 'vout':utxo['vout']}],[{address:0.00010000}])168psbt = self.nodes[node].walletprocesspsbt(psbt, False, "ALL", True)169decode = self.nodes[node].decodepsbt(psbt['psbt'])170key_descs = {}171for deriv in decode['inputs'][0]['bip32_derivs']:172assert_equal(len(deriv['master_fingerprint']), 8)173assert_equal(deriv['path'][0], 'm')174key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + deriv['path'][1:].replace("'","h") + ']' + deriv['pubkey']176# Verify the descriptor checksum against the Python implementation177assert descsum_check(info['desc'])178# Verify that stripping the checksum and recreating it using Python roundtrips179assert_equal(info['desc'], descsum_create(info['desc'][:-9]))180# Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips181assert_equal(info['desc'], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor'])182assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum'])183# Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips184assert_equal(info['desc'], self.nodes[0].getdescriptorinfo(info['desc'])['descriptor'])185assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum'])187if not multisig and typ == 'legacy':188# P2PKH189assert_equal(info['desc'], descsum_create("pkh(%s)" % key_descs[info['pubkey']]))190elif not multisig and typ == 'p2sh-segwit':191# P2SH-P2WPKH192assert_equal(info['desc'], descsum_create("sh(wpkh(%s))" % key_descs[info['pubkey']]))193elif not multisig and typ == 'bech32':194# P2WPKH195assert_equal(info['desc'], descsum_create("wpkh(%s)" % key_descs[info['pubkey']]))196elif typ == 'legacy':197# P2SH-multisig198assert_equal(info['desc'], descsum_create("sh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]])))199elif typ == 'p2sh-segwit':200# P2SH-P2WSH-multisig201assert_equal(info['desc'], descsum_create("sh(wsh(multi(2,%s,%s)))" % (key_descs[info['embedded']['pubkeys'][0]], key_descs[info['embedded']['pubkeys'][1]])))202elif typ == 'bech32':203# P2WSH-multisig204assert_equal(info['desc'], descsum_create("wsh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]])))205else:206# Unknown type207assert False209def test_change_output_type(self, node_sender, destinations, expected_type):210txid = self.nodes[node_sender].sendmany(dummy="", amounts=dict.fromkeys(destinations, 0.001))211tx = self.nodes[node_sender].gettransaction(txid=txid, verbose=True)['decoded']213# Make sure the transaction has change:214assert_equal(len(tx["vout"]), len(destinations) + 1)216# Make sure the destinations are included, and remove them:217output_addresses = [vout['scriptPubKey']['address'] for vout in tx["vout"]]218change_addresses = [d for d in output_addresses if d not in destinations]219assert_equal(len(change_addresses), 1)221self.log.debug("Check if change address " + change_addresses[0] + " is " + expected_type)222self.test_address(node_sender, change_addresses[0], multisig=False, typ=expected_type)224def run_test(self):225# Mine 101 blocks on node5 to bring nodes out of IBD and make sure that226# no coinbases are maturing for the nodes-under-test during the test227self.generate(self.nodes[5], COINBASE_MATURITY + 1)229compressed_1 = "0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"230compressed_2 = "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"232do_multisigs = [False]234for explicit_type, multisig, from_node in itertools.product([False, True], do_multisigs, range(4)):235address_type = None236if explicit_type and not multisig:237if from_node == 1:238address_type = 'bech32'239elif from_node == 0 or from_node == 3:240address_type = 'p2sh-segwit'241else:242address_type = 'legacy'243self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))244old_balances = self.get_balances()245self.log.debug("Old balances are {}".format(old_balances))246to_send = (old_balances[from_node] / (COINBASE_MATURITY + 1)).quantize(Decimal("0.00000001"))247sends = {}248addresses = {}250self.log.debug("Prepare sends")
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.