Add 'sethdseed' RPC to initialize or replace HD seed

This commit is contained in:
Chris Moore 2017-09-12 14:01:12 -07:00 committed by Andrew Chow
parent dd3c07acce
commit b5ba01a187
5 changed files with 80 additions and 0 deletions

View file

@ -70,6 +70,7 @@ namespace {
const QStringList historyFilter = QStringList() const QStringList historyFilter = QStringList()
<< "importprivkey" << "importprivkey"
<< "importmulti" << "importmulti"
<< "sethdseed"
<< "signmessagewithprivkey" << "signmessagewithprivkey"
<< "signrawtransaction" << "signrawtransaction"
<< "signrawtransactionwithkey" << "signrawtransactionwithkey"

View file

@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 5 , "replaceable" }, { "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" }, { "sendtoaddress", 6 , "conf_target" },
{ "settxfee", 0, "amount" }, { "settxfee", 0, "amount" },
{ "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" }, { "getreceivedbyaddress", 1, "minconf" },
{ "getreceivedbyaccount", 1, "minconf" }, { "getreceivedbyaccount", 1, "minconf" },
{ "getreceivedbylabel", 1, "minconf" }, { "getreceivedbylabel", 1, "minconf" },

View file

@ -4079,6 +4079,76 @@ static UniValue listlabels(const JSONRPCRequest& request)
return ret; return ret;
} }
UniValue sethdseed(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"sethdseed ( \"newkeypool\" \"seed\" )\n"
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
+ HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
" keypool will be used until it has been depleted.\n"
"2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdmaster=1\n"
"\nExamples:\n"
+ HelpExampleCli("sethdseed", "")
+ HelpExampleCli("sethdseed", "false")
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
);
}
if (IsInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
LOCK2(cs_main, pwallet->cs_wallet);
// Do not do anything to non-HD wallets
if (!pwallet->IsHDEnabled()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
}
EnsureWalletIsUnlocked(pwallet);
bool flush_key_pool = true;
if (!request.params[0].isNull()) {
flush_key_pool = request.params[0].get_bool();
}
CPubKey master_pub_key;
if (request.params[1].isNull()) {
master_pub_key = pwallet->GenerateNewHDMasterKey();
} else {
CKey key = DecodeSecret(request.params[1].get_str());
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
if (HaveKey(*pwallet, key)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
}
master_pub_key = pwallet->DeriveNewMasterHDKey(key);
}
pwallet->SetHDMasterKey(master_pub_key);
if (flush_key_pool) pwallet->NewKeyPool();
return NullUniValue;
}
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request); extern UniValue importprivkey(const JSONRPCRequest& request);
@ -4139,6 +4209,7 @@ static const CRPCCommand commands[] =
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
/** Account functions (deprecated) */ /** Account functions (deprecated) */
{ "wallet", "getaccountaddress", &getlabeladdress, {"account"} }, { "wallet", "getaccountaddress", &getlabeladdress, {"account"} },

View file

@ -1452,7 +1452,11 @@ CPubKey CWallet::GenerateNewHDMasterKey()
{ {
CKey key; CKey key;
key.MakeNewKey(true); key.MakeNewKey(true);
return DeriveNewMasterHDKey(key);
}
CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key)
{
int64_t nCreationTime = GetTime(); int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime); CKeyMetadata metadata(nCreationTime);

View file

@ -1127,6 +1127,9 @@ public:
/* Generates a new HD master key (will not be activated) */ /* Generates a new HD master key (will not be activated) */
CPubKey GenerateNewHDMasterKey(); CPubKey GenerateNewHDMasterKey();
/* Derives a new HD master key (will not be activated) */
CPubKey DeriveNewMasterHDKey(const CKey& key);
/* Set the current HD master key (will reset the chain child index counters) /* Set the current HD master key (will reset the chain child index counters)
Sets the master key's version based on the current wallet version (so the Sets the master key's version based on the current wallet version (so the
caller must ensure the current wallet version is correct before calling caller must ensure the current wallet version is correct before calling