Branch data Line data Source code
1 : : // Copyright (c) 2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present 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 <cstdint>
11 : : #include <set>
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 : : bool also_string{false}; //!< The parameter is also a string
22 : : };
23 : :
24 : : // clang-format off
25 : : /**
26 : : * Specify a (method, idx, name) here if the argument is a non-string RPC
27 : : * argument and needs to be converted from JSON.
28 : : *
29 : : * @note Parameter indexes start from 0.
30 : : */
31 : : static const CRPCConvertParam vRPCConvertParams[] =
32 : : {
33 : : { "setmocktime", 0, "timestamp" },
34 : : { "mockscheduler", 0, "delta_time" },
35 : : { "utxoupdatepsbt", 1, "descriptors" },
36 : : { "generatetoaddress", 0, "nblocks" },
37 : : { "generatetoaddress", 2, "maxtries" },
38 : : { "generatetodescriptor", 0, "num_blocks" },
39 : : { "generatetodescriptor", 2, "maxtries" },
40 : : { "generateblock", 1, "transactions" },
41 : : { "generateblock", 2, "submit" },
42 : : { "getnetworkhashps", 0, "nblocks" },
43 : : { "getnetworkhashps", 1, "height" },
44 : : { "sendtoaddress", 1, "amount" },
45 : : { "sendtoaddress", 4, "subtractfeefromamount" },
46 : : { "sendtoaddress", 5 , "replaceable" },
47 : : { "sendtoaddress", 6 , "conf_target" },
48 : : { "sendtoaddress", 8, "avoid_reuse" },
49 : : { "sendtoaddress", 9, "fee_rate"},
50 : : { "sendtoaddress", 10, "verbose"},
51 : : { "settxfee", 0, "amount" },
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 : : { "getdescriptoractivity", 0, "blockhashes" },
96 : : { "getdescriptoractivity", 1, "scanobjects" },
97 : : { "getdescriptoractivity", 2, "include_mempool" },
98 : : { "scantxoutset", 1, "scanobjects" },
99 : : { "createmultisig", 0, "nrequired" },
100 : : { "createmultisig", 1, "keys" },
101 : : { "listunspent", 0, "minconf" },
102 : : { "listunspent", 1, "maxconf" },
103 : : { "listunspent", 2, "addresses" },
104 : : { "listunspent", 3, "include_unsafe" },
105 : : { "listunspent", 4, "query_options" },
106 : : { "listunspent", 4, "minimumAmount" },
107 : : { "listunspent", 4, "maximumAmount" },
108 : : { "listunspent", 4, "maximumCount" },
109 : : { "listunspent", 4, "minimumSumAmount" },
110 : : { "listunspent", 4, "include_immature_coinbase" },
111 : : { "getblock", 1, "verbosity" },
112 : : { "getblock", 1, "verbose" },
113 : : { "getblockheader", 1, "verbose" },
114 : : { "getchaintxstats", 0, "nblocks" },
115 : : { "gettransaction", 1, "include_watchonly" },
116 : : { "gettransaction", 2, "verbose" },
117 : : { "getrawtransaction", 1, "verbosity" },
118 : : { "getrawtransaction", 1, "verbose" },
119 : : { "createrawtransaction", 0, "inputs" },
120 : : { "createrawtransaction", 1, "outputs" },
121 : : { "createrawtransaction", 2, "locktime" },
122 : : { "createrawtransaction", 3, "replaceable" },
123 : : { "createrawtransaction", 4, "version" },
124 : : { "decoderawtransaction", 1, "iswitness" },
125 : : { "signrawtransactionwithkey", 1, "privkeys" },
126 : : { "signrawtransactionwithkey", 2, "prevtxs" },
127 : : { "signrawtransactionwithwallet", 1, "prevtxs" },
128 : : { "sendrawtransaction", 1, "maxfeerate" },
129 : : { "sendrawtransaction", 2, "maxburnamount" },
130 : : { "testmempoolaccept", 0, "rawtxs" },
131 : : { "testmempoolaccept", 1, "maxfeerate" },
132 : : { "submitpackage", 0, "package" },
133 : : { "submitpackage", 1, "maxfeerate" },
134 : : { "submitpackage", 2, "maxburnamount" },
135 : : { "combinerawtransaction", 0, "txs" },
136 : : { "fundrawtransaction", 1, "options" },
137 : : { "fundrawtransaction", 1, "add_inputs"},
138 : : { "fundrawtransaction", 1, "include_unsafe"},
139 : : { "fundrawtransaction", 1, "minconf"},
140 : : { "fundrawtransaction", 1, "maxconf"},
141 : : { "fundrawtransaction", 1, "changePosition"},
142 : : { "fundrawtransaction", 1, "includeWatching"},
143 : : { "fundrawtransaction", 1, "lockUnspents"},
144 : : { "fundrawtransaction", 1, "fee_rate"},
145 : : { "fundrawtransaction", 1, "feeRate"},
146 : : { "fundrawtransaction", 1, "subtractFeeFromOutputs"},
147 : : { "fundrawtransaction", 1, "input_weights"},
148 : : { "fundrawtransaction", 1, "conf_target"},
149 : : { "fundrawtransaction", 1, "replaceable"},
150 : : { "fundrawtransaction", 1, "solving_data"},
151 : : { "fundrawtransaction", 1, "max_tx_weight"},
152 : : { "fundrawtransaction", 2, "iswitness" },
153 : : { "walletcreatefundedpsbt", 0, "inputs" },
154 : : { "walletcreatefundedpsbt", 1, "outputs" },
155 : : { "walletcreatefundedpsbt", 2, "locktime" },
156 : : { "walletcreatefundedpsbt", 3, "options" },
157 : : { "walletcreatefundedpsbt", 3, "add_inputs"},
158 : : { "walletcreatefundedpsbt", 3, "include_unsafe"},
159 : : { "walletcreatefundedpsbt", 3, "minconf"},
160 : : { "walletcreatefundedpsbt", 3, "maxconf"},
161 : : { "walletcreatefundedpsbt", 3, "changePosition"},
162 : : { "walletcreatefundedpsbt", 3, "includeWatching"},
163 : : { "walletcreatefundedpsbt", 3, "lockUnspents"},
164 : : { "walletcreatefundedpsbt", 3, "fee_rate"},
165 : : { "walletcreatefundedpsbt", 3, "feeRate"},
166 : : { "walletcreatefundedpsbt", 3, "subtractFeeFromOutputs"},
167 : : { "walletcreatefundedpsbt", 3, "conf_target"},
168 : : { "walletcreatefundedpsbt", 3, "replaceable"},
169 : : { "walletcreatefundedpsbt", 3, "solving_data"},
170 : : { "walletcreatefundedpsbt", 3, "max_tx_weight"},
171 : : { "walletcreatefundedpsbt", 4, "bip32derivs" },
172 : : { "walletcreatefundedpsbt", 5, "version" },
173 : : { "walletprocesspsbt", 1, "sign" },
174 : : { "walletprocesspsbt", 3, "bip32derivs" },
175 : : { "walletprocesspsbt", 4, "finalize" },
176 : : { "descriptorprocesspsbt", 1, "descriptors"},
177 : : { "descriptorprocesspsbt", 3, "bip32derivs" },
178 : : { "descriptorprocesspsbt", 4, "finalize" },
179 : : { "createpsbt", 0, "inputs" },
180 : : { "createpsbt", 1, "outputs" },
181 : : { "createpsbt", 2, "locktime" },
182 : : { "createpsbt", 3, "replaceable" },
183 : : { "createpsbt", 4, "version" },
184 : : { "combinepsbt", 0, "txs"},
185 : : { "joinpsbts", 0, "txs"},
186 : : { "finalizepsbt", 1, "extract"},
187 : : { "converttopsbt", 1, "permitsigdata"},
188 : : { "converttopsbt", 2, "iswitness"},
189 : : { "gettxout", 1, "n" },
190 : : { "gettxout", 2, "include_mempool" },
191 : : { "gettxoutproof", 0, "txids" },
192 : : { "gettxoutsetinfo", 1, "hash_or_height", /*also_string=*/true },
193 : : { "gettxoutsetinfo", 2, "use_index"},
194 : : { "dumptxoutset", 2, "options" },
195 : : { "dumptxoutset", 2, "rollback", /*also_string=*/true },
196 : : { "lockunspent", 0, "unlock" },
197 : : { "lockunspent", 1, "transactions" },
198 : : { "lockunspent", 2, "persistent" },
199 : : { "send", 0, "outputs" },
200 : : { "send", 1, "conf_target" },
201 : : { "send", 3, "fee_rate"},
202 : : { "send", 4, "options" },
203 : : { "send", 4, "add_inputs"},
204 : : { "send", 4, "include_unsafe"},
205 : : { "send", 4, "minconf"},
206 : : { "send", 4, "maxconf"},
207 : : { "send", 4, "add_to_wallet"},
208 : : { "send", 4, "change_position"},
209 : : { "send", 4, "fee_rate"},
210 : : { "send", 4, "include_watching"},
211 : : { "send", 4, "inputs"},
212 : : { "send", 4, "locktime"},
213 : : { "send", 4, "lock_unspents"},
214 : : { "send", 4, "psbt"},
215 : : { "send", 4, "subtract_fee_from_outputs"},
216 : : { "send", 4, "conf_target"},
217 : : { "send", 4, "replaceable"},
218 : : { "send", 4, "solving_data"},
219 : : { "send", 4, "max_tx_weight"},
220 : : { "send", 5, "version"},
221 : : { "sendall", 0, "recipients" },
222 : : { "sendall", 1, "conf_target" },
223 : : { "sendall", 3, "fee_rate"},
224 : : { "sendall", 4, "options" },
225 : : { "sendall", 4, "add_to_wallet"},
226 : : { "sendall", 4, "fee_rate"},
227 : : { "sendall", 4, "include_watching"},
228 : : { "sendall", 4, "inputs"},
229 : : { "sendall", 4, "locktime"},
230 : : { "sendall", 4, "lock_unspents"},
231 : : { "sendall", 4, "psbt"},
232 : : { "sendall", 4, "send_max"},
233 : : { "sendall", 4, "minconf"},
234 : : { "sendall", 4, "maxconf"},
235 : : { "sendall", 4, "conf_target"},
236 : : { "sendall", 4, "replaceable"},
237 : : { "sendall", 4, "solving_data"},
238 : : { "sendall", 4, "version"},
239 : : { "simulaterawtransaction", 0, "rawtxs" },
240 : : { "simulaterawtransaction", 1, "options" },
241 : : { "simulaterawtransaction", 1, "include_watchonly"},
242 : : { "importmempool", 1, "options" },
243 : : { "importmempool", 1, "apply_fee_delta_priority" },
244 : : { "importmempool", 1, "use_current_time" },
245 : : { "importmempool", 1, "apply_unbroadcast_set" },
246 : : { "importdescriptors", 0, "requests" },
247 : : { "listdescriptors", 0, "private" },
248 : : { "verifychain", 0, "checklevel" },
249 : : { "verifychain", 1, "nblocks" },
250 : : { "getblockstats", 0, "hash_or_height", /*also_string=*/true },
251 : : { "getblockstats", 1, "stats" },
252 : : { "pruneblockchain", 0, "height" },
253 : : { "keypoolrefill", 0, "newsize" },
254 : : { "getrawmempool", 0, "verbose" },
255 : : { "getrawmempool", 1, "mempool_sequence" },
256 : : { "getorphantxs", 0, "verbosity" },
257 : : { "estimatesmartfee", 0, "conf_target" },
258 : : { "estimaterawfee", 0, "conf_target" },
259 : : { "estimaterawfee", 1, "threshold" },
260 : : { "prioritisetransaction", 1, "dummy" },
261 : : { "prioritisetransaction", 2, "fee_delta" },
262 : : { "setban", 2, "bantime" },
263 : : { "setban", 3, "absolute" },
264 : : { "setnetworkactive", 0, "state" },
265 : : { "setwalletflag", 1, "value" },
266 : : { "getmempoolancestors", 1, "verbose" },
267 : : { "getmempooldescendants", 1, "verbose" },
268 : : { "gettxspendingprevout", 0, "outputs" },
269 : : { "bumpfee", 1, "options" },
270 : : { "bumpfee", 1, "conf_target"},
271 : : { "bumpfee", 1, "fee_rate"},
272 : : { "bumpfee", 1, "replaceable"},
273 : : { "bumpfee", 1, "outputs"},
274 : : { "bumpfee", 1, "original_change_index"},
275 : : { "psbtbumpfee", 1, "options" },
276 : : { "psbtbumpfee", 1, "conf_target"},
277 : : { "psbtbumpfee", 1, "fee_rate"},
278 : : { "psbtbumpfee", 1, "replaceable"},
279 : : { "psbtbumpfee", 1, "outputs"},
280 : : { "psbtbumpfee", 1, "original_change_index"},
281 : : { "logging", 0, "include" },
282 : : { "logging", 1, "exclude" },
283 : : { "disconnectnode", 1, "nodeid" },
284 : : { "gethdkeys", 0, "active_only" },
285 : : { "gethdkeys", 0, "options" },
286 : : { "gethdkeys", 0, "private" },
287 : : { "createwalletdescriptor", 1, "options" },
288 : : { "createwalletdescriptor", 1, "internal" },
289 : : // Echo with conversion (For testing only)
290 : : { "echojson", 0, "arg0" },
291 : : { "echojson", 1, "arg1" },
292 : : { "echojson", 2, "arg2" },
293 : : { "echojson", 3, "arg3" },
294 : : { "echojson", 4, "arg4" },
295 : : { "echojson", 5, "arg5" },
296 : : { "echojson", 6, "arg6" },
297 : : { "echojson", 7, "arg7" },
298 : : { "echojson", 8, "arg8" },
299 : : { "echojson", 9, "arg9" },
300 : : { "rescanblockchain", 0, "start_height"},
301 : : { "rescanblockchain", 1, "stop_height"},
302 : : { "createwallet", 1, "disable_private_keys"},
303 : : { "createwallet", 2, "blank"},
304 : : { "createwallet", 4, "avoid_reuse"},
305 : : { "createwallet", 5, "descriptors"},
306 : : { "createwallet", 6, "load_on_startup"},
307 : : { "createwallet", 7, "external_signer"},
308 : : { "restorewallet", 2, "load_on_startup"},
309 : : { "loadwallet", 1, "load_on_startup"},
310 : : { "unloadwallet", 1, "load_on_startup"},
311 : : { "getnodeaddresses", 0, "count"},
312 : : { "addpeeraddress", 1, "port"},
313 : : { "addpeeraddress", 2, "tried"},
314 : : { "sendmsgtopeer", 0, "peer_id" },
315 : : { "stop", 0, "wait" },
316 : : { "addnode", 2, "v2transport" },
317 : : { "addconnection", 2, "v2transport" },
318 : : };
319 : : // clang-format on
320 : :
321 : : /** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
322 : 9624 : static UniValue Parse(std::string_view raw, bool also_string)
323 : : {
324 [ + - ]: 9624 : UniValue parsed;
325 [ + - + + ]: 9624 : if (!parsed.read(raw)) {
326 [ + + + - : 3841 : if (!also_string) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
+ - ]
327 [ + - ]: 195 : return raw;
328 : : }
329 : 7606 : return parsed;
330 : 9624 : }
331 : :
332 : : class CRPCConvertTable
333 : : {
334 : : private:
335 : : std::map<std::pair<std::string, int>, bool> members;
336 : : std::map<std::pair<std::string, std::string>, bool> membersByName;
337 : :
338 : : public:
339 : : CRPCConvertTable();
340 : :
341 : : /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
342 : 43812 : UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
343 : : {
344 [ - + ]: 87624 : const auto& it = members.find({method, param_idx});
345 [ + + ]: 43812 : if (it != members.end()) {
346 : 9383 : return Parse(arg_value, it->second);
347 : : }
348 : 34429 : return arg_value;
349 : : }
350 : :
351 : : /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
352 : 1202 : UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
353 : : {
354 : 1202 : const auto& it = membersByName.find({method, param_name});
355 [ + + ]: 1202 : if (it != membersByName.end()) {
356 : 241 : return Parse(arg_value, it->second);
357 : : }
358 : 961 : return arg_value;
359 : : }
360 : : };
361 : :
362 : 228 : CRPCConvertTable::CRPCConvertTable()
363 : : {
364 [ + + ]: 64980 : for (const auto& cp : vRPCConvertParams) {
365 [ - + + - ]: 129504 : members.emplace(std::make_pair(cp.methodName, cp.paramIdx), cp.also_string);
366 [ + - + - ]: 129504 : membersByName.emplace(std::make_pair(cp.methodName, cp.paramName), cp.also_string);
367 : : }
368 : 228 : }
369 : :
370 : : static CRPCConvertTable rpcCvtTable;
371 : :
372 : 13222 : UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
373 : : {
374 : 13222 : UniValue params(UniValue::VARR);
375 : :
376 [ - + + + ]: 53231 : for (unsigned int idx = 0; idx < strParams.size(); idx++) {
377 [ - + ]: 41272 : std::string_view value{strParams[idx]};
378 [ + + + - ]: 41272 : params.push_back(rpcCvtTable.ArgToUniValue(value, strMethod, idx));
379 : : }
380 : :
381 : 11959 : return params;
382 : 1263 : }
383 : :
384 : 985 : UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams)
385 : : {
386 : 985 : UniValue params(UniValue::VOBJ);
387 : 985 : UniValue positional_args{UniValue::VARR};
388 : :
389 [ - + + + ]: 4167 : for (std::string_view s: strParams) {
390 : 3742 : size_t pos = s.find('=');
391 [ + + ]: 3742 : if (pos == std::string::npos) {
392 [ - + + + : 2540 : positional_args.push_back(rpcCvtTable.ArgToUniValue(s, strMethod, positional_args.size()));
+ - ]
393 : 2005 : continue;
394 : : }
395 : :
396 [ + - + - ]: 1202 : std::string name{s.substr(0, pos)};
397 [ + - ]: 1202 : std::string_view value{s.substr(pos+1)};
398 : :
399 : : // Intentionally overwrite earlier named values with later ones as a
400 : : // convenience for scripts and command line users that want to merge
401 : : // options.
402 [ + + + - ]: 3556 : params.pushKV(name, rpcCvtTable.ArgToUniValue(value, strMethod, name));
403 : 1202 : }
404 : :
405 [ - + + + ]: 425 : if (!positional_args.empty()) {
406 : : // Use pushKVEnd instead of pushKV to avoid overwriting an explicit
407 : : // "args" value with an implicit one. Let the RPC server handle the
408 : : // request as given.
409 [ + - + - ]: 240 : params.pushKVEnd("args", std::move(positional_args));
410 : : }
411 : :
412 : 425 : return params;
413 : 1545 : }
|