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

fix(rpc): Improve upgradetohd #5455

Merged
merged 4 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createwallet", 1, "disable_private_keys"},
{ "createwallet", 2, "blank"},
{ "createwallet", 4, "avoid_reuse"},
{ "upgradetohd", 3, "rescan"},
{ "createwallet", 5, "load_on_startup"},
{ "loadwallet", 1, "load_on_startup"},
{ "unloadwallet", 1, "load_on_startup"},
Expand Down
110 changes: 59 additions & 51 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2665,67 +2665,66 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
}

LOCK(pwallet->cs_wallet);

// Do not do anything to HD wallets
if (pwallet->IsHDEnabled()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade a wallet to HD if it is already upgraded to HD.");
}

if (!pwallet->SetMaxVersion(FEATURE_HD)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot downgrade wallet");
}
bool generate_mnemonic = request.params[0].isNull() || request.params[0].get_str().empty();

if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
{
LOCK(pwallet->cs_wallet);

bool prev_encrypted = pwallet->IsCrypted();
// Do not do anything to HD wallets
if (pwallet->IsHDEnabled()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade a wallet to HD if it is already upgraded to HD.");
}

SecureString secureWalletPassphrase;
secureWalletPassphrase.reserve(100);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
if (request.params[2].isNull()) {
if (prev_encrypted) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Cannot upgrade encrypted wallet to HD without the wallet passphrase");
if (!pwallet->SetMaxVersion(FEATURE_HD)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot downgrade wallet");
}
} else {
secureWalletPassphrase = request.params[2].get_str().c_str();
if (!pwallet->Unlock(secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "The wallet passphrase entered was incorrect");

if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
}

bool generate_mnemonic = request.params[0].isNull() || request.params[0].get_str().empty();
bool prev_encrypted = pwallet->IsCrypted();

SecureString secureMnemonic;
secureMnemonic.reserve(256);
if (!generate_mnemonic) {
if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set mnemonic while still in Initial Block Download");
SecureString secureWalletPassphrase;
secureWalletPassphrase.reserve(100);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
if (request.params[2].isNull()) {
if (prev_encrypted) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Cannot upgrade encrypted wallet to HD without the wallet passphrase");
}
} else {
secureWalletPassphrase = request.params[2].get_str().c_str();
if (!pwallet->Unlock(secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "The wallet passphrase entered was incorrect");
}
}
secureMnemonic = request.params[0].get_str().c_str();
}

SecureString secureMnemonicPassphrase;
secureMnemonicPassphrase.reserve(256);
if (!request.params[1].isNull()) {
secureMnemonicPassphrase = request.params[1].get_str().c_str();
}

pwallet->WalletLogPrintf("Upgrading wallet to HD\n");
pwallet->SetMinVersion(FEATURE_HD);
SecureString secureMnemonic;
secureMnemonic.reserve(256);
if (!generate_mnemonic) {
secureMnemonic = request.params[0].get_str().c_str();
}

if (prev_encrypted) {
if (!pwallet->GenerateNewHDChainEncrypted(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to generate encrypted HD wallet");
SecureString secureMnemonicPassphrase;
secureMnemonicPassphrase.reserve(256);
if (!request.params[1].isNull()) {
secureMnemonicPassphrase = request.params[1].get_str().c_str();
}
} else {
spk_man->GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase);
if (!secureWalletPassphrase.empty()) {
if (!pwallet->EncryptWallet(secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to encrypt HD wallet");

pwallet->WalletLogPrintf("Upgrading wallet to HD\n");
pwallet->SetMinVersion(FEATURE_HD);

if (prev_encrypted) {
if (!pwallet->GenerateNewHDChainEncrypted(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to generate encrypted HD wallet");
}
} else {
spk_man->GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase);
if (!secureWalletPassphrase.empty()) {
if (!pwallet->EncryptWallet(secureWalletPassphrase)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to encrypt HD wallet");
}
}
}
}
Expand All @@ -2737,7 +2736,16 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
if (!reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}
pwallet->ScanForWalletTransactions(pwallet->chain().getBlockHash(0), 0, {}, reserver, true);
CWallet::ScanResult result = pwallet->ScanForWalletTransactions(pwallet->chain().getBlockHash(0), 0, {}, reserver, true);
switch (result.status) {
case CWallet::ScanResult::SUCCESS:
break;
case CWallet::ScanResult::FAILURE:
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
case CWallet::ScanResult::USER_ABORT:
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
// no default case, so the compiler can warn about missing cases
}
}

return true;
Expand Down
Loading