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.
1001// Write amount and spk1002SerializeToVector(s, CompactSizeWriter(PSBT_OUT_AMOUNT));1003SerializeToVector(s, amount);1005SerializeToVector(s, CompactSizeWriter(PSBT_OUT_SCRIPT));1006s << script;1007}1009// Write proprietary things1010for (const auto& entry : m_proprietary) {1011s << entry.key;1012s << entry.value;1013}1015// Write taproot internal key1016if (!m_tap_internal_key.IsNull()) {1017SerializeToVector(s, PSBT_OUT_TAP_INTERNAL_KEY);1018s << ToByteVector(m_tap_internal_key);1019}1021// Write taproot tree1022if (!m_tap_tree.empty()) {1023SerializeToVector(s, PSBT_OUT_TAP_TREE);1024std::vector<unsigned char> value;1025VectorWriter s_value{value, 0};1026for (const auto& [depth, leaf_ver, script] : m_tap_tree) {1027s_value << depth;1028s_value << leaf_ver;1029s_value << script;1030}1031s << value;1032}1034// Write taproot bip32 keypaths1035for (const auto& [xonly, leaf] : m_tap_bip32_paths) {1036const auto& [leaf_hashes, origin] = leaf;1037SerializeToVector(s, PSBT_OUT_TAP_BIP32_DERIVATION, xonly);1038std::vector<unsigned char> value;1039VectorWriter s_value{value, 0};1040s_value << leaf_hashes;1041SerializeKeyOrigin(s_value, origin);1042s << value;1043}1045// Write MuSig2 Participants1046for (const auto& [agg_pubkey, part_pubs] : m_musig2_participants) {1047SerializeToVector(s, CompactSizeWriter(PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS), std::span{agg_pubkey});1048std::vector<unsigned char> value;1049VectorWriter s_value{value, 0};1050for (auto& pk : part_pubs) {1051s_value << std::span{pk};1052}1053s << value;1054}1056// Write unknown things1057for (auto& entry : unknown) {1058s << entry.first;1059s << entry.second;1060}1062s << PSBT_SEPARATOR;1063}1066template <typename Stream>1067inline void Unserialize(Stream& s) {1068// Used for duplicate key detection1069std::set<std::vector<unsigned char>> key_lookup;1070// Cache whether PSBTv2 required fields are found1071bool found_amount = false;1072bool found_script = false;1074// Read loop1075bool found_sep = false;1076while(!s.empty()) {1077// Read the key of format "<keylen><keytype><keydata>" after which1078// "key" will contain "<keytype><keydata>"1079std::vector<unsigned char> key;1080s >> key;1082// the key is empty if that was actually a separator byte1083// This is a special case for key lengths 0 as those are not allowed (except for separator)1084if (key.empty()) {1085found_sep = true;1086break;1087}1089// Duplicate keys are not permitted1090if (!key_lookup.emplace(key).second) {1091throw std::ios_base::failure(tfm::format("Duplicate Key, output key \"%s\" already provided", HexStr(key)));1092}1094// "skey" is used so that "key" is unchanged after reading keytype below1095SpanReader skey{key};1096// keytype is of the format compact size uint at the beginning of "key"1097uint64_t type = ReadCompactSize(skey);1099// Do stuff based on keytype "type", i.e., key checks, reading values of the1100// format "<valuelen><valuedata>" from the stream "s", and value checks1101switch(type) {1102case PSBT_OUT_REDEEMSCRIPT:1103{1104ExpectedKeySize("Output redeemScript", key, 1);1105s >> redeem_script;1106break;1107}1108case PSBT_OUT_WITNESSSCRIPT:1109{1110ExpectedKeySize("Output witnessScript", key, 1);1111s >> witness_script;1112break;1113}1114case PSBT_OUT_BIP32_DERIVATION:1115{1116DeserializeHDKeypaths(s, key, hd_keypaths);1117break;1118}1119case PSBT_OUT_AMOUNT:1120{1121ExpectedKeySize("Output Amount", key, 1);1122if (m_psbt_version < 2) {1123throw std::ios_base::failure("Output amount is not allowed in PSBTv0");1124}1125UnserializeFromVector(s, amount);1126found_amount = true;1127break;1128}1129case PSBT_OUT_SCRIPT:1130{1131ExpectedKeySize("Output Script", key, 1);1132if (m_psbt_version < 2) {1133throw std::ios_base::failure("Output script is not allowed in PSBTv0");1134}1135s >> script;1136found_script = true;1137break;1138}1139case PSBT_OUT_TAP_INTERNAL_KEY:1140{1141ExpectedKeySize("Output Taproot Internal Key", key, 1);1142UnserializeFromVector(s, m_tap_internal_key);1143break;1144}1145case PSBT_OUT_TAP_TREE:1146{1147ExpectedKeySize("Output Taproot Tree Key", key, 1);1148std::vector<unsigned char> tree_v;1149s >> tree_v;1150SpanReader s_tree{tree_v};1151if (s_tree.empty()) {1152throw std::ios_base::failure("Output Taproot tree must not be empty");1153}1154TaprootBuilder builder;1155while (!s_tree.empty()) {1156uint8_t depth;1157uint8_t leaf_ver;1158std::vector<unsigned char> script;1159s_tree >> depth;1160s_tree >> leaf_ver;1161s_tree >> script;1162if (depth > TAPROOT_CONTROL_MAX_NODE_COUNT) {1163throw std::ios_base::failure("Output Taproot tree has as leaf greater than Taproot maximum depth");1164}1165if ((leaf_ver & ~TAPROOT_LEAF_MASK) != 0) {1166throw std::ios_base::failure("Output Taproot tree has a leaf with an invalid leaf version");1167}1168m_tap_tree.emplace_back(depth, leaf_ver, script);1169builder.Add((int)depth, script, (int)leaf_ver, /*track=*/true);1170}1171if (!builder.IsComplete()) {1172throw std::ios_base::failure("Output Taproot tree is malformed");1173}1174break;1175}1176case PSBT_OUT_TAP_BIP32_DERIVATION:1177{1178ExpectedKeySize("Output Taproot BIP32 Keypath", key, 33);1179XOnlyPubKey xonly(uint256(std::span<uint8_t>(key).last(32)));1180std::set<uint256> leaf_hashes;1181uint64_t value_len = ReadCompactSize(s);1182size_t before_hashes = s.size();1183s >> leaf_hashes;1184size_t after_hashes = s.size();1185size_t hashes_len = before_hashes - after_hashes;1186if (hashes_len > value_len) {1187throw std::ios_base::failure("Output Taproot BIP32 keypath has an invalid length");1188}1189size_t origin_len = value_len - hashes_len;1190m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len)));1191break;1192}1193case PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS:1194{1195ExpectedKeySize("Output MuSig2 Participants Pubkeys", key, CPubKey::COMPRESSED_SIZE + 1);1196DeserializeMuSig2ParticipantPubkeys(s, skey, m_musig2_participants, std::string{"Output"});1197break;1198}1199case PSBT_OUT_PROPRIETARY:1200{1201PSBTProprietary this_prop;1202skey >> this_prop.identifier;1203this_prop.subtype = ReadCompactSize(skey);1204this_prop.key = key;1206s >> this_prop.value;1207m_proprietary.insert(this_prop);1208break;1209}1210// Unknown stuff1211default: {1212// Read in the value1213std::vector<unsigned char> val_bytes;1214s >> val_bytes;1215unknown.emplace(std::move(key), std::move(val_bytes));1216break;1217}1218}1219}1221if (!found_sep) {1222throw std::ios_base::failure("Separator is missing at the end of an output map");1223}1225// Make sure required PSBTv2 fields are present1226if (m_psbt_version >= 2) {1227if (!found_amount) {1228throw std::ios_base::failure("Output amount is required in PSBTv2");1229}1230if (!found_script) {1231throw std::ios_base::failure("Output script is required in PSBTv2");1232}1233}1234}1235};1237/** A version of CTransaction with the PSBT format*/1238class PartiallySignedTransaction1239{1240private:1241std::optional<uint32_t> m_version;1243public:1244// We use a vector of CExtPubKey in the event that there happens to be the same KeyOriginInfos for different CExtPubKeys1245// Note that this map swaps the key and values from the serialization1246std::map<KeyOriginInfo, std::set<CExtPubKey>> m_xpubs;1247std::optional<std::bitset<8>> m_tx_modifiable;1248std::vector<PSBTInput> inputs;1249std::vector<PSBTOutput> outputs;1250std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
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.