Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/client.cpp
// Copyright (c) 2010 Satoshi Nakamoto | // Copyright (c) 2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "rpc/client.h" | #include "rpc/client.h" | ||||
#include "rpc/protocol.h" | #include "rpc/protocol.h" | ||||
#include "util.h" | #include "util.h" | ||||
#include <cstdint> | |||||
#include <set> | #include <set> | ||||
#include <stdint.h> | |||||
#include <boost/algorithm/string/case_conv.hpp> // for to_lower() | #include <boost/algorithm/string/case_conv.hpp> // for to_lower() | ||||
#include <univalue.h> | #include <univalue.h> | ||||
using namespace std; | using namespace std; | ||||
class CRPCConvertParam | class CRPCConvertParam { | ||||
{ | |||||
public: | public: | ||||
std::string methodName; //!< method whose params want conversion | std::string methodName; //!< method whose params want conversion | ||||
int paramIdx; //!< 0-based idx of param to convert | int paramIdx; //!< 0-based idx of param to convert | ||||
std::string paramName; //!< parameter name | std::string paramName; //!< parameter name | ||||
}; | }; | ||||
/** | /** | ||||
* Specifiy a (method, idx, name) here if the argument is a non-string RPC | * Specifiy a (method, idx, name) here if the argument is a non-string RPC | ||||
* argument and needs to be converted from JSON. | * argument and needs to be converted from JSON. | ||||
* | * | ||||
* @note Parameter indexes start from 0. | * @note Parameter indexes start from 0. | ||||
*/ | */ | ||||
static const CRPCConvertParam vRPCConvertParams[] = | static const CRPCConvertParam vRPCConvertParams[] = { | ||||
{ | |||||
{ "setmocktime", 0, "timestamp" }, | {"setmocktime", 0, "timestamp"}, | ||||
{ "generate", 0, "nblocks" }, | {"generate", 0, "nblocks"}, | ||||
{ "generate", 1, "maxtries" }, | {"generate", 1, "maxtries"}, | ||||
{ "generatetoaddress", 0, "nblocks" }, | {"generatetoaddress", 0, "nblocks"}, | ||||
{ "generatetoaddress", 2, "maxtries" }, | {"generatetoaddress", 2, "maxtries"}, | ||||
{ "getnetworkhashps", 0, "nblocks" }, | {"getnetworkhashps", 0, "nblocks"}, | ||||
{ "getnetworkhashps", 1, "height" }, | {"getnetworkhashps", 1, "height"}, | ||||
{ "sendtoaddress", 1, "amount" }, | {"sendtoaddress", 1, "amount"}, | ||||
{ "sendtoaddress", 4, "subtractfeefromamount" }, | {"sendtoaddress", 4, "subtractfeefromamount"}, | ||||
{ "settxfee", 0, "amount" }, | {"settxfee", 0, "amount"}, | ||||
{ "getreceivedbyaddress", 1, "minconf" }, | {"getreceivedbyaddress", 1, "minconf"}, | ||||
{ "getreceivedbyaccount", 1, "minconf" }, | {"getreceivedbyaccount", 1, "minconf"}, | ||||
{ "listreceivedbyaddress", 0, "minconf" }, | {"listreceivedbyaddress", 0, "minconf"}, | ||||
{ "listreceivedbyaddress", 1, "include_empty" }, | {"listreceivedbyaddress", 1, "include_empty"}, | ||||
{ "listreceivedbyaddress", 2, "include_watchonly" }, | {"listreceivedbyaddress", 2, "include_watchonly"}, | ||||
{ "listreceivedbyaccount", 0, "minconf" }, | {"listreceivedbyaccount", 0, "minconf"}, | ||||
{ "listreceivedbyaccount", 1, "include_empty" }, | {"listreceivedbyaccount", 1, "include_empty"}, | ||||
{ "listreceivedbyaccount", 2, "include_watchonly" }, | {"listreceivedbyaccount", 2, "include_watchonly"}, | ||||
{ "getbalance", 1, "minconf" }, | {"getbalance", 1, "minconf"}, | ||||
{ "getbalance", 2, "include_watchonly" }, | {"getbalance", 2, "include_watchonly"}, | ||||
{ "getblockhash", 0, "height" }, | {"getblockhash", 0, "height"}, | ||||
{ "waitforblockheight", 0, "height" }, | {"waitforblockheight", 0, "height"}, | ||||
{ "waitforblockheight", 1, "timeout" }, | {"waitforblockheight", 1, "timeout"}, | ||||
{ "waitforblock", 1, "timeout" }, | {"waitforblock", 1, "timeout"}, | ||||
{ "waitfornewblock", 0, "timeout" }, | {"waitfornewblock", 0, "timeout"}, | ||||
{ "move", 2, "amount" }, | {"move", 2, "amount"}, | ||||
{ "move", 3, "minconf" }, | {"move", 3, "minconf"}, | ||||
{ "sendfrom", 2, "amount" }, | {"sendfrom", 2, "amount"}, | ||||
{ "sendfrom", 3, "minconf" }, | {"sendfrom", 3, "minconf"}, | ||||
{ "listtransactions", 1, "count" }, | {"listtransactions", 1, "count"}, | ||||
{ "listtransactions", 2, "skip" }, | {"listtransactions", 2, "skip"}, | ||||
{ "listtransactions", 3, "include_watchonly" }, | {"listtransactions", 3, "include_watchonly"}, | ||||
{ "listaccounts", 0, "minconf" }, | {"listaccounts", 0, "minconf"}, | ||||
{ "listaccounts", 1, "include_watchonly" }, | {"listaccounts", 1, "include_watchonly"}, | ||||
{ "walletpassphrase", 1, "timeout" }, | {"walletpassphrase", 1, "timeout"}, | ||||
{ "getblocktemplate", 0, "template_request" }, | {"getblocktemplate", 0, "template_request"}, | ||||
{ "listsinceblock", 1, "target_confirmations" }, | {"listsinceblock", 1, "target_confirmations"}, | ||||
{ "listsinceblock", 2, "include_watchonly" }, | {"listsinceblock", 2, "include_watchonly"}, | ||||
{ "sendmany", 1, "amounts" }, | {"sendmany", 1, "amounts"}, | ||||
{ "sendmany", 2, "minconf" }, | {"sendmany", 2, "minconf"}, | ||||
{ "sendmany", 4, "subtractfeefrom" }, | {"sendmany", 4, "subtractfeefrom"}, | ||||
{ "addmultisigaddress", 0, "nrequired" }, | {"addmultisigaddress", 0, "nrequired"}, | ||||
{ "addmultisigaddress", 1, "keys" }, | {"addmultisigaddress", 1, "keys"}, | ||||
{ "createmultisig", 0, "nrequired" }, | {"createmultisig", 0, "nrequired"}, | ||||
{ "createmultisig", 1, "keys" }, | {"createmultisig", 1, "keys"}, | ||||
{ "listunspent", 0, "minconf" }, | {"listunspent", 0, "minconf"}, | ||||
{ "listunspent", 1, "maxconf" }, | {"listunspent", 1, "maxconf"}, | ||||
{ "listunspent", 2, "addresses" }, | {"listunspent", 2, "addresses"}, | ||||
{ "getblock", 1, "verbose" }, | {"getblock", 1, "verbose"}, | ||||
{ "getblockheader", 1, "verbose" }, | {"getblockheader", 1, "verbose"}, | ||||
{ "gettransaction", 1, "include_watchonly" }, | {"gettransaction", 1, "include_watchonly"}, | ||||
{ "getrawtransaction", 1, "verbose" }, | {"getrawtransaction", 1, "verbose"}, | ||||
{ "createrawtransaction", 0, "inputs" }, | {"createrawtransaction", 0, "inputs"}, | ||||
{ "createrawtransaction", 1, "outputs" }, | {"createrawtransaction", 1, "outputs"}, | ||||
{ "createrawtransaction", 2, "locktime" }, | {"createrawtransaction", 2, "locktime"}, | ||||
{ "signrawtransaction", 1, "prevtxs" }, | {"signrawtransaction", 1, "prevtxs"}, | ||||
{ "signrawtransaction", 2, "privkeys" }, | {"signrawtransaction", 2, "privkeys"}, | ||||
{ "sendrawtransaction", 1, "allowhighfees" }, | {"sendrawtransaction", 1, "allowhighfees"}, | ||||
{ "fundrawtransaction", 1, "options" }, | {"fundrawtransaction", 1, "options"}, | ||||
{ "gettxout", 1, "n" }, | {"gettxout", 1, "n"}, | ||||
{ "gettxout", 2, "include_mempool" }, | {"gettxout", 2, "include_mempool"}, | ||||
{ "gettxoutproof", 0, "txids" }, | {"gettxoutproof", 0, "txids"}, | ||||
{ "lockunspent", 0, "unlock" }, | {"lockunspent", 0, "unlock"}, | ||||
{ "lockunspent", 1, "transactions" }, | {"lockunspent", 1, "transactions"}, | ||||
{ "importprivkey", 2, "rescan" }, | {"importprivkey", 2, "rescan"}, | ||||
{ "importaddress", 2, "rescan" }, | {"importaddress", 2, "rescan"}, | ||||
{ "importaddress", 3, "p2sh" }, | {"importaddress", 3, "p2sh"}, | ||||
{ "importpubkey", 2, "rescan" }, | {"importpubkey", 2, "rescan"}, | ||||
{ "importmulti", 0, "requests" }, | {"importmulti", 0, "requests"}, | ||||
{ "importmulti", 1, "options" }, | {"importmulti", 1, "options"}, | ||||
{ "verifychain", 0, "checklevel" }, | {"verifychain", 0, "checklevel"}, | ||||
{ "verifychain", 1, "nblocks" }, | {"verifychain", 1, "nblocks"}, | ||||
{ "pruneblockchain", 0, "height" }, | {"pruneblockchain", 0, "height"}, | ||||
{ "keypoolrefill", 0, "newsize" }, | {"keypoolrefill", 0, "newsize"}, | ||||
{ "getrawmempool", 0, "verbose" }, | {"getrawmempool", 0, "verbose"}, | ||||
{ "estimatefee", 0, "nblocks" }, | {"estimatefee", 0, "nblocks"}, | ||||
{ "estimatepriority", 0, "nblocks" }, | {"estimatepriority", 0, "nblocks"}, | ||||
{ "estimatesmartfee", 0, "nblocks" }, | {"estimatesmartfee", 0, "nblocks"}, | ||||
{ "estimatesmartpriority", 0, "nblocks" }, | {"estimatesmartpriority", 0, "nblocks"}, | ||||
{ "prioritisetransaction", 1, "priority_delta" }, | {"prioritisetransaction", 1, "priority_delta"}, | ||||
{ "prioritisetransaction", 2, "fee_delta" }, | {"prioritisetransaction", 2, "fee_delta"}, | ||||
{ "setban", 2, "bantime" }, | {"setban", 2, "bantime"}, | ||||
{ "setban", 3, "absolute" }, | {"setban", 3, "absolute"}, | ||||
{ "setnetworkactive", 0, "state" }, | {"setnetworkactive", 0, "state"}, | ||||
{ "getmempoolancestors", 1, "verbose" }, | {"getmempoolancestors", 1, "verbose"}, | ||||
{ "getmempooldescendants", 1, "verbose" }, | {"getmempooldescendants", 1, "verbose"}, | ||||
// Echo with conversion (For testing only) | // Echo with conversion (For testing only) | ||||
{ "echojson", 0, "arg0" }, | {"echojson", 0, "arg0"}, | ||||
{ "echojson", 1, "arg1" }, | {"echojson", 1, "arg1"}, | ||||
{ "echojson", 2, "arg2" }, | {"echojson", 2, "arg2"}, | ||||
{ "echojson", 3, "arg3" }, | {"echojson", 3, "arg3"}, | ||||
{ "echojson", 4, "arg4" }, | {"echojson", 4, "arg4"}, | ||||
{ "echojson", 5, "arg5" }, | {"echojson", 5, "arg5"}, | ||||
{ "echojson", 6, "arg6" }, | {"echojson", 6, "arg6"}, | ||||
{ "echojson", 7, "arg7" }, | {"echojson", 7, "arg7"}, | ||||
{ "echojson", 8, "arg8" }, | {"echojson", 8, "arg8"}, | ||||
{ "echojson", 9, "arg9" }, | {"echojson", 9, "arg9"}, | ||||
}; | }; | ||||
class CRPCConvertTable | class CRPCConvertTable { | ||||
{ | |||||
private: | private: | ||||
std::set<std::pair<std::string, int>> members; | std::set<std::pair<std::string, int>> members; | ||||
std::set<std::pair<std::string, std::string>> membersByName; | std::set<std::pair<std::string, std::string>> membersByName; | ||||
public: | public: | ||||
CRPCConvertTable(); | CRPCConvertTable(); | ||||
bool convert(const std::string& method, int idx) { | bool convert(const std::string &method, int idx) { | ||||
return (members.count(std::make_pair(method, idx)) > 0); | return (members.count(std::make_pair(method, idx)) > 0); | ||||
} | } | ||||
bool convert(const std::string& method, const std::string& name) { | bool convert(const std::string &method, const std::string &name) { | ||||
return (membersByName.count(std::make_pair(method, name)) > 0); | return (membersByName.count(std::make_pair(method, name)) > 0); | ||||
} | } | ||||
}; | }; | ||||
CRPCConvertTable::CRPCConvertTable() | CRPCConvertTable::CRPCConvertTable() { | ||||
{ | |||||
const unsigned int n_elem = | const unsigned int n_elem = | ||||
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); | (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); | ||||
for (unsigned int i = 0; i < n_elem; i++) { | for (unsigned int i = 0; i < n_elem; i++) { | ||||
members.insert(std::make_pair(vRPCConvertParams[i].methodName, | members.insert(std::make_pair(vRPCConvertParams[i].methodName, | ||||
vRPCConvertParams[i].paramIdx)); | vRPCConvertParams[i].paramIdx)); | ||||
membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, | membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, | ||||
vRPCConvertParams[i].paramName)); | vRPCConvertParams[i].paramName)); | ||||
} | } | ||||
} | } | ||||
static CRPCConvertTable rpcCvtTable; | static CRPCConvertTable rpcCvtTable; | ||||
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) | /** | ||||
* as well as objects and arrays. | * 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 ParseNonRFCJSONValue(const std::string &strVal) { | ||||
{ | |||||
UniValue jVal; | UniValue jVal; | ||||
if (!jVal.read(std::string("[")+strVal+std::string("]")) || | if (!jVal.read(std::string("[") + strVal + std::string("]")) || | ||||
!jVal.isArray() || jVal.size()!=1) | !jVal.isArray() || jVal.size() != 1) | ||||
throw runtime_error(string("Error parsing JSON:")+strVal); | throw runtime_error(string("Error parsing JSON:") + strVal); | ||||
return jVal[0]; | return jVal[0]; | ||||
} | } | ||||
UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) | UniValue RPCConvertValues(const std::string &strMethod, | ||||
{ | const std::vector<std::string> &strParams) { | ||||
UniValue params(UniValue::VARR); | UniValue params(UniValue::VARR); | ||||
for (unsigned int idx = 0; idx < strParams.size(); idx++) { | for (unsigned int idx = 0; idx < strParams.size(); idx++) { | ||||
const std::string& strVal = strParams[idx]; | const std::string &strVal = strParams[idx]; | ||||
if (!rpcCvtTable.convert(strMethod, idx)) { | if (!rpcCvtTable.convert(strMethod, idx)) { | ||||
// insert string value directly | // insert string value directly | ||||
params.push_back(strVal); | params.push_back(strVal); | ||||
} else { | } else { | ||||
// parse string as JSON, insert bool/number/object/etc. value | // parse string as JSON, insert bool/number/object/etc. value | ||||
params.push_back(ParseNonRFCJSONValue(strVal)); | params.push_back(ParseNonRFCJSONValue(strVal)); | ||||
} | } | ||||
} | } | ||||
return params; | return params; | ||||
} | } | ||||
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams) | UniValue RPCConvertNamedValues(const std::string &strMethod, | ||||
{ | const std::vector<std::string> &strParams) { | ||||
UniValue params(UniValue::VOBJ); | UniValue params(UniValue::VOBJ); | ||||
for (const std::string &s: strParams) { | for (const std::string &s : strParams) { | ||||
size_t pos = s.find("="); | size_t pos = s.find("="); | ||||
if (pos == std::string::npos) { | 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)")); | 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 name = s.substr(0, pos); | ||||
std::string value = s.substr(pos+1); | std::string value = s.substr(pos + 1); | ||||
if (!rpcCvtTable.convert(strMethod, name)) { | if (!rpcCvtTable.convert(strMethod, name)) { | ||||
// insert string value directly | // insert string value directly | ||||
params.pushKV(name, value); | params.pushKV(name, value); | ||||
} else { | } else { | ||||
// parse string as JSON, insert bool/number/object/etc. value | // parse string as JSON, insert bool/number/object/etc. value | ||||
params.pushKV(name, ParseNonRFCJSONValue(value)); | params.pushKV(name, ParseNonRFCJSONValue(value)); | ||||
} | } | ||||
} | } | ||||
return params; | return params; | ||||
} | } |