@@ -179,6 +179,9 @@ struct PubkeyProvider
179
179
/* * Get the descriptor string form including private data (if available in arg). */
180
180
virtual bool ToPrivateString (const SigningProvider& arg, std::string& out) const = 0;
181
181
182
+ /* * Get the descriptor string form with the xpub at the last hardened derivation */
183
+ virtual bool ToNormalizedString (const SigningProvider& arg, std::string& out, bool priv) const = 0;
184
+
182
185
/* * Derive a private key, if private data is available in arg. */
183
186
virtual bool GetPrivKey (int pos, const SigningProvider& arg, CKey& key) const = 0;
184
187
};
@@ -212,6 +215,21 @@ class OriginPubkeyProvider final : public PubkeyProvider
212
215
ret = " [" + OriginString () + " ]" + std::move (sub);
213
216
return true ;
214
217
}
218
+ bool ToNormalizedString (const SigningProvider& arg, std::string& ret, bool priv) const override
219
+ {
220
+ std::string sub;
221
+ if (!m_provider->ToNormalizedString (arg, sub, priv)) return false ;
222
+ // If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider
223
+ // In that case, we need to strip out the leading square bracket and fingerprint from the substring,
224
+ // and append that to our own origin string.
225
+ if (sub[0 ] == ' [' ) {
226
+ sub = sub.substr (9 );
227
+ ret = " [" + OriginString () + std::move (sub);
228
+ } else {
229
+ ret = " [" + OriginString () + " ]" + std::move (sub);
230
+ }
231
+ return true ;
232
+ }
215
233
bool GetPrivKey (int pos, const SigningProvider& arg, CKey& key) const override
216
234
{
217
235
return m_provider->GetPrivKey (pos, arg, key);
@@ -243,6 +261,12 @@ class ConstPubkeyProvider final : public PubkeyProvider
243
261
ret = EncodeSecret (key);
244
262
return true ;
245
263
}
264
+ bool ToNormalizedString (const SigningProvider& arg, std::string& ret, bool priv) const override
265
+ {
266
+ if (priv) return ToPrivateString (arg, ret);
267
+ ret = ToString ();
268
+ return true ;
269
+ }
246
270
bool GetPrivKey (int pos, const SigningProvider& arg, CKey& key) const override
247
271
{
248
272
return arg.GetKey (m_pubkey.GetID (), key);
@@ -386,6 +410,56 @@ class BIP32PubkeyProvider final : public PubkeyProvider
386
410
}
387
411
return true ;
388
412
}
413
+ bool ToNormalizedString (const SigningProvider& arg, std::string& out, bool priv) const override
414
+ {
415
+ // For hardened derivation type, just return the typical string, nothing to normalize
416
+ if (m_derive == DeriveType::HARDENED) {
417
+ if (priv) return ToPrivateString (arg, out);
418
+ out = ToString ();
419
+ return true ;
420
+ }
421
+ // Step backwards to find the last hardened step in the path
422
+ int i = (int )m_path.size () - 1 ;
423
+ for (; i >= 0 ; --i) {
424
+ if (m_path.at (i) >> 31 ) {
425
+ break ;
426
+ }
427
+ }
428
+ // Either no derivation or all unhardened derivation
429
+ if (i == -1 ) {
430
+ if (priv) return ToPrivateString (arg, out);
431
+ out = ToString ();
432
+ return true ;
433
+ }
434
+ // Derive the xpub at the last hardened step
435
+ CExtKey xprv;
436
+ if (!GetExtKey (arg, xprv)) return false ;
437
+ KeyOriginInfo origin;
438
+ int k = 0 ;
439
+ for (; k <= i; ++k) {
440
+ // Derive
441
+ xprv.Derive (xprv, m_path.at (k));
442
+ // Add to the path
443
+ origin.path .push_back (m_path.at (k));
444
+ // First derivation element, get the fingerprint for origin
445
+ if (k == 0 ) {
446
+ std::copy (xprv.vchFingerprint , xprv.vchFingerprint + 4 , origin.fingerprint );
447
+ }
448
+ }
449
+ // Build the remaining path
450
+ KeyPath end_path;
451
+ for (; k < (int )m_path.size (); ++k) {
452
+ end_path.push_back (m_path.at (k));
453
+ }
454
+ // Build the string
455
+ std::string origin_str = HexStr (origin.fingerprint ) + FormatHDKeypath (origin.path );
456
+ out = " [" + origin_str + " ]" + (priv ? EncodeExtKey (xprv) : EncodeExtPubKey (xprv.Neuter ())) + FormatHDKeypath (end_path);
457
+ if (IsRange ()) {
458
+ out += " /*" ;
459
+ assert (m_derive == DeriveType::UNHARDENED);
460
+ }
461
+ return true ;
462
+ }
389
463
bool GetPrivKey (int pos, const SigningProvider& arg, CKey& key) const override
390
464
{
391
465
CExtKey extkey;
@@ -449,15 +523,17 @@ class DescriptorImpl : public Descriptor
449
523
return false ;
450
524
}
451
525
452
- bool ToStringHelper (const SigningProvider* arg, std::string& out, bool priv) const
526
+ bool ToStringHelper (const SigningProvider* arg, std::string& out, bool priv, bool normalized ) const
453
527
{
454
528
std::string extra = ToStringExtra ();
455
529
size_t pos = extra.size () > 0 ? 1 : 0 ;
456
530
std::string ret = m_name + " (" + extra;
457
531
for (const auto & pubkey : m_pubkey_args) {
458
532
if (pos++) ret += " ," ;
459
533
std::string tmp;
460
- if (priv) {
534
+ if (normalized) {
535
+ if (!pubkey->ToNormalizedString (*arg, tmp, priv)) return false ;
536
+ } else if (priv) {
461
537
if (!pubkey->ToPrivateString (*arg, tmp)) return false ;
462
538
} else {
463
539
tmp = pubkey->ToString ();
@@ -467,7 +543,7 @@ class DescriptorImpl : public Descriptor
467
543
if (m_subdescriptor_arg) {
468
544
if (pos++) ret += " ," ;
469
545
std::string tmp;
470
- if (!m_subdescriptor_arg->ToStringHelper (arg, tmp, priv)) return false ;
546
+ if (!m_subdescriptor_arg->ToStringHelper (arg, tmp, priv, normalized )) return false ;
471
547
ret += std::move (tmp);
472
548
}
473
549
out = std::move (ret) + " )" ;
@@ -477,13 +553,20 @@ class DescriptorImpl : public Descriptor
477
553
std::string ToString () const final
478
554
{
479
555
std::string ret;
480
- ToStringHelper (nullptr , ret, false );
556
+ ToStringHelper (nullptr , ret, false , false );
481
557
return AddChecksum (ret);
482
558
}
483
559
484
560
bool ToPrivateString (const SigningProvider& arg, std::string& out) const final
485
561
{
486
- bool ret = ToStringHelper (&arg, out, true );
562
+ bool ret = ToStringHelper (&arg, out, true , false );
563
+ out = AddChecksum (out);
564
+ return ret;
565
+ }
566
+
567
+ bool ToNormalizedString (const SigningProvider& arg, std::string& out, bool priv) const override final
568
+ {
569
+ bool ret = ToStringHelper (&arg, out, priv, true );
487
570
out = AddChecksum (out);
488
571
return ret;
489
572
}
0 commit comments