]> Git Repo - VerusCoin.git/blame - src/bitcoinrpc.cpp
Merge pull request #1693 from jgarzik/rpcwallet
[VerusCoin.git] / src / bitcoinrpc.cpp
CommitLineData
69d605f4 1// Copyright (c) 2010 Satoshi Nakamoto
88216419 2// Copyright (c) 2009-2012 The Bitcoin developers
69d605f4 3// Distributed under the MIT/X11 software license, see the accompanying
3a25a2b9 4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
69d605f4 5
0eeb4f5d 6#include "init.h"
e3bc5698
JG
7#include "util.h"
8#include "sync.h"
ed6d0b5f 9#include "ui_interface.h"
10254401 10#include "base58.h"
e46704dd 11#include "bitcoinrpc.h"
ed6d0b5f 12
69d605f4
WL
13#undef printf
14#include <boost/asio.hpp>
c1ecab81 15#include <boost/asio/ip/v6_only.hpp>
914dc012 16#include <boost/bind.hpp>
95d888a6 17#include <boost/filesystem.hpp>
a0780ba0 18#include <boost/foreach.hpp>
69d605f4
WL
19#include <boost/iostreams/concepts.hpp>
20#include <boost/iostreams/stream.hpp>
0eeb4f5d 21#include <boost/algorithm/string.hpp>
30ab2c9c 22#include <boost/lexical_cast.hpp>
9247134e 23#include <boost/asio/ssl.hpp>
0eeb4f5d 24#include <boost/filesystem/fstream.hpp>
914dc012 25#include <boost/shared_ptr.hpp>
a0780ba0 26#include <list>
5ce4c2a2 27
69d605f4 28#define printf OutputDebugStringF
69d605f4
WL
29
30using namespace std;
31using namespace boost;
32using namespace boost::asio;
33using namespace json_spirit;
34
35void ThreadRPCServer2(void* parg);
69d605f4 36
f81ce5bd
GA
37static std::string strRPCUserColonPass;
38
74335bd3
LD
39const Object emptyobj;
40
e9205293
DJS
41void ThreadRPCServer3(void* parg);
42
69d605f4
WL
43Object JSONRPCError(int code, const string& message)
44{
45 Object error;
46 error.push_back(Pair("code", code));
47 error.push_back(Pair("message", message));
48 return error;
49}
50
899d373b 51void RPCTypeCheck(const Array& params,
cc6dfd1f
GA
52 const list<Value_type>& typesExpected,
53 bool fAllowNull)
899d373b 54{
dab9fa7f 55 unsigned int i = 0;
899d373b
GA
56 BOOST_FOREACH(Value_type t, typesExpected)
57 {
58 if (params.size() <= i)
59 break;
60
cc6dfd1f
GA
61 const Value& v = params[i];
62 if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
899d373b
GA
63 {
64 string err = strprintf("Expected type %s, got %s",
65 Value_type_name[t], Value_type_name[v.type()]);
66 throw JSONRPCError(-3, err);
67 }
68 i++;
69 }
70}
71
72void RPCTypeCheck(const Object& o,
cc6dfd1f
GA
73 const map<string, Value_type>& typesExpected,
74 bool fAllowNull)
899d373b
GA
75{
76 BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
77 {
78 const Value& v = find_value(o, t.first);
cc6dfd1f 79 if (!fAllowNull && v.type() == null_type)
899d373b 80 throw JSONRPCError(-3, strprintf("Missing %s", t.first.c_str()));
cc6dfd1f
GA
81
82 if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
899d373b
GA
83 {
84 string err = strprintf("Expected type %s for %s, got %s",
85 Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
86 throw JSONRPCError(-3, err);
87 }
88 }
89}
90
bde280b9 91int64 AmountFromValue(const Value& value)
69d605f4
WL
92{
93 double dAmount = value.get_real();
94 if (dAmount <= 0.0 || dAmount > 21000000.0)
95 throw JSONRPCError(-3, "Invalid amount");
bde280b9 96 int64 nAmount = roundint64(dAmount * COIN);
69d605f4
WL
97 if (!MoneyRange(nAmount))
98 throw JSONRPCError(-3, "Invalid amount");
99 return nAmount;
100}
101
bde280b9 102Value ValueFromAmount(int64 amount)
69d605f4
WL
103{
104 return (double)amount / (double)COIN;
105}
106
34f87889
LD
107std::string
108HexBits(unsigned int nBits)
109{
110 union {
111 int32_t nBits;
112 char cBits[4];
113 } uBits;
114 uBits.nBits = htonl((int32_t)nBits);
115 return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
116}
117
69d605f4 118
74335bd3 119
69d605f4
WL
120///
121/// Note: This interface may still be subject to change.
122///
123
9862229d 124string CRPCTable::help(string strCommand) const
69d605f4 125{
69d605f4
WL
126 string strRet;
127 set<rpcfn_type> setDone;
9862229d 128 for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
69d605f4 129 {
9862229d 130 const CRPCCommand *pcmd = mi->second;
dc42bf52 131 string strMethod = mi->first;
69d605f4 132 // We already filter duplicates, but these deprecated screw up the sort order
47f48a65 133 if (strMethod.find("label") != string::npos)
69d605f4
WL
134 continue;
135 if (strCommand != "" && strMethod != strCommand)
136 continue;
137 try
138 {
139 Array params;
dc42bf52 140 rpcfn_type pfn = pcmd->actor;
69d605f4
WL
141 if (setDone.insert(pfn).second)
142 (*pfn)(params, true);
143 }
144 catch (std::exception& e)
145 {
146 // Help text is returned in an exception
147 string strHelp = string(e.what());
148 if (strCommand == "")
ab9dc75a 149 if (strHelp.find('\n') != string::npos)
69d605f4
WL
150 strHelp = strHelp.substr(0, strHelp.find('\n'));
151 strRet += strHelp + "\n";
152 }
153 }
154 if (strRet == "")
155 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
156 strRet = strRet.substr(0,strRet.size()-1);
157 return strRet;
158}
159
9862229d
PW
160Value help(const Array& params, bool fHelp)
161{
162 if (fHelp || params.size() > 1)
163 throw runtime_error(
164 "help [command]\n"
165 "List commands, or get help for a command.");
166
167 string strCommand;
168 if (params.size() > 0)
169 strCommand = params[0].get_str();
170
171 return tableRPC.help(strCommand);
172}
173
69d605f4
WL
174
175Value stop(const Array& params, bool fHelp)
176{
177 if (fHelp || params.size() != 0)
178 throw runtime_error(
179 "stop\n"
ff0ee876 180 "Stop Bitcoin server.");
69d605f4 181 // Shutdown will take long enough that the response should get back
9247134e 182 StartShutdown();
ff0ee876 183 return "Bitcoin server stopping";
69d605f4
WL
184}
185
186
69d605f4
WL
187
188//
189// Call Table
190//
191
dc42bf52 192
e46704dd 193static const CRPCCommand vRPCCommands[] =
dc42bf52
JG
194{ // name function safe mode?
195 // ------------------------ ----------------------- ----------
196 { "help", &help, true },
197 { "stop", &stop, true },
198 { "getblockcount", &getblockcount, true },
dc42bf52 199 { "getconnectioncount", &getconnectioncount, true },
1006f070 200 { "getpeerinfo", &getpeerinfo, true },
dc42bf52
JG
201 { "getdifficulty", &getdifficulty, true },
202 { "getgenerate", &getgenerate, true },
203 { "setgenerate", &setgenerate, true },
204 { "gethashespersec", &gethashespersec, true },
205 { "getinfo", &getinfo, true },
206 { "getmininginfo", &getmininginfo, true },
207 { "getnewaddress", &getnewaddress, true },
208 { "getaccountaddress", &getaccountaddress, true },
209 { "setaccount", &setaccount, true },
210 { "getaccount", &getaccount, false },
211 { "getaddressesbyaccount", &getaddressesbyaccount, true },
212 { "sendtoaddress", &sendtoaddress, false },
213 { "getreceivedbyaddress", &getreceivedbyaddress, false },
214 { "getreceivedbyaccount", &getreceivedbyaccount, false },
215 { "listreceivedbyaddress", &listreceivedbyaddress, false },
216 { "listreceivedbyaccount", &listreceivedbyaccount, false },
217 { "backupwallet", &backupwallet, true },
218 { "keypoolrefill", &keypoolrefill, true },
219 { "walletpassphrase", &walletpassphrase, true },
220 { "walletpassphrasechange", &walletpassphrasechange, false },
221 { "walletlock", &walletlock, true },
222 { "encryptwallet", &encryptwallet, false },
223 { "validateaddress", &validateaddress, true },
224 { "getbalance", &getbalance, false },
225 { "move", &movecmd, false },
226 { "sendfrom", &sendfrom, false },
227 { "sendmany", &sendmany, false },
228 { "addmultisigaddress", &addmultisigaddress, false },
25d5c195 229 { "getrawmempool", &getrawmempool, true },
dc42bf52
JG
230 { "getblock", &getblock, false },
231 { "getblockhash", &getblockhash, false },
232 { "gettransaction", &gettransaction, false },
233 { "listtransactions", &listtransactions, false },
234 { "signmessage", &signmessage, false },
235 { "verifymessage", &verifymessage, false },
236 { "getwork", &getwork, true },
237 { "listaccounts", &listaccounts, false },
238 { "settxfee", &settxfee, false },
44427fa8 239 { "getblocktemplate", &getblocktemplate, true },
79f2525a 240 { "submitblock", &submitblock, false },
dc42bf52
JG
241 { "listsinceblock", &listsinceblock, false },
242 { "dumpprivkey", &dumpprivkey, false },
243 { "importprivkey", &importprivkey, false },
a2709fad
GA
244 { "listunspent", &listunspent, false },
245 { "getrawtransaction", &getrawtransaction, false },
246 { "createrawtransaction", &createrawtransaction, false },
247 { "decoderawtransaction", &decoderawtransaction, false },
248 { "signrawtransaction", &signrawtransaction, false },
249 { "sendrawtransaction", &sendrawtransaction, false },
69d605f4 250};
69d605f4 251
9862229d 252CRPCTable::CRPCTable()
dc42bf52 253{
dc42bf52
JG
254 unsigned int vcidx;
255 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
256 {
e46704dd 257 const CRPCCommand *pcmd;
69d605f4 258
dc42bf52
JG
259 pcmd = &vRPCCommands[vcidx];
260 mapCommands[pcmd->name] = pcmd;
261 }
262}
69d605f4 263
9862229d
PW
264const CRPCCommand *CRPCTable::operator[](string name) const
265{
266 map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
267 if (it == mapCommands.end())
268 return NULL;
269 return (*it).second;
270}
69d605f4
WL
271
272//
273// HTTP protocol
274//
275// This ain't Apache. We're just using HTTP header for the length field
276// and to be compatible with other JSON-RPC implementations.
277//
278
279string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
280{
281 ostringstream s;
282 s << "POST / HTTP/1.1\r\n"
283 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
284 << "Host: 127.0.0.1\r\n"
285 << "Content-Type: application/json\r\n"
286 << "Content-Length: " << strMsg.size() << "\r\n"
3552497a 287 << "Connection: close\r\n"
69d605f4
WL
288 << "Accept: application/json\r\n";
289 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
290 s << item.first << ": " << item.second << "\r\n";
291 s << "\r\n" << strMsg;
292
293 return s.str();
294}
295
296string rfc1123Time()
297{
298 char buffer[64];
299 time_t now;
300 time(&now);
301 struct tm* now_gmt = gmtime(&now);
302 string locale(setlocale(LC_TIME, NULL));
814efd6f 303 setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
69d605f4
WL
304 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
305 setlocale(LC_TIME, locale.c_str());
306 return string(buffer);
307}
308
96c52695 309static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
69d605f4
WL
310{
311 if (nStatus == 401)
312 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
313 "Date: %s\r\n"
314 "Server: bitcoin-json-rpc/%s\r\n"
315 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
316 "Content-Type: text/html\r\n"
317 "Content-Length: 296\r\n"
318 "\r\n"
319 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
320 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
321 "<HTML>\r\n"
322 "<HEAD>\r\n"
323 "<TITLE>Error</TITLE>\r\n"
324 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
325 "</HEAD>\r\n"
326 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
327 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
ae81b82f
DJS
328 const char *cStatus;
329 if (nStatus == 200) cStatus = "OK";
330 else if (nStatus == 400) cStatus = "Bad Request";
331 else if (nStatus == 403) cStatus = "Forbidden";
332 else if (nStatus == 404) cStatus = "Not Found";
333 else if (nStatus == 500) cStatus = "Internal Server Error";
334 else cStatus = "";
69d605f4
WL
335 return strprintf(
336 "HTTP/1.1 %d %s\r\n"
337 "Date: %s\r\n"
96c52695 338 "Connection: %s\r\n"
69d605f4
WL
339 "Content-Length: %d\r\n"
340 "Content-Type: application/json\r\n"
341 "Server: bitcoin-json-rpc/%s\r\n"
342 "\r\n"
343 "%s",
344 nStatus,
ae81b82f 345 cStatus,
69d605f4 346 rfc1123Time().c_str(),
96c52695 347 keepalive ? "keep-alive" : "close",
69d605f4
WL
348 strMsg.size(),
349 FormatFullVersion().c_str(),
350 strMsg.c_str());
351}
352
96c52695 353int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
69d605f4
WL
354{
355 string str;
356 getline(stream, str);
357 vector<string> vWords;
358 boost::split(vWords, str, boost::is_any_of(" "));
359 if (vWords.size() < 2)
360 return 500;
96c52695
DJS
361 proto = 0;
362 const char *ver = strstr(str.c_str(), "HTTP/1.");
363 if (ver != NULL)
364 proto = atoi(ver+7);
69d605f4
WL
365 return atoi(vWords[1].c_str());
366}
367
368int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
369{
370 int nLen = 0;
371 loop
372 {
373 string str;
374 std::getline(stream, str);
375 if (str.empty() || str == "\r")
376 break;
377 string::size_type nColon = str.find(":");
378 if (nColon != string::npos)
379 {
380 string strHeader = str.substr(0, nColon);
381 boost::trim(strHeader);
382 boost::to_lower(strHeader);
383 string strValue = str.substr(nColon+1);
384 boost::trim(strValue);
385 mapHeadersRet[strHeader] = strValue;
386 if (strHeader == "content-length")
387 nLen = atoi(strValue.c_str());
388 }
389 }
390 return nLen;
391}
392
393int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
394{
395 mapHeadersRet.clear();
396 strMessageRet = "";
397
398 // Read status
22db3f2c 399 int nProto = 0;
96c52695 400 int nStatus = ReadHTTPStatus(stream, nProto);
69d605f4
WL
401
402 // Read header
403 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
1d8c7a95 404 if (nLen < 0 || nLen > (int)MAX_SIZE)
69d605f4
WL
405 return 500;
406
407 // Read message
408 if (nLen > 0)
409 {
410 vector<char> vch(nLen);
411 stream.read(&vch[0], nLen);
412 strMessageRet = string(vch.begin(), vch.end());
413 }
414
96c52695
DJS
415 string sConHdr = mapHeadersRet["connection"];
416
417 if ((sConHdr != "close") && (sConHdr != "keep-alive"))
418 {
419 if (nProto >= 1)
420 mapHeadersRet["connection"] = "keep-alive";
421 else
422 mapHeadersRet["connection"] = "close";
423 }
424
69d605f4
WL
425 return nStatus;
426}
427
69d605f4
WL
428bool HTTPAuthorized(map<string, string>& mapHeaders)
429{
430 string strAuth = mapHeaders["authorization"];
431 if (strAuth.substr(0,6) != "Basic ")
432 return false;
433 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
434 string strUserPass = DecodeBase64(strUserPass64);
f81ce5bd 435 return strUserPass == strRPCUserColonPass;
69d605f4
WL
436}
437
438//
439// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
440// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
441// unspecified (HTTP errors and contents of 'error').
442//
443// 1.0 spec: http://json-rpc.org/wiki/specification
444// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
445// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
446//
447
448string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
449{
450 Object request;
451 request.push_back(Pair("method", strMethod));
452 request.push_back(Pair("params", params));
453 request.push_back(Pair("id", id));
454 return write_string(Value(request), false) + "\n";
455}
456
c6494d82 457Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
69d605f4
WL
458{
459 Object reply;
460 if (error.type() != null_type)
461 reply.push_back(Pair("result", Value::null));
462 else
463 reply.push_back(Pair("result", result));
464 reply.push_back(Pair("error", error));
465 reply.push_back(Pair("id", id));
c6494d82
JG
466 return reply;
467}
468
469string JSONRPCReply(const Value& result, const Value& error, const Value& id)
470{
471 Object reply = JSONRPCReplyObj(result, error, id);
69d605f4
WL
472 return write_string(Value(reply), false) + "\n";
473}
474
475void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
476{
477 // Send error reply from json-rpc error object
478 int nStatus = 500;
479 int code = find_value(objError, "code").get_int();
480 if (code == -32600) nStatus = 400;
481 else if (code == -32601) nStatus = 404;
482 string strReply = JSONRPCReply(Value::null, objError, id);
96c52695 483 stream << HTTPReply(nStatus, strReply, false) << std::flush;
69d605f4
WL
484}
485
43b6dafa 486bool ClientAllowed(const boost::asio::ip::address& address)
69d605f4 487{
43b6dafa
GS
488 // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
489 if (address.is_v6()
490 && (address.to_v6().is_v4_compatible()
491 || address.to_v6().is_v4_mapped()))
492 return ClientAllowed(address.to_v6().to_v4());
493
494 if (address == asio::ip::address_v4::loopback()
7cc2ceae
GS
495 || address == asio::ip::address_v6::loopback()
496 || (address.is_v4()
814efd6f 497 // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
7cc2ceae 498 && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
69d605f4 499 return true;
43b6dafa
GS
500
501 const string strAddress = address.to_string();
69d605f4
WL
502 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
503 BOOST_FOREACH(string strAllow, vAllow)
504 if (WildcardMatch(strAddress, strAllow))
505 return true;
506 return false;
507}
508
69d605f4
WL
509//
510// IOStream device that speaks SSL but can also speak non-SSL
511//
a0780ba0 512template <typename Protocol>
69d605f4
WL
513class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
514public:
a0780ba0 515 SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
69d605f4
WL
516 {
517 fUseSSL = fUseSSLIn;
518 fNeedHandshake = fUseSSLIn;
519 }
520
521 void handshake(ssl::stream_base::handshake_type role)
522 {
523 if (!fNeedHandshake) return;
524 fNeedHandshake = false;
525 stream.handshake(role);
526 }
527 std::streamsize read(char* s, std::streamsize n)
528 {
529 handshake(ssl::stream_base::server); // HTTPS servers read first
530 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
531 return stream.next_layer().read_some(asio::buffer(s, n));
532 }
533 std::streamsize write(const char* s, std::streamsize n)
534 {
535 handshake(ssl::stream_base::client); // HTTPS clients write first
536 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
537 return asio::write(stream.next_layer(), asio::buffer(s, n));
538 }
539 bool connect(const std::string& server, const std::string& port)
540 {
541 ip::tcp::resolver resolver(stream.get_io_service());
542 ip::tcp::resolver::query query(server.c_str(), port.c_str());
543 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
544 ip::tcp::resolver::iterator end;
545 boost::system::error_code error = asio::error::host_not_found;
546 while (error && endpoint_iterator != end)
547 {
548 stream.lowest_layer().close();
549 stream.lowest_layer().connect(*endpoint_iterator++, error);
550 }
551 if (error)
552 return false;
553 return true;
554 }
555
556private:
557 bool fNeedHandshake;
558 bool fUseSSL;
a0780ba0 559 asio::ssl::stream<typename Protocol::socket>& stream;
69d605f4 560};
69d605f4 561
e9205293
DJS
562class AcceptedConnection
563{
a0780ba0
GS
564public:
565 virtual ~AcceptedConnection() {}
566
567 virtual std::iostream& stream() = 0;
568 virtual std::string peer_address_to_string() const = 0;
569 virtual void close() = 0;
570};
e9205293 571
a0780ba0
GS
572template <typename Protocol>
573class AcceptedConnectionImpl : public AcceptedConnection
574{
575public:
576 AcceptedConnectionImpl(
577 asio::io_service& io_service,
578 ssl::context &context,
579 bool fUseSSL) :
580 sslStream(io_service, context),
581 _d(sslStream, fUseSSL),
582 _stream(_d)
583 {
584 }
585
586 virtual std::iostream& stream()
587 {
588 return _stream;
589 }
590
591 virtual std::string peer_address_to_string() const
592 {
593 return peer.address().to_string();
594 }
e9205293 595
a0780ba0
GS
596 virtual void close()
597 {
598 _stream.close();
599 }
600
601 typename Protocol::endpoint peer;
602 asio::ssl::stream<typename Protocol::socket> sslStream;
603
604private:
605 SSLIOStreamDevice<Protocol> _d;
606 iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
e9205293
DJS
607};
608
69d605f4
WL
609void ThreadRPCServer(void* parg)
610{
611 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
96931d6f
GS
612
613 // Make this thread recognisable as the RPC listener
614 RenameThread("bitcoin-rpclist");
615
69d605f4
WL
616 try
617 {
e9205293 618 vnThreadsRunning[THREAD_RPCLISTENER]++;
69d605f4 619 ThreadRPCServer2(parg);
e9205293 620 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
621 }
622 catch (std::exception& e) {
e9205293 623 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
624 PrintException(&e, "ThreadRPCServer()");
625 } catch (...) {
e9205293 626 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
627 PrintException(NULL, "ThreadRPCServer()");
628 }
1d764d63 629 printf("ThreadRPCServer exited\n");
69d605f4
WL
630}
631
914dc012 632// Forward declaration required for RPCListen
a0780ba0
GS
633template <typename Protocol, typename SocketAcceptorService>
634static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
635 ssl::context& context,
636 bool fUseSSL,
637 AcceptedConnection* conn,
638 const boost::system::error_code& error);
639
640/**
641 * Sets up I/O resources to accept and handle a new connection.
642 */
a0780ba0
GS
643template <typename Protocol, typename SocketAcceptorService>
644static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
645 ssl::context& context,
646 const bool fUseSSL)
647{
914dc012 648 // Accept connection
a0780ba0 649 AcceptedConnectionImpl<Protocol>* conn = new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL);
914dc012
GS
650
651 acceptor->async_accept(
652 conn->sslStream.lowest_layer(),
653 conn->peer,
a0780ba0 654 boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
914dc012
GS
655 acceptor,
656 boost::ref(context),
657 fUseSSL,
658 conn,
659 boost::asio::placeholders::error));
660}
661
662/**
663 * Accept and handle incoming connection.
664 */
a0780ba0
GS
665template <typename Protocol, typename SocketAcceptorService>
666static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
667 ssl::context& context,
668 const bool fUseSSL,
669 AcceptedConnection* conn,
670 const boost::system::error_code& error)
671{
672 vnThreadsRunning[THREAD_RPCLISTENER]++;
673
814efd6f 674 // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
460d8786 675 if (error != asio::error::operation_aborted
ad25804f
GS
676 && acceptor->is_open())
677 RPCListen(acceptor, context, fUseSSL);
914dc012 678
a0780ba0
GS
679 AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
680
914dc012
GS
681 // TODO: Actually handle errors
682 if (error)
683 {
684 delete conn;
685 }
686
687 // Restrict callers by IP. It is important to
688 // do this before starting client thread, to filter out
689 // certain DoS and misbehaving clients.
a0780ba0
GS
690 else if (tcp_conn
691 && !ClientAllowed(tcp_conn->peer.address()))
914dc012
GS
692 {
693 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
694 if (!fUseSSL)
a0780ba0 695 conn->stream() << HTTPReply(403, "", false) << std::flush;
914dc012
GS
696 delete conn;
697 }
698
699 // start HTTP client thread
700 else if (!CreateThread(ThreadRPCServer3, conn)) {
701 printf("Failed to create RPC server client thread\n");
702 delete conn;
703 }
704
705 vnThreadsRunning[THREAD_RPCLISTENER]--;
706}
707
69d605f4
WL
708void ThreadRPCServer2(void* parg)
709{
710 printf("ThreadRPCServer started\n");
711
f81ce5bd 712 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
b04f301c 713 if (mapArgs["-rpcpassword"] == "")
69d605f4 714 {
b04f301c
GM
715 unsigned char rand_pwd[32];
716 RAND_bytes(rand_pwd, 32);
69d605f4
WL
717 string strWhatAmI = "To use bitcoind";
718 if (mapArgs.count("-server"))
719 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
720 else if (mapArgs.count("-daemon"))
721 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
ab1b288f 722 uiInterface.ThreadSafeMessageBox(strprintf(
5a60b66a
WL
723 _("%s, you must set a rpcpassword in the configuration file:\n %s\n"
724 "It is recommended you use the following random password:\n"
725 "rpcuser=bitcoinrpc\n"
726 "rpcpassword=%s\n"
727 "(you do not need to remember this password)\n"
728 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
69d605f4 729 strWhatAmI.c_str(),
ee12c3d6 730 GetConfigFile().string().c_str(),
5a60b66a 731 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
239c11d0 732 _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
9247134e 733 StartShutdown();
69d605f4
WL
734 return;
735 }
736
914dc012 737 const bool fUseSSL = GetBoolArg("-rpcssl");
69d605f4
WL
738
739 asio::io_service io_service;
fbf9df2e 740
69d605f4
WL
741 ssl::context context(io_service, ssl::context::sslv23);
742 if (fUseSSL)
743 {
744 context.set_options(ssl::context::no_sslv2);
93fb7489
PK
745
746 filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
747 if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
ee12c3d6 748 if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
93fb7489
PK
749 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
750
751 filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
752 if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
ee12c3d6 753 if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
93fb7489
PK
754 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
755
756 string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
757 SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
69d605f4 758 }
69d605f4 759
c1ecab81
GS
760 // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
761 const bool loopback = !mapArgs.count("-rpcallowip");
762 asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
763 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
764
7cf3d2cc
MC
765 boost::signals2::signal<void ()> StopRequests;
766
c1ecab81
GS
767 try
768 {
ad25804f
GS
769 boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service));
770 acceptor->open(endpoint.protocol());
771 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
c1ecab81
GS
772
773 // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
774 boost::system::error_code v6_only_error;
ad25804f 775 acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
c1ecab81 776
ad25804f
GS
777 acceptor->bind(endpoint);
778 acceptor->listen(socket_base::max_connections);
c1ecab81 779
ad25804f
GS
780 RPCListen(acceptor, context, fUseSSL);
781 // Cancel outstanding listen-requests for this acceptor when shutting down
7cf3d2cc 782 StopRequests.connect(signals2::slot<void ()>(
ad25804f
GS
783 static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
784 .track(acceptor));
c1ecab81
GS
785
786 // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
787 if (loopback || v6_only_error)
788 {
789 bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
790 endpoint.address(bindAddress);
791
ad25804f
GS
792 acceptor.reset(new ip::tcp::acceptor(io_service));
793 acceptor->open(endpoint.protocol());
794 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
795 acceptor->bind(endpoint);
796 acceptor->listen(socket_base::max_connections);
797
798 RPCListen(acceptor, context, fUseSSL);
799 // Cancel outstanding listen-requests for this acceptor when shutting down
7cf3d2cc 800 StopRequests.connect(signals2::slot<void ()>(
ad25804f
GS
801 static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
802 .track(acceptor));
c1ecab81
GS
803 }
804 }
805 catch(boost::system::system_error &e)
806 {
814efd6f 807 uiInterface.ThreadSafeMessageBox(strprintf(_("An error occurred while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
c1ecab81 808 _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
07368a9e 809 StartShutdown();
c1ecab81
GS
810 return;
811 }
69d605f4 812
914dc012 813 vnThreadsRunning[THREAD_RPCLISTENER]--;
18c4beb0
MC
814 while (!fShutdown)
815 io_service.run_one();
914dc012 816 vnThreadsRunning[THREAD_RPCLISTENER]++;
7cf3d2cc 817 StopRequests();
e9205293
DJS
818}
819
c6494d82
JG
820class JSONRequest
821{
822public:
823 Value id;
824 string strMethod;
825 Array params;
826
827 JSONRequest() { id = Value::null; }
828 void parse(const Value& valRequest);
829};
830
831void JSONRequest::parse(const Value& valRequest)
832{
833 // Parse request
834 if (valRequest.type() != obj_type)
835 throw JSONRPCError(-32600, "Invalid Request object");
836 const Object& request = valRequest.get_obj();
837
838 // Parse id now so errors from here on will have the id
839 id = find_value(request, "id");
840
841 // Parse method
842 Value valMethod = find_value(request, "method");
843 if (valMethod.type() == null_type)
844 throw JSONRPCError(-32600, "Missing method");
845 if (valMethod.type() != str_type)
846 throw JSONRPCError(-32600, "Method must be a string");
847 strMethod = valMethod.get_str();
44427fa8 848 if (strMethod != "getwork" && strMethod != "getblocktemplate")
c6494d82
JG
849 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
850
851 // Parse params
852 Value valParams = find_value(request, "params");
853 if (valParams.type() == array_type)
854 params = valParams.get_array();
855 else if (valParams.type() == null_type)
856 params = Array();
857 else
858 throw JSONRPCError(-32600, "Params must be an array");
859}
860
61338901
JG
861static Object JSONRPCExecOne(const Value& req)
862{
863 Object rpc_result;
864
865 JSONRequest jreq;
866 try {
867 jreq.parse(req);
868
869 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
870 rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
871 }
872 catch (Object& objError)
873 {
874 rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
875 }
876 catch (std::exception& e)
877 {
878 rpc_result = JSONRPCReplyObj(Value::null,
879 JSONRPCError(-32700, e.what()), jreq.id);
880 }
881
882 return rpc_result;
883}
884
885static string JSONRPCExecBatch(const Array& vReq)
886{
887 Array ret;
888 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
889 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
890
891 return write_string(Value(ret), false) + "\n";
892}
893
4e97a9d9
MC
894static CCriticalSection cs_THREAD_RPCHANDLER;
895
e9205293
DJS
896void ThreadRPCServer3(void* parg)
897{
898 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer3(parg));
96931d6f
GS
899
900 // Make this thread recognisable as the RPC handler
901 RenameThread("bitcoin-rpchand");
902
4e97a9d9
MC
903 {
904 LOCK(cs_THREAD_RPCHANDLER);
905 vnThreadsRunning[THREAD_RPCHANDLER]++;
906 }
e9205293 907 AcceptedConnection *conn = (AcceptedConnection *) parg;
69d605f4 908
96c52695
DJS
909 bool fRun = true;
910 loop {
911 if (fShutdown || !fRun)
912 {
a0780ba0 913 conn->close();
96c52695 914 delete conn;
4e97a9d9
MC
915 {
916 LOCK(cs_THREAD_RPCHANDLER);
917 --vnThreadsRunning[THREAD_RPCHANDLER];
918 }
96c52695
DJS
919 return;
920 }
69d605f4
WL
921 map<string, string> mapHeaders;
922 string strRequest;
923
a0780ba0 924 ReadHTTP(conn->stream(), mapHeaders, strRequest);
69d605f4
WL
925
926 // Check authorization
927 if (mapHeaders.count("authorization") == 0)
928 {
a0780ba0 929 conn->stream() << HTTPReply(401, "", false) << std::flush;
e9205293 930 break;
69d605f4
WL
931 }
932 if (!HTTPAuthorized(mapHeaders))
933 {
a0780ba0 934 printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str());
b04f301c
GM
935 /* Deter brute-forcing short passwords.
936 If this results in a DOS the user really
937 shouldn't have their RPC port exposed.*/
938 if (mapArgs["-rpcpassword"].size() < 20)
939 Sleep(250);
69d605f4 940
a0780ba0 941 conn->stream() << HTTPReply(401, "", false) << std::flush;
e9205293 942 break;
69d605f4 943 }
96c52695
DJS
944 if (mapHeaders["connection"] == "close")
945 fRun = false;
69d605f4 946
c6494d82 947 JSONRequest jreq;
69d605f4
WL
948 try
949 {
950 // Parse request
951 Value valRequest;
61338901 952 if (!read_string(strRequest, valRequest))
69d605f4 953 throw JSONRPCError(-32700, "Parse error");
69d605f4 954
61338901
JG
955 string strReply;
956
957 // singleton request
958 if (valRequest.type() == obj_type) {
959 jreq.parse(valRequest);
c6494d82 960
61338901 961 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
460c51fd 962
61338901
JG
963 // Send reply
964 strReply = JSONRPCReply(result, Value::null, jreq.id);
965
966 // array of requests
967 } else if (valRequest.type() == array_type)
968 strReply = JSONRPCExecBatch(valRequest.get_array());
969 else
970 throw JSONRPCError(-32700, "Top-level object parse error");
971
a0780ba0 972 conn->stream() << HTTPReply(200, strReply, fRun) << std::flush;
69d605f4
WL
973 }
974 catch (Object& objError)
975 {
c6494d82 976 ErrorReply(conn->stream(), objError, jreq.id);
e9205293 977 break;
69d605f4
WL
978 }
979 catch (std::exception& e)
980 {
c6494d82 981 ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), jreq.id);
e9205293 982 break;
69d605f4
WL
983 }
984 }
96c52695 985
e9205293 986 delete conn;
4e97a9d9
MC
987 {
988 LOCK(cs_THREAD_RPCHANDLER);
989 vnThreadsRunning[THREAD_RPCHANDLER]--;
990 }
69d605f4
WL
991}
992
460c51fd
WL
993json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
994{
995 // Find method
996 const CRPCCommand *pcmd = tableRPC[strMethod];
997 if (!pcmd)
998 throw JSONRPCError(-32601, "Method not found");
69d605f4 999
460c51fd
WL
1000 // Observe safe mode
1001 string strWarning = GetWarnings("rpc");
1002 if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
1003 !pcmd->okSafeMode)
1004 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
1005
1006 try
1007 {
1008 // Execute
1009 Value result;
1010 {
1011 LOCK2(cs_main, pwalletMain->cs_wallet);
1012 result = pcmd->actor(params, false);
1013 }
1014 return result;
1015 }
1016 catch (std::exception& e)
1017 {
1018 throw JSONRPCError(-1, e.what());
1019 }
1020}
69d605f4
WL
1021
1022
1023Object CallRPC(const string& strMethod, const Array& params)
1024{
1025 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
1026 throw runtime_error(strprintf(
1027 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
1028 "If the file does not exist, create it with owner-readable-only file permissions."),
ee12c3d6 1029 GetConfigFile().string().c_str()));
69d605f4
WL
1030
1031 // Connect to localhost
1032 bool fUseSSL = GetBoolArg("-rpcssl");
69d605f4
WL
1033 asio::io_service io_service;
1034 ssl::context context(io_service, ssl::context::sslv23);
1035 context.set_options(ssl::context::no_sslv2);
a0780ba0
GS
1036 asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
1037 SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
1038 iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
69d605f4
WL
1039 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
1040 throw runtime_error("couldn't connect to server");
69d605f4
WL
1041
1042 // HTTP basic authentication
1043 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
1044 map<string, string> mapRequestHeaders;
1045 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
1046
1047 // Send request
1048 string strRequest = JSONRPCRequest(strMethod, params, 1);
1049 string strPost = HTTPPost(strRequest, mapRequestHeaders);
1050 stream << strPost << std::flush;
1051
1052 // Receive reply
1053 map<string, string> mapHeaders;
1054 string strReply;
1055 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
1056 if (nStatus == 401)
1057 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
1058 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
1059 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
1060 else if (strReply.empty())
1061 throw runtime_error("no response from server");
1062
1063 // Parse reply
1064 Value valReply;
1065 if (!read_string(strReply, valReply))
1066 throw runtime_error("couldn't parse reply from server");
1067 const Object& reply = valReply.get_obj();
1068 if (reply.empty())
1069 throw runtime_error("expected reply to have result, error and id properties");
1070
1071 return reply;
1072}
1073
1074
1075
1076
1077template<typename T>
cc6dfd1f 1078void ConvertTo(Value& value, bool fAllowNull=false)
69d605f4 1079{
cc6dfd1f
GA
1080 if (fAllowNull && value.type() == null_type)
1081 return;
69d605f4
WL
1082 if (value.type() == str_type)
1083 {
1084 // reinterpret string as unquoted json value
1085 Value value2;
18871d47
GA
1086 string strJSON = value.get_str();
1087 if (!read_string(strJSON, value2))
1088 throw runtime_error(string("Error parsing JSON:")+strJSON);
cc6dfd1f
GA
1089 ConvertTo<T>(value2, fAllowNull);
1090 value = value2;
69d605f4
WL
1091 }
1092 else
1093 {
1094 value = value.get_value<T>();
1095 }
1096}
1097
460c51fd
WL
1098// Convert strings to command-specific RPC representation
1099Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
1100{
1101 Array params;
1102 BOOST_FOREACH(const std::string &param, strParams)
1103 params.push_back(param);
1104
1105 int n = params.size();
1106
1107 //
1108 // Special case non-string parameter types
1109 //
1110 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
1111 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1112 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
1113 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
1114 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1115 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1116 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1117 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
1118 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1119 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
1120 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1121 if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1122 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
1123 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
1124 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
1125 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
1126 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1127 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
1128 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1129 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
44427fa8 1130 if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
460c51fd 1131 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
98474d3d
GA
1132 if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
1133 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
1134 if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1135 if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
a2709fad
GA
1136 if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1137 if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1138 if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1139 if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
1140 if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
cc6dfd1f
GA
1141 if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
1142 if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
98474d3d 1143
460c51fd
WL
1144 return params;
1145}
1146
69d605f4
WL
1147int CommandLineRPC(int argc, char *argv[])
1148{
1149 string strPrint;
1150 int nRet = 0;
1151 try
1152 {
1153 // Skip switches
1154 while (argc > 1 && IsSwitchChar(argv[1][0]))
1155 {
1156 argc--;
1157 argv++;
1158 }
1159
1160 // Method
1161 if (argc < 2)
1162 throw runtime_error("too few parameters");
1163 string strMethod = argv[1];
1164
1165 // Parameters default to strings
460c51fd
WL
1166 std::vector<std::string> strParams(&argv[2], &argv[argc]);
1167 Array params = RPCConvertValues(strMethod, strParams);
69d605f4
WL
1168
1169 // Execute
1170 Object reply = CallRPC(strMethod, params);
1171
1172 // Parse reply
1173 const Value& result = find_value(reply, "result");
1174 const Value& error = find_value(reply, "error");
69d605f4
WL
1175
1176 if (error.type() != null_type)
1177 {
1178 // Error
1179 strPrint = "error: " + write_string(error, false);
1180 int code = find_value(error.get_obj(), "code").get_int();
1181 nRet = abs(code);
1182 }
1183 else
1184 {
1185 // Result
1186 if (result.type() == null_type)
1187 strPrint = "";
1188 else if (result.type() == str_type)
1189 strPrint = result.get_str();
1190 else
1191 strPrint = write_string(result, true);
1192 }
1193 }
1194 catch (std::exception& e)
1195 {
1196 strPrint = string("error: ") + e.what();
1197 nRet = 87;
1198 }
1199 catch (...)
1200 {
1201 PrintException(NULL, "CommandLineRPC()");
1202 }
1203
1204 if (strPrint != "")
1205 {
69d605f4 1206 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
69d605f4
WL
1207 }
1208 return nRet;
1209}
1210
1211
1212
1213
1214#ifdef TEST
1215int main(int argc, char *argv[])
1216{
1217#ifdef _MSC_VER
814efd6f 1218 // Turn off Microsoft heap dump noise
69d605f4
WL
1219 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1220 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
1221#endif
1222 setbuf(stdin, NULL);
1223 setbuf(stdout, NULL);
1224 setbuf(stderr, NULL);
1225
1226 try
1227 {
1228 if (argc >= 2 && string(argv[1]) == "-server")
1229 {
1230 printf("server ready\n");
1231 ThreadRPCServer(NULL);
1232 }
1233 else
1234 {
1235 return CommandLineRPC(argc, argv);
1236 }
1237 }
1238 catch (std::exception& e) {
1239 PrintException(&e, "main()");
1240 } catch (...) {
1241 PrintException(NULL, "main()");
1242 }
1243 return 0;
1244}
1245#endif
e46704dd
PW
1246
1247const CRPCTable tableRPC;
This page took 0.383356 seconds and 4 git commands to generate.