]>
Commit | Line | Data |
---|---|---|
69d605f4 | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
77920402 | 3 | // Distributed under the MIT software license, see the accompanying |
3a25a2b9 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
69d605f4 | 5 | |
fb78cc23 | 6 | #include "rpcserver.h" |
51ed9ec9 BD |
7 | |
8 | #include "base58.h" | |
0eeb4f5d | 9 | #include "init.h" |
4401b2d7 EL |
10 | #include "random.h" |
11 | #include "sync.h" | |
48ba56cd | 12 | #include "ui_interface.h" |
c037531d | 13 | #include "util.h" |
4401b2d7 | 14 | #include "utilstrencodings.h" |
fc72c078 S |
15 | #include "asyncrpcqueue.h" |
16 | ||
17 | #include <memory> | |
51ed9ec9 | 18 | |
afd64f76 WL |
19 | #include <univalue.h> |
20 | ||
914dc012 | 21 | #include <boost/bind.hpp> |
95d888a6 | 22 | #include <boost/filesystem.hpp> |
a0780ba0 | 23 | #include <boost/foreach.hpp> |
69d605f4 WL |
24 | #include <boost/iostreams/concepts.hpp> |
25 | #include <boost/iostreams/stream.hpp> | |
914dc012 | 26 | #include <boost/shared_ptr.hpp> |
4401b2d7 | 27 | #include <boost/signals2/signal.hpp> |
ad49c256 | 28 | #include <boost/thread.hpp> |
afd64f76 | 29 | #include <boost/algorithm/string/case_conv.hpp> // for to_upper() |
ed21d5bd | 30 | |
4401b2d7 | 31 | using namespace RPCServer; |
40a158e1 | 32 | using namespace std; |
69d605f4 | 33 | |
ff6a7af1 | 34 | static bool fRPCRunning = false; |
af82884a DK |
35 | static bool fRPCInWarmup = true; |
36 | static std::string rpcWarmupStatus("RPC server started"); | |
37 | static CCriticalSection cs_rpcWarmup; | |
afd64f76 WL |
38 | /* Timer-creating functions */ |
39 | static std::vector<RPCTimerInterface*> timerInterfaces; | |
40 | /* Map of name to timer. | |
41 | * @note Can be changed to std::unique_ptr when C++11 */ | |
42 | static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers; | |
e9205293 | 43 | |
4401b2d7 EL |
44 | static struct CRPCSignals |
45 | { | |
46 | boost::signals2::signal<void ()> Started; | |
47 | boost::signals2::signal<void ()> Stopped; | |
48 | boost::signals2::signal<void (const CRPCCommand&)> PreCommand; | |
49 | boost::signals2::signal<void (const CRPCCommand&)> PostCommand; | |
50 | } g_rpcSignals; | |
51 | ||
52 | void RPCServer::OnStarted(boost::function<void ()> slot) | |
53 | { | |
54 | g_rpcSignals.Started.connect(slot); | |
55 | } | |
56 | ||
57 | void RPCServer::OnStopped(boost::function<void ()> slot) | |
58 | { | |
59 | g_rpcSignals.Stopped.connect(slot); | |
60 | } | |
61 | ||
62 | void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot) | |
63 | { | |
64 | g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); | |
65 | } | |
66 | ||
67 | void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot) | |
68 | { | |
69 | g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); | |
70 | } | |
71 | ||
d014114d JS |
72 | void RPCTypeCheck(const UniValue& params, |
73 | const list<UniValue::VType>& typesExpected, | |
cc6dfd1f | 74 | bool fAllowNull) |
899d373b | 75 | { |
cc71666a | 76 | size_t i = 0; |
d014114d | 77 | BOOST_FOREACH(UniValue::VType t, typesExpected) |
899d373b GA |
78 | { |
79 | if (params.size() <= i) | |
80 | break; | |
81 | ||
d014114d | 82 | const UniValue& v = params[i]; |
ed21d5bd | 83 | if (!((v.type() == t) || (fAllowNull && (v.isNull())))) |
899d373b GA |
84 | { |
85 | string err = strprintf("Expected type %s, got %s", | |
ed21d5bd | 86 | uvTypeName(t), uvTypeName(v.type())); |
738835d7 | 87 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
88 | } |
89 | i++; | |
90 | } | |
91 | } | |
92 | ||
ed21d5bd JG |
93 | void RPCTypeCheckObj(const UniValue& o, |
94 | const map<string, UniValue::VType>& typesExpected, | |
cc6dfd1f | 95 | bool fAllowNull) |
899d373b | 96 | { |
d014114d | 97 | BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) |
899d373b | 98 | { |
d014114d | 99 | const UniValue& v = find_value(o, t.first); |
ed21d5bd | 100 | if (!fAllowNull && v.isNull()) |
7d9d134b | 101 | throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); |
cc6dfd1f | 102 | |
ed21d5bd | 103 | if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) |
899d373b GA |
104 | { |
105 | string err = strprintf("Expected type %s for %s, got %s", | |
ed21d5bd | 106 | uvTypeName(t.second), t.first, uvTypeName(v.type())); |
738835d7 | 107 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
108 | } |
109 | } | |
110 | } | |
111 | ||
d014114d | 112 | CAmount AmountFromValue(const UniValue& value) |
69d605f4 | 113 | { |
84d1d5fd WL |
114 | if (!value.isNum() && !value.isStr()) |
115 | throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); | |
c66dff3d | 116 | CAmount amount; |
fed500e2 | 117 | if (!ParseFixedPoint(value.getValStr(), 8, &amount)) |
738835d7 | 118 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); |
c66dff3d WL |
119 | if (!MoneyRange(amount)) |
120 | throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); | |
121 | return amount; | |
69d605f4 WL |
122 | } |
123 | ||
851f58f9 | 124 | UniValue ValueFromAmount(const CAmount& amount) |
69d605f4 | 125 | { |
d5bf1afa WL |
126 | bool sign = amount < 0; |
127 | int64_t n_abs = (sign ? -amount : amount); | |
128 | int64_t quotient = n_abs / COIN; | |
129 | int64_t remainder = n_abs % COIN; | |
130 | return UniValue(UniValue::VNUM, | |
131 | strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); | |
69d605f4 WL |
132 | } |
133 | ||
d014114d | 134 | uint256 ParseHashV(const UniValue& v, string strName) |
463c9710 PT |
135 | { |
136 | string strHex; | |
ed21d5bd | 137 | if (v.isStr()) |
463c9710 PT |
138 | strHex = v.get_str(); |
139 | if (!IsHex(strHex)) // Note: IsHex("") is false | |
140 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
141 | uint256 result; | |
142 | result.SetHex(strHex); | |
143 | return result; | |
144 | } | |
d014114d | 145 | uint256 ParseHashO(const UniValue& o, string strKey) |
463c9710 PT |
146 | { |
147 | return ParseHashV(find_value(o, strKey), strKey); | |
148 | } | |
d014114d | 149 | vector<unsigned char> ParseHexV(const UniValue& v, string strName) |
463c9710 PT |
150 | { |
151 | string strHex; | |
ed21d5bd | 152 | if (v.isStr()) |
463c9710 PT |
153 | strHex = v.get_str(); |
154 | if (!IsHex(strHex)) | |
155 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
156 | return ParseHex(strHex); | |
157 | } | |
d014114d | 158 | vector<unsigned char> ParseHexO(const UniValue& o, string strKey) |
463c9710 PT |
159 | { |
160 | return ParseHexV(find_value(o, strKey), strKey); | |
161 | } | |
69d605f4 | 162 | |
77920402 MF |
163 | /** |
164 | * Note: This interface may still be subject to change. | |
165 | */ | |
69d605f4 | 166 | |
db954a65 | 167 | std::string CRPCTable::help(const std::string& strCommand) const |
69d605f4 | 168 | { |
69d605f4 | 169 | string strRet; |
6b5b7cbf | 170 | string category; |
69d605f4 | 171 | set<rpcfn_type> setDone; |
6b5b7cbf CL |
172 | vector<pair<string, const CRPCCommand*> > vCommands; |
173 | ||
9862229d | 174 | for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) |
6b5b7cbf CL |
175 | vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); |
176 | sort(vCommands.begin(), vCommands.end()); | |
177 | ||
178 | BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands) | |
69d605f4 | 179 | { |
6b5b7cbf CL |
180 | const CRPCCommand *pcmd = command.second; |
181 | string strMethod = pcmd->name; | |
69d605f4 | 182 | // We already filter duplicates, but these deprecated screw up the sort order |
47f48a65 | 183 | if (strMethod.find("label") != string::npos) |
69d605f4 | 184 | continue; |
bd9aebf1 | 185 | if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) |
69d605f4 WL |
186 | continue; |
187 | try | |
188 | { | |
851f58f9 | 189 | UniValue params; |
dc42bf52 | 190 | rpcfn_type pfn = pcmd->actor; |
69d605f4 WL |
191 | if (setDone.insert(pfn).second) |
192 | (*pfn)(params, true); | |
193 | } | |
27df4123 | 194 | catch (const std::exception& e) |
69d605f4 WL |
195 | { |
196 | // Help text is returned in an exception | |
197 | string strHelp = string(e.what()); | |
198 | if (strCommand == "") | |
6b5b7cbf | 199 | { |
ab9dc75a | 200 | if (strHelp.find('\n') != string::npos) |
69d605f4 | 201 | strHelp = strHelp.substr(0, strHelp.find('\n')); |
6b5b7cbf CL |
202 | |
203 | if (category != pcmd->category) | |
204 | { | |
205 | if (!category.empty()) | |
206 | strRet += "\n"; | |
207 | category = pcmd->category; | |
208 | string firstLetter = category.substr(0,1); | |
209 | boost::to_upper(firstLetter); | |
210 | strRet += "== " + firstLetter + category.substr(1) + " ==\n"; | |
211 | } | |
212 | } | |
69d605f4 WL |
213 | strRet += strHelp + "\n"; |
214 | } | |
215 | } | |
216 | if (strRet == "") | |
7d9d134b | 217 | strRet = strprintf("help: unknown command: %s\n", strCommand); |
69d605f4 WL |
218 | strRet = strRet.substr(0,strRet.size()-1); |
219 | return strRet; | |
220 | } | |
221 | ||
d014114d | 222 | UniValue help(const UniValue& params, bool fHelp) |
9862229d PW |
223 | { |
224 | if (fHelp || params.size() > 1) | |
225 | throw runtime_error( | |
a6099ef3 | 226 | "help ( \"command\" )\n" |
227 | "\nList all commands, or get help for a specified command.\n" | |
228 | "\nArguments:\n" | |
229 | "1. \"command\" (string, optional) The command to get help on\n" | |
230 | "\nResult:\n" | |
231 | "\"text\" (string) The help text\n" | |
232 | ); | |
9862229d PW |
233 | |
234 | string strCommand; | |
235 | if (params.size() > 0) | |
236 | strCommand = params[0].get_str(); | |
237 | ||
238 | return tableRPC.help(strCommand); | |
239 | } | |
240 | ||
69d605f4 | 241 | |
d014114d | 242 | UniValue stop(const UniValue& params, bool fHelp) |
69d605f4 | 243 | { |
6e65420b | 244 | // Accept the deprecated and ignored 'detach' boolean argument |
3731f578 | 245 | if (fHelp || params.size() > 1) |
69d605f4 | 246 | throw runtime_error( |
92467073 | 247 | "stop\n" |
6c7cc8eb | 248 | "\nStop Zcash server."); |
69d605f4 | 249 | // Shutdown will take long enough that the response should get back |
9247134e | 250 | StartShutdown(); |
6c7cc8eb | 251 | return "Zcash server stopping"; |
69d605f4 WL |
252 | } |
253 | ||
77920402 MF |
254 | /** |
255 | * Call Table | |
256 | */ | |
e46704dd | 257 | static const CRPCCommand vRPCCommands[] = |
b9fb692d JS |
258 | { // category name actor (function) okSafeMode |
259 | // --------------------- ------------------------ ----------------------- ---------- | |
ab88ed93 | 260 | /* Overall control/query calls */ |
b9fb692d JS |
261 | { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ |
262 | { "control", "help", &help, true }, | |
263 | { "control", "stop", &stop, true }, | |
ab88ed93 WL |
264 | |
265 | /* P2P networking */ | |
b9fb692d JS |
266 | { "network", "getnetworkinfo", &getnetworkinfo, true }, |
267 | { "network", "addnode", &addnode, true }, | |
94ee48c4 | 268 | { "network", "disconnectnode", &disconnectnode, true }, |
b9fb692d JS |
269 | { "network", "getaddednodeinfo", &getaddednodeinfo, true }, |
270 | { "network", "getconnectioncount", &getconnectioncount, true }, | |
271 | { "network", "getnettotals", &getnettotals, true }, | |
272 | { "network", "getpeerinfo", &getpeerinfo, true }, | |
273 | { "network", "ping", &ping, true }, | |
ed3f13a0 JS |
274 | { "network", "setban", &setban, true }, |
275 | { "network", "listbanned", &listbanned, true }, | |
276 | { "network", "clearbanned", &clearbanned, true }, | |
ab88ed93 WL |
277 | |
278 | /* Block chain and UTXO */ | |
b9fb692d JS |
279 | { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, |
280 | { "blockchain", "getbestblockhash", &getbestblockhash, true }, | |
281 | { "blockchain", "getblockcount", &getblockcount, true }, | |
282 | { "blockchain", "getblock", &getblock, true }, | |
283 | { "blockchain", "getblockhash", &getblockhash, true }, | |
d3d5483e | 284 | { "blockchain", "getblockheader", &getblockheader, true }, |
b9fb692d JS |
285 | { "blockchain", "getchaintips", &getchaintips, true }, |
286 | { "blockchain", "getdifficulty", &getdifficulty, true }, | |
287 | { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, | |
288 | { "blockchain", "getrawmempool", &getrawmempool, true }, | |
289 | { "blockchain", "gettxout", &gettxout, true }, | |
59ed61b3 MC |
290 | { "blockchain", "gettxoutproof", &gettxoutproof, true }, |
291 | { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, | |
b9fb692d JS |
292 | { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, |
293 | { "blockchain", "verifychain", &verifychain, true }, | |
48ba56cd | 294 | |
4a85e067 | 295 | /* Mining */ |
f3e49686 | 296 | #ifdef ENABLE_WALLET |
b9fb692d | 297 | { "mining", "getblocktemplate", &getblocktemplate, true }, |
f3e49686 | 298 | #endif |
b9fb692d | 299 | { "mining", "getmininginfo", &getmininginfo, true }, |
000499ae JG |
300 | { "mining", "getlocalsolps", &getlocalsolps, true }, |
301 | { "mining", "getnetworksolps", &getnetworksolps, true }, | |
b9fb692d JS |
302 | { "mining", "getnetworkhashps", &getnetworkhashps, true }, |
303 | { "mining", "prioritisetransaction", &prioritisetransaction, true }, | |
304 | { "mining", "submitblock", &submitblock, true }, | |
1b114e54 | 305 | { "mining", "getblocksubsidy", &getblocksubsidy, true }, |
6b5b7cbf | 306 | |
8e8b6d70 | 307 | #ifdef ENABLE_MINING |
6b5b7cbf | 308 | /* Coin generation */ |
b9fb692d JS |
309 | { "generating", "getgenerate", &getgenerate, true }, |
310 | { "generating", "setgenerate", &setgenerate, true }, | |
311 | { "generating", "generate", &generate, true }, | |
6b5b7cbf | 312 | #endif |
ab88ed93 WL |
313 | |
314 | /* Raw transactions */ | |
b9fb692d JS |
315 | { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, |
316 | { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, | |
317 | { "rawtransactions", "decodescript", &decodescript, true }, | |
318 | { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, | |
319 | { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, | |
320 | { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ | |
3d8013a0 MC |
321 | #ifdef ENABLE_WALLET |
322 | { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false }, | |
323 | #endif | |
ab88ed93 WL |
324 | |
325 | /* Utility functions */ | |
b9fb692d JS |
326 | { "util", "createmultisig", &createmultisig, true }, |
327 | { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ | |
328 | { "util", "verifymessage", &verifymessage, true }, | |
329 | { "util", "estimatefee", &estimatefee, true }, | |
330 | { "util", "estimatepriority", &estimatepriority, true }, | |
4e16a724 | 331 | { "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */ |
4a85e067 | 332 | |
bd9aebf1 | 333 | /* Not shown in help */ |
b9fb692d JS |
334 | { "hidden", "invalidateblock", &invalidateblock, true }, |
335 | { "hidden", "reconsiderblock", &reconsiderblock, true }, | |
336 | { "hidden", "setmocktime", &setmocktime, true }, | |
0f5954c4 | 337 | #ifdef ENABLE_WALLET |
b9fb692d | 338 | { "hidden", "resendwallettransactions", &resendwallettransactions, true}, |
0f5954c4 | 339 | #endif |
bd9aebf1 | 340 | |
4a85e067 WL |
341 | #ifdef ENABLE_WALLET |
342 | /* Wallet */ | |
b9fb692d JS |
343 | { "wallet", "addmultisigaddress", &addmultisigaddress, true }, |
344 | { "wallet", "backupwallet", &backupwallet, true }, | |
345 | { "wallet", "dumpprivkey", &dumpprivkey, true }, | |
346 | { "wallet", "dumpwallet", &dumpwallet, true }, | |
347 | { "wallet", "encryptwallet", &encryptwallet, true }, | |
348 | { "wallet", "getaccountaddress", &getaccountaddress, true }, | |
349 | { "wallet", "getaccount", &getaccount, true }, | |
350 | { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, | |
351 | { "wallet", "getbalance", &getbalance, false }, | |
352 | { "wallet", "getnewaddress", &getnewaddress, true }, | |
353 | { "wallet", "getrawchangeaddress", &getrawchangeaddress, true }, | |
354 | { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false }, | |
355 | { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false }, | |
356 | { "wallet", "gettransaction", &gettransaction, false }, | |
357 | { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false }, | |
358 | { "wallet", "getwalletinfo", &getwalletinfo, false }, | |
359 | { "wallet", "importprivkey", &importprivkey, true }, | |
360 | { "wallet", "importwallet", &importwallet, true }, | |
361 | { "wallet", "importaddress", &importaddress, true }, | |
362 | { "wallet", "keypoolrefill", &keypoolrefill, true }, | |
363 | { "wallet", "listaccounts", &listaccounts, false }, | |
364 | { "wallet", "listaddressgroupings", &listaddressgroupings, false }, | |
365 | { "wallet", "listlockunspent", &listlockunspent, false }, | |
366 | { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false }, | |
367 | { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, | |
368 | { "wallet", "listsinceblock", &listsinceblock, false }, | |
369 | { "wallet", "listtransactions", &listtransactions, false }, | |
370 | { "wallet", "listunspent", &listunspent, false }, | |
371 | { "wallet", "lockunspent", &lockunspent, true }, | |
372 | { "wallet", "move", &movecmd, false }, | |
373 | { "wallet", "sendfrom", &sendfrom, false }, | |
374 | { "wallet", "sendmany", &sendmany, false }, | |
375 | { "wallet", "sendtoaddress", &sendtoaddress, false }, | |
376 | { "wallet", "setaccount", &setaccount, true }, | |
377 | { "wallet", "settxfee", &settxfee, true }, | |
378 | { "wallet", "signmessage", &signmessage, true }, | |
379 | { "wallet", "walletlock", &walletlock, true }, | |
380 | { "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, | |
381 | { "wallet", "walletpassphrase", &walletpassphrase, true }, | |
6962bb3d | 382 | { "wallet", "zcbenchmark", &zc_benchmark, true }, |
730790f7 | 383 | { "wallet", "zcrawkeygen", &zc_raw_keygen, true }, |
b7e4abd6 | 384 | { "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true }, |
1737627c | 385 | { "wallet", "zcrawreceive", &zc_raw_receive, true }, |
c1c45943 | 386 | { "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true }, |
6c41028f | 387 | { "wallet", "z_listreceivedbyaddress",&z_listreceivedbyaddress,false }, |
a0a3334c S |
388 | { "wallet", "z_getbalance", &z_getbalance, false }, |
389 | { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, | |
6d2d045c | 390 | { "wallet", "z_sendmany", &z_sendmany, false }, |
fc72c078 | 391 | { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, |
c1eae280 | 392 | { "wallet", "z_getoperationresult", &z_getoperationresult, true }, |
34f0001c | 393 | { "wallet", "z_listoperationids", &z_listoperationids, true }, |
c1c45943 | 394 | { "wallet", "z_getnewaddress", &z_getnewaddress, true }, |
e709997f | 395 | { "wallet", "z_listaddresses", &z_listaddresses, true }, |
c1c45943 | 396 | { "wallet", "z_exportkey", &z_exportkey, true }, |
92444edc S |
397 | { "wallet", "z_importkey", &z_importkey, true }, |
398 | { "wallet", "z_exportwallet", &z_exportwallet, true }, | |
399 | { "wallet", "z_importwallet", &z_importwallet, true } | |
48ba56cd | 400 | #endif // ENABLE_WALLET |
69d605f4 | 401 | }; |
69d605f4 | 402 | |
9862229d | 403 | CRPCTable::CRPCTable() |
dc42bf52 | 404 | { |
dc42bf52 JG |
405 | unsigned int vcidx; |
406 | for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) | |
407 | { | |
e46704dd | 408 | const CRPCCommand *pcmd; |
69d605f4 | 409 | |
dc42bf52 JG |
410 | pcmd = &vRPCCommands[vcidx]; |
411 | mapCommands[pcmd->name] = pcmd; | |
412 | } | |
413 | } | |
69d605f4 | 414 | |
afd64f76 | 415 | const CRPCCommand *CRPCTable::operator[](const std::string &name) const |
9862229d PW |
416 | { |
417 | map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name); | |
418 | if (it == mapCommands.end()) | |
419 | return NULL; | |
420 | return (*it).second; | |
421 | } | |
69d605f4 | 422 | |
afd64f76 | 423 | bool StartRPC() |
69d605f4 | 424 | { |
afd64f76 | 425 | LogPrint("rpc", "Starting RPC\n"); |
ff6a7af1 | 426 | fRPCRunning = true; |
4401b2d7 | 427 | g_rpcSignals.Started(); |
fc72c078 | 428 | |
008fccfa | 429 | // Launch one async rpc worker. The ability to launch multiple workers is not recommended at present and thus the option is disabled. |
f86f625d | 430 | getAsyncRPCQueue()->addWorker(); |
008fccfa | 431 | /* |
8d08172d S |
432 | int n = GetArg("-rpcasyncthreads", 1); |
433 | if (n<1) { | |
434 | LogPrintf("ERROR: Invalid value %d for -rpcasyncthreads. Must be at least 1.\n", n); | |
435 | strerr = strprintf(_("An error occurred while setting up the Async RPC threads, invalid parameter value of %d (must be at least 1)."), n); | |
436 | uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); | |
437 | StartShutdown(); | |
438 | return; | |
439 | } | |
440 | for (int i = 0; i < n; i++) | |
f86f625d | 441 | getAsyncRPCQueue()->addWorker(); |
008fccfa | 442 | */ |
afd64f76 | 443 | return true; |
21eb5ada GA |
444 | } |
445 | ||
afd64f76 | 446 | void InterruptRPC() |
a8db31c8 | 447 | { |
afd64f76 WL |
448 | LogPrint("rpc", "Interrupting RPC\n"); |
449 | // Interrupt e.g. running longpolls | |
450 | fRPCRunning = false; | |
a8db31c8 WL |
451 | } |
452 | ||
afd64f76 | 453 | void StopRPC() |
21eb5ada | 454 | { |
afd64f76 | 455 | LogPrint("rpc", "Stopping RPC\n"); |
92f2c1fe | 456 | deadlineTimers.clear(); |
4401b2d7 | 457 | g_rpcSignals.Stopped(); |
fc72c078 S |
458 | |
459 | // Tells async queue to cancel all operations and shutdown. | |
3b54bf58 | 460 | LogPrintf("%s: waiting for async rpc workers to stop\n", __func__); |
f86f625d | 461 | getAsyncRPCQueue()->closeAndWait(); |
e9205293 DJS |
462 | } |
463 | ||
ff6a7af1 LD |
464 | bool IsRPCRunning() |
465 | { | |
466 | return fRPCRunning; | |
467 | } | |
468 | ||
af82884a DK |
469 | void SetRPCWarmupStatus(const std::string& newStatus) |
470 | { | |
471 | LOCK(cs_rpcWarmup); | |
472 | rpcWarmupStatus = newStatus; | |
473 | } | |
474 | ||
475 | void SetRPCWarmupFinished() | |
476 | { | |
477 | LOCK(cs_rpcWarmup); | |
478 | assert(fRPCInWarmup); | |
479 | fRPCInWarmup = false; | |
480 | } | |
481 | ||
78bdc810 JS |
482 | bool RPCIsInWarmup(std::string *outStatus) |
483 | { | |
484 | LOCK(cs_rpcWarmup); | |
485 | if (outStatus) | |
486 | *outStatus = rpcWarmupStatus; | |
487 | return fRPCInWarmup; | |
488 | } | |
489 | ||
d014114d | 490 | void JSONRequest::parse(const UniValue& valRequest) |
c6494d82 JG |
491 | { |
492 | // Parse request | |
ed21d5bd | 493 | if (!valRequest.isObject()) |
738835d7 | 494 | throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); |
d014114d | 495 | const UniValue& request = valRequest.get_obj(); |
c6494d82 JG |
496 | |
497 | // Parse id now so errors from here on will have the id | |
498 | id = find_value(request, "id"); | |
499 | ||
500 | // Parse method | |
851f58f9 | 501 | UniValue valMethod = find_value(request, "method"); |
ed21d5bd | 502 | if (valMethod.isNull()) |
738835d7 | 503 | throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); |
ed21d5bd | 504 | if (!valMethod.isStr()) |
738835d7 | 505 | throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); |
c6494d82 | 506 | strMethod = valMethod.get_str(); |
cf0c47b2 | 507 | if (strMethod != "getblocktemplate") |
28d4cff0 | 508 | LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); |
c6494d82 JG |
509 | |
510 | // Parse params | |
851f58f9 | 511 | UniValue valParams = find_value(request, "params"); |
ed21d5bd | 512 | if (valParams.isArray()) |
c6494d82 | 513 | params = valParams.get_array(); |
ed21d5bd | 514 | else if (valParams.isNull()) |
d014114d | 515 | params = UniValue(UniValue::VARR); |
c6494d82 | 516 | else |
738835d7 | 517 | throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); |
c6494d82 JG |
518 | } |
519 | ||
d014114d | 520 | static UniValue JSONRPCExecOne(const UniValue& req) |
61338901 | 521 | { |
38fc4b70 | 522 | UniValue rpc_result(UniValue::VOBJ); |
61338901 JG |
523 | |
524 | JSONRequest jreq; | |
525 | try { | |
526 | jreq.parse(req); | |
527 | ||
851f58f9 | 528 | UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); |
ed21d5bd | 529 | rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); |
61338901 | 530 | } |
d014114d | 531 | catch (const UniValue& objError) |
61338901 | 532 | { |
ed21d5bd | 533 | rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); |
61338901 | 534 | } |
27df4123 | 535 | catch (const std::exception& e) |
61338901 | 536 | { |
ed21d5bd | 537 | rpc_result = JSONRPCReplyObj(NullUniValue, |
738835d7 | 538 | JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); |
61338901 JG |
539 | } |
540 | ||
541 | return rpc_result; | |
542 | } | |
543 | ||
afd64f76 | 544 | std::string JSONRPCExecBatch(const UniValue& vReq) |
61338901 | 545 | { |
bf3f5602 | 546 | UniValue ret(UniValue::VARR); |
cc71666a | 547 | for (size_t reqIdx = 0; reqIdx < vReq.size(); reqIdx++) |
61338901 JG |
548 | ret.push_back(JSONRPCExecOne(vReq[reqIdx])); |
549 | ||
ed21d5bd | 550 | return ret.write() + "\n"; |
61338901 JG |
551 | } |
552 | ||
851f58f9 | 553 | UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const |
460c51fd | 554 | { |
483672f7 FV |
555 | // Return immediately if in warmup |
556 | { | |
557 | LOCK(cs_rpcWarmup); | |
558 | if (fRPCInWarmup) | |
559 | throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); | |
560 | } | |
561 | ||
460c51fd WL |
562 | // Find method |
563 | const CRPCCommand *pcmd = tableRPC[strMethod]; | |
564 | if (!pcmd) | |
738835d7 | 565 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); |
69d605f4 | 566 | |
4401b2d7 | 567 | g_rpcSignals.PreCommand(*pcmd); |
460c51fd WL |
568 | |
569 | try | |
570 | { | |
571 | // Execute | |
4401b2d7 | 572 | return pcmd->actor(params, false); |
460c51fd | 573 | } |
27df4123 | 574 | catch (const std::exception& e) |
460c51fd | 575 | { |
738835d7 | 576 | throw JSONRPCError(RPC_MISC_ERROR, e.what()); |
460c51fd | 577 | } |
4401b2d7 EL |
578 | |
579 | g_rpcSignals.PostCommand(*pcmd); | |
460c51fd | 580 | } |
69d605f4 | 581 | |
db954a65 PK |
582 | std::string HelpExampleCli(const std::string& methodname, const std::string& args) |
583 | { | |
58c4c0bb | 584 | return "> zcash-cli " + methodname + " " + args + "\n"; |
bbb09365 WL |
585 | } |
586 | ||
db954a65 PK |
587 | std::string HelpExampleRpc(const std::string& methodname, const std::string& args) |
588 | { | |
bbb09365 | 589 | return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " |
3985a40d | 590 | "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8232/\n"; |
bbb09365 WL |
591 | } |
592 | ||
afd64f76 WL |
593 | void RPCRegisterTimerInterface(RPCTimerInterface *iface) |
594 | { | |
595 | timerInterfaces.push_back(iface); | |
596 | } | |
597 | ||
598 | void RPCUnregisterTimerInterface(RPCTimerInterface *iface) | |
599 | { | |
600 | std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface); | |
601 | assert(i != timerInterfaces.end()); | |
602 | timerInterfaces.erase(i); | |
603 | } | |
604 | ||
605 | void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds) | |
606 | { | |
607 | if (timerInterfaces.empty()) | |
608 | throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); | |
609 | deadlineTimers.erase(name); | |
610 | RPCTimerInterface* timerInterface = timerInterfaces[0]; | |
611 | LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); | |
c922edd0 | 612 | deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)))); |
afd64f76 WL |
613 | } |
614 | ||
e46704dd | 615 | const CRPCTable tableRPC; |
fc72c078 S |
616 | |
617 | // Return async rpc queue | |
618 | std::shared_ptr<AsyncRPCQueue> getAsyncRPCQueue() | |
619 | { | |
f86f625d | 620 | return AsyncRPCQueue::sharedInstance(); |
fc72c078 | 621 | } |