Skip to content

Commit cc1a60b

Browse files
committed
[Wallet] Upgrade to HD flow with -upgradewallet flag.
* Essentially marks non HD wallet keys as pre_split keys, stop creating pre_split keys and creates a new HD seed and HD keypool to be used from there on.
1 parent 25ee29b commit cc1a60b

File tree

5 files changed

+88
-1
lines changed

5 files changed

+88
-1
lines changed

src/init.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,7 @@ bool AppInit2()
16711671
strErrors << _("Error loading wallet.dat") << "\n";
16721672
}
16731673

1674+
int prev_version = pwalletMain->GetVersion();
16741675
if (GetBoolArg("-upgradewallet", fFirstRun)) {
16751676
int nMaxVersion = GetArg("-upgradewallet", 0);
16761677
if (nMaxVersion == 0) // the -upgradewallet without argument case
@@ -1685,6 +1686,12 @@ bool AppInit2()
16851686
pwalletMain->SetMaxVersion(nMaxVersion);
16861687
}
16871688

1689+
// Upgrade to HD if explicit upgrade was requested.
1690+
std::string upgradeError;
1691+
if (!pwalletMain->Upgrade(upgradeError, prev_version)) {
1692+
strErrors << upgradeError << "\n";
1693+
}
1694+
16881695
if (fFirstRun) {
16891696
// Create new HD Wallet
16901697
LogPrintf("Creating HD Wallet\n");

src/wallet/scriptpubkeyman.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,37 @@ bool ScriptPubKeyMan::SetupGeneration(bool force)
2020
return true;
2121
}
2222

23+
bool ScriptPubKeyMan::Upgrade(const int& prev_version, std::string& error)
24+
{
25+
LOCK(wallet->cs_KeyStore);
26+
error = "";
27+
bool hd_upgrade = false;
28+
bool split_upgrade = false;
29+
if (wallet->CanSupportFeature(FEATURE_HD_SPLIT) && !IsHDEnabled()) {
30+
LogPrintf("Upgrading wallet to use HD chain split\n");
31+
wallet->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
32+
33+
// generate a new master key
34+
CPubKey masterPubKey = GenerateNewSeed();
35+
SetHDSeed(masterPubKey);
36+
hd_upgrade = true;
37+
split_upgrade = FEATURE_HD_SPLIT > prev_version;
38+
}
39+
40+
// Mark all keys currently in the keypool as pre-split
41+
if (split_upgrade) {
42+
MarkPreSplitKeys();
43+
}
44+
// Regenerate the keypool if upgraded to HD
45+
if (hd_upgrade) {
46+
if (!TopUp()) {
47+
error = _("Unable to generate keys");
48+
return false;
49+
}
50+
}
51+
return true;
52+
}
53+
2354
bool ScriptPubKeyMan::CanGenerateKeys()
2455
{
2556
// A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD)
@@ -231,6 +262,24 @@ void ScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
231262
}
232263
}
233264

265+
void ScriptPubKeyMan::MarkPreSplitKeys()
266+
{
267+
CWalletDB batch(wallet->strWalletFile);
268+
for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
269+
int64_t index = *it;
270+
CKeyPool keypool;
271+
if (!batch.ReadPool(index, keypool)) {
272+
throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
273+
}
274+
keypool.m_pre_split = true;
275+
if (!batch.WritePool(index, keypool)) {
276+
throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
277+
}
278+
set_pre_split_keypool.insert(index);
279+
it = setExternalKeyPool.erase(it);
280+
}
281+
}
282+
234283
/**
235284
* Mark old keypool keys as used,
236285
* and generate all new keys

src/wallet/scriptpubkeyman.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class ScriptPubKeyMan {
3737
*/
3838
bool SetupGeneration(bool force = false);
3939

40+
/** Upgrades the wallet to the specified version */
41+
bool Upgrade(const int& prev_version, std::string& error);
42+
4043
/* Returns true if the wallet can generate new keys */
4144
bool CanGenerateKeys();
4245

@@ -71,6 +74,8 @@ class ScriptPubKeyMan {
7174
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
7275
//! Key pool
7376
bool NewKeyPool();
77+
//! Update pre HD keys in db with the pre-split flag enabled.
78+
void MarkPreSplitKeys();
7479

7580
/** Fills internal address pool. Use within ScriptPubKeyMan implementations should be used sparingly and only
7681
* when something from the address pool is removed, excluding GetNewDestination and GetReservedDestination.

src/wallet/wallet.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,24 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb)
15421542
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
15431543
}
15441544

1545+
bool CWallet::Upgrade(std::string& error, const int& prevVersion)
1546+
{
1547+
// Upgrade to HD if explicit upgrade
1548+
if (GetBoolArg("-upgradewallet", false)) {
1549+
LOCK(cs_wallet);
1550+
1551+
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
1552+
int max_version = GetVersion();
1553+
if (!CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
1554+
error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.");
1555+
return false;
1556+
}
1557+
1558+
return m_spk_man->Upgrade(prevVersion, error);
1559+
}
1560+
return true;
1561+
}
1562+
15451563
/**
15461564
* Scan the block chain (starting in pindexStart) for transactions
15471565
* from or to us. If fUpdate is true, found transactions that already

src/wallet/wallet.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ enum WalletFeature {
8686

8787
FEATURE_HD_SPLIT = 139900, // Wallet with HD chain split (change outputs will use m/0'/1'/k)
8888

89-
FEATURE_LATEST = FEATURE_HD_SPLIT
89+
FEATURE_PRE_SPLIT_KEYPOOL = 169900, // Upgraded to HD SPLIT and can have a pre-split keypool
90+
91+
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
9092
};
9193

9294
enum AvailableCoinsType {
@@ -408,6 +410,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
408410
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
409411
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
410412
void EraseFromWallet(const uint256& hash);
413+
414+
/**
415+
* Upgrade wallet to HD if needed. Does nothing if not.
416+
*/
417+
bool Upgrade(std::string& error, const int& prevVersion);
418+
411419
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false, bool fromStartup = false);
412420
void ReacceptWalletTransactions(bool fFirstLoad = false);
413421
void ResendWalletTransactions();

0 commit comments

Comments
 (0)