From b5ba01a18707b1d50c4d073bd40e1e92d78d04c1 Mon Sep 17 00:00:00 2001 From: Chris Moore Date: Tue, 12 Sep 2017 14:01:12 -0700 Subject: Add 'sethdseed' RPC to initialize or replace HD seed --- src/wallet/wallet.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9533e6ff5..3ca31ac9e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1452,7 +1452,11 @@ CPubKey CWallet::GenerateNewHDMasterKey() { CKey key; key.MakeNewKey(true); + return DeriveNewMasterHDKey(key); +} +CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key) +{ int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); -- cgit v1.2.3 From 5c50e93d52c14fc7bc41130cdb1568f2c11e45de Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Sat, 31 Mar 2018 13:37:27 -0400 Subject: Allow -upgradewallet to upgradewallets to HD Changes the maximum upgradewallet version to the latest wallet version number, 159900. Non-HD wallets will be upgraded to use HD derivation. Non HD chain split wallets will be upgraded to HD chain split. If a non-HD wallet is upgraded to HD, the keypool will be entirely regenerated. Since upgradewallet is effectively run during a first run, all of the first run initial setup stuff is combined with the upgrade to HD --- src/wallet/wallet.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3ca31ac9e..57dc6c3b7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4025,6 +4025,35 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& walletInstance->SetMaxVersion(nMaxVersion); } + // Upgrade to HD if explicit upgrade + if (gArgs.GetBoolArg("-upgradewallet", false)) { + LOCK(walletInstance->cs_wallet); + bool hd_upgrade = false; + if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) { + LogPrintf("Upgrading wallet to HD\n"); + walletInstance->SetMinVersion(FEATURE_HD); + + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) { + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + } + hd_upgrade = true; + } + // Upgrade to HD chain split if necessary + if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { + LogPrintf("Upgrading wallet to use HD chain split\n"); + walletInstance->SetMinVersion(FEATURE_HD_SPLIT); + } + // Regenerate the keypool if upgraded to HD + if (hd_upgrade) { + if (!walletInstance->NewKeyPool()) { + InitError(_("Unable to generate keys") += "\n"); + return nullptr; + } + } + } + if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key @@ -4032,7 +4061,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); return nullptr; } - walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); + walletInstance->SetMinVersion(FEATURE_LATEST); // generate a new master key CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); -- cgit v1.2.3 From dfcd9f3e6abf3d53903227a085ff4cfecbfeb07f Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Sat, 21 Apr 2018 03:10:12 -0400 Subject: 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. --- src/wallet/wallet.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 8 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 57dc6c3b7..26eb7198f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -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()) { @@ -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); @@ -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; @@ -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& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool; + std::set& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; // Get the oldest key if(setKeyPool.empty()) @@ -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"); } @@ -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); } @@ -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; @@ -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 *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool; + if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); + std::set *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); auto it = setKeyPool->begin(); WalletBatch batch(*database); @@ -3955,6 +3968,24 @@ std::vector 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; @@ -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); @@ -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); @@ -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; } @@ -4275,6 +4313,7 @@ CKeyPool::CKeyPool() { nTime = GetTime(); fInternal = false; + m_pre_split = false; } CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) @@ -4282,6 +4321,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) nTime = GetTime(); vchPubKey = vchPubKeyIn; fInternal = internalIn; + m_pre_split = false; } CWalletKey::CWalletKey(int64_t nExpires) -- cgit v1.2.3 From a8da482a8bc87ff26194612727d4a7b86b2fb60d Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 27 Apr 2018 23:32:59 -0400 Subject: Bump wallet version for pre split keypool Bump the wallet version to indicate support for the pre split keypool. Also prevents any wallets from upgrading to versions between HD_SPLIT and PRE_SPLIT_KEYPOOL. --- src/wallet/wallet.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 26eb7198f..74f79a011 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4060,6 +4060,14 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& // Upgrade to HD if explicit upgrade if (gArgs.GetBoolArg("-upgradewallet", false)) { LOCK(walletInstance->cs_wallet); + + // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT + int max_version = walletInstance->nWalletVersion; + if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) { + InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.")); + return nullptr; + } + bool hd_upgrade = false; bool split_upgrade = false; if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) { @@ -4076,7 +4084,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& // Upgrade to HD chain split if necessary if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { LogPrintf("Upgrading wallet to use HD chain split\n"); - walletInstance->SetMinVersion(FEATURE_HD_SPLIT); + walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); split_upgrade = FEATURE_HD_SPLIT > prev_version; } // Mark all keys currently in the keypool as pre-split -- cgit v1.2.3