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.
751assert_greater_than(large_tx_weight_without_inputs, min_tx_weight)752# Test for max_tx_weight less than Transaction weight without inputs753assert_raises_rpc_error(-4, "Maximum transaction weight is less than transaction weight without inputs", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": min_tx_weight})754assert_raises_rpc_error(-4, "Maximum transaction weight is less than transaction weight without inputs", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": large_tx_weight_without_inputs})756# Test for max_tx_weight just enough to include inputs but not change output757assert_raises_rpc_error(-4, "Maximum transaction weight is too low, can not accommodate change output", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": (large_tx_vsize_without_inputs + 1) * WITNESS_SCALE_FACTOR})758self.log.info("Test that a funded PSBT is always faithful to max_tx_weight option")759large_tx_vsize_with_change = large_tx_vsize_without_inputs + p2wpkh_output_vsize760# It's enough but won't accommodate selected input size761assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": (large_tx_vsize_with_change) * WITNESS_SCALE_FACTOR})763max_tx_weight_sufficient = 1000 # 1k vbytes is enough764psbt = self.nodes[0].walletcreatefundedpsbt(outputs=dest_arg,locktime=0, options={"max_tx_weight": max_tx_weight_sufficient})["psbt"]765psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"]766final_tx = self.nodes[0].finalizepsbt(psbt)["hex"]767weight = self.nodes[0].decoderawtransaction(final_tx)["weight"]768# ensure the transaction's weight is below the specified max_tx_weight.769assert_greater_than_or_equal(max_tx_weight_sufficient, weight)771# If inputs are specified, do not automatically add more:772utxo1 = self.nodes[0].listunspent()[0]773assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. "774"Please allow other inputs to be automatically selected or include more coins manually",775self.nodes[0].walletcreatefundedpsbt, [{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90})777psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt']778assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['inputs']), 2)780# Inputs argument can be null781self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress():10})783# Node 1 should not be able to add anything to it but still return the psbtx same as before784psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt']785assert_equal(psbtx1, psbtx)787# Node 0 should not be able to sign the transaction with the wallet is locked788self.nodes[0].encryptwallet("password")789assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].walletprocesspsbt, psbtx)791# Node 0 should be able to process without signing though792unsigned_tx = self.nodes[0].walletprocesspsbt(psbtx, False)793assert_equal(unsigned_tx['complete'], False)795self.nodes[0].walletpassphrase(passphrase="password", timeout=1000000)797# Sign the transaction but don't finalize798processed_psbt = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=False)799assert "hex" not in processed_psbt800signed_psbt = processed_psbt['psbt']802# Finalize and send803finalized_hex = self.nodes[0].finalizepsbt(signed_psbt)['hex']804self.nodes[0].sendrawtransaction(finalized_hex)806# Alternative method: sign AND finalize in one command807processed_finalized_psbt = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=True)808finalized_psbt = processed_finalized_psbt['psbt']809finalized_psbt_hex = processed_finalized_psbt['hex']810assert_not_equal(signed_psbt, finalized_psbt)811assert_equal(finalized_psbt_hex, finalized_hex)813# Manually selected inputs can be locked:814assert_equal(len(self.nodes[0].listlockunspent()), 0)815utxo1 = self.nodes[0].listunspent()[0]816psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():1}, 0,{"lockUnspents": True})["psbt"]817assert_equal(len(self.nodes[0].listlockunspent()), 1)819# Locks are ignored for manually selected inputs820self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():1}, 0)822# Create p2sh, p2wpkh, and p2wsh addresses823pubkey0 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey']824pubkey1 = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey']825pubkey2 = self.nodes[2].getaddressinfo(self.nodes[2].getnewaddress())['pubkey']827# Setup watchonly wallets828self.nodes[2].createwallet(wallet_name='wmulti', disable_private_keys=True)829wmulti = self.nodes[2].get_wallet_rpc('wmulti')831# Create all the addresses832p2sh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="legacy")833p2sh = p2sh_ms["address"]834p2wsh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="bech32")835p2wsh = p2wsh_ms["address"]836p2sh_p2wsh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="p2sh-segwit")837p2sh_p2wsh = p2sh_p2wsh_ms["address"]838import_res = wmulti.importdescriptors(839[840{"desc": p2sh_ms["descriptor"], "timestamp": "now"},841{"desc": p2wsh_ms["descriptor"], "timestamp": "now"},842{"desc": p2sh_p2wsh_ms["descriptor"], "timestamp": "now"},843])844assert_equal(all([r["success"] for r in import_res]), True)845p2wpkh = self.nodes[1].getnewaddress("", "bech32")846p2pkh = self.nodes[1].getnewaddress("", "legacy")847p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit")849# fund those addresses850rawtx = self.nodes[0].createrawtransaction([], {p2sh:10, p2wsh:10, p2wpkh:10, p2sh_p2wsh:10, p2sh_p2wpkh:10, p2pkh:10})851rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition":3})852signed_tx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])['hex']853txid = self.nodes[0].sendrawtransaction(signed_tx)854self.generate(self.nodes[0], 6)856# Find the output pos857p2sh_pos = -1858p2wsh_pos = -1859p2wpkh_pos = -1860p2pkh_pos = -1861p2sh_p2wsh_pos = -1862p2sh_p2wpkh_pos = -1863decoded = self.nodes[0].decoderawtransaction(signed_tx)864for out in decoded['vout']:865if out['scriptPubKey']['address'] == p2sh:866p2sh_pos = out['n']867elif out['scriptPubKey']['address'] == p2wsh:868p2wsh_pos = out['n']869elif out['scriptPubKey']['address'] == p2wpkh:870p2wpkh_pos = out['n']871elif out['scriptPubKey']['address'] == p2sh_p2wsh:872p2sh_p2wsh_pos = out['n']873elif out['scriptPubKey']['address'] == p2sh_p2wpkh:874p2sh_p2wpkh_pos = out['n']875elif out['scriptPubKey']['address'] == p2pkh:876p2pkh_pos = out['n']878inputs = [{"txid": txid, "vout": p2wpkh_pos}, {"txid": txid, "vout": p2sh_p2wpkh_pos}, {"txid": txid, "vout": p2pkh_pos}]879outputs = [{self.nodes[1].getnewaddress(): 29.99}]881# spend single key from node 1882created_psbt = self.nodes[1].walletcreatefundedpsbt(inputs, outputs)883walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(created_psbt['psbt'])884# Make sure it has both types of UTXOs885decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out['psbt'])886assert 'non_witness_utxo' in decoded['inputs'][0]887assert 'witness_utxo' in decoded['inputs'][0]888# Check decodepsbt fee calculation (input values shall only be counted once per UTXO)889assert_equal(decoded['fee'], created_psbt['fee'])890assert_equal(walletprocesspsbt_out['complete'], True)891self.nodes[1].sendrawtransaction(walletprocesspsbt_out['hex'])893self.log.info("Test walletcreatefundedpsbt fee rate of 10000 sat/vB and 0.1 BTC/kvB produces a total fee at or slightly below -maxtxfee (~0.05290000)")894res1 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 10000, "add_inputs": True})895assert_approx(res1["fee"], 0.055, 0.005)896res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": "0.1", "add_inputs": True})897assert_approx(res2["fee"], 0.055, 0.005)899self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed")900res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": "0.999", "add_inputs": True})901assert_approx(res3["fee"], 0.00000381, 0.0000001)902res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.00000999, "add_inputs": True})903assert_approx(res4["fee"], 0.00000381, 0.0000001)905self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed and that funding non-standard 'zero-fee' transactions is valid")906for param, zero_value in product(["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]):907assert_equal(0, self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {param: zero_value, "add_inputs": True})["fee"])909self.log.info("Test invalid fee rate settings")910for param, value in {("fee_rate", 100000), ("feeRate", 1)}:911assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",912self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: value, "add_inputs": True})913assert_raises_rpc_error(-3, "Amount out of range",914self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: -1, "add_inputs": True})915assert_raises_rpc_error(-3, "Amount is not a number or string",916self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: {"foo": "bar"}, "add_inputs": True})917# Test fee rate values that don't pass fixed-point parsing checks.918for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:919assert_raises_rpc_error(-3, "Invalid amount",920self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: invalid_value, "add_inputs": True})921# Test fee_rate values that cannot be represented in sat/vB.922for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:923assert_raises_rpc_error(-3, "Invalid amount",924self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": invalid_value, "add_inputs": True})926self.log.info("- raises RPC error if both feeRate and fee_rate are passed")927assert_raises_rpc_error(-8, "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)",928self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": 0.1, "feeRate": 0.1, "add_inputs": True})930self.log.info("- raises RPC error if both feeRate and estimate_mode passed")931assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and feeRate",932self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": "economical", "feeRate": 0.1, "add_inputs": True})934for param in ["feeRate", "fee_rate"]:935self.log.info("- raises RPC error if both {} and conf_target are passed".format(param))936assert_raises_rpc_error(-8, "Cannot specify both conf_target and {}. Please provide either a confirmation "937"target in blocks for automatic fee estimation, or an explicit fee rate.".format(param),938self.nodes[1].walletcreatefundedpsbt ,inputs, outputs, 0, {param: 1, "conf_target": 1, "add_inputs": True})940self.log.info("- raises RPC error if both fee_rate and estimate_mode are passed")941assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and fee_rate",942self.nodes[1].walletcreatefundedpsbt ,inputs, outputs, 0, {"fee_rate": 1, "estimate_mode": "economical", "add_inputs": True})944self.log.info("- raises RPC error with invalid estimate_mode settings")945for k, v in {"number": 42, "object": {"foo": "bar"}}.items():946assert_raises_rpc_error(-3, f"JSON value of type {k} for field estimate_mode is not of expected type string",947self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": v, "conf_target": 0.1, "add_inputs": True})948for mode in ["", "foo", Decimal("3.141592")]:949assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"',950self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": mode, "conf_target": 0.1, "add_inputs": True})952self.log.info("- raises RPC error with invalid conf_target settings")953for mode in ["unset", "economical", "conservative"]:954self.log.debug("{}".format(mode))955for k, v in {"string": "", "object": {"foo": "bar"}}.items():956assert_raises_rpc_error(-3, f"JSON value of type {k} for field conf_target is not of expected type number",957self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": mode, "conf_target": v, "add_inputs": True})958for n in [-1, 0, 1009]:959assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees/block_policy_estimator.h960self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": mode, "conf_target": n, "add_inputs": True})962self.log.info("Test walletcreatefundedpsbt with too-high fee rate produces total fee well above -maxtxfee and raises RPC error")963# previously this was silently capped at -maxtxfee964for bool_add, outputs_array in {True: outputs, False: [{self.nodes[1].getnewaddress(): 1}]}.items():965msg = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"966assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"fee_rate": 1000000, "add_inputs": bool_add})967assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"feeRate": 1, "add_inputs": bool_add})969self.log.info("Test various PSBT operations")970# partially sign multisig things with node 1971psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs={self.nodes[1].getnewaddress():29.99}, changeAddress=self.nodes[1].getrawchangeaddress())['psbt']972walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx)973psbtx = walletprocesspsbt_out['psbt']974assert_equal(walletprocesspsbt_out['complete'], False)976# Unload wmulti, we don't need it anymore977wmulti.unloadwallet()979# partially sign with node 2. This should be complete and sendable980walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx)981assert_equal(walletprocesspsbt_out['complete'], True)982self.nodes[2].sendrawtransaction(walletprocesspsbt_out['hex'])984# check that walletprocesspsbt fails to decode a non-psbt985rawtx = self.nodes[1].createrawtransaction([{"txid":txid,"vout":p2wpkh_pos}], {self.nodes[1].getnewaddress():9.99})986assert_raises_rpc_error(-22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx)988# Convert a non-psbt to psbt and make sure we can decode it989rawtx = self.nodes[0].createrawtransaction([], {self.nodes[1].getnewaddress():10})990rawtx = self.nodes[0].fundrawtransaction(rawtx)991new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])992self.nodes[0].decodepsbt(new_psbt)994# Make sure that a non-psbt with signatures cannot be converted995signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])996assert_raises_rpc_error(-22, "Inputs must not have scriptSigs and scriptWitnesses",997self.nodes[0].converttopsbt, hexstring=signedtx['hex']) # permitsigdata=False by default998assert_raises_rpc_error(-22, "Inputs must not have scriptSigs and scriptWitnesses",999self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False)1000assert_raises_rpc_error(-22, "Inputs must not have scriptSigs and scriptWitnesses",
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.