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// Copyright (c) 2009-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#ifndef BITCOIN_PSBT_H6#define BITCOIN_PSBT_H8#include <common/types.h>9#include <musig.h>10#include <node/transaction.h>11#include <policy/feerate.h>12#include <primitives/transaction.h>13#include <pubkey.h>14#include <script/keyorigin.h>15#include <script/sign.h>16#include <script/signingprovider.h>17#include <span.h>18#include <streams.h>19#include <uint256.h>20#include <util/result.h>21#include <util/expected.h>23#include <optional>24#include <bitset>26namespace node {27enum class TransactionError;28} // namespace node30using common::PSBTError;32// Magic bytes33inline constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};35// Global types36inline constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;37inline constexpr uint8_t PSBT_GLOBAL_XPUB = 0x01;38inline constexpr uint8_t PSBT_GLOBAL_TX_VERSION = 0x02;39inline constexpr uint8_t PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03;40inline constexpr uint8_t PSBT_GLOBAL_INPUT_COUNT = 0x04;41inline constexpr uint8_t PSBT_GLOBAL_OUTPUT_COUNT = 0x05;42inline constexpr uint8_t PSBT_GLOBAL_TX_MODIFIABLE = 0x06;43inline constexpr uint8_t PSBT_GLOBAL_VERSION = 0xFB;44inline constexpr uint8_t PSBT_GLOBAL_PROPRIETARY = 0xFC;46// Input types47inline constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;48inline constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;49inline constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;50inline constexpr uint8_t PSBT_IN_SIGHASH = 0x03;51inline constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;52inline constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;53inline constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;54inline constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;55inline constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;56inline constexpr uint8_t PSBT_IN_RIPEMD160 = 0x0A;57inline constexpr uint8_t PSBT_IN_SHA256 = 0x0B;58inline constexpr uint8_t PSBT_IN_HASH160 = 0x0C;59inline constexpr uint8_t PSBT_IN_HASH256 = 0x0D;60inline constexpr uint8_t PSBT_IN_PREVIOUS_TXID = 0x0e;61inline constexpr uint8_t PSBT_IN_OUTPUT_INDEX = 0x0f;62inline constexpr uint8_t PSBT_IN_SEQUENCE = 0x10;63inline constexpr uint8_t PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11;64inline constexpr uint8_t PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12;65inline constexpr uint8_t PSBT_IN_TAP_KEY_SIG = 0x13;66inline constexpr uint8_t PSBT_IN_TAP_SCRIPT_SIG = 0x14;67inline constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15;68inline constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16;69inline constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17;70inline constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18;71inline constexpr uint8_t PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a;72inline constexpr uint8_t PSBT_IN_MUSIG2_PUB_NONCE = 0x1b;73inline constexpr uint8_t PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c;74inline constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC;76// Output types77inline constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;78inline constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;79inline constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;80inline constexpr uint8_t PSBT_OUT_AMOUNT = 0x03;81inline constexpr uint8_t PSBT_OUT_SCRIPT = 0x04;82inline constexpr uint8_t PSBT_OUT_TAP_INTERNAL_KEY = 0x05;83inline constexpr uint8_t PSBT_OUT_TAP_TREE = 0x06;84inline constexpr uint8_t PSBT_OUT_TAP_BIP32_DERIVATION = 0x07;85inline constexpr uint8_t PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08;86inline constexpr uint8_t PSBT_OUT_PROPRIETARY = 0xFC;88// The separator is 0x00. Reading this in means that the unserializer can interpret it89// as a 0 length key which indicates that this is the separator. The separator has no value.90inline constexpr uint8_t PSBT_SEPARATOR = 0x00;92// BIP 174 does not specify a maximum file size, but we set a limit anyway93// to prevent reading a stream indefinitely and running out of memory.94inline constexpr std::streamsize MAX_FILE_SIZE_PSBT{100'000'000}; // 100 MB96// PSBT version number97inline constexpr uint32_t PSBT_HIGHEST_VERSION = 2;99/** A structure for PSBT proprietary types */100struct PSBTProprietary101{102uint64_t subtype;103std::vector<unsigned char> identifier;104std::vector<unsigned char> key;105std::vector<unsigned char> value;107bool operator<(const PSBTProprietary &b) const {108return key < b.key;109}110bool operator==(const PSBTProprietary &b) const {111return key == b.key;112}113};115// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream116// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.117template<typename Stream, typename... X>118void SerializeToVector(Stream& s, const X&... args)119{120SizeComputer sizecomp;121SerializeMany(sizecomp, args...);122WriteCompactSize(s, sizecomp.size());123SerializeMany(s, args...);124}126// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments127template<typename Stream, typename... X>128void UnserializeFromVector(Stream& s, X&&... args)129{130size_t expected_size = ReadCompactSize(s);131size_t remaining_before = s.size();132UnserializeMany(s, args...);133size_t remaining_after = s.size();134if (remaining_after + expected_size != remaining_before) {135throw std::ios_base::failure("Size of value was not the stated size");136}137}139// Deserialize bytes of given length from the stream as a KeyOriginInfo140template<typename Stream>141KeyOriginInfo DeserializeKeyOrigin(Stream& s, uint64_t length)142{143// Read in key path144if (length % 4 || length == 0) {145throw std::ios_base::failure("Invalid length for HD key path");146}148KeyOriginInfo hd_keypath;149s >> hd_keypath.fingerprint;150for (unsigned int i = 4; i < length; i += sizeof(uint32_t)) {151uint32_t index;152s >> index;153hd_keypath.path.push_back(index);154}155return hd_keypath;156}158// Deserialize a length prefixed KeyOriginInfo from a stream159template<typename Stream>160void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath)161{162hd_keypath = DeserializeKeyOrigin(s, ReadCompactSize(s));163}165// Deserialize HD keypaths into a map166template<typename Stream>167void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)168{169// Make sure that the key is the size of pubkey + 1170if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {171throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");172}173// Read in the pubkey from key174CPubKey pubkey(key.begin() + 1, key.end());175if (!pubkey.IsFullyValid()) {176throw std::ios_base::failure("Invalid pubkey");177}179KeyOriginInfo keypath;180DeserializeHDKeypath(s, keypath);182// Add to map183hd_keypaths.emplace(pubkey, std::move(keypath));184}186// Serialize a KeyOriginInfo to a stream187template<typename Stream>188void SerializeKeyOrigin(Stream& s, KeyOriginInfo hd_keypath)189{190s << hd_keypath.fingerprint;191for (const auto& path : hd_keypath.path) {192s << path;193}194}196// Serialize a length prefixed KeyOriginInfo to a stream197template<typename Stream>198void SerializeHDKeypath(Stream& s, KeyOriginInfo hd_keypath)199{200WriteCompactSize(s, (hd_keypath.path.size() + 1) * sizeof(uint32_t));201SerializeKeyOrigin(s, hd_keypath);202}204// Serialize HD keypaths to a stream from a map205template<typename Stream>206void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, CompactSizeWriter type)207{208for (const auto& keypath_pair : hd_keypaths) {209if (!keypath_pair.first.IsValid()) {210throw std::ios_base::failure("Invalid CPubKey being serialized");211}212SerializeToVector(s, type, std::span{keypath_pair.first});213SerializeHDKeypath(s, keypath_pair.second);214}215}217// Deserialize a PSBT_{IN/OUT}_MUSIG2_PARTICIPANT_PUBKEYS field218template<typename Stream>219void DeserializeMuSig2ParticipantPubkeys(Stream& s, SpanReader& skey, std::map<CPubKey, std::vector<CPubKey>>& out, std::string context)220{221std::array<unsigned char, CPubKey::COMPRESSED_SIZE> agg_pubkey_bytes;222skey >> std::as_writable_bytes(std::span{agg_pubkey_bytes});223CPubKey agg_pubkey(agg_pubkey_bytes);224if (!agg_pubkey.IsFullyValid()) {225throw std::ios_base::failure(context + " musig2 aggregate pubkey is invalid");226}228std::vector<CPubKey> participants;229std::vector<unsigned char> val;230s >> val;231SpanReader s_val{val};232while (s_val.size() >= CPubKey::COMPRESSED_SIZE) {233std::array<unsigned char, CPubKey::COMPRESSED_SIZE> part_pubkey_bytes;234s_val >> std::as_writable_bytes(std::span{part_pubkey_bytes});235CPubKey participant(part_pubkey_bytes);236if (!participant.IsFullyValid()) {237throw std::ios_base::failure(context + " musig2 participant pubkey is invalid");238}239participants.push_back(participant);240}241if (!s_val.empty()) {242throw std::ios_base::failure(context + " musig2 participants pubkeys value size is not a multiple of 33");243}245out.emplace(agg_pubkey, participants);246}248// Deserialize the MuSig2 participant identifiers from PSBT_MUSIG2_{PUBNONCE/PARTIAL_SIG} fields249// Both fields contain the same data after the type byte - aggregate pubkey | participant pubkey | leaf script hash250template<typename Stream>
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.