@@ -23,6 +23,7 @@ static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
2323
2424// Global types
2525static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00 ;
26+ static constexpr uint8_t PSBT_GLOBAL_XPUB = 0x01 ;
2627static constexpr uint8_t PSBT_GLOBAL_VERSION = 0xFB ;
2728static constexpr uint8_t PSBT_GLOBAL_PROPRIETARY = 0xFC ;
2829
@@ -551,6 +552,9 @@ struct PSBTOutput
551552struct PartiallySignedTransaction
552553{
553554 std::optional<CMutableTransaction> tx;
555+ // We use a vector of CExtPubKey in the event that there happens to be the same KeyOriginInfos for different CExtPubKeys
556+ // Note that this map swaps the key and values from the serialization
557+ std::map<KeyOriginInfo, std::set<CExtPubKey>> m_xpubs;
554558 std::vector<PSBTInput> inputs;
555559 std::vector<PSBTOutput> outputs;
556560 std::map<std::vector<unsigned char >, std::vector<unsigned char >> unknown;
@@ -589,6 +593,18 @@ struct PartiallySignedTransaction
589593 OverrideStream<Stream> os (&s, s.GetType (), s.GetVersion () | SERIALIZE_TRANSACTION_NO_WITNESS);
590594 SerializeToVector (os, *tx);
591595
596+ // Write xpubs
597+ for (const auto & xpub_pair : m_xpubs) {
598+ for (const auto & xpub : xpub_pair.second ) {
599+ unsigned char ser_xpub[BIP32_EXTKEY_WITH_VERSION_SIZE];
600+ xpub.EncodeWithVersion (ser_xpub);
601+ // Note that the serialization swaps the key and value
602+ // The xpub is the key (for uniqueness) while the path is the value
603+ SerializeToVector (s, PSBT_GLOBAL_XPUB, ser_xpub);
604+ SerializeHDKeypath (s, xpub_pair.first );
605+ }
606+ }
607+
592608 // PSBT version
593609 if (GetVersion () > 0 ) {
594610 SerializeToVector (s, CompactSizeWriter (PSBT_GLOBAL_VERSION));
@@ -633,6 +649,9 @@ struct PartiallySignedTransaction
633649 // Used for duplicate key detection
634650 std::set<std::vector<unsigned char >> key_lookup;
635651
652+ // Track the global xpubs we have already seen. Just for sanity checking
653+ std::set<CExtPubKey> global_xpubs;
654+
636655 // Read global data
637656 bool found_sep = false ;
638657 while (!s.empty ()) {
@@ -673,6 +692,36 @@ struct PartiallySignedTransaction
673692 }
674693 break ;
675694 }
695+ case PSBT_GLOBAL_XPUB:
696+ {
697+ if (key.size () != BIP32_EXTKEY_WITH_VERSION_SIZE + 1 ) {
698+ throw std::ios_base::failure (" Size of key was not the expected size for the type global xpub" );
699+ }
700+ // Read in the xpub from key
701+ CExtPubKey xpub;
702+ xpub.DecodeWithVersion (&key.data ()[1 ]);
703+ if (!xpub.pubkey .IsFullyValid ()) {
704+ throw std::ios_base::failure (" Invalid pubkey" );
705+ }
706+ if (global_xpubs.count (xpub) > 0 ) {
707+ throw std::ios_base::failure (" Duplicate key, global xpub already provided" );
708+ }
709+ global_xpubs.insert (xpub);
710+ // Read in the keypath from stream
711+ KeyOriginInfo keypath;
712+ DeserializeHDKeypath (s, keypath);
713+
714+ // Note that we store these swapped to make searches faster.
715+ // Serialization uses xpub -> keypath to enqure key uniqueness
716+ if (m_xpubs.count (keypath) == 0 ) {
717+ // Make a new set to put the xpub in
718+ m_xpubs[keypath] = {xpub};
719+ } else {
720+ // Insert xpub into existing set
721+ m_xpubs[keypath].insert (xpub);
722+ }
723+ break ;
724+ }
676725 case PSBT_GLOBAL_VERSION:
677726 {
678727 if (m_version) {
0 commit comments