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.
This commit is contained in:
Andrew Chow 2019-12-05 15:55:49 -05:00
parent 45f2f6a0e8
commit c93082ece4
2 changed files with 68 additions and 3 deletions

View File

@ -12,6 +12,9 @@
#include <util/translation.h>
#include <wallet/scriptpubkeyman.h>
//! 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.
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
LOCK(cs_KeyStore);
@ -290,6 +293,43 @@ bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool i
return true;
}
bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal)
{
LOCK(cs_KeyStore);
if (m_storage.IsLocked()) return false;
auto it = m_inactive_hd_chains.find(seed_id);
if (it == m_inactive_hd_chains.end()) {
return false;
}
CHDChain& chain = it->second;
// Top up key pool
int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
// "size" of the keypools. Not really the size, actually the difference between index and the chain counter
// Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
int64_t kp_size = (internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - (index + 1);
// make sure the keypool fits the user-selected target (-keypool)
int64_t missing = std::max(target_size - kp_size, (int64_t) 0);
if (missing > 0) {
WalletBatch batch(m_storage.GetDatabase());
for (int64_t i = missing; i > 0; --i) {
GenerateNewKey(batch, chain, internal);
}
if (internal) {
WalletLogPrintf("inactive seed with id %s added %d internal keys\n", HexStr(seed_id), missing);
} else {
WalletLogPrintf("inactive seed with id %s added %d keys\n", HexStr(seed_id), missing);
}
}
return true;
}
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
{
LOCK(cs_KeyStore);
@ -297,13 +337,28 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
for (const auto& keyid : GetAffectedKeys(script, *this)) {
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second);
if (!TopUp()) {
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
}
}
// 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.
// If so, TopUp the inactive hd chain
auto it = mapKeyMetadata.find(keyid);
if (it != mapKeyMetadata.end()){
CKeyMetadata meta = it->second;
if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) {
bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT;
if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
}
}
}
}
}
@ -975,8 +1030,6 @@ CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_c
return pubkey;
}
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal)
{
// for now we use a fixed keypath scheme of m/0'/0'/k

View File

@ -333,6 +333,18 @@ private:
*/
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
/**
* Like TopUp() but adds keys for inactive HD chains.
* Ensures that there are at least -keypool number of keys derived after the given index.
*
* @param seed_id the CKeyID for the HD seed.
* @param index the index to start generating keys from
* @param internal whether the internal chain should be used. true for internal chain, false for external chain.
*
* @return true if seed was found and keys were derived. false if unable to derive seeds
*/
bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
public:
using ScriptPubKeyMan::ScriptPubKeyMan;