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) 2025-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or https://www.opensource.org/licenses/mit-license.php.6import time8from test_framework.blocktools import MAX_FUTURE_BLOCK_TIME9from test_framework.descriptors import descsum_create10from test_framework.messages import (11COutPoint,12CTxIn,13CTxInWitness,14CTxOut,15)16from test_framework.script_util import (17ANCHOR_ADDRESS,18PAY_TO_ANCHOR,19)20from test_framework.test_framework import BitcoinTestFramework21from test_framework.util import (22assert_equal,23assert_raises_rpc_error,24)25from test_framework.wallet import MiniWallet27class WalletAnchorTest(BitcoinTestFramework):28def set_test_params(self):29self.num_nodes = 131def skip_test_if_missing_module(self):32self.skip_if_no_wallet()34def test_0_value_anchor_listunspent(self):35self.log.info("Test that 0-value anchor outputs are detected as UTXOs")37# Create an anchor output, and spend it38sender = MiniWallet(self.nodes[0])39anchor_tx = sender.create_self_transfer(fee_rate=0, version=3)["tx"]40anchor_tx.vout.append(CTxOut(0, PAY_TO_ANCHOR))41anchor_spend = sender.create_self_transfer(version=3)["tx"]42anchor_spend.vin.append(CTxIn(COutPoint(anchor_tx.txid_int, 1), b""))43anchor_spend.wit.vtxinwit.append(CTxInWitness())44submit_res = self.nodes[0].submitpackage([anchor_tx.serialize().hex(), anchor_spend.serialize().hex()])45assert_equal(submit_res["package_msg"], "success")46anchor_txid = anchor_tx.txid_hex47anchor_spend_txid = anchor_spend.txid_hex49# Mine each tx in separate blocks50self.generateblock(self.nodes[0], sender.get_address(), [anchor_tx.serialize().hex()])51anchor_tx_height = self.nodes[0].getblockcount()52self.generateblock(self.nodes[0], sender.get_address(), [anchor_spend.serialize().hex()])54# Mock time forward and generate some blocks to avoid rescanning of latest blocks55self.nodes[0].setmocktime(int(time.time()) + MAX_FUTURE_BLOCK_TIME + 1)56self.generate(self.nodes[0], 10)58self.nodes[0].createwallet(wallet_name="anchor", disable_private_keys=True)59wallet = self.nodes[0].get_wallet_rpc("anchor")60import_res = wallet.importdescriptors([{"desc": descsum_create(f"addr({ANCHOR_ADDRESS})"), "timestamp": "now"}])61assert_equal(import_res[0]["success"], True)63# The wallet should have no UTXOs, and not know of the anchor tx or its spend64assert_equal(wallet.listunspent(), [])65assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_txid)66assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_spend_txid)68# Rescanning the block containing the anchor so that listunspent will list the output69wallet.rescanblockchain(0, anchor_tx_height)70utxos = wallet.listunspent()71assert_equal(len(utxos), 1)72assert_equal(utxos[0]["txid"], anchor_txid)73assert_equal(utxos[0]["address"], ANCHOR_ADDRESS)74assert_equal(utxos[0]["amount"], 0)75wallet.gettransaction(anchor_txid)76assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", wallet.gettransaction, anchor_spend_txid)78# Rescan the rest of the blockchain to see the anchor was spent79wallet.rescanblockchain()80assert_equal(wallet.listunspent(), [])81wallet.gettransaction(anchor_spend_txid)83def test_cannot_sign_anchors(self):84self.log.info("Test that the wallet cannot spend anchor outputs")85for disable_privkeys in [False, True]:86self.nodes[0].createwallet(wallet_name=f"anchor_spend_{disable_privkeys}", disable_private_keys=disable_privkeys)87wallet = self.nodes[0].get_wallet_rpc(f"anchor_spend_{disable_privkeys}")88import_res = wallet.importdescriptors([89{"desc": descsum_create(f"addr({ANCHOR_ADDRESS})"), "timestamp": "now"},90{"desc": descsum_create(f"raw({PAY_TO_ANCHOR.hex()})"), "timestamp": "now"}91])92assert_equal(import_res[0]["success"], disable_privkeys)93assert_equal(import_res[1]["success"], disable_privkeys)95anchor_txid = self.default_wallet.sendtoaddress(ANCHOR_ADDRESS, 1)96self.generate(self.nodes[0], 1)98wallet = self.nodes[0].get_wallet_rpc("anchor_spend_True")99utxos = wallet.listunspent()100assert_equal(len(utxos), 1)101assert_equal(utxos[0]["txid"], anchor_txid)102assert_equal(utxos[0]["address"], ANCHOR_ADDRESS)103assert_equal(utxos[0]["amount"], 1)105assert_raises_rpc_error(-4, "Missing solving data for estimating transaction size", wallet.send, [{self.default_wallet.getnewaddress(): 0.9999}])106assert_raises_rpc_error(-4, "Error: Private keys are disabled for this wallet", wallet.sendtoaddress, self.default_wallet.getnewaddress(), 0.9999)107assert_raises_rpc_error(-4, "Unable to determine the size of the transaction, the wallet contains unsolvable descriptors", wallet.sendall, recipients=[self.default_wallet.getnewaddress()], inputs=utxos)108assert_raises_rpc_error(-4, "Unable to determine the size of the transaction, the wallet contains unsolvable descriptors", wallet.sendall, recipients=[self.default_wallet.getnewaddress()])110def run_test(self):111self.default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)112self.test_0_value_anchor_listunspent()113self.test_cannot_sign_anchors()115if __name__ == '__main__':116WalletAnchorTest(__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.