1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
11 #include "ui_interface.h"
17 #include <boost/algorithm/string.hpp>
18 #include <boost/asio.hpp>
19 #include <boost/asio/ssl.hpp>
20 #include <boost/bind.hpp>
21 #include <boost/filesystem.hpp>
22 #include <boost/foreach.hpp>
23 #include <boost/iostreams/concepts.hpp>
24 #include <boost/iostreams/stream.hpp>
25 #include <boost/shared_ptr.hpp>
26 #include "json/json_spirit_writer_template.h"
29 using namespace boost
;
30 using namespace boost::asio
;
31 using namespace json_spirit
;
33 static std::string strRPCUserColonPass
;
35 // These are created by StartRPCThreads, destroyed in StopRPCThreads
36 static asio::io_service
* rpc_io_service
= NULL
;
37 static map
<string
, boost::shared_ptr
<deadline_timer
> > deadlineTimers
;
38 static ssl::context
* rpc_ssl_context
= NULL
;
39 static boost::thread_group
* rpc_worker_group
= NULL
;
40 static boost::asio::io_service::work
*rpc_dummy_work
= NULL
;
42 void RPCTypeCheck(const Array
& params
,
43 const list
<Value_type
>& typesExpected
,
47 BOOST_FOREACH(Value_type t
, typesExpected
)
49 if (params
.size() <= i
)
52 const Value
& v
= params
[i
];
53 if (!((v
.type() == t
) || (fAllowNull
&& (v
.type() == null_type
))))
55 string err
= strprintf("Expected type %s, got %s",
56 Value_type_name
[t
], Value_type_name
[v
.type()]);
57 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
63 void RPCTypeCheck(const Object
& o
,
64 const map
<string
, Value_type
>& typesExpected
,
67 BOOST_FOREACH(const PAIRTYPE(string
, Value_type
)& t
, typesExpected
)
69 const Value
& v
= find_value(o
, t
.first
);
70 if (!fAllowNull
&& v
.type() == null_type
)
71 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Missing %s", t
.first
));
73 if (!((v
.type() == t
.second
) || (fAllowNull
&& (v
.type() == null_type
))))
75 string err
= strprintf("Expected type %s for %s, got %s",
76 Value_type_name
[t
.second
], t
.first
, Value_type_name
[v
.type()]);
77 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
82 int64_t AmountFromValue(const Value
& value
)
84 double dAmount
= value
.get_real();
85 if (dAmount
<= 0.0 || dAmount
> 21000000.0)
86 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
87 int64_t nAmount
= roundint64(dAmount
* COIN
);
88 if (!MoneyRange(nAmount
))
89 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
93 Value
ValueFromAmount(int64_t amount
)
95 return (double)amount
/ (double)COIN
;
98 std::string
HexBits(unsigned int nBits
)
104 uBits
.nBits
= htonl((int32_t)nBits
);
105 return HexStr(BEGIN(uBits
.cBits
), END(uBits
.cBits
));
108 uint256
ParseHashV(const Value
& v
, string strName
)
111 if (v
.type() == str_type
)
112 strHex
= v
.get_str();
113 if (!IsHex(strHex
)) // Note: IsHex("") is false
114 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
116 result
.SetHex(strHex
);
119 uint256
ParseHashO(const Object
& o
, string strKey
)
121 return ParseHashV(find_value(o
, strKey
), strKey
);
123 vector
<unsigned char> ParseHexV(const Value
& v
, string strName
)
126 if (v
.type() == str_type
)
127 strHex
= v
.get_str();
129 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
130 return ParseHex(strHex
);
132 vector
<unsigned char> ParseHexO(const Object
& o
, string strKey
)
134 return ParseHexV(find_value(o
, strKey
), strKey
);
139 /// Note: This interface may still be subject to change.
142 string
CRPCTable::help(string strCommand
) const
145 set
<rpcfn_type
> setDone
;
146 for (map
<string
, const CRPCCommand
*>::const_iterator mi
= mapCommands
.begin(); mi
!= mapCommands
.end(); ++mi
)
148 const CRPCCommand
*pcmd
= mi
->second
;
149 string strMethod
= mi
->first
;
150 // We already filter duplicates, but these deprecated screw up the sort order
151 if (strMethod
.find("label") != string::npos
)
153 if (strCommand
!= "" && strMethod
!= strCommand
)
156 if (pcmd
->reqWallet
&& !pwalletMain
)
163 rpcfn_type pfn
= pcmd
->actor
;
164 if (setDone
.insert(pfn
).second
)
165 (*pfn
)(params
, true);
167 catch (std::exception
& e
)
169 // Help text is returned in an exception
170 string strHelp
= string(e
.what());
171 if (strCommand
== "")
172 if (strHelp
.find('\n') != string::npos
)
173 strHelp
= strHelp
.substr(0, strHelp
.find('\n'));
174 strRet
+= strHelp
+ "\n";
178 strRet
= strprintf("help: unknown command: %s\n", strCommand
);
179 strRet
= strRet
.substr(0,strRet
.size()-1);
183 Value
help(const Array
& params
, bool fHelp
)
185 if (fHelp
|| params
.size() > 1)
187 "help ( \"command\" )\n"
188 "\nList all commands, or get help for a specified command.\n"
190 "1. \"command\" (string, optional) The command to get help on\n"
192 "\"text\" (string) The help text\n"
196 if (params
.size() > 0)
197 strCommand
= params
[0].get_str();
199 return tableRPC
.help(strCommand
);
203 Value
stop(const Array
& params
, bool fHelp
)
205 // Accept the deprecated and ignored 'detach' boolean argument
206 if (fHelp
|| params
.size() > 1)
209 "\nStop Bitcoin server.");
210 // Shutdown will take long enough that the response should get back
212 return "Bitcoin server stopping";
222 static const CRPCCommand vRPCCommands
[] =
223 { // name actor (function) okSafeMode threadSafe reqWallet
224 // ------------------------ ----------------------- ---------- ---------- ---------
225 /* Overall control/query calls */
226 { "getinfo", &getinfo
, true, false, false }, /* uses wallet if enabled */
227 { "help", &help
, true, true, false },
228 { "stop", &stop
, true, true, false },
231 { "addnode", &addnode
, true, true, false },
232 { "getaddednodeinfo", &getaddednodeinfo
, true, true, false },
233 { "getconnectioncount", &getconnectioncount
, true, false, false },
234 { "getnettotals", &getnettotals
, true, true, false },
235 { "getpeerinfo", &getpeerinfo
, true, false, false },
236 { "ping", &ping
, true, false, false },
238 /* Block chain and UTXO */
239 { "getbestblockhash", &getbestblockhash
, true, false, false },
240 { "getblockcount", &getblockcount
, true, false, false },
241 { "getblock", &getblock
, false, false, false },
242 { "getblockhash", &getblockhash
, false, false, false },
243 { "getdifficulty", &getdifficulty
, true, false, false },
244 { "getrawmempool", &getrawmempool
, true, false, false },
245 { "gettxout", &gettxout
, true, false, false },
246 { "gettxoutsetinfo", &gettxoutsetinfo
, true, false, false },
247 { "verifychain", &verifychain
, true, false, false },
250 { "getblocktemplate", &getblocktemplate
, true, false, false },
251 { "getmininginfo", &getmininginfo
, true, false, false },
252 { "getnetworkhashps", &getnetworkhashps
, true, false, false },
253 { "submitblock", &submitblock
, false, false, false },
255 /* Raw transactions */
256 { "createrawtransaction", &createrawtransaction
, false, false, false },
257 { "decoderawtransaction", &decoderawtransaction
, false, false, false },
258 { "decodescript", &decodescript
, false, false, false },
259 { "getrawtransaction", &getrawtransaction
, false, false, false },
260 { "sendrawtransaction", &sendrawtransaction
, false, false, false },
261 { "signrawtransaction", &signrawtransaction
, false, false, false }, /* uses wallet if enabled */
263 /* Utility functions */
264 { "createmultisig", &createmultisig
, true, true , false },
265 { "validateaddress", &validateaddress
, true, false, false }, /* uses wallet if enabled */
266 { "verifymessage", &verifymessage
, false, false, false },
270 { "addmultisigaddress", &addmultisigaddress
, false, false, true },
271 { "backupwallet", &backupwallet
, true, false, true },
272 { "dumpprivkey", &dumpprivkey
, true, false, true },
273 { "dumpwallet", &dumpwallet
, true, false, true },
274 { "encryptwallet", &encryptwallet
, false, false, true },
275 { "getaccountaddress", &getaccountaddress
, true, false, true },
276 { "getaccount", &getaccount
, false, false, true },
277 { "getaddressesbyaccount", &getaddressesbyaccount
, true, false, true },
278 { "getbalance", &getbalance
, false, false, true },
279 { "getnewaddress", &getnewaddress
, true, false, true },
280 { "getrawchangeaddress", &getrawchangeaddress
, true, false, true },
281 { "getreceivedbyaccount", &getreceivedbyaccount
, false, false, true },
282 { "getreceivedbyaddress", &getreceivedbyaddress
, false, false, true },
283 { "gettransaction", &gettransaction
, false, false, true },
284 { "getunconfirmedbalance", &getunconfirmedbalance
, false, false, true },
285 { "getwalletinfo", &getwalletinfo
, true, false, true },
286 { "importprivkey", &importprivkey
, false, false, true },
287 { "importwallet", &importwallet
, false, false, true },
288 { "keypoolrefill", &keypoolrefill
, true, false, true },
289 { "listaccounts", &listaccounts
, false, false, true },
290 { "listaddressgroupings", &listaddressgroupings
, false, false, true },
291 { "listlockunspent", &listlockunspent
, false, false, true },
292 { "listreceivedbyaccount", &listreceivedbyaccount
, false, false, true },
293 { "listreceivedbyaddress", &listreceivedbyaddress
, false, false, true },
294 { "listsinceblock", &listsinceblock
, false, false, true },
295 { "listtransactions", &listtransactions
, false, false, true },
296 { "listunspent", &listunspent
, false, false, true },
297 { "lockunspent", &lockunspent
, false, false, true },
298 { "move", &movecmd
, false, false, true },
299 { "sendfrom", &sendfrom
, false, false, true },
300 { "sendmany", &sendmany
, false, false, true },
301 { "sendtoaddress", &sendtoaddress
, false, false, true },
302 { "setaccount", &setaccount
, true, false, true },
303 { "settxfee", &settxfee
, false, false, true },
304 { "signmessage", &signmessage
, false, false, true },
305 { "walletlock", &walletlock
, true, false, true },
306 { "walletpassphrasechange", &walletpassphrasechange
, false, false, true },
307 { "walletpassphrase", &walletpassphrase
, true, false, true },
309 /* Wallet-enabled mining */
310 { "getgenerate", &getgenerate
, true, false, false },
311 { "gethashespersec", &gethashespersec
, true, false, false },
312 { "getwork", &getwork
, true, false, true },
313 { "setgenerate", &setgenerate
, true, true, false },
314 #endif // ENABLE_WALLET
317 CRPCTable::CRPCTable()
320 for (vcidx
= 0; vcidx
< (sizeof(vRPCCommands
) / sizeof(vRPCCommands
[0])); vcidx
++)
322 const CRPCCommand
*pcmd
;
324 pcmd
= &vRPCCommands
[vcidx
];
325 mapCommands
[pcmd
->name
] = pcmd
;
329 const CRPCCommand
*CRPCTable::operator[](string name
) const
331 map
<string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
332 if (it
== mapCommands
.end())
338 bool HTTPAuthorized(map
<string
, string
>& mapHeaders
)
340 string strAuth
= mapHeaders
["authorization"];
341 if (strAuth
.substr(0,6) != "Basic ")
343 string strUserPass64
= strAuth
.substr(6); boost::trim(strUserPass64
);
344 string strUserPass
= DecodeBase64(strUserPass64
);
345 return TimingResistantEqual(strUserPass
, strRPCUserColonPass
);
348 void ErrorReply(std::ostream
& stream
, const Object
& objError
, const Value
& id
)
350 // Send error reply from json-rpc error object
351 int nStatus
= HTTP_INTERNAL_SERVER_ERROR
;
352 int code
= find_value(objError
, "code").get_int();
353 if (code
== RPC_INVALID_REQUEST
) nStatus
= HTTP_BAD_REQUEST
;
354 else if (code
== RPC_METHOD_NOT_FOUND
) nStatus
= HTTP_NOT_FOUND
;
355 string strReply
= JSONRPCReply(Value::null
, objError
, id
);
356 stream
<< HTTPReply(nStatus
, strReply
, false) << std::flush
;
359 bool ClientAllowed(const boost::asio::ip::address
& address
)
361 // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
363 && (address
.to_v6().is_v4_compatible()
364 || address
.to_v6().is_v4_mapped()))
365 return ClientAllowed(address
.to_v6().to_v4());
367 if (address
== asio::ip::address_v4::loopback()
368 || address
== asio::ip::address_v6::loopback()
370 // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
371 && (address
.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
374 const string strAddress
= address
.to_string();
375 const vector
<string
>& vAllow
= mapMultiArgs
["-rpcallowip"];
376 BOOST_FOREACH(string strAllow
, vAllow
)
377 if (WildcardMatch(strAddress
, strAllow
))
382 class AcceptedConnection
385 virtual ~AcceptedConnection() {}
387 virtual std::iostream
& stream() = 0;
388 virtual std::string
peer_address_to_string() const = 0;
389 virtual void close() = 0;
392 template <typename Protocol
>
393 class AcceptedConnectionImpl
: public AcceptedConnection
396 AcceptedConnectionImpl(
397 asio::io_service
& io_service
,
398 ssl::context
&context
,
400 sslStream(io_service
, context
),
401 _d(sslStream
, fUseSSL
),
406 virtual std::iostream
& stream()
411 virtual std::string
peer_address_to_string() const
413 return peer
.address().to_string();
421 typename
Protocol::endpoint peer
;
422 asio::ssl::stream
<typename
Protocol::socket
> sslStream
;
425 SSLIOStreamDevice
<Protocol
> _d
;
426 iostreams::stream
< SSLIOStreamDevice
<Protocol
> > _stream
;
429 void ServiceConnection(AcceptedConnection
*conn
);
431 // Forward declaration required for RPCListen
432 template <typename Protocol
, typename SocketAcceptorService
>
433 static void RPCAcceptHandler(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
434 ssl::context
& context
,
436 AcceptedConnection
* conn
,
437 const boost::system::error_code
& error
);
440 * Sets up I/O resources to accept and handle a new connection.
442 template <typename Protocol
, typename SocketAcceptorService
>
443 static void RPCListen(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
444 ssl::context
& context
,
448 AcceptedConnectionImpl
<Protocol
>* conn
= new AcceptedConnectionImpl
<Protocol
>(acceptor
->get_io_service(), context
, fUseSSL
);
450 acceptor
->async_accept(
451 conn
->sslStream
.lowest_layer(),
453 boost::bind(&RPCAcceptHandler
<Protocol
, SocketAcceptorService
>,
458 boost::asio::placeholders::error
));
463 * Accept and handle incoming connection.
465 template <typename Protocol
, typename SocketAcceptorService
>
466 static void RPCAcceptHandler(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
467 ssl::context
& context
,
469 AcceptedConnection
* conn
,
470 const boost::system::error_code
& error
)
472 // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
473 if (error
!= asio::error::operation_aborted
&& acceptor
->is_open())
474 RPCListen(acceptor
, context
, fUseSSL
);
476 AcceptedConnectionImpl
<ip::tcp
>* tcp_conn
= dynamic_cast< AcceptedConnectionImpl
<ip::tcp
>* >(conn
);
478 // TODO: Actually handle errors
484 // Restrict callers by IP. It is important to
485 // do this before starting client thread, to filter out
486 // certain DoS and misbehaving clients.
487 else if (tcp_conn
&& !ClientAllowed(tcp_conn
->peer
.address()))
489 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
491 conn
->stream() << HTTPReply(HTTP_FORBIDDEN
, "", false) << std::flush
;
495 ServiceConnection(conn
);
501 void StartRPCThreads()
503 strRPCUserColonPass
= mapArgs
["-rpcuser"] + ":" + mapArgs
["-rpcpassword"];
504 if (((mapArgs
["-rpcpassword"] == "") ||
505 (mapArgs
["-rpcuser"] == mapArgs
["-rpcpassword"])) && Params().RequireRPCPassword())
507 unsigned char rand_pwd
[32];
508 RAND_bytes(rand_pwd
, 32);
509 string strWhatAmI
= "To use bitcoind";
510 if (mapArgs
.count("-server"))
511 strWhatAmI
= strprintf(_("To use the %s option"), "\"-server\"");
512 else if (mapArgs
.count("-daemon"))
513 strWhatAmI
= strprintf(_("To use the %s option"), "\"-daemon\"");
514 uiInterface
.ThreadSafeMessageBox(strprintf(
515 _("%s, you must set a rpcpassword in the configuration file:\n"
517 "It is recommended you use the following random password:\n"
518 "rpcuser=bitcoinrpc\n"
520 "(you do not need to remember this password)\n"
521 "The username and password MUST NOT be the same.\n"
522 "If the file does not exist, create it with owner-readable-only file permissions.\n"
523 "It is also recommended to set alertnotify so you are notified of problems;\n"
524 "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"),
526 GetConfigFile().string(),
527 EncodeBase58(&rand_pwd
[0],&rand_pwd
[0]+32)),
528 "", CClientUIInterface::MSG_ERROR
);
533 assert(rpc_io_service
== NULL
);
534 rpc_io_service
= new asio::io_service();
535 rpc_ssl_context
= new ssl::context(*rpc_io_service
, ssl::context::sslv23
);
537 const bool fUseSSL
= GetBoolArg("-rpcssl", false);
541 rpc_ssl_context
->set_options(ssl::context::no_sslv2
);
543 filesystem::path
pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
544 if (!pathCertFile
.is_complete()) pathCertFile
= filesystem::path(GetDataDir()) / pathCertFile
;
545 if (filesystem::exists(pathCertFile
)) rpc_ssl_context
->use_certificate_chain_file(pathCertFile
.string());
546 else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile
.string());
548 filesystem::path
pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
549 if (!pathPKFile
.is_complete()) pathPKFile
= filesystem::path(GetDataDir()) / pathPKFile
;
550 if (filesystem::exists(pathPKFile
)) rpc_ssl_context
->use_private_key_file(pathPKFile
.string(), ssl::context::pem
);
551 else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile
.string());
553 string strCiphers
= GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH");
554 SSL_CTX_set_cipher_list(rpc_ssl_context
->impl(), strCiphers
.c_str());
557 // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
558 const bool loopback
= !mapArgs
.count("-rpcallowip");
559 asio::ip::address bindAddress
= loopback
? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
560 ip::tcp::endpoint
endpoint(bindAddress
, GetArg("-rpcport", Params().RPCPort()));
561 boost::system::error_code v6_only_error
;
562 boost::shared_ptr
<ip::tcp::acceptor
> acceptor(new ip::tcp::acceptor(*rpc_io_service
));
564 bool fListening
= false;
568 acceptor
->open(endpoint
.protocol());
569 acceptor
->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
571 // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
572 acceptor
->set_option(boost::asio::ip::v6_only(loopback
), v6_only_error
);
574 acceptor
->bind(endpoint
);
575 acceptor
->listen(socket_base::max_connections
);
577 RPCListen(acceptor
, *rpc_ssl_context
, fUseSSL
);
581 catch(boost::system::system_error
&e
)
583 strerr
= strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint
.port(), e
.what());
587 // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
588 if (!fListening
|| loopback
|| v6_only_error
)
590 bindAddress
= loopback
? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
591 endpoint
.address(bindAddress
);
593 acceptor
.reset(new ip::tcp::acceptor(*rpc_io_service
));
594 acceptor
->open(endpoint
.protocol());
595 acceptor
->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
596 acceptor
->bind(endpoint
);
597 acceptor
->listen(socket_base::max_connections
);
599 RPCListen(acceptor
, *rpc_ssl_context
, fUseSSL
);
604 catch(boost::system::system_error
&e
)
606 strerr
= strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint
.port(), e
.what());
610 uiInterface
.ThreadSafeMessageBox(strerr
, "", CClientUIInterface::MSG_ERROR
);
615 rpc_worker_group
= new boost::thread_group();
616 for (int i
= 0; i
< GetArg("-rpcthreads", 4); i
++)
617 rpc_worker_group
->create_thread(boost::bind(&asio::io_service::run
, rpc_io_service
));
620 void StartDummyRPCThread()
622 if(rpc_io_service
== NULL
)
624 rpc_io_service
= new asio::io_service();
625 /* Create dummy "work" to keep the thread from exiting when no timeouts active,
626 * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */
627 rpc_dummy_work
= new asio::io_service::work(*rpc_io_service
);
628 rpc_worker_group
= new boost::thread_group();
629 rpc_worker_group
->create_thread(boost::bind(&asio::io_service::run
, rpc_io_service
));
633 void StopRPCThreads()
635 if (rpc_io_service
== NULL
) return;
637 deadlineTimers
.clear();
638 rpc_io_service
->stop();
639 if (rpc_worker_group
!= NULL
)
640 rpc_worker_group
->join_all();
641 delete rpc_dummy_work
; rpc_dummy_work
= NULL
;
642 delete rpc_worker_group
; rpc_worker_group
= NULL
;
643 delete rpc_ssl_context
; rpc_ssl_context
= NULL
;
644 delete rpc_io_service
; rpc_io_service
= NULL
;
647 void RPCRunHandler(const boost::system::error_code
& err
, boost::function
<void(void)> func
)
653 void RPCRunLater(const std::string
& name
, boost::function
<void(void)> func
, int64_t nSeconds
)
655 assert(rpc_io_service
!= NULL
);
657 if (deadlineTimers
.count(name
) == 0)
659 deadlineTimers
.insert(make_pair(name
,
660 boost::shared_ptr
<deadline_timer
>(new deadline_timer(*rpc_io_service
))));
662 deadlineTimers
[name
]->expires_from_now(posix_time::seconds(nSeconds
));
663 deadlineTimers
[name
]->async_wait(boost::bind(RPCRunHandler
, _1
, func
));
673 JSONRequest() { id
= Value::null
; }
674 void parse(const Value
& valRequest
);
677 void JSONRequest::parse(const Value
& valRequest
)
680 if (valRequest
.type() != obj_type
)
681 throw JSONRPCError(RPC_INVALID_REQUEST
, "Invalid Request object");
682 const Object
& request
= valRequest
.get_obj();
684 // Parse id now so errors from here on will have the id
685 id
= find_value(request
, "id");
688 Value valMethod
= find_value(request
, "method");
689 if (valMethod
.type() == null_type
)
690 throw JSONRPCError(RPC_INVALID_REQUEST
, "Missing method");
691 if (valMethod
.type() != str_type
)
692 throw JSONRPCError(RPC_INVALID_REQUEST
, "Method must be a string");
693 strMethod
= valMethod
.get_str();
694 if (strMethod
!= "getwork" && strMethod
!= "getblocktemplate")
695 LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod
);
698 Value valParams
= find_value(request
, "params");
699 if (valParams
.type() == array_type
)
700 params
= valParams
.get_array();
701 else if (valParams
.type() == null_type
)
704 throw JSONRPCError(RPC_INVALID_REQUEST
, "Params must be an array");
708 static Object
JSONRPCExecOne(const Value
& req
)
716 Value result
= tableRPC
.execute(jreq
.strMethod
, jreq
.params
);
717 rpc_result
= JSONRPCReplyObj(result
, Value::null
, jreq
.id
);
719 catch (Object
& objError
)
721 rpc_result
= JSONRPCReplyObj(Value::null
, objError
, jreq
.id
);
723 catch (std::exception
& e
)
725 rpc_result
= JSONRPCReplyObj(Value::null
,
726 JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
732 static string
JSONRPCExecBatch(const Array
& vReq
)
735 for (unsigned int reqIdx
= 0; reqIdx
< vReq
.size(); reqIdx
++)
736 ret
.push_back(JSONRPCExecOne(vReq
[reqIdx
]));
738 return write_string(Value(ret
), false) + "\n";
741 void ServiceConnection(AcceptedConnection
*conn
)
744 while (fRun
&& !ShutdownRequested())
747 map
<string
, string
> mapHeaders
;
748 string strRequest
, strMethod
, strURI
;
750 // Read HTTP request line
751 if (!ReadHTTPRequestLine(conn
->stream(), nProto
, strMethod
, strURI
))
754 // Read HTTP message headers and body
755 ReadHTTPMessage(conn
->stream(), mapHeaders
, strRequest
, nProto
);
758 conn
->stream() << HTTPReply(HTTP_NOT_FOUND
, "", false) << std::flush
;
762 // Check authorization
763 if (mapHeaders
.count("authorization") == 0)
765 conn
->stream() << HTTPReply(HTTP_UNAUTHORIZED
, "", false) << std::flush
;
768 if (!HTTPAuthorized(mapHeaders
))
770 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn
->peer_address_to_string());
771 /* Deter brute-forcing short passwords.
772 If this results in a DoS the user really
773 shouldn't have their RPC port exposed. */
774 if (mapArgs
["-rpcpassword"].size() < 20)
777 conn
->stream() << HTTPReply(HTTP_UNAUTHORIZED
, "", false) << std::flush
;
780 if (mapHeaders
["connection"] == "close")
788 if (!read_string(strRequest
, valRequest
))
789 throw JSONRPCError(RPC_PARSE_ERROR
, "Parse error");
794 if (valRequest
.type() == obj_type
) {
795 jreq
.parse(valRequest
);
797 Value result
= tableRPC
.execute(jreq
.strMethod
, jreq
.params
);
800 strReply
= JSONRPCReply(result
, Value::null
, jreq
.id
);
803 } else if (valRequest
.type() == array_type
)
804 strReply
= JSONRPCExecBatch(valRequest
.get_array());
806 throw JSONRPCError(RPC_PARSE_ERROR
, "Top-level object parse error");
808 conn
->stream() << HTTPReply(HTTP_OK
, strReply
, fRun
) << std::flush
;
810 catch (Object
& objError
)
812 ErrorReply(conn
->stream(), objError
, jreq
.id
);
815 catch (std::exception
& e
)
817 ErrorReply(conn
->stream(), JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
823 json_spirit::Value
CRPCTable::execute(const std::string
&strMethod
, const json_spirit::Array
¶ms
) const
826 const CRPCCommand
*pcmd
= tableRPC
[strMethod
];
828 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found");
830 if (pcmd
->reqWallet
&& !pwalletMain
)
831 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found (disabled)");
835 string strWarning
= GetWarnings("rpc");
836 if (strWarning
!= "" && !GetBoolArg("-disablesafemode", false) &&
838 throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE
, string("Safe mode: ") + strWarning
);
845 if (pcmd
->threadSafe
)
846 result
= pcmd
->actor(params
, false);
848 else if (!pwalletMain
) {
850 result
= pcmd
->actor(params
, false);
852 LOCK2(cs_main
, pwalletMain
->cs_wallet
);
853 result
= pcmd
->actor(params
, false);
855 #else // ENABLE_WALLET
858 result
= pcmd
->actor(params
, false);
860 #endif // !ENABLE_WALLET
864 catch (std::exception
& e
)
866 throw JSONRPCError(RPC_MISC_ERROR
, e
.what());
870 std::string
HelpExampleCli(string methodname
, string args
){
871 return "> bitcoin-cli " + methodname
+ " " + args
+ "\n";
874 std::string
HelpExampleRpc(string methodname
, string args
){
875 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
876 "\"method\": \"" + methodname
+ "\", \"params\": [" + args
+ "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
879 const CRPCTable tableRPC
;