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 
convert(const std::string & method,int idx)206     bool convert(const std::string& method, int idx) {
207         return (members.count(std::make_pair(method, idx)) > 0);
208     }
convert(const std::string & method,const std::string & name)209     bool convert(const std::string& method, const std::string& name) {
210         return (membersByName.count(std::make_pair(method, name)) > 0);
211     }
212 };
213 
CRPCConvertTable()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  */
ParseNonRFCJSONValue(const std::string & strVal)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 
RPCConvertValues(const std::string & strMethod,const std::vector<std::string> & strParams)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 
RPCConvertNamedValues(const std::string & strMethod,const std::vector<std::string> & strParams)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