Skip to content

Commit 58c7651

Browse files
committed
Implement TopUp in DescriptorScriptPubKeyMan
1 parent e014886 commit 58c7651

File tree

5 files changed

+120
-1
lines changed

5 files changed

+120
-1
lines changed

src/pubkey.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ struct CExtPubKey {
219219
a.pubkey == b.pubkey;
220220
}
221221

222+
friend bool operator!=(const CExtPubKey &a, const CExtPubKey &b)
223+
{
224+
return !(a == b);
225+
}
226+
222227
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
223228
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
224229
bool Derive(CExtPubKey& out, unsigned int nChild) const;

src/wallet/scriptpubkeyman.cpp

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1533,9 +1533,99 @@ void DescriptorScriptPubKeyMan::ReturnDestination(int64_t index, bool internal,
15331533
{
15341534
}
15351535

1536+
std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const
1537+
{
1538+
AssertLockHeld(cs_desc_man);
1539+
if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) {
1540+
KeyMap keys;
1541+
for (auto key_pair : m_map_crypted_keys) {
1542+
const CPubKey& pubkey = key_pair.second.first;
1543+
const std::vector<unsigned char>& crypted_secret = key_pair.second.second;
1544+
CKey key;
1545+
DecryptKey(m_storage.GetEncryptionKey(), crypted_secret, pubkey, key);
1546+
keys[pubkey.GetID()] = key;
1547+
}
1548+
return keys;
1549+
}
1550+
return m_map_keys;
1551+
}
1552+
15361553
bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
15371554
{
1538-
return false;
1555+
LOCK(cs_desc_man);
1556+
unsigned int target_size;
1557+
if (size > 0) {
1558+
target_size = size;
1559+
} else {
1560+
target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
1561+
}
1562+
1563+
// Calculate the new range_end
1564+
int32_t new_range_end = std::max(m_wallet_descriptor.next_index + (int32_t)target_size, m_wallet_descriptor.range_end);
1565+
1566+
// If the descriptor is not ranged, we actually just want to fill the first cache item
1567+
if (!m_wallet_descriptor.descriptor->IsRange()) {
1568+
new_range_end = 1;
1569+
m_wallet_descriptor.range_end = 1;
1570+
m_wallet_descriptor.range_start = 0;
1571+
}
1572+
1573+
FlatSigningProvider provider;
1574+
provider.keys = GetKeys();
1575+
1576+
WalletBatch batch(m_storage.GetDatabase());
1577+
uint256 id = GetID();
1578+
for (int32_t i = m_max_cached_index + 1; i < new_range_end; ++i) {
1579+
FlatSigningProvider out_keys;
1580+
std::vector<CScript> scripts_temp;
1581+
DescriptorCache temp_cache;
1582+
// Maybe we have a cached xpub and we can expand from the cache first
1583+
if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) {
1584+
if (!m_wallet_descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &temp_cache)) return false;
1585+
}
1586+
// Add all of the scriptPubKeys to the scriptPubKey set
1587+
for (const CScript& script : scripts_temp) {
1588+
m_map_script_pub_keys[script] = i;
1589+
}
1590+
// Write the cache
1591+
for (const auto& parent_xpub_pair : temp_cache.GetCachedParentExtPubKeys()) {
1592+
CExtPubKey xpub;
1593+
if (m_wallet_descriptor.cache.GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) {
1594+
if (xpub != parent_xpub_pair.second) {
1595+
throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub");
1596+
}
1597+
continue;
1598+
}
1599+
if (!batch.WriteDescriptorParentCache(parent_xpub_pair.second, id, parent_xpub_pair.first)) {
1600+
throw std::runtime_error(std::string(__func__) + ": writing cache item failed");
1601+
}
1602+
m_wallet_descriptor.cache.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second);
1603+
}
1604+
for (const auto& derived_xpub_map_pair : temp_cache.GetCachedDerivedExtPubKeys()) {
1605+
for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
1606+
CExtPubKey xpub;
1607+
if (m_wallet_descriptor.cache.GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) {
1608+
if (xpub != derived_xpub_pair.second) {
1609+
throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub");
1610+
}
1611+
continue;
1612+
}
1613+
if (!batch.WriteDescriptorDerivedCache(derived_xpub_pair.second, id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
1614+
throw std::runtime_error(std::string(__func__) + ": writing cache item failed");
1615+
}
1616+
m_wallet_descriptor.cache.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second);
1617+
}
1618+
}
1619+
m_max_cached_index++;
1620+
}
1621+
m_wallet_descriptor.range_end = new_range_end;
1622+
batch.WriteDescriptor(GetID(), m_wallet_descriptor);
1623+
1624+
// By this point, the cache size should be the size of the entire range
1625+
assert(m_wallet_descriptor.range_end - 1 == m_max_cached_index);
1626+
1627+
NotifyCanGetAddressesChanged();
1628+
return true;
15391629
}
15401630

15411631
void DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
@@ -1753,6 +1843,7 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache)
17531843
}
17541844
m_map_script_pub_keys[script] = i;
17551845
}
1846+
m_max_cached_index++;
17561847
}
17571848
}
17581849

src/wallet/scriptpubkeyman.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
492492
using KeyMap = std::map<CKeyID, CKey>;
493493

494494
ScriptPubKeyMap m_map_script_pub_keys GUARDED_BY(cs_desc_man);
495+
int32_t m_max_cached_index = -1;
495496

496497
OutputType m_address_type;
497498
bool m_internal;
@@ -505,6 +506,8 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
505506
bool m_decryption_thoroughly_checked = false;
506507

507508
bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
509+
510+
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
508511
public:
509512
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
510513
: ScriptPubKeyMan(storage),
@@ -526,6 +529,10 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
526529
bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override;
527530
void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override;
528531

532+
// Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file
533+
// and is used to expand the descriptor in GetNewDestination. DescriptorScriptPubKeyMan relies
534+
// more on ephemeral data than LegacyScriptPubKeyMan. For wallets using unhardened derivation
535+
// (with or without private keys), the "keypool" is a single xpub.
529536
bool TopUp(unsigned int size = 0) override;
530537

531538
void MarkUnusedAddresses(const CScript& script) override;

src/wallet/walletdb.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,20 @@ bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor
216216
return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
217217
}
218218

219+
bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
220+
{
221+
std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
222+
xpub.Encode(ser_xpub.data());
223+
return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
224+
}
225+
226+
bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
227+
{
228+
std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
229+
xpub.Encode(ser_xpub.data());
230+
return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
231+
}
232+
219233
class CWalletScanState {
220234
public:
221235
unsigned int nKeys{0};

src/wallet/walletdb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class WalletBatch
249249
bool WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey);
250250
bool WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret);
251251
bool WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor);
252+
bool WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index);
253+
bool WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index);
252254

253255
/// Write destination data key,value tuple to database
254256
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);

0 commit comments

Comments
 (0)