]> Git Repo - VerusCoin.git/blame - rpc.cpp
more addr message error checking
[VerusCoin.git] / rpc.cpp
CommitLineData
22f721db 1// Copyright (c) 2010 Satoshi Nakamoto\r
2// Distributed under the MIT/X11 software license, see the accompanying\r
3// file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
4\r
5#include "headers.h"\r
6#undef printf\r
7#include <boost/asio.hpp>\r
8#include "json/json_spirit_reader_template.h"\r
9#include "json/json_spirit_writer_template.h"\r
10#include "json/json_spirit_utils.h"\r
11#define printf OutputDebugStringF\r
12// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are\r
13// precompiled in headers.h. The problem might be when the pch file goes over\r
14// a certain size around 145MB. If we need access to json_spirit outside this\r
15// file, we could use the compiled json_spirit option.\r
16\r
17using boost::asio::ip::tcp;\r
18using namespace json_spirit;\r
19\r
20void ThreadRPCServer2(void* parg);\r
3b318ed0 21typedef Value(*rpcfn_type)(const Array& params, bool fHelp);\r
22extern map<string, rpcfn_type> mapCallTable;\r
22f721db 23\r
24\r
25\r
26\r
27\r
28\r
29\r
30///\r
7a47324c 31/// Note: This interface may still be subject to change.\r
22f721db 32///\r
33\r
34\r
35\r
3b318ed0 36Value help(const Array& params, bool fHelp)\r
22f721db 37{\r
3b318ed0 38 if (fHelp || params.size() != 0)\r
22f721db 39 throw runtime_error(\r
3b318ed0 40 "help\n"\r
41 "List commands.");\r
42\r
43 string strRet;\r
44 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)\r
45 {\r
46 try\r
47 {\r
48 Array params;\r
49 (*(*mi).second)(params, true);\r
50 }\r
51 catch (std::exception& e)\r
52 {\r
53 // Help text is returned in an exception\r
54 string strHelp = string(e.what());\r
55 if (strHelp.find('\n') != -1)\r
56 strHelp = strHelp.substr(0, strHelp.find('\n'));\r
57 strRet += strHelp + "\n";\r
58 }\r
59 }\r
60 strRet = strRet.substr(0,strRet.size()-1);\r
61 return strRet;\r
62}\r
63\r
64\r
65Value stop(const Array& params, bool fHelp)\r
66{\r
67 if (fHelp || params.size() != 0)\r
68 throw runtime_error(\r
69 "stop\n"\r
22f721db 70 "Stop bitcoin server.");\r
71\r
72 // Shutdown will take long enough that the response should get back\r
73 CreateThread(Shutdown, NULL);\r
74 return "bitcoin server stopping";\r
75}\r
76\r
77\r
3b318ed0 78Value getblockcount(const Array& params, bool fHelp)\r
22f721db 79{\r
3b318ed0 80 if (fHelp || params.size() != 0)\r
22f721db 81 throw runtime_error(\r
3b318ed0 82 "getblockcount\n"\r
22f721db 83 "Returns the number of blocks in the longest block chain.");\r
84\r
85 return nBestHeight + 1;\r
86}\r
87\r
88\r
3b318ed0 89Value getblocknumber(const Array& params, bool fHelp)\r
22f721db 90{\r
3b318ed0 91 if (fHelp || params.size() != 0)\r
22f721db 92 throw runtime_error(\r
3b318ed0 93 "getblocknumber\n"\r
22f721db 94 "Returns the block number of the latest block in the longest block chain.");\r
95\r
96 return nBestHeight;\r
97}\r
98\r
99\r
3b318ed0 100Value getconnectioncount(const Array& params, bool fHelp)\r
30158c77 101{\r
3b318ed0 102 if (fHelp || params.size() != 0)\r
30158c77 103 throw runtime_error(\r
3b318ed0 104 "getconnectioncount\n"\r
30158c77 105 "Returns the number of connections to other nodes.");\r
106\r
107 return (int)vNodes.size();\r
108}\r
109\r
110\r
240f3fbe 111double GetDifficulty()\r
22f721db 112{\r
22f721db 113 // Floating point number that is a multiple of the minimum difficulty,\r
114 // minimum difficulty = 1.0.\r
240f3fbe 115 if (pindexBest == NULL)\r
116 return 1.0;\r
22f721db 117 int nShift = 256 - 32 - 31; // to fit in a uint\r
118 double dMinimum = (CBigNum().SetCompact(bnProofOfWorkLimit.GetCompact()) >> nShift).getuint();\r
119 double dCurrently = (CBigNum().SetCompact(pindexBest->nBits) >> nShift).getuint();\r
120 return dMinimum / dCurrently;\r
121}\r
122\r
3b318ed0 123Value getdifficulty(const Array& params, bool fHelp)\r
240f3fbe 124{\r
3b318ed0 125 if (fHelp || params.size() != 0)\r
240f3fbe 126 throw runtime_error(\r
3b318ed0 127 "getdifficulty\n"\r
240f3fbe 128 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");\r
129\r
130 return GetDifficulty();\r
131}\r
132\r
22f721db 133\r
3b318ed0 134Value getbalance(const Array& params, bool fHelp)\r
30158c77 135{\r
3b318ed0 136 if (fHelp || params.size() != 0)\r
30158c77 137 throw runtime_error(\r
3b318ed0 138 "getbalance\n"\r
30158c77 139 "Returns the server's available balance.");\r
140\r
141 return ((double)GetBalance() / (double)COIN);\r
142}\r
143\r
144\r
3b318ed0 145Value getgenerate(const Array& params, bool fHelp)\r
30158c77 146{\r
3b318ed0 147 if (fHelp || params.size() != 0)\r
30158c77 148 throw runtime_error(\r
3b318ed0 149 "getgenerate\n"\r
30158c77 150 "Returns true or false.");\r
151\r
152 return (bool)fGenerateBitcoins;\r
153}\r
154\r
155\r
3b318ed0 156Value setgenerate(const Array& params, bool fHelp)\r
30158c77 157{\r
3b318ed0 158 if (fHelp || params.size() < 1 || params.size() > 2)\r
30158c77 159 throw runtime_error(\r
160 "setgenerate <generate> [genproclimit]\n"\r
161 "<generate> is true or false to turn generation on or off.\n"\r
162 "Generation is limited to [genproclimit] processors, -1 is unlimited.");\r
163\r
164 bool fGenerate = true;\r
165 if (params.size() > 0)\r
166 fGenerate = params[0].get_bool();\r
167\r
168 if (params.size() > 1)\r
169 {\r
170 int nGenProcLimit = params[1].get_int();\r
171 fLimitProcessors = (nGenProcLimit != -1);\r
172 CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors);\r
173 if (nGenProcLimit != -1)\r
174 CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);\r
175 }\r
176\r
177 GenerateBitcoins(fGenerate);\r
178 return Value::null;\r
179}\r
180\r
181\r
3b318ed0 182Value getinfo(const Array& params, bool fHelp)\r
30158c77 183{\r
3b318ed0 184 if (fHelp || params.size() != 0)\r
30158c77 185 throw runtime_error(\r
3b318ed0 186 "getinfo");\r
30158c77 187\r
188 Object obj;\r
189 obj.push_back(Pair("balance", (double)GetBalance() / (double)COIN));\r
190 obj.push_back(Pair("blocks", (int)nBestHeight + 1));\r
191 obj.push_back(Pair("connections", (int)vNodes.size()));\r
192 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));\r
193 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));\r
194 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));\r
240f3fbe 195 obj.push_back(Pair("difficulty", (double)GetDifficulty()));\r
30158c77 196 return obj;\r
197}\r
198\r
199\r
3b318ed0 200Value getnewaddress(const Array& params, bool fHelp)\r
22f721db 201{\r
3b318ed0 202 if (fHelp || params.size() > 1)\r
22f721db 203 throw runtime_error(\r
204 "getnewaddress [label]\n"\r
205 "Returns a new bitcoin address for receiving payments. "\r
206 "If [label] is specified (recommended), it is added to the address book "\r
207 "so payments received with the address will be labeled.");\r
208\r
209 // Parse the label first so we don't generate a key if there's an error\r
210 string strLabel;\r
211 if (params.size() > 0)\r
212 strLabel = params[0].get_str();\r
213\r
214 // Generate a new key that is added to wallet\r
215 string strAddress = PubKeyToAddress(GenerateNewKey());\r
216\r
30158c77 217 SetAddressBookName(strAddress, strLabel);\r
22f721db 218 return strAddress;\r
219}\r
220\r
221\r
3b318ed0 222Value setlabel(const Array& params, bool fHelp)\r
7a47324c 223{\r
3b318ed0 224 if (fHelp || params.size() < 1 || params.size() > 2)\r
7a47324c 225 throw runtime_error(\r
226 "setlabel <bitcoinaddress> <label>\n"\r
227 "Sets the label associated with the given address.");\r
228\r
229 string strAddress = params[0].get_str();\r
230 string strLabel;\r
231 if (params.size() > 1)\r
232 strLabel = params[1].get_str();\r
233\r
234 SetAddressBookName(strAddress, strLabel);\r
235 return Value::null;\r
236}\r
237\r
238\r
3b318ed0 239Value getlabel(const Array& params, bool fHelp)\r
7a47324c 240{\r
3b318ed0 241 if (fHelp || params.size() != 1)\r
7a47324c 242 throw runtime_error(\r
243 "getlabel <bitcoinaddress>\n"\r
244 "Returns the label associated with the given address.");\r
245\r
246 string strAddress = params[0].get_str();\r
247\r
248 string strLabel;\r
249 CRITICAL_BLOCK(cs_mapAddressBook)\r
250 {\r
251 map<string, string>::iterator mi = mapAddressBook.find(strAddress);\r
252 if (mi != mapAddressBook.end() && !(*mi).second.empty())\r
253 strLabel = (*mi).second;\r
254 }\r
255 return strLabel;\r
256}\r
257\r
258\r
3b318ed0 259Value getaddressesbylabel(const Array& params, bool fHelp)\r
7a47324c 260{\r
3b318ed0 261 if (fHelp || params.size() != 1)\r
7a47324c 262 throw runtime_error(\r
263 "getaddressesbylabel <label>\n"\r
264 "Returns the list of addresses with the given label.");\r
265\r
266 string strLabel = params[0].get_str();\r
267\r
268 // Find all addresses that have the given label\r
269 Array ret;\r
270 CRITICAL_BLOCK(cs_mapAddressBook)\r
271 {\r
272 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
273 {\r
274 const string& strAddress = item.first;\r
275 const string& strName = item.second;\r
276 if (strName == strLabel)\r
277 {\r
278 // We're only adding valid bitcoin addresses and not ip addresses\r
279 CScript scriptPubKey;\r
280 if (scriptPubKey.SetBitcoinAddress(strAddress))\r
281 ret.push_back(strAddress);\r
282 }\r
283 }\r
284 }\r
285 return ret;\r
286}\r
287\r
288\r
3b318ed0 289Value sendtoaddress(const Array& params, bool fHelp)\r
22f721db 290{\r
3b318ed0 291 if (fHelp || params.size() < 2 || params.size() > 4)\r
22f721db 292 throw runtime_error(\r
293 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"\r
294 "<amount> is a real and is rounded to the nearest 0.01");\r
295\r
296 string strAddress = params[0].get_str();\r
297\r
298 // Amount\r
299 if (params[1].get_real() <= 0.0 || params[1].get_real() > 21000000.0)\r
300 throw runtime_error("Invalid amount");\r
301 int64 nAmount = roundint64(params[1].get_real() * 100.00) * CENT;\r
302\r
303 // Wallet comments\r
304 CWalletTx wtx;\r
305 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())\r
306 wtx.mapValue["message"] = params[2].get_str();\r
307 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())\r
308 wtx.mapValue["to"] = params[3].get_str();\r
309\r
310 string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx);\r
311 if (strError != "")\r
312 throw runtime_error(strError);\r
313 return "sent";\r
314}\r
315\r
316\r
3b318ed0 317Value listtransactions(const Array& params, bool fHelp)\r
22f721db 318{\r
3b318ed0 319 if (fHelp || params.size() > 2)\r
22f721db 320 throw runtime_error(\r
321 "listtransactions [count=10] [includegenerated=false]\n"\r
322 "Returns up to [count] most recent transactions.");\r
323\r
324 int64 nCount = 10;\r
325 if (params.size() > 0)\r
326 nCount = params[0].get_int64();\r
327 bool fGenerated = false;\r
328 if (params.size() > 1)\r
329 fGenerated = params[1].get_bool();\r
330\r
331 Array ret;\r
332 //// not finished\r
333 ret.push_back("not implemented yet");\r
334 return ret;\r
335}\r
336\r
337\r
3b318ed0 338Value getreceivedbyaddress(const Array& params, bool fHelp)\r
22f721db 339{\r
3b318ed0 340 if (fHelp || params.size() < 1 || params.size() > 2)\r
22f721db 341 throw runtime_error(\r
7a47324c 342 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"\r
297a50a0 343 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");\r
22f721db 344\r
345 // Bitcoin address\r
346 string strAddress = params[0].get_str();\r
347 CScript scriptPubKey;\r
348 if (!scriptPubKey.SetBitcoinAddress(strAddress))\r
349 throw runtime_error("Invalid bitcoin address");\r
7a47324c 350 if (!IsMine(scriptPubKey))\r
351 return (double)0.0;\r
22f721db 352\r
353 // Minimum confirmations\r
354 int nMinDepth = 1;\r
355 if (params.size() > 1)\r
356 nMinDepth = params[1].get_int();\r
357\r
358 // Tally\r
359 int64 nAmount = 0;\r
360 CRITICAL_BLOCK(cs_mapWallet)\r
361 {\r
362 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
363 {\r
364 const CWalletTx& wtx = (*it).second;\r
365 if (wtx.IsCoinBase() || !wtx.IsFinal())\r
366 continue;\r
367\r
368 foreach(const CTxOut& txout, wtx.vout)\r
369 if (txout.scriptPubKey == scriptPubKey)\r
370 if (wtx.GetDepthInMainChain() >= nMinDepth)\r
371 nAmount += txout.nValue;\r
372 }\r
373 }\r
374\r
375 return (double)nAmount / (double)COIN;\r
376}\r
377\r
378\r
3b318ed0 379Value getreceivedbylabel(const Array& params, bool fHelp)\r
7a47324c 380{\r
3b318ed0 381 if (fHelp || params.size() < 1 || params.size() > 2)\r
7a47324c 382 throw runtime_error(\r
383 "getreceivedbylabel <label> [minconf=1]\n"\r
384 "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.");\r
385\r
386 // Get the set of pub keys that have the label\r
387 string strLabel = params[0].get_str();\r
388 set<CScript> setPubKey;\r
389 CRITICAL_BLOCK(cs_mapAddressBook)\r
390 {\r
391 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
392 {\r
393 const string& strAddress = item.first;\r
394 const string& strName = item.second;\r
395 if (strName == strLabel)\r
396 {\r
397 // We're only counting our own valid bitcoin addresses and not ip addresses\r
398 CScript scriptPubKey;\r
399 if (scriptPubKey.SetBitcoinAddress(strAddress))\r
400 if (IsMine(scriptPubKey))\r
401 setPubKey.insert(scriptPubKey);\r
402 }\r
403 }\r
404 }\r
405\r
406 // Minimum confirmations\r
407 int nMinDepth = 1;\r
408 if (params.size() > 1)\r
409 nMinDepth = params[1].get_int();\r
410\r
411 // Tally\r
412 int64 nAmount = 0;\r
413 CRITICAL_BLOCK(cs_mapWallet)\r
414 {\r
415 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
416 {\r
417 const CWalletTx& wtx = (*it).second;\r
418 if (wtx.IsCoinBase() || !wtx.IsFinal())\r
419 continue;\r
420\r
421 foreach(const CTxOut& txout, wtx.vout)\r
422 if (setPubKey.count(txout.scriptPubKey))\r
423 if (wtx.GetDepthInMainChain() >= nMinDepth)\r
424 nAmount += txout.nValue;\r
425 }\r
426 }\r
427\r
428 return (double)nAmount / (double)COIN;\r
429}\r
430\r
431\r
22f721db 432struct tallyitem\r
433{\r
434 int64 nAmount;\r
435 int nConf;\r
436 tallyitem()\r
437 {\r
438 nAmount = 0;\r
439 nConf = INT_MAX;\r
440 }\r
441};\r
442\r
7a47324c 443Value ListReceived(const Array& params, bool fByLabels)\r
22f721db 444{\r
22f721db 445 // Minimum confirmations\r
446 int nMinDepth = 1;\r
447 if (params.size() > 0)\r
448 nMinDepth = params[0].get_int();\r
449\r
7a47324c 450 // Whether to include empty accounts\r
451 bool fIncludeEmpty = false;\r
452 if (params.size() > 1)\r
453 fIncludeEmpty = params[1].get_bool();\r
454\r
22f721db 455 // Tally\r
456 map<uint160, tallyitem> mapTally;\r
457 CRITICAL_BLOCK(cs_mapWallet)\r
458 {\r
459 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
460 {\r
461 const CWalletTx& wtx = (*it).second;\r
462 if (wtx.IsCoinBase() || !wtx.IsFinal())\r
463 continue;\r
464\r
465 int nDepth = wtx.GetDepthInMainChain();\r
30158c77 466 if (nDepth < nMinDepth)\r
467 continue;\r
468\r
30158c77 469 foreach(const CTxOut& txout, wtx.vout)\r
22f721db 470 {\r
7a47324c 471 // Only counting our own bitcoin addresses and not ip addresses\r
30158c77 472 uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160();\r
7a47324c 473 if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine\r
30158c77 474 continue;\r
475\r
476 tallyitem& item = mapTally[hash160];\r
477 item.nAmount += txout.nValue;\r
478 item.nConf = min(item.nConf, nDepth);\r
22f721db 479 }\r
480 }\r
481 }\r
482\r
483 // Reply\r
484 Array ret;\r
7a47324c 485 map<string, tallyitem> mapLabelTally;\r
22f721db 486 CRITICAL_BLOCK(cs_mapAddressBook)\r
487 {\r
7a47324c 488 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
22f721db 489 {\r
7a47324c 490 const string& strAddress = item.first;\r
491 const string& strLabel = item.second;\r
492 uint160 hash160;\r
493 if (!AddressToHash160(strAddress, hash160))\r
494 continue;\r
495 map<uint160, tallyitem>::iterator it = mapTally.find(hash160);\r
496 if (it == mapTally.end() && !fIncludeEmpty)\r
497 continue;\r
498\r
499 int64 nAmount = 0;\r
500 int nConf = INT_MAX;\r
501 if (it != mapTally.end())\r
502 {\r
503 nAmount = (*it).second.nAmount;\r
504 nConf = (*it).second.nConf;\r
505 }\r
506\r
507 if (fByLabels)\r
508 {\r
509 tallyitem& item = mapLabelTally[strLabel];\r
510 item.nAmount += nAmount;\r
511 item.nConf = min(item.nConf, nConf);\r
512 }\r
513 else\r
514 {\r
515 Object obj;\r
516 obj.push_back(Pair("address", strAddress));\r
517 obj.push_back(Pair("label", strLabel));\r
518 obj.push_back(Pair("amount", (double)nAmount / (double)COIN));\r
519 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));\r
520 ret.push_back(obj);\r
521 }\r
522 }\r
523 }\r
22f721db 524\r
7a47324c 525 if (fByLabels)\r
526 {\r
527 for (map<string, tallyitem>::iterator it = mapLabelTally.begin(); it != mapLabelTally.end(); ++it)\r
528 {\r
529 int64 nAmount = (*it).second.nAmount;\r
530 int nConf = (*it).second.nConf;\r
22f721db 531 Object obj;\r
7a47324c 532 obj.push_back(Pair("label", (*it).first));\r
533 obj.push_back(Pair("amount", (double)nAmount / (double)COIN));\r
534 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));\r
22f721db 535 ret.push_back(obj);\r
536 }\r
537 }\r
7a47324c 538\r
22f721db 539 return ret;\r
540}\r
541\r
3b318ed0 542Value listreceivedbyaddress(const Array& params, bool fHelp)\r
7a47324c 543{\r
3b318ed0 544 if (fHelp || params.size() > 2)\r
7a47324c 545 throw runtime_error(\r
546 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"\r
547 "[minconf] is the minimum number of confirmations before payments are included.\n"\r
548 "[includeempty] whether to include addresses that haven't received any payments.\n"\r
549 "Returns an array of objects containing:\n"\r
550 " \"address\" : receiving address\n"\r
551 " \"label\" : the label of the receiving address\n"\r
552 " \"amount\" : total amount received by the address\n"\r
553 " \"confirmations\" : number of confirmations of the most recent transaction included");\r
554\r
555 return ListReceived(params, false);\r
556}\r
557\r
3b318ed0 558Value listreceivedbylabel(const Array& params, bool fHelp)\r
7a47324c 559{\r
3b318ed0 560 if (fHelp || params.size() > 2)\r
7a47324c 561 throw runtime_error(\r
562 "listreceivedbylabel [minconf=1] [includeempty=false]\n"\r
563 "[minconf] is the minimum number of confirmations before payments are included.\n"\r
564 "[includeempty] whether to include labels that haven't received any payments.\n"\r
565 "Returns an array of objects containing:\n"\r
566 " \"label\" : the label of the receiving addresses\n"\r
567 " \"amount\" : total amount received by addresses with this label\n"\r
568 " \"confirmations\" : number of confirmations of the most recent transaction included");\r
569\r
570 return ListReceived(params, true);\r
571}\r
572\r
573\r
574\r
575\r
576\r
577\r
578\r
22f721db 579\r
580\r
581\r
582\r
583\r
584\r
585//\r
586// Call Table\r
587//\r
588\r
22f721db 589pair<string, rpcfn_type> pCallTable[] =\r
590{\r
3b318ed0 591 make_pair("help", &help),\r
7a47324c 592 make_pair("stop", &stop),\r
593 make_pair("getblockcount", &getblockcount),\r
594 make_pair("getblocknumber", &getblocknumber),\r
595 make_pair("getconnectioncount", &getconnectioncount),\r
596 make_pair("getdifficulty", &getdifficulty),\r
597 make_pair("getbalance", &getbalance),\r
598 make_pair("getgenerate", &getgenerate),\r
599 make_pair("setgenerate", &setgenerate),\r
600 make_pair("getinfo", &getinfo),\r
601 make_pair("getnewaddress", &getnewaddress),\r
602 make_pair("setlabel", &setlabel),\r
603 make_pair("getlabel", &getlabel),\r
604 make_pair("getaddressesbylabel", &getaddressesbylabel),\r
605 make_pair("sendtoaddress", &sendtoaddress),\r
606 make_pair("listtransactions", &listtransactions),\r
607 make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress\r
608 make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress\r
609 make_pair("getreceivedbyaddress", &getreceivedbyaddress),\r
610 make_pair("getreceivedbylabel", &getreceivedbylabel),\r
611 make_pair("listreceivedbyaddress", &listreceivedbyaddress),\r
612 make_pair("listreceivedbylabel", &listreceivedbylabel),\r
22f721db 613};\r
614map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));\r
615\r
616\r
617\r
618\r
619//\r
620// HTTP protocol\r
621//\r
622// This ain't Apache. We're just using HTTP header for the length field\r
623// and to be compatible with other JSON-RPC implementations.\r
624//\r
625\r
626string HTTPPost(const string& strMsg)\r
627{\r
628 return strprintf(\r
629 "POST / HTTP/1.1\r\n"\r
630 "User-Agent: json-rpc/1.0\r\n"\r
631 "Host: 127.0.0.1\r\n"\r
632 "Content-Type: application/json\r\n"\r
633 "Content-Length: %d\r\n"\r
634 "Accept: application/json\r\n"\r
635 "\r\n"\r
636 "%s",\r
637 strMsg.size(),\r
638 strMsg.c_str());\r
639}\r
640\r
641string HTTPReply(const string& strMsg, int nStatus=200)\r
642{\r
643 string strStatus;\r
644 if (nStatus == 200) strStatus = "OK";\r
645 if (nStatus == 500) strStatus = "Internal Server Error";\r
646 return strprintf(\r
647 "HTTP/1.1 %d %s\r\n"\r
648 "Connection: close\r\n"\r
649 "Content-Length: %d\r\n"\r
650 "Content-Type: application/json\r\n"\r
651 "Date: Sat, 08 Jul 2006 12:04:08 GMT\r\n"\r
652 "Server: json-rpc/1.0\r\n"\r
653 "\r\n"\r
654 "%s",\r
655 nStatus,\r
656 strStatus.c_str(),\r
657 strMsg.size(),\r
658 strMsg.c_str());\r
659}\r
660\r
661int ReadHTTPHeader(tcp::iostream& stream)\r
662{\r
663 int nLen = 0;\r
664 loop\r
665 {\r
666 string str;\r
667 std::getline(stream, str);\r
668 if (str.empty() || str == "\r")\r
669 break;\r
670 if (str.substr(0,15) == "Content-Length:")\r
671 nLen = atoi(str.substr(15));\r
672 }\r
673 return nLen;\r
674}\r
675\r
676inline string ReadHTTP(tcp::iostream& stream)\r
677{\r
678 // Read header\r
679 int nLen = ReadHTTPHeader(stream);\r
680 if (nLen <= 0)\r
681 return string();\r
682\r
683 // Read message\r
684 vector<char> vch(nLen);\r
685 stream.read(&vch[0], nLen);\r
686 return string(vch.begin(), vch.end());\r
687}\r
688\r
689\r
690\r
691//\r
692// JSON-RPC protocol\r
693//\r
694// http://json-rpc.org/wiki/specification\r
695// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx\r
696//\r
697\r
698string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)\r
699{\r
700 Object request;\r
701 request.push_back(Pair("method", strMethod));\r
702 request.push_back(Pair("params", params));\r
703 request.push_back(Pair("id", id));\r
704 return write_string(Value(request), false) + "\n";\r
705}\r
706\r
707string JSONRPCReply(const Value& result, const Value& error, const Value& id)\r
708{\r
709 Object reply;\r
710 if (error.type() != null_type)\r
711 reply.push_back(Pair("result", Value::null));\r
712 else\r
713 reply.push_back(Pair("result", result));\r
714 reply.push_back(Pair("error", error));\r
715 reply.push_back(Pair("id", id));\r
716 return write_string(Value(reply), false) + "\n";\r
717}\r
718\r
719\r
720\r
721\r
722void ThreadRPCServer(void* parg)\r
723{\r
724 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));\r
725 try\r
726 {\r
727 vnThreadsRunning[4]++;\r
728 ThreadRPCServer2(parg);\r
729 vnThreadsRunning[4]--;\r
730 }\r
731 catch (std::exception& e) {\r
732 vnThreadsRunning[4]--;\r
733 PrintException(&e, "ThreadRPCServer()");\r
734 } catch (...) {\r
735 vnThreadsRunning[4]--;\r
736 PrintException(NULL, "ThreadRPCServer()");\r
737 }\r
738 printf("ThreadRPCServer exiting\n");\r
739}\r
740\r
741void ThreadRPCServer2(void* parg)\r
742{\r
743 printf("ThreadRPCServer started\n");\r
744\r
745 // Bind to loopback 127.0.0.1 so the socket can only be accessed locally\r
746 boost::asio::io_service io_service;\r
747 tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 8332);\r
748 tcp::acceptor acceptor(io_service, endpoint);\r
749\r
750 loop\r
751 {\r
752 // Accept connection\r
753 tcp::iostream stream;\r
754 tcp::endpoint peer;\r
755 vnThreadsRunning[4]--;\r
756 acceptor.accept(*stream.rdbuf(), peer);\r
757 vnThreadsRunning[4]++;\r
758 if (fShutdown)\r
759 return;\r
760\r
761 // Shouldn't be possible for anyone else to connect, but just in case\r
762 if (peer.address().to_string() != "127.0.0.1")\r
763 continue;\r
764\r
765 // Receive request\r
766 string strRequest = ReadHTTP(stream);\r
767 printf("ThreadRPCServer request=%s", strRequest.c_str());\r
768\r
769 // Handle multiple invocations per request\r
770 string::iterator begin = strRequest.begin();\r
771 while (skipspaces(begin), begin != strRequest.end())\r
772 {\r
773 string::iterator prev = begin;\r
774 Value id;\r
775 try\r
776 {\r
777 // Parse request\r
778 Value valRequest;\r
779 if (!read_range(begin, strRequest.end(), valRequest))\r
780 throw runtime_error("Parse error.");\r
781 const Object& request = valRequest.get_obj();\r
782 if (find_value(request, "method").type() != str_type ||\r
783 find_value(request, "params").type() != array_type)\r
784 throw runtime_error("Invalid request.");\r
785\r
786 string strMethod = find_value(request, "method").get_str();\r
787 const Array& params = find_value(request, "params").get_array();\r
788 id = find_value(request, "id");\r
789\r
790 // Execute\r
791 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);\r
792 if (mi == mapCallTable.end())\r
793 throw runtime_error("Method not found.");\r
3b318ed0 794 Value result = (*(*mi).second)(params, false);\r
22f721db 795\r
796 // Send reply\r
797 string strReply = JSONRPCReply(result, Value::null, id);\r
798 stream << HTTPReply(strReply, 200) << std::flush;\r
799 }\r
800 catch (std::exception& e)\r
801 {\r
802 // Send error reply\r
803 string strReply = JSONRPCReply(Value::null, e.what(), id);\r
804 stream << HTTPReply(strReply, 500) << std::flush;\r
805 }\r
806 if (begin == prev)\r
807 break;\r
808 }\r
809 }\r
810}\r
811\r
812\r
813\r
814\r
815Value CallRPC(const string& strMethod, const Array& params)\r
816{\r
817 // Connect to localhost\r
818 tcp::iostream stream("127.0.0.1", "8332");\r
819 if (stream.fail())\r
7271c7ff 820 throw runtime_error("couldn't connect to server");\r
22f721db 821\r
822 // Send request\r
823 string strRequest = JSONRPCRequest(strMethod, params, 1);\r
824 stream << HTTPPost(strRequest) << std::flush;\r
825\r
826 // Receive reply\r
827 string strReply = ReadHTTP(stream);\r
828 if (strReply.empty())\r
829 throw runtime_error("no response from server");\r
830\r
831 // Parse reply\r
832 Value valReply;\r
833 if (!read_string(strReply, valReply))\r
834 throw runtime_error("couldn't parse reply from server");\r
835 const Object& reply = valReply.get_obj();\r
836 if (reply.empty())\r
837 throw runtime_error("expected reply to have result, error and id properties");\r
838\r
839 const Value& result = find_value(reply, "result");\r
840 const Value& error = find_value(reply, "error");\r
841 const Value& id = find_value(reply, "id");\r
842\r
843 if (error.type() == str_type)\r
844 throw runtime_error(error.get_str());\r
845 else if (error.type() != null_type)\r
846 throw runtime_error(write_string(error, false));\r
847 return result;\r
848}\r
849\r
850\r
851\r
852\r
853template<typename T>\r
854void ConvertTo(Value& value)\r
855{\r
856 if (value.type() == str_type)\r
857 {\r
858 // reinterpret string as unquoted json value\r
859 Value value2;\r
860 if (!read_string(value.get_str(), value2))\r
861 throw runtime_error("type mismatch");\r
862 value = value2.get_value<T>();\r
863 }\r
864 else\r
865 {\r
866 value = value.get_value<T>();\r
867 }\r
868}\r
869\r
870int CommandLineRPC(int argc, char *argv[])\r
871{\r
872 try\r
873 {\r
874 // Check that method exists\r
875 if (argc < 2)\r
876 throw runtime_error("too few parameters");\r
877 string strMethod = argv[1];\r
878 if (!mapCallTable.count(strMethod))\r
879 throw runtime_error(strprintf("unknown command: %s", strMethod.c_str()));\r
880\r
3b318ed0 881 Value result;\r
882 if (argc == 3 && strcmp(argv[2], "-?") == 0)\r
883 {\r
884 // Call help locally, help text is returned in an exception\r
885 try\r
886 {\r
887 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);\r
888 Array params;\r
889 (*(*mi).second)(params, true);\r
890 }\r
891 catch (std::exception& e)\r
892 {\r
893 result = e.what();\r
894 }\r
895 }\r
896 else\r
897 {\r
898 // Parameters default to strings\r
899 Array params;\r
900 for (int i = 2; i < argc; i++)\r
901 params.push_back(argv[i]);\r
902 int n = params.size();\r
903\r
904 //\r
905 // Special case non-string parameter types\r
906 //\r
907 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);\r
908 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);\r
909 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);\r
910 if (strMethod == "listtransactions" && n > 0) ConvertTo<boost::int64_t>(params[0]);\r
911 if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);\r
912 if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated\r
913 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);\r
914 if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]);\r
915 if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated\r
916 if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]);\r
917 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);\r
918 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);\r
919 if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]);\r
920 if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]);\r
921\r
922 // Execute\r
923 result = CallRPC(strMethod, params);\r
924 }\r
22f721db 925\r
926 // Print result\r
927 string strResult = (result.type() == str_type ? result.get_str() : write_string(result, true));\r
928 if (result.type() != null_type)\r
929 {\r
5eede9d4 930 if (fWindows && fGUI)\r
22f721db 931 // Windows GUI apps can't print to command line,\r
5eede9d4 932 // so settle for a message box yuck\r
933 MyMessageBox(strResult.c_str(), "Bitcoin", wxOK);\r
22f721db 934 else\r
935 fprintf(stdout, "%s\n", strResult.c_str());\r
936 }\r
937 return 0;\r
938 }\r
939 catch (std::exception& e) {\r
5eede9d4 940 if (fWindows && fGUI)\r
941 MyMessageBox(strprintf("error: %s\n", e.what()).c_str(), "Bitcoin", wxOK);\r
22f721db 942 else\r
943 fprintf(stderr, "error: %s\n", e.what());\r
944 } catch (...) {\r
945 PrintException(NULL, "CommandLineRPC()");\r
946 }\r
947 return 1;\r
948}\r
949\r
950\r
951\r
952\r
953#ifdef TEST\r
954int main(int argc, char *argv[])\r
955{\r
956#ifdef _MSC_VER\r
957 // Turn off microsoft heap dump noise\r
958 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);\r
959 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));\r
960#endif\r
961 setbuf(stdin, NULL);\r
962 setbuf(stdout, NULL);\r
963 setbuf(stderr, NULL);\r
964\r
965 try\r
966 {\r
967 if (argc >= 2 && string(argv[1]) == "-server")\r
968 {\r
969 printf("server ready\n");\r
970 ThreadRPCServer(NULL);\r
971 }\r
972 else\r
973 {\r
974 return CommandLineRPC(argc, argv);\r
975 }\r
976 }\r
977 catch (std::exception& e) {\r
978 PrintException(&e, "main()");\r
979 } catch (...) {\r
980 PrintException(NULL, "main()");\r
981 }\r
982 return 0;\r
983}\r
984#endif\r
This page took 0.145471 seconds and 4 git commands to generate.