Skip to content

Commit 9038485

Browse files
committed
Implement serializations for PSBT_GLOBAL_XPUB
1 parent c5c63b8 commit 9038485

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

src/psbt.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
2323

2424
// Global types
2525
static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
26+
static constexpr uint8_t PSBT_GLOBAL_XPUB = 0x01;
2627
static constexpr uint8_t PSBT_GLOBAL_VERSION = 0xFB;
2728
static constexpr uint8_t PSBT_GLOBAL_PROPRIETARY = 0xFC;
2829

@@ -551,6 +552,9 @@ struct PSBTOutput
551552
struct 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

Comments
 (0)