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.
12 #include "ui_interface.h"
14 #include "utilstrencodings.h"
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()
28 using namespace RPCServer
;
31 static bool fRPCRunning
= false;
32 static bool fRPCInWarmup
= true;
33 static std::string
rpcWarmupStatus("RPC server started");
34 static CCriticalSection cs_rpcWarmup
;
35 /* Timer-creating functions */
36 static std::vector
<RPCTimerInterface
*> timerInterfaces
;
37 /* Map of name to timer.
38 * @note Can be changed to std::unique_ptr when C++11 */
39 static std::map
<std::string
, boost::shared_ptr
<RPCTimerBase
> > deadlineTimers
;
41 static struct CRPCSignals
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
;
49 void RPCServer::OnStarted(boost::function
<void ()> slot
)
51 g_rpcSignals
.Started
.connect(slot
);
54 void RPCServer::OnStopped(boost::function
<void ()> slot
)
56 g_rpcSignals
.Stopped
.connect(slot
);
59 void RPCServer::OnPreCommand(boost::function
<void (const CRPCCommand
&)> slot
)
61 g_rpcSignals
.PreCommand
.connect(boost::bind(slot
, _1
));
64 void RPCServer::OnPostCommand(boost::function
<void (const CRPCCommand
&)> slot
)
66 g_rpcSignals
.PostCommand
.connect(boost::bind(slot
, _1
));
69 void RPCTypeCheck(const UniValue
& params
,
70 const list
<UniValue::VType
>& typesExpected
,
74 BOOST_FOREACH(UniValue::VType t
, typesExpected
)
76 if (params
.size() <= i
)
79 const UniValue
& v
= params
[i
];
80 if (!((v
.type() == t
) || (fAllowNull
&& (v
.isNull()))))
82 string err
= strprintf("Expected type %s, got %s",
83 uvTypeName(t
), uvTypeName(v
.type()));
84 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
90 void RPCTypeCheckObj(const UniValue
& o
,
91 const map
<string
, UniValue::VType
>& typesExpected
,
94 BOOST_FOREACH(const PAIRTYPE(string
, UniValue::VType
)& t
, typesExpected
)
96 const UniValue
& v
= find_value(o
, t
.first
);
97 if (!fAllowNull
&& v
.isNull())
98 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Missing %s", t
.first
));
100 if (!((v
.type() == t
.second
) || (fAllowNull
&& (v
.isNull()))))
102 string err
= strprintf("Expected type %s for %s, got %s",
103 uvTypeName(t
.second
), t
.first
, uvTypeName(v
.type()));
104 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
109 CAmount
AmountFromValue(const UniValue
& value
)
111 if (!value
.isNum() && !value
.isStr())
112 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount is not a number or string");
114 if (!ParseFixedPoint(value
.getValStr(), 8, &amount
))
115 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
116 if (!MoneyRange(amount
))
117 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount out of range");
121 UniValue
ValueFromAmount(const CAmount
& amount
)
123 bool sign
= amount
< 0;
124 int64_t n_abs
= (sign
? -amount
: amount
);
125 int64_t quotient
= n_abs
/ COIN
;
126 int64_t remainder
= n_abs
% COIN
;
127 return UniValue(UniValue::VNUM
,
128 strprintf("%s%d.%08d", sign
? "-" : "", quotient
, remainder
));
131 uint256
ParseHashV(const UniValue
& v
, string strName
)
135 strHex
= v
.get_str();
136 if (!IsHex(strHex
)) // Note: IsHex("") is false
137 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
139 result
.SetHex(strHex
);
142 uint256
ParseHashO(const UniValue
& o
, string strKey
)
144 return ParseHashV(find_value(o
, strKey
), strKey
);
146 vector
<unsigned char> ParseHexV(const UniValue
& v
, string strName
)
150 strHex
= v
.get_str();
152 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
153 return ParseHex(strHex
);
155 vector
<unsigned char> ParseHexO(const UniValue
& o
, string strKey
)
157 return ParseHexV(find_value(o
, strKey
), strKey
);
161 * Note: This interface may still be subject to change.
164 std::string
CRPCTable::help(const std::string
& strCommand
) const
168 set
<rpcfn_type
> setDone
;
169 vector
<pair
<string
, const CRPCCommand
*> > vCommands
;
171 for (map
<string
, const CRPCCommand
*>::const_iterator mi
= mapCommands
.begin(); mi
!= mapCommands
.end(); ++mi
)
172 vCommands
.push_back(make_pair(mi
->second
->category
+ mi
->first
, mi
->second
));
173 sort(vCommands
.begin(), vCommands
.end());
175 BOOST_FOREACH(const PAIRTYPE(string
, const CRPCCommand
*)& command
, vCommands
)
177 const CRPCCommand
*pcmd
= command
.second
;
178 string strMethod
= pcmd
->name
;
179 // We already filter duplicates, but these deprecated screw up the sort order
180 if (strMethod
.find("label") != string::npos
)
182 if ((strCommand
!= "" || pcmd
->category
== "hidden") && strMethod
!= strCommand
)
187 rpcfn_type pfn
= pcmd
->actor
;
188 if (setDone
.insert(pfn
).second
)
189 (*pfn
)(params
, true);
191 catch (const std::exception
& e
)
193 // Help text is returned in an exception
194 string strHelp
= string(e
.what());
195 if (strCommand
== "")
197 if (strHelp
.find('\n') != string::npos
)
198 strHelp
= strHelp
.substr(0, strHelp
.find('\n'));
200 if (category
!= pcmd
->category
)
202 if (!category
.empty())
204 category
= pcmd
->category
;
205 string firstLetter
= category
.substr(0,1);
206 boost::to_upper(firstLetter
);
207 strRet
+= "== " + firstLetter
+ category
.substr(1) + " ==\n";
210 strRet
+= strHelp
+ "\n";
214 strRet
= strprintf("help: unknown command: %s\n", strCommand
);
215 strRet
= strRet
.substr(0,strRet
.size()-1);
219 UniValue
help(const UniValue
& params
, bool fHelp
)
221 if (fHelp
|| params
.size() > 1)
223 "help ( \"command\" )\n"
224 "\nList all commands, or get help for a specified command.\n"
226 "1. \"command\" (string, optional) The command to get help on\n"
228 "\"text\" (string) The help text\n"
232 if (params
.size() > 0)
233 strCommand
= params
[0].get_str();
235 return tableRPC
.help(strCommand
);
239 UniValue
stop(const UniValue
& params
, bool fHelp
)
241 // Accept the deprecated and ignored 'detach' boolean argument
242 if (fHelp
|| params
.size() > 1)
245 "\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.
249 return "Bitcoin server stopping";
255 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 },
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 },
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 },
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 },
300 /* Coin generation */
301 { "generating", "getgenerate", &getgenerate
, true },
302 { "generating", "setgenerate", &setgenerate
, true },
303 { "generating", "generate", &generate
, true },
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 */
313 { "rawtransactions", "fundrawtransaction", &fundrawtransaction
, false },
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 },
323 /* Not shown in help */
324 { "hidden", "invalidateblock", &invalidateblock
, true },
325 { "hidden", "reconsiderblock", &reconsiderblock
, true },
326 { "hidden", "setmocktime", &setmocktime
, true },
328 { "hidden", "resendwallettransactions", &resendwallettransactions
, true},
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
376 CRPCTable::CRPCTable()
379 for (vcidx
= 0; vcidx
< (sizeof(vRPCCommands
) / sizeof(vRPCCommands
[0])); vcidx
++)
381 const CRPCCommand
*pcmd
;
383 pcmd
= &vRPCCommands
[vcidx
];
384 mapCommands
[pcmd
->name
] = pcmd
;
388 const CRPCCommand
*CRPCTable::operator[](const std::string
&name
) const
390 map
<string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
391 if (it
== mapCommands
.end())
398 LogPrint("rpc", "Starting RPC\n");
400 g_rpcSignals
.Started();
406 LogPrint("rpc", "Interrupting RPC\n");
407 // Interrupt e.g. running longpolls
413 LogPrint("rpc", "Stopping RPC\n");
414 deadlineTimers
.clear();
415 g_rpcSignals
.Stopped();
423 void SetRPCWarmupStatus(const std::string
& newStatus
)
426 rpcWarmupStatus
= newStatus
;
429 void SetRPCWarmupFinished()
432 assert(fRPCInWarmup
);
433 fRPCInWarmup
= false;
436 bool RPCIsInWarmup(std::string
*outStatus
)
440 *outStatus
= rpcWarmupStatus
;
444 void JSONRequest::parse(const UniValue
& valRequest
)
447 if (!valRequest
.isObject())
448 throw JSONRPCError(RPC_INVALID_REQUEST
, "Invalid Request object");
449 const UniValue
& request
= valRequest
.get_obj();
451 // Parse id now so errors from here on will have the id
452 id
= find_value(request
, "id");
455 UniValue valMethod
= find_value(request
, "method");
456 if (valMethod
.isNull())
457 throw JSONRPCError(RPC_INVALID_REQUEST
, "Missing method");
458 if (!valMethod
.isStr())
459 throw JSONRPCError(RPC_INVALID_REQUEST
, "Method must be a string");
460 strMethod
= valMethod
.get_str();
461 if (strMethod
!= "getblocktemplate")
462 LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod
));
465 UniValue valParams
= find_value(request
, "params");
466 if (valParams
.isArray())
467 params
= valParams
.get_array();
468 else if (valParams
.isNull())
469 params
= UniValue(UniValue::VARR
);
471 throw JSONRPCError(RPC_INVALID_REQUEST
, "Params must be an array");
474 static UniValue
JSONRPCExecOne(const UniValue
& req
)
476 UniValue
rpc_result(UniValue::VOBJ
);
482 UniValue result
= tableRPC
.execute(jreq
.strMethod
, jreq
.params
);
483 rpc_result
= JSONRPCReplyObj(result
, NullUniValue
, jreq
.id
);
485 catch (const UniValue
& objError
)
487 rpc_result
= JSONRPCReplyObj(NullUniValue
, objError
, jreq
.id
);
489 catch (const std::exception
& e
)
491 rpc_result
= JSONRPCReplyObj(NullUniValue
,
492 JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
498 std::string
JSONRPCExecBatch(const UniValue
& vReq
)
500 UniValue
ret(UniValue::VARR
);
501 for (unsigned int reqIdx
= 0; reqIdx
< vReq
.size(); reqIdx
++)
502 ret
.push_back(JSONRPCExecOne(vReq
[reqIdx
]));
504 return ret
.write() + "\n";
507 UniValue
CRPCTable::execute(const std::string
&strMethod
, const UniValue
¶ms
) const
509 // Return immediately if in warmup
513 throw JSONRPCError(RPC_IN_WARMUP
, rpcWarmupStatus
);
517 const CRPCCommand
*pcmd
= tableRPC
[strMethod
];
519 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found");
521 g_rpcSignals
.PreCommand(*pcmd
);
526 return pcmd
->actor(params
, false);
528 catch (const std::exception
& e
)
530 throw JSONRPCError(RPC_MISC_ERROR
, e
.what());
533 g_rpcSignals
.PostCommand(*pcmd
);
536 std::string
HelpExampleCli(const std::string
& methodname
, const std::string
& args
)
538 return "> bitcoin-cli " + methodname
+ " " + args
+ "\n";
541 std::string
HelpExampleRpc(const std::string
& methodname
, const std::string
& args
)
543 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
544 "\"method\": \"" + methodname
+ "\", \"params\": [" + args
+ "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
547 void RPCRegisterTimerInterface(RPCTimerInterface
*iface
)
549 timerInterfaces
.push_back(iface
);
552 void RPCUnregisterTimerInterface(RPCTimerInterface
*iface
)
554 std::vector
<RPCTimerInterface
*>::iterator i
= std::find(timerInterfaces
.begin(), timerInterfaces
.end(), iface
);
555 assert(i
!= timerInterfaces
.end());
556 timerInterfaces
.erase(i
);
559 void RPCRunLater(const std::string
& name
, boost::function
<void(void)> func
, int64_t nSeconds
)
561 if (timerInterfaces
.empty())
562 throw JSONRPCError(RPC_INTERNAL_ERROR
, "No timer handler registered for RPC");
563 deadlineTimers
.erase(name
);
564 RPCTimerInterface
* timerInterface
= timerInterfaces
[0];
565 LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name
, nSeconds
, timerInterface
->Name());
566 deadlineTimers
.insert(std::make_pair(name
, boost::shared_ptr
<RPCTimerBase
>(timerInterface
->NewTimer(func
, nSeconds
*1000))));
569 const CRPCTable tableRPC
;