Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2021 The Bitcoin Core developers
3 : // Copyright (c) 2014-2025 The Dash Core developers
4 : // Distributed under the MIT software license, see the accompanying
5 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 :
7 : #include <rpc/client.h>
8 : #include <tinyformat.h>
9 : #include <util/system.h>
10 :
11 : #include <map>
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 preserve_str{false}; //!< only parse if array or object
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 0 : static const CRPCConvertParam vRPCConvertParams[] =
32 403039 : {
33 1339 : { "setmocktime", 0, "timestamp" },
34 1339 : { "mockscheduler", 0, "delta_time" },
35 1339 : { "utxoupdatepsbt", 1, "descriptors" },
36 : #if ENABLE_MINER
37 1339 : { "generatetoaddress", 0, "nblocks" },
38 1339 : { "generatetoaddress", 2, "maxtries" },
39 1339 : { "generatetodescriptor", 0, "num_blocks" },
40 1339 : { "generatetodescriptor", 2, "maxtries" },
41 1339 : { "generateblock", 1, "transactions" },
42 : #endif // ENABLE_MINER
43 1339 : { "getnetworkhashps", 0, "nblocks" },
44 1339 : { "getnetworkhashps", 1, "height" },
45 1339 : { "sendtoaddress", 1, "amount" },
46 1339 : { "sendtoaddress", 4, "subtractfeefromamount" },
47 1339 : { "sendtoaddress", 5, "use_is" },
48 1339 : { "sendtoaddress", 6, "use_cj" },
49 1339 : { "sendtoaddress", 7, "conf_target" },
50 1339 : { "sendtoaddress", 9, "avoid_reuse" },
51 1339 : { "sendtoaddress", 10, "fee_rate"},
52 1339 : { "sendtoaddress", 11, "verbose"},
53 1339 : { "settxfee", 0, "amount" },
54 1339 : { "sethdseed", 0, "newkeypool" },
55 1339 : { "getreceivedbyaddress", 1, "minconf" },
56 1339 : { "getreceivedbyaddress", 2, "addlocked" },
57 1339 : { "getreceivedbyaddress", 3, "include_immature_coinbase" },
58 1339 : { "getreceivedbylabel", 1, "minconf" },
59 1339 : { "getreceivedbylabel", 2, "addlocked" },
60 1339 : { "getreceivedbylabel", 3, "include_immature_coinbase" },
61 1339 : { "listaddressbalances", 0, "minamount" },
62 1339 : { "listreceivedbyaddress", 0, "minconf" },
63 1339 : { "listreceivedbyaddress", 1, "addlocked" },
64 1339 : { "listreceivedbyaddress", 2, "include_empty" },
65 1339 : { "listreceivedbyaddress", 3, "include_watchonly" },
66 1339 : { "listreceivedbyaddress", 5, "include_immature_coinbase" },
67 1339 : { "listreceivedbylabel", 0, "minconf" },
68 1339 : { "listreceivedbylabel", 1, "addlocked" },
69 1339 : { "listreceivedbylabel", 2, "include_empty" },
70 1339 : { "listreceivedbylabel", 3, "include_watchonly" },
71 1339 : { "listreceivedbylabel", 4, "include_immature_coinbase" },
72 1339 : { "getassetunlockstatuses", 0, "indexes" },
73 1339 : { "getassetunlockstatuses", 1, "height" },
74 1339 : { "getbalance", 1, "minconf" },
75 1339 : { "getbalance", 2, "addlocked" },
76 1339 : { "getbalance", 3, "include_watchonly" },
77 1339 : { "getbalance", 4, "avoid_reuse" },
78 1339 : { "getblockfrompeer", 1, "peer_id" },
79 1339 : { "getchaintips", 0, "count" },
80 1339 : { "getchaintips", 1, "branchlen" },
81 1339 : { "getblockhash", 0, "height" },
82 1339 : { "getsuperblockbudget", 0, "index" },
83 1339 : { "waitforblockheight", 0, "height" },
84 1339 : { "waitforblockheight", 1, "timeout" },
85 1339 : { "waitforblock", 1, "timeout" },
86 1339 : { "reconsiderblock", 1, "ignore_chainlocks" },
87 1339 : { "waitfornewblock", 0, "timeout" },
88 1339 : { "listtransactions", 1, "count" },
89 1339 : { "listtransactions", 2, "skip" },
90 1339 : { "listtransactions", 3, "include_watchonly" },
91 1339 : { "walletpassphrase", 1, "timeout" },
92 1339 : { "walletpassphrase", 2, "mixingonly" },
93 1339 : { "getblocktemplate", 0, "template_request" },
94 1339 : { "listsinceblock", 1, "target_confirmations" },
95 1339 : { "listsinceblock", 2, "include_watchonly" },
96 1339 : { "listsinceblock", 3, "include_removed" },
97 1339 : { "sendmany", 1, "amounts" },
98 1339 : { "sendmany", 2, "minconf" },
99 1339 : { "sendmany", 3, "addlocked" },
100 1339 : { "sendmany", 5, "subtractfeefrom" },
101 1339 : { "sendmany", 6, "use_is" },
102 1339 : { "sendmany", 7, "use_cj" },
103 1339 : { "sendmany", 8, "conf_target" },
104 1339 : { "sendmany", 10, "fee_rate" },
105 1339 : { "sendmany", 11, "verbose" },
106 1339 : { "deriveaddresses", 1, "range" },
107 1339 : { "scantxoutset", 1, "scanobjects" },
108 1339 : { "addmultisigaddress", 0, "nrequired" },
109 1339 : { "addmultisigaddress", 1, "keys" },
110 1339 : { "createmultisig", 0, "nrequired" },
111 1339 : { "createmultisig", 1, "keys" },
112 1339 : { "listunspent", 0, "minconf" },
113 1339 : { "listunspent", 1, "maxconf" },
114 1339 : { "listunspent", 2, "addresses" },
115 1339 : { "listunspent", 3, "include_unsafe" },
116 1339 : { "listunspent", 4, "query_options" },
117 1339 : { "getblock", 1, "verbosity" },
118 1339 : { "getblock", 1, "verbose" },
119 1339 : { "getblockheader", 1, "verbose" },
120 1339 : { "getblockheaders", 1, "count" },
121 1339 : { "getblockheaders", 2, "verbose" },
122 1339 : { "getchaintxstats", 0, "nblocks" },
123 1339 : { "getmerkleblocks", 2, "count" },
124 1339 : { "gettransaction", 1, "include_watchonly" },
125 1339 : { "gettransaction", 2, "verbose" },
126 1339 : { "getrawtransaction", 1, "verbose" },
127 1339 : { "getislocks", 0, "txids" },
128 1339 : { "getrawtransactionmulti", 0, "transactions" },
129 1339 : { "getrawtransactionmulti", 1, "verbose" },
130 1339 : { "gettxchainlocks", 0, "txids" },
131 1339 : { "createrawtransaction", 0, "inputs" },
132 1339 : { "createrawtransaction", 1, "outputs" },
133 1339 : { "createrawtransaction", 2, "locktime" },
134 1339 : { "signrawtransactionwithkey", 1, "privkeys" },
135 1339 : { "signrawtransactionwithkey", 2, "prevtxs" },
136 1339 : { "signrawtransactionwithwallet", 1, "prevtxs" },
137 1339 : { "sendrawtransaction", 1, "maxfeerate" },
138 1339 : { "sendrawtransaction", 2, "instantsend" },
139 1339 : { "sendrawtransaction", 3, "bypasslimits" },
140 1339 : { "testmempoolaccept", 0, "rawtxs" },
141 1339 : { "testmempoolaccept", 1, "maxfeerate" },
142 1339 : { "submitpackage", 0, "package" },
143 1339 : { "combinerawtransaction", 0, "txs" },
144 1339 : { "fundrawtransaction", 1, "options" },
145 1339 : { "walletcreatefundedpsbt", 0, "inputs" },
146 1339 : { "walletcreatefundedpsbt", 1, "outputs" },
147 1339 : { "walletcreatefundedpsbt", 2, "locktime" },
148 1339 : { "walletcreatefundedpsbt", 3, "options" },
149 1339 : { "walletcreatefundedpsbt", 4, "bip32derivs" },
150 1339 : { "walletprocesspsbt", 1, "sign" },
151 1339 : { "walletprocesspsbt", 3, "bip32derivs" },
152 1339 : { "walletprocesspsbt", 4, "finalize" },
153 1339 : { "createpsbt", 0, "inputs" },
154 1339 : { "createpsbt", 1, "outputs" },
155 1339 : { "createpsbt", 2, "locktime" },
156 1339 : { "combinepsbt", 0, "txs"},
157 1339 : { "joinpsbts", 0, "txs"},
158 1339 : { "finalizepsbt", 1, "extract"},
159 1339 : { "converttopsbt", 1, "permitsigdata"},
160 1339 : { "gettxout", 1, "n" },
161 1339 : { "gettxout", 2, "include_mempool" },
162 1339 : { "gettxoutproof", 0, "txids" },
163 1339 : { "gettxoutsetinfo", 1, "hash_or_height" },
164 1339 : { "gettxoutsetinfo", 2, "use_index"},
165 1339 : { "lockunspent", 0, "unlock" },
166 1339 : { "lockunspent", 1, "transactions" },
167 1339 : { "lockunspent", 2, "persistent" },
168 1339 : { "send", 0, "outputs" },
169 1339 : { "send", 1, "conf_target" },
170 1339 : { "send", 3, "fee_rate"},
171 1339 : { "send", 4, "options" },
172 1339 : { "sendall", 0, "recipients" },
173 1339 : { "sendall", 1, "conf_target" },
174 1339 : { "sendall", 3, "fee_rate"},
175 1339 : { "sendall", 4, "options" },
176 1339 : { "simulaterawtransaction", 0, "rawtxs" },
177 1339 : { "simulaterawtransaction", 1, "options" },
178 1339 : { "importprivkey", 2, "rescan" },
179 1339 : { "importelectrumwallet", 1, "index" },
180 1339 : { "importaddress", 2, "rescan" },
181 1339 : { "importaddress", 3, "p2sh" },
182 1339 : { "importpubkey", 2, "rescan" },
183 1339 : { "importmulti", 0, "requests" },
184 1339 : { "importmulti", 1, "options" },
185 1339 : { "importdescriptors", 0, "requests" },
186 1339 : { "listdescriptors", 0, "private" },
187 1339 : { "verifychain", 0, "checklevel" },
188 1339 : { "verifychain", 1, "nblocks" },
189 1339 : { "getblockstats", 0, "hash_or_height" },
190 1339 : { "getblockstats", 1, "stats" },
191 1339 : { "pruneblockchain", 0, "height" },
192 1339 : { "keypoolrefill", 0, "newsize" },
193 1339 : { "getrawmempool", 0, "verbose" },
194 1339 : { "getrawmempool", 1, "mempool_sequence" },
195 1339 : { "estimatesmartfee", 0, "conf_target" },
196 1339 : { "estimaterawfee", 0, "conf_target" },
197 1339 : { "estimaterawfee", 1, "threshold" },
198 1339 : { "prioritisetransaction", 1, "fee_delta" },
199 1339 : { "setban", 2, "bantime" },
200 1339 : { "setban", 3, "absolute" },
201 1339 : { "setmnthreadactive", 0, "state" },
202 1339 : { "setnetworkactive", 0, "state" },
203 1339 : { "setcoinjoinrounds", 0, "rounds" },
204 1339 : { "setcoinjoinamount", 0, "amount" },
205 1339 : { "setwalletflag", 1, "value" },
206 1339 : { "getmempoolancestors", 1, "verbose" },
207 1339 : { "getmempooldescendants", 1, "verbose" },
208 1339 : { "gettxspendingprevout", 0, "outputs" },
209 1339 : { "logging", 0, "include" },
210 1339 : { "logging", 1, "exclude" },
211 1339 : { "sporkupdate", 1, "value" },
212 1339 : { "voteraw", 1, "mn-collateral-tx-index" },
213 1339 : { "voteraw", 5, "time" },
214 1339 : { "getblockhashes", 0, "high"},
215 1339 : { "getblockhashes", 1, "low" },
216 1339 : { "getspentinfo", 0, "request" },
217 1339 : { "getaddresstxids", 0, "addresses" },
218 1339 : { "getaddressbalance", 0, "addresses" },
219 1339 : { "getaddressdeltas", 0, "addresses" },
220 1339 : { "getaddressutxos", 0, "addresses" },
221 1339 : { "getaddressmempool", 0, "addresses" },
222 1339 : { "getspecialtxes", 1, "type" },
223 1339 : { "getspecialtxes", 2, "count" },
224 1339 : { "getspecialtxes", 3, "skip" },
225 1339 : { "getspecialtxes", 4, "verbosity" },
226 1339 : { "disconnectnode", 1, "nodeid" },
227 1339 : { "upgradewallet", 0, "version" },
228 : // Echo with conversion (For testing only)
229 1339 : { "echojson", 0, "arg0" },
230 1339 : { "echojson", 1, "arg1" },
231 1339 : { "echojson", 2, "arg2" },
232 1339 : { "echojson", 3, "arg3" },
233 1339 : { "echojson", 4, "arg4" },
234 1339 : { "echojson", 5, "arg5" },
235 1339 : { "echojson", 6, "arg6" },
236 1339 : { "echojson", 7, "arg7" },
237 1339 : { "echojson", 8, "arg8" },
238 1339 : { "echojson", 9, "arg9" },
239 1339 : { "rescanblockchain", 0, "start_height"},
240 1339 : { "rescanblockchain", 1, "stop_height"},
241 1339 : { "wipewallettxes", 0, "keep_confirmed"},
242 1339 : { "createwallet", 1, "disable_private_keys"},
243 1339 : { "createwallet", 2, "blank"},
244 1339 : { "createwallet", 4, "avoid_reuse"},
245 1339 : { "createwallet", 5, "descriptors"},
246 1339 : { "createwallet", 6, "load_on_startup"},
247 1339 : { "createwallet", 7, "external_signer"},
248 1339 : { "restorewallet", 2, "load_on_startup"},
249 1339 : { "loadwallet", 1, "load_on_startup"},
250 1339 : { "unloadwallet", 1, "load_on_startup"},
251 1339 : { "upgradetohd", 3, "rescan"},
252 1339 : { "getnodeaddresses", 0, "count"},
253 1339 : { "addpeeraddress", 1, "port"},
254 1339 : { "addpeeraddress", 2, "tried"},
255 1339 : { "sendmsgtopeer", 0, "peer_id" },
256 1339 : { "stop", 0, "wait" },
257 1339 : { "addnode", 2, "v2transport" },
258 1339 : { "addconnection", 2, "v2transport" },
259 1339 : { "verifychainlock", 2, "blockHeight" },
260 1339 : { "verifyislock", 3, "maxHeight" },
261 1339 : { "submitchainlock", 2, "blockHeight" },
262 1339 : { "mnauth", 0, "nodeId" },
263 : // Compound RPCs (note: index position is offset by one to account for subcommand)
264 1339 : { "bls generate", 1, "legacy" },
265 1339 : { "bls fromsecret", 2, "legacy" },
266 1339 : { "coinjoinsalt generate", 1, "overwrite" },
267 1339 : { "coinjoinsalt set", 2, "overwrite" },
268 1339 : { "gobject list-prepared", 1, "count" },
269 1339 : { "gobject prepare", 2, "revision" },
270 1339 : { "gobject prepare", 3, "time" },
271 1339 : { "gobject prepare", 7, "outputIndex" },
272 1339 : { "gobject submit", 2, "revision" },
273 1339 : { "gobject submit", 3, "time" },
274 1339 : { "masternode connect", 2, "v2transport" },
275 1339 : { "masternode payments", 2, "count" },
276 1339 : { "masternode winners", 1, "count" },
277 1339 : { "protx diff", 3, "extended" },
278 1339 : { "protx list", 2, "detailed" },
279 1339 : { "protx list", 3, "height" },
280 1339 : { "protx register", 2, "collateralIndex" },
281 1339 : { "protx register", 3, "coreP2PAddrs", true },
282 1339 : { "protx register", 10, "submit" },
283 1339 : { "protx register_legacy", 2, "collateralIndex" },
284 1339 : { "protx register_legacy", 3, "coreP2PAddrs", true },
285 1339 : { "protx register_legacy", 10, "submit" },
286 1339 : { "protx register_evo", 2, "collateralIndex" },
287 1339 : { "protx register_evo", 3, "coreP2PAddrs", true },
288 1339 : { "protx register_evo", 10, "platformP2PAddrs", true },
289 1339 : { "protx register_evo", 11, "platformHTTPSAddrs", true },
290 1339 : { "protx register_evo", 13, "submit" },
291 1339 : { "protx register_fund", 2, "coreP2PAddrs", true },
292 1339 : { "protx register_fund", 9, "submit" },
293 1339 : { "protx register_fund_legacy", 2, "coreP2PAddrs", true },
294 1339 : { "protx register_fund_legacy", 9, "submit" },
295 1339 : { "protx register_fund_evo", 2, "coreP2PAddrs", true },
296 1339 : { "protx register_fund_evo", 9, "platformP2PAddrs", true },
297 1339 : { "protx register_fund_evo", 10, "platformHTTPSAddrs", true },
298 1339 : { "protx register_fund_evo", 12, "submit" },
299 1339 : { "protx register_prepare", 2, "collateralIndex" },
300 1339 : { "protx register_prepare", 3, "coreP2PAddrs", true },
301 1339 : { "protx register_prepare_legacy", 2, "collateralIndex" },
302 1339 : { "protx register_prepare_legacy", 3, "coreP2PAddrs", true },
303 1339 : { "protx register_prepare_evo", 2, "collateralIndex" },
304 1339 : { "protx register_prepare_evo", 3, "coreP2PAddrs", true },
305 1339 : { "protx register_prepare_evo", 10, "platformP2PAddrs", true },
306 1339 : { "protx register_prepare_evo", 11, "platformHTTPSAddrs", true },
307 1339 : { "protx revoke", 3, "reason" },
308 1339 : { "protx revoke", 5, "submit" },
309 1339 : { "protx update_registrar", 6, "submit" },
310 1339 : { "protx update_registrar_legacy", 6, "submit" },
311 1339 : { "protx update_service", 2, "coreP2PAddrs", true },
312 1339 : { "protx update_service", 6, "submit" },
313 1339 : { "protx update_service_evo", 2, "coreP2PAddrs", true },
314 1339 : { "protx update_service_evo", 5, "platformP2PAddrs", true },
315 1339 : { "protx update_service_evo", 6, "platformHTTPSAddrs", true },
316 1339 : { "protx update_service_evo", 9, "submit" },
317 1339 : { "quorum dkgsimerror", 2, "rate" },
318 1339 : { "quorum dkgstatus", 1, "detail_level" },
319 1339 : { "quorum getdata", 1, "nodeId" },
320 1339 : { "quorum getdata", 2, "llmqType" },
321 1339 : { "quorum getdata", 4, "dataMask" },
322 1339 : { "quorum getrecsig", 1, "llmqType" },
323 1339 : { "quorum hasrecsig", 1, "llmqType" },
324 1339 : { "quorum info", 1, "llmqType" },
325 1339 : { "quorum info", 3, "includeSkShare" },
326 1339 : { "quorum isconflicting", 1, "llmqType" },
327 1339 : { "quorum listextended", 1, "height" },
328 1339 : { "quorum list", 1, "count" },
329 1339 : { "quorum memberof", 2, "scanQuorumsCount" },
330 1339 : { "quorum platformsign", 4, "submit" },
331 1339 : { "quorum rotationinfo", 2, "extraShare" },
332 1339 : { "quorum rotationinfo", 3, "baseBlockHashes" },
333 1339 : { "quorum selectquorum", 1, "llmqType" },
334 1339 : { "quorum sign", 1, "llmqType" },
335 1339 : { "quorum sign", 5, "submit" },
336 1339 : { "quorum verify", 1, "llmqType" },
337 1339 : { "quorum verify", 6, "signHeight" },
338 : };
339 : // clang-format on
340 :
341 : /** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
342 730 : static UniValue Parse(std::string_view raw)
343 : {
344 730 : UniValue parsed;
345 730 : if (!parsed.read(raw)) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
346 717 : return parsed;
347 743 : }
348 :
349 : class CRPCConvertTable
350 : {
351 : private:
352 : std::map<std::pair<std::string, int>, bool> members;
353 : std::map<std::pair<std::string, std::string>, bool> membersByName;
354 :
355 740 : std::string_view MaybeUnquoteString(std::string_view arg_value)
356 : {
357 740 : if (arg_value.size() >= 2 && ((arg_value.front() == '\'' && arg_value.back() == '\'') || (arg_value.front() == '\"' && arg_value.back() == '\"'))) {
358 4 : return arg_value.substr(1, arg_value.size() - 2);
359 : }
360 736 : return arg_value;
361 740 : }
362 :
363 10 : bool LikelyJSONType(std::string_view arg_value)
364 : {
365 10 : arg_value = MaybeUnquoteString(arg_value);
366 19 : return arg_value.size() >= 2 && ((arg_value.front() == '[' && arg_value.back() == ']') || (arg_value.front() == '{' && arg_value.back() == '}'));
367 : }
368 :
369 : public:
370 : CRPCConvertTable();
371 :
372 : /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
373 1335 : UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
374 : {
375 1976 : if (const auto it = members.find({method, param_idx}); it != members.end() && (!it->second || (it->second && LikelyJSONType(arg_value)))) {
376 639 : return Parse(MaybeUnquoteString(arg_value));
377 : }
378 696 : return arg_value;
379 1335 : }
380 :
381 : /** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
382 158 : UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
383 : {
384 249 : if (const auto it = membersByName.find({method, param_name}); it != membersByName.end() && (!it->second || (it->second && LikelyJSONType(arg_value)))) {
385 91 : return Parse(MaybeUnquoteString(arg_value));
386 : }
387 67 : return arg_value;
388 158 : }
389 :
390 : /** Check if we have any conversion rules for this method */
391 558 : bool IsDefined(const std::string& method, bool named) const
392 : {
393 558 : return named ?
394 3807 : std::find_if(membersByName.begin(), membersByName.end(), [&method](const auto& kv) { return kv.first.first == method; }) != membersByName.end()
395 160269 : : std::find_if(members.begin(), members.end(), [&method](const auto& kv) { return kv.first.first == method; }) != members.end();
396 : }
397 : };
398 :
399 2678 : CRPCConvertTable::CRPCConvertTable()
400 1339 : {
401 404378 : for (const auto& cp : vRPCConvertParams) {
402 403039 : members.try_emplace({cp.methodName, cp.paramIdx}, cp.preserve_str);
403 403039 : membersByName.try_emplace({cp.methodName, cp.paramName}, cp.preserve_str);
404 : }
405 2678 : }
406 :
407 1339 : static CRPCConvertTable rpcCvtTable;
408 :
409 1228 : UniValue RPCConvertValues(std::string strMethod, const std::vector<std::string> &strParams)
410 : {
411 1241 : UniValue params(UniValue::VARR);
412 :
413 : // If we are using a subcommand that is in the table, update the method name
414 2456 : strMethod = [&strMethod, &strParams]() {
415 1228 : if (!strParams.empty() && strMethod.find(' ') == std::string::npos) {
416 545 : std::string candidate{strMethod + " " + strParams[0]};
417 545 : return rpcCvtTable.IsDefined(candidate, /*named=*/false) ? candidate : strMethod;
418 545 : }
419 683 : return strMethod;
420 1228 : }();
421 :
422 2507 : for (unsigned int idx = 0; idx < strParams.size(); idx++) {
423 1292 : std::string_view value{strParams[idx]};
424 1292 : params.push_back(rpcCvtTable.ArgToUniValue(value, strMethod, idx));
425 1279 : }
426 :
427 1215 : return params;
428 1228 : }
429 :
430 97 : UniValue RPCConvertNamedValues(std::string strMethod, const std::vector<std::string> &strParams)
431 : {
432 97 : UniValue params(UniValue::VOBJ);
433 97 : UniValue positional_args{UniValue::VARR};
434 :
435 : // If we are using a subcommand that is in the table, update the method name
436 194 : strMethod = [&strMethod, &strParams]() {
437 97 : if (strMethod.find(' ') == std::string::npos && !strParams.empty() && strParams[0].find('=') == std::string::npos) {
438 13 : std::string candidate{strMethod + " " + strParams[0]};
439 13 : return rpcCvtTable.IsDefined(candidate, /*named=*/true) ? candidate : strMethod;
440 13 : }
441 84 : return strMethod;
442 97 : }();
443 :
444 298 : for (std::string_view s: strParams) {
445 201 : size_t pos = s.find('=');
446 201 : if (pos == std::string::npos) {
447 43 : positional_args.push_back(rpcCvtTable.ArgToUniValue(s, strMethod, positional_args.size()));
448 43 : continue;
449 : }
450 :
451 158 : std::string name{s.substr(0, pos)};
452 158 : std::string_view value{s.substr(pos+1)};
453 :
454 : // Intentionally overwrite earlier named values with later ones as a
455 : // convenience for scripts and command line users that want to merge
456 : // options.
457 158 : params.pushKV(name, rpcCvtTable.ArgToUniValue(value, strMethod, name));
458 158 : }
459 :
460 97 : if (!positional_args.empty()) {
461 : // Use __pushKV instead of pushKV to avoid overwriting an explicit
462 : // "args" value with an implicit one. Let the RPC server handle the
463 : // request as given.
464 17 : params.__pushKV("args", positional_args);
465 17 : }
466 :
467 97 : return params;
468 97 : }
|