Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 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 "rpcserver.h"
7 :
8 : #include "base58.h"
9 : #include "init.h"
10 : #include "random.h"
11 : #include "sync.h"
12 : #include "ui_interface.h"
13 : #include "util.h"
14 : #include "utilstrencodings.h"
15 :
16 : #include <univalue.h>
17 :
18 : #include <boost/bind.hpp>
19 : #include <boost/filesystem.hpp>
20 : #include <boost/foreach.hpp>
21 : #include <boost/iostreams/concepts.hpp>
22 : #include <boost/iostreams/stream.hpp>
23 : #include <boost/shared_ptr.hpp>
24 : #include <boost/signals2/signal.hpp>
25 : #include <boost/thread.hpp>
26 : #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
27 :
28 : using namespace RPCServer;
29 : using namespace std;
30 :
31 : static bool fRPCRunning = false;
32 : static bool fRPCInWarmup = true;
33 192 : static std::string rpcWarmupStatus("RPC server started");
34 96 : static CCriticalSection cs_rpcWarmup;
35 : /* Timer-creating functions */
36 96 : static std::vector<RPCTimerInterface*> timerInterfaces;
37 : /* Map of name to timer.
38 : * @note Can be changed to std::unique_ptr when C++11 */
39 96 : static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers;
40 :
41 960 : static struct CRPCSignals
42 : {
43 : boost::signals2::signal<void ()> Started;
44 : boost::signals2::signal<void ()> Stopped;
45 : boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
46 : boost::signals2::signal<void (const CRPCCommand&)> PostCommand;
47 96 : } g_rpcSignals;
48 :
49 0 : void RPCServer::OnStarted(boost::function<void ()> slot)
50 : {
51 0 : g_rpcSignals.Started.connect(slot);
52 0 : }
53 :
54 94 : void RPCServer::OnStopped(boost::function<void ()> slot)
55 : {
56 188 : g_rpcSignals.Stopped.connect(slot);
57 94 : }
58 :
59 94 : void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot)
60 : {
61 376 : g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
62 94 : }
63 :
64 0 : void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
65 : {
66 0 : g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
67 0 : }
68 :
69 1016 : void RPCTypeCheck(const UniValue& params,
70 : const list<UniValue::VType>& typesExpected,
71 : bool fAllowNull)
72 : {
73 1016 : unsigned int i = 0;
74 10822 : BOOST_FOREACH(UniValue::VType t, typesExpected)
75 : {
76 1191 : if (params.size() <= i)
77 : break;
78 :
79 1089 : const UniValue& v = params[i];
80 1089 : if (!((v.type() == t) || (fAllowNull && (v.isNull()))))
81 : {
82 : string err = strprintf("Expected type %s, got %s",
83 6 : uvTypeName(t), uvTypeName(v.type()));
84 3 : throw JSONRPCError(RPC_TYPE_ERROR, err);
85 : }
86 1086 : i++;
87 : }
88 1013 : }
89 :
90 9 : void RPCTypeCheckObj(const UniValue& o,
91 : const map<string, UniValue::VType>& typesExpected,
92 : bool fAllowNull)
93 : {
94 277 : BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected)
95 : {
96 29 : const UniValue& v = find_value(o, t.first);
97 29 : if (!fAllowNull && v.isNull())
98 0 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
99 :
100 29 : if (!((v.type() == t.second) || (fAllowNull && (v.isNull()))))
101 : {
102 : string err = strprintf("Expected type %s for %s, got %s",
103 0 : uvTypeName(t.second), t.first, uvTypeName(v.type()));
104 0 : throw JSONRPCError(RPC_TYPE_ERROR, err);
105 : }
106 : }
107 9 : }
108 :
109 222 : CAmount AmountFromValue(const UniValue& value)
110 : {
111 222 : if (!value.isNum() && !value.isStr())
112 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
113 : CAmount amount;
114 222 : if (!ParseFixedPoint(value.getValStr(), 8, &amount))
115 24 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
116 428 : if (!MoneyRange(amount))
117 3 : throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
118 213 : return amount;
119 : }
120 :
121 790 : UniValue ValueFromAmount(const CAmount& amount)
122 : {
123 790 : bool sign = amount < 0;
124 790 : int64_t n_abs = (sign ? -amount : amount);
125 790 : int64_t quotient = n_abs / COIN;
126 790 : int64_t remainder = n_abs % COIN;
127 : return UniValue(UniValue::VNUM,
128 1580 : strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
129 : }
130 :
131 54 : uint256 ParseHashV(const UniValue& v, string strName)
132 : {
133 : string strHex;
134 54 : if (v.isStr())
135 108 : strHex = v.get_str();
136 54 : if (!IsHex(strHex)) // Note: IsHex("") is false
137 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
138 : uint256 result;
139 53 : result.SetHex(strHex);
140 53 : return result;
141 : }
142 50 : uint256 ParseHashO(const UniValue& o, string strKey)
143 : {
144 100 : return ParseHashV(find_value(o, strKey), strKey);
145 : }
146 71 : vector<unsigned char> ParseHexV(const UniValue& v, string strName)
147 : {
148 : string strHex;
149 71 : if (v.isStr())
150 142 : strHex = v.get_str();
151 71 : if (!IsHex(strHex))
152 12 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
153 136 : return ParseHex(strHex);
154 : }
155 7 : vector<unsigned char> ParseHexO(const UniValue& o, string strKey)
156 : {
157 14 : return ParseHexV(find_value(o, strKey), strKey);
158 : }
159 :
160 : /**
161 : * Note: This interface may still be subject to change.
162 : */
163 :
164 0 : std::string CRPCTable::help(const std::string& strCommand) const
165 : {
166 : string strRet;
167 : string category;
168 : set<rpcfn_type> setDone;
169 0 : vector<pair<string, const CRPCCommand*> > vCommands;
170 :
171 0 : for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
172 0 : vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
173 : sort(vCommands.begin(), vCommands.end());
174 :
175 0 : BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands)
176 : {
177 0 : const CRPCCommand *pcmd = command.second;
178 0 : string strMethod = pcmd->name;
179 : // We already filter duplicates, but these deprecated screw up the sort order
180 0 : if (strMethod.find("label") != string::npos)
181 : continue;
182 0 : if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
183 : continue;
184 : try
185 : {
186 : UniValue params;
187 0 : rpcfn_type pfn = pcmd->actor;
188 0 : if (setDone.insert(pfn).second)
189 0 : (*pfn)(params, true);
190 : }
191 0 : catch (const std::exception& e)
192 : {
193 : // Help text is returned in an exception
194 0 : string strHelp = string(e.what());
195 0 : if (strCommand == "")
196 : {
197 0 : if (strHelp.find('\n') != string::npos)
198 0 : strHelp = strHelp.substr(0, strHelp.find('\n'));
199 :
200 0 : if (category != pcmd->category)
201 : {
202 0 : if (!category.empty())
203 : strRet += "\n";
204 0 : category = pcmd->category;
205 0 : string firstLetter = category.substr(0,1);
206 0 : boost::to_upper(firstLetter);
207 0 : strRet += "== " + firstLetter + category.substr(1) + " ==\n";
208 : }
209 : }
210 0 : strRet += strHelp + "\n";
211 : }
212 : }
213 0 : if (strRet == "")
214 0 : strRet = strprintf("help: unknown command: %s\n", strCommand);
215 0 : strRet = strRet.substr(0,strRet.size()-1);
216 0 : return strRet;
217 : }
218 :
219 0 : UniValue help(const UniValue& params, bool fHelp)
220 : {
221 0 : if (fHelp || params.size() > 1)
222 : throw runtime_error(
223 : "help ( \"command\" )\n"
224 : "\nList all commands, or get help for a specified command.\n"
225 : "\nArguments:\n"
226 : "1. \"command\" (string, optional) The command to get help on\n"
227 : "\nResult:\n"
228 : "\"text\" (string) The help text\n"
229 0 : );
230 :
231 : string strCommand;
232 0 : if (params.size() > 0)
233 0 : strCommand = params[0].get_str();
234 :
235 0 : return tableRPC.help(strCommand);
236 : }
237 :
238 :
239 92 : UniValue stop(const UniValue& params, bool fHelp)
240 : {
241 : // Accept the deprecated and ignored 'detach' boolean argument
242 184 : if (fHelp || params.size() > 1)
243 : throw runtime_error(
244 : "stop\n"
245 0 : "\nStop Bitcoin server.");
246 : // Event loop will exit after current HTTP requests have been handled, so
247 : // this reply will get back to the client.
248 92 : StartShutdown();
249 92 : return "Bitcoin server stopping";
250 : }
251 :
252 : /**
253 : * Call Table
254 : */
255 192 : static const CRPCCommand vRPCCommands[] =
256 : { // category name actor (function) okSafeMode
257 : // --------------------- ------------------------ ----------------------- ----------
258 : /* Overall control/query calls */
259 : { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
260 : { "control", "help", &help, true },
261 : { "control", "stop", &stop, true },
262 :
263 : /* P2P networking */
264 : { "network", "getnetworkinfo", &getnetworkinfo, true },
265 : { "network", "addnode", &addnode, true },
266 : { "network", "disconnectnode", &disconnectnode, true },
267 : { "network", "getaddednodeinfo", &getaddednodeinfo, true },
268 : { "network", "getconnectioncount", &getconnectioncount, true },
269 : { "network", "getnettotals", &getnettotals, true },
270 : { "network", "getpeerinfo", &getpeerinfo, true },
271 : { "network", "ping", &ping, true },
272 : { "network", "setban", &setban, true },
273 : { "network", "listbanned", &listbanned, true },
274 : { "network", "clearbanned", &clearbanned, true },
275 :
276 : /* Block chain and UTXO */
277 : { "blockchain", "getblockchaininfo", &getblockchaininfo, true },
278 : { "blockchain", "getbestblockhash", &getbestblockhash, true },
279 : { "blockchain", "getblockcount", &getblockcount, true },
280 : { "blockchain", "getblock", &getblock, true },
281 : { "blockchain", "getblockhash", &getblockhash, true },
282 : { "blockchain", "getblockheader", &getblockheader, true },
283 : { "blockchain", "getchaintips", &getchaintips, true },
284 : { "blockchain", "getdifficulty", &getdifficulty, true },
285 : { "blockchain", "getmempoolinfo", &getmempoolinfo, true },
286 : { "blockchain", "getrawmempool", &getrawmempool, true },
287 : { "blockchain", "gettxout", &gettxout, true },
288 : { "blockchain", "gettxoutproof", &gettxoutproof, true },
289 : { "blockchain", "verifytxoutproof", &verifytxoutproof, true },
290 : { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
291 : { "blockchain", "verifychain", &verifychain, true },
292 :
293 : /* Mining */
294 : { "mining", "getblocktemplate", &getblocktemplate, true },
295 : { "mining", "getmininginfo", &getmininginfo, true },
296 : { "mining", "getnetworkhashps", &getnetworkhashps, true },
297 : { "mining", "prioritisetransaction", &prioritisetransaction, true },
298 : { "mining", "submitblock", &submitblock, true },
299 :
300 : /* Coin generation */
301 : { "generating", "getgenerate", &getgenerate, true },
302 : { "generating", "setgenerate", &setgenerate, true },
303 : { "generating", "generate", &generate, true },
304 :
305 : /* Raw transactions */
306 : { "rawtransactions", "createrawtransaction", &createrawtransaction, true },
307 : { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true },
308 : { "rawtransactions", "decodescript", &decodescript, true },
309 : { "rawtransactions", "getrawtransaction", &getrawtransaction, true },
310 : { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
311 : { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
312 : #ifdef ENABLE_WALLET
313 : { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
314 : #endif
315 :
316 : /* Utility functions */
317 : { "util", "createmultisig", &createmultisig, true },
318 : { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
319 : { "util", "verifymessage", &verifymessage, true },
320 : { "util", "estimatefee", &estimatefee, true },
321 : { "util", "estimatepriority", &estimatepriority, true },
322 :
323 : /* Not shown in help */
324 : { "hidden", "invalidateblock", &invalidateblock, true },
325 : { "hidden", "reconsiderblock", &reconsiderblock, true },
326 : { "hidden", "setmocktime", &setmocktime, true },
327 : #ifdef ENABLE_WALLET
328 : { "hidden", "resendwallettransactions", &resendwallettransactions, true},
329 : #endif
330 :
331 : #ifdef ENABLE_WALLET
332 : /* Wallet */
333 : { "wallet", "addmultisigaddress", &addmultisigaddress, true },
334 : { "wallet", "backupwallet", &backupwallet, true },
335 : { "wallet", "dumpprivkey", &dumpprivkey, true },
336 : { "wallet", "dumpwallet", &dumpwallet, true },
337 : { "wallet", "encryptwallet", &encryptwallet, true },
338 : { "wallet", "getaccountaddress", &getaccountaddress, true },
339 : { "wallet", "getaccount", &getaccount, true },
340 : { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true },
341 : { "wallet", "getbalance", &getbalance, false },
342 : { "wallet", "getnewaddress", &getnewaddress, true },
343 : { "wallet", "getrawchangeaddress", &getrawchangeaddress, true },
344 : { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false },
345 : { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false },
346 : { "wallet", "gettransaction", &gettransaction, false },
347 : { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
348 : { "wallet", "getwalletinfo", &getwalletinfo, false },
349 : { "wallet", "importprivkey", &importprivkey, true },
350 : { "wallet", "importwallet", &importwallet, true },
351 : { "wallet", "importaddress", &importaddress, true },
352 : { "wallet", "importpubkey", &importpubkey, true },
353 : { "wallet", "keypoolrefill", &keypoolrefill, true },
354 : { "wallet", "listaccounts", &listaccounts, false },
355 : { "wallet", "listaddressgroupings", &listaddressgroupings, false },
356 : { "wallet", "listlockunspent", &listlockunspent, false },
357 : { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false },
358 : { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false },
359 : { "wallet", "listsinceblock", &listsinceblock, false },
360 : { "wallet", "listtransactions", &listtransactions, false },
361 : { "wallet", "listunspent", &listunspent, false },
362 : { "wallet", "lockunspent", &lockunspent, true },
363 : { "wallet", "move", &movecmd, false },
364 : { "wallet", "sendfrom", &sendfrom, false },
365 : { "wallet", "sendmany", &sendmany, false },
366 : { "wallet", "sendtoaddress", &sendtoaddress, false },
367 : { "wallet", "setaccount", &setaccount, true },
368 : { "wallet", "settxfee", &settxfee, true },
369 : { "wallet", "signmessage", &signmessage, true },
370 : { "wallet", "walletlock", &walletlock, true },
371 : { "wallet", "walletpassphrasechange", &walletpassphrasechange, true },
372 : { "wallet", "walletpassphrase", &walletpassphrase, true },
373 : #endif // ENABLE_WALLET
374 17952 : };
375 :
376 96 : CRPCTable::CRPCTable()
377 : {
378 : unsigned int vcidx;
379 9024 : for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
380 : {
381 : const CRPCCommand *pcmd;
382 :
383 8928 : pcmd = &vRPCCommands[vcidx];
384 8928 : mapCommands[pcmd->name] = pcmd;
385 : }
386 96 : }
387 :
388 104 : const CRPCCommand *CRPCTable::operator[](const std::string &name) const
389 : {
390 7244 : map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
391 7244 : if (it == mapCommands.end())
392 : return NULL;
393 3622 : return (*it).second;
394 : }
395 :
396 94 : bool StartRPC()
397 : {
398 94 : LogPrint("rpc", "Starting RPC\n");
399 94 : fRPCRunning = true;
400 94 : g_rpcSignals.Started();
401 94 : return true;
402 : }
403 :
404 94 : void InterruptRPC()
405 : {
406 94 : LogPrint("rpc", "Interrupting RPC\n");
407 : // Interrupt e.g. running longpolls
408 94 : fRPCRunning = false;
409 94 : }
410 :
411 94 : void StopRPC()
412 : {
413 94 : LogPrint("rpc", "Stopping RPC\n");
414 : deadlineTimers.clear();
415 94 : g_rpcSignals.Stopped();
416 94 : }
417 :
418 0 : bool IsRPCRunning()
419 : {
420 0 : return fRPCRunning;
421 : }
422 :
423 709 : void SetRPCWarmupStatus(const std::string& newStatus)
424 : {
425 709 : LOCK(cs_rpcWarmup);
426 : rpcWarmupStatus = newStatus;
427 709 : }
428 :
429 94 : void SetRPCWarmupFinished()
430 : {
431 94 : LOCK(cs_rpcWarmup);
432 94 : assert(fRPCInWarmup);
433 94 : fRPCInWarmup = false;
434 94 : }
435 :
436 27 : bool RPCIsInWarmup(std::string *outStatus)
437 : {
438 27 : LOCK(cs_rpcWarmup);
439 27 : if (outStatus)
440 : *outStatus = rpcWarmupStatus;
441 54 : return fRPCInWarmup;
442 : }
443 :
444 3519 : void JSONRequest::parse(const UniValue& valRequest)
445 : {
446 : // Parse request
447 3519 : if (!valRequest.isObject())
448 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
449 3519 : const UniValue& request = valRequest.get_obj();
450 :
451 : // Parse id now so errors from here on will have the id
452 10557 : id = find_value(request, "id");
453 :
454 : // Parse method
455 10557 : UniValue valMethod = find_value(request, "method");
456 3519 : if (valMethod.isNull())
457 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
458 3519 : if (!valMethod.isStr())
459 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
460 7038 : strMethod = valMethod.get_str();
461 7038 : if (strMethod != "getblocktemplate")
462 7038 : LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
463 :
464 : // Parse params
465 14076 : UniValue valParams = find_value(request, "params");
466 3519 : if (valParams.isArray())
467 3512 : params = valParams.get_array();
468 7 : else if (valParams.isNull())
469 21 : params = UniValue(UniValue::VARR);
470 : else
471 3519 : throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
472 3519 : }
473 :
474 0 : static UniValue JSONRPCExecOne(const UniValue& req)
475 : {
476 0 : UniValue rpc_result(UniValue::VOBJ);
477 :
478 0 : JSONRequest jreq;
479 : try {
480 0 : jreq.parse(req);
481 :
482 0 : UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
483 0 : rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
484 : }
485 0 : catch (const UniValue& objError)
486 : {
487 0 : rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
488 : }
489 0 : catch (const std::exception& e)
490 : {
491 0 : rpc_result = JSONRPCReplyObj(NullUniValue,
492 0 : JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
493 : }
494 :
495 0 : return rpc_result;
496 : }
497 :
498 0 : std::string JSONRPCExecBatch(const UniValue& vReq)
499 : {
500 0 : UniValue ret(UniValue::VARR);
501 0 : for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
502 0 : ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
503 :
504 0 : return ret.write() + "\n";
505 : }
506 :
507 3519 : UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const
508 : {
509 : // Return immediately if in warmup
510 : {
511 3519 : LOCK(cs_rpcWarmup);
512 3519 : if (fRPCInWarmup)
513 1 : throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
514 : }
515 :
516 : // Find method
517 3518 : const CRPCCommand *pcmd = tableRPC[strMethod];
518 3518 : if (!pcmd)
519 0 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
520 :
521 3518 : g_rpcSignals.PreCommand(*pcmd);
522 :
523 : try
524 : {
525 : // Execute
526 3518 : return pcmd->actor(params, false);
527 : }
528 1 : catch (const std::exception& e)
529 : {
530 3 : throw JSONRPCError(RPC_MISC_ERROR, e.what());
531 : }
532 :
533 : g_rpcSignals.PostCommand(*pcmd);
534 : }
535 :
536 28 : std::string HelpExampleCli(const std::string& methodname, const std::string& args)
537 : {
538 112 : return "> bitcoin-cli " + methodname + " " + args + "\n";
539 : }
540 :
541 17 : std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
542 : {
543 : return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
544 68 : "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
545 : }
546 :
547 94 : void RPCRegisterTimerInterface(RPCTimerInterface *iface)
548 : {
549 94 : timerInterfaces.push_back(iface);
550 94 : }
551 :
552 94 : void RPCUnregisterTimerInterface(RPCTimerInterface *iface)
553 : {
554 94 : std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface);
555 188 : assert(i != timerInterfaces.end());
556 : timerInterfaces.erase(i);
557 94 : }
558 :
559 1 : void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
560 : {
561 1 : if (timerInterfaces.empty())
562 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
563 : deadlineTimers.erase(name);
564 1 : RPCTimerInterface* timerInterface = timerInterfaces[0];
565 1 : LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
566 5 : deadlineTimers.insert(std::make_pair(name, timerInterface->NewTimer(func, nSeconds*1000)));
567 1 : }
568 :
569 288 : const CRPCTable tableRPC;
|