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.
1251std::set<PSBTProprietary> m_proprietary;1253uint32_t tx_version;1254std::optional<uint32_t> fallback_locktime;1256uint32_t GetVersion() const;1258/** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the1259* same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */1260[[nodiscard]] bool Merge(const PartiallySignedTransaction& psbt);1261/** Merge the global xpubs of psbt into this, keeping the existing origin for an xpub1262* seen again with a different one, as the serialized records are keyed by xpub. */1263void MergeGlobalXPubs(const PartiallySignedTransaction& psbt);1264bool AddInput(const PSBTInput& psbtin);1265bool AddOutput(const PSBTOutput& psbtout);1266std::optional<uint32_t> ComputeTimeLock() const;1267std::optional<CMutableTransaction> GetUnsignedTx() const;1268std::optional<Txid> GetUniqueID() const;1269explicit PartiallySignedTransaction(const CMutableTransaction& tx, uint32_t version = 2);1271template <typename Stream>1272inline void Serialize(Stream& s) const {1274// magic bytes1275s << PSBT_MAGIC_BYTES;1277if (GetVersion() < 2) {1278// unsigned tx flag1279SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_UNSIGNED_TX));1281// Write serialized tx to a stream1282SerializeToVector(s, TX_NO_WITNESS(*GetUnsignedTx()));1283}1285// Write xpubs1286for (const auto& xpub_pair : m_xpubs) {1287for (const auto& xpub : xpub_pair.second) {1288unsigned char ser_xpub[BIP32_EXTKEY_WITH_VERSION_SIZE];1289xpub.EncodeWithVersion(ser_xpub);1290// Note that the serialization swaps the key and value1291// The xpub is the key (for uniqueness) while the path is the value1292SerializeToVector(s, PSBT_GLOBAL_XPUB, ser_xpub);1293SerializeHDKeypath(s, xpub_pair.first);1294}1295}1297if (GetVersion() >= 2) {1298// Write PSBTv2 tx version, locktime, counts, etc.1299SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_TX_VERSION));1300SerializeToVector(s, tx_version);1301if (fallback_locktime != std::nullopt) {1302SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_FALLBACK_LOCKTIME));1303SerializeToVector(s, *fallback_locktime);1304}1306SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_INPUT_COUNT));1307SerializeToVector(s, CompactSizeWriter(inputs.size()));1308SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_OUTPUT_COUNT));1309SerializeToVector(s, CompactSizeWriter(outputs.size()));1311if (m_tx_modifiable != std::nullopt) {1312SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_TX_MODIFIABLE));1313SerializeToVector(s, static_cast<uint8_t>(m_tx_modifiable->to_ulong()));1314}1315}1317// PSBT version1318if (GetVersion() > 0) {1319SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_VERSION));1320SerializeToVector(s, *m_version);1321}1323// Write proprietary things1324for (const auto& entry : m_proprietary) {1325s << entry.key;1326s << entry.value;1327}1329// Write the unknown things1330for (auto& entry : unknown) {1331s << entry.first;1332s << entry.second;1333}1335// Separator1336s << PSBT_SEPARATOR;1338// Write inputs1339for (const PSBTInput& input : inputs) {1340s << input;1341}1342// Write outputs1343for (const PSBTOutput& output : outputs) {1344s << output;1345}1346}1349template <typename Stream>1350inline void Unserialize(Stream& s) {1351// Read the magic bytes1352uint8_t magic[5];1353s >> magic;1354if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {1355throw std::ios_base::failure("Invalid PSBT magic bytes");1356}1358// Used for duplicate key detection1359std::set<std::vector<unsigned char>> key_lookup;1361// Read global data1362bool found_sep = false;1363std::optional<CMutableTransaction> tx;1364uint64_t input_count = 0;1365uint64_t output_count = 0;1366bool found_input_count = false;1367bool found_output_count = false;1368bool found_tx_version = false;1369bool found_fallback_locktime = false;1370while(!s.empty()) {1371// Read the key of format "<keylen><keytype><keydata>" after which1372// "key" will contain "<keytype><keydata>"1373std::vector<unsigned char> key;1374s >> key;1376// the key is empty if that was actually a separator byte1377// This is a special case for key lengths 0 as those are not allowed (except for separator)1378if (key.empty()) {1379found_sep = true;1380break;1381}1383// Duplicate keys are not permitted1384if (!key_lookup.emplace(key).second) {1385throw std::ios_base::failure(tfm::format("Duplicate Key, global key \"%s\" already provided", HexStr(key)));1386}1388// "skey" is used so that "key" is unchanged after reading keytype below1389SpanReader skey{key};1390// keytype is of the format compact size uint at the beginning of "key"1391uint64_t type = ReadCompactSize(skey);1393// Do stuff based on keytype "type", i.e., key checks, reading values of the1394// format "<valuelen><valuedata>" from the stream "s", and value checks1395switch(type) {1396case PSBT_GLOBAL_UNSIGNED_TX:1397{1398ExpectedKeySize("Global Unsigned TX", key, 1);1399// Set the stream to serialize with non-witness since this should always be non-witness1400tx.emplace();1401UnserializeFromVector(s, TX_NO_WITNESS(*tx));1402// Make sure that all scriptSigs and scriptWitnesses are empty1403for (const CTxIn& txin : tx->vin) {1404if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {1405throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");1406}1407}1408tx_version = tx->version;1409fallback_locktime = tx->nLockTime;1410// Set the input and output counts1411input_count = tx->vin.size();1412output_count = tx->vout.size();1413break;1414}1415case PSBT_GLOBAL_TX_VERSION:1416{1417ExpectedKeySize("Global Transaction Version", key, 1);1418UnserializeFromVector(s, tx_version);1419found_tx_version = true;1420break;1421}1422case PSBT_GLOBAL_FALLBACK_LOCKTIME:1423{1424ExpectedKeySize("Global Fallback Locktime", key, 1);1425fallback_locktime.emplace();1426UnserializeFromVector(s, *fallback_locktime);1427found_fallback_locktime = true;1428break;1429}1430case PSBT_GLOBAL_INPUT_COUNT:1431{1432ExpectedKeySize("Global Input Count", key, 1);1433CompactSizeReader reader(input_count);1434UnserializeFromVector(s, reader);1435found_input_count = true;1436break;1437}1438case PSBT_GLOBAL_OUTPUT_COUNT:1439{1440ExpectedKeySize("Global Output Count", key, 1);1441CompactSizeReader reader(output_count);1442UnserializeFromVector(s, reader);1443found_output_count = true;1444break;1445}1446case PSBT_GLOBAL_TX_MODIFIABLE:1447{1448ExpectedKeySize("Global TX Modifiable Flags", key, 1);1449uint8_t tx_mod;1450UnserializeFromVector(s, tx_mod);1451m_tx_modifiable.emplace(tx_mod);1452break;1453}1454case PSBT_GLOBAL_XPUB:1455{1456ExpectedKeySize("Global XPUB", key, BIP32_EXTKEY_WITH_VERSION_SIZE + 1);1457// Read in the xpub from key1458CExtPubKey xpub;1459xpub.DecodeWithVersion(&key.data()[1]);1460if (!xpub.pubkey.IsFullyValid()) {1461throw std::ios_base::failure("Invalid pubkey");1462}1463// Read in the keypath from stream1464KeyOriginInfo keypath;1465DeserializeHDKeypath(s, keypath);1467// Note that we store these swapped to make searches faster.1468// Serialization uses xpub -> keypath to enqure key uniqueness1469if (!m_xpubs.contains(keypath)) {1470// Make a new set to put the xpub in1471m_xpubs[keypath] = {xpub};1472} else {1473// Insert xpub into existing set1474m_xpubs[keypath].insert(xpub);1475}1476break;1477}1478case PSBT_GLOBAL_VERSION:1479{1480ExpectedKeySize("Global PSBT Version", key, 1);1481uint32_t v;1482UnserializeFromVector(s, v);1483m_version = v;1484if (*m_version > PSBT_HIGHEST_VERSION) {1485throw std::ios_base::failure("Unsupported version number");1486}1487break;1488}1489case PSBT_GLOBAL_PROPRIETARY:1490{1491PSBTProprietary this_prop;1492skey >> this_prop.identifier;1493this_prop.subtype = ReadCompactSize(skey);1494this_prop.key = key;1496s >> this_prop.value;1497m_proprietary.insert(this_prop);1498break;1499}1500// Unknown stuff
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.