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.
1001self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False, iswitness=True)1002# Unless we allow it to convert and strip signatures1003self.nodes[0].converttopsbt(hexstring=signedtx['hex'], permitsigdata=True)1005# Create outputs to nodes 1 and 21006# (note that we intentionally create two different txs here, as we want1007# to check that each node is missing prevout data for one of the two1008# utxos, see "should only have data for one input" test below)1009node1_addr = self.nodes[1].getnewaddress()1010node2_addr = self.nodes[2].getnewaddress()1011utxo1 = self.create_outpoints(self.nodes[0], outputs=[{node1_addr: 13}])[0]1012utxo2 = self.create_outpoints(self.nodes[0], outputs=[{node2_addr: 13}])[0]1013self.generate(self.nodes[0], 6)[0]1015# Create a psbt spending outputs from nodes 1 and 21016psbt_orig = self.nodes[0].createpsbt([utxo1, utxo2], {self.nodes[0].getnewaddress():25.999})1018# Check that the default psbt version is 21019assert_equal(self.nodes[0].decodepsbt(psbt_orig)["psbt_version"], 2)1021# Update psbts, should only have data for one input and not the other1022psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']1023psbt1_decoded = self.nodes[0].decodepsbt(psbt1)1024assert len(psbt1_decoded['inputs'][0].keys()) > 31025assert len(psbt1_decoded['inputs'][1].keys()) == 31026# Check that BIP32 path was added1027assert "bip32_derivs" in psbt1_decoded['inputs'][0]1028psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt']1029psbt2_decoded = self.nodes[0].decodepsbt(psbt2)1030assert len(psbt2_decoded['inputs'][0].keys()) == 31031assert len(psbt2_decoded['inputs'][1].keys()) > 31032# Check that BIP32 paths were not added1033assert "bip32_derivs" not in psbt2_decoded['inputs'][1]1035# Sign PSBTs (workaround issue #18039)1036psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt']1037psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt']1039# Combine, finalize, and send the psbts1040combined = self.nodes[0].combinepsbt([psbt1, psbt2])1041finalized = self.nodes[0].finalizepsbt(combined)['hex']1042self.nodes[0].sendrawtransaction(finalized)1043self.generate(self.nodes[0], 6)1045# Test additional args in walletcreatepsbt1046# Make sure both pre-included and funded inputs1047# have the correct sequence numbers based on1048# replaceable arg1049block_height = self.nodes[0].getblockcount()1050unspent = self.nodes[0].listunspent()[0]1051psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False, "add_inputs": True}, False)1052decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])1053for psbt_in in decoded_psbt["inputs"]:1054assert_greater_than(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)1055assert "bip32_derivs" not in psbt_in1056assert_equal(decoded_psbt["fallback_locktime"], block_height+2)1058# Same construction with only locktime set and RBF explicitly enabled1059psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True, "add_inputs": True}, True)1060decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])1061for psbt_in in decoded_psbt["inputs"]:1062assert_equal(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)1063assert "bip32_derivs" in psbt_in1064assert_equal(decoded_psbt["fallback_locktime"], block_height)1066# Same construction without optional arguments1067psbtx_info = self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])1068decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])1069for psbt_in in decoded_psbt["inputs"]:1070assert_equal(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)1071assert "bip32_derivs" in psbt_in1072assert_equal(decoded_psbt["fallback_locktime"], 0)1074# Make sure change address wallet does not have P2SH innerscript access to results in success1075# when attempting BnB coin selection1076self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)1078# Make sure the wallet's change type is respected by default1079small_output = {self.nodes[0].getnewaddress():0.1}1080psbtx_native = self.nodes[0].walletcreatefundedpsbt([], [small_output])1081self.assert_change_type(psbtx_native, "witness_v0_keyhash")1082psbtx_legacy = self.nodes[1].walletcreatefundedpsbt([], [small_output])1083self.assert_change_type(psbtx_legacy, "pubkeyhash")1085# Make sure the change type of the wallet can also be overwritten1086psbtx_np2wkh = self.nodes[1].walletcreatefundedpsbt([], [small_output], 0, {"change_type":"p2sh-segwit"})1087self.assert_change_type(psbtx_np2wkh, "scripthash")1089# Make sure the change type cannot be specified if a change address is given1090invalid_options = {"change_type":"legacy","changeAddress":self.nodes[0].getnewaddress()}1091assert_raises_rpc_error(-8, "both change address and address type options", self.nodes[0].walletcreatefundedpsbt, [], [small_output], 0, invalid_options)1093# Regression test for 14473 (mishandling of already-signed witness transaction):1094psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], 0, {"add_inputs": True})1095complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"])1096double_processed_psbt = self.nodes[0].walletprocesspsbt(complete_psbt["psbt"])1097assert_equal(complete_psbt, double_processed_psbt)1098# We don't care about the decode result, but decoding must succeed.1099self.nodes[0].decodepsbt(double_processed_psbt["psbt"])1101# Make sure unsafe inputs are included if specified1102self.nodes[2].createwallet(wallet_name="unsafe")1103wunsafe = self.nodes[2].get_wallet_rpc("unsafe")1104self.nodes[0].sendtoaddress(wunsafe.getnewaddress(), 2)1105self.sync_mempools()1106assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{self.nodes[0].getnewaddress(): 1}])1107wunsafe.walletcreatefundedpsbt([], [{self.nodes[0].getnewaddress(): 1}], 0, {"include_unsafe": True})1109self.log.info("Test that unknown values are just passed through")1110unknown_type = b'\xf0'1111unknown_key = unknown_type + bytes(range(1, 10))1112unknown_value = bytes(range(1, 16))1114unknown_psbt = self.create_psbt(inputs={unknown_key: unknown_value}).to_base64()1115unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)['psbt']1116assert_equal(unknown_psbt, unknown_out)1118unknown_key2 = unknown_type + bytes(range(1, 9)) + bytes(11)1119unknown_psbt2 = self.create_psbt(inputs={unknown_key2: unknown_value}).to_base64()1120merged_unknown = self.nodes[0].combinepsbt([unknown_psbt, unknown_psbt2])1121merged_unknown_psbt = PSBT.from_base64(merged_unknown)1122assert_equal(merged_unknown_psbt.i[0].map[unknown_key], unknown_value)1123assert_equal(merged_unknown_psbt.i[0].map[unknown_key2], unknown_value)1125# The explicit PSBT_GLOBAL_VERSION=0 encoding is equivalent to omitting the1126# version field, and both accepted v0 encodings can be merged.1127explicit_v0_psbt = self.create_psbt(inputs={unknown_key2: unknown_value})1128explicit_v0_psbt.g.map[PSBT_GLOBAL_VERSION] = (0).to_bytes(4, "little")1129merged_v0_versions = self.nodes[0].combinepsbt([unknown_psbt, explicit_v0_psbt.to_base64()])1130merged_v0_versions_psbt = PSBT.from_base64(merged_v0_versions)1131assert_equal(merged_v0_versions_psbt.i[0].map[unknown_key], unknown_value)1132assert_equal(merged_v0_versions_psbt.i[0].map[unknown_key2], unknown_value)1134# BIP 174 Test Vectors1136# Open the data file1137with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_psbt.json')) as f:1138d = json.load(f)1139invalids = d['invalid']1140invalid_with_msgs = d["invalid_with_msg"]1141valids = d['valid']1142creators = d['creator']1143signers = d['signer']1144combiners = d['combiner']1145finalizers = d['finalizer']1146extractors = d['extractor']1148# Invalid PSBTs1149for invalid in invalids:1150assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decodepsbt, invalid)1151for invalid in invalid_with_msgs:1152psbt, msg = invalid1153assert_raises_rpc_error(-22, f"TX decode failed {msg}", self.nodes[0].decodepsbt, psbt)1155# Valid PSBTs1156for valid in valids:1157self.nodes[0].decodepsbt(valid)1159# Creator Tests1160for creator in creators:1161created_tx = self.nodes[0].createpsbt(inputs=creator['inputs'], outputs=creator['outputs'], replaceable=False, psbt_version=creator['version'])1162assert_equal(created_tx, creator['result'])1164# Signer tests1165for i, signer in enumerate(signers):1166self.nodes[2].createwallet(wallet_name="wallet{}".format(i))1167wrpc = self.nodes[2].get_wallet_rpc("wallet{}".format(i))1168for key in signer['privkeys']:1169wallet_importprivkey(wrpc, key, "now")1170signed_tx = wrpc.walletprocesspsbt(signer['psbt'], True, "ALL")['psbt']1171assert_equal(signed_tx, signer['result'])1173# Combiner test1174for combiner in combiners:1175combined = self.nodes[2].combinepsbt(combiner['combine'])1176assert_equal(combined, combiner['result'])1178# Empty combiner test1179assert_raises_rpc_error(-8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, [])1181# Finalizer test1182for finalizer in finalizers:1183finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt']1184assert_equal(finalized, finalizer['result'])1186# Extractor test1187for extractor in extractors:1188extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex']1189assert_equal(extracted, extractor['result'])1191# Unload extra wallets1192for i, signer in enumerate(signers):1193self.nodes[2].unloadwallet("wallet{}".format(i))1195self.test_utxo_conversion()1196self.test_psbt_incomplete_after_invalid_modification()1198self.test_input_confs_control()1200# Test that psbts with p2pkh outputs are created properly1201p2pkh = self.nodes[0].getnewaddress(address_type='legacy')1202psbt = self.nodes[1].walletcreatefundedpsbt(inputs=[], outputs=[{p2pkh : 1}], bip32derivs=True)1203self.nodes[0].decodepsbt(psbt['psbt'])1205# Test decoding error: invalid base641206assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")1208# Send to all types of addresses1209addr1 = self.nodes[1].getnewaddress("", "bech32")1210addr2 = self.nodes[1].getnewaddress("", "legacy")1211addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")1212utxo1, utxo2, utxo3 = self.create_outpoints(self.nodes[1], outputs=[{addr1: 11}, {addr2: 11}, {addr3: 11}])1213self.sync_all()1215psbt_v2_required_keys = ["previous_txid", "previous_vout", "sequence"]1217def test_psbt_input_keys(psbt_input, keys):1218"""Check that the psbt input has only the expected keys."""1219keys.extend(["previous_txid", "previous_vout", "sequence"])1220assert_equal(set(keys), set(psbt_input.keys()))1222# Create a PSBT. None of the inputs are filled initially1223psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})1224decoded = self.nodes[1].decodepsbt(psbt)1225test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys)1226test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys)1227test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys)1229# Update a PSBT with UTXOs from the node1230# Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness1231updated = self.nodes[1].utxoupdatepsbt(psbt)1232decoded = self.nodes[1].decodepsbt(updated)1233test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys + ['witness_utxo', 'non_witness_utxo'])1234test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys + ['non_witness_utxo'])1235test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys + ['non_witness_utxo'])1237# Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in1238descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]1239updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)1240decoded = self.nodes[1].decodepsbt(updated)1241test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys + ['witness_utxo', 'non_witness_utxo', 'bip32_derivs'])1242test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys + ['non_witness_utxo', 'bip32_derivs'])1243test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys + ['non_witness_utxo', 'witness_utxo', 'bip32_derivs', 'redeem_script'])1245# Cannot join PSBTv2s1246psbt1 = self.nodes[1].createpsbt(inputs=[utxo1], outputs={self.nodes[0].getnewaddress():Decimal('10.999')}, psbt_version=0)1247psbt2 = self.nodes[1].createpsbt(inputs=[utxo1], outputs={self.nodes[0].getnewaddress():Decimal('10.999')}, psbt_version=2)1248assert_raises_rpc_error(-8, "joinpsbts only operates on version 0 PSBTs", self.nodes[1].joinpsbts, [psbt1, psbt2])1250# Two PSBTs with a common input should not be joinable
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.