Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade encrypted wallet #16360

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Expand Up @@ -151,6 +151,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
{ "upgradewallet", 0, "wallet_version" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
Expand Down
49 changes: 49 additions & 0 deletions src/wallet/rpcwallet.cpp
Expand Up @@ -4277,6 +4277,54 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
return result;
}

static UniValue upgradewallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();

if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}

if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet to the last version (default) or to a specific version.\n",
{
{"version", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "A specific version to which upgrade the wallet."},
},
RPCResult{
" \"wallet_version\" : version (numeric) The new wallet version\n"
},
RPCExamples{
"Upgrade wallet to the latest version: "
+ HelpExampleCli("upgradewallet", "")
},
}.ToString());

EnsureWalletIsUnlocked(pwallet);
LOCK(pwallet->cs_wallet);

int newVersion;
newVersion = request.params[0].isNull() ? FEATURE_LATEST : request.params[0].get_int();

if (!pwallet->CanSupportFeature(FEATURE_LATEST) && newVersion >= FEATURE_HD_SPLIT && newVersion < FEATURE_PRE_SPLIT_KEYPOOL)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot upgrade a non HD split wallet without upgrading to support pre split keypool.");
if (pwallet->GetVersion() > newVersion)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot downgrade the wallet.");
if (pwallet->GetVersion() == newVersion)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet already at latest version.");

pwallet->SetMaxVersion(newVersion);
if (!pwallet->UpgradeWallet())
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Cannot upgrade the wallet. Current version: %i.", pwallet->GetVersion()));

UniValue result(UniValue::VOBJ);
result.pushKV("wallet_version", pwallet->GetVersion());
return result;
}


UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue importprivkey(const JSONRPCRequest& request);
Expand Down Expand Up @@ -4343,6 +4391,7 @@ static const CRPCCommand commands[] =
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
{ "wallet", "upgradewallet", &upgradewallet, {"wallet_version"} },
{ "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
{ "wallet", "walletlock", &walletlock, {} },
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
Expand Down
51 changes: 26 additions & 25 deletions src/wallet/wallet.cpp
Expand Up @@ -4080,6 +4080,31 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
}

bool CWallet::UpgradeWallet()
{
LOCK(cs_wallet);

int previousVersion = GetVersion();
if (CanSupportFeature(FEATURE_HD) && !IsHDEnabled()) {
WalletLogPrintf("Upgrading wallet to HD\n");
SetMinVersion(FEATURE_HD);

// generate a new master key
CPubKey masterPubKey = GenerateNewSeed();
SetHDSeed(masterPubKey);
}
// Upgrade to HD chain split if necessary
if (CanSupportFeature(FEATURE_HD_SPLIT)) {
WalletLogPrintf("Upgrading wallet to use HD chain split\n");
SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);

// Mark all keys currently in the keypool as pre-split
MarkPreSplitKeys();
}

return nWalletVersion > previousVersion;
}

std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{
const std::string& walletFile = WalletDataFilePath(location.GetPath()).string();
Expand Down Expand Up @@ -4133,7 +4158,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}

int prev_version = walletInstance->GetVersion();
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
Expand All @@ -4156,37 +4180,14 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// 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->GetVersion();
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
chain.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()) {
walletInstance->WalletLogPrintf("Upgrading wallet to HD\n");
walletInstance->SetMinVersion(FEATURE_HD);

// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewSeed();
walletInstance->SetHDSeed(masterPubKey);
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
walletInstance->WalletLogPrintf("Upgrading wallet to use HD chain split\n");
walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
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->UpgradeWallet()) {
if (!walletInstance->TopUpKeyPool()) {
chain.initError(_("Unable to generate keys"));
return nullptr;
Expand Down
3 changes: 3 additions & 0 deletions src/wallet/wallet.h
Expand Up @@ -1246,6 +1246,9 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);

/** Upgrade, if supported, the minimum version to HD wallet or HD_SPLIT wallet **/
bool UpgradeWallet();

//! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string);

Expand Down