Skip to content

Commit c93082e

Browse files
committed
Generate new keys for inactive seeds after marking used
When a key from an inactive seed is used, generate replacements to fill a keypool that would have been there.
1 parent 45f2f6a commit c93082e

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/wallet/scriptpubkeyman.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include <util/translation.h>
1313
#include <wallet/scriptpubkeyman.h>
1414

15+
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
16+
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
17+
1518
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
1619
{
1720
LOCK(cs_KeyStore);
@@ -290,20 +293,72 @@ bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool i
290293
return true;
291294
}
292295

296+
bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal)
297+
{
298+
LOCK(cs_KeyStore);
299+
300+
if (m_storage.IsLocked()) return false;
301+
302+
auto it = m_inactive_hd_chains.find(seed_id);
303+
if (it == m_inactive_hd_chains.end()) {
304+
return false;
305+
}
306+
307+
CHDChain& chain = it->second;
308+
309+
// Top up key pool
310+
int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
311+
312+
// "size" of the keypools. Not really the size, actually the difference between index and the chain counter
313+
// Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
314+
int64_t kp_size = (internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - (index + 1);
315+
316+
// make sure the keypool fits the user-selected target (-keypool)
317+
int64_t missing = std::max(target_size - kp_size, (int64_t) 0);
318+
319+
if (missing > 0) {
320+
WalletBatch batch(m_storage.GetDatabase());
321+
for (int64_t i = missing; i > 0; --i) {
322+
GenerateNewKey(batch, chain, internal);
323+
}
324+
if (internal) {
325+
WalletLogPrintf("inactive seed with id %s added %d internal keys\n", HexStr(seed_id), missing);
326+
} else {
327+
WalletLogPrintf("inactive seed with id %s added %d keys\n", HexStr(seed_id), missing);
328+
}
329+
}
330+
return true;
331+
}
332+
293333
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
294334
{
295335
LOCK(cs_KeyStore);
296336
// extract addresses and check if they match with an unused keypool key
297337
for (const auto& keyid : GetAffectedKeys(script, *this)) {
298338
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
299339
if (mi != m_pool_key_to_index.end()) {
300-
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
340+
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__);
301341
MarkReserveKeysAsUsed(mi->second);
302342

303343
if (!TopUp()) {
304344
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
305345
}
306346
}
347+
348+
// Find the key's metadata and check if it's seed id (if it has one) is inactive, i.e. it is not the current m_hd_chain seed id.
349+
// If so, TopUp the inactive hd chain
350+
auto it = mapKeyMetadata.find(keyid);
351+
if (it != mapKeyMetadata.end()){
352+
CKeyMetadata meta = it->second;
353+
if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) {
354+
bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
355+
int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT;
356+
357+
if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
358+
WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
359+
}
360+
}
361+
}
307362
}
308363
}
309364

@@ -975,8 +1030,6 @@ CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_c
9751030
return pubkey;
9761031
}
9771032

978-
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
979-
9801033
void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal)
9811034
{
9821035
// for now we use a fixed keypath scheme of m/0'/0'/k

src/wallet/scriptpubkeyman.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,18 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
333333
*/
334334
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
335335

336+
/**
337+
* Like TopUp() but adds keys for inactive HD chains.
338+
* Ensures that there are at least -keypool number of keys derived after the given index.
339+
*
340+
* @param seed_id the CKeyID for the HD seed.
341+
* @param index the index to start generating keys from
342+
* @param internal whether the internal chain should be used. true for internal chain, false for external chain.
343+
*
344+
* @return true if seed was found and keys were derived. false if unable to derive seeds
345+
*/
346+
bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
347+
336348
public:
337349
using ScriptPubKeyMan::ScriptPubKeyMan;
338350

0 commit comments

Comments
 (0)