mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-05 10:17:30 -05:00
b470c75847
0d32d66148
Remove -upgradewallet startup option (Andrew Chow)92263cce5b
Add upgradewallet RPC (Andrew Chow)1e48796c99
Make UpgradeWallet a member function of CWallet (Andrew Chow)c988f27937
Have UpgradeWallet take the version to upgrade to and an error message out parameter (Andrew Chow)1833237123
Only run UpgradeWallet if the wallet needs to be upgraded (Andrew Chow)9c16b1735f
Move wallet upgrading to its own function (Andrew Chow) Pull request description: `-upgradewallet` is largely incompatible with many recent wallet features and versions. For example, it was disabled if multiple wallets were used and would not work with encrypted wallets that were being upgraded to HD. This PR does away with the old method of upgrading upon startup and instead allows users to upgrade their wallets via an `upgradewallet` RPC. This does largely the same thing as the old `-upgradewallet` option but because the wallet is loaded, it can be unlocked to upgrade to HD. Furthermore it is compatible with multiwallet as it works on the individual wallet that is specified by the RPC. ACKs for top commit: meshcollider: Code review ACK0d32d66148
darosior: ACK0d32d66148
MarcoFalke: ACK0d32d66148
🚵 Tree-SHA512: b425bf6f5d605e26506889d63c780895482f07cbc086193218e031e8504d3072d41e90d65cd41bcc98ee4c1eb048954bc5d4ac85435f7394892373aac89a3b0a
264 lines
9.4 KiB
C++
264 lines
9.4 KiB
C++
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <rpc/client.h>
|
|
#include <util/system.h>
|
|
|
|
#include <set>
|
|
#include <stdint.h>
|
|
|
|
class CRPCConvertParam
|
|
{
|
|
public:
|
|
std::string methodName; //!< method whose params want conversion
|
|
int paramIdx; //!< 0-based idx of param to convert
|
|
std::string paramName; //!< parameter name
|
|
};
|
|
|
|
// clang-format off
|
|
/**
|
|
* Specify a (method, idx, name) here if the argument is a non-string RPC
|
|
* argument and needs to be converted from JSON.
|
|
*
|
|
* @note Parameter indexes start from 0.
|
|
*/
|
|
static const CRPCConvertParam vRPCConvertParams[] =
|
|
{
|
|
{ "setmocktime", 0, "timestamp" },
|
|
{ "mockscheduler", 0, "delta_time" },
|
|
{ "utxoupdatepsbt", 1, "descriptors" },
|
|
{ "generatetoaddress", 0, "nblocks" },
|
|
{ "generatetoaddress", 2, "maxtries" },
|
|
{ "generatetodescriptor", 0, "num_blocks" },
|
|
{ "generatetodescriptor", 2, "maxtries" },
|
|
{ "generateblock", 1, "transactions" },
|
|
{ "getnetworkhashps", 0, "nblocks" },
|
|
{ "getnetworkhashps", 1, "height" },
|
|
{ "sendtoaddress", 1, "amount" },
|
|
{ "sendtoaddress", 4, "subtractfeefromamount" },
|
|
{ "sendtoaddress", 5 , "replaceable" },
|
|
{ "sendtoaddress", 6 , "conf_target" },
|
|
{ "sendtoaddress", 8, "avoid_reuse" },
|
|
{ "settxfee", 0, "amount" },
|
|
{ "sethdseed", 0, "newkeypool" },
|
|
{ "getreceivedbyaddress", 1, "minconf" },
|
|
{ "getreceivedbylabel", 1, "minconf" },
|
|
{ "listreceivedbyaddress", 0, "minconf" },
|
|
{ "listreceivedbyaddress", 1, "include_empty" },
|
|
{ "listreceivedbyaddress", 2, "include_watchonly" },
|
|
{ "listreceivedbylabel", 0, "minconf" },
|
|
{ "listreceivedbylabel", 1, "include_empty" },
|
|
{ "listreceivedbylabel", 2, "include_watchonly" },
|
|
{ "getbalance", 1, "minconf" },
|
|
{ "getbalance", 2, "include_watchonly" },
|
|
{ "getbalance", 3, "avoid_reuse" },
|
|
{ "getblockhash", 0, "height" },
|
|
{ "waitforblockheight", 0, "height" },
|
|
{ "waitforblockheight", 1, "timeout" },
|
|
{ "waitforblock", 1, "timeout" },
|
|
{ "waitfornewblock", 0, "timeout" },
|
|
{ "listtransactions", 1, "count" },
|
|
{ "listtransactions", 2, "skip" },
|
|
{ "listtransactions", 3, "include_watchonly" },
|
|
{ "walletpassphrase", 1, "timeout" },
|
|
{ "getblocktemplate", 0, "template_request" },
|
|
{ "listsinceblock", 1, "target_confirmations" },
|
|
{ "listsinceblock", 2, "include_watchonly" },
|
|
{ "listsinceblock", 3, "include_removed" },
|
|
{ "sendmany", 1, "amounts" },
|
|
{ "sendmany", 2, "minconf" },
|
|
{ "sendmany", 4, "subtractfeefrom" },
|
|
{ "sendmany", 5 , "replaceable" },
|
|
{ "sendmany", 6 , "conf_target" },
|
|
{ "deriveaddresses", 1, "range" },
|
|
{ "scantxoutset", 1, "scanobjects" },
|
|
{ "addmultisigaddress", 0, "nrequired" },
|
|
{ "addmultisigaddress", 1, "keys" },
|
|
{ "createmultisig", 0, "nrequired" },
|
|
{ "createmultisig", 1, "keys" },
|
|
{ "listunspent", 0, "minconf" },
|
|
{ "listunspent", 1, "maxconf" },
|
|
{ "listunspent", 2, "addresses" },
|
|
{ "listunspent", 3, "include_unsafe" },
|
|
{ "listunspent", 4, "query_options" },
|
|
{ "getblock", 1, "verbosity" },
|
|
{ "getblock", 1, "verbose" },
|
|
{ "getblockheader", 1, "verbose" },
|
|
{ "getchaintxstats", 0, "nblocks" },
|
|
{ "gettransaction", 1, "include_watchonly" },
|
|
{ "gettransaction", 2, "verbose" },
|
|
{ "getrawtransaction", 1, "verbose" },
|
|
{ "createrawtransaction", 0, "inputs" },
|
|
{ "createrawtransaction", 1, "outputs" },
|
|
{ "createrawtransaction", 2, "locktime" },
|
|
{ "createrawtransaction", 3, "replaceable" },
|
|
{ "decoderawtransaction", 1, "iswitness" },
|
|
{ "signrawtransactionwithkey", 1, "privkeys" },
|
|
{ "signrawtransactionwithkey", 2, "prevtxs" },
|
|
{ "signrawtransactionwithwallet", 1, "prevtxs" },
|
|
{ "sendrawtransaction", 1, "maxfeerate" },
|
|
{ "testmempoolaccept", 0, "rawtxs" },
|
|
{ "testmempoolaccept", 1, "maxfeerate" },
|
|
{ "combinerawtransaction", 0, "txs" },
|
|
{ "fundrawtransaction", 1, "options" },
|
|
{ "fundrawtransaction", 2, "iswitness" },
|
|
{ "walletcreatefundedpsbt", 0, "inputs" },
|
|
{ "walletcreatefundedpsbt", 1, "outputs" },
|
|
{ "walletcreatefundedpsbt", 2, "locktime" },
|
|
{ "walletcreatefundedpsbt", 3, "options" },
|
|
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
|
|
{ "walletprocesspsbt", 1, "sign" },
|
|
{ "walletprocesspsbt", 3, "bip32derivs" },
|
|
{ "createpsbt", 0, "inputs" },
|
|
{ "createpsbt", 1, "outputs" },
|
|
{ "createpsbt", 2, "locktime" },
|
|
{ "createpsbt", 3, "replaceable" },
|
|
{ "combinepsbt", 0, "txs"},
|
|
{ "joinpsbts", 0, "txs"},
|
|
{ "finalizepsbt", 1, "extract"},
|
|
{ "converttopsbt", 1, "permitsigdata"},
|
|
{ "converttopsbt", 2, "iswitness"},
|
|
{ "gettxout", 1, "n" },
|
|
{ "gettxout", 2, "include_mempool" },
|
|
{ "gettxoutproof", 0, "txids" },
|
|
{ "lockunspent", 0, "unlock" },
|
|
{ "lockunspent", 1, "transactions" },
|
|
{ "importprivkey", 2, "rescan" },
|
|
{ "importaddress", 2, "rescan" },
|
|
{ "importaddress", 3, "p2sh" },
|
|
{ "importpubkey", 2, "rescan" },
|
|
{ "importmulti", 0, "requests" },
|
|
{ "importmulti", 1, "options" },
|
|
{ "verifychain", 0, "checklevel" },
|
|
{ "verifychain", 1, "nblocks" },
|
|
{ "getblockstats", 0, "hash_or_height" },
|
|
{ "getblockstats", 1, "stats" },
|
|
{ "pruneblockchain", 0, "height" },
|
|
{ "keypoolrefill", 0, "newsize" },
|
|
{ "getrawmempool", 0, "verbose" },
|
|
{ "estimatesmartfee", 0, "conf_target" },
|
|
{ "estimaterawfee", 0, "conf_target" },
|
|
{ "estimaterawfee", 1, "threshold" },
|
|
{ "prioritisetransaction", 1, "dummy" },
|
|
{ "prioritisetransaction", 2, "fee_delta" },
|
|
{ "setban", 2, "bantime" },
|
|
{ "setban", 3, "absolute" },
|
|
{ "setnetworkactive", 0, "state" },
|
|
{ "setwalletflag", 1, "value" },
|
|
{ "getmempoolancestors", 1, "verbose" },
|
|
{ "getmempooldescendants", 1, "verbose" },
|
|
{ "bumpfee", 1, "options" },
|
|
{ "logging", 0, "include" },
|
|
{ "logging", 1, "exclude" },
|
|
{ "disconnectnode", 1, "nodeid" },
|
|
{ "upgradewallet", 0, "version" },
|
|
// Echo with conversion (For testing only)
|
|
{ "echojson", 0, "arg0" },
|
|
{ "echojson", 1, "arg1" },
|
|
{ "echojson", 2, "arg2" },
|
|
{ "echojson", 3, "arg3" },
|
|
{ "echojson", 4, "arg4" },
|
|
{ "echojson", 5, "arg5" },
|
|
{ "echojson", 6, "arg6" },
|
|
{ "echojson", 7, "arg7" },
|
|
{ "echojson", 8, "arg8" },
|
|
{ "echojson", 9, "arg9" },
|
|
{ "rescanblockchain", 0, "start_height"},
|
|
{ "rescanblockchain", 1, "stop_height"},
|
|
{ "createwallet", 1, "disable_private_keys"},
|
|
{ "createwallet", 2, "blank"},
|
|
{ "createwallet", 4, "avoid_reuse"},
|
|
{ "getnodeaddresses", 0, "count"},
|
|
{ "stop", 0, "wait" },
|
|
};
|
|
// clang-format on
|
|
|
|
class CRPCConvertTable
|
|
{
|
|
private:
|
|
std::set<std::pair<std::string, int>> members;
|
|
std::set<std::pair<std::string, std::string>> membersByName;
|
|
|
|
public:
|
|
CRPCConvertTable();
|
|
|
|
bool convert(const std::string& method, int idx) {
|
|
return (members.count(std::make_pair(method, idx)) > 0);
|
|
}
|
|
bool convert(const std::string& method, const std::string& name) {
|
|
return (membersByName.count(std::make_pair(method, name)) > 0);
|
|
}
|
|
};
|
|
|
|
CRPCConvertTable::CRPCConvertTable()
|
|
{
|
|
const unsigned int n_elem =
|
|
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0]));
|
|
|
|
for (unsigned int i = 0; i < n_elem; i++) {
|
|
members.insert(std::make_pair(vRPCConvertParams[i].methodName,
|
|
vRPCConvertParams[i].paramIdx));
|
|
membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName,
|
|
vRPCConvertParams[i].paramName));
|
|
}
|
|
}
|
|
|
|
static CRPCConvertTable rpcCvtTable;
|
|
|
|
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
|
|
* as well as objects and arrays.
|
|
*/
|
|
UniValue ParseNonRFCJSONValue(const std::string& strVal)
|
|
{
|
|
UniValue jVal;
|
|
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
|
|
!jVal.isArray() || jVal.size()!=1)
|
|
throw std::runtime_error(std::string("Error parsing JSON:")+strVal);
|
|
return jVal[0];
|
|
}
|
|
|
|
UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
|
{
|
|
UniValue params(UniValue::VARR);
|
|
|
|
for (unsigned int idx = 0; idx < strParams.size(); idx++) {
|
|
const std::string& strVal = strParams[idx];
|
|
|
|
if (!rpcCvtTable.convert(strMethod, idx)) {
|
|
// insert string value directly
|
|
params.push_back(strVal);
|
|
} else {
|
|
// parse string as JSON, insert bool/number/object/etc. value
|
|
params.push_back(ParseNonRFCJSONValue(strVal));
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
|
{
|
|
UniValue params(UniValue::VOBJ);
|
|
|
|
for (const std::string &s: strParams) {
|
|
size_t pos = s.find('=');
|
|
if (pos == std::string::npos) {
|
|
throw(std::runtime_error("No '=' in named argument '"+s+"', this needs to be present for every argument (even if it is empty)"));
|
|
}
|
|
|
|
std::string name = s.substr(0, pos);
|
|
std::string value = s.substr(pos+1);
|
|
|
|
if (!rpcCvtTable.convert(strMethod, name)) {
|
|
// insert string value directly
|
|
params.pushKV(name, value);
|
|
} else {
|
|
// parse string as JSON, insert bool/number/object/etc. value
|
|
params.pushKV(name, ParseNonRFCJSONValue(value));
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|