@@ -181,7 +181,7 @@ struct PubkeyProvider
181181 virtual bool ToPrivateString (const SigningProvider& arg, std::string& out) const = 0;
182182
183183 /* * Get the descriptor string form with the xpub at the last hardened derivation */
184- virtual bool ToNormalizedString (const SigningProvider& arg, std::string& out) const = 0;
184+ virtual bool ToNormalizedString (const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr ) const = 0;
185185
186186 /* * Derive a private key, if private data is available in arg. */
187187 virtual bool GetPrivKey (int pos, const SigningProvider& arg, CKey& key) const = 0;
@@ -216,10 +216,10 @@ class OriginPubkeyProvider final : public PubkeyProvider
216216 ret = " [" + OriginString () + " ]" + std::move (sub);
217217 return true ;
218218 }
219- bool ToNormalizedString (const SigningProvider& arg, std::string& ret) const override
219+ bool ToNormalizedString (const SigningProvider& arg, std::string& ret, const DescriptorCache* cache ) const override
220220 {
221221 std::string sub;
222- if (!m_provider->ToNormalizedString (arg, sub)) return false ;
222+ if (!m_provider->ToNormalizedString (arg, sub, cache )) return false ;
223223 // If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider
224224 // In that case, we need to strip out the leading square bracket and fingerprint from the substring,
225225 // and append that to our own origin string.
@@ -263,7 +263,7 @@ class ConstPubkeyProvider final : public PubkeyProvider
263263 ret = EncodeSecret (key);
264264 return true ;
265265 }
266- bool ToNormalizedString (const SigningProvider& arg, std::string& ret) const override
266+ bool ToNormalizedString (const SigningProvider& arg, std::string& ret, const DescriptorCache* cache ) const override
267267 {
268268 ret = ToString ();
269269 return true ;
@@ -412,7 +412,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
412412 }
413413 return true ;
414414 }
415- bool ToNormalizedString (const SigningProvider& arg, std::string& out) const override
415+ bool ToNormalizedString (const SigningProvider& arg, std::string& out, const DescriptorCache* cache ) const override
416416 {
417417 // For hardened derivation type, just return the typical string, nothing to normalize
418418 if (m_derive == DeriveType::HARDENED) {
@@ -431,29 +431,39 @@ class BIP32PubkeyProvider final : public PubkeyProvider
431431 out = ToString ();
432432 return true ;
433433 }
434- // Derive the xpub at the last hardened step
435- CExtKey xprv;
436- if (!GetExtKey (arg, xprv)) return false ;
434+ // Get the path to the last hardened stup
437435 KeyOriginInfo origin;
438436 int k = 0 ;
439437 for (; k <= i; ++k) {
440- // Derive
441- xprv.Derive (xprv, m_path.at (k));
442438 // Add to the path
443439 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- }
448440 }
449441 // Build the remaining path
450442 KeyPath end_path;
451443 for (; k < (int )m_path.size (); ++k) {
452444 end_path.push_back (m_path.at (k));
453445 }
446+ // Get the fingerprint
447+ CKeyID id = m_root_extkey.pubkey .GetID ();
448+ std::copy (id.begin (), id.begin () + 4 , origin.fingerprint );
449+
450+ CExtPubKey xpub;
451+ CExtKey lh_xprv;
452+ // If we have the cache, just get the parent xpub
453+ if (cache != nullptr ) {
454+ cache->GetCachedLastHardenedExtPubKey (m_expr_index, xpub);
455+ }
456+ if (!xpub.pubkey .IsValid ()) {
457+ // Cache miss, or nor cache, or need privkey
458+ CExtKey xprv;
459+ if (!GetDerivedExtKey (arg, xprv, lh_xprv)) return false ;
460+ xpub = lh_xprv.Neuter ();
461+ }
462+ assert (xpub.pubkey .IsValid ());
463+
454464 // Build the string
455465 std::string origin_str = HexStr (origin.fingerprint ) + FormatHDKeypath (origin.path );
456- out = " [" + origin_str + " ]" + EncodeExtPubKey (xprv. Neuter () ) + FormatHDKeypath (end_path);
466+ out = " [" + origin_str + " ]" + EncodeExtPubKey (xpub ) + FormatHDKeypath (end_path);
457467 if (IsRange ()) {
458468 out += " /*" ;
459469 assert (m_derive == DeriveType::UNHARDENED);
@@ -533,19 +543,19 @@ class DescriptorImpl : public Descriptor
533543 return false ;
534544 }
535545
536- virtual bool ToStringSubScriptHelper (const SigningProvider* arg, std::string& ret, const StringType type) const
546+ virtual bool ToStringSubScriptHelper (const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr ) const
537547 {
538548 size_t pos = 0 ;
539549 for (const auto & scriptarg : m_subdescriptor_args) {
540550 if (pos++) ret += " ," ;
541551 std::string tmp;
542- if (!scriptarg->ToStringHelper (arg, tmp, type)) return false ;
552+ if (!scriptarg->ToStringHelper (arg, tmp, type, cache )) return false ;
543553 ret += std::move (tmp);
544554 }
545555 return true ;
546556 }
547557
548- bool ToStringHelper (const SigningProvider* arg, std::string& out, const StringType type) const
558+ bool ToStringHelper (const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr ) const
549559 {
550560 std::string extra = ToStringExtra ();
551561 size_t pos = extra.size () > 0 ? 1 : 0 ;
@@ -555,7 +565,7 @@ class DescriptorImpl : public Descriptor
555565 std::string tmp;
556566 switch (type) {
557567 case StringType::NORMALIZED:
558- if (!pubkey->ToNormalizedString (*arg, tmp)) return false ;
568+ if (!pubkey->ToNormalizedString (*arg, tmp, cache )) return false ;
559569 break ;
560570 case StringType::PRIVATE:
561571 if (!pubkey->ToPrivateString (*arg, tmp)) return false ;
@@ -567,7 +577,7 @@ class DescriptorImpl : public Descriptor
567577 ret += std::move (tmp);
568578 }
569579 std::string subscript;
570- if (!ToStringSubScriptHelper (arg, subscript, type)) return false ;
580+ if (!ToStringSubScriptHelper (arg, subscript, type, cache )) return false ;
571581 if (pos && subscript.size ()) ret += ' ,' ;
572582 out = std::move (ret) + std::move (subscript) + " )" ;
573583 return true ;
@@ -587,9 +597,9 @@ class DescriptorImpl : public Descriptor
587597 return ret;
588598 }
589599
590- bool ToNormalizedString (const SigningProvider& arg, std::string& out) const override final
600+ bool ToNormalizedString (const SigningProvider& arg, std::string& out, const DescriptorCache* cache ) const override final
591601 {
592- bool ret = ToStringHelper (&arg, out, StringType::NORMALIZED);
602+ bool ret = ToStringHelper (&arg, out, StringType::NORMALIZED, cache );
593603 out = AddChecksum (out);
594604 return ret;
595605 }
@@ -843,7 +853,7 @@ class TRDescriptor final : public DescriptorImpl
843853 out.tr_spenddata [output].Merge (builder.GetSpendData ());
844854 return Vector (GetScriptForDestination (output));
845855 }
846- bool ToStringSubScriptHelper (const SigningProvider* arg, std::string& ret, const StringType type) const override
856+ bool ToStringSubScriptHelper (const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr ) const override
847857 {
848858 if (m_depths.empty ()) return true ;
849859 std::vector<bool > path;
@@ -854,7 +864,7 @@ class TRDescriptor final : public DescriptorImpl
854864 path.push_back (false );
855865 }
856866 std::string tmp;
857- if (!m_subdescriptor_args[pos]->ToStringHelper (arg, tmp, type)) return false ;
867+ if (!m_subdescriptor_args[pos]->ToStringHelper (arg, tmp, type, cache )) return false ;
858868 ret += std::move (tmp);
859869 while (!path.empty () && path.back ()) {
860870 if (path.size () > 1 ) ret += ' }' ;
0 commit comments