/ src / rpc / client.cpp
client.cpp
  1  // Copyright (c) 2010 Satoshi Nakamoto
  2  // Copyright (c) 2009-2022 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 <common/args.h>
  7  #include <rpc/client.h>
  8  #include <tinyformat.h>
  9  
 10  #include <set>
 11  #include <stdint.h>
 12  #include <string>
 13  #include <string_view>
 14  
 15  class CRPCConvertParam
 16  {
 17  public:
 18      std::string methodName; //!< method whose params want conversion
 19      int paramIdx;           //!< 0-based idx of param to convert
 20      std::string paramName;  //!< parameter name
 21  };
 22  
 23  // clang-format off
 24  /**
 25   * Specify a (method, idx, name) here if the argument is a non-string RPC
 26   * argument and needs to be converted from JSON.
 27   *
 28   * @note Parameter indexes start from 0.
 29   */
 30  static const CRPCConvertParam vRPCConvertParams[] =
 31  {
 32      { "setmocktime", 0, "timestamp" },
 33      { "mockscheduler", 0, "delta_time" },
 34      { "utxoupdatepsbt", 1, "descriptors" },
 35      { "generatetoaddress", 0, "nblocks" },
 36      { "generatetoaddress", 2, "maxtries" },
 37      { "generatetodescriptor", 0, "num_blocks" },
 38      { "generatetodescriptor", 2, "maxtries" },
 39      { "generateblock", 1, "transactions" },
 40      { "generateblock", 2, "submit" },
 41      { "getnetworkhashps", 0, "nblocks" },
 42      { "getnetworkhashps", 1, "height" },
 43      { "sendtoaddress", 1, "amount" },
 44      { "sendtoaddress", 4, "subtractfeefromamount" },
 45      { "sendtoaddress", 5 , "replaceable" },
 46      { "sendtoaddress", 6 , "conf_target" },
 47      { "sendtoaddress", 8, "avoid_reuse" },
 48      { "sendtoaddress", 9, "fee_rate"},
 49      { "sendtoaddress", 10, "verbose"},
 50      { "settxfee", 0, "amount" },
 51      { "sethdseed", 0, "newkeypool" },
 52      { "getreceivedbyaddress", 1, "minconf" },
 53      { "getreceivedbyaddress", 2, "include_immature_coinbase" },
 54      { "getreceivedbylabel", 1, "minconf" },
 55      { "getreceivedbylabel", 2, "include_immature_coinbase" },
 56      { "listreceivedbyaddress", 0, "minconf" },
 57      { "listreceivedbyaddress", 1, "include_empty" },
 58      { "listreceivedbyaddress", 2, "include_watchonly" },
 59      { "listreceivedbyaddress", 4, "include_immature_coinbase" },
 60      { "listreceivedbylabel", 0, "minconf" },
 61      { "listreceivedbylabel", 1, "include_empty" },
 62      { "listreceivedbylabel", 2, "include_watchonly" },
 63      { "listreceivedbylabel", 3, "include_immature_coinbase" },
 64      { "getbalance", 1, "minconf" },
 65      { "getbalance", 2, "include_watchonly" },
 66      { "getbalance", 3, "avoid_reuse" },
 67      { "getblockfrompeer", 1, "peer_id" },
 68      { "getblockhash", 0, "height" },
 69      { "waitforblockheight", 0, "height" },
 70      { "waitforblockheight", 1, "timeout" },
 71      { "waitforblock", 1, "timeout" },
 72      { "waitfornewblock", 0, "timeout" },
 73      { "listtransactions", 1, "count" },
 74      { "listtransactions", 2, "skip" },
 75      { "listtransactions", 3, "include_watchonly" },
 76      { "walletpassphrase", 1, "timeout" },
 77      { "getblocktemplate", 0, "template_request" },
 78      { "listsinceblock", 1, "target_confirmations" },
 79      { "listsinceblock", 2, "include_watchonly" },
 80      { "listsinceblock", 3, "include_removed" },
 81      { "listsinceblock", 4, "include_change" },
 82      { "sendmany", 1, "amounts" },
 83      { "sendmany", 2, "minconf" },
 84      { "sendmany", 4, "subtractfeefrom" },
 85      { "sendmany", 5 , "replaceable" },
 86      { "sendmany", 6 , "conf_target" },
 87      { "sendmany", 8, "fee_rate"},
 88      { "sendmany", 9, "verbose" },
 89      { "deriveaddresses", 1, "range" },
 90      { "scanblocks", 1, "scanobjects" },
 91      { "scanblocks", 2, "start_height" },
 92      { "scanblocks", 3, "stop_height" },
 93      { "scanblocks", 5, "options" },
 94      { "scanblocks", 5, "filter_false_positives" },
 95      { "scantxoutset", 1, "scanobjects" },
 96      { "addmultisigaddress", 0, "nrequired" },
 97      { "addmultisigaddress", 1, "keys" },
 98      { "createmultisig", 0, "nrequired" },
 99      { "createmultisig", 1, "keys" },
100      { "listunspent", 0, "minconf" },
101      { "listunspent", 1, "maxconf" },
102      { "listunspent", 2, "addresses" },
103      { "listunspent", 3, "include_unsafe" },
104      { "listunspent", 4, "query_options" },
105      { "listunspent", 4, "minimumAmount" },
106      { "listunspent", 4, "maximumAmount" },
107      { "listunspent", 4, "maximumCount" },
108      { "listunspent", 4, "minimumSumAmount" },
109      { "listunspent", 4, "include_immature_coinbase" },
110      { "getblock", 1, "verbosity" },
111      { "getblock", 1, "verbose" },
112      { "getblockheader", 1, "verbose" },
113      { "getchaintxstats", 0, "nblocks" },
114      { "gettransaction", 1, "include_watchonly" },
115      { "gettransaction", 2, "verbose" },
116      { "getrawtransaction", 1, "verbosity" },
117      { "getrawtransaction", 1, "verbose" },
118      { "createrawtransaction", 0, "inputs" },
119      { "createrawtransaction", 1, "outputs" },
120      { "createrawtransaction", 2, "locktime" },
121      { "createrawtransaction", 3, "replaceable" },
122      { "decoderawtransaction", 1, "iswitness" },
123      { "signrawtransactionwithkey", 1, "privkeys" },
124      { "signrawtransactionwithkey", 2, "prevtxs" },
125      { "signrawtransactionwithwallet", 1, "prevtxs" },
126      { "sendrawtransaction", 1, "maxfeerate" },
127      { "sendrawtransaction", 2, "maxburnamount" },
128      { "testmempoolaccept", 0, "rawtxs" },
129      { "testmempoolaccept", 1, "maxfeerate" },
130      { "submitpackage", 0, "package" },
131      { "submitpackage", 1, "maxfeerate" },
132      { "submitpackage", 2, "maxburnamount" },
133      { "combinerawtransaction", 0, "txs" },
134      { "fundrawtransaction", 1, "options" },
135      { "fundrawtransaction", 1, "add_inputs"},
136      { "fundrawtransaction", 1, "include_unsafe"},
137      { "fundrawtransaction", 1, "minconf"},
138      { "fundrawtransaction", 1, "maxconf"},
139      { "fundrawtransaction", 1, "changePosition"},
140      { "fundrawtransaction", 1, "includeWatching"},
141      { "fundrawtransaction", 1, "lockUnspents"},
142      { "fundrawtransaction", 1, "fee_rate"},
143      { "fundrawtransaction", 1, "feeRate"},
144      { "fundrawtransaction", 1, "subtractFeeFromOutputs"},
145      { "fundrawtransaction", 1, "input_weights"},
146      { "fundrawtransaction", 1, "conf_target"},
147      { "fundrawtransaction", 1, "replaceable"},
148      { "fundrawtransaction", 1, "solving_data"},
149      { "fundrawtransaction", 2, "iswitness" },
150      { "walletcreatefundedpsbt", 0, "inputs" },
151      { "walletcreatefundedpsbt", 1, "outputs" },
152      { "walletcreatefundedpsbt", 2, "locktime" },
153      { "walletcreatefundedpsbt", 3, "options" },
154      { "walletcreatefundedpsbt", 3, "add_inputs"},
155      { "walletcreatefundedpsbt", 3, "include_unsafe"},
156      { "walletcreatefundedpsbt", 3, "minconf"},
157      { "walletcreatefundedpsbt", 3, "maxconf"},
158      { "walletcreatefundedpsbt", 3, "changePosition"},
159      { "walletcreatefundedpsbt", 3, "includeWatching"},
160      { "walletcreatefundedpsbt", 3, "lockUnspents"},
161      { "walletcreatefundedpsbt", 3, "fee_rate"},
162      { "walletcreatefundedpsbt", 3, "feeRate"},
163      { "walletcreatefundedpsbt", 3, "subtractFeeFromOutputs"},
164      { "walletcreatefundedpsbt", 3, "conf_target"},
165      { "walletcreatefundedpsbt", 3, "replaceable"},
166      { "walletcreatefundedpsbt", 3, "solving_data"},
167      { "walletcreatefundedpsbt", 4, "bip32derivs" },
168      { "walletprocesspsbt", 1, "sign" },
169      { "walletprocesspsbt", 3, "bip32derivs" },
170      { "walletprocesspsbt", 4, "finalize" },
171      { "descriptorprocesspsbt", 1, "descriptors"},
172      { "descriptorprocesspsbt", 3, "bip32derivs" },
173      { "descriptorprocesspsbt", 4, "finalize" },
174      { "createpsbt", 0, "inputs" },
175      { "createpsbt", 1, "outputs" },
176      { "createpsbt", 2, "locktime" },
177      { "createpsbt", 3, "replaceable" },
178      { "combinepsbt", 0, "txs"},
179      { "joinpsbts", 0, "txs"},
180      { "finalizepsbt", 1, "extract"},
181      { "converttopsbt", 1, "permitsigdata"},
182      { "converttopsbt", 2, "iswitness"},
183      { "gettxout", 1, "n" },
184      { "gettxout", 2, "include_mempool" },
185      { "gettxoutproof", 0, "txids" },
186      { "gettxoutsetinfo", 1, "hash_or_height" },
187      { "gettxoutsetinfo", 2, "use_index"},
188      { "lockunspent", 0, "unlock" },
189      { "lockunspent", 1, "transactions" },
190      { "lockunspent", 2, "persistent" },
191      { "send", 0, "outputs" },
192      { "send", 1, "conf_target" },
193      { "send", 3, "fee_rate"},
194      { "send", 4, "options" },
195      { "send", 4, "add_inputs"},
196      { "send", 4, "include_unsafe"},
197      { "send", 4, "minconf"},
198      { "send", 4, "maxconf"},
199      { "send", 4, "add_to_wallet"},
200      { "send", 4, "change_position"},
201      { "send", 4, "fee_rate"},
202      { "send", 4, "include_watching"},
203      { "send", 4, "inputs"},
204      { "send", 4, "locktime"},
205      { "send", 4, "lock_unspents"},
206      { "send", 4, "psbt"},
207      { "send", 4, "subtract_fee_from_outputs"},
208      { "send", 4, "conf_target"},
209      { "send", 4, "replaceable"},
210      { "send", 4, "solving_data"},
211      { "sendall", 0, "recipients" },
212      { "sendall", 1, "conf_target" },
213      { "sendall", 3, "fee_rate"},
214      { "sendall", 4, "options" },
215      { "sendall", 4, "add_to_wallet"},
216      { "sendall", 4, "fee_rate"},
217      { "sendall", 4, "include_watching"},
218      { "sendall", 4, "inputs"},
219      { "sendall", 4, "locktime"},
220      { "sendall", 4, "lock_unspents"},
221      { "sendall", 4, "psbt"},
222      { "sendall", 4, "send_max"},
223      { "sendall", 4, "minconf"},
224      { "sendall", 4, "maxconf"},
225      { "sendall", 4, "conf_target"},
226      { "sendall", 4, "replaceable"},
227      { "sendall", 4, "solving_data"},
228      { "simulaterawtransaction", 0, "rawtxs" },
229      { "simulaterawtransaction", 1, "options" },
230      { "simulaterawtransaction", 1, "include_watchonly"},
231      { "importprivkey", 2, "rescan" },
232      { "importaddress", 2, "rescan" },
233      { "importaddress", 3, "p2sh" },
234      { "importpubkey", 2, "rescan" },
235      { "importmempool", 1, "options" },
236      { "importmempool", 1, "apply_fee_delta_priority" },
237      { "importmempool", 1, "use_current_time" },
238      { "importmempool", 1, "apply_unbroadcast_set" },
239      { "importmulti", 0, "requests" },
240      { "importmulti", 1, "options" },
241      { "importmulti", 1, "rescan" },
242      { "importdescriptors", 0, "requests" },
243      { "listdescriptors", 0, "private" },
244      { "verifychain", 0, "checklevel" },
245      { "verifychain", 1, "nblocks" },
246      { "getblockstats", 0, "hash_or_height" },
247      { "getblockstats", 1, "stats" },
248      { "pruneblockchain", 0, "height" },
249      { "keypoolrefill", 0, "newsize" },
250      { "getrawmempool", 0, "verbose" },
251      { "getrawmempool", 1, "mempool_sequence" },
252      { "estimatesmartfee", 0, "conf_target" },
253      { "estimaterawfee", 0, "conf_target" },
254      { "estimaterawfee", 1, "threshold" },
255      { "prioritisetransaction", 1, "dummy" },
256      { "prioritisetransaction", 2, "fee_delta" },
257      { "setban", 2, "bantime" },
258      { "setban", 3, "absolute" },
259      { "setnetworkactive", 0, "state" },
260      { "setwalletflag", 1, "value" },
261      { "getmempoolancestors", 1, "verbose" },
262      { "getmempooldescendants", 1, "verbose" },
263      { "gettxspendingprevout", 0, "outputs" },
264      { "bumpfee", 1, "options" },
265      { "bumpfee", 1, "conf_target"},
266      { "bumpfee", 1, "fee_rate"},
267      { "bumpfee", 1, "replaceable"},
268      { "bumpfee", 1, "outputs"},
269      { "bumpfee", 1, "original_change_index"},
270      { "psbtbumpfee", 1, "options" },
271      { "psbtbumpfee", 1, "conf_target"},
272      { "psbtbumpfee", 1, "fee_rate"},
273      { "psbtbumpfee", 1, "replaceable"},
274      { "psbtbumpfee", 1, "outputs"},
275      { "psbtbumpfee", 1, "original_change_index"},
276      { "logging", 0, "include" },
277      { "logging", 1, "exclude" },
278      { "disconnectnode", 1, "nodeid" },
279      { "upgradewallet", 0, "version" },
280      // Echo with conversion (For testing only)
281      { "echojson", 0, "arg0" },
282      { "echojson", 1, "arg1" },
283      { "echojson", 2, "arg2" },
284      { "echojson", 3, "arg3" },
285      { "echojson", 4, "arg4" },
286      { "echojson", 5, "arg5" },
287      { "echojson", 6, "arg6" },
288      { "echojson", 7, "arg7" },
289      { "echojson", 8, "arg8" },
290      { "echojson", 9, "arg9" },
291      { "rescanblockchain", 0, "start_height"},
292      { "rescanblockchain", 1, "stop_height"},
293      { "createwallet", 1, "disable_private_keys"},
294      { "createwallet", 2, "blank"},
295      { "createwallet", 4, "avoid_reuse"},
296      { "createwallet", 5, "descriptors"},
297      { "createwallet", 6, "load_on_startup"},
298      { "createwallet", 7, "external_signer"},
299      { "restorewallet", 2, "load_on_startup"},
300      { "loadwallet", 1, "load_on_startup"},
301      { "unloadwallet", 1, "load_on_startup"},
302      { "getnodeaddresses", 0, "count"},
303      { "addpeeraddress", 1, "port"},
304      { "addpeeraddress", 2, "tried"},
305      { "sendmsgtopeer", 0, "peer_id" },
306      { "stop", 0, "wait" },
307      { "addnode", 2, "v2transport" },
308      { "addconnection", 2, "v2transport" },
309  };
310  // clang-format on
311  
312  /** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
313  static UniValue Parse(std::string_view raw)
314  {
315      UniValue parsed;
316      if (!parsed.read(raw)) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
317      return parsed;
318  }
319  
320  class CRPCConvertTable
321  {
322  private:
323      std::set<std::pair<std::string, int>> members;
324      std::set<std::pair<std::string, std::string>> membersByName;
325  
326  public:
327      CRPCConvertTable();
328  
329      /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
330      UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
331      {
332          return members.count({method, param_idx}) > 0 ? Parse(arg_value) : arg_value;
333      }
334  
335      /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
336      UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
337      {
338          return membersByName.count({method, param_name}) > 0 ? Parse(arg_value) : arg_value;
339      }
340  };
341  
342  CRPCConvertTable::CRPCConvertTable()
343  {
344      for (const auto& cp : vRPCConvertParams) {
345          members.emplace(cp.methodName, cp.paramIdx);
346          membersByName.emplace(cp.methodName, cp.paramName);
347      }
348  }
349  
350  static CRPCConvertTable rpcCvtTable;
351  
352  UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
353  {
354      UniValue params(UniValue::VARR);
355  
356      for (unsigned int idx = 0; idx < strParams.size(); idx++) {
357          std::string_view value{strParams[idx]};
358          params.push_back(rpcCvtTable.ArgToUniValue(value, strMethod, idx));
359      }
360  
361      return params;
362  }
363  
364  UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams)
365  {
366      UniValue params(UniValue::VOBJ);
367      UniValue positional_args{UniValue::VARR};
368  
369      for (std::string_view s: strParams) {
370          size_t pos = s.find('=');
371          if (pos == std::string::npos) {
372              positional_args.push_back(rpcCvtTable.ArgToUniValue(s, strMethod, positional_args.size()));
373              continue;
374          }
375  
376          std::string name{s.substr(0, pos)};
377          std::string_view value{s.substr(pos+1)};
378  
379          // Intentionally overwrite earlier named values with later ones as a
380          // convenience for scripts and command line users that want to merge
381          // options.
382          params.pushKV(name, rpcCvtTable.ArgToUniValue(value, strMethod, name));
383      }
384  
385      if (!positional_args.empty()) {
386          // Use pushKVEnd instead of pushKV to avoid overwriting an explicit
387          // "args" value with an implicit one. Let the RPC server handle the
388          // request as given.
389          params.pushKVEnd("args", positional_args);
390      }
391  
392      return params;
393  }