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.
251self.log.info("Don't broadcast...")252res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)253assert res["hex"]255self.log.info("Return PSBT...")256res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True)257assert res["psbt"]259self.log.info("Create transaction that spends to address, but don't broadcast...")260self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)261# conf_target & estimate_mode can be set as argument or option262res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical", add_to_wallet=False)263res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=1, estimate_mode="economical", add_to_wallet=False)264assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],265self.nodes[1].decodepsbt(res2["psbt"])["fee"])266# but not at the same time267for mode in ["unset", "economical", "conservative"]:268self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical",269conf_target=1, estimate_mode=mode, add_to_wallet=False,270expect_error=(-8, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both"))272self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")273res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)274res = w2.walletprocesspsbt(res["psbt"])275assert res["complete"]277self.log.info("Create OP_RETURN...")278self.test_send(from_wallet=w0, to_wallet=w1, amount=1)279self.test_send(from_wallet=w0, data="Hello World", expect_error=(-8, "Data must be hexadecimal string (not 'Hello World')"))280self.test_send(from_wallet=w0, data="23")281res = self.test_send(from_wallet=w3, data="23")282res = w2.walletprocesspsbt(res["psbt"])283assert res["complete"]285self.log.info("Create mempool-invalid tx (due to large OP_RETURN)...")286self.test_send(from_wallet=w0, data=b"The quick brown fox jumps over the lazy dog".hex(), nonmempool=True)288self.log.info("Test setting explicit fee rate")289res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate="1", add_to_wallet=False)290res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate="1", add_to_wallet=False)291assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"], self.nodes[1].decodepsbt(res2["psbt"])["fee"])293res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=7, add_to_wallet=False)294fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]295assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00007"))297# "unset" and None are treated the same for estimate_mode298res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=2, estimate_mode="unset", add_to_wallet=False)299fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]300assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00002"))302res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=4.531, add_to_wallet=False)303fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]304assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00004531"))306res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=3, add_to_wallet=False)307fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]308assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00003"))310# Test that passing fee_rate as both an argument and an option raises.311self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=1, fee_rate=1, add_to_wallet=False,312expect_error=(-8, "Pass the fee_rate either as an argument, or in the options object, but not both"))314assert_raises_rpc_error(-8, "Use fee_rate (sat/vB) instead of feeRate", w0.send, {w1.getnewaddress(): 1}, 6, "conservative", 1, {"feeRate": 0.01})316assert_raises_rpc_error(-3, "Unexpected key totalFee", w0.send, {w1.getnewaddress(): 1}, 6, "conservative", 1, {"totalFee": 0.01})318for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):319self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=target, estimate_mode=mode,320expect_error=(-8, "Invalid conf_target, must be between 1 and 1008")) # max value of 1008 per src/policy/fees/block_policy_estimator.h321msg = 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"'322for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):323self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=target, estimate_mode=mode, expect_error=(-8, msg))324for mode in ["", "foo", Decimal("3.141592")]:325self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode=mode, expect_error=(-8, msg))326self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=0.1, arg_estimate_mode=mode, expect_error=(-8, msg))327assert_raises_rpc_error(-8, msg, w0.send, {w1.getnewaddress(): 1}, 0.1, mode)329for mode in ["economical", "conservative"]:330for k, v in {"string": "true", "bool": True, "object": {"foo": "bar"}}.items():331self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode,332expect_error=(-3, f"JSON value of type {k} for field conf_target is not of expected type number"))334# Test setting explicit fee rate just below the minimum of 1 sat/vB.335self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed")336msg = "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"337self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.999, expect_error=(-4, msg))338self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.999, expect_error=(-4, msg))340self.log.info("Explicit fee rate raises if invalid fee_rate is passed")341# Test fee_rate with zero values.342msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"343for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:344self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=zero_value, expect_error=(-4, msg))345self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=zero_value, expect_error=(-4, msg))346msg = "Invalid amount"347# Test fee_rate values that don't pass fixed-point parsing checks.348for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:349self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))350self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))351# Test fee_rate values that cannot be represented in sat/vB.352for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:353self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))354self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))355# Test fee_rate out of range (negative number).356msg = "Amount out of range"357self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=-1, expect_error=(-3, msg))358self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=-1, expect_error=(-3, msg))359# Test type error.360msg = "Amount is not a number or string"361for invalid_value in [True, {"foo": "bar"}]:362self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))363self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))365# TODO: Return hex if fee rate is below -maxmempool366# res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)367# assert res["hex"]368# hex = res["hex"]369# res = self.nodes[0].testmempoolaccept([hex])370# assert not res[0]["allowed"]371# assert_equal(res[0]["reject-reason"], "...") # low fee372# assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.000001"))374self.log.info("If inputs are specified, do not automatically add more...")375res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[], add_to_wallet=False)376assert res["complete"]377utxo1 = w0.listunspent()[0]378assert_equal(utxo1["amount"], 50)379ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \380"Please allow other inputs to be automatically selected or include more coins manually"381self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1],382expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS))383self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=False,384expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS))385res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=True, add_to_wallet=False)386assert res["complete"]388self.log.info("Manual change address and position...")389self.test_send(from_wallet=w0, to_wallet=w1, amount=1, change_address="not an address",390expect_error=(-5, "Change address must be a valid bitcoin address"))391change_address = w0.getnewaddress()392self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address)393assert res["complete"]394res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)395assert res["complete"]396assert_equal(self.nodes[0].decodepsbt(res["psbt"])["outputs"][0]["script"]["address"], change_address)397res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)398assert res["complete"]399change_address = self.nodes[0].decodepsbt(res["psbt"])["outputs"][0]["script"]["address"]400assert change_address[0] in ("m", "n")402self.log.info("Set lock time...")403height = self.nodes[0].getblockchaininfo()["blocks"]404res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, locktime=height + 1)405assert res["complete"]406assert res["txid"]407txid = res["txid"]408# Although the wallet finishes the transaction, it can't be added to the mempool yet:409hex = self.nodes[0].gettransaction(res["txid"])["hex"]410res = self.nodes[0].testmempoolaccept([hex])411assert not res[0]["allowed"]412assert_equal(res[0]["reject-reason"], "non-final")413# It shouldn't be confirmed in the next block414self.generate(self.nodes[0], 1)415assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 0)416# The mempool should allow it now:417res = self.nodes[0].testmempoolaccept([hex])418assert res[0]["allowed"]419# Don't wait for wallet to add it to the mempool:420res = self.nodes[0].sendrawtransaction(hex)421self.generate(self.nodes[0], 1)422assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 1)424self.log.info("Lock unspents...")425utxo1 = w0.listunspent()[0]426assert_greater_than(utxo1["amount"], 1)427res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False, lock_unspents=True)428assert res["complete"]429locked_coins = w0.listlockunspent()430assert_equal(len(locked_coins), 1)431# Locked coins are automatically unlocked when manually selected432res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False)433assert res["complete"]435self.log.info("Subtract fee from output")436self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])438self.log.info("Include unsafe inputs")439self.nodes[1].createwallet(wallet_name="w5")440w5 = self.nodes[1].get_wallet_rpc("w5")441self.test_send(from_wallet=w0, to_wallet=w5, amount=2)442self.test_send(from_wallet=w5, to_wallet=w0, amount=1, expect_error=(-4, "Insufficient funds"))443res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)444assert res["complete"]446self.log.info("Minconf")447self.nodes[1].createwallet(wallet_name="minconfw")448minconfw= self.nodes[1].get_wallet_rpc("minconfw")449self.test_send(from_wallet=w0, to_wallet=minconfw, amount=2)450self.generate(self.nodes[0], 3)451self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=4, expect_error=(-4, "Insufficient funds"))452self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=-4, expect_error=(-8, "Negative minconf"))453res = self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=3)454assert res["complete"]456self.log.info("External outputs")457privkey, _ = generate_keypair(wif=True)459self.nodes[1].createwallet("extsend")460ext_wallet = self.nodes[1].get_wallet_rpc("extsend")461self.nodes[1].createwallet("extfund")462ext_fund = self.nodes[1].get_wallet_rpc("extfund")464# Make a weird but signable script. sh(wsh(pkh())) descriptor accomplishes this465desc = descsum_create("sh(wsh(pkh({})))".format(privkey))466res = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])467assert res[0]["success"]468addr = self.nodes[0].deriveaddresses(desc)[0]469addr_info = ext_fund.getaddressinfo(addr)471self.nodes[0].sendtoaddress(addr, 10)472self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)473self.generate(self.nodes[0], 6)474ext_utxo = ext_fund.listunspent(addresses=[addr])[0]476# An external input without solving data should result in an error477self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, expect_error=(-4, "Not solvable pre-selected input COutPoint(%s, %s)" % (ext_utxo["txid"][0:10], ext_utxo["vout"])))479# But funding should work when the solving data is provided480res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]})481signed = ext_wallet.walletprocesspsbt(res["psbt"])482signed = ext_fund.walletprocesspsbt(res["psbt"])483assert signed["complete"]485res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, solving_data={"descriptors": [desc]})486signed = ext_wallet.walletprocesspsbt(res["psbt"])487signed = ext_fund.walletprocesspsbt(res["psbt"])488assert signed["complete"]490dec = self.nodes[0].decodepsbt(signed["psbt"])491for psbt_in in dec["inputs"]:492if psbt_in["previous_txid"] == ext_utxo["txid"] and psbt_in["previous_vout"] == ext_utxo["vout"]:493break494scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""495witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None496input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)498# Input weight error conditions499assert_raises_rpc_error(500-8,
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.