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) 2023-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 wallet change address selection"""7import re9from test_framework.blocktools import COINBASE_MATURITY10from test_framework.test_framework import BitcoinTestFramework11from test_framework.util import (12assert_equal,13)16class WalletChangeAddressTest(BitcoinTestFramework):17def set_test_params(self):18self.setup_clean_chain = True19self.num_nodes = 320# discardfee is used to make change outputs less likely in the change_pos test21self.extra_args = [22[],23["-discardfee=1"],24["-avoidpartialspends", "-discardfee=1"]25]27def skip_test_if_missing_module(self):28self.skip_if_no_wallet()30def assert_change_index(self, node, tx, index):31change_index = None32for vout in tx["vout"]:33info = node.getaddressinfo(vout["scriptPubKey"]["address"])34if (info["ismine"] and info["ischange"]):35change_index = int(re.findall(r'\d+', info["hdkeypath"])[-1])36break37assert_equal(change_index, index)39def assert_change_pos(self, wallet, tx, pos):40change_pos = None41for index, output in enumerate(tx["vout"]):42info = wallet.getaddressinfo(output["scriptPubKey"]["address"])43if (info["ismine"] and info["ischange"]):44change_pos = index45break46assert_equal(change_pos, pos)48def run_test(self):49self.log.info("Setting up")50# Mine some coins51self.generate(self.nodes[0], COINBASE_MATURITY + 1)53# Get some addresses from the two nodes54addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]55addr2 = [self.nodes[2].getnewaddress() for _ in range(3)]56addrs = addr1 + addr258# Send 1 + 0.5 coin to each address59[self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs]60[self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs]61self.generate(self.nodes[0], 1)63for i in range(20):64for n in [1, 2]:65self.log.debug(f"Send transaction from node {n}: expected change index {i}")66txid = self.nodes[n].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)67tx = self.nodes[n].getrawtransaction(txid, True)68# find the change output and ensure that expected change index was used69self.assert_change_index(self.nodes[n], tx, i)71# Start next test with fresh wallets and new coins72self.nodes[1].createwallet("w1")73self.nodes[2].createwallet("w2")74w1 = self.nodes[1].get_wallet_rpc("w1")75w2 = self.nodes[2].get_wallet_rpc("w2")76addr1 = w1.getnewaddress()77addr2 = w2.getnewaddress()78self.nodes[0].sendtoaddress(addr1, 3.0)79self.nodes[0].sendtoaddress(addr1, 0.1)80self.nodes[0].sendtoaddress(addr2, 3.0)81self.nodes[0].sendtoaddress(addr2, 0.1)82self.generate(self.nodes[0], 1)84sendTo1 = self.nodes[0].getnewaddress()85sendTo2 = self.nodes[0].getnewaddress()86sendTo3 = self.nodes[0].getnewaddress()88# The avoid partial spends wallet will always create a change output89node = self.nodes[2]90res = w2.send({sendTo1: "1.0", sendTo2: "1.0", sendTo3: "0.9999"}, options={"change_position": 0})91tx = node.getrawtransaction(res["txid"], True)92self.assert_change_pos(w2, tx, 0)94# The default wallet will internally create a tx without change first,95# then create a second candidate using APS that requires a change output.96# Ensure that the user-configured change position is kept97node = self.nodes[1]98res = w1.send({sendTo1: "1.0", sendTo2: "1.0", sendTo3: "0.9999"}, options={"change_position": 0})99tx = node.getrawtransaction(res["txid"], True)100# If the wallet ignores the user's change_position there is still a 25%101# that the random change position passes the test102self.assert_change_pos(w1, tx, 0)104if __name__ == '__main__':105WalletChangeAddressTest(__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.