@@ -23,6 +23,7 @@ static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
23
23
24
24
// Global types
25
25
static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00 ;
26
+ static constexpr uint8_t PSBT_GLOBAL_XPUB = 0x01 ;
26
27
static constexpr uint8_t PSBT_GLOBAL_VERSION = 0xFB ;
27
28
static constexpr uint8_t PSBT_GLOBAL_PROPRIETARY = 0xFC ;
28
29
@@ -551,6 +552,9 @@ struct PSBTOutput
551
552
struct PartiallySignedTransaction
552
553
{
553
554
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;
554
558
std::vector<PSBTInput> inputs;
555
559
std::vector<PSBTOutput> outputs;
556
560
std::map<std::vector<unsigned char >, std::vector<unsigned char >> unknown;
@@ -589,6 +593,18 @@ struct PartiallySignedTransaction
589
593
OverrideStream<Stream> os (&s, s.GetType (), s.GetVersion () | SERIALIZE_TRANSACTION_NO_WITNESS);
590
594
SerializeToVector (os, *tx);
591
595
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
+
592
608
// PSBT version
593
609
if (GetVersion () > 0 ) {
594
610
SerializeToVector (s, CompactSizeWriter (PSBT_GLOBAL_VERSION));
@@ -633,6 +649,9 @@ struct PartiallySignedTransaction
633
649
// Used for duplicate key detection
634
650
std::set<std::vector<unsigned char >> key_lookup;
635
651
652
+ // Track the global xpubs we have already seen. Just for sanity checking
653
+ std::set<CExtPubKey> global_xpubs;
654
+
636
655
// Read global data
637
656
bool found_sep = false ;
638
657
while (!s.empty ()) {
@@ -673,6 +692,36 @@ struct PartiallySignedTransaction
673
692
}
674
693
break ;
675
694
}
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
+ }
676
725
case PSBT_GLOBAL_VERSION:
677
726
{
678
727
if (m_version) {
0 commit comments