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) 2022-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.6import base647import struct9from io import BytesIO11from .util import assert_equal12from .messages import (13CTransaction,14deser_string,15deser_compact_size,16from_binary,17ser_compact_size,18)21# global types22PSBT_GLOBAL_UNSIGNED_TX = 0x0023PSBT_GLOBAL_XPUB = 0x0124PSBT_GLOBAL_TX_VERSION = 0x0225PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x0326PSBT_GLOBAL_INPUT_COUNT = 0x0427PSBT_GLOBAL_OUTPUT_COUNT = 0x0528PSBT_GLOBAL_TX_MODIFIABLE = 0x0629PSBT_GLOBAL_VERSION = 0xfb30PSBT_GLOBAL_PROPRIETARY = 0xfc32# per-input types33PSBT_IN_NON_WITNESS_UTXO = 0x0034PSBT_IN_WITNESS_UTXO = 0x0135PSBT_IN_PARTIAL_SIG = 0x0236PSBT_IN_SIGHASH_TYPE = 0x0337PSBT_IN_REDEEM_SCRIPT = 0x0438PSBT_IN_WITNESS_SCRIPT = 0x0539PSBT_IN_BIP32_DERIVATION = 0x0640PSBT_IN_FINAL_SCRIPTSIG = 0x0741PSBT_IN_FINAL_SCRIPTWITNESS = 0x0842PSBT_IN_POR_COMMITMENT = 0x0943PSBT_IN_RIPEMD160 = 0x0a44PSBT_IN_SHA256 = 0x0b45PSBT_IN_HASH160 = 0x0c46PSBT_IN_HASH256 = 0x0d47PSBT_IN_PREVIOUS_TXID = 0x0e48PSBT_IN_OUTPUT_INDEX = 0x0f49PSBT_IN_SEQUENCE = 0x1050PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x1151PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x1252PSBT_IN_TAP_KEY_SIG = 0x1353PSBT_IN_TAP_SCRIPT_SIG = 0x1454PSBT_IN_TAP_LEAF_SCRIPT = 0x1555PSBT_IN_TAP_BIP32_DERIVATION = 0x1656PSBT_IN_TAP_INTERNAL_KEY = 0x1757PSBT_IN_TAP_MERKLE_ROOT = 0x1858PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a59PSBT_IN_MUSIG2_PUB_NONCE = 0x1b60PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c61PSBT_IN_PROPRIETARY = 0xfc63# per-output types64PSBT_OUT_REDEEM_SCRIPT = 0x0065PSBT_OUT_WITNESS_SCRIPT = 0x0166PSBT_OUT_BIP32_DERIVATION = 0x0267PSBT_OUT_AMOUNT = 0x0368PSBT_OUT_SCRIPT = 0x0469PSBT_OUT_TAP_INTERNAL_KEY = 0x0570PSBT_OUT_TAP_TREE = 0x0671PSBT_OUT_TAP_BIP32_DERIVATION = 0x0772PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x0873PSBT_OUT_PROPRIETARY = 0xfc76class PSBTMap:77"""Class for serializing and deserializing PSBT maps"""79def __init__(self, map=None):80self.map = map if map is not None else {}82def deserialize(self, f):83m = {}84while True:85k = deser_string(f)86if len(k) == 0:87break88v = deser_string(f)89if len(k) == 1:90k = k[0]91assert k not in m92m[k] = v93self.map = m95def serialize(self):96m = b""97for k,v in self.map.items():98if isinstance(k, int) and 0 <= k and k <= 255:99k = bytes([k])100if isinstance(v, list):101assert all(type(elem) is bytes for elem in v)102v = b"".join(v) # simply concatenate the byte-strings w/o size prefixes103m += ser_compact_size(len(k)) + k104m += ser_compact_size(len(v)) + v105m += b"\x00"106return m108class PSBT:109"""Class for serializing and deserializing PSBTs"""111def __init__(self, *, g=None, i=None, o=None):112self.g = g if g is not None else PSBTMap()113self.i = i if i is not None else []114self.o = o if o is not None else []115self.version = None117def deserialize(self, f):118assert_equal(f.read(5), b"psbt\xff")119self.g = from_binary(PSBTMap, f)121self.version = 0122if PSBT_GLOBAL_VERSION in self.g.map:123self.version = struct.unpack("<I", self.g.map[PSBT_GLOBAL_VERSION])[0]124assert self.version in [0, 2]125if self.version == 2:126assert PSBT_GLOBAL_INPUT_COUNT in self.g.map127assert PSBT_GLOBAL_OUTPUT_COUNT in self.g.map128in_count = deser_compact_size(BytesIO(self.g.map[PSBT_GLOBAL_INPUT_COUNT]))129out_count = deser_compact_size(BytesIO(self.g.map[PSBT_GLOBAL_OUTPUT_COUNT]))130else:131assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map132tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX])133in_count = len(tx.vin)134out_count = len(tx.vout)136self.i = [from_binary(PSBTMap, f) for _ in range(in_count)]137self.o = [from_binary(PSBTMap, f) for _ in range(out_count)]138return self140def serialize(self):141assert isinstance(self.g, PSBTMap)142assert isinstance(self.i, list) and all(isinstance(x, PSBTMap) for x in self.i)143assert isinstance(self.o, list) and all(isinstance(x, PSBTMap) for x in self.o)144if self.version is not None and self.version == 2:145self.g.map[PSBT_GLOBAL_INPUT_COUNT] = ser_compact_size(len(self.i))146self.g.map[PSBT_GLOBAL_OUTPUT_COUNT] = ser_compact_size(len(self.o))147if self.version is None or (self.version is not None and self.version == 0):148assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map149tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX])150assert_equal(len(tx.vin), len(self.i))151assert_equal(len(tx.vout), len(self.o))153psbt = [x.serialize() for x in [self.g] + self.i + self.o]154return b"psbt\xff" + b"".join(psbt)156def make_blank(self):157"""158Remove all fields except for required fields depending on version159"""160if self.version == 0:161for m in self.i + self.o:162m.map.clear()164self.g = PSBTMap(map={PSBT_GLOBAL_UNSIGNED_TX: self.g.map[PSBT_GLOBAL_UNSIGNED_TX]})165elif self.version == 2:166self.g = PSBTMap(map={167PSBT_GLOBAL_TX_VERSION: self.g.map[PSBT_GLOBAL_TX_VERSION],168PSBT_GLOBAL_INPUT_COUNT: self.g.map[PSBT_GLOBAL_INPUT_COUNT],169PSBT_GLOBAL_OUTPUT_COUNT: self.g.map[PSBT_GLOBAL_OUTPUT_COUNT],170PSBT_GLOBAL_VERSION: self.g.map[PSBT_GLOBAL_VERSION],171PSBT_GLOBAL_FALLBACK_LOCKTIME: self.g.map[PSBT_GLOBAL_FALLBACK_LOCKTIME],172})174new_i = []175for m in self.i:176new_i.append(PSBTMap(map={177PSBT_IN_PREVIOUS_TXID: m.map[PSBT_IN_PREVIOUS_TXID],178PSBT_IN_OUTPUT_INDEX: m.map[PSBT_IN_OUTPUT_INDEX],179}))180self.i = new_i182new_o = []183for m in self.o:184new_o.append(PSBTMap(map={185PSBT_OUT_SCRIPT: m.map[PSBT_OUT_SCRIPT],186PSBT_OUT_AMOUNT: m.map[PSBT_OUT_AMOUNT],187}))188self.o = new_o189else:190assert False192def to_base64(self):193return base64.b64encode(self.serialize()).decode("utf8")195@classmethod196def from_base64(cls, b64psbt):197return from_binary(cls, base64.b64decode(b64psbt))
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.