1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2017 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>
13 #include <ui_interface.h>
15 #include <utilstrencodings.h>
17 #include <boost/bind.hpp>
18 #include <boost/signals2/signal.hpp>
19 #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
20 #include <boost/algorithm/string/classification.hpp>
21 #include <boost/algorithm/string/split.hpp>
23 #include <memory> // for unique_ptr
24 #include <unordered_map>
26 static bool fRPCRunning
= false;
27 static bool fRPCInWarmup
= true;
28 static std::string
rpcWarmupStatus("RPC server started");
29 static CCriticalSection cs_rpcWarmup
;
30 /* Timer-creating functions */
31 static RPCTimerInterface
* timerInterface
= nullptr;
32 /* Map of name to timer. */
33 static std::map
<std::string
, std::unique_ptr
<RPCTimerBase
> > deadlineTimers
;
35 static struct CRPCSignals
37 boost::signals2::signal
<void ()> Started
;
38 boost::signals2::signal
<void ()> Stopped
;
39 boost::signals2::signal
<void (const CRPCCommand
&)> PreCommand
;
42 void RPCServer::OnStarted(std::function
<void ()> slot
)
44 g_rpcSignals
.Started
.connect(slot
);
47 void RPCServer::OnStopped(std::function
<void ()> slot
)
49 g_rpcSignals
.Stopped
.connect(slot
);
52 void RPCTypeCheck(const UniValue
& params
,
53 const std::list
<UniValue::VType
>& typesExpected
,
57 for (UniValue::VType t
: typesExpected
)
59 if (params
.size() <= i
)
62 const UniValue
& v
= params
[i
];
63 if (!(fAllowNull
&& v
.isNull())) {
64 RPCTypeCheckArgument(v
, t
);
70 void RPCTypeCheckArgument(const UniValue
& value
, UniValue::VType typeExpected
)
72 if (value
.type() != typeExpected
) {
73 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Expected type %s, got %s", uvTypeName(typeExpected
), uvTypeName(value
.type())));
77 void RPCTypeCheckObj(const UniValue
& o
,
78 const std::map
<std::string
, UniValueType
>& typesExpected
,
82 for (const auto& t
: typesExpected
) {
83 const UniValue
& v
= find_value(o
, t
.first
);
84 if (!fAllowNull
&& v
.isNull())
85 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Missing %s", t
.first
));
87 if (!(t
.second
.typeAny
|| v
.type() == t
.second
.type
|| (fAllowNull
&& v
.isNull()))) {
88 std::string err
= strprintf("Expected type %s for %s, got %s",
89 uvTypeName(t
.second
.type
), t
.first
, uvTypeName(v
.type()));
90 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
96 for (const std::string
& k
: o
.getKeys())
98 if (typesExpected
.count(k
) == 0)
100 std::string err
= strprintf("Unexpected key %s", k
);
101 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
107 CAmount
AmountFromValue(const UniValue
& value
)
109 if (!value
.isNum() && !value
.isStr())
110 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount is not a number or string");
112 if (!ParseFixedPoint(value
.getValStr(), 8, &amount
))
113 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
114 if (!MoneyRange(amount
))
115 throw JSONRPCError(RPC_TYPE_ERROR
, "Amount out of range");
119 uint256
ParseHashV(const UniValue
& v
, std::string strName
)
123 strHex
= v
.get_str();
124 if (!IsHex(strHex
)) // Note: IsHex("") is false
125 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
126 if (64 != strHex
.length())
127 throw JSONRPCError(RPC_INVALID_PARAMETER
, strprintf("%s must be of length %d (not %d)", strName
, 64, strHex
.length()));
129 result
.SetHex(strHex
);
132 uint256
ParseHashO(const UniValue
& o
, std::string strKey
)
134 return ParseHashV(find_value(o
, strKey
), strKey
);
136 std::vector
<unsigned char> ParseHexV(const UniValue
& v
, std::string strName
)
140 strHex
= v
.get_str();
142 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
143 return ParseHex(strHex
);
145 std::vector
<unsigned char> ParseHexO(const UniValue
& o
, std::string strKey
)
147 return ParseHexV(find_value(o
, strKey
), strKey
);
151 * Note: This interface may still be subject to change.
154 std::string
CRPCTable::help(const std::string
& strCommand
, const JSONRPCRequest
& helpreq
) const
157 std::string category
;
158 std::set
<rpcfn_type
> setDone
;
159 std::vector
<std::pair
<std::string
, const CRPCCommand
*> > vCommands
;
161 for (const auto& entry
: mapCommands
)
162 vCommands
.push_back(make_pair(entry
.second
->category
+ entry
.first
, entry
.second
));
163 sort(vCommands
.begin(), vCommands
.end());
165 JSONRPCRequest
jreq(helpreq
);
167 jreq
.params
= UniValue();
169 for (const std::pair
<std::string
, const CRPCCommand
*>& command
: vCommands
)
171 const CRPCCommand
*pcmd
= command
.second
;
172 std::string strMethod
= pcmd
->name
;
173 if ((strCommand
!= "" || pcmd
->category
== "hidden") && strMethod
!= strCommand
)
175 jreq
.strMethod
= strMethod
;
178 rpcfn_type pfn
= pcmd
->actor
;
179 if (setDone
.insert(pfn
).second
)
182 catch (const std::exception
& e
)
184 // Help text is returned in an exception
185 std::string strHelp
= std::string(e
.what());
186 if (strCommand
== "")
188 if (strHelp
.find('\n') != std::string::npos
)
189 strHelp
= strHelp
.substr(0, strHelp
.find('\n'));
191 if (category
!= pcmd
->category
)
193 if (!category
.empty())
195 category
= pcmd
->category
;
196 std::string firstLetter
= category
.substr(0,1);
197 boost::to_upper(firstLetter
);
198 strRet
+= "== " + firstLetter
+ category
.substr(1) + " ==\n";
201 strRet
+= strHelp
+ "\n";
205 strRet
= strprintf("help: unknown command: %s\n", strCommand
);
206 strRet
= strRet
.substr(0,strRet
.size()-1);
210 UniValue
help(const JSONRPCRequest
& jsonRequest
)
212 if (jsonRequest
.fHelp
|| jsonRequest
.params
.size() > 1)
213 throw std::runtime_error(
214 "help ( \"command\" )\n"
215 "\nList all commands, or get help for a specified command.\n"
217 "1. \"command\" (string, optional) The command to get help on\n"
219 "\"text\" (string) The help text\n"
222 std::string strCommand
;
223 if (jsonRequest
.params
.size() > 0)
224 strCommand
= jsonRequest
.params
[0].get_str();
226 return tableRPC
.help(strCommand
, jsonRequest
);
230 UniValue
stop(const JSONRPCRequest
& jsonRequest
)
232 // Accept the deprecated and ignored 'detach' boolean argument
233 if (jsonRequest
.fHelp
|| jsonRequest
.params
.size() > 1)
234 throw std::runtime_error(
236 "\nStop Bitcoin server.");
237 // Event loop will exit after current HTTP requests have been handled, so
238 // this reply will get back to the client.
240 return "Bitcoin server stopping";
243 UniValue
uptime(const JSONRPCRequest
& jsonRequest
)
245 if (jsonRequest
.fHelp
|| jsonRequest
.params
.size() > 1)
246 throw std::runtime_error(
248 "\nReturns the total uptime of the server.\n"
250 "ttt (numeric) The number of seconds that the server has been running\n"
252 + HelpExampleCli("uptime", "")
253 + HelpExampleRpc("uptime", "")
256 return GetTime() - GetStartupTime();
262 static const CRPCCommand vRPCCommands
[] =
263 { // category name actor (function) argNames
264 // --------------------- ------------------------ ----------------------- ----------
265 /* Overall control/query calls */
266 { "control", "help", &help
, {"command"} },
267 { "control", "stop", &stop
, {} },
268 { "control", "uptime", &uptime
, {} },
271 CRPCTable::CRPCTable()
274 for (vcidx
= 0; vcidx
< (sizeof(vRPCCommands
) / sizeof(vRPCCommands
[0])); vcidx
++)
276 const CRPCCommand
*pcmd
;
278 pcmd
= &vRPCCommands
[vcidx
];
279 mapCommands
[pcmd
->name
] = pcmd
;
283 const CRPCCommand
*CRPCTable::operator[](const std::string
&name
) const
285 std::map
<std::string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
286 if (it
== mapCommands
.end())
291 bool CRPCTable::appendCommand(const std::string
& name
, const CRPCCommand
* pcmd
)
296 // don't allow overwriting for now
297 std::map
<std::string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
298 if (it
!= mapCommands
.end())
301 mapCommands
[name
] = pcmd
;
307 LogPrint(BCLog::RPC
, "Starting RPC\n");
309 g_rpcSignals
.Started();
315 LogPrint(BCLog::RPC
, "Interrupting RPC\n");
316 // Interrupt e.g. running longpolls
322 LogPrint(BCLog::RPC
, "Stopping RPC\n");
323 deadlineTimers
.clear();
325 g_rpcSignals
.Stopped();
333 void SetRPCWarmupStatus(const std::string
& newStatus
)
336 rpcWarmupStatus
= newStatus
;
339 void SetRPCWarmupFinished()
342 assert(fRPCInWarmup
);
343 fRPCInWarmup
= false;
346 bool RPCIsInWarmup(std::string
*outStatus
)
350 *outStatus
= rpcWarmupStatus
;
354 void JSONRPCRequest::parse(const UniValue
& valRequest
)
357 if (!valRequest
.isObject())
358 throw JSONRPCError(RPC_INVALID_REQUEST
, "Invalid Request object");
359 const UniValue
& request
= valRequest
.get_obj();
361 // Parse id now so errors from here on will have the id
362 id
= find_value(request
, "id");
365 UniValue valMethod
= find_value(request
, "method");
366 if (valMethod
.isNull())
367 throw JSONRPCError(RPC_INVALID_REQUEST
, "Missing method");
368 if (!valMethod
.isStr())
369 throw JSONRPCError(RPC_INVALID_REQUEST
, "Method must be a string");
370 strMethod
= valMethod
.get_str();
371 LogPrint(BCLog::RPC
, "ThreadRPCServer method=%s\n", SanitizeString(strMethod
));
374 UniValue valParams
= find_value(request
, "params");
375 if (valParams
.isArray() || valParams
.isObject())
377 else if (valParams
.isNull())
378 params
= UniValue(UniValue::VARR
);
380 throw JSONRPCError(RPC_INVALID_REQUEST
, "Params must be an array or object");
383 bool IsDeprecatedRPCEnabled(const std::string
& method
)
385 const std::vector
<std::string
> enabled_methods
= gArgs
.GetArgs("-deprecatedrpc");
387 return find(enabled_methods
.begin(), enabled_methods
.end(), method
) != enabled_methods
.end();
390 static UniValue
JSONRPCExecOne(JSONRPCRequest jreq
, const UniValue
& req
)
392 UniValue
rpc_result(UniValue::VOBJ
);
397 UniValue result
= tableRPC
.execute(jreq
);
398 rpc_result
= JSONRPCReplyObj(result
, NullUniValue
, jreq
.id
);
400 catch (const UniValue
& objError
)
402 rpc_result
= JSONRPCReplyObj(NullUniValue
, objError
, jreq
.id
);
404 catch (const std::exception
& e
)
406 rpc_result
= JSONRPCReplyObj(NullUniValue
,
407 JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
413 std::string
JSONRPCExecBatch(const JSONRPCRequest
& jreq
, const UniValue
& vReq
)
415 UniValue
ret(UniValue::VARR
);
416 for (unsigned int reqIdx
= 0; reqIdx
< vReq
.size(); reqIdx
++)
417 ret
.push_back(JSONRPCExecOne(jreq
, vReq
[reqIdx
]));
419 return ret
.write() + "\n";
423 * Process named arguments into a vector of positional arguments, based on the
424 * passed-in specification for the RPC call's arguments.
426 static inline JSONRPCRequest
transformNamedArguments(const JSONRPCRequest
& in
, const std::vector
<std::string
>& argNames
)
428 JSONRPCRequest out
= in
;
429 out
.params
= UniValue(UniValue::VARR
);
430 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
431 // there is an unknown one.
432 const std::vector
<std::string
>& keys
= in
.params
.getKeys();
433 const std::vector
<UniValue
>& values
= in
.params
.getValues();
434 std::unordered_map
<std::string
, const UniValue
*> argsIn
;
435 for (size_t i
=0; i
<keys
.size(); ++i
) {
436 argsIn
[keys
[i
]] = &values
[i
];
438 // Process expected parameters.
440 for (const std::string
&argNamePattern
: argNames
) {
441 std::vector
<std::string
> vargNames
;
442 boost::algorithm::split(vargNames
, argNamePattern
, boost::algorithm::is_any_of("|"));
443 auto fr
= argsIn
.end();
444 for (const std::string
& argName
: vargNames
) {
445 fr
= argsIn
.find(argName
);
446 if (fr
!= argsIn
.end()) {
450 if (fr
!= argsIn
.end()) {
451 for (int i
= 0; i
< hole
; ++i
) {
452 // Fill hole between specified parameters with JSON nulls,
453 // but not at the end (for backwards compatibility with calls
454 // that act based on number of specified parameters).
455 out
.params
.push_back(UniValue());
458 out
.params
.push_back(*fr
->second
);
464 // If there are still arguments in the argsIn map, this is an error.
465 if (!argsIn
.empty()) {
466 throw JSONRPCError(RPC_INVALID_PARAMETER
, "Unknown named parameter " + argsIn
.begin()->first
);
468 // Return request with named arguments transformed to positional arguments
472 UniValue
CRPCTable::execute(const JSONRPCRequest
&request
) const
474 // Return immediately if in warmup
478 throw JSONRPCError(RPC_IN_WARMUP
, rpcWarmupStatus
);
482 const CRPCCommand
*pcmd
= tableRPC
[request
.strMethod
];
484 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found");
486 g_rpcSignals
.PreCommand(*pcmd
);
490 // Execute, convert arguments to array if necessary
491 if (request
.params
.isObject()) {
492 return pcmd
->actor(transformNamedArguments(request
, pcmd
->argNames
));
494 return pcmd
->actor(request
);
497 catch (const std::exception
& e
)
499 throw JSONRPCError(RPC_MISC_ERROR
, e
.what());
503 std::vector
<std::string
> CRPCTable::listCommands() const
505 std::vector
<std::string
> commandList
;
506 typedef std::map
<std::string
, const CRPCCommand
*> commandMap
;
508 std::transform( mapCommands
.begin(), mapCommands
.end(),
509 std::back_inserter(commandList
),
510 boost::bind(&commandMap::value_type::first
,_1
) );
514 std::string
HelpExampleCli(const std::string
& methodname
, const std::string
& args
)
516 return "> bitcoin-cli " + methodname
+ " " + args
+ "\n";
519 std::string
HelpExampleRpc(const std::string
& methodname
, const std::string
& args
)
521 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
522 "\"method\": \"" + methodname
+ "\", \"params\": [" + args
+ "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
525 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface
*iface
)
528 timerInterface
= iface
;
531 void RPCSetTimerInterface(RPCTimerInterface
*iface
)
533 timerInterface
= iface
;
536 void RPCUnsetTimerInterface(RPCTimerInterface
*iface
)
538 if (timerInterface
== iface
)
539 timerInterface
= nullptr;
542 void RPCRunLater(const std::string
& name
, std::function
<void(void)> func
, int64_t nSeconds
)
545 throw JSONRPCError(RPC_INTERNAL_ERROR
, "No timer handler registered for RPC");
546 deadlineTimers
.erase(name
);
547 LogPrint(BCLog::RPC
, "queue run of timer %s in %i seconds (using %s)\n", name
, nSeconds
, timerInterface
->Name());
548 deadlineTimers
.emplace(name
, std::unique_ptr
<RPCTimerBase
>(timerInterface
->NewTimer(func
, nSeconds
*1000)));
551 int RPCSerializationFlags()
554 if (gArgs
.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION
) == 0)
555 flag
|= SERIALIZE_TRANSACTION_NO_WITNESS
;