Skip to content

Commit deb791c

Browse files
committed
Only cache xpubs that have a hardened last step
Also adds tests for this: For ranged descriptors with unhardened derivation, we expect to find parent keys in the cache but no child keys. For descriptors containing an xpub but do not have unhardened derivation (i.e. hardened derivation or single xpub with or without derivation), we expect to find all of the keys in the cache, and the same number of keys in the cache as in the SigningProvider. For everything else (no xpub), nothing should be cached at all.
1 parent f76733e commit deb791c

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

src/script/descriptor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,11 @@ class BIP32PubkeyProvider final : public PubkeyProvider
345345
key_out = final_extkey.pubkey;
346346

347347
if (write_cache) {
348-
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);
349348
// Only cache parent if there is any unhardened derivation
350349
if (m_derive != DeriveType::HARDENED) {
351350
write_cache->CacheParentExtPubKey(m_expr_index, parent_extkey);
351+
} else if (final_info_out.path.size() > 0) {
352+
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);
352353
}
353354
}
354355

src/test/descriptor_tests.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,54 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
149149
BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);
150150
BOOST_CHECK(script_provider.origins == script_provider_cached.origins);
151151

152+
// Check whether keys are in the cache
153+
const auto& der_xpub_cache = desc_cache.GetCachedDerivedExtPubKeys();
154+
const auto& parent_xpub_cache = desc_cache.GetCachedParentExtPubKeys();
155+
if ((flags & RANGE) && !(flags & DERIVE_HARDENED)) {
156+
// For ranged, unhardened derivation, None of the keys in origins should appear in the cache but the cache should have parent keys
157+
// But we can derive one level from each of those parent keys and find them all
158+
BOOST_CHECK(der_xpub_cache.empty());
159+
BOOST_CHECK(parent_xpub_cache.size() > 0);
160+
std::set<CPubKey> pubkeys;
161+
for (const auto& xpub_pair : parent_xpub_cache) {
162+
const CExtPubKey& xpub = xpub_pair.second;
163+
CExtPubKey der;
164+
xpub.Derive(der, i);
165+
pubkeys.insert(der.pubkey);
166+
}
167+
for (const auto& origin_pair : script_provider_cached.origins) {
168+
const CPubKey& pk = origin_pair.second.first;
169+
BOOST_CHECK(pubkeys.count(pk) > 0);
170+
}
171+
} else if (pub1.find("xpub") != std::string::npos) {
172+
// For ranged, hardened derivation, or not ranged, but has an xpub, all of the keys should appear in the cache
173+
BOOST_CHECK(der_xpub_cache.size() + parent_xpub_cache.size() == script_provider_cached.origins.size());
174+
// Get all of the derived pubkeys
175+
std::set<CPubKey> pubkeys;
176+
for (const auto& xpub_map_pair : der_xpub_cache) {
177+
for (const auto& xpub_pair : xpub_map_pair.second) {
178+
const CExtPubKey& xpub = xpub_pair.second;
179+
pubkeys.insert(xpub.pubkey);
180+
}
181+
}
182+
// Derive one level from all of the parents
183+
for (const auto& xpub_pair : parent_xpub_cache) {
184+
const CExtPubKey& xpub = xpub_pair.second;
185+
pubkeys.insert(xpub.pubkey);
186+
CExtPubKey der;
187+
xpub.Derive(der, i);
188+
pubkeys.insert(der.pubkey);
189+
}
190+
for (const auto& origin_pair : script_provider_cached.origins) {
191+
const CPubKey& pk = origin_pair.second.first;
192+
BOOST_CHECK(pubkeys.count(pk) > 0);
193+
}
194+
} else {
195+
// No xpub, nothing should be cached
196+
BOOST_CHECK(der_xpub_cache.empty());
197+
BOOST_CHECK(parent_xpub_cache.empty());
198+
}
199+
152200
// Make sure we can expand using cached xpubs for unhardened derivation
153201
if (!(flags & DERIVE_HARDENED)) {
154202
// Evaluate the descriptor at i + 1

0 commit comments

Comments
 (0)