diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9a7861f978ee..148c77675021 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -30,6 +30,7 @@ #include // For StartShutdown +#include #include #include @@ -3306,6 +3307,68 @@ UniValue bumpfee(const JSONRPCRequest& request) return result; } +UniValue downgradewallet(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() != 1) { + std::string msg = "downgradewallet \"version\"\n" + "\nDowngrades wallet for compatibilty with earlier versions. Requires a new wallet backup.\n" + + "\nArguments:\n" + "1. \"version\" (string, required) A previous version of Bitcoin Core\n" + + "\nSupported versions:\n" + "0.12 Removes HD seed. Generated addresses are preserved.\n" + " Note that non-HD wallets currently can't be upgraded to HD.\n" + ; + throw std::runtime_error(msg); + } + + // Check version argument + + uint32_t version; + std::string version_s = request.params[0].get_str(); + std::smatch match; + const std::regex regex("^(\\d+)\\.(\\d+)$"); + if (std::regex_match (version_s, match, regex)) { + uint32_t version_major = std::stoi(match[1]); + uint32_t version_minor = std::stoi(match[2]); + version = 1000000 * version_major + 10000 * version_minor; + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid version format, use e.g. 0.12"); + } + + // Check against array of supported downgrade versions + const std::array supported_versions = {{120000}}; + + bool supported_version = std::any_of(supported_versions.begin(), supported_versions.end(), [&](uint32_t i) + { + return i == version; + }); + + if (!supported_version) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet downgrade to " + version_s + " not supported"); + } + + // Future code to downgrade to versions above 0.12 goes here. + + if (version <= 120000) { // Downgrade to 0.12 (remove HD) + // Check if HD seed exists + if(pwallet->IsHDEnabled()) { + // Delete hd key + pwallet->DeleteHDChain(false); + } + } + + // Future code to downgrade to versions below 0.12 goes here. + + return NullUniValue; +} + UniValue generate(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3449,6 +3512,7 @@ static const CRPCCommand commands[] = { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "downgradewallet", &downgradewallet, {"version"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index edc1ca6ef88b..b2851b67a452 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1493,6 +1493,18 @@ bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) return true; } +bool CWallet::DeleteHDChain(bool memonly) +{ + LOCK(cs_wallet); + + hdChain.SetNull(); + + if (!memonly && !CWalletDB(*dbw).WriteHDChain(hdChain)) + throw std::runtime_error(std::string(__func__) + ": deleting chain failed"); + + return true; +} + bool CWallet::IsHDEnabled() const { return !hdChain.masterKeyID.IsNull(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1bd0be7bd034..21f77cf0a7e9 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1106,6 +1106,9 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /* Set the HD chain model (chain child index counters) */ bool SetHDChain(const CHDChain& chain, bool memonly); const CHDChain& GetHDChain() const { return hdChain; } + + /* Delete the HD chain model */ + bool DeleteHDChain(bool memonly); /* Returns true if HD is enabled */ bool IsHDEnabled() const;