/root/bitcoin/src/rpc/client.cpp
Line | Count | Source |
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 | | //! Specify whether parameter should be parsed by bitcoin-cli as a JSON value, |
16 | | //! or passed unchanged as a string, or a combination of both. |
17 | | enum ParamFormat { JSON, STRING, JSON_OR_STRING }; |
18 | | |
19 | | class CRPCConvertParam |
20 | | { |
21 | | public: |
22 | | std::string methodName; //!< method whose params want conversion |
23 | | int paramIdx; //!< 0-based idx of param to convert |
24 | | std::string paramName; //!< parameter name |
25 | | ParamFormat format{ParamFormat::JSON}; //!< parameter format |
26 | | }; |
27 | | |
28 | | // clang-format off |
29 | | /** |
30 | | * Specify a (method, idx, name, format) here if the argument is a non-string RPC |
31 | | * argument and needs to be converted from JSON, or if it is a string argument |
32 | | * passed to a method that accepts '=' characters in any string arguments. |
33 | | * |
34 | | * JSON parameters need to be listed here to make bitcoin-cli parse command line |
35 | | * arguments as JSON, instead of passing them as raw strings. `JSON` and |
36 | | * `JSON_OR_STRING` formats both make `bitcoin-cli` attempt to parse the |
37 | | * argument as JSON. But if parsing fails, the former triggers an error while |
38 | | * the latter falls back to passing the argument as a raw string. This is |
39 | | * useful for arguments like hash_or_height, allowing invocations such as |
40 | | * `bitcoin-cli getblockstats <hash>` without needing to quote the hash string |
41 | | * as JSON (`'"<hash>"'`). |
42 | | * |
43 | | * String parameters that may contain an '=' character (e.g. base64 strings, |
44 | | * filenames, or labels) need to be listed here with format `ParamFormat::STRING` |
45 | | * to make bitcoin-cli treat them as positional parameters when `-named` is used. |
46 | | * This prevents `bitcoin-cli` from splitting strings like "my=wallet" into a named |
47 | | * argument "my" and value "wallet" when the whole string is intended to be a |
48 | | * single positional argument. And if one string parameter is listed for a method, |
49 | | * other string parameters for that method need to be listed as well so bitcoin-cli |
50 | | * does not make the opposite mistake and pass other arguments by position instead of |
51 | | * name because it does not recognize their names. See \ref RPCConvertNamedValues |
52 | | * for more information on how named and positional arguments are distinguished with |
53 | | * -named. |
54 | | * |
55 | | * @note Parameter indexes start from 0. |
56 | | */ |
57 | | static const CRPCConvertParam vRPCConvertParams[] = |
58 | | { |
59 | | { "setmocktime", 0, "timestamp" }, |
60 | | { "mockscheduler", 0, "delta_time" }, |
61 | | { "utxoupdatepsbt", 0, "psbt", ParamFormat::STRING }, |
62 | | { "utxoupdatepsbt", 1, "descriptors" }, |
63 | | { "generatetoaddress", 0, "nblocks" }, |
64 | | { "generatetoaddress", 2, "maxtries" }, |
65 | | { "generatetodescriptor", 0, "num_blocks" }, |
66 | | { "generatetodescriptor", 2, "maxtries" }, |
67 | | { "generateblock", 1, "transactions" }, |
68 | | { "generateblock", 2, "submit" }, |
69 | | { "getnetworkhashps", 0, "nblocks" }, |
70 | | { "getnetworkhashps", 1, "height" }, |
71 | | { "sendtoaddress", 0, "address", ParamFormat::STRING }, |
72 | | { "sendtoaddress", 1, "amount" }, |
73 | | { "sendtoaddress", 2, "comment", ParamFormat::STRING }, |
74 | | { "sendtoaddress", 3, "comment_to", ParamFormat::STRING }, |
75 | | { "sendtoaddress", 4, "subtractfeefromamount" }, |
76 | | { "sendtoaddress", 5 , "replaceable" }, |
77 | | { "sendtoaddress", 6 , "conf_target" }, |
78 | | { "sendtoaddress", 7, "estimate_mode", ParamFormat::STRING }, |
79 | | { "sendtoaddress", 8, "avoid_reuse" }, |
80 | | { "sendtoaddress", 9, "fee_rate"}, |
81 | | { "sendtoaddress", 10, "verbose"}, |
82 | | { "getreceivedbyaddress", 1, "minconf" }, |
83 | | { "getreceivedbyaddress", 2, "include_immature_coinbase" }, |
84 | | { "getreceivedbylabel", 0, "label", ParamFormat::STRING }, |
85 | | { "getreceivedbylabel", 1, "minconf" }, |
86 | | { "getreceivedbylabel", 2, "include_immature_coinbase" }, |
87 | | { "listreceivedbyaddress", 0, "minconf" }, |
88 | | { "listreceivedbyaddress", 1, "include_empty" }, |
89 | | { "listreceivedbyaddress", 2, "include_watchonly" }, |
90 | | { "listreceivedbyaddress", 4, "include_immature_coinbase" }, |
91 | | { "listreceivedbylabel", 0, "minconf" }, |
92 | | { "listreceivedbylabel", 1, "include_empty" }, |
93 | | { "listreceivedbylabel", 2, "include_watchonly" }, |
94 | | { "listreceivedbylabel", 3, "include_immature_coinbase" }, |
95 | | { "getbalance", 1, "minconf" }, |
96 | | { "getbalance", 2, "include_watchonly" }, |
97 | | { "getbalance", 3, "avoid_reuse" }, |
98 | | { "getblockfrompeer", 1, "peer_id" }, |
99 | | { "getblockhash", 0, "height" }, |
100 | | { "waitforblockheight", 0, "height" }, |
101 | | { "waitforblockheight", 1, "timeout" }, |
102 | | { "waitforblock", 1, "timeout" }, |
103 | | { "waitfornewblock", 0, "timeout" }, |
104 | | { "listtransactions", 0, "label", ParamFormat::STRING }, |
105 | | { "listtransactions", 1, "count" }, |
106 | | { "listtransactions", 2, "skip" }, |
107 | | { "listtransactions", 3, "include_watchonly" }, |
108 | | { "walletpassphrase", 0, "passphrase", ParamFormat::STRING }, |
109 | | { "walletpassphrase", 1, "timeout" }, |
110 | | { "getblocktemplate", 0, "template_request" }, |
111 | | { "listsinceblock", 0, "blockhash", ParamFormat::STRING }, |
112 | | { "listsinceblock", 1, "target_confirmations" }, |
113 | | { "listsinceblock", 2, "include_watchonly" }, |
114 | | { "listsinceblock", 3, "include_removed" }, |
115 | | { "listsinceblock", 4, "include_change" }, |
116 | | { "listsinceblock", 5, "label", ParamFormat::STRING }, |
117 | | { "sendmany", 0, "dummy", ParamFormat::STRING }, |
118 | | { "sendmany", 1, "amounts" }, |
119 | | { "sendmany", 2, "minconf" }, |
120 | | { "sendmany", 3, "comment", ParamFormat::STRING }, |
121 | | { "sendmany", 4, "subtractfeefrom" }, |
122 | | { "sendmany", 5 , "replaceable" }, |
123 | | { "sendmany", 6 , "conf_target" }, |
124 | | { "sendmany", 7, "estimate_mode", ParamFormat::STRING }, |
125 | | { "sendmany", 8, "fee_rate"}, |
126 | | { "sendmany", 9, "verbose" }, |
127 | | { "deriveaddresses", 1, "range" }, |
128 | | { "scanblocks", 1, "scanobjects" }, |
129 | | { "scanblocks", 2, "start_height" }, |
130 | | { "scanblocks", 3, "stop_height" }, |
131 | | { "scanblocks", 5, "options" }, |
132 | | { "scanblocks", 5, "filter_false_positives" }, |
133 | | { "getdescriptoractivity", 0, "blockhashes" }, |
134 | | { "getdescriptoractivity", 1, "scanobjects" }, |
135 | | { "getdescriptoractivity", 2, "include_mempool" }, |
136 | | { "scantxoutset", 1, "scanobjects" }, |
137 | | { "createmultisig", 0, "nrequired" }, |
138 | | { "createmultisig", 1, "keys" }, |
139 | | { "listunspent", 0, "minconf" }, |
140 | | { "listunspent", 1, "maxconf" }, |
141 | | { "listunspent", 2, "addresses" }, |
142 | | { "listunspent", 3, "include_unsafe" }, |
143 | | { "listunspent", 4, "query_options" }, |
144 | | { "listunspent", 4, "minimumAmount" }, |
145 | | { "listunspent", 4, "maximumAmount" }, |
146 | | { "listunspent", 4, "maximumCount" }, |
147 | | { "listunspent", 4, "minimumSumAmount" }, |
148 | | { "listunspent", 4, "include_immature_coinbase" }, |
149 | | { "getblock", 1, "verbosity" }, |
150 | | { "getblock", 1, "verbose" }, |
151 | | { "getblockheader", 1, "verbose" }, |
152 | | { "getchaintxstats", 0, "nblocks" }, |
153 | | { "gettransaction", 1, "include_watchonly" }, |
154 | | { "gettransaction", 2, "verbose" }, |
155 | | { "getrawtransaction", 1, "verbosity" }, |
156 | | { "getrawtransaction", 1, "verbose" }, |
157 | | { "createrawtransaction", 0, "inputs" }, |
158 | | { "createrawtransaction", 1, "outputs" }, |
159 | | { "createrawtransaction", 2, "locktime" }, |
160 | | { "createrawtransaction", 3, "replaceable" }, |
161 | | { "createrawtransaction", 4, "version" }, |
162 | | { "decoderawtransaction", 1, "iswitness" }, |
163 | | { "signrawtransactionwithkey", 1, "privkeys" }, |
164 | | { "signrawtransactionwithkey", 2, "prevtxs" }, |
165 | | { "signrawtransactionwithwallet", 1, "prevtxs" }, |
166 | | { "sendrawtransaction", 1, "maxfeerate" }, |
167 | | { "sendrawtransaction", 2, "maxburnamount" }, |
168 | | { "testmempoolaccept", 0, "rawtxs" }, |
169 | | { "testmempoolaccept", 1, "maxfeerate" }, |
170 | | { "submitpackage", 0, "package" }, |
171 | | { "submitpackage", 1, "maxfeerate" }, |
172 | | { "submitpackage", 2, "maxburnamount" }, |
173 | | { "combinerawtransaction", 0, "txs" }, |
174 | | { "fundrawtransaction", 1, "options" }, |
175 | | { "fundrawtransaction", 1, "add_inputs"}, |
176 | | { "fundrawtransaction", 1, "include_unsafe"}, |
177 | | { "fundrawtransaction", 1, "minconf"}, |
178 | | { "fundrawtransaction", 1, "maxconf"}, |
179 | | { "fundrawtransaction", 1, "changePosition"}, |
180 | | { "fundrawtransaction", 1, "includeWatching"}, |
181 | | { "fundrawtransaction", 1, "lockUnspents"}, |
182 | | { "fundrawtransaction", 1, "fee_rate"}, |
183 | | { "fundrawtransaction", 1, "feeRate"}, |
184 | | { "fundrawtransaction", 1, "subtractFeeFromOutputs"}, |
185 | | { "fundrawtransaction", 1, "input_weights"}, |
186 | | { "fundrawtransaction", 1, "conf_target"}, |
187 | | { "fundrawtransaction", 1, "replaceable"}, |
188 | | { "fundrawtransaction", 1, "solving_data"}, |
189 | | { "fundrawtransaction", 1, "max_tx_weight"}, |
190 | | { "fundrawtransaction", 2, "iswitness" }, |
191 | | { "walletcreatefundedpsbt", 0, "inputs" }, |
192 | | { "walletcreatefundedpsbt", 1, "outputs" }, |
193 | | { "walletcreatefundedpsbt", 2, "locktime" }, |
194 | | { "walletcreatefundedpsbt", 3, "options" }, |
195 | | { "walletcreatefundedpsbt", 3, "add_inputs"}, |
196 | | { "walletcreatefundedpsbt", 3, "include_unsafe"}, |
197 | | { "walletcreatefundedpsbt", 3, "minconf"}, |
198 | | { "walletcreatefundedpsbt", 3, "maxconf"}, |
199 | | { "walletcreatefundedpsbt", 3, "changePosition"}, |
200 | | { "walletcreatefundedpsbt", 3, "includeWatching"}, |
201 | | { "walletcreatefundedpsbt", 3, "lockUnspents"}, |
202 | | { "walletcreatefundedpsbt", 3, "fee_rate"}, |
203 | | { "walletcreatefundedpsbt", 3, "feeRate"}, |
204 | | { "walletcreatefundedpsbt", 3, "subtractFeeFromOutputs"}, |
205 | | { "walletcreatefundedpsbt", 3, "conf_target"}, |
206 | | { "walletcreatefundedpsbt", 3, "replaceable"}, |
207 | | { "walletcreatefundedpsbt", 3, "solving_data"}, |
208 | | { "walletcreatefundedpsbt", 3, "max_tx_weight"}, |
209 | | { "walletcreatefundedpsbt", 4, "bip32derivs" }, |
210 | | { "walletcreatefundedpsbt", 5, "version" }, |
211 | | { "walletprocesspsbt", 0, "psbt", ParamFormat::STRING }, |
212 | | { "walletprocesspsbt", 1, "sign" }, |
213 | | { "walletprocesspsbt", 2, "sighashtype", ParamFormat::STRING }, |
214 | | { "walletprocesspsbt", 3, "bip32derivs" }, |
215 | | { "walletprocesspsbt", 4, "finalize" }, |
216 | | { "descriptorprocesspsbt", 0, "psbt", ParamFormat::STRING }, |
217 | | { "descriptorprocesspsbt", 1, "descriptors"}, |
218 | | { "descriptorprocesspsbt", 2, "sighashtype", ParamFormat::STRING }, |
219 | | { "descriptorprocesspsbt", 3, "bip32derivs" }, |
220 | | { "descriptorprocesspsbt", 4, "finalize" }, |
221 | | { "createpsbt", 0, "inputs" }, |
222 | | { "createpsbt", 1, "outputs" }, |
223 | | { "createpsbt", 2, "locktime" }, |
224 | | { "createpsbt", 3, "replaceable" }, |
225 | | { "createpsbt", 4, "version" }, |
226 | | { "combinepsbt", 0, "txs"}, |
227 | | { "joinpsbts", 0, "txs"}, |
228 | | { "finalizepsbt", 0, "psbt", ParamFormat::STRING }, |
229 | | { "finalizepsbt", 1, "extract"}, |
230 | | { "converttopsbt", 1, "permitsigdata"}, |
231 | | { "converttopsbt", 2, "iswitness"}, |
232 | | { "gettxout", 1, "n" }, |
233 | | { "gettxout", 2, "include_mempool" }, |
234 | | { "gettxoutproof", 0, "txids" }, |
235 | | { "gettxoutsetinfo", 1, "hash_or_height", ParamFormat::JSON_OR_STRING }, |
236 | | { "gettxoutsetinfo", 2, "use_index"}, |
237 | | { "dumptxoutset", 0, "path", ParamFormat::STRING }, |
238 | | { "dumptxoutset", 1, "type", ParamFormat::STRING }, |
239 | | { "dumptxoutset", 2, "options" }, |
240 | | { "dumptxoutset", 2, "rollback", ParamFormat::JSON_OR_STRING }, |
241 | | { "lockunspent", 0, "unlock" }, |
242 | | { "lockunspent", 1, "transactions" }, |
243 | | { "lockunspent", 2, "persistent" }, |
244 | | { "send", 0, "outputs" }, |
245 | | { "send", 1, "conf_target" }, |
246 | | { "send", 3, "fee_rate"}, |
247 | | { "send", 4, "options" }, |
248 | | { "send", 4, "add_inputs"}, |
249 | | { "send", 4, "include_unsafe"}, |
250 | | { "send", 4, "minconf"}, |
251 | | { "send", 4, "maxconf"}, |
252 | | { "send", 4, "add_to_wallet"}, |
253 | | { "send", 4, "change_position"}, |
254 | | { "send", 4, "fee_rate"}, |
255 | | { "send", 4, "include_watching"}, |
256 | | { "send", 4, "inputs"}, |
257 | | { "send", 4, "locktime"}, |
258 | | { "send", 4, "lock_unspents"}, |
259 | | { "send", 4, "psbt"}, |
260 | | { "send", 4, "subtract_fee_from_outputs"}, |
261 | | { "send", 4, "conf_target"}, |
262 | | { "send", 4, "replaceable"}, |
263 | | { "send", 4, "solving_data"}, |
264 | | { "send", 4, "max_tx_weight"}, |
265 | | { "send", 5, "version"}, |
266 | | { "sendall", 0, "recipients" }, |
267 | | { "sendall", 1, "conf_target" }, |
268 | | { "sendall", 3, "fee_rate"}, |
269 | | { "sendall", 4, "options" }, |
270 | | { "sendall", 4, "add_to_wallet"}, |
271 | | { "sendall", 4, "fee_rate"}, |
272 | | { "sendall", 4, "include_watching"}, |
273 | | { "sendall", 4, "inputs"}, |
274 | | { "sendall", 4, "locktime"}, |
275 | | { "sendall", 4, "lock_unspents"}, |
276 | | { "sendall", 4, "psbt"}, |
277 | | { "sendall", 4, "send_max"}, |
278 | | { "sendall", 4, "minconf"}, |
279 | | { "sendall", 4, "maxconf"}, |
280 | | { "sendall", 4, "conf_target"}, |
281 | | { "sendall", 4, "replaceable"}, |
282 | | { "sendall", 4, "solving_data"}, |
283 | | { "sendall", 4, "version"}, |
284 | | { "simulaterawtransaction", 0, "rawtxs" }, |
285 | | { "simulaterawtransaction", 1, "options" }, |
286 | | { "simulaterawtransaction", 1, "include_watchonly"}, |
287 | | { "importmempool", 0, "filepath", ParamFormat::STRING }, |
288 | | { "importmempool", 1, "options" }, |
289 | | { "importmempool", 1, "apply_fee_delta_priority" }, |
290 | | { "importmempool", 1, "use_current_time" }, |
291 | | { "importmempool", 1, "apply_unbroadcast_set" }, |
292 | | { "importdescriptors", 0, "requests" }, |
293 | | { "listdescriptors", 0, "private" }, |
294 | | { "verifychain", 0, "checklevel" }, |
295 | | { "verifychain", 1, "nblocks" }, |
296 | | { "getblockstats", 0, "hash_or_height", ParamFormat::JSON_OR_STRING }, |
297 | | { "getblockstats", 1, "stats" }, |
298 | | { "pruneblockchain", 0, "height" }, |
299 | | { "keypoolrefill", 0, "newsize" }, |
300 | | { "getrawmempool", 0, "verbose" }, |
301 | | { "getrawmempool", 1, "mempool_sequence" }, |
302 | | { "getorphantxs", 0, "verbosity" }, |
303 | | { "estimatesmartfee", 0, "conf_target" }, |
304 | | { "estimaterawfee", 0, "conf_target" }, |
305 | | { "estimaterawfee", 1, "threshold" }, |
306 | | { "prioritisetransaction", 1, "dummy" }, |
307 | | { "prioritisetransaction", 2, "fee_delta" }, |
308 | | { "setban", 2, "bantime" }, |
309 | | { "setban", 3, "absolute" }, |
310 | | { "setnetworkactive", 0, "state" }, |
311 | | { "setwalletflag", 1, "value" }, |
312 | | { "getmempoolancestors", 1, "verbose" }, |
313 | | { "getmempooldescendants", 1, "verbose" }, |
314 | | { "gettxspendingprevout", 0, "outputs" }, |
315 | | { "gettxspendingprevout", 1, "options" }, |
316 | | { "gettxspendingprevout", 1, "mempool_only" }, |
317 | | { "gettxspendingprevout", 1, "return_spending_tx" }, |
318 | | { "bumpfee", 1, "options" }, |
319 | | { "bumpfee", 1, "conf_target"}, |
320 | | { "bumpfee", 1, "fee_rate"}, |
321 | | { "bumpfee", 1, "replaceable"}, |
322 | | { "bumpfee", 1, "outputs"}, |
323 | | { "bumpfee", 1, "original_change_index"}, |
324 | | { "psbtbumpfee", 1, "options" }, |
325 | | { "psbtbumpfee", 1, "conf_target"}, |
326 | | { "psbtbumpfee", 1, "fee_rate"}, |
327 | | { "psbtbumpfee", 1, "replaceable"}, |
328 | | { "psbtbumpfee", 1, "outputs"}, |
329 | | { "psbtbumpfee", 1, "original_change_index"}, |
330 | | { "logging", 0, "include" }, |
331 | | { "logging", 1, "exclude" }, |
332 | | { "disconnectnode", 1, "nodeid" }, |
333 | | { "gethdkeys", 0, "active_only" }, |
334 | | { "gethdkeys", 0, "options" }, |
335 | | { "gethdkeys", 0, "private" }, |
336 | | { "createwalletdescriptor", 1, "options" }, |
337 | | { "createwalletdescriptor", 1, "internal" }, |
338 | | // Echo with conversion (For testing only) |
339 | | { "echojson", 0, "arg0" }, |
340 | | { "echojson", 1, "arg1" }, |
341 | | { "echojson", 2, "arg2" }, |
342 | | { "echojson", 3, "arg3" }, |
343 | | { "echojson", 4, "arg4" }, |
344 | | { "echojson", 5, "arg5" }, |
345 | | { "echojson", 6, "arg6" }, |
346 | | { "echojson", 7, "arg7" }, |
347 | | { "echojson", 8, "arg8" }, |
348 | | { "echojson", 9, "arg9" }, |
349 | | { "rescanblockchain", 0, "start_height"}, |
350 | | { "rescanblockchain", 1, "stop_height"}, |
351 | | { "createwallet", 0, "wallet_name", ParamFormat::STRING }, |
352 | | { "createwallet", 1, "disable_private_keys"}, |
353 | | { "createwallet", 2, "blank"}, |
354 | | { "createwallet", 3, "passphrase", ParamFormat::STRING }, |
355 | | { "createwallet", 4, "avoid_reuse"}, |
356 | | { "createwallet", 5, "descriptors"}, |
357 | | { "createwallet", 6, "load_on_startup"}, |
358 | | { "createwallet", 7, "external_signer"}, |
359 | | { "restorewallet", 0, "wallet_name", ParamFormat::STRING }, |
360 | | { "restorewallet", 1, "backup_file", ParamFormat::STRING }, |
361 | | { "restorewallet", 2, "load_on_startup"}, |
362 | | { "loadwallet", 0, "filename", ParamFormat::STRING }, |
363 | | { "loadwallet", 1, "load_on_startup"}, |
364 | | { "unloadwallet", 0, "wallet_name", ParamFormat::STRING }, |
365 | | { "unloadwallet", 1, "load_on_startup"}, |
366 | | { "getnodeaddresses", 0, "count"}, |
367 | | { "addpeeraddress", 1, "port"}, |
368 | | { "addpeeraddress", 2, "tried"}, |
369 | | { "sendmsgtopeer", 0, "peer_id" }, |
370 | | { "stop", 0, "wait" }, |
371 | | { "addnode", 2, "v2transport" }, |
372 | | { "addconnection", 2, "v2transport" }, |
373 | | { "decodepsbt", 0, "psbt", ParamFormat::STRING }, |
374 | | { "analyzepsbt", 0, "psbt", ParamFormat::STRING}, |
375 | | { "verifymessage", 1, "signature", ParamFormat::STRING }, |
376 | | { "verifymessage", 2, "message", ParamFormat::STRING }, |
377 | | { "getnewaddress", 0, "label", ParamFormat::STRING }, |
378 | | { "getnewaddress", 1, "address_type", ParamFormat::STRING }, |
379 | | { "backupwallet", 0, "destination", ParamFormat::STRING }, |
380 | | { "echoipc", 0, "arg", ParamFormat::STRING }, |
381 | | { "encryptwallet", 0, "passphrase", ParamFormat::STRING }, |
382 | | { "getaddressesbylabel", 0, "label", ParamFormat::STRING }, |
383 | | { "loadtxoutset", 0, "path", ParamFormat::STRING }, |
384 | | { "migratewallet", 0, "wallet_name", ParamFormat::STRING }, |
385 | | { "migratewallet", 1, "passphrase", ParamFormat::STRING }, |
386 | | { "setlabel", 1, "label", ParamFormat::STRING }, |
387 | | { "signmessage", 1, "message", ParamFormat::STRING }, |
388 | | { "signmessagewithprivkey", 1, "message", ParamFormat::STRING }, |
389 | | { "walletpassphrasechange", 0, "oldpassphrase", ParamFormat::STRING }, |
390 | | { "walletpassphrasechange", 1, "newpassphrase", ParamFormat::STRING }, |
391 | | }; |
392 | | // clang-format on |
393 | | |
394 | | /** Parse string to UniValue or throw runtime_error if string contains invalid JSON */ |
395 | | static UniValue Parse(std::string_view raw, ParamFormat format = ParamFormat::JSON) |
396 | 0 | { |
397 | 0 | UniValue parsed; |
398 | 0 | if (!parsed.read(raw)) { |
399 | 0 | if (format != ParamFormat::JSON_OR_STRING) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw)); |
400 | 0 | return UniValue(std::string(raw)); |
401 | 0 | } |
402 | 0 | return parsed; |
403 | 0 | } |
404 | | |
405 | | namespace rpc_convert |
406 | | { |
407 | | const CRPCConvertParam* FromPosition(std::string_view method, size_t pos) |
408 | 0 | { |
409 | 0 | auto it = std::ranges::find_if(vRPCConvertParams, [&](const auto& p) { |
410 | 0 | return p.methodName == method && p.paramIdx == static_cast<int>(pos); |
411 | 0 | }); |
412 | |
|
413 | 0 | return it == std::end(vRPCConvertParams) ? nullptr : &*it; |
414 | 0 | } |
415 | | |
416 | | const CRPCConvertParam* FromName(std::string_view method, std::string_view name) |
417 | 0 | { |
418 | 0 | auto it = std::ranges::find_if(vRPCConvertParams, [&](const auto& p) { |
419 | 0 | return p.methodName == method && p.paramName == name; |
420 | 0 | }); |
421 | |
|
422 | 0 | return it == std::end(vRPCConvertParams) ? nullptr : &*it; |
423 | 0 | } |
424 | | } // namespace rpc_convert |
425 | | |
426 | | static UniValue ParseParam(const CRPCConvertParam* param, std::string_view raw) |
427 | 0 | { |
428 | | // Only parse parameters which have the JSON or JSON_OR_STRING format; otherwise, treat them as strings. |
429 | 0 | return (param && (param->format == ParamFormat::JSON || param->format == ParamFormat::JSON_OR_STRING)) ? Parse(raw, param->format) : UniValue(std::string(raw)); |
430 | 0 | } |
431 | | |
432 | | /** |
433 | | * Convert command lines arguments to params object when -named is disabled. |
434 | | */ |
435 | | UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) |
436 | 0 | { |
437 | 0 | UniValue params(UniValue::VARR); |
438 | |
|
439 | 0 | for (std::string_view s : strParams) { |
440 | 0 | params.push_back(ParseParam(rpc_convert::FromPosition(strMethod, params.size()), s)); |
441 | 0 | } |
442 | |
|
443 | 0 | return params; |
444 | 0 | } |
445 | | |
446 | | /** |
447 | | * Convert command line arguments to params object when -named is enabled. |
448 | | * |
449 | | * The -named syntax accepts named arguments in NAME=VALUE format, as well as |
450 | | * positional arguments without names. The syntax is inherently ambiguous if |
451 | | * names are omitted and values contain '=', so a heuristic is used to |
452 | | * disambiguate: |
453 | | * |
454 | | * - Arguments that do not contain '=' are treated as positional parameters. |
455 | | * |
456 | | * - Arguments that do contain '=' are assumed to be named parameters in |
457 | | * NAME=VALUE format except for two special cases: |
458 | | * |
459 | | * 1. The case where NAME is not a known parameter name, and the next |
460 | | * positional parameter requires a JSON value, and the argument parses as |
461 | | * JSON. E.g. ["list", "with", "="]. |
462 | | * |
463 | | * 2. The case where NAME is not a known parameter name and the next |
464 | | * positional parameter requires a string value. E.g. "my=wallet". |
465 | | * |
466 | | * For example, the command `bitcoin-cli -named createwallet "my=wallet"`, |
467 | | * the parser initially sees "my=wallet" and attempts to process it as a |
468 | | * parameter named "my". When it finds that "my" is not a valid named parameter |
469 | | * parameter for this method, it falls back to checking the rule for the |
470 | | * next available positional parameter (index 0). Because it finds the rule |
471 | | * that this parameter is a ParamFormat::STRING, it correctly treats the entire |
472 | | * "my=wallet" as a single positional string, successfully creating a |
473 | | * wallet with that literal name. |
474 | | */ |
475 | | UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams) |
476 | 0 | { |
477 | 0 | UniValue params(UniValue::VOBJ); |
478 | 0 | UniValue positional_args{UniValue::VARR}; |
479 | |
|
480 | 0 | for (std::string_view s: strParams) { |
481 | 0 | size_t pos = s.find('='); |
482 | 0 | if (pos == std::string_view::npos) { |
483 | 0 | positional_args.push_back(ParseParam(rpc_convert::FromPosition(strMethod, positional_args.size()), s)); |
484 | 0 | continue; |
485 | 0 | } |
486 | | |
487 | 0 | std::string name{s.substr(0, pos)}; |
488 | 0 | std::string_view value{s.substr(pos+1)}; |
489 | |
|
490 | 0 | const CRPCConvertParam* named_param{rpc_convert::FromName(strMethod, name)}; |
491 | 0 | if (!named_param) { |
492 | 0 | const CRPCConvertParam* positional_param = rpc_convert::FromPosition(strMethod, positional_args.size()); |
493 | 0 | UniValue parsed_value; |
494 | 0 | if (positional_param && positional_param->format == ParamFormat::JSON && parsed_value.read(s)) { |
495 | 0 | positional_args.push_back(std::move(parsed_value)); |
496 | 0 | continue; |
497 | 0 | } else if (positional_param && positional_param->format == ParamFormat::STRING) { |
498 | 0 | positional_args.push_back(s); |
499 | 0 | continue; |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | | // Intentionally overwrite earlier named values with later ones as a |
504 | | // convenience for scripts and command line users that want to merge |
505 | | // options. |
506 | 0 | params.pushKV(name, ParseParam(named_param, value)); |
507 | 0 | } |
508 | |
|
509 | 0 | if (!positional_args.empty()) { |
510 | | // Use pushKVEnd instead of pushKV to avoid overwriting an explicit |
511 | | // "args" value with an implicit one. Let the RPC server handle the |
512 | | // request as given. |
513 | 0 | params.pushKVEnd("args", std::move(positional_args)); |
514 | 0 | } |
515 | |
|
516 | 0 | return params; |
517 | 0 | } |