1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
6 #include "rpc/server.h"
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 #include <memory> // for unique_ptr
29 #include <unordered_map>
31 using namespace RPCServer
;
34 static bool fRPCRunning
= false;
35 static bool fRPCInWarmup
= true;
36 static std::string
rpcWarmupStatus("RPC server started");
37 static CCriticalSection cs_rpcWarmup
;
38 /* Timer-creating functions */
39 static RPCTimerInterface
* timerInterface
= NULL
;
40 /* Map of name to timer. */
41 static std::map
<std::string
, std::unique_ptr
<RPCTimerBase
> > deadlineTimers
;
43 static struct CRPCSignals
45 boost::signals2::signal
<void ()> Started
;
46 boost::signals2::signal
<void ()> Stopped
;
47 boost::signals2::signal
<void (const CRPCCommand
&)> PreCommand
;
50 void RPCServer::OnStarted(boost::function
<void ()> slot
)
52 g_rpcSignals
.Started
.connect(slot
);
55 void RPCServer::OnStopped(boost::function
<void ()> slot
)
57 g_rpcSignals
.Stopped
.connect(slot
);
60 void RPCServer::OnPreCommand(boost::function
<void (const CRPCCommand
&)> slot
)
62 g_rpcSignals
.PreCommand
.connect(boost::bind(slot
, _1
));
65 void RPCTypeCheck(const UniValue
& params
,
66 const list
<UniValue::VType
>& typesExpected
,
70 BOOST_FOREACH(UniValue::VType t
, typesExpected
)
72 if (params
.size() <= i
)
75 const UniValue
& v
= params
[i
];
76 if (!((v
.type() == t
) || (fAllowNull
&& (v
.isNull()))))
78 string err
= strprintf("Expected type %s, got %s",
79 uvTypeName(t
), uvTypeName(v
.type()));
80 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
86 void RPCTypeCheckObj(const UniValue
& o
,
87 const map
<string
, UniValueType
>& typesExpected
,
91 for (const auto& t
: typesExpected
) {
92 const UniValue
& v
= find_value(o
, t
.first
);
93 if (!fAllowNull
&& v
.isNull())
94 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Missing %s", t
.first
));
96 if (!(t
.second
.typeAny
|| v
.type() == t
.second
.type
|| (fAllowNull
&& v
.isNull()))) {
97 string err
= strprintf("Expected type %s for %s, got %s",
98 uvTypeName(t
.second
.type
), t
.first
, uvTypeName(v
.type()));
99 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
105 BOOST_FOREACH(const string
& k
, o
.getKeys())
107 if (typesExpected
.count(k
) == 0)
109 string err
= strprintf("Unexpected key %s", k
);
110 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
116 CAmount
AmountFromValue(const UniValue
& value
)
118 if (!value
.isNum() && !value
.isStr())
119 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount is not a number or string");
121 if (!ParseFixedPoint(value
.getValStr(), 8, &amount
))
122 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
123 if (!MoneyRange(amount
))
124 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount out of range");
128 UniValue
ValueFromAmount(const CAmount
& amount
)
130 bool sign
= amount
< 0;
131 int64_t n_abs
= (sign
? -amount
: amount
);
132 int64_t quotient
= n_abs
/ COIN
;
133 int64_t remainder
= n_abs
% COIN
;
134 return UniValue(UniValue::VNUM
,
135 strprintf("%s%d.%08d", sign
? "-" : "", quotient
, remainder
));
138 uint256
ParseHashV(const UniValue
& v
, string strName
)
142 strHex
= v
.get_str();
143 if (!IsHex(strHex
)) // Note: IsHex("") is false
144 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
145 if (64 != strHex
.length())
146 throw JSONRPCError(RPC_INVALID_PARAMETER
, strprintf("%s must be of length %d (not %d)", strName
, 64, strHex
.length()));
148 result
.SetHex(strHex
);
151 uint256
ParseHashO(const UniValue
& o
, string strKey
)
153 return ParseHashV(find_value(o
, strKey
), strKey
);
155 vector
<unsigned char> ParseHexV(const UniValue
& v
, string strName
)
159 strHex
= v
.get_str();
161 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
162 return ParseHex(strHex
);
164 vector
<unsigned char> ParseHexO(const UniValue
& o
, string strKey
)
166 return ParseHexV(find_value(o
, strKey
), strKey
);
170 * Note: This interface may still be subject to change.
173 std::string
CRPCTable::help(const std::string
& strCommand
) const
177 set
<rpcfn_type
> setDone
;
178 vector
<pair
<string
, const CRPCCommand
*> > vCommands
;
180 for (map
<string
, const CRPCCommand
*>::const_iterator mi
= mapCommands
.begin(); mi
!= mapCommands
.end(); ++mi
)
181 vCommands
.push_back(make_pair(mi
->second
->category
+ mi
->first
, mi
->second
));
182 sort(vCommands
.begin(), vCommands
.end());
184 BOOST_FOREACH(const PAIRTYPE(string
, const CRPCCommand
*)& command
, vCommands
)
186 const CRPCCommand
*pcmd
= command
.second
;
187 string strMethod
= pcmd
->name
;
188 // We already filter duplicates, but these deprecated screw up the sort order
189 if (strMethod
.find("label") != string::npos
)
191 if ((strCommand
!= "" || pcmd
->category
== "hidden") && strMethod
!= strCommand
)
197 rpcfn_type pfn
= pcmd
->actor
;
198 if (setDone
.insert(pfn
).second
)
201 catch (const std::exception
& e
)
203 // Help text is returned in an exception
204 string strHelp
= string(e
.what());
205 if (strCommand
== "")
207 if (strHelp
.find('\n') != string::npos
)
208 strHelp
= strHelp
.substr(0, strHelp
.find('\n'));
210 if (category
!= pcmd
->category
)
212 if (!category
.empty())
214 category
= pcmd
->category
;
215 string firstLetter
= category
.substr(0,1);
216 boost::to_upper(firstLetter
);
217 strRet
+= "== " + firstLetter
+ category
.substr(1) + " ==\n";
220 strRet
+= strHelp
+ "\n";
224 strRet
= strprintf("help: unknown command: %s\n", strCommand
);
225 strRet
= strRet
.substr(0,strRet
.size()-1);
229 UniValue
help(const JSONRPCRequest
& jsonRequest
)
231 if (jsonRequest
.fHelp
|| jsonRequest
.params
.size() > 1)
233 "help ( \"command\" )\n"
234 "\nList all commands, or get help for a specified command.\n"
236 "1. \"command\" (string, optional) The command to get help on\n"
238 "\"text\" (string) The help text\n"
242 if (jsonRequest
.params
.size() > 0)
243 strCommand
= jsonRequest
.params
[0].get_str();
245 return tableRPC
.help(strCommand
);
249 UniValue
stop(const JSONRPCRequest
& jsonRequest
)
251 // Accept the deprecated and ignored 'detach' boolean argument
252 if (jsonRequest
.fHelp
|| jsonRequest
.params
.size() > 1)
255 "\nStop Bitcoin server.");
256 // Event loop will exit after current HTTP requests have been handled, so
257 // this reply will get back to the client.
259 return "Bitcoin server stopping";
265 static const CRPCCommand vRPCCommands
[] =
266 { // category name actor (function) okSafe argNames
267 // --------------------- ------------------------ ----------------------- ------ ----------
268 /* Overall control/query calls */
269 { "control", "help", &help
, true, {"command"} },
270 { "control", "stop", &stop
, true, {} },
273 CRPCTable::CRPCTable()
276 for (vcidx
= 0; vcidx
< (sizeof(vRPCCommands
) / sizeof(vRPCCommands
[0])); vcidx
++)
278 const CRPCCommand
*pcmd
;
280 pcmd
= &vRPCCommands
[vcidx
];
281 mapCommands
[pcmd
->name
] = pcmd
;
285 const CRPCCommand
*CRPCTable::operator[](const std::string
&name
) const
287 map
<string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
288 if (it
== mapCommands
.end())
293 bool CRPCTable::appendCommand(const std::string
& name
, const CRPCCommand
* pcmd
)
298 // don't allow overwriting for now
299 map
<string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
300 if (it
!= mapCommands
.end())
303 mapCommands
[name
] = pcmd
;
309 LogPrint("rpc", "Starting RPC\n");
311 g_rpcSignals
.Started();
317 LogPrint("rpc", "Interrupting RPC\n");
318 // Interrupt e.g. running longpolls
324 LogPrint("rpc", "Stopping RPC\n");
325 deadlineTimers
.clear();
326 g_rpcSignals
.Stopped();
334 void SetRPCWarmupStatus(const std::string
& newStatus
)
337 rpcWarmupStatus
= newStatus
;
340 void SetRPCWarmupFinished()
343 assert(fRPCInWarmup
);
344 fRPCInWarmup
= false;
347 bool RPCIsInWarmup(std::string
*outStatus
)
351 *outStatus
= rpcWarmupStatus
;
355 void JSONRPCRequest::parse(const UniValue
& valRequest
)
358 if (!valRequest
.isObject())
359 throw JSONRPCError(RPC_INVALID_REQUEST
, "Invalid Request object");
360 const UniValue
& request
= valRequest
.get_obj();
362 // Parse id now so errors from here on will have the id
363 id
= find_value(request
, "id");
366 UniValue valMethod
= find_value(request
, "method");
367 if (valMethod
.isNull())
368 throw JSONRPCError(RPC_INVALID_REQUEST
, "Missing method");
369 if (!valMethod
.isStr())
370 throw JSONRPCError(RPC_INVALID_REQUEST
, "Method must be a string");
371 strMethod
= valMethod
.get_str();
372 if (strMethod
!= "getblocktemplate")
373 LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod
));
376 UniValue valParams
= find_value(request
, "params");
377 if (valParams
.isArray() || valParams
.isObject())
379 else if (valParams
.isNull())
380 params
= UniValue(UniValue::VARR
);
382 throw JSONRPCError(RPC_INVALID_REQUEST
, "Params must be an array or object");
385 static UniValue
JSONRPCExecOne(const UniValue
& req
)
387 UniValue
rpc_result(UniValue::VOBJ
);
393 UniValue result
= tableRPC
.execute(jreq
);
394 rpc_result
= JSONRPCReplyObj(result
, NullUniValue
, jreq
.id
);
396 catch (const UniValue
& objError
)
398 rpc_result
= JSONRPCReplyObj(NullUniValue
, objError
, jreq
.id
);
400 catch (const std::exception
& e
)
402 rpc_result
= JSONRPCReplyObj(NullUniValue
,
403 JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
409 std::string
JSONRPCExecBatch(const UniValue
& vReq
)
411 UniValue
ret(UniValue::VARR
);
412 for (unsigned int reqIdx
= 0; reqIdx
< vReq
.size(); reqIdx
++)
413 ret
.push_back(JSONRPCExecOne(vReq
[reqIdx
]));
415 return ret
.write() + "\n";
419 * Process named arguments into a vector of positional arguments, based on the
420 * passed-in specification for the RPC call's arguments.
422 static inline JSONRPCRequest
transformNamedArguments(const JSONRPCRequest
& in
, const std::vector
<std::string
>& argNames
)
424 JSONRPCRequest out
= in
;
425 out
.params
= UniValue(UniValue::VARR
);
426 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
427 // there is an unknown one.
428 const std::vector
<std::string
>& keys
= in
.params
.getKeys();
429 const std::vector
<UniValue
>& values
= in
.params
.getValues();
430 std::unordered_map
<std::string
, const UniValue
*> argsIn
;
431 for (size_t i
=0; i
<keys
.size(); ++i
) {
432 argsIn
[keys
[i
]] = &values
[i
];
434 // Process expected parameters.
436 for (const std::string
&argName
: argNames
) {
437 auto fr
= argsIn
.find(argName
);
438 if (fr
!= argsIn
.end()) {
439 for (int i
= 0; i
< hole
; ++i
) {
440 // Fill hole between specified parameters with JSON nulls,
441 // but not at the end (for backwards compatibility with calls
442 // that act based on number of specified parameters).
443 out
.params
.push_back(UniValue());
446 out
.params
.push_back(*fr
->second
);
452 // If there are still arguments in the argsIn map, this is an error.
453 if (!argsIn
.empty()) {
454 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Unknown named parameter " + argsIn
.begin()->first
);
456 // Return request with named arguments transformed to positional arguments
460 UniValue
CRPCTable::execute(const JSONRPCRequest
&request
) const
462 // Return immediately if in warmup
466 throw JSONRPCError(RPC_IN_WARMUP
, rpcWarmupStatus
);
470 const CRPCCommand
*pcmd
= tableRPC
[request
.strMethod
];
472 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found");
474 g_rpcSignals
.PreCommand(*pcmd
);
478 // Execute, convert arguments to array if necessary
479 if (request
.params
.isObject()) {
480 return pcmd
->actor(transformNamedArguments(request
, pcmd
->argNames
));
482 return pcmd
->actor(request
);
485 catch (const std::exception
& e
)
487 throw JSONRPCError(RPC_MISC_ERROR
, e
.what());
491 std::vector
<std::string
> CRPCTable::listCommands() const
493 std::vector
<std::string
> commandList
;
494 typedef std::map
<std::string
, const CRPCCommand
*> commandMap
;
496 std::transform( mapCommands
.begin(), mapCommands
.end(),
497 std::back_inserter(commandList
),
498 boost::bind(&commandMap::value_type::first
,_1
) );
502 std::string
HelpExampleCli(const std::string
& methodname
, const std::string
& args
)
504 return "> bitcoin-cli " + methodname
+ " " + args
+ "\n";
507 std::string
HelpExampleRpc(const std::string
& methodname
, const std::string
& args
)
509 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
510 "\"method\": \"" + methodname
+ "\", \"params\": [" + args
+ "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
513 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface
*iface
)
516 timerInterface
= iface
;
519 void RPCSetTimerInterface(RPCTimerInterface
*iface
)
521 timerInterface
= iface
;
524 void RPCUnsetTimerInterface(RPCTimerInterface
*iface
)
526 if (timerInterface
== iface
)
527 timerInterface
= NULL
;
530 void RPCRunLater(const std::string
& name
, boost::function
<void(void)> func
, int64_t nSeconds
)
533 throw JSONRPCError(RPC_INTERNAL_ERROR
, "No timer handler registered for RPC");
534 deadlineTimers
.erase(name
);
535 LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name
, nSeconds
, timerInterface
->Name());
536 deadlineTimers
.emplace(name
, std::unique_ptr
<RPCTimerBase
>(timerInterface
->NewTimer(func
, nSeconds
*1000)));
539 int RPCSerializationFlags()
542 if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION
) == 0)
543 flag
|= SERIALIZE_TRANSACTION_NO_WITNESS
;