Use a keypool of presplit keys after upgrading to hd chain split
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.
This commit is contained in:
parent
5c50e93d52
commit
dfcd9f3e6a
2 changed files with 60 additions and 8 deletions
|
@ -3326,6 +3326,11 @@ bool CWallet::NewKeyPool()
|
||||||
}
|
}
|
||||||
setExternalKeyPool.clear();
|
setExternalKeyPool.clear();
|
||||||
|
|
||||||
|
for (int64_t nIndex : set_pre_split_keypool) {
|
||||||
|
batch.ErasePool(nIndex);
|
||||||
|
}
|
||||||
|
set_pre_split_keypool.clear();
|
||||||
|
|
||||||
m_pool_key_to_index.clear();
|
m_pool_key_to_index.clear();
|
||||||
|
|
||||||
if (!TopUpKeyPool()) {
|
if (!TopUpKeyPool()) {
|
||||||
|
@ -3339,13 +3344,15 @@ bool CWallet::NewKeyPool()
|
||||||
size_t CWallet::KeypoolCountExternalKeys()
|
size_t CWallet::KeypoolCountExternalKeys()
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet); // setExternalKeyPool
|
AssertLockHeld(cs_wallet); // setExternalKeyPool
|
||||||
return setExternalKeyPool.size();
|
return setExternalKeyPool.size() + set_pre_split_keypool.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
if (keypool.fInternal) {
|
if (keypool.m_pre_split) {
|
||||||
|
set_pre_split_keypool.insert(nIndex);
|
||||||
|
} else if (keypool.fInternal) {
|
||||||
setInternalKeyPool.insert(nIndex);
|
setInternalKeyPool.insert(nIndex);
|
||||||
} else {
|
} else {
|
||||||
setExternalKeyPool.insert(nIndex);
|
setExternalKeyPool.insert(nIndex);
|
||||||
|
@ -3410,7 +3417,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
||||||
m_pool_key_to_index[pubkey.GetID()] = index;
|
m_pool_key_to_index[pubkey.GetID()] = index;
|
||||||
}
|
}
|
||||||
if (missingInternal + missingExternal > 0) {
|
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;
|
return true;
|
||||||
|
@ -3427,7 +3434,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
|
||||||
TopUpKeyPool();
|
TopUpKeyPool();
|
||||||
|
|
||||||
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
|
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
|
// Get the oldest key
|
||||||
if(setKeyPool.empty())
|
if(setKeyPool.empty())
|
||||||
|
@ -3444,7 +3451,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
|
||||||
if (!HaveKey(keypool.vchPubKey.GetID())) {
|
if (!HaveKey(keypool.vchPubKey.GetID())) {
|
||||||
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
|
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");
|
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3469,6 +3477,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
if (fInternal) {
|
if (fInternal) {
|
||||||
setInternalKeyPool.insert(nIndex);
|
setInternalKeyPool.insert(nIndex);
|
||||||
|
} else if (!set_pre_split_keypool.empty()) {
|
||||||
|
set_pre_split_keypool.insert(nIndex);
|
||||||
} else {
|
} else {
|
||||||
setExternalKeyPool.insert(nIndex);
|
setExternalKeyPool.insert(nIndex);
|
||||||
}
|
}
|
||||||
|
@ -3521,6 +3531,9 @@ int64_t CWallet::GetOldestKeyPoolTime()
|
||||||
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
|
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
|
||||||
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
|
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
|
||||||
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey);
|
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;
|
return oldestKey;
|
||||||
|
@ -3718,8 +3731,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
bool internal = setInternalKeyPool.count(keypool_id);
|
bool internal = setInternalKeyPool.count(keypool_id);
|
||||||
if (!internal) assert(setExternalKeyPool.count(keypool_id));
|
if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id));
|
||||||
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
|
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool);
|
||||||
auto it = setKeyPool->begin();
|
auto it = setKeyPool->begin();
|
||||||
|
|
||||||
WalletBatch batch(*database);
|
WalletBatch batch(*database);
|
||||||
|
@ -3955,6 +3968,24 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
|
||||||
return values;
|
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)
|
CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
|
||||||
{
|
{
|
||||||
const std::string& walletFile = name;
|
const std::string& walletFile = name;
|
||||||
|
@ -4006,6 +4037,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int prev_version = walletInstance->nWalletVersion;
|
||||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
||||||
{
|
{
|
||||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
||||||
|
@ -4029,6 +4061,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
||||||
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
||||||
LOCK(walletInstance->cs_wallet);
|
LOCK(walletInstance->cs_wallet);
|
||||||
bool hd_upgrade = false;
|
bool hd_upgrade = false;
|
||||||
|
bool split_upgrade = false;
|
||||||
if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) {
|
if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) {
|
||||||
LogPrintf("Upgrading wallet to HD\n");
|
LogPrintf("Upgrading wallet to HD\n");
|
||||||
walletInstance->SetMinVersion(FEATURE_HD);
|
walletInstance->SetMinVersion(FEATURE_HD);
|
||||||
|
@ -4044,10 +4077,15 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
||||||
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
||||||
LogPrintf("Upgrading wallet to use HD chain split\n");
|
LogPrintf("Upgrading wallet to use HD chain split\n");
|
||||||
walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
|
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
|
// Regenerate the keypool if upgraded to HD
|
||||||
if (hd_upgrade) {
|
if (hd_upgrade) {
|
||||||
if (!walletInstance->NewKeyPool()) {
|
if (!walletInstance->TopUpKeyPool()) {
|
||||||
InitError(_("Unable to generate keys") += "\n");
|
InitError(_("Unable to generate keys") += "\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -4275,6 +4313,7 @@ CKeyPool::CKeyPool()
|
||||||
{
|
{
|
||||||
nTime = GetTime();
|
nTime = GetTime();
|
||||||
fInternal = false;
|
fInternal = false;
|
||||||
|
m_pre_split = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
||||||
|
@ -4282,6 +4321,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
||||||
nTime = GetTime();
|
nTime = GetTime();
|
||||||
vchPubKey = vchPubKeyIn;
|
vchPubKey = vchPubKeyIn;
|
||||||
fInternal = internalIn;
|
fInternal = internalIn;
|
||||||
|
m_pre_split = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CWalletKey::CWalletKey(int64_t nExpires)
|
CWalletKey::CWalletKey(int64_t nExpires)
|
||||||
|
|
|
@ -119,6 +119,7 @@ public:
|
||||||
int64_t nTime;
|
int64_t nTime;
|
||||||
CPubKey vchPubKey;
|
CPubKey vchPubKey;
|
||||||
bool fInternal; // for change outputs
|
bool fInternal; // for change outputs
|
||||||
|
bool m_pre_split; // For keys generated before keypool split upgrade
|
||||||
|
|
||||||
CKeyPool();
|
CKeyPool();
|
||||||
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
||||||
|
@ -141,9 +142,18 @@ public:
|
||||||
(this will be the case for any wallet before the HD chain split version) */
|
(this will be the case for any wallet before the HD chain split version) */
|
||||||
fInternal = false;
|
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 {
|
else {
|
||||||
READWRITE(fInternal);
|
READWRITE(fInternal);
|
||||||
|
READWRITE(m_pre_split);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -708,6 +718,7 @@ private:
|
||||||
|
|
||||||
std::set<int64_t> setInternalKeyPool;
|
std::set<int64_t> setInternalKeyPool;
|
||||||
std::set<int64_t> setExternalKeyPool;
|
std::set<int64_t> setExternalKeyPool;
|
||||||
|
std::set<int64_t> set_pre_split_keypool;
|
||||||
int64_t m_max_keypool_index = 0;
|
int64_t m_max_keypool_index = 0;
|
||||||
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
||||||
|
|
||||||
|
@ -774,6 +785,7 @@ public:
|
||||||
const std::string& GetName() const { return m_name; }
|
const std::string& GetName() const { return m_name; }
|
||||||
|
|
||||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
||||||
|
void MarkPreSplitKeys();
|
||||||
|
|
||||||
// Map from Key ID to key metadata.
|
// Map from Key ID to key metadata.
|
||||||
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
|
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
|
||||||
|
|
Loading…
Add table
Reference in a new issue