@@ -41,7 +41,7 @@ struct PubkeyProvider
41
41
virtual ~PubkeyProvider () = default ;
42
42
43
43
/* * Derive a public key. */
44
- virtual bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& out ) const = 0;
44
+ virtual bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info ) const = 0;
45
45
46
46
/* * Whether this represent multiple public keys at different positions. */
47
47
virtual bool IsRange () const = 0;
@@ -56,16 +56,50 @@ struct PubkeyProvider
56
56
virtual bool ToPrivateString (const SigningProvider& arg, std::string& out) const = 0;
57
57
};
58
58
59
+ class OriginPubkeyProvider final : public PubkeyProvider
60
+ {
61
+ KeyOriginInfo m_origin;
62
+ std::unique_ptr<PubkeyProvider> m_provider;
63
+
64
+ std::string OriginString () const
65
+ {
66
+ return HexStr (std::begin (m_origin.fingerprint ), std::end (m_origin.fingerprint )) + FormatKeyPath (m_origin.path );
67
+ }
68
+
69
+ public:
70
+ OriginPubkeyProvider (KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : m_origin(std::move(info)), m_provider(std::move(provider)) {}
71
+ bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
72
+ {
73
+ if (!m_provider->GetPubKey (pos, arg, key, info)) return false ;
74
+ std::copy (std::begin (m_origin.fingerprint ), std::end (m_origin.fingerprint ), info.fingerprint );
75
+ info.path .insert (info.path .begin (), m_origin.path .begin (), m_origin.path .end ());
76
+ return true ;
77
+ }
78
+ bool IsRange () const override { return m_provider->IsRange (); }
79
+ size_t GetSize () const override { return m_provider->GetSize (); }
80
+ std::string ToString () const override { return " [" + OriginString () + " ]" + m_provider->ToString (); }
81
+ bool ToPrivateString (const SigningProvider& arg, std::string& ret) const override
82
+ {
83
+ std::string sub;
84
+ if (!m_provider->ToPrivateString (arg, sub)) return false ;
85
+ ret = " [" + OriginString () + " ]" + std::move (sub);
86
+ return true ;
87
+ }
88
+ };
89
+
59
90
/* * An object representing a parsed constant public key in a descriptor. */
60
91
class ConstPubkeyProvider final : public PubkeyProvider
61
92
{
62
93
CPubKey m_pubkey;
63
94
64
95
public:
65
96
ConstPubkeyProvider (const CPubKey& pubkey) : m_pubkey(pubkey) {}
66
- bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& out ) const override
97
+ bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info ) const override
67
98
{
68
- out = m_pubkey;
99
+ key = m_pubkey;
100
+ info.path .clear ();
101
+ CKeyID keyid = m_pubkey.GetID ();
102
+ std::copy (keyid.begin (), keyid.begin () + sizeof (info.fingerprint ), info.fingerprint );
69
103
return true ;
70
104
}
71
105
bool IsRange () const override { return false ; }
@@ -98,7 +132,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
98
132
CKey key;
99
133
if (!arg.GetKey (m_extkey.pubkey .GetID (), key)) return false ;
100
134
ret.nDepth = m_extkey.nDepth ;
101
- std::copy (m_extkey.vchFingerprint , m_extkey.vchFingerprint + 4 , ret.vchFingerprint );
135
+ std::copy (m_extkey.vchFingerprint , m_extkey.vchFingerprint + sizeof (ret. vchFingerprint ) , ret.vchFingerprint );
102
136
ret.nChild = m_extkey.nChild ;
103
137
ret.chaincode = m_extkey.chaincode ;
104
138
ret.key = key;
@@ -118,27 +152,32 @@ class BIP32PubkeyProvider final : public PubkeyProvider
118
152
BIP32PubkeyProvider (const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
119
153
bool IsRange () const override { return m_derive != DeriveType::NO; }
120
154
size_t GetSize () const override { return 33 ; }
121
- bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& out ) const override
155
+ bool GetPubKey (int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info ) const override
122
156
{
123
157
if (IsHardened ()) {
124
- CExtKey key ;
125
- if (!GetExtKey (arg, key )) return false ;
158
+ CExtKey extkey ;
159
+ if (!GetExtKey (arg, extkey )) return false ;
126
160
for (auto entry : m_path) {
127
- key .Derive (key , entry);
161
+ extkey .Derive (extkey , entry);
128
162
}
129
- if (m_derive == DeriveType::UNHARDENED) key .Derive (key , pos);
130
- if (m_derive == DeriveType::HARDENED) key .Derive (key , pos | 0x80000000UL );
131
- out = key .Neuter ().pubkey ;
163
+ if (m_derive == DeriveType::UNHARDENED) extkey .Derive (extkey , pos);
164
+ if (m_derive == DeriveType::HARDENED) extkey .Derive (extkey , pos | 0x80000000UL );
165
+ key = extkey .Neuter ().pubkey ;
132
166
} else {
133
167
// TODO: optimize by caching
134
- CExtPubKey key = m_extkey;
168
+ CExtPubKey extkey = m_extkey;
135
169
for (auto entry : m_path) {
136
- key .Derive (key , entry);
170
+ extkey .Derive (extkey , entry);
137
171
}
138
- if (m_derive == DeriveType::UNHARDENED) key .Derive (key , pos);
172
+ if (m_derive == DeriveType::UNHARDENED) extkey .Derive (extkey , pos);
139
173
assert (m_derive != DeriveType::HARDENED);
140
- out = key .pubkey ;
174
+ key = extkey .pubkey ;
141
175
}
176
+ CKeyID keyid = m_extkey.pubkey .GetID ();
177
+ std::copy (keyid.begin (), keyid.begin () + sizeof (info.fingerprint ), info.fingerprint );
178
+ info.path = m_path;
179
+ if (m_derive == DeriveType::UNHARDENED) info.path .push_back ((uint32_t )pos);
180
+ if (m_derive == DeriveType::HARDENED) info.path .push_back (((uint32_t )pos) | 0x80000000L );
142
181
return true ;
143
182
}
144
183
std::string ToString () const override
@@ -221,9 +260,11 @@ class SingleKeyDescriptor final : public Descriptor
221
260
bool Expand (int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
222
261
{
223
262
CPubKey key;
224
- if (!m_provider->GetPubKey (pos, arg, key)) return false ;
263
+ KeyOriginInfo info;
264
+ if (!m_provider->GetPubKey (pos, arg, key, info)) return false ;
225
265
output_scripts = std::vector<CScript>{m_script_fn (key)};
226
- out.pubkeys .emplace (key.GetID (), std::move (key));
266
+ out.origins .emplace (key.GetID (), std::move (info));
267
+ out.pubkeys .emplace (key.GetID (), key);
227
268
return true ;
228
269
}
229
270
};
@@ -272,15 +313,19 @@ class MultisigDescriptor : public Descriptor
272
313
273
314
bool Expand (int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
274
315
{
275
- std::vector<CPubKey> pubkeys;
276
- pubkeys.reserve (m_providers.size ());
316
+ std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
317
+ entries.reserve (m_providers.size ());
318
+ // Construct temporary data in `entries`, to avoid producing output in case of failure.
277
319
for (const auto & p : m_providers) {
278
- CPubKey key;
279
- if (!p->GetPubKey (pos, arg, key)) return false ;
280
- pubkeys.push_back (key);
320
+ entries.emplace_back ();
321
+ if (!p->GetPubKey (pos, arg, entries.back ().first , entries.back ().second )) return false ;
281
322
}
282
- for (const CPubKey& key : pubkeys) {
283
- out.pubkeys .emplace (key.GetID (), std::move (key));
323
+ std::vector<CPubKey> pubkeys;
324
+ pubkeys.reserve (entries.size ());
325
+ for (auto & entry : entries) {
326
+ pubkeys.push_back (entry.first );
327
+ out.origins .emplace (entry.first .GetID (), std::move (entry.second ));
328
+ out.pubkeys .emplace (entry.first .GetID (), entry.first );
284
329
}
285
330
output_scripts = std::vector<CScript>{GetScriptForMultisig (m_threshold, pubkeys)};
286
331
return true ;
@@ -343,13 +388,15 @@ class ComboDescriptor final : public Descriptor
343
388
bool Expand (int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
344
389
{
345
390
CPubKey key;
346
- if (!m_provider->GetPubKey (pos, arg, key)) return false ;
391
+ KeyOriginInfo info;
392
+ if (!m_provider->GetPubKey (pos, arg, key, info)) return false ;
347
393
CKeyID keyid = key.GetID ();
348
394
{
349
395
CScript p2pk = GetScriptForRawPubKey (key);
350
396
CScript p2pkh = GetScriptForDestination (keyid);
351
397
output_scripts = std::vector<CScript>{std::move (p2pk), std::move (p2pkh)};
352
398
out.pubkeys .emplace (keyid, key);
399
+ out.origins .emplace (keyid, std::move (info));
353
400
}
354
401
if (key.IsCompressed ()) {
355
402
CScript p2wpkh = GetScriptForDestination (WitnessV0KeyHash (keyid));
@@ -447,7 +494,8 @@ bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
447
494
return true ;
448
495
}
449
496
450
- std::unique_ptr<PubkeyProvider> ParsePubkey (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out)
497
+ /* * Parse a public key that excludes origin information. */
498
+ std::unique_ptr<PubkeyProvider> ParsePubkeyInner (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out)
451
499
{
452
500
auto split = Split (sp, ' /' );
453
501
std::string str (split[0 ].begin (), split[0 ].end ());
@@ -484,6 +532,28 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool per
484
532
return MakeUnique<BIP32PubkeyProvider>(extpubkey, std::move (path), type);
485
533
}
486
534
535
+ /* * Parse a public key including origin information (if enabled). */
536
+ std::unique_ptr<PubkeyProvider> ParsePubkey (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out)
537
+ {
538
+ auto origin_split = Split (sp, ' ]' );
539
+ if (origin_split.size () > 2 ) return nullptr ;
540
+ if (origin_split.size () == 1 ) return ParsePubkeyInner (origin_split[0 ], permit_uncompressed, out);
541
+ if (origin_split[0 ].size () < 1 || origin_split[0 ][0 ] != ' [' ) return nullptr ;
542
+ auto slash_split = Split (origin_split[0 ].subspan (1 ), ' /' );
543
+ if (slash_split[0 ].size () != 8 ) return nullptr ;
544
+ std::string fpr_hex = std::string (slash_split[0 ].begin (), slash_split[0 ].end ());
545
+ if (!IsHex (fpr_hex)) return nullptr ;
546
+ auto fpr_bytes = ParseHex (fpr_hex);
547
+ KeyOriginInfo info;
548
+ static_assert (sizeof (info.fingerprint ) == 4 , " Fingerprint must be 4 bytes" );
549
+ assert (fpr_bytes.size () == 4 );
550
+ std::copy (fpr_bytes.begin (), fpr_bytes.end (), info.fingerprint );
551
+ if (!ParseKeyPath (slash_split, info.path )) return nullptr ;
552
+ auto provider = ParsePubkeyInner (origin_split[1 ], permit_uncompressed, out);
553
+ if (!provider) return nullptr ;
554
+ return MakeUnique<OriginPubkeyProvider>(std::move (info), std::move (provider));
555
+ }
556
+
487
557
/* * Parse a script in a particular context. */
488
558
std::unique_ptr<Descriptor> ParseScript (Span<const char >& sp, ParseScriptContext ctx, FlatSigningProvider& out)
489
559
{
0 commit comments