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.
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
, ...)
29 int limit
= sizeof(buffer
);
31 va_start(arg_ptr
, format
);
32 int ret
= _vsnprintf(buffer
, limit
, format
, arg_ptr
);
34 if (ret
< 0 || ret
>= limit
)
39 #if defined(__WXMSW__) && wxUSE_GUI
40 MyMessageBox(buffer
, "Bitcoin", wxOK
| wxICON_EXCLAMATION
);
42 fprintf(stdout
, "%s", buffer
);
53 /// Note: This interface may still be subject to change.
57 Value
help(const Array
& params
, bool fHelp
)
59 if (fHelp
|| params
.size() != 0)
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")
76 rpcfn_type pfn
= (*mi
).second
;
77 if (setDone
.insert(pfn
).second
)
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);
94 Value
stop(const Array
& params
, bool fHelp
)
96 if (fHelp
|| params
.size() != 0)
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)
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)
123 "Returns the block number of the latest block in the longest block chain.");
129 Value
getconnectioncount(const Array
& params
, bool fHelp
)
131 if (fHelp
|| params
.size() != 0)
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
)
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)
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)
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)
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)
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
);
211 Value
getinfo(const Array
& params
, bool fHelp
)
213 if (fHelp
|| params
.size() != 0)
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()));
229 Value
getnewaddress(const Array
& params
, bool fHelp
)
231 if (fHelp
|| params
.size() > 1)
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
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
);
251 Value
setlabel(const Array
& params
, bool fHelp
)
253 if (fHelp
|| params
.size() < 1 || params
.size() > 2)
255 "setlabel <bitcoinaddress> <label>\n"
256 "Sets the label associated with the given address.");
258 string strAddress
= params
[0].get_str();
260 if (params
.size() > 1)
261 strLabel
= params
[1].get_str();
263 SetAddressBookName(strAddress
, strLabel
);
268 Value
getlabel(const Array
& params
, bool fHelp
)
270 if (fHelp
|| params
.size() != 1)
272 "getlabel <bitcoinaddress>\n"
273 "Returns the label associated with the given address.");
275 string strAddress
= params
[0].get_str();
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
;
288 Value
getaddressesbylabel(const Array
& params
, bool fHelp
)
290 if (fHelp
|| params
.size() != 1)
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
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
);
318 Value
sendtoaddress(const Array
& params
, bool fHelp
)
320 if (fHelp
|| params
.size() < 2 || params
.size() > 4)
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();
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
;
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
);
341 throw runtime_error(strError
);
346 Value
listtransactions(const Array
& params
, bool fHelp
)
348 if (fHelp
|| params
.size() > 2)
350 "listtransactions [count=10] [includegenerated=false]\n"
351 "Returns up to [count] most recent transactions.");
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();
362 ret
.push_back("not implemented yet");
367 Value
getreceivedbyaddress(const Array
& params
, bool fHelp
)
369 if (fHelp
|| params
.size() < 1 || params
.size() > 2)
371 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
372 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
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
))
382 // Minimum confirmations
384 if (params
.size() > 1)
385 nMinDepth
= params
[1].get_int();
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())
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)
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
437 if (params
.size() > 1)
438 nMinDepth
= params
[1].get_int();
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())
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
;
472 Value
ListReceived(const Array
& params
, bool fByLabels
)
474 // Minimum confirmations
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();
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())
494 int nDepth
= wtx
.GetDepthInMainChain();
495 if (nDepth
< nMinDepth
)
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
505 tallyitem
& item
= mapTally
[hash160
];
506 item
.nAmount
+= txout
.nValue
;
507 item
.nConf
= min(item
.nConf
, nDepth
);
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
;
522 if (!AddressToHash160(strAddress
, hash160
))
524 map
<uint160
, tallyitem
>::iterator it
= mapTally
.find(hash160
);
525 if (it
== mapTally
.end() && !fIncludeEmpty
)
530 if (it
!= mapTally
.end())
532 nAmount
= (*it
).second
.nAmount
;
533 nConf
= (*it
).second
.nConf
;
538 tallyitem
& item
= mapLabelTally
[strLabel
];
539 item
.nAmount
+= nAmount
;
540 item
.nConf
= min(item
.nConf
, nConf
);
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
)));
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
;
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
)));
571 Value
listreceivedbyaddress(const Array
& params
, bool fHelp
)
573 if (fHelp
|| params
.size() > 2)
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)
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);
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]));
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
)
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
;
670 string
HTTPReply(const string
& strMsg
, int nStatus
=200)
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"
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"
684 "<TITLE>Error</TITLE>\r\n"
685 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
687 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
690 if (nStatus
== 200) strStatus
= "OK";
691 if (nStatus
== 500) strStatus
= "Internal Server Error";
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"
707 int ReadHTTPStatus(tcp::iostream
& stream
)
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());
717 int ReadHTTPHeader(tcp::iostream
& stream
, map
<string
, string
>& mapHeadersRet
)
723 std::getline(stream
, str
);
724 if (str
.empty() || str
== "\r")
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());
741 int ReadHTTP(tcp::iostream
& stream
, map
<string
, string
>& mapHeadersRet
, string
& strMessageRet
)
743 mapHeadersRet
.clear();
747 int nStatus
= ReadHTTPStatus(stream
);
750 int nLen
= ReadHTTPHeader(stream
, mapHeadersRet
);
755 vector
<char> vch(nLen
);
756 stream
.read(&vch
[0], nLen
);
757 strMessageRet
= string(vch
.begin(), vch
.end());
762 string
EncodeBase64(string s
)
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());
773 BIO_get_mem_ptr(b64
, &bptr
);
775 string
result(bptr
->data
, bptr
->length
);
781 string
DecodeBase64(string s
)
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());
794 string
result(buffer
);
799 bool HTTPAuthorized(map
<string
, string
>& mapHeaders
)
801 string strAuth
= mapHeaders
["Authorization"];
802 if (strAuth
.substr(0,6) != "Basic ")
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
)
809 string strUser
= strUserPass
.substr(0, nColon
);
810 string strPassword
= strUserPass
.substr(nColon
+1);
811 return (strUser
== mapArgs
["-rpcuser"] && strPassword
== mapArgs
["-rpcpassword"]);
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
)
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
)
833 if (error
.type() != null_type
)
834 reply
.push_back(Pair("result", Value::null
));
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()");
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\"");
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"),
879 GetConfigFile().c_str());
880 CreateThread(Shutdown
, NULL
);
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
);
892 tcp::iostream stream
;
894 vnThreadsRunning
[4]--;
895 acceptor
.accept(*stream
.rdbuf(), peer
);
896 vnThreadsRunning
[4]++;
900 // Shouldn't be possible for anyone else to connect, but just in case
901 if (peer
.address().to_string() != "127.0.0.1")
905 map
<string
, string
> mapHeaders
;
907 ReadHTTP(stream
, mapHeaders
, strRequest
);
909 // Check authorization
910 if (mapHeaders
.count("Authorization") == 0)
912 stream
<< HTTPReply("", 401) << std::flush
;
915 if (!HTTPAuthorized(mapHeaders
))
917 // Deter brute-forcing short passwords
918 if (mapArgs
["-rpcpassword"].size() < 15)
921 stream
<< HTTPReply("", 401) << std::flush
;
922 printf("ThreadRPCServer incorrect password attempt\n");
926 // Handle multiple invocations per request
927 string::iterator begin
= strRequest
.begin();
928 while (skipspaces(begin
), begin
!= strRequest
.end())
930 string::iterator prev
= begin
;
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());
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);
956 string strReply
= JSONRPCReply(result
, Value::null
, id
);
957 stream
<< HTTPReply(strReply
, 200) << std::flush
;
959 catch (std::exception
& e
)
962 string strReply
= JSONRPCReply(Value::null
, e
.what(), id
);
963 stream
<< HTTPReply(strReply
, 500) << std::flush
;
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");
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
;
993 string strRequest
= JSONRPCRequest(strMethod
, params
, 1);
994 string strPost
= HTTPPost(strRequest
, mapRequestHeaders
);
995 stream
<< strPost
<< std::flush
;
998 map
<string
, string
> mapHeaders
;
1000 int nStatus
= ReadHTTP(stream
, mapHeaders
, strReply
);
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");
1010 if (!read_string(strReply
, valReply
))
1011 throw runtime_error("couldn't parse reply from server");
1012 const Object
& reply
= valReply
.get_obj();
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));
1030 template<typename T
>
1031 void ConvertTo(Value
& value
)
1033 if (value
.type() == str_type
)
1035 // reinterpret string as unquoted json value
1037 if (!read_string(value
.get_str(), value2
))
1038 throw runtime_error("type mismatch");
1039 value
= value2
.get_value
<T
>();
1043 value
= value
.get_value
<T
>();
1047 int CommandLineRPC(int argc
, char *argv
[])
1052 while (argc
> 1 && IsSwitchChar(argv
[1][0]))
1058 // Check that the method exists
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()));
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
);
1073 (*(*mi
).second
)(params
, true);
1075 catch (std::exception
& e
)
1082 // Parameters default to strings
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]);
1107 result
= CallRPC(strMethod
, params
);
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
);
1119 fprintf(stdout
, "%s\n", strResult
.c_str());
1124 catch (std::exception
& e
) {
1125 #if defined(__WXMSW__) && wxUSE_GUI
1126 MyMessageBox(strprintf("error: %s\n", e
.what()).c_str(), "Bitcoin", wxOK
);
1128 fprintf(stderr
, "error: %s\n", e
.what());
1131 PrintException(NULL
, "CommandLineRPC()");
1140 int main(int argc
, char *argv
[])
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));
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
);
1160 return CommandLineRPC(argc
, argv
);
1163 catch (std::exception
& e
) {
1164 PrintException(&e
, "main()");
1166 PrintException(NULL
, "main()");