Skip to content

Commit f5eaa90

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 9e33fa1 commit f5eaa90

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

src/wallet/scriptpubkeyman.cpp

Lines changed: 53 additions & 2 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,6 +293,43 @@ 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) 0);
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(std::max((int64_t) target_size, (int64_t) 1) - kp_size, (int64_t) 0);
318+
319+
WalletBatch batch(m_storage.GetDatabase());
320+
for (int64_t i = missing; i > 0; --i) {
321+
GenerateNewKey(batch, chain, internal);
322+
}
323+
if (missing > 0) {
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);
@@ -304,6 +344,19 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
304344
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
305345
}
306346
}
347+
348+
auto it = mapKeyMetadata.find(keyid);
349+
if (it != mapKeyMetadata.end()){
350+
CKeyMetadata meta = it->second;
351+
if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) {
352+
bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
353+
int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT;
354+
355+
if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
356+
WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
357+
}
358+
}
359+
}
307360
}
308361
}
309362

@@ -965,8 +1018,6 @@ CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_c
9651018
return pubkey;
9661019
}
9671020

968-
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
969-
9701021
void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal)
9711022
{
9721023
// 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)