Skip to content

Commit d87b544

Browse files
committed
descriptors: Cache last hardened xpub
Cache the last hardenex xpub in the DescriptorCache
1 parent cacc391 commit d87b544

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

src/script/descriptor.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,14 @@ class BIP32PubkeyProvider final : public PubkeyProvider
302302
}
303303

304304
// Derives the last xprv
305-
bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv) const
305+
bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv, CExtKey& last_hardened) const
306306
{
307307
if (!GetExtKey(arg, xprv)) return false;
308308
for (auto entry : m_path) {
309309
xprv.Derive(xprv, entry);
310+
if (entry >> 31) {
311+
last_hardened = xprv;
312+
}
310313
}
311314
return true;
312315
}
@@ -340,6 +343,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
340343
// Derive keys or fetch them from cache
341344
CExtPubKey final_extkey = m_root_extkey;
342345
CExtPubKey parent_extkey = m_root_extkey;
346+
CExtPubKey last_hardened_extkey;
343347
bool der = true;
344348
if (read_cache) {
345349
if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, final_extkey)) {
@@ -351,11 +355,15 @@ class BIP32PubkeyProvider final : public PubkeyProvider
351355
}
352356
} else if (IsHardened()) {
353357
CExtKey xprv;
354-
if (!GetDerivedExtKey(arg, xprv)) return false;
358+
CExtKey lh_xprv;
359+
if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false;
355360
parent_extkey = xprv.Neuter();
356361
if (m_derive == DeriveType::UNHARDENED) der = xprv.Derive(xprv, pos);
357362
if (m_derive == DeriveType::HARDENED) der = xprv.Derive(xprv, pos | 0x80000000UL);
358363
final_extkey = xprv.Neuter();
364+
if (lh_xprv.key.IsValid()) {
365+
last_hardened_extkey = lh_xprv.Neuter();
366+
}
359367
} else {
360368
for (auto entry : m_path) {
361369
der = parent_extkey.Derive(parent_extkey, entry);
@@ -374,6 +382,10 @@ class BIP32PubkeyProvider final : public PubkeyProvider
374382
// Only cache parent if there is any unhardened derivation
375383
if (m_derive != DeriveType::HARDENED) {
376384
write_cache->CacheParentExtPubKey(m_expr_index, parent_extkey);
385+
// Cache last hardened xpub if we have it
386+
if (last_hardened_extkey.pubkey.IsValid()) {
387+
write_cache->CacheLastHardenedExtPubKey(m_expr_index, last_hardened_extkey);
388+
}
377389
} else if (final_info_out.path.size() > 0) {
378390
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);
379391
}
@@ -454,7 +466,8 @@ class BIP32PubkeyProvider final : public PubkeyProvider
454466
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
455467
{
456468
CExtKey extkey;
457-
if (!GetDerivedExtKey(arg, extkey)) return false;
469+
CExtKey dummy;
470+
if (!GetDerivedExtKey(arg, extkey, dummy)) return false;
458471
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
459472
if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
460473
key = extkey.key;
@@ -1400,6 +1413,11 @@ void DescriptorCache::CacheDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_i
14001413
xpubs[der_index] = xpub;
14011414
}
14021415

1416+
void DescriptorCache::CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub)
1417+
{
1418+
m_last_hardened_xpubs[key_exp_pos] = xpub;
1419+
}
1420+
14031421
bool DescriptorCache::GetCachedParentExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const
14041422
{
14051423
const auto& it = m_parent_xpubs.find(key_exp_pos);
@@ -1418,6 +1436,14 @@ bool DescriptorCache::GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t d
14181436
return true;
14191437
}
14201438

1439+
bool DescriptorCache::GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const
1440+
{
1441+
const auto& it = m_last_hardened_xpubs.find(key_exp_pos);
1442+
if (it == m_last_hardened_xpubs.end()) return false;
1443+
xpub = it->second;
1444+
return true;
1445+
}
1446+
14211447
DescriptorCache DescriptorCache::MergeAndDiff(const DescriptorCache& other)
14221448
{
14231449
DescriptorCache diff;
@@ -1445,6 +1471,17 @@ DescriptorCache DescriptorCache::MergeAndDiff(const DescriptorCache& other)
14451471
diff.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second);
14461472
}
14471473
}
1474+
for (const auto& lh_xpub_pair : other.GetCachedLastHardenedExtPubKeys()) {
1475+
CExtPubKey xpub;
1476+
if (GetCachedLastHardenedExtPubKey(lh_xpub_pair.first, xpub)) {
1477+
if (xpub != lh_xpub_pair.second) {
1478+
throw std::runtime_error(std::string(__func__) + ": New cached last hardened xpub does not match already cached last hardened xpub");
1479+
}
1480+
continue;
1481+
}
1482+
CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second);
1483+
diff.CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second);
1484+
}
14481485
return diff;
14491486
}
14501487

@@ -1457,3 +1494,8 @@ const std::unordered_map<uint32_t, ExtPubKeyMap> DescriptorCache::GetCachedDeriv
14571494
{
14581495
return m_derived_xpubs;
14591496
}
1497+
1498+
const ExtPubKeyMap DescriptorCache::GetCachedLastHardenedExtPubKeys() const
1499+
{
1500+
return m_last_hardened_xpubs;
1501+
}

src/script/descriptor.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class DescriptorCache {
2222
std::unordered_map<uint32_t, ExtPubKeyMap> m_derived_xpubs;
2323
/** Map key expression index -> parent xpub */
2424
ExtPubKeyMap m_parent_xpubs;
25+
/** Map key expression index -> last hardened xpub */
26+
ExtPubKeyMap m_last_hardened_xpubs;
2527

2628
public:
2729
/** Cache a parent xpub
@@ -50,11 +52,25 @@ class DescriptorCache {
5052
* @param[in] xpub The CExtPubKey to get from cache
5153
*/
5254
bool GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_index, CExtPubKey& xpub) const;
55+
/** Cache a last hardened xpub
56+
*
57+
* @param[in] key_exp_pos Position of the key expression within the descriptor
58+
* @param[in] xpub The CExtPubKey to cache
59+
*/
60+
void CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub);
61+
/** Retrieve a cached last hardened xpub
62+
*
63+
* @param[in] key_exp_pos Position of the key expression within the descriptor
64+
* @param[in] xpub The CExtPubKey to get from cache
65+
*/
66+
bool GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const;
5367

5468
/** Retrieve all cached parent xpubs */
5569
const ExtPubKeyMap GetCachedParentExtPubKeys() const;
5670
/** Retrieve all cached derived xpubs */
5771
const std::unordered_map<uint32_t, ExtPubKeyMap> GetCachedDerivedExtPubKeys() const;
72+
/** Retrieve all cached last hardened xpubs */
73+
const ExtPubKeyMap GetCachedLastHardenedExtPubKeys() const;
5874

5975
/** Combine another DescriptorCache into this one.
6076
* Returns a cache containing the items from the other cache unknown to current cache

0 commit comments

Comments
 (0)