1 // Copyright (c) 2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2020 The Bitcoin Core developers 3 // Distributed under the MIT software license, see the accompanying 4 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 #include <rpc/client.h> 7 #include <util/system.h> 8 9 #include <set> 10 #include <stdint.h> 11 12 class CRPCConvertParam 13 { 14 public: 15 std::string methodName; //!< method whose params want conversion 16 int paramIdx; //!< 0-based idx of param to convert 17 std::string paramName; //!< parameter name 18 }; 19 20 // clang-format off 21 /** 22 * Specify a (method, idx, name) here if the argument is a non-string RPC 23 * argument and needs to be converted from JSON. 24 * 25 * @note Parameter indexes start from 0. 26 */ 27 static const CRPCConvertParam vRPCConvertParams[] = 28 { 29 { "setmocktime", 0, "timestamp" }, 30 { "mockscheduler", 0, "delta_time" }, 31 { "utxoupdatepsbt", 1, "descriptors" }, 32 { "generatetoaddress", 0, "nblocks" }, 33 { "generatetoaddress", 2, "maxtries" }, 34 { "generatetodescriptor", 0, "num_blocks" }, 35 { "generatetodescriptor", 2, "maxtries" }, 36 { "generateblock", 1, "transactions" }, 37 { "getnetworkhashps", 0, "nblocks" }, 38 { "getnetworkhashps", 1, "height" }, 39 { "sendtoaddress", 1, "amount" }, 40 { "sendtoaddress", 4, "subtractfeefromamount" }, 41 { "sendtoaddress", 5 , "replaceable" }, 42 { "sendtoaddress", 6 , "conf_target" }, 43 { "sendtoaddress", 8, "avoid_reuse" }, 44 { "sendtoaddress", 9, "fee_rate"}, 45 { "sendtoaddress", 10, "verbose"}, 46 { "settxfee", 0, "amount" }, 47 { "sethdseed", 0, "newkeypool" }, 48 { "getreceivedbyaddress", 1, "minconf" }, 49 { "getreceivedbylabel", 1, "minconf" }, 50 { "listreceivedbyaddress", 0, "minconf" }, 51 { "listreceivedbyaddress", 1, "include_empty" }, 52 { "listreceivedbyaddress", 2, "include_watchonly" }, 53 { "listreceivedbylabel", 0, "minconf" }, 54 { "listreceivedbylabel", 1, "include_empty" }, 55 { "listreceivedbylabel", 2, "include_watchonly" }, 56 { "getbalance", 1, "minconf" }, 57 { "getbalance", 2, "include_watchonly" }, 58 { "getbalance", 3, "avoid_reuse" }, 59 { "getblockhash", 0, "height" }, 60 { "waitforblockheight", 0, "height" }, 61 { "waitforblockheight", 1, "timeout" }, 62 { "waitforblock", 1, "timeout" }, 63 { "waitfornewblock", 0, "timeout" }, 64 { "listtransactions", 1, "count" }, 65 { "listtransactions", 2, "skip" }, 66 { "listtransactions", 3, "include_watchonly" }, 67 { "walletpassphrase", 1, "timeout" }, 68 { "getblocktemplate", 0, "template_request" }, 69 { "listsinceblock", 1, "target_confirmations" }, 70 { "listsinceblock", 2, "include_watchonly" }, 71 { "listsinceblock", 3, "include_removed" }, 72 { "sendmany", 1, "amounts" }, 73 { "sendmany", 2, "minconf" }, 74 { "sendmany", 4, "subtractfeefrom" }, 75 { "sendmany", 5 , "replaceable" }, 76 { "sendmany", 6 , "conf_target" }, 77 { "sendmany", 8, "fee_rate"}, 78 { "sendmany", 9, "verbose" }, 79 { "deriveaddresses", 1, "range" }, 80 { "scantxoutset", 1, "scanobjects" }, 81 { "addmultisigaddress", 0, "nrequired" }, 82 { "addmultisigaddress", 1, "keys" }, 83 { "createmultisig", 0, "nrequired" }, 84 { "createmultisig", 1, "keys" }, 85 { "listunspent", 0, "minconf" }, 86 { "listunspent", 1, "maxconf" }, 87 { "listunspent", 2, "addresses" }, 88 { "listunspent", 3, "include_unsafe" }, 89 { "listunspent", 4, "query_options" }, 90 { "getblock", 1, "verbosity" }, 91 { "getblock", 1, "verbose" }, 92 { "getblockheader", 1, "verbose" }, 93 { "getchaintxstats", 0, "nblocks" }, 94 { "gettransaction", 1, "include_watchonly" }, 95 { "gettransaction", 2, "verbose" }, 96 { "getrawtransaction", 1, "verbose" }, 97 { "createrawtransaction", 0, "inputs" }, 98 { "createrawtransaction", 1, "outputs" }, 99 { "createrawtransaction", 2, "locktime" }, 100 { "createrawtransaction", 3, "replaceable" }, 101 { "decoderawtransaction", 1, "iswitness" }, 102 { "signrawtransactionwithkey", 1, "privkeys" }, 103 { "signrawtransactionwithkey", 2, "prevtxs" }, 104 { "signrawtransactionwithwallet", 1, "prevtxs" }, 105 { "sendrawtransaction", 1, "maxfeerate" }, 106 { "testmempoolaccept", 0, "rawtxs" }, 107 { "testmempoolaccept", 1, "maxfeerate" }, 108 { "combinerawtransaction", 0, "txs" }, 109 { "fundrawtransaction", 1, "options" }, 110 { "fundrawtransaction", 2, "iswitness" }, 111 { "walletcreatefundedpsbt", 0, "inputs" }, 112 { "walletcreatefundedpsbt", 1, "outputs" }, 113 { "walletcreatefundedpsbt", 2, "locktime" }, 114 { "walletcreatefundedpsbt", 3, "options" }, 115 { "walletcreatefundedpsbt", 4, "bip32derivs" }, 116 { "walletprocesspsbt", 1, "sign" }, 117 { "walletprocesspsbt", 3, "bip32derivs" }, 118 { "createpsbt", 0, "inputs" }, 119 { "createpsbt", 1, "outputs" }, 120 { "createpsbt", 2, "locktime" }, 121 { "createpsbt", 3, "replaceable" }, 122 { "combinepsbt", 0, "txs"}, 123 { "joinpsbts", 0, "txs"}, 124 { "finalizepsbt", 1, "extract"}, 125 { "converttopsbt", 1, "permitsigdata"}, 126 { "converttopsbt", 2, "iswitness"}, 127 { "gettxout", 1, "n" }, 128 { "gettxout", 2, "include_mempool" }, 129 { "gettxoutproof", 0, "txids" }, 130 { "gettxoutsetinfo", 1, "hash_or_height" }, 131 { "gettxoutsetinfo", 2, "use_index"}, 132 { "lockunspent", 0, "unlock" }, 133 { "lockunspent", 1, "transactions" }, 134 { "send", 0, "outputs" }, 135 { "send", 1, "conf_target" }, 136 { "send", 3, "fee_rate"}, 137 { "send", 4, "options" }, 138 { "importprivkey", 2, "rescan" }, 139 { "importaddress", 2, "rescan" }, 140 { "importaddress", 3, "p2sh" }, 141 { "importpubkey", 2, "rescan" }, 142 { "importmulti", 0, "requests" }, 143 { "importmulti", 1, "options" }, 144 { "importdescriptors", 0, "requests" }, 145 { "verifychain", 0, "checklevel" }, 146 { "verifychain", 1, "nblocks" }, 147 { "getblockstats", 0, "hash_or_height" }, 148 { "getblockstats", 1, "stats" }, 149 { "pruneblockchain", 0, "height" }, 150 { "keypoolrefill", 0, "newsize" }, 151 { "getrawmempool", 0, "verbose" }, 152 { "getrawmempool", 1, "mempool_sequence" }, 153 { "estimatesmartfee", 0, "conf_target" }, 154 { "estimaterawfee", 0, "conf_target" }, 155 { "estimaterawfee", 1, "threshold" }, 156 { "prioritisetransaction", 1, "dummy" }, 157 { "prioritisetransaction", 2, "fee_delta" }, 158 { "setban", 2, "bantime" }, 159 { "setban", 3, "absolute" }, 160 { "setnetworkactive", 0, "state" }, 161 { "setwalletflag", 1, "value" }, 162 { "getmempoolancestors", 1, "verbose" }, 163 { "getmempooldescendants", 1, "verbose" }, 164 { "bumpfee", 1, "options" }, 165 { "psbtbumpfee", 1, "options" }, 166 { "logging", 0, "include" }, 167 { "logging", 1, "exclude" }, 168 { "disconnectnode", 1, "nodeid" }, 169 { "upgradewallet", 0, "version" }, 170 // Echo with conversion (For testing only) 171 { "echojson", 0, "arg0" }, 172 { "echojson", 1, "arg1" }, 173 { "echojson", 2, "arg2" }, 174 { "echojson", 3, "arg3" }, 175 { "echojson", 4, "arg4" }, 176 { "echojson", 5, "arg5" }, 177 { "echojson", 6, "arg6" }, 178 { "echojson", 7, "arg7" }, 179 { "echojson", 8, "arg8" }, 180 { "echojson", 9, "arg9" }, 181 { "rescanblockchain", 0, "start_height"}, 182 { "rescanblockchain", 1, "stop_height"}, 183 { "createwallet", 1, "disable_private_keys"}, 184 { "createwallet", 2, "blank"}, 185 { "createwallet", 4, "avoid_reuse"}, 186 { "createwallet", 5, "descriptors"}, 187 { "createwallet", 6, "load_on_startup"}, 188 { "createwallet", 7, "external_signer"}, 189 { "loadwallet", 1, "load_on_startup"}, 190 { "unloadwallet", 1, "load_on_startup"}, 191 { "getnodeaddresses", 0, "count"}, 192 { "addpeeraddress", 1, "port"}, 193 { "stop", 0, "wait" }, 194 }; 195 // clang-format on 196 197 class CRPCConvertTable 198 { 199 private: 200 std::set<std::pair<std::string, int>> members; 201 std::set<std::pair<std::string, std::string>> membersByName; 202 203 public: 204 CRPCConvertTable(); 205 206 bool convert(const std::string& method, int idx) { 207 return (members.count(std::make_pair(method, idx)) > 0); 208 } 209 bool convert(const std::string& method, const std::string& name) { 210 return (membersByName.count(std::make_pair(method, name)) > 0); 211 } 212 }; 213 214 CRPCConvertTable::CRPCConvertTable() 215 { 216 for (const auto& cp : vRPCConvertParams) { 217 members.emplace(cp.methodName, cp.paramIdx); 218 membersByName.emplace(cp.methodName, cp.paramName); 219 } 220 } 221 222 static CRPCConvertTable rpcCvtTable; 223 224 /** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) 225 * as well as objects and arrays. 226 */ 227 UniValue ParseNonRFCJSONValue(const std::string& strVal) 228 { 229 UniValue jVal; 230 if (!jVal.read(std::string("[")+strVal+std::string("]")) || 231 !jVal.isArray() || jVal.size()!=1) 232 throw std::runtime_error(std::string("Error parsing JSON: ") + strVal); 233 return jVal[0]; 234 } 235 236 UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) 237 { 238 UniValue params(UniValue::VARR); 239 240 for (unsigned int idx = 0; idx < strParams.size(); idx++) { 241 const std::string& strVal = strParams[idx]; 242 243 if (!rpcCvtTable.convert(strMethod, idx)) { 244 // insert string value directly 245 params.push_back(strVal); 246 } else { 247 // parse string as JSON, insert bool/number/object/etc. value 248 params.push_back(ParseNonRFCJSONValue(strVal)); 249 } 250 } 251 252 return params; 253 } 254 255 UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams) 256 { 257 UniValue params(UniValue::VOBJ); 258 259 for (const std::string &s: strParams) { 260 size_t pos = s.find('='); 261 if (pos == std::string::npos) { 262 throw(std::runtime_error("No '=' in named argument '"+s+"', this needs to be present for every argument (even if it is empty)")); 263 } 264 265 std::string name = s.substr(0, pos); 266 std::string value = s.substr(pos+1); 267 268 if (!rpcCvtTable.convert(strMethod, name)) { 269 // insert string value directly 270 params.pushKV(name, value); 271 } else { 272 // parse string as JSON, insert bool/number/object/etc. value 273 params.pushKV(name, ParseNonRFCJSONValue(value)); 274 } 275 } 276 277 return params; 278 } 279