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.
1001utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 10}])[0]1003rawtx = self.nodes[0].createrawtransaction([utxo], [{self.nodes[0].getnewaddress(): 5}])1004fundedtx = self.nodes[0].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0], fee_rate=self.fee_rate_sats_per_vb)1005signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex'])1006self.nodes[0].sendrawtransaction(signedtx['hex'])1008def test_transaction_too_large(self):1009self.log.info("Test fundrawtx where BnB solution would result in a too large transaction, but Knapsack would not")1010self.nodes[0].createwallet("large")1011wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)1012recipient = self.nodes[0].get_wallet_rpc("large")1013outputs = {}1014rawtx = recipient.createrawtransaction([], {wallet.getnewaddress(): 147.99899260})1016# Make 1500 0.1 BTC outputs. The amount that we target for funding is in1017# the BnB range when these outputs are used. However if these outputs1018# are selected, the transaction will end up being too large, so it1019# shouldn't use BnB and instead fall back to Knapsack but that behavior1020# is not implemented yet. For now we just check that we get an error.1021# First, force the wallet to bulk-generate the addresses we'll need.1022recipient.keypoolrefill(1500)1023for _ in range(1500):1024outputs[recipient.getnewaddress()] = 0.11025wallet.sendmany("", outputs, fee_rate=self.fee_rate_sats_per_vb)1026self.generate(self.nodes[0], 10)1027assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. "1028"Please try sending a smaller amount or manually consolidating your wallet's UTXOs",1029recipient.fundrawtransaction, rawtx)1030self.nodes[0].unloadwallet("large")1032def test_external_inputs(self):1033self.log.info("Test funding with external inputs")1034privkey, _ = generate_keypair(wif=True)1035self.nodes[2].createwallet("extfund")1036wallet = self.nodes[2].get_wallet_rpc("extfund")1038# Make a weird but signable script. sh(pkh()) descriptor accomplishes this1039desc = descsum_create("sh(pkh({}))".format(privkey))1040res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])1041assert res[0]["success"]1042addr = self.nodes[0].deriveaddresses(desc)[0]1043addr_info = self.nodes[0].getaddressinfo(addr)1045self.nodes[0].sendtoaddress(addr, 10, fee_rate=self.fee_rate_sats_per_vb)1046self.nodes[0].sendtoaddress(wallet.getnewaddress(), 10, fee_rate=self.fee_rate_sats_per_vb)1047self.generate(self.nodes[0], 6)1048ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]1050# An external input without solving data should result in an error1051raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): ext_utxo["amount"] / 2})1052assert_raises_rpc_error(-4, "Not solvable pre-selected input COutPoint(%s, %s)" % (ext_utxo["txid"][0:10], ext_utxo["vout"]), wallet.fundrawtransaction, raw_tx)1054# Error conditions1055assert_raises_rpc_error(-5, 'Pubkey "not a pubkey" must be a hex string', wallet.fundrawtransaction, raw_tx, solving_data={"pubkeys":["not a pubkey"]})1056assert_raises_rpc_error(-5, 'Pubkey "01234567890a0b0c0d0e0f" must have a length of either 33 or 65 bytes', wallet.fundrawtransaction, raw_tx, solving_data={"pubkeys":["01234567890a0b0c0d0e0f"]})1057assert_raises_rpc_error(-5, "'not a script' is not hex", wallet.fundrawtransaction, raw_tx, solving_data={"scripts":["not a script"]})1058assert_raises_rpc_error(-8, "Unable to parse descriptor 'not a descriptor'", wallet.fundrawtransaction, raw_tx, solving_data={"descriptors":["not a descriptor"]})1059assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"]}])1060assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": -1}])1061assert_raises_rpc_error(-8, "Invalid parameter, missing weight key", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"]}])1062assert_raises_rpc_error(-8, "Invalid parameter, weight cannot be less than 165", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 164}])1063assert_raises_rpc_error(-8, "Invalid parameter, weight cannot be less than 165", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": -1}])1064assert_raises_rpc_error(-8, "Invalid parameter, weight cannot be greater than", wallet.fundrawtransaction, raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 400001}])1066# But funding should work when the solving data is provided1067funded_tx = wallet.fundrawtransaction(raw_tx, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]})1068signed_tx = wallet.signrawtransactionwithwallet(funded_tx['hex'])1069assert not signed_tx['complete']1070signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx['hex'])1071assert signed_tx['complete']1073funded_tx = wallet.fundrawtransaction(raw_tx, solving_data={"descriptors": [desc]}, fee_rate=self.fee_rate_sats_per_vb)1074signed_tx1 = wallet.signrawtransactionwithwallet(funded_tx['hex'])1075assert not signed_tx1['complete']1076signed_tx2 = self.nodes[0].signrawtransactionwithwallet(signed_tx1['hex'])1077assert signed_tx2['complete']1079unsigned_weight = self.nodes[0].decoderawtransaction(signed_tx1["hex"])["weight"]1080signed_weight = self.nodes[0].decoderawtransaction(signed_tx2["hex"])["weight"]1081# Input's weight is difference between weight of signed and unsigned,1082# and the weight of stuff that didn't change (prevout, sequence, 1 byte of scriptSig)1083input_weight = signed_weight - unsigned_weight + (41 * 4)1084low_input_weight = input_weight // 21085high_input_weight = input_weight * 21087# Funding should also work if the input weight is provided1088funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}], fee_rate=2)1089signed_tx = wallet.signrawtransactionwithwallet(funded_tx["hex"])1090signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx["hex"])1091assert_equal(self.nodes[0].testmempoolaccept([signed_tx["hex"]])[0]["allowed"], True)1092assert_equal(signed_tx["complete"], True)1093# Reducing the weight should have a lower fee1094funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": low_input_weight}], fee_rate=2)1095assert_greater_than(funded_tx["fee"], funded_tx2["fee"])1096# Increasing the weight should have a higher fee1097funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=2)1098assert_greater_than(funded_tx2["fee"], funded_tx["fee"])1099# The provided weight should override the calculated weight when solving data is provided1100funded_tx3 = wallet.fundrawtransaction(raw_tx, solving_data={"descriptors": [desc]}, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=2)1101assert_equal(funded_tx2["fee"], funded_tx3["fee"])1102# The feerate should be met1103funded_tx4 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=10)1104input_add_weight = high_input_weight - (41 * 4)1105tx4_weight = wallet.decoderawtransaction(funded_tx4["hex"])["weight"] + input_add_weight1106tx4_vsize = int(ceil(tx4_weight / 4))1107assert_fee_amount(funded_tx4["fee"], tx4_vsize, Decimal(0.0001))1109# Funding with weight at csuint boundaries should not cause problems1110funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 255}], fee_rate=2)1111funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 65539}], fee_rate=2)1113self.nodes[2].unloadwallet("extfund")1115def test_add_inputs_default_value(self):1116self.log.info("Test 'add_inputs' default value")1118# Create and fund the wallet with 5 BTC1119self.nodes[2].createwallet("test_preset_inputs")1120wallet = self.nodes[2].get_wallet_rpc("test_preset_inputs")1121addr1 = wallet.getnewaddress(address_type="bech32")1122self.nodes[0].sendtoaddress(addr1, 5, fee_rate=self.fee_rate_sats_per_vb)1123self.generate(self.nodes[0], 1)1125# Covered cases:1126# 1. Default add_inputs value with no preset inputs (add_inputs=true):1127# Expect: automatically add coins from the wallet to the tx.1128# 2. Default add_inputs value with preset inputs (add_inputs=false):1129# Expect: disallow automatic coin selection.1130# 3. Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount).1131# Expect: include inputs from the wallet.1132# 4. Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount).1133# Expect: only preset inputs are used.1134# 5. Explicit add_inputs=true, no preset inputs (same as (1) but with an explicit set):1135# Expect: include inputs from the wallet.1136# 6. Explicit add_inputs=false, no preset inputs:1137# Expect: failure as we did not provide inputs and the process cannot automatically select coins.1139# Case (1), 'send' command1140# 'add_inputs' value is true unless "inputs" are specified, in such case, add_inputs=false.1141# So, the wallet will automatically select coins and create the transaction if only the outputs are provided.1142tx = wallet.send(outputs=[{addr1: 3}], fee_rate=self.fee_rate_sats_per_vb)1143assert tx["complete"]1145# Case (2), 'send' command1146# Select an input manually, which doesn't cover the entire output amount and1147# verify that the dynamically set 'add_inputs=false' value works.1149# Fund wallet with 2 outputs, 5 BTC each.1150addr2 = wallet.getnewaddress(address_type="bech32")1151source_tx = self.nodes[0].send(outputs=[{addr1: 5}, {addr2: 5}], change_position=0, fee_rate=self.fee_rate_sats_per_vb)1152self.generate(self.nodes[0], 1)1154# Select only one input.1155options = {1156"inputs": [1157{1158"txid": source_tx["txid"],1159"vout": 1 # change position was hardcoded to index 01160}1161]1162}1163assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.send, outputs=[{addr1: 8}], **options)1165# Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount)1166options["add_inputs"] = True1167options["add_to_wallet"] = False1168options["fee_rate"] = self.fee_rate_sats_per_vb1169tx = wallet.send(outputs=[{addr1: 8}], **options)1170assert tx["complete"]1172# Case (4), Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount)1173options["inputs"].append({1174"txid": source_tx["txid"],1175"vout": 2 # change position was hardcoded to index 01176})1177tx = wallet.send(outputs=[{addr1: 8}], **options)1178assert tx["complete"]1179# Check that only the preset inputs were added to the tx1180decoded_psbt_inputs = self.nodes[0].decodepsbt(tx["psbt"])["inputs"]1181assert_equal(len(decoded_psbt_inputs), 2)1182for input in decoded_psbt_inputs:1183assert_equal(input["previous_txid"], source_tx["txid"])1185# Case (5), assert that inputs are added to the tx by explicitly setting add_inputs=true1186options = {"add_inputs": True, "add_to_wallet": True}1187tx = wallet.send(outputs=[{addr1: 8}], **options)1188assert tx["complete"]1190# 6. Explicit add_inputs=false, no preset inputs:1191options = {"add_inputs": False}1192assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.send, outputs=[{addr1: 3}], **options)1194################################################1196# Case (1), 'walletcreatefundedpsbt' command1197# Default add_inputs value with no preset inputs (add_inputs=true)1198inputs = []1199outputs = {self.nodes[1].getnewaddress(): 8}1200assert "psbt" in wallet.walletcreatefundedpsbt(inputs=inputs, outputs=outputs, options={'fee_rate': self.fee_rate_sats_per_vb})1202# Case (2), 'walletcreatefundedpsbt' command1203# Default add_inputs value with preset inputs (add_inputs=false).1204inputs = [{1205"txid": source_tx["txid"],1206"vout": 1 # change position was hardcoded to index 01207}]1208outputs = {self.nodes[1].getnewaddress(): 8}1209assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.walletcreatefundedpsbt, inputs=inputs, outputs=outputs, options={'fee_rate': self.fee_rate_sats_per_vb})1211# Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount)1212options["add_inputs"] = True1213assert "psbt" in wallet.walletcreatefundedpsbt(outputs=[{addr1: 8}], inputs=inputs, **options)1215# Case (4), Explicit add_inputs=true and preset inputs (with preset inputs covering the target amount)1216inputs.append({1217"txid": source_tx["txid"],1218"vout": 2 # change position was hardcoded to index 01219})1220psbt_tx = wallet.walletcreatefundedpsbt(outputs=[{addr1: 8}], inputs=inputs, **options)1221# Check that only the preset inputs were added to the tx1222decoded_psbt_inputs = self.nodes[0].decodepsbt(psbt_tx["psbt"])["inputs"]1223assert_equal(len(decoded_psbt_inputs), 2)1224for input in decoded_psbt_inputs:1225assert_equal(input["previous_txid"], source_tx["txid"])1227# Case (5), 'walletcreatefundedpsbt' command1228# Explicit add_inputs=true, no preset inputs1229options = {1230"add_inputs": True,1231}1232assert "psbt" in wallet.walletcreatefundedpsbt(inputs=[], outputs=outputs, **options)1234# Case (6). Explicit add_inputs=false, no preset inputs:1235options = {"add_inputs": False}1236assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.walletcreatefundedpsbt, inputs=[], outputs=outputs, **options)1238self.nodes[2].unloadwallet("test_preset_inputs")1240def test_preset_inputs_selection(self):1241self.log.info('Test wallet preset inputs are not double-counted or reused in coin selection')1243# Create and fund the wallet with 4 UTXO of 5 BTC each (20 BTC total)1244self.nodes[2].createwallet("test_preset_inputs_selection")1245wallet = self.nodes[2].get_wallet_rpc("test_preset_inputs_selection")1246outputs = {}1247for _ in range(4):1248outputs[wallet.getnewaddress(address_type="bech32")] = 51249self.nodes[0].sendmany("", outputs, fee_rate=self.fee_rate_sats_per_vb)1250self.generate(self.nodes[0], 1)
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.