Gavin: BIO_FLAGS_BASE64_NO_NL
[bitcoinplatinum.git] / rpc.cpp
blob5c0674fd08173a78666149667fc558dbac692cff
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
5 #include "headers.h"
6 #undef printf
7 #include <boost/asio.hpp>
8 #include "json/json_spirit_reader_template.h"
9 #include "json/json_spirit_writer_template.h"
10 #include "json/json_spirit_utils.h"
11 #define printf OutputDebugStringF
12 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
13 // precompiled in headers.h. The problem might be when the pch file goes over
14 // a certain size around 145MB. If we need access to json_spirit outside this
15 // file, we could use the compiled json_spirit option.
17 using boost::asio::ip::tcp;
18 using namespace json_spirit;
20 void ThreadRPCServer2(void* parg);
21 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
22 extern map<string, rpcfn_type> mapCallTable;
26 void PrintConsole(const char* format, ...)
28 char buffer[50000];
29 int limit = sizeof(buffer);
30 va_list arg_ptr;
31 va_start(arg_ptr, format);
32 int ret = _vsnprintf(buffer, limit, format, arg_ptr);
33 va_end(arg_ptr);
34 if (ret < 0 || ret >= limit)
36 ret = limit - 1;
37 buffer[limit-1] = 0;
39 #if defined(__WXMSW__) && wxUSE_GUI
40 MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION);
41 #else
42 fprintf(stdout, "%s", buffer);
43 #endif
52 ///
53 /// Note: This interface may still be subject to change.
54 ///
57 Value help(const Array& params, bool fHelp)
59 if (fHelp || params.size() != 0)
60 throw runtime_error(
61 "help\n"
62 "List commands.");
64 string strRet;
65 set<rpcfn_type> setDone;
66 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
68 string strMethod = (*mi).first;
69 // We already filter duplicates, but these deprecated screw up the sort order
70 if (strMethod == "getamountreceived" ||
71 strMethod == "getallreceived")
72 continue;
73 try
75 Array params;
76 rpcfn_type pfn = (*mi).second;
77 if (setDone.insert(pfn).second)
78 (*pfn)(params, true);
80 catch (std::exception& e)
82 // Help text is returned in an exception
83 string strHelp = string(e.what());
84 if (strHelp.find('\n') != -1)
85 strHelp = strHelp.substr(0, strHelp.find('\n'));
86 strRet += strHelp + "\n";
89 strRet = strRet.substr(0,strRet.size()-1);
90 return strRet;
94 Value stop(const Array& params, bool fHelp)
96 if (fHelp || params.size() != 0)
97 throw runtime_error(
98 "stop\n"
99 "Stop bitcoin server.");
101 // Shutdown will take long enough that the response should get back
102 CreateThread(Shutdown, NULL);
103 return "bitcoin server stopping";
107 Value getblockcount(const Array& params, bool fHelp)
109 if (fHelp || params.size() != 0)
110 throw runtime_error(
111 "getblockcount\n"
112 "Returns the number of blocks in the longest block chain.");
114 return nBestHeight + 1;
118 Value getblocknumber(const Array& params, bool fHelp)
120 if (fHelp || params.size() != 0)
121 throw runtime_error(
122 "getblocknumber\n"
123 "Returns the block number of the latest block in the longest block chain.");
125 return nBestHeight;
129 Value getconnectioncount(const Array& params, bool fHelp)
131 if (fHelp || params.size() != 0)
132 throw runtime_error(
133 "getconnectioncount\n"
134 "Returns the number of connections to other nodes.");
136 return (int)vNodes.size();
140 double GetDifficulty()
142 // Floating point number that is a multiple of the minimum difficulty,
143 // minimum difficulty = 1.0.
144 if (pindexBest == NULL)
145 return 1.0;
146 int nShift = 256 - 32 - 31; // to fit in a uint
147 double dMinimum = (CBigNum().SetCompact(bnProofOfWorkLimit.GetCompact()) >> nShift).getuint();
148 double dCurrently = (CBigNum().SetCompact(pindexBest->nBits) >> nShift).getuint();
149 return dMinimum / dCurrently;
152 Value getdifficulty(const Array& params, bool fHelp)
154 if (fHelp || params.size() != 0)
155 throw runtime_error(
156 "getdifficulty\n"
157 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
159 return GetDifficulty();
163 Value getbalance(const Array& params, bool fHelp)
165 if (fHelp || params.size() != 0)
166 throw runtime_error(
167 "getbalance\n"
168 "Returns the server's available balance.");
170 return ((double)GetBalance() / (double)COIN);
174 Value getgenerate(const Array& params, bool fHelp)
176 if (fHelp || params.size() != 0)
177 throw runtime_error(
178 "getgenerate\n"
179 "Returns true or false.");
181 return (bool)fGenerateBitcoins;
185 Value setgenerate(const Array& params, bool fHelp)
187 if (fHelp || params.size() < 1 || params.size() > 2)
188 throw runtime_error(
189 "setgenerate <generate> [genproclimit]\n"
190 "<generate> is true or false to turn generation on or off.\n"
191 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
193 bool fGenerate = true;
194 if (params.size() > 0)
195 fGenerate = params[0].get_bool();
197 if (params.size() > 1)
199 int nGenProcLimit = params[1].get_int();
200 fLimitProcessors = (nGenProcLimit != -1);
201 CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors);
202 if (nGenProcLimit != -1)
203 CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
206 GenerateBitcoins(fGenerate);
207 return Value::null;
211 Value getinfo(const Array& params, bool fHelp)
213 if (fHelp || params.size() != 0)
214 throw runtime_error(
215 "getinfo");
217 Object obj;
218 obj.push_back(Pair("balance", (double)GetBalance() / (double)COIN));
219 obj.push_back(Pair("blocks", (int)nBestHeight + 1));
220 obj.push_back(Pair("connections", (int)vNodes.size()));
221 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
222 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
223 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
224 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
225 return obj;
229 Value getnewaddress(const Array& params, bool fHelp)
231 if (fHelp || params.size() > 1)
232 throw runtime_error(
233 "getnewaddress [label]\n"
234 "Returns a new bitcoin address for receiving payments. "
235 "If [label] is specified (recommended), it is added to the address book "
236 "so payments received with the address will be labeled.");
238 // Parse the label first so we don't generate a key if there's an error
239 string strLabel;
240 if (params.size() > 0)
241 strLabel = params[0].get_str();
243 // Generate a new key that is added to wallet
244 string strAddress = PubKeyToAddress(GenerateNewKey());
246 SetAddressBookName(strAddress, strLabel);
247 return strAddress;
251 Value setlabel(const Array& params, bool fHelp)
253 if (fHelp || params.size() < 1 || params.size() > 2)
254 throw runtime_error(
255 "setlabel <bitcoinaddress> <label>\n"
256 "Sets the label associated with the given address.");
258 string strAddress = params[0].get_str();
259 string strLabel;
260 if (params.size() > 1)
261 strLabel = params[1].get_str();
263 SetAddressBookName(strAddress, strLabel);
264 return Value::null;
268 Value getlabel(const Array& params, bool fHelp)
270 if (fHelp || params.size() != 1)
271 throw runtime_error(
272 "getlabel <bitcoinaddress>\n"
273 "Returns the label associated with the given address.");
275 string strAddress = params[0].get_str();
277 string strLabel;
278 CRITICAL_BLOCK(cs_mapAddressBook)
280 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
281 if (mi != mapAddressBook.end() && !(*mi).second.empty())
282 strLabel = (*mi).second;
284 return strLabel;
288 Value getaddressesbylabel(const Array& params, bool fHelp)
290 if (fHelp || params.size() != 1)
291 throw runtime_error(
292 "getaddressesbylabel <label>\n"
293 "Returns the list of addresses with the given label.");
295 string strLabel = params[0].get_str();
297 // Find all addresses that have the given label
298 Array ret;
299 CRITICAL_BLOCK(cs_mapAddressBook)
301 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
303 const string& strAddress = item.first;
304 const string& strName = item.second;
305 if (strName == strLabel)
307 // We're only adding valid bitcoin addresses and not ip addresses
308 CScript scriptPubKey;
309 if (scriptPubKey.SetBitcoinAddress(strAddress))
310 ret.push_back(strAddress);
314 return ret;
318 Value sendtoaddress(const Array& params, bool fHelp)
320 if (fHelp || params.size() < 2 || params.size() > 4)
321 throw runtime_error(
322 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
323 "<amount> is a real and is rounded to the nearest 0.01");
325 string strAddress = params[0].get_str();
327 // Amount
328 if (params[1].get_real() <= 0.0 || params[1].get_real() > 21000000.0)
329 throw runtime_error("Invalid amount");
330 int64 nAmount = roundint64(params[1].get_real() * 100.00) * CENT;
332 // Wallet comments
333 CWalletTx wtx;
334 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
335 wtx.mapValue["message"] = params[2].get_str();
336 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
337 wtx.mapValue["to"] = params[3].get_str();
339 string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx);
340 if (strError != "")
341 throw runtime_error(strError);
342 return "sent";
346 Value listtransactions(const Array& params, bool fHelp)
348 if (fHelp || params.size() > 2)
349 throw runtime_error(
350 "listtransactions [count=10] [includegenerated=false]\n"
351 "Returns up to [count] most recent transactions.");
353 int64 nCount = 10;
354 if (params.size() > 0)
355 nCount = params[0].get_int64();
356 bool fGenerated = false;
357 if (params.size() > 1)
358 fGenerated = params[1].get_bool();
360 Array ret;
361 //// not finished
362 ret.push_back("not implemented yet");
363 return ret;
367 Value getreceivedbyaddress(const Array& params, bool fHelp)
369 if (fHelp || params.size() < 1 || params.size() > 2)
370 throw runtime_error(
371 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
372 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
374 // Bitcoin address
375 string strAddress = params[0].get_str();
376 CScript scriptPubKey;
377 if (!scriptPubKey.SetBitcoinAddress(strAddress))
378 throw runtime_error("Invalid bitcoin address");
379 if (!IsMine(scriptPubKey))
380 return (double)0.0;
382 // Minimum confirmations
383 int nMinDepth = 1;
384 if (params.size() > 1)
385 nMinDepth = params[1].get_int();
387 // Tally
388 int64 nAmount = 0;
389 CRITICAL_BLOCK(cs_mapWallet)
391 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
393 const CWalletTx& wtx = (*it).second;
394 if (wtx.IsCoinBase() || !wtx.IsFinal())
395 continue;
397 foreach(const CTxOut& txout, wtx.vout)
398 if (txout.scriptPubKey == scriptPubKey)
399 if (wtx.GetDepthInMainChain() >= nMinDepth)
400 nAmount += txout.nValue;
404 return (double)nAmount / (double)COIN;
408 Value getreceivedbylabel(const Array& params, bool fHelp)
410 if (fHelp || params.size() < 1 || params.size() > 2)
411 throw runtime_error(
412 "getreceivedbylabel <label> [minconf=1]\n"
413 "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.");
415 // Get the set of pub keys that have the label
416 string strLabel = params[0].get_str();
417 set<CScript> setPubKey;
418 CRITICAL_BLOCK(cs_mapAddressBook)
420 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
422 const string& strAddress = item.first;
423 const string& strName = item.second;
424 if (strName == strLabel)
426 // We're only counting our own valid bitcoin addresses and not ip addresses
427 CScript scriptPubKey;
428 if (scriptPubKey.SetBitcoinAddress(strAddress))
429 if (IsMine(scriptPubKey))
430 setPubKey.insert(scriptPubKey);
435 // Minimum confirmations
436 int nMinDepth = 1;
437 if (params.size() > 1)
438 nMinDepth = params[1].get_int();
440 // Tally
441 int64 nAmount = 0;
442 CRITICAL_BLOCK(cs_mapWallet)
444 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
446 const CWalletTx& wtx = (*it).second;
447 if (wtx.IsCoinBase() || !wtx.IsFinal())
448 continue;
450 foreach(const CTxOut& txout, wtx.vout)
451 if (setPubKey.count(txout.scriptPubKey))
452 if (wtx.GetDepthInMainChain() >= nMinDepth)
453 nAmount += txout.nValue;
457 return (double)nAmount / (double)COIN;
461 struct tallyitem
463 int64 nAmount;
464 int nConf;
465 tallyitem()
467 nAmount = 0;
468 nConf = INT_MAX;
472 Value ListReceived(const Array& params, bool fByLabels)
474 // Minimum confirmations
475 int nMinDepth = 1;
476 if (params.size() > 0)
477 nMinDepth = params[0].get_int();
479 // Whether to include empty accounts
480 bool fIncludeEmpty = false;
481 if (params.size() > 1)
482 fIncludeEmpty = params[1].get_bool();
484 // Tally
485 map<uint160, tallyitem> mapTally;
486 CRITICAL_BLOCK(cs_mapWallet)
488 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
490 const CWalletTx& wtx = (*it).second;
491 if (wtx.IsCoinBase() || !wtx.IsFinal())
492 continue;
494 int nDepth = wtx.GetDepthInMainChain();
495 if (nDepth < nMinDepth)
496 continue;
498 foreach(const CTxOut& txout, wtx.vout)
500 // Only counting our own bitcoin addresses and not ip addresses
501 uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160();
502 if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine
503 continue;
505 tallyitem& item = mapTally[hash160];
506 item.nAmount += txout.nValue;
507 item.nConf = min(item.nConf, nDepth);
512 // Reply
513 Array ret;
514 map<string, tallyitem> mapLabelTally;
515 CRITICAL_BLOCK(cs_mapAddressBook)
517 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
519 const string& strAddress = item.first;
520 const string& strLabel = item.second;
521 uint160 hash160;
522 if (!AddressToHash160(strAddress, hash160))
523 continue;
524 map<uint160, tallyitem>::iterator it = mapTally.find(hash160);
525 if (it == mapTally.end() && !fIncludeEmpty)
526 continue;
528 int64 nAmount = 0;
529 int nConf = INT_MAX;
530 if (it != mapTally.end())
532 nAmount = (*it).second.nAmount;
533 nConf = (*it).second.nConf;
536 if (fByLabels)
538 tallyitem& item = mapLabelTally[strLabel];
539 item.nAmount += nAmount;
540 item.nConf = min(item.nConf, nConf);
542 else
544 Object obj;
545 obj.push_back(Pair("address", strAddress));
546 obj.push_back(Pair("label", strLabel));
547 obj.push_back(Pair("amount", (double)nAmount / (double)COIN));
548 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
549 ret.push_back(obj);
554 if (fByLabels)
556 for (map<string, tallyitem>::iterator it = mapLabelTally.begin(); it != mapLabelTally.end(); ++it)
558 int64 nAmount = (*it).second.nAmount;
559 int nConf = (*it).second.nConf;
560 Object obj;
561 obj.push_back(Pair("label", (*it).first));
562 obj.push_back(Pair("amount", (double)nAmount / (double)COIN));
563 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
564 ret.push_back(obj);
568 return ret;
571 Value listreceivedbyaddress(const Array& params, bool fHelp)
573 if (fHelp || params.size() > 2)
574 throw runtime_error(
575 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
576 "[minconf] is the minimum number of confirmations before payments are included.\n"
577 "[includeempty] whether to include addresses that haven't received any payments.\n"
578 "Returns an array of objects containing:\n"
579 " \"address\" : receiving address\n"
580 " \"label\" : the label of the receiving address\n"
581 " \"amount\" : total amount received by the address\n"
582 " \"confirmations\" : number of confirmations of the most recent transaction included");
584 return ListReceived(params, false);
587 Value listreceivedbylabel(const Array& params, bool fHelp)
589 if (fHelp || params.size() > 2)
590 throw runtime_error(
591 "listreceivedbylabel [minconf=1] [includeempty=false]\n"
592 "[minconf] is the minimum number of confirmations before payments are included.\n"
593 "[includeempty] whether to include labels that haven't received any payments.\n"
594 "Returns an array of objects containing:\n"
595 " \"label\" : the label of the receiving addresses\n"
596 " \"amount\" : total amount received by addresses with this label\n"
597 " \"confirmations\" : number of confirmations of the most recent transaction included");
599 return ListReceived(params, true);
615 // Call Table
618 pair<string, rpcfn_type> pCallTable[] =
620 make_pair("help", &help),
621 make_pair("stop", &stop),
622 make_pair("getblockcount", &getblockcount),
623 make_pair("getblocknumber", &getblocknumber),
624 make_pair("getconnectioncount", &getconnectioncount),
625 make_pair("getdifficulty", &getdifficulty),
626 make_pair("getbalance", &getbalance),
627 make_pair("getgenerate", &getgenerate),
628 make_pair("setgenerate", &setgenerate),
629 make_pair("getinfo", &getinfo),
630 make_pair("getnewaddress", &getnewaddress),
631 make_pair("setlabel", &setlabel),
632 make_pair("getlabel", &getlabel),
633 make_pair("getaddressesbylabel", &getaddressesbylabel),
634 make_pair("sendtoaddress", &sendtoaddress),
635 make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
636 make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
637 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
638 make_pair("getreceivedbylabel", &getreceivedbylabel),
639 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
640 make_pair("listreceivedbylabel", &listreceivedbylabel),
642 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
648 // HTTP protocol
650 // This ain't Apache. We're just using HTTP header for the length field
651 // and to be compatible with other JSON-RPC implementations.
654 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
656 ostringstream s;
657 s << "POST / HTTP/1.1\r\n"
658 << "User-Agent: json-rpc/1.0\r\n"
659 << "Host: 127.0.0.1\r\n"
660 << "Content-Type: application/json\r\n"
661 << "Content-Length: " << strMsg.size() << "\r\n"
662 << "Accept: application/json\r\n";
663 for (map<string,string>::const_iterator it = mapRequestHeaders.begin(); it != mapRequestHeaders.end(); ++it)
664 s << it->first << ": " << it->second << "\r\n";
665 s << "\r\n" << strMsg;
667 return s.str();
670 string HTTPReply(const string& strMsg, int nStatus=200)
672 if (nStatus == 401)
673 return "HTTP/1.0 401 Authorization Required\r\n"
674 "Server: HTTPd/1.0\r\n"
675 "Date: Sat, 08 Jul 2006 12:04:08 GMT\r\n"
676 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
677 "Content-Type: text/html\r\n"
678 "Content-Length: 311\r\n"
679 "\r\n"
680 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
681 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
682 "<HTML>\r\n"
683 "<HEAD>\r\n"
684 "<TITLE>Error</TITLE>\r\n"
685 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
686 "</HEAD>\r\n"
687 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
688 "</HTML>\r\n";
689 string strStatus;
690 if (nStatus == 200) strStatus = "OK";
691 if (nStatus == 500) strStatus = "Internal Server Error";
692 return strprintf(
693 "HTTP/1.1 %d %s\r\n"
694 "Connection: close\r\n"
695 "Content-Length: %d\r\n"
696 "Content-Type: application/json\r\n"
697 "Date: Sat, 08 Jul 2006 12:04:08 GMT\r\n"
698 "Server: json-rpc/1.0\r\n"
699 "\r\n"
700 "%s",
701 nStatus,
702 strStatus.c_str(),
703 strMsg.size(),
704 strMsg.c_str());
707 int ReadHTTPStatus(tcp::iostream& stream)
709 string str;
710 getline(stream, str);
711 vector<string> vWords;
712 boost::split(vWords, str, boost::is_any_of(" "));
713 int nStatus = atoi(vWords[1].c_str());
714 return nStatus;
717 int ReadHTTPHeader(tcp::iostream& stream, map<string, string>& mapHeadersRet)
719 int nLen = 0;
720 loop
722 string str;
723 std::getline(stream, str);
724 if (str.empty() || str == "\r")
725 break;
726 string::size_type nColon = str.find(":");
727 if (nColon != string::npos)
729 string strHeader = str.substr(0, nColon);
730 boost::trim(strHeader);
731 string strValue = str.substr(nColon+1);
732 boost::trim(strValue);
733 mapHeadersRet[strHeader] = strValue;
734 if (strHeader == "Content-Length")
735 nLen = atoi(strValue.c_str());
738 return nLen;
741 int ReadHTTP(tcp::iostream& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
743 mapHeadersRet.clear();
744 strMessageRet = "";
746 // Read status
747 int nStatus = ReadHTTPStatus(stream);
749 // Read header
750 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
751 if (nLen <= 0)
752 return 500;
754 // Read message
755 vector<char> vch(nLen);
756 stream.read(&vch[0], nLen);
757 strMessageRet = string(vch.begin(), vch.end());
759 return nStatus;
762 string EncodeBase64(string s)
764 BIO *b64, *bmem;
765 BUF_MEM *bptr;
767 b64 = BIO_new(BIO_f_base64());
768 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
769 bmem = BIO_new(BIO_s_mem());
770 b64 = BIO_push(b64, bmem);
771 BIO_write(b64, s.c_str(), s.size());
772 BIO_flush(b64);
773 BIO_get_mem_ptr(b64, &bptr);
775 string result(bptr->data, bptr->length);
776 BIO_free_all(b64);
778 return result;
781 string DecodeBase64(string s)
783 BIO *b64, *bmem;
785 char* buffer = static_cast<char*>(calloc(s.size(), sizeof(char)));
787 b64 = BIO_new(BIO_f_base64());
788 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
789 bmem = BIO_new_mem_buf(const_cast<char*>(s.c_str()), s.size());
790 bmem = BIO_push(b64, bmem);
791 BIO_read(bmem, buffer, s.size());
792 BIO_free_all(bmem);
794 string result(buffer);
795 free(buffer);
796 return result;
799 bool HTTPAuthorized(map<string, string>& mapHeaders)
801 string strAuth = mapHeaders["Authorization"];
802 if (strAuth.substr(0,6) != "Basic ")
803 return false;
804 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
805 string strUserPass = DecodeBase64(strUserPass64);
806 string::size_type nColon = strUserPass.find(":");
807 if (nColon == string::npos)
808 return false;
809 string strUser = strUserPass.substr(0, nColon);
810 string strPassword = strUserPass.substr(nColon+1);
811 return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]);
815 // JSON-RPC protocol
817 // http://json-rpc.org/wiki/specification
818 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
821 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
823 Object request;
824 request.push_back(Pair("method", strMethod));
825 request.push_back(Pair("params", params));
826 request.push_back(Pair("id", id));
827 return write_string(Value(request), false) + "\n";
830 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
832 Object reply;
833 if (error.type() != null_type)
834 reply.push_back(Pair("result", Value::null));
835 else
836 reply.push_back(Pair("result", result));
837 reply.push_back(Pair("error", error));
838 reply.push_back(Pair("id", id));
839 return write_string(Value(reply), false) + "\n";
845 void ThreadRPCServer(void* parg)
847 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
850 vnThreadsRunning[4]++;
851 ThreadRPCServer2(parg);
852 vnThreadsRunning[4]--;
854 catch (std::exception& e) {
855 vnThreadsRunning[4]--;
856 PrintException(&e, "ThreadRPCServer()");
857 } catch (...) {
858 vnThreadsRunning[4]--;
859 PrintException(NULL, "ThreadRPCServer()");
861 printf("ThreadRPCServer exiting\n");
864 void ThreadRPCServer2(void* parg)
866 printf("ThreadRPCServer started\n");
868 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
870 string strWhatAmI = "To use bitcoind";
871 if (mapArgs.count("-server"))
872 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
873 else if (mapArgs.count("-daemon"))
874 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
875 PrintConsole(
876 _("Warning: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
877 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
878 strWhatAmI.c_str(),
879 GetConfigFile().c_str());
880 CreateThread(Shutdown, NULL);
881 return;
884 // Bind to loopback 127.0.0.1 so the socket can only be accessed locally
885 boost::asio::io_service io_service;
886 tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 8332);
887 tcp::acceptor acceptor(io_service, endpoint);
889 loop
891 // Accept connection
892 tcp::iostream stream;
893 tcp::endpoint peer;
894 vnThreadsRunning[4]--;
895 acceptor.accept(*stream.rdbuf(), peer);
896 vnThreadsRunning[4]++;
897 if (fShutdown)
898 return;
900 // Shouldn't be possible for anyone else to connect, but just in case
901 if (peer.address().to_string() != "127.0.0.1")
902 continue;
904 // Receive request
905 map<string, string> mapHeaders;
906 string strRequest;
907 ReadHTTP(stream, mapHeaders, strRequest);
909 // Check authorization
910 if (mapHeaders.count("Authorization") == 0)
912 stream << HTTPReply("", 401) << std::flush;
913 continue;
915 if (!HTTPAuthorized(mapHeaders))
917 // Deter brute-forcing short passwords
918 if (mapArgs["-rpcpassword"].size() < 15)
919 Sleep(50);
921 stream << HTTPReply("", 401) << std::flush;
922 printf("ThreadRPCServer incorrect password attempt\n");
923 continue;
926 // Handle multiple invocations per request
927 string::iterator begin = strRequest.begin();
928 while (skipspaces(begin), begin != strRequest.end())
930 string::iterator prev = begin;
931 Value id;
934 // Parse request
935 Value valRequest;
936 if (!read_range(begin, strRequest.end(), valRequest))
937 throw runtime_error("Parse error.");
938 const Object& request = valRequest.get_obj();
939 if (find_value(request, "method").type() != str_type ||
940 find_value(request, "params").type() != array_type)
941 throw runtime_error("Invalid request.");
943 string strMethod = find_value(request, "method").get_str();
944 const Array& params = find_value(request, "params").get_array();
945 id = find_value(request, "id");
947 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
949 // Execute
950 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
951 if (mi == mapCallTable.end())
952 throw runtime_error("Method not found.");
953 Value result = (*(*mi).second)(params, false);
955 // Send reply
956 string strReply = JSONRPCReply(result, Value::null, id);
957 stream << HTTPReply(strReply, 200) << std::flush;
959 catch (std::exception& e)
961 // Send error reply
962 string strReply = JSONRPCReply(Value::null, e.what(), id);
963 stream << HTTPReply(strReply, 500) << std::flush;
965 if (begin == prev)
966 break;
974 Value CallRPC(const string& strMethod, const Array& params)
976 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
977 throw runtime_error(strprintf(
978 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
979 "If the file does not exist, create it with owner-readable-only file permissions."),
980 GetConfigFile().c_str()));
982 // Connect to localhost
983 tcp::iostream stream("127.0.0.1", "8332");
984 if (stream.fail())
985 throw runtime_error("couldn't connect to server");
987 // HTTP basic authentication
988 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
989 map<string, string> mapRequestHeaders;
990 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
992 // Send request
993 string strRequest = JSONRPCRequest(strMethod, params, 1);
994 string strPost = HTTPPost(strRequest, mapRequestHeaders);
995 stream << strPost << std::flush;
997 // Receive reply
998 map<string, string> mapHeaders;
999 string strReply;
1000 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
1001 if (nStatus == 401)
1002 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
1003 else if (nStatus >= 400 && nStatus != 500)
1004 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
1005 else if (strReply.empty())
1006 throw runtime_error("no response from server");
1008 // Parse reply
1009 Value valReply;
1010 if (!read_string(strReply, valReply))
1011 throw runtime_error("couldn't parse reply from server");
1012 const Object& reply = valReply.get_obj();
1013 if (reply.empty())
1014 throw runtime_error("expected reply to have result, error and id properties");
1016 const Value& result = find_value(reply, "result");
1017 const Value& error = find_value(reply, "error");
1018 const Value& id = find_value(reply, "id");
1020 if (error.type() == str_type)
1021 throw runtime_error(error.get_str());
1022 else if (error.type() != null_type)
1023 throw runtime_error(write_string(error, false));
1024 return result;
1030 template<typename T>
1031 void ConvertTo(Value& value)
1033 if (value.type() == str_type)
1035 // reinterpret string as unquoted json value
1036 Value value2;
1037 if (!read_string(value.get_str(), value2))
1038 throw runtime_error("type mismatch");
1039 value = value2.get_value<T>();
1041 else
1043 value = value.get_value<T>();
1047 int CommandLineRPC(int argc, char *argv[])
1051 // Skip switches
1052 while (argc > 1 && IsSwitchChar(argv[1][0]))
1054 argc--;
1055 argv++;
1058 // Check that the method exists
1059 if (argc < 2)
1060 throw runtime_error("too few parameters");
1061 string strMethod = argv[1];
1062 if (!mapCallTable.count(strMethod))
1063 throw runtime_error(strprintf("unknown command: %s", strMethod.c_str()));
1065 Value result;
1066 if (argc == 3 && strcmp(argv[2], "-?") == 0)
1068 // Call help locally, help text is returned in an exception
1071 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
1072 Array params;
1073 (*(*mi).second)(params, true);
1075 catch (std::exception& e)
1077 result = e.what();
1080 else
1082 // Parameters default to strings
1083 Array params;
1084 for (int i = 2; i < argc; i++)
1085 params.push_back(argv[i]);
1086 int n = params.size();
1089 // Special case non-string parameter types
1091 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
1092 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1093 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
1094 if (strMethod == "listtransactions" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1095 if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);
1096 if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
1097 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1098 if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1099 if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
1100 if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]);
1101 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1102 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
1103 if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1104 if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]);
1106 // Execute
1107 result = CallRPC(strMethod, params);
1110 // Print result
1111 string strResult = (result.type() == str_type ? result.get_str() : write_string(result, true));
1112 if (result.type() != null_type)
1114 #if defined(__WXMSW__) && wxUSE_GUI
1115 // Windows GUI apps can't print to command line,
1116 // so settle for a message box yuck
1117 MyMessageBox(strResult.c_str(), "Bitcoin", wxOK);
1118 #else
1119 fprintf(stdout, "%s\n", strResult.c_str());
1120 #endif
1122 return 0;
1124 catch (std::exception& e) {
1125 #if defined(__WXMSW__) && wxUSE_GUI
1126 MyMessageBox(strprintf("error: %s\n", e.what()).c_str(), "Bitcoin", wxOK);
1127 #else
1128 fprintf(stderr, "error: %s\n", e.what());
1129 #endif
1130 } catch (...) {
1131 PrintException(NULL, "CommandLineRPC()");
1133 return 1;
1139 #ifdef TEST
1140 int main(int argc, char *argv[])
1142 #ifdef _MSC_VER
1143 // Turn off microsoft heap dump noise
1144 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1145 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
1146 #endif
1147 setbuf(stdin, NULL);
1148 setbuf(stdout, NULL);
1149 setbuf(stderr, NULL);
1153 if (argc >= 2 && string(argv[1]) == "-server")
1155 printf("server ready\n");
1156 ThreadRPCServer(NULL);
1158 else
1160 return CommandLineRPC(argc, argv);
1163 catch (std::exception& e) {
1164 PrintException(&e, "main()");
1165 } catch (...) {
1166 PrintException(NULL, "main()");
1168 return 0;
1170 #endif