Skip to content

Commit

Permalink
Use a keypool of presplit keys after upgrading to hd chain split
Browse files Browse the repository at this point in the history
After upgrading to HD chain split, we want to continue to use keys
from the old keypool. To do this, before we generate any new keys after
upgrading, we mark all of the keypool entries as being pre-chain
split and move them to a separate pre chain split keypool. Keys are
fetched from that keypool until it is emptied. Only then are the new
internal and external keypools used.
  • Loading branch information
achow101 committed May 12, 2018
1 parent 5c50e93 commit dfcd9f3
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 8 deletions.
56 changes: 48 additions & 8 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3326,6 +3326,11 @@ bool CWallet::NewKeyPool()
}
setExternalKeyPool.clear();

for (int64_t nIndex : set_pre_split_keypool) {
batch.ErasePool(nIndex);
}
set_pre_split_keypool.clear();

m_pool_key_to_index.clear();

if (!TopUpKeyPool()) {
Expand All @@ -3339,13 +3344,15 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys()
{
AssertLockHeld(cs_wallet); // setExternalKeyPool
return setExternalKeyPool.size();
return setExternalKeyPool.size() + set_pre_split_keypool.size();
}

void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
{
AssertLockHeld(cs_wallet);
if (keypool.fInternal) {
if (keypool.m_pre_split) {
set_pre_split_keypool.insert(nIndex);
} else if (keypool.fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
Expand Down Expand Up @@ -3410,7 +3417,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
}
}
return true;
Expand All @@ -3427,7 +3434,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
TopUpKeyPool();

bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;

// Get the oldest key
if(setKeyPool.empty())
Expand All @@ -3444,7 +3451,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
if (!HaveKey(keypool.vchPubKey.GetID())) {
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
}
if (keypool.fInternal != fReturningInternal) {
// If the key was pre-split keypool, we don't care about what type it is
if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) {
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
}

Expand All @@ -3469,6 +3477,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
LOCK(cs_wallet);
if (fInternal) {
setInternalKeyPool.insert(nIndex);
} else if (!set_pre_split_keypool.empty()) {
set_pre_split_keypool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
Expand Down Expand Up @@ -3521,6 +3531,9 @@ int64_t CWallet::GetOldestKeyPoolTime()
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey);
if (!set_pre_split_keypool.empty()) {
oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey);
}
}

return oldestKey;
Expand Down Expand Up @@ -3718,8 +3731,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
{
AssertLockHeld(cs_wallet);
bool internal = setInternalKeyPool.count(keypool_id);
if (!internal) assert(setExternalKeyPool.count(keypool_id));
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id));
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool);
auto it = setKeyPool->begin();

WalletBatch batch(*database);
Expand Down Expand Up @@ -3955,6 +3968,24 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}

void CWallet::MarkPreSplitKeys()
{
WalletBatch batch(*database);
for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
int64_t index = *it;
CKeyPool keypool;
if (!batch.ReadPool(index, keypool)) {
throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
}
keypool.m_pre_split = true;
if (!batch.WritePool(index, keypool)) {
throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
}
set_pre_split_keypool.insert(index);
it = setExternalKeyPool.erase(it);
}
}

CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
{
const std::string& walletFile = name;
Expand Down Expand Up @@ -4006,6 +4037,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
}
}

int prev_version = walletInstance->nWalletVersion;
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
Expand All @@ -4029,6 +4061,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
if (gArgs.GetBoolArg("-upgradewallet", false)) {
LOCK(walletInstance->cs_wallet);
bool hd_upgrade = false;
bool split_upgrade = false;
if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) {
LogPrintf("Upgrading wallet to HD\n");
walletInstance->SetMinVersion(FEATURE_HD);
Expand All @@ -4044,10 +4077,15 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
LogPrintf("Upgrading wallet to use HD chain split\n");
walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
split_upgrade = FEATURE_HD_SPLIT > prev_version;
}
// Mark all keys currently in the keypool as pre-split
if (split_upgrade) {
walletInstance->MarkPreSplitKeys();
}
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->NewKeyPool()) {
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate keys") += "\n");
return nullptr;
}
Expand Down Expand Up @@ -4275,13 +4313,15 @@ CKeyPool::CKeyPool()
{
nTime = GetTime();
fInternal = false;
m_pre_split = false;
}

CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
{
nTime = GetTime();
vchPubKey = vchPubKeyIn;
fInternal = internalIn;
m_pre_split = false;
}

CWalletKey::CWalletKey(int64_t nExpires)
Expand Down
12 changes: 12 additions & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class CKeyPool
int64_t nTime;
CPubKey vchPubKey;
bool fInternal; // for change outputs
bool m_pre_split; // For keys generated before keypool split upgrade

CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
Expand All @@ -141,9 +142,18 @@ class CKeyPool
(this will be the case for any wallet before the HD chain split version) */
fInternal = false;
}
try {
READWRITE(m_pre_split);
}
catch (std::ios_base::failure&) {
/* flag as postsplit address if we can't read the m_pre_split boolean
(this will be the case for any wallet that upgrades to HD chain split)*/
m_pre_split = false;
}
}
else {
READWRITE(fInternal);
READWRITE(m_pre_split);
}
}
};
Expand Down Expand Up @@ -708,6 +718,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface

std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
std::set<int64_t> set_pre_split_keypool;
int64_t m_max_keypool_index = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;

Expand Down Expand Up @@ -774,6 +785,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
const std::string& GetName() const { return m_name; }

void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
void MarkPreSplitKeys();

// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
Expand Down

0 comments on commit dfcd9f3

Please sign in to comment.