Projects : bitcoin : bitcoin_boost_prune_built_libs

bitcoin/src/bitcoinrpc.cpp

Dir - Raw

1// Copyright (c) 2010 Satoshi Nakamoto
2// Copyright (c) 2009-2012 The Bitcoin developers
3// Distributed under the MIT/X11 software license, see the accompanying
4// file license.txt or http://www.opensource.org/licenses/mit-license.php.
5
6#include "headers.h"
7#include "db.h"
8#include "net.h"
9#include "init.h"
10#include "util.h"
11#undef printf
12#include <boost/asio.hpp>
13// These two comment lines are to preserve line numbering and thus identical disassembly in a pruning of unused boost includes (the line number dependence there comes from ENTER_CRITICAL_SECTION).
14// They can be freely removed at a later stage.
15#include <boost/algorithm/string.hpp>
16#include "json/json_spirit_reader_template.h"
17#include "json/json_spirit_writer_template.h"
18#include "json/json_spirit_utils.h"
19#define printf OutputDebugStringF
20// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
21// precompiled in headers.h. The problem might be when the pch file goes over
22// a certain size around 145MB. If we need access to json_spirit outside this
23// file, we could use the compiled json_spirit option.
24
25// v0.5.4 RELEASE (keccak)
26
27using namespace std;
28using namespace boost;
29using namespace boost::asio;
30using namespace json_spirit;
31
32void ThreadRPCServer2(void* parg);
33typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
34extern map<string, rpcfn_type> mapCallTable;
35
36static std::string strRPCUserColonPass;
37
38static int64 nWalletUnlockTime;
39static CCriticalSection cs_nWalletUnlockTime;
40
41
42Object JSONRPCError(int code, const string& message)
43{
44 Object error;
45 error.push_back(Pair("code", code));
46 error.push_back(Pair("message", message));
47 return error;
48}
49
50
51void PrintConsole(const std::string &format, ...)
52{
53 char buffer[50000];
54 int limit = sizeof(buffer);
55 va_list arg_ptr;
56 va_start(arg_ptr, format);
57 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
58 va_end(arg_ptr);
59 if (ret < 0 || ret >= limit)
60 {
61 ret = limit - 1;
62 buffer[limit-1] = 0;
63 }
64 printf("%s", buffer);
65 fprintf(stdout, "%s", buffer);
66}
67
68
69int64 AmountFromValue(const Value& value)
70{
71 double dAmount = value.get_real();
72 if (dAmount <= 0.0 || dAmount > 21000000.0)
73 throw JSONRPCError(-3, "Invalid amount");
74 int64 nAmount = roundint64(dAmount * COIN);
75 if (!MoneyRange(nAmount))
76 throw JSONRPCError(-3, "Invalid amount");
77 return nAmount;
78}
79
80Value ValueFromAmount(int64 amount)
81{
82 return (double)amount / (double)COIN;
83}
84
85void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
86{
87 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
88 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
89 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
90 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
91 entry.push_back(Pair(item.first, item.second));
92}
93
94string AccountFromValue(const Value& value)
95{
96 string strAccount = value.get_str();
97 if (strAccount == "*")
98 throw JSONRPCError(-11, "Invalid account name");
99 return strAccount;
100}
101
102
103
104///
105/// Note: This interface may still be subject to change.
106///
107
108
109Value help(const Array& params, bool fHelp)
110{
111 if (fHelp || params.size() > 1)
112 throw runtime_error(
113 "help [command]\n"
114 "List commands, or get help for a command.");
115
116 string strCommand;
117 if (params.size() > 0)
118 strCommand = params[0].get_str();
119
120 string strRet;
121 set<rpcfn_type> setDone;
122 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
123 {
124 string strMethod = (*mi).first;
125 // We already filter duplicates, but these deprecated screw up the sort order
126 if (strMethod == "getamountreceived" ||
127 strMethod == "getallreceived" ||
128 strMethod == "getblocknumber" || // deprecated
129 (strMethod.find("label") != string::npos))
130 continue;
131 if (strCommand != "" && strMethod != strCommand)
132 continue;
133 try
134 {
135 Array params;
136 rpcfn_type pfn = (*mi).second;
137 if (setDone.insert(pfn).second)
138 (*pfn)(params, true);
139 }
140 catch (std::exception& e)
141 {
142 // Help text is returned in an exception
143 string strHelp = string(e.what());
144 if (strCommand == "")
145 if (strHelp.find('\n') != -1)
146 strHelp = strHelp.substr(0, strHelp.find('\n'));
147 strRet += strHelp + "\n";
148 }
149 }
150 if (strRet == "")
151 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
152 strRet = strRet.substr(0,strRet.size()-1);
153 return strRet;
154}
155
156
157Value stop(const Array& params, bool fHelp)
158{
159 if (fHelp || params.size() != 0)
160 throw runtime_error(
161 "stop\n"
162 "Stop bitcoin server.");
163 // Shutdown will take long enough that the response should get back
164 CreateThread(Shutdown, NULL);
165 return "bitcoin server stopping";
166}
167
168
169Value getblockcount(const Array& params, bool fHelp)
170{
171 if (fHelp || params.size() != 0)
172 throw runtime_error(
173 "getblockcount\n"
174 "Returns the number of blocks in the longest block chain.");
175
176 return nBestHeight;
177}
178
179
180// deprecated
181Value getblocknumber(const Array& params, bool fHelp)
182{
183 if (fHelp || params.size() != 0)
184 throw runtime_error(
185 "getblocknumber\n"
186 "Deprecated. Use getblockcount.");
187
188 return nBestHeight;
189}
190
191
192Value getconnectioncount(const Array& params, bool fHelp)
193{
194 if (fHelp || params.size() != 0)
195 throw runtime_error(
196 "getconnectioncount\n"
197 "Returns the number of connections to other nodes.");
198
199 return (int)vNodes.size();
200}
201
202
203double GetDifficulty()
204{
205 // Floating point number that is a multiple of the minimum difficulty,
206 // minimum difficulty = 1.0.
207
208 if (pindexBest == NULL)
209 return 1.0;
210 int nShift = (pindexBest->nBits >> 24) & 0xff;
211
212 double dDiff =
213 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
214
215 while (nShift < 29)
216 {
217 dDiff *= 256.0;
218 nShift++;
219 }
220 while (nShift > 29)
221 {
222 dDiff /= 256.0;
223 nShift--;
224 }
225
226 return dDiff;
227}
228
229Value getdifficulty(const Array& params, bool fHelp)
230{
231 if (fHelp || params.size() != 0)
232 throw runtime_error(
233 "getdifficulty\n"
234 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
235
236 return GetDifficulty();
237}
238
239
240Value getgenerate(const Array& params, bool fHelp)
241{
242 if (fHelp || params.size() != 0)
243 throw runtime_error(
244 "getgenerate\n"
245 "Returns true or false.");
246
247 return (bool)fGenerateBitcoins;
248}
249
250
251Value setgenerate(const Array& params, bool fHelp)
252{
253 if (fHelp || params.size() < 1 || params.size() > 2)
254 throw runtime_error(
255 "setgenerate <generate> [genproclimit]\n"
256 "<generate> is true or false to turn generation on or off.\n"
257 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
258
259 bool fGenerate = true;
260 if (params.size() > 0)
261 fGenerate = params[0].get_bool();
262
263 if (params.size() > 1)
264 {
265 int nGenProcLimit = params[1].get_int();
266 fLimitProcessors = (nGenProcLimit != -1);
267 WriteSetting("fLimitProcessors", fLimitProcessors);
268 if (nGenProcLimit != -1)
269 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
270 if (nGenProcLimit == 0)
271 fGenerate = false;
272 }
273
274 GenerateBitcoins(fGenerate, pwalletMain);
275 return Value::null;
276}
277
278
279Value gethashespersec(const Array& params, bool fHelp)
280{
281 if (fHelp || params.size() != 0)
282 throw runtime_error(
283 "gethashespersec\n"
284 "Returns a recent hashes per second performance measurement while generating.");
285
286 if (GetTimeMillis() - nHPSTimerStart > 8000)
287 return (boost::int64_t)0;
288 return (boost::int64_t)dHashesPerSec;
289}
290
291
292Value getinfo(const Array& params, bool fHelp)
293{
294 if (fHelp || params.size() != 0)
295 throw runtime_error(
296 "getinfo\n"
297 "Returns an object containing various state info.");
298
299 Object obj;
300 obj.push_back(Pair("version", (int)VERSION));
301 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
302 obj.push_back(Pair("blocks", (int)nBestHeight));
303 obj.push_back(Pair("connections", (int)vNodes.size()));
304 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
305 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
306 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
307 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
308 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
309 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
310 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
311 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
312 if (pwalletMain->IsCrypted())
313 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
314 obj.push_back(Pair("errors", GetWarnings("statusbar")));
315 return obj;
316}
317
318
319Value getnewaddress(const Array& params, bool fHelp)
320{
321 if (fHelp || params.size() > 1)
322 throw runtime_error(
323 "getnewaddress [account]\n"
324 "Returns a new bitcoin address for receiving payments. "
325 "If [account] is specified (recommended), it is added to the address book "
326 "so payments received with the address will be credited to [account].");
327
328 // Parse the account first so we don't generate a key if there's an error
329 string strAccount;
330 if (params.size() > 0)
331 strAccount = AccountFromValue(params[0]);
332
333 if (!pwalletMain->IsLocked())
334 pwalletMain->TopUpKeyPool();
335
336 // Generate a new key that is added to wallet
337 std::vector<unsigned char> newKey;
338 if (!pwalletMain->GetKeyFromPool(newKey, false))
339 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
340 CBitcoinAddress address(newKey);
341
342 pwalletMain->SetAddressBookName(address, strAccount);
343
344 return address.ToString();
345}
346
347
348CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
349{
350 CWalletDB walletdb(pwalletMain->strWalletFile);
351
352 CAccount account;
353 walletdb.ReadAccount(strAccount, account);
354
355 bool bKeyUsed = false;
356
357 // Check if the current key has been used
358 if (!account.vchPubKey.empty())
359 {
360 CScript scriptPubKey;
361 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
362 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
363 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
364 ++it)
365 {
366 const CWalletTx& wtx = (*it).second;
367 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
368 if (txout.scriptPubKey == scriptPubKey)
369 bKeyUsed = true;
370 }
371 }
372
373 // Generate a new key
374 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
375 {
376 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
377 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
378
379 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
380 walletdb.WriteAccount(strAccount, account);
381 }
382
383 return CBitcoinAddress(account.vchPubKey);
384}
385
386Value getaccountaddress(const Array& params, bool fHelp)
387{
388 if (fHelp || params.size() != 1)
389 throw runtime_error(
390 "getaccountaddress <account>\n"
391 "Returns the current bitcoin address for receiving payments to this account.");
392
393 // Parse the account first so we don't generate a key if there's an error
394 string strAccount = AccountFromValue(params[0]);
395
396 Value ret;
397
398 ret = GetAccountAddress(strAccount).ToString();
399
400 return ret;
401}
402
403
404
405Value setaccount(const Array& params, bool fHelp)
406{
407 if (fHelp || params.size() < 1 || params.size() > 2)
408 throw runtime_error(
409 "setaccount <bitcoinaddress> <account>\n"
410 "Sets the account associated with the given address.");
411
412 CBitcoinAddress address(params[0].get_str());
413 if (!address.IsValid())
414 throw JSONRPCError(-5, "Invalid bitcoin address");
415
416
417 string strAccount;
418 if (params.size() > 1)
419 strAccount = AccountFromValue(params[1]);
420
421 // Detect when changing the account of an address that is the 'unused current key' of another account:
422 if (pwalletMain->mapAddressBook.count(address))
423 {
424 string strOldAccount = pwalletMain->mapAddressBook[address];
425 if (address == GetAccountAddress(strOldAccount))
426 GetAccountAddress(strOldAccount, true);
427 }
428
429 pwalletMain->SetAddressBookName(address, strAccount);
430
431 return Value::null;
432}
433
434
435Value getaccount(const Array& params, bool fHelp)
436{
437 if (fHelp || params.size() != 1)
438 throw runtime_error(
439 "getaccount <bitcoinaddress>\n"
440 "Returns the account associated with the given address.");
441
442 CBitcoinAddress address(params[0].get_str());
443 if (!address.IsValid())
444 throw JSONRPCError(-5, "Invalid bitcoin address");
445
446 string strAccount;
447 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
448 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
449 strAccount = (*mi).second;
450 return strAccount;
451}
452
453
454Value getaddressesbyaccount(const Array& params, bool fHelp)
455{
456 if (fHelp || params.size() != 1)
457 throw runtime_error(
458 "getaddressesbyaccount <account>\n"
459 "Returns the list of addresses for the given account.");
460
461 string strAccount = AccountFromValue(params[0]);
462
463 // Find all addresses that have the given account
464 Array ret;
465 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
466 {
467 const CBitcoinAddress& address = item.first;
468 const string& strName = item.second;
469 if (strName == strAccount)
470 ret.push_back(address.ToString());
471 }
472 return ret;
473}
474
475Value settxfee(const Array& params, bool fHelp)
476{
477 if (fHelp || params.size() < 1 || params.size() > 1)
478 throw runtime_error(
479 "settxfee <amount>\n"
480 "<amount> is a real and is rounded to the nearest 0.00000001");
481
482 // Amount
483 int64 nAmount = 0;
484 if (params[0].get_real() != 0.0)
485 nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
486
487 nTransactionFee = nAmount;
488 return true;
489}
490
491Value sendtoaddress(const Array& params, bool fHelp)
492{
493 if (fHelp || params.size() < 2 || params.size() > 4)
494 throw runtime_error(
495 "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
496 "<amount> is a real and is rounded to the nearest 0.00000001\n"
497 "if wallet is encrypted, requires passphrase to be stored with walletpassphrase first");
498
499 CBitcoinAddress address(params[0].get_str());
500 if (!address.IsValid())
501 throw JSONRPCError(-5, "Invalid bitcoin address");
502
503 // Amount
504 int64 nAmount = AmountFromValue(params[1]);
505
506 // Wallet comments
507 CWalletTx wtx;
508 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
509 wtx.mapValue["comment"] = params[2].get_str();
510 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
511 wtx.mapValue["to"] = params[3].get_str();
512
513 if (pwalletMain->IsLocked())
514 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
515
516 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
517 if (strError != "")
518 throw JSONRPCError(-4, strError);
519
520 return wtx.GetHash().GetHex();
521}
522
523static const string strMessageMagic = "Bitcoin Signed Message:\n";
524
525Value signmessage(const Array& params, bool fHelp)
526{
527 if (fHelp || params.size() != 2)
528 throw runtime_error(
529 "signmessage <bitcoinaddress> <message>\n"
530 "Sign a message with the private key of an address");
531
532 if (pwalletMain->IsLocked())
533 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
534
535 string strAddress = params[0].get_str();
536 string strMessage = params[1].get_str();
537
538 CBitcoinAddress addr(strAddress);
539 if (!addr.IsValid())
540 throw JSONRPCError(-3, "Invalid address");
541
542 CKey key;
543 if (!pwalletMain->GetKey(addr, key))
544 throw JSONRPCError(-4, "Private key not available");
545
546 CDataStream ss(SER_GETHASH);
547 ss << strMessageMagic;
548 ss << strMessage;
549
550 vector<unsigned char> vchSig;
551 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
552 throw JSONRPCError(-5, "Sign failed");
553
554 return EncodeBase64(&vchSig[0], vchSig.size());
555}
556
557Value verifymessage(const Array& params, bool fHelp)
558{
559 if (fHelp || params.size() != 3)
560 throw runtime_error(
561 "verifymessage <bitcoinaddress> <signature> <message>\n"
562 "Verify a signed message");
563
564 string strAddress = params[0].get_str();
565 string strSign = params[1].get_str();
566 string strMessage = params[2].get_str();
567
568 CBitcoinAddress addr(strAddress);
569 if (!addr.IsValid())
570 throw JSONRPCError(-3, "Invalid address");
571
572 bool fInvalid = false;
573 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
574
575 if (fInvalid)
576 throw JSONRPCError(-5, "Malformed base64 encoding");
577
578 CDataStream ss(SER_GETHASH);
579 ss << strMessageMagic;
580 ss << strMessage;
581
582 CKey key;
583 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
584 return false;
585
586 return (CBitcoinAddress(key.GetPubKey()) == addr);
587}
588
589
590Value getreceivedbyaddress(const Array& params, bool fHelp)
591{
592 if (fHelp || params.size() < 1 || params.size() > 2)
593 throw runtime_error(
594 "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
595 "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
596
597 // Bitcoin address
598 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
599 CScript scriptPubKey;
600 if (!address.IsValid())
601 throw JSONRPCError(-5, "Invalid bitcoin address");
602 scriptPubKey.SetBitcoinAddress(address);
603 if (!IsMine(*pwalletMain,scriptPubKey))
604 return (double)0.0;
605
606 // Minimum confirmations
607 int nMinDepth = 1;
608 if (params.size() > 1)
609 nMinDepth = params[1].get_int();
610
611 // Tally
612 int64 nAmount = 0;
613 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
614 {
615 const CWalletTx& wtx = (*it).second;
616 if (wtx.IsCoinBase() || !wtx.IsFinal())
617 continue;
618
619 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
620 if (txout.scriptPubKey == scriptPubKey)
621 if (wtx.GetDepthInMainChain() >= nMinDepth)
622 nAmount += txout.nValue;
623 }
624
625 return ValueFromAmount(nAmount);
626}
627
628
629void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
630{
631 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
632 {
633 const CBitcoinAddress& address = item.first;
634 const string& strName = item.second;
635 if (strName == strAccount)
636 setAddress.insert(address);
637 }
638}
639
640
641Value getreceivedbyaccount(const Array& params, bool fHelp)
642{
643 if (fHelp || params.size() < 1 || params.size() > 2)
644 throw runtime_error(
645 "getreceivedbyaccount <account> [minconf=1]\n"
646 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
647
648 // Minimum confirmations
649 int nMinDepth = 1;
650 if (params.size() > 1)
651 nMinDepth = params[1].get_int();
652
653 // Get the set of pub keys that have the label
654 string strAccount = AccountFromValue(params[0]);
655 set<CBitcoinAddress> setAddress;
656 GetAccountAddresses(strAccount, setAddress);
657
658 // Tally
659 int64 nAmount = 0;
660 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
661 {
662 const CWalletTx& wtx = (*it).second;
663 if (wtx.IsCoinBase() || !wtx.IsFinal())
664 continue;
665
666 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
667 {
668 CBitcoinAddress address;
669 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
670 if (wtx.GetDepthInMainChain() >= nMinDepth)
671 nAmount += txout.nValue;
672 }
673 }
674
675 return (double)nAmount / (double)COIN;
676}
677
678
679int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
680{
681 int64 nBalance = 0;
682
683 // Tally wallet transactions
684 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
685 {
686 const CWalletTx& wtx = (*it).second;
687 if (!wtx.IsFinal())
688 continue;
689
690 int64 nGenerated, nReceived, nSent, nFee;
691 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
692
693 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
694 nBalance += nReceived;
695 nBalance += nGenerated - nSent - nFee;
696 }
697
698 // Tally internal accounting entries
699 nBalance += walletdb.GetAccountCreditDebit(strAccount);
700
701 return nBalance;
702}
703
704int64 GetAccountBalance(const string& strAccount, int nMinDepth)
705{
706 CWalletDB walletdb(pwalletMain->strWalletFile);
707 return GetAccountBalance(walletdb, strAccount, nMinDepth);
708}
709
710
711Value getbalance(const Array& params, bool fHelp)
712{
713 if (fHelp || params.size() > 2)
714 throw runtime_error(
715 "getbalance [account] [minconf=1]\n"
716 "If [account] is not specified, returns the server's total available balance.\n"
717 "If [account] is specified, returns the balance in the account.");
718
719 if (params.size() == 0)
720 return ValueFromAmount(pwalletMain->GetBalance());
721
722 int nMinDepth = 1;
723 if (params.size() > 1)
724 nMinDepth = params[1].get_int();
725
726 if (params[0].get_str() == "*") {
727 // Calculate total balance a different way from GetBalance()
728 // (GetBalance() sums up all unspent TxOuts)
729 // getbalance and getbalance '*' should always return the same number.
730 int64 nBalance = 0;
731 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
732 {
733 const CWalletTx& wtx = (*it).second;
734 if (!wtx.IsFinal())
735 continue;
736
737 int64 allGeneratedImmature, allGeneratedMature, allFee;
738 allGeneratedImmature = allGeneratedMature = allFee = 0;
739 string strSentAccount;
740 list<pair<CBitcoinAddress, int64> > listReceived;
741 list<pair<CBitcoinAddress, int64> > listSent;
742 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
743 if (wtx.GetDepthInMainChain() >= nMinDepth)
744 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
745 nBalance += r.second;
746 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
747 nBalance -= r.second;
748 nBalance -= allFee;
749 nBalance += allGeneratedMature;
750 }
751 return ValueFromAmount(nBalance);
752 }
753
754 string strAccount = AccountFromValue(params[0]);
755
756 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
757
758 return ValueFromAmount(nBalance);
759}
760
761
762Value movecmd(const Array& params, bool fHelp)
763{
764 if (fHelp || params.size() < 3 || params.size() > 5)
765 throw runtime_error(
766 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
767 "Move from one account in your wallet to another.");
768
769 string strFrom = AccountFromValue(params[0]);
770 string strTo = AccountFromValue(params[1]);
771 int64 nAmount = AmountFromValue(params[2]);
772 if (params.size() > 3)
773 // unused parameter, used to be nMinDepth, keep type-checking it though
774 (void)params[3].get_int();
775 string strComment;
776 if (params.size() > 4)
777 strComment = params[4].get_str();
778
779 CWalletDB walletdb(pwalletMain->strWalletFile);
780 walletdb.TxnBegin();
781
782 int64 nNow = GetAdjustedTime();
783
784 // Debit
785 CAccountingEntry debit;
786 debit.strAccount = strFrom;
787 debit.nCreditDebit = -nAmount;
788 debit.nTime = nNow;
789 debit.strOtherAccount = strTo;
790 debit.strComment = strComment;
791 walletdb.WriteAccountingEntry(debit);
792
793 // Credit
794 CAccountingEntry credit;
795 credit.strAccount = strTo;
796 credit.nCreditDebit = nAmount;
797 credit.nTime = nNow;
798 credit.strOtherAccount = strFrom;
799 credit.strComment = strComment;
800 walletdb.WriteAccountingEntry(credit);
801
802 walletdb.TxnCommit();
803
804 return true;
805}
806
807
808Value sendfrom(const Array& params, bool fHelp)
809{
810 if (fHelp || params.size() < 3 || params.size() > 6)
811 throw runtime_error(
812 "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
813 "<amount> is a real and is rounded to the nearest 0.00000001\n"
814 "if wallet is encrypted, requires passphrase to be stored with walletpassphrase first");
815
816 string strAccount = AccountFromValue(params[0]);
817 CBitcoinAddress address(params[1].get_str());
818 if (!address.IsValid())
819 throw JSONRPCError(-5, "Invalid bitcoin address");
820 int64 nAmount = AmountFromValue(params[2]);
821 int nMinDepth = 1;
822 if (params.size() > 3)
823 nMinDepth = params[3].get_int();
824
825 CWalletTx wtx;
826 wtx.strFromAccount = strAccount;
827 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
828 wtx.mapValue["comment"] = params[4].get_str();
829 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
830 wtx.mapValue["to"] = params[5].get_str();
831
832 if (pwalletMain->IsLocked())
833 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
834
835 // Check funds
836 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
837 if (nAmount > nBalance)
838 throw JSONRPCError(-6, "Account has insufficient funds");
839
840 // Send
841 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
842 if (strError != "")
843 throw JSONRPCError(-4, strError);
844
845 return wtx.GetHash().GetHex();
846}
847
848
849Value sendmany(const Array& params, bool fHelp)
850{
851 if (fHelp || params.size() < 2 || params.size() > 4)
852 throw runtime_error(
853 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
854 "amounts are double-precision floating point numbers\n"
855 "if wallet is encrypted, requires passphrase to be stored with walletpassphrase first");
856
857 string strAccount = AccountFromValue(params[0]);
858 Object sendTo = params[1].get_obj();
859 int nMinDepth = 1;
860 if (params.size() > 2)
861 nMinDepth = params[2].get_int();
862
863 CWalletTx wtx;
864 wtx.strFromAccount = strAccount;
865 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
866 wtx.mapValue["comment"] = params[3].get_str();
867
868 set<CBitcoinAddress> setAddress;
869 vector<pair<CScript, int64> > vecSend;
870
871 int64 totalAmount = 0;
872 BOOST_FOREACH(const Pair& s, sendTo)
873 {
874 CBitcoinAddress address(s.name_);
875 if (!address.IsValid())
876 throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_);
877
878 if (setAddress.count(address))
879 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
880 setAddress.insert(address);
881
882 CScript scriptPubKey;
883 scriptPubKey.SetBitcoinAddress(address);
884 int64 nAmount = AmountFromValue(s.value_);
885 totalAmount += nAmount;
886
887 vecSend.push_back(make_pair(scriptPubKey, nAmount));
888 }
889
890 if (pwalletMain->IsLocked())
891 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
892
893 // Check funds
894 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
895 if (totalAmount > nBalance)
896 throw JSONRPCError(-6, "Account has insufficient funds");
897
898 // Send
899 CReserveKey keyChange(pwalletMain);
900 int64 nFeeRequired = 0;
901 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
902 if (!fCreated)
903 {
904 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
905 throw JSONRPCError(-6, "Insufficient funds");
906 throw JSONRPCError(-4, "Transaction creation failed");
907 }
908 if (!pwalletMain->CommitTransaction(wtx, keyChange))
909 throw JSONRPCError(-4, "Transaction commit failed");
910
911 return wtx.GetHash().GetHex();
912}
913
914
915struct tallyitem
916{
917 int64 nAmount;
918 int nConf;
919 tallyitem()
920 {
921 nAmount = 0;
922 nConf = INT_MAX;
923 }
924};
925
926Value ListReceived(const Array& params, bool fByAccounts)
927{
928 // Minimum confirmations
929 int nMinDepth = 1;
930 if (params.size() > 0)
931 nMinDepth = params[0].get_int();
932
933 // Whether to include empty accounts
934 bool fIncludeEmpty = false;
935 if (params.size() > 1)
936 fIncludeEmpty = params[1].get_bool();
937
938 // Tally
939 map<CBitcoinAddress, tallyitem> mapTally;
940 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
941 {
942 const CWalletTx& wtx = (*it).second;
943 if (wtx.IsCoinBase() || !wtx.IsFinal())
944 continue;
945
946 int nDepth = wtx.GetDepthInMainChain();
947 if (nDepth < nMinDepth)
948 continue;
949
950 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
951 {
952 CBitcoinAddress address;
953 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
954 continue;
955
956 tallyitem& item = mapTally[address];
957 item.nAmount += txout.nValue;
958 item.nConf = min(item.nConf, nDepth);
959 }
960 }
961
962 // Reply
963 Array ret;
964 map<string, tallyitem> mapAccountTally;
965 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
966 {
967 const CBitcoinAddress& address = item.first;
968 const string& strAccount = item.second;
969 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
970 if (it == mapTally.end() && !fIncludeEmpty)
971 continue;
972
973 int64 nAmount = 0;
974 int nConf = INT_MAX;
975 if (it != mapTally.end())
976 {
977 nAmount = (*it).second.nAmount;
978 nConf = (*it).second.nConf;
979 }
980
981 if (fByAccounts)
982 {
983 tallyitem& item = mapAccountTally[strAccount];
984 item.nAmount += nAmount;
985 item.nConf = min(item.nConf, nConf);
986 }
987 else
988 {
989 Object obj;
990 obj.push_back(Pair("address", address.ToString()));
991 obj.push_back(Pair("account", strAccount));
992 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
993 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
994 ret.push_back(obj);
995 }
996 }
997
998 if (fByAccounts)
999 {
1000 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1001 {
1002 int64 nAmount = (*it).second.nAmount;
1003 int nConf = (*it).second.nConf;
1004 Object obj;
1005 obj.push_back(Pair("account", (*it).first));
1006 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1007 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1008 ret.push_back(obj);
1009 }
1010 }
1011
1012 return ret;
1013}
1014
1015Value listreceivedbyaddress(const Array& params, bool fHelp)
1016{
1017 if (fHelp || params.size() > 2)
1018 throw runtime_error(
1019 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1020 "[minconf] is the minimum number of confirmations before payments are included.\n"
1021 "[includeempty] whether to include addresses that haven't received any payments.\n"
1022 "Returns an array of objects containing:\n"
1023 " \"address\" : receiving address\n"
1024 " \"account\" : the account of the receiving address\n"
1025 " \"amount\" : total amount received by the address\n"
1026 " \"confirmations\" : number of confirmations of the most recent transaction included");
1027
1028 return ListReceived(params, false);
1029}
1030
1031Value listreceivedbyaccount(const Array& params, bool fHelp)
1032{
1033 if (fHelp || params.size() > 2)
1034 throw runtime_error(
1035 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1036 "[minconf] is the minimum number of confirmations before payments are included.\n"
1037 "[includeempty] whether to include accounts that haven't received any payments.\n"
1038 "Returns an array of objects containing:\n"
1039 " \"account\" : the account of the receiving addresses\n"
1040 " \"amount\" : total amount received by addresses with this account\n"
1041 " \"confirmations\" : number of confirmations of the most recent transaction included");
1042
1043 return ListReceived(params, true);
1044}
1045
1046void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1047{
1048 int64 nGeneratedImmature, nGeneratedMature, nFee;
1049 string strSentAccount;
1050 list<pair<CBitcoinAddress, int64> > listReceived;
1051 list<pair<CBitcoinAddress, int64> > listSent;
1052 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1053
1054 bool fAllAccounts = (strAccount == string("*"));
1055
1056 // Generated blocks assigned to account ""
1057 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1058 {
1059 Object entry;
1060 entry.push_back(Pair("account", string("")));
1061 if (nGeneratedImmature)
1062 {
1063 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1064 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1065 }
1066 else
1067 {
1068 entry.push_back(Pair("category", "generate"));
1069 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1070 }
1071 if (fLong)
1072 WalletTxToJSON(wtx, entry);
1073 ret.push_back(entry);
1074 }
1075
1076 // Sent
1077 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1078 {
1079 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1080 {
1081 Object entry;
1082 entry.push_back(Pair("account", strSentAccount));
1083 entry.push_back(Pair("address", s.first.ToString()));
1084 entry.push_back(Pair("category", "send"));
1085 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1086 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1087 if (fLong)
1088 WalletTxToJSON(wtx, entry);
1089 ret.push_back(entry);
1090 }
1091 }
1092
1093 // Received
1094 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1095 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1096 {
1097 string account;
1098 if (pwalletMain->mapAddressBook.count(r.first))
1099 account = pwalletMain->mapAddressBook[r.first];
1100 if (fAllAccounts || (account == strAccount))
1101 {
1102 Object entry;
1103 entry.push_back(Pair("account", account));
1104 entry.push_back(Pair("address", r.first.ToString()));
1105 entry.push_back(Pair("category", "receive"));
1106 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1107 if (fLong)
1108 WalletTxToJSON(wtx, entry);
1109 ret.push_back(entry);
1110 }
1111 }
1112}
1113
1114void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1115{
1116 bool fAllAccounts = (strAccount == string("*"));
1117
1118 if (fAllAccounts || acentry.strAccount == strAccount)
1119 {
1120 Object entry;
1121 entry.push_back(Pair("account", acentry.strAccount));
1122 entry.push_back(Pair("category", "move"));
1123 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1124 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1125 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1126 entry.push_back(Pair("comment", acentry.strComment));
1127 ret.push_back(entry);
1128 }
1129}
1130
1131Value listtransactions(const Array& params, bool fHelp)
1132{
1133 if (fHelp || params.size() > 3)
1134 throw runtime_error(
1135 "listtransactions [account] [count=10] [from=0]\n"
1136 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1137
1138 string strAccount = "*";
1139 if (params.size() > 0)
1140 strAccount = params[0].get_str();
1141 int nCount = 10;
1142 if (params.size() > 1)
1143 nCount = params[1].get_int();
1144 int nFrom = 0;
1145 if (params.size() > 2)
1146 nFrom = params[2].get_int();
1147
1148 Array ret;
1149 CWalletDB walletdb(pwalletMain->strWalletFile);
1150
1151 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1152 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1153 typedef multimap<int64, TxPair > TxItems;
1154 TxItems txByTime;
1155
1156 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1157 {
1158 CWalletTx* wtx = &((*it).second);
1159 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1160 }
1161 list<CAccountingEntry> acentries;
1162 walletdb.ListAccountCreditDebit(strAccount, acentries);
1163 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1164 {
1165 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1166 }
1167
1168 // Now: iterate backwards until we have nCount items to return:
1169 TxItems::reverse_iterator it = txByTime.rbegin();
1170 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1171 for (; it != txByTime.rend(); ++it)
1172 {
1173 CWalletTx *const pwtx = (*it).second.first;
1174 if (pwtx != 0)
1175 ListTransactions(*pwtx, strAccount, 0, true, ret);
1176 CAccountingEntry *const pacentry = (*it).second.second;
1177 if (pacentry != 0)
1178 AcentryToJSON(*pacentry, strAccount, ret);
1179
1180 if (ret.size() >= nCount) break;
1181 }
1182 // ret is now newest to oldest
1183
1184 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1185 if (ret.size() > nCount)
1186 {
1187 Array::iterator last = ret.begin();
1188 std::advance(last, nCount);
1189 ret.erase(last, ret.end());
1190 }
1191 std::reverse(ret.begin(), ret.end()); // oldest to newest
1192
1193 return ret;
1194}
1195
1196Value listaccounts(const Array& params, bool fHelp)
1197{
1198 if (fHelp || params.size() > 1)
1199 throw runtime_error(
1200 "listaccounts [minconf=1]\n"
1201 "Returns Object that has account names as keys, account balances as values.");
1202
1203 int nMinDepth = 1;
1204 if (params.size() > 0)
1205 nMinDepth = params[0].get_int();
1206
1207 map<string, int64> mapAccountBalances;
1208 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1209 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1210 mapAccountBalances[entry.second] = 0;
1211 }
1212
1213 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1214 {
1215 const CWalletTx& wtx = (*it).second;
1216 int64 nGeneratedImmature, nGeneratedMature, nFee;
1217 string strSentAccount;
1218 list<pair<CBitcoinAddress, int64> > listReceived;
1219 list<pair<CBitcoinAddress, int64> > listSent;
1220 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1221 mapAccountBalances[strSentAccount] -= nFee;
1222 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1223 mapAccountBalances[strSentAccount] -= s.second;
1224 if (wtx.GetDepthInMainChain() >= nMinDepth)
1225 {
1226 mapAccountBalances[""] += nGeneratedMature;
1227 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1228 if (pwalletMain->mapAddressBook.count(r.first))
1229 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1230 else
1231 mapAccountBalances[""] += r.second;
1232 }
1233 }
1234
1235 list<CAccountingEntry> acentries;
1236 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1237 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1238 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1239
1240 Object ret;
1241 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1242 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1243 }
1244 return ret;
1245}
1246
1247Value listsinceblock(const Array& params, bool fHelp)
1248{
1249 if (fHelp)
1250 throw runtime_error(
1251 "listsinceblock [blockid] [target-confirmations]\n"
1252 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1253
1254 CBlockIndex *pindex = NULL;
1255 int target_confirms = 1;
1256
1257 if (params.size() > 0)
1258 {
1259 uint256 blockId = 0;
1260
1261 blockId.SetHex(params[0].get_str());
1262 pindex = CBlockLocator(blockId).GetBlockIndex();
1263 }
1264
1265 if (params.size() > 1)
1266 {
1267 target_confirms = params[1].get_int();
1268
1269 if (target_confirms < 1)
1270 throw JSONRPCError(-8, "Invalid parameter");
1271 }
1272
1273 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1274
1275 Array transactions;
1276
1277 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1278 {
1279 CWalletTx tx = (*it).second;
1280
1281 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1282 ListTransactions(tx, "*", 0, true, transactions);
1283 }
1284
1285 uint256 lastblock;
1286
1287 if (target_confirms == 1)
1288 {
1289 printf("oops!\n");
1290 lastblock = hashBestChain;
1291 }
1292 else
1293 {
1294 int target_height = pindexBest->nHeight + 1 - target_confirms;
1295
1296 CBlockIndex *block;
1297 for (block = pindexBest;
1298 block && block->nHeight > target_height;
1299 block = block->pprev) { }
1300
1301 lastblock = block ? block->GetBlockHash() : 0;
1302 }
1303
1304 Object ret;
1305 ret.push_back(Pair("transactions", transactions));
1306 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1307
1308 return ret;
1309}
1310
1311Value gettransaction(const Array& params, bool fHelp)
1312{
1313 if (fHelp || params.size() != 1)
1314 throw runtime_error(
1315 "gettransaction <txid>\n"
1316 "Get detailed information about <txid>");
1317
1318 uint256 hash;
1319 hash.SetHex(params[0].get_str());
1320
1321 Object entry;
1322
1323 if (!pwalletMain->mapWallet.count(hash))
1324 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1325 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1326
1327 int64 nCredit = wtx.GetCredit();
1328 int64 nDebit = wtx.GetDebit();
1329 int64 nNet = nCredit - nDebit;
1330 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1331
1332 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1333 if (wtx.IsFromMe())
1334 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1335
1336 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1337
1338 Array details;
1339 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1340 entry.push_back(Pair("details", details));
1341
1342 return entry;
1343}
1344
1345
1346Value backupwallet(const Array& params, bool fHelp)
1347{
1348 if (fHelp || params.size() != 1)
1349 throw runtime_error(
1350 "backupwallet <destination>\n"
1351 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1352
1353 string strDest = params[0].get_str();
1354 BackupWallet(*pwalletMain, strDest);
1355
1356 return Value::null;
1357}
1358
1359
1360Value keypoolrefill(const Array& params, bool fHelp)
1361{
1362 if (fHelp || params.size() > 0)
1363 throw runtime_error(
1364 "keypoolrefill\n"
1365 "Fills the keypool. If wallet is encrypted, requires passphrase to be stored.");
1366
1367 if (pwalletMain->IsLocked())
1368 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1369
1370 pwalletMain->TopUpKeyPool();
1371
1372 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1373 throw JSONRPCError(-4, "Error refreshing keypool.");
1374
1375 return Value::null;
1376}
1377
1378
1379void ThreadTopUpKeyPool(void* parg)
1380{
1381 pwalletMain->TopUpKeyPool();
1382}
1383
1384void ThreadCleanWalletPassphrase(void* parg)
1385{
1386 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1387
1388 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1389
1390 if (nWalletUnlockTime == 0)
1391 {
1392 nWalletUnlockTime = nMyWakeTime;
1393
1394 do
1395 {
1396 if (nWalletUnlockTime==0)
1397 break;
1398 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1399 if (nToSleep <= 0)
1400 break;
1401
1402 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1403 Sleep(nToSleep);
1404 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1405
1406 } while(1);
1407
1408 if (nWalletUnlockTime)
1409 {
1410 nWalletUnlockTime = 0;
1411 pwalletMain->Lock();
1412 }
1413 }
1414 else
1415 {
1416 if (nWalletUnlockTime < nMyWakeTime)
1417 nWalletUnlockTime = nMyWakeTime;
1418 }
1419
1420 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1421
1422 delete (int64*)parg;
1423}
1424
1425Value walletpassphrase(const Array& params, bool fHelp)
1426{
1427 if (fHelp || params.size() != 2 || params[0].get_str().length() == 0)
1428 throw runtime_error(
1429 "walletpassphrase <passphrase> <timeout>\n"
1430 "Stores the wallet decryption key in memory for <timeout> seconds.");
1431 if (!pwalletMain->IsCrypted())
1432 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1433
1434 if (!pwalletMain->IsLocked())
1435 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
1436
1437 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1438 SecureString strWalletPass;
1439 strWalletPass.reserve(100);
1440 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1441 // Alternately, find a way to make params[0] mlock()'d to begin with.
1442 strWalletPass = params[0].get_str().c_str();
1443
1444 if (strWalletPass.length() > 0)
1445 {
1446 if (!pwalletMain->Unlock(strWalletPass))
1447 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1448 }
1449 else
1450 throw runtime_error(
1451 "walletpassphrase <passphrase> <timeout>\n"
1452 "Stores the wallet decryption key in memory for <timeout> seconds.");
1453
1454 CreateThread(ThreadTopUpKeyPool, NULL);
1455 int64* pnSleepTime = new int64(params[1].get_int64());
1456 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1457
1458 return Value::null;
1459}
1460
1461
1462Value walletpassphrasechange(const Array& params, bool fHelp)
1463{
1464 if (fHelp || params.size() != 2)
1465 throw runtime_error(
1466 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1467 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1468 if (!pwalletMain->IsCrypted())
1469 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1470
1471 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1472 // Alternately, find a way to make params[0] mlock()'d to begin with.
1473 SecureString strOldWalletPass;
1474 strOldWalletPass.reserve(100);
1475 strOldWalletPass = params[0].get_str().c_str();
1476
1477 SecureString strNewWalletPass;
1478 strNewWalletPass.reserve(100);
1479 strNewWalletPass = params[1].get_str().c_str();
1480
1481 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1482 throw runtime_error(
1483 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1484 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1485
1486 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1487 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1488
1489 return Value::null;
1490}
1491
1492
1493Value walletlock(const Array& params, bool fHelp)
1494{
1495 if (fHelp || params.size() != 0)
1496 throw runtime_error(
1497 "walletlock\n"
1498 "Removes the wallet encryption key from memory, locking the wallet.\n"
1499 "After calling this method, you will need to call walletpassphrase again\n"
1500 "before being able to call any methods which require the wallet to be unlocked.");
1501 if (!pwalletMain->IsCrypted())
1502 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1503
1504 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1505 {
1506 pwalletMain->Lock();
1507 nWalletUnlockTime = 0;
1508 }
1509
1510 return Value::null;
1511}
1512
1513
1514Value encryptwallet(const Array& params, bool fHelp)
1515{
1516 if (fHelp || params.size() != 1)
1517 throw runtime_error(
1518 "encryptwallet <passphrase>\n"
1519 "Encrypts an as-yet unencrypted wallet with <passphrase>.");
1520 if (pwalletMain->IsCrypted())
1521 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1522
1523 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1524 // Alternately, find a way to make params[0] mlock()'d to begin with.
1525 SecureString strWalletPass;
1526 strWalletPass.reserve(100);
1527 strWalletPass = params[0].get_str().c_str();
1528
1529 if (strWalletPass.length() < 1)
1530 throw runtime_error(
1531 "encryptwallet <passphrase>\n"
1532 "Encrypts the wallet with <passphrase>.");
1533
1534 if (!pwalletMain->EncryptWallet(strWalletPass))
1535 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1536
1537 // BDB seems to have a bad habit of writing old data into
1538 // slack space in .dat files; that is bad if the old data is
1539 // unencrypted private keys. So:
1540 CreateThread(Shutdown, NULL);
1541 return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet";
1542}
1543
1544
1545Value validateaddress(const Array& params, bool fHelp)
1546{
1547 if (fHelp || params.size() != 1)
1548 throw runtime_error(
1549 "validateaddress <bitcoinaddress>\n"
1550 "Return information about <bitcoinaddress>.");
1551
1552 CBitcoinAddress address(params[0].get_str());
1553 bool isValid = address.IsValid();
1554
1555 Object ret;
1556 ret.push_back(Pair("isvalid", isValid));
1557 if (isValid)
1558 {
1559 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1560 // version of the address:
1561 string currentAddress = address.ToString();
1562 ret.push_back(Pair("address", currentAddress));
1563 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1564 if (pwalletMain->mapAddressBook.count(address))
1565 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1566 }
1567 return ret;
1568}
1569
1570
1571Value getwork(const Array& params, bool fHelp)
1572{
1573 if (fHelp || params.size() > 1)
1574 throw runtime_error(
1575 "getwork [data]\n"
1576 "If [data] is not specified, returns formatted hash data to work on:\n"
1577 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1578 " \"data\" : block data\n"
1579 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1580 " \"target\" : little endian hash target\n"
1581 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1582
1583 if (vNodes.empty())
1584 throw JSONRPCError(-9, "Bitcoin is not connected!");
1585
1586 if (IsInitialBlockDownload())
1587 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
1588
1589 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1590 static mapNewBlock_t mapNewBlock;
1591 static vector<CBlock*> vNewBlock;
1592 static CReserveKey reservekey(pwalletMain);
1593
1594 if (params.size() == 0)
1595 {
1596 // Update block
1597 static unsigned int nTransactionsUpdatedLast;
1598 static CBlockIndex* pindexPrev;
1599 static int64 nStart;
1600 static CBlock* pblock;
1601 if (pindexPrev != pindexBest ||
1602 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1603 {
1604 if (pindexPrev != pindexBest)
1605 {
1606 // Deallocate old blocks since they're obsolete now
1607 mapNewBlock.clear();
1608 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1609 delete pblock;
1610 vNewBlock.clear();
1611 }
1612 nTransactionsUpdatedLast = nTransactionsUpdated;
1613 pindexPrev = pindexBest;
1614 nStart = GetTime();
1615
1616 // Create new block
1617 pblock = CreateNewBlock(reservekey);
1618 if (!pblock)
1619 throw JSONRPCError(-7, "Out of memory");
1620 vNewBlock.push_back(pblock);
1621 }
1622
1623 // Update nTime
1624 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1625 pblock->nNonce = 0;
1626
1627 // Update nExtraNonce
1628 static unsigned int nExtraNonce = 0;
1629 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1630
1631 // Save
1632 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1633
1634 // Prebuild hash buffers
1635 char pmidstate[32];
1636 char pdata[128];
1637 char phash1[64];
1638 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1639
1640 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1641
1642 Object result;
1643 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1644 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1645 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1646 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1647 return result;
1648 }
1649 else
1650 {
1651 // Parse parameters
1652 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1653 if (vchData.size() != 128)
1654 throw JSONRPCError(-8, "Invalid parameter");
1655 CBlock* pdata = (CBlock*)&vchData[0];
1656
1657 // Byte reverse
1658 for (int i = 0; i < 128/4; i++)
1659 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1660
1661 // Get saved block
1662 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1663 return false;
1664 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1665
1666 pblock->nTime = pdata->nTime;
1667 pblock->nNonce = pdata->nNonce;
1668 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1669 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1670
1671 return CheckWork(pblock, *pwalletMain, reservekey);
1672 }
1673}
1674
1675
1676Value getmemorypool(const Array& params, bool fHelp)
1677{
1678 if (fHelp || params.size() > 1)
1679 throw runtime_error(
1680 "getmemorypool [data]\n"
1681 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1682 " \"version\" : block version\n"
1683 " \"previousblockhash\" : hash of current highest block\n"
1684 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1685 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1686 " \"time\" : timestamp appropriate for next block\n"
1687 " \"bits\" : compressed target of next block\n"
1688 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1689
1690 if (params.size() == 0)
1691 {
1692 if (vNodes.empty())
1693 throw JSONRPCError(-9, "Bitcoin is not connected!");
1694
1695 if (IsInitialBlockDownload())
1696 throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
1697
1698 static CReserveKey reservekey(pwalletMain);
1699
1700 // Update block
1701 static unsigned int nTransactionsUpdatedLast;
1702 static CBlockIndex* pindexPrev;
1703 static int64 nStart;
1704 static CBlock* pblock;
1705 if (pindexPrev != pindexBest ||
1706 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1707 {
1708 nTransactionsUpdatedLast = nTransactionsUpdated;
1709 pindexPrev = pindexBest;
1710 nStart = GetTime();
1711
1712 // Create new block
1713 if(pblock)
1714 delete pblock;
1715 pblock = CreateNewBlock(reservekey);
1716 if (!pblock)
1717 throw JSONRPCError(-7, "Out of memory");
1718 }
1719
1720 // Update nTime
1721 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1722 pblock->nNonce = 0;
1723
1724 Array transactions;
1725 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1726 if(tx.IsCoinBase())
1727 continue;
1728
1729 CDataStream ssTx;
1730 ssTx << tx;
1731
1732 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1733 }
1734
1735 Object result;
1736 result.push_back(Pair("version", pblock->nVersion));
1737 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1738 result.push_back(Pair("transactions", transactions));
1739 result.push_back(Pair("coinbasevalue", (boost::int64_t)pblock->vtx[0].vout[0].nValue));
1740 result.push_back(Pair("time", (boost::int64_t)pblock->nTime));
1741
1742 union {
1743 int32_t nBits;
1744 char cBits[4];
1745 } uBits;
1746 uBits.nBits = htonl((int32_t)pblock->nBits);
1747 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1748
1749 return result;
1750 }
1751 else
1752 {
1753 // Parse parameters
1754 CDataStream ssBlock(ParseHex(params[0].get_str()));
1755 CBlock pblock;
1756 ssBlock >> pblock;
1757
1758 return ProcessBlock(NULL, &pblock);
1759 }
1760}
1761
1762
1763Value dumpblock(const Array& params, bool fHelp)
1764{
1765 if (fHelp || params.size() != 2)
1766 throw runtime_error(
1767 "dumpblock <height> <filename>\n"
1768 "Emit the block at <height> to <filename>.");
1769
1770 int want_height = 0;
1771 if (params.size() > 0)
1772 want_height = params[0].get_int();
1773
1774 if (want_height > nBestHeight)
1775 throw runtime_error("Requested block exceeds current nBestHeight!\n");
1776
1777 // path to dump block to
1778 string filename = params[1].get_str();
1779
1780 // this is O(n^2)...
1781 // possibly could be improved if we descend from best height if requested height is closer to it
1782 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
1783 {
1784 CBlockIndex *pindex = (*mi).second;
1785 if (pindex->nHeight == want_height && pindex->IsInMainChain()) {
1786 CBlock block;
1787 block.ReadFromDisk(pindex);
1788 printf("Dumping block %d to %s\n", want_height, filename.c_str());
1789 CAutoFile fileout = fopen(filename.c_str(), "wb+");
1790 fileout << block;
1791 return true;
1792 }
1793 }
1794 return false;
1795}
1796
1797
1798Value eatblock(const Array& params, bool fHelp)
1799{
1800 if (fHelp || params.size() < 1 || params.size() > 1)
1801 throw runtime_error(
1802 "eatblock <filename>\n"
1803 "Load a candidate for the next block directly from <filename>.");
1804
1805 // path to load block from
1806 string filename = params[0].get_str();
1807
1808 printf("Attempting to create block #%d from file %s\n", nBestHeight + 1, filename.c_str());
1809 CAutoFile filein = fopen(filename.c_str(), "rb");
1810 CBlock block;
1811 filein >> block;
1812 return ProcessBlock(NULL, &block); // note that 'true' even if it was rejected (bastard, etc)
1813} // ... but will return 'false' if we already have the block.
1814
1815
1816Value importprivkey(const Array& params, bool fHelp)
1817{
1818 if (fHelp || params.size() < 1 || params.size() > 2)
1819 throw runtime_error(
1820 "importprivkey <bitcoinprivkey> [label]\n"
1821 "Adds a private key (as returned by dumpprivkey) to your wallet.");
1822
1823 string strSecret = params[0].get_str();
1824 string strLabel = "";
1825 if (params.size() > 1)
1826 strLabel = params[1].get_str();
1827 CBitcoinSecret vchSecret;
1828 bool fGood = vchSecret.SetString(strSecret);
1829
1830 if (!fGood) throw JSONRPCError(-5,"Invalid private key");
1831
1832 CKey key;
1833 CSecret secret = vchSecret.GetSecret();
1834 key.SetSecret(secret);
1835 CBitcoinAddress vchAddress = CBitcoinAddress(key.GetPubKey());
1836
1837 CRITICAL_BLOCK(cs_main)
1838 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1839 {
1840 pwalletMain->MarkDirty();
1841 pwalletMain->SetAddressBookName(vchAddress, strLabel);
1842
1843 if (!pwalletMain->AddKey(key))
1844 throw JSONRPCError(-4,"Error adding key to wallet");
1845
1846 pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
1847 pwalletMain->ReacceptWalletTransactions();
1848 }
1849
1850 return Value::null;
1851}
1852
1853Value dumpprivkey(const Array& params, bool fHelp)
1854{
1855 if (fHelp || params.size() != 1)
1856 throw runtime_error(
1857 "dumpprivkey <bitcoinaddress>\n"
1858 "Reveals the private key corresponding to <bitcoinaddress>.");
1859
1860 string strAddress = params[0].get_str();
1861 CBitcoinAddress address;
1862 if (!address.SetString(strAddress))
1863 throw JSONRPCError(-5, "Invalid bitcoin address");
1864 CSecret vchSecret;
1865 if (!pwalletMain->GetSecret(address, vchSecret))
1866 throw JSONRPCError(-4,"Private key for address " + strAddress + " is not known");
1867 return CBitcoinSecret(vchSecret).ToString();
1868}
1869
1870Value getrawtransaction(const Array& params, bool fHelp)
1871{
1872 if (fHelp || params.size() != 1)
1873 throw runtime_error(
1874 "getrawtransaction <txid>\n"
1875 "Get hex serialization of <txid> from memory pool or database.");
1876
1877 uint256 hash;
1878 hash.SetHex(params[0].get_str());
1879
1880 CTransaction tx;
1881 if (!GetMempoolTx(hash, tx) && !CTxDB("r").ReadDiskTx(hash, tx))
1882 throw JSONRPCError(-5, "Transaction not found in memory pool or database.");
1883
1884 CDataStream ssTx;
1885 ssTx << tx;
1886 return HexStr(ssTx.begin(), ssTx.end());
1887}
1888
1889Value sendrawtransaction(const Array& params, bool fHelp)
1890{
1891 if (fHelp || params.size() < 1 || params.size() > 1)
1892 throw runtime_error(
1893 "sendrawtransaction <hex string>\n"
1894 "Submits raw transaction (serialized, hex-encoded) to local node and network.");
1895
1896 CDataStream ssData(ParseHex(params[0].get_str()));
1897 CTransaction tx;
1898 try
1899 {
1900 ssData >> tx;
1901 }
1902 catch (std::exception &e)
1903 {
1904 throw JSONRPCError(-22, "tx decode failed");
1905 }
1906
1907 uint256 hash = tx.GetHash();
1908 if (!MempoolContainsTx(hash))
1909 {
1910 if (!tx.AcceptToMemoryPool(true))
1911 {
1912 if (CTxDB("r").ContainsTx(hash))
1913 throw JSONRPCError(-27, "tx already included in block");
1914 throw JSONRPCError(-25, "tx rejected, see log for details");
1915 }
1916
1917 SyncWithWallets(tx, NULL, true);
1918 }
1919
1920 CInv inv(MSG_TX, hash);
1921 RelayMessage(inv, tx);
1922
1923 return hash.GetHex();
1924}
1925
1926
1927//
1928// Call Table
1929//
1930
1931pair<string, rpcfn_type> pCallTable[] =
1932{
1933 make_pair("help", &help),
1934 make_pair("stop", &stop),
1935 make_pair("getblockcount", &getblockcount),
1936 make_pair("getblocknumber", &getblocknumber),
1937 make_pair("getconnectioncount", &getconnectioncount),
1938 make_pair("getdifficulty", &getdifficulty),
1939 make_pair("getgenerate", &getgenerate),
1940 make_pair("setgenerate", &setgenerate),
1941 make_pair("gethashespersec", &gethashespersec),
1942 make_pair("getinfo", &getinfo),
1943 make_pair("getnewaddress", &getnewaddress),
1944 make_pair("getaccountaddress", &getaccountaddress),
1945 make_pair("setaccount", &setaccount),
1946 make_pair("getaccount", &getaccount),
1947 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1948 make_pair("sendtoaddress", &sendtoaddress),
1949 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1950 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1951 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1952 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1953 make_pair("backupwallet", &backupwallet),
1954 make_pair("keypoolrefill", &keypoolrefill),
1955 make_pair("walletpassphrase", &walletpassphrase),
1956 make_pair("walletpassphrasechange", &walletpassphrasechange),
1957 make_pair("walletlock", &walletlock),
1958 make_pair("encryptwallet", &encryptwallet),
1959 make_pair("validateaddress", &validateaddress),
1960 make_pair("getbalance", &getbalance),
1961 make_pair("move", &movecmd),
1962 make_pair("sendfrom", &sendfrom),
1963 make_pair("sendmany", &sendmany),
1964 make_pair("gettransaction", &gettransaction),
1965 make_pair("listtransactions", &listtransactions),
1966 make_pair("signmessage", &signmessage),
1967 make_pair("verifymessage", &verifymessage),
1968 make_pair("getwork", &getwork),
1969 make_pair("listaccounts", &listaccounts),
1970 make_pair("settxfee", &settxfee),
1971 make_pair("getmemorypool", &getmemorypool),
1972 make_pair("listsinceblock", &listsinceblock),
1973 make_pair("dumpblock", &dumpblock),
1974 make_pair("eatblock", &eatblock),
1975 make_pair("importprivkey", &importprivkey),
1976 make_pair("dumpprivkey", &dumpprivkey),
1977 make_pair("getrawtransaction", &getrawtransaction),
1978 make_pair("sendrawtransaction", &sendrawtransaction),
1979};
1980map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1981
1982string pAllowInSafeMode[] =
1983{
1984 "help",
1985 "stop",
1986 "getblockcount",
1987 "getblocknumber", // deprecated
1988 "getconnectioncount",
1989 "getdifficulty",
1990 "getgenerate",
1991 "setgenerate",
1992 "gethashespersec",
1993 "getinfo",
1994 "getnewaddress",
1995 "getaccountaddress",
1996 "getaccount",
1997 "getaddressesbyaccount",
1998 "backupwallet",
1999 "keypoolrefill",
2000 "walletpassphrase",
2001 "walletlock",
2002 "validateaddress",
2003 "getwork",
2004 "getmemorypool",
2005 "dumpblock",
2006};
2007set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2008
2009
2010
2011
2012//
2013// HTTP protocol
2014//
2015// This ain't Apache. We're just using HTTP header for the length field
2016// and to be compatible with other JSON-RPC implementations.
2017//
2018
2019string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2020{
2021 ostringstream s;
2022 s << "POST / HTTP/1.1\r\n"
2023 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2024 << "Host: 127.0.0.1\r\n"
2025 << "Content-Type: application/json\r\n"
2026 << "Content-Length: " << strMsg.size() << "\r\n"
2027 << "Connection: close\r\n"
2028 << "Accept: application/json\r\n";
2029 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2030 s << item.first << ": " << item.second << "\r\n";
2031 s << "\r\n" << strMsg;
2032
2033 return s.str();
2034}
2035
2036string rfc1123Time()
2037{
2038 char buffer[64];
2039 time_t now;
2040 time(&now);
2041 struct tm* now_gmt = gmtime(&now);
2042 string locale(setlocale(LC_TIME, NULL));
2043 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2044 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2045 setlocale(LC_TIME, locale.c_str());
2046 return string(buffer);
2047}
2048
2049static string HTTPReply(int nStatus, const string& strMsg)
2050{
2051 if (nStatus == 401)
2052 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2053 "Date: %s\r\n"
2054 "Server: bitcoin-json-rpc/%s\r\n"
2055 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2056 "Content-Type: text/html\r\n"
2057 "Content-Length: 296\r\n"
2058 "\r\n"
2059 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2060 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2061 "<HTML>\r\n"
2062 "<HEAD>\r\n"
2063 "<TITLE>Error</TITLE>\r\n"
2064 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2065 "</HEAD>\r\n"
2066 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2067 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2068 const char *cStatus;
2069 if (nStatus == 200) cStatus = "OK";
2070 else if (nStatus == 400) cStatus = "Bad Request";
2071 else if (nStatus == 403) cStatus = "Forbidden";
2072 else if (nStatus == 404) cStatus = "Not Found";
2073 else if (nStatus == 500) cStatus = "Internal Server Error";
2074 else cStatus = "";
2075 return strprintf(
2076 "HTTP/1.1 %d %s\r\n"
2077 "Date: %s\r\n"
2078 "Connection: close\r\n"
2079 "Content-Length: %d\r\n"
2080 "Content-Type: application/json\r\n"
2081 "Server: bitcoin-json-rpc/%s\r\n"
2082 "\r\n"
2083 "%s",
2084 nStatus,
2085 cStatus,
2086 rfc1123Time().c_str(),
2087 strMsg.size(),
2088 FormatFullVersion().c_str(),
2089 strMsg.c_str());
2090}
2091
2092int ReadHTTPStatus(std::basic_istream<char>& stream)
2093{
2094 string str;
2095 getline(stream, str);
2096 vector<string> vWords;
2097 boost::split(vWords, str, boost::is_any_of(" "));
2098 if (vWords.size() < 2)
2099 return 500;
2100 return atoi(vWords[1].c_str());
2101}
2102
2103int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2104{
2105 int nLen = 0;
2106 loop
2107 {
2108 string str;
2109 std::getline(stream, str);
2110 if (str.empty() || str == "\r")
2111 break;
2112 string::size_type nColon = str.find(":");
2113 if (nColon != string::npos)
2114 {
2115 string strHeader = str.substr(0, nColon);
2116 boost::trim(strHeader);
2117 boost::to_lower(strHeader);
2118 string strValue = str.substr(nColon+1);
2119 boost::trim(strValue);
2120 mapHeadersRet[strHeader] = strValue;
2121 if (strHeader == "content-length")
2122 nLen = atoi(strValue.c_str());
2123 }
2124 }
2125 return nLen;
2126}
2127
2128int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2129{
2130 mapHeadersRet.clear();
2131 strMessageRet = "";
2132
2133 // Read status
2134 int nStatus = ReadHTTPStatus(stream);
2135
2136 // Read header
2137 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2138 if (nLen < 0 || nLen > MAX_SIZE)
2139 return 500;
2140
2141 // Read message
2142 if (nLen > 0)
2143 {
2144 vector<char> vch(nLen);
2145 stream.read(&vch[0], nLen);
2146 strMessageRet = string(vch.begin(), vch.end());
2147 }
2148
2149 return nStatus;
2150}
2151
2152bool HTTPAuthorized(map<string, string>& mapHeaders)
2153{
2154 string strAuth = mapHeaders["authorization"];
2155 if (strAuth.substr(0,6) != "Basic ")
2156 return false;
2157 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2158 string strUserPass = DecodeBase64(strUserPass64);
2159 return strUserPass == strRPCUserColonPass;
2160}
2161
2162//
2163// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2164// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2165// unspecified (HTTP errors and contents of 'error').
2166//
2167// 1.0 spec: http://json-rpc.org/wiki/specification
2168// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2169// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2170//
2171
2172string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2173{
2174 Object request;
2175 request.push_back(Pair("method", strMethod));
2176 request.push_back(Pair("params", params));
2177 request.push_back(Pair("id", id));
2178 return write_string(Value(request), false) + "\n";
2179}
2180
2181string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2182{
2183 Object reply;
2184 if (error.type() != null_type)
2185 reply.push_back(Pair("result", Value::null));
2186 else
2187 reply.push_back(Pair("result", result));
2188 reply.push_back(Pair("error", error));
2189 reply.push_back(Pair("id", id));
2190 return write_string(Value(reply), false) + "\n";
2191}
2192
2193void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2194{
2195 // Send error reply from json-rpc error object
2196 int nStatus = 500;
2197 int code = find_value(objError, "code").get_int();
2198 if (code == -32600) nStatus = 400;
2199 else if (code == -32601) nStatus = 404;
2200 string strReply = JSONRPCReply(Value::null, objError, id);
2201 stream << HTTPReply(nStatus, strReply) << std::flush;
2202}
2203
2204bool ClientAllowed(const string& strAddress)
2205{
2206 if (strAddress == asio::ip::address_v4::loopback().to_string())
2207 return true;
2208 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2209 BOOST_FOREACH(string strAllow, vAllow)
2210 if (WildcardMatch(strAddress, strAllow))
2211 return true;
2212 return false;
2213}
2214
2215void ThreadRPCServer(void* parg)
2216{
2217 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2218 try
2219 {
2220 vnThreadsRunning[4]++;
2221 ThreadRPCServer2(parg);
2222 vnThreadsRunning[4]--;
2223 }
2224 catch (std::exception& e) {
2225 vnThreadsRunning[4]--;
2226 PrintException(&e, "ThreadRPCServer()");
2227 } catch (...) {
2228 vnThreadsRunning[4]--;
2229 PrintException(NULL, "ThreadRPCServer()");
2230 }
2231 printf("ThreadRPCServer exiting\n");
2232}
2233
2234void ThreadRPCServer2(void* parg)
2235{
2236 printf("ThreadRPCServer started\n");
2237
2238 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2239 if (strRPCUserColonPass == ":")
2240 {
2241 unsigned char rand_pwd[32];
2242 RAND_bytes(rand_pwd, 32);
2243 string strWhatAmI = "To use bitcoind";
2244 if (mapArgs.count("-server"))
2245 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2246 else if (mapArgs.count("-daemon"))
2247 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2248 PrintConsole(
2249 _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
2250 "It is recommended you use the following random password:\n"
2251 "rpcuser=bitcoinrpc\n"
2252 "rpcpassword=%s\n"
2253 "(you do not need to remember this password)\n"
2254 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2255 strWhatAmI.c_str(),
2256 GetConfigFile().c_str(),
2257 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
2258 CreateThread(Shutdown, NULL);
2259 return;
2260 }
2261
2262 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2263
2264 asio::io_service io_service;
2265 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
2266 ip::tcp::acceptor acceptor(io_service, endpoint);
2267
2268 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2269
2270 loop
2271 {
2272 // Accept connection
2273 ip::tcp::iostream stream;
2274
2275 ip::tcp::endpoint peer;
2276 vnThreadsRunning[4]--;
2277 acceptor.accept(*stream.rdbuf(), peer);
2278 vnThreadsRunning[4]++;
2279 if (fShutdown)
2280 return;
2281
2282 // Restrict callers by IP
2283 if (!ClientAllowed(peer.address().to_string()))
2284 {
2285 // snipsnipsnip
2286 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2287 //if (!fUseSSL)
2288 stream << HTTPReply(403, "") << std::flush;
2289 continue;
2290 }
2291
2292 map<string, string> mapHeaders;
2293 string strRequest;
2294
2295 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2296 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2297 { // Timed out:
2298 acceptor.cancel();
2299 printf("ThreadRPCServer ReadHTTP timeout\n");
2300 continue;
2301 }
2302
2303 // Check authorization
2304 if (mapHeaders.count("authorization") == 0)
2305 {
2306 stream << HTTPReply(401, "") << std::flush;
2307 continue;
2308 }
2309 if (!HTTPAuthorized(mapHeaders))
2310 {
2311 printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str());
2312 /* Deter brute-forcing short passwords.
2313 If this results in a DOS the user really
2314 shouldn't have their RPC port exposed.*/
2315 if (mapArgs["-rpcpassword"].size() < 20)
2316 Sleep(250);
2317
2318 stream << HTTPReply(401, "") << std::flush;
2319 continue;
2320 }
2321
2322 Value id = Value::null;
2323 try
2324 {
2325 // Parse request
2326 Value valRequest;
2327 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2328 throw JSONRPCError(-32700, "Parse error");
2329 const Object& request = valRequest.get_obj();
2330
2331 // Parse id now so errors from here on will have the id
2332 id = find_value(request, "id");
2333
2334 // Parse method
2335 Value valMethod = find_value(request, "method");
2336 if (valMethod.type() == null_type)
2337 throw JSONRPCError(-32600, "Missing method");
2338 if (valMethod.type() != str_type)
2339 throw JSONRPCError(-32600, "Method must be a string");
2340 string strMethod = valMethod.get_str();
2341 if (strMethod != "getwork" && strMethod != "getmemorypool")
2342 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2343
2344 // Parse params
2345 Value valParams = find_value(request, "params");
2346 Array params;
2347 if (valParams.type() == array_type)
2348 params = valParams.get_array();
2349 else if (valParams.type() == null_type)
2350 params = Array();
2351 else
2352 throw JSONRPCError(-32600, "Params must be an array");
2353
2354 // Find method
2355 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2356 if (mi == mapCallTable.end())
2357 throw JSONRPCError(-32601, "Method not found");
2358
2359 // Observe safe mode
2360 if (!fDisableSafeMode) {
2361 string strWarning = GetWarnings("rpc");
2362 if (strWarning != "" && !setAllowInSafeMode.count(strMethod))
2363 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2364 }
2365
2366 try
2367 {
2368 // Execute
2369 Value result;
2370 CRITICAL_BLOCK(cs_main)
2371 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2372 result = (*(*mi).second)(params, false);
2373
2374 // Send reply
2375 string strReply = JSONRPCReply(result, Value::null, id);
2376 stream << HTTPReply(200, strReply) << std::flush;
2377 }
2378 catch (std::exception& e)
2379 {
2380 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2381 }
2382 }
2383 catch (Object& objError)
2384 {
2385 ErrorReply(stream, objError, id);
2386 }
2387 catch (std::exception& e)
2388 {
2389 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2390 }
2391 }
2392}
2393
2394
2395
2396
2397Object CallRPC(const string& strMethod, const Array& params)
2398{
2399 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2400 throw runtime_error(strprintf(
2401 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2402 "If the file does not exist, create it with owner-readable-only file permissions."),
2403 GetConfigFile().c_str()));
2404
2405 // Connect to localhost
2406 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
2407 if (stream.fail())
2408 throw runtime_error("couldn't connect to server");
2409
2410 // HTTP basic authentication
2411 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2412 map<string, string> mapRequestHeaders;
2413 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2414
2415 // Send request
2416 string strRequest = JSONRPCRequest(strMethod, params, 1);
2417 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2418 stream << strPost << std::flush;
2419
2420 // Receive reply
2421 map<string, string> mapHeaders;
2422 string strReply;
2423 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2424 if (nStatus == 401)
2425 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2426 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2427 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2428 else if (strReply.empty())
2429 throw runtime_error("no response from server");
2430
2431 // Parse reply
2432 Value valReply;
2433 if (!read_string(strReply, valReply))
2434 throw runtime_error("couldn't parse reply from server");
2435 const Object& reply = valReply.get_obj();
2436 if (reply.empty())
2437 throw runtime_error("expected reply to have result, error and id properties");
2438
2439 return reply;
2440}
2441
2442
2443
2444
2445template<typename T>
2446void ConvertTo(Value& value)
2447{
2448 if (value.type() == str_type)
2449 {
2450 // reinterpret string as unquoted json value
2451 Value value2;
2452 if (!read_string(value.get_str(), value2))
2453 throw runtime_error("type mismatch");
2454 value = value2.get_value<T>();
2455 }
2456 else
2457 {
2458 value = value.get_value<T>();
2459 }
2460}
2461
2462int CommandLineRPC(int argc, char *argv[])
2463{
2464 string strPrint;
2465 int nRet = 0;
2466 try
2467 {
2468 // Skip switches
2469 while (argc > 1 && IsSwitchChar(argv[1][0]))
2470 {
2471 argc--;
2472 argv++;
2473 }
2474
2475 // Method
2476 if (argc < 2)
2477 throw runtime_error("too few parameters");
2478 string strMethod = argv[1];
2479
2480 // Parameters default to strings
2481 Array params;
2482 for (int i = 2; i < argc; i++)
2483 params.push_back(argv[i]);
2484 int n = params.size();
2485
2486 //
2487 // Special case non-string parameter types
2488 //
2489 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2490 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2491 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2492 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2493 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2494 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2495 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2496 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2497 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2498 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2499 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2500 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2501 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2502 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2503 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2504 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2505 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2506 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2507 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2508 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2509 if (strMethod == "dumpblock" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2510 if (strMethod == "sendmany" && n > 1)
2511 {
2512 string s = params[1].get_str();
2513 Value v;
2514 if (!read_string(s, v) || v.type() != obj_type)
2515 throw runtime_error("type mismatch");
2516 params[1] = v.get_obj();
2517 }
2518 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2519
2520 // Execute
2521 Object reply = CallRPC(strMethod, params);
2522
2523 // Parse reply
2524 const Value& result = find_value(reply, "result");
2525 const Value& error = find_value(reply, "error");
2526
2527 if (error.type() != null_type)
2528 {
2529 // Error
2530 strPrint = "error: " + write_string(error, false);
2531 int code = find_value(error.get_obj(), "code").get_int();
2532 nRet = abs(code);
2533 }
2534 else
2535 {
2536 // Result
2537 if (result.type() == null_type)
2538 strPrint = "";
2539 else if (result.type() == str_type)
2540 strPrint = result.get_str();
2541 else
2542 strPrint = write_string(result, true);
2543 }
2544 }
2545 catch (std::exception& e)
2546 {
2547 strPrint = string("error: ") + e.what();
2548 nRet = 87;
2549 }
2550 catch (...)
2551 {
2552 PrintException(NULL, "CommandLineRPC()");
2553 }
2554
2555 if (strPrint != "")
2556 {
2557 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2558 }
2559 return nRet;
2560}
2561
2562
2563
2564
2565#ifdef TEST
2566int main(int argc, char *argv[])
2567{
2568 setbuf(stdin, NULL);
2569 setbuf(stdout, NULL);
2570 setbuf(stderr, NULL);
2571
2572 try
2573 {
2574 if (argc >= 2 && string(argv[1]) == "-server")
2575 {
2576 printf("server ready\n");
2577 ThreadRPCServer(NULL);
2578 }
2579 else
2580 {
2581 return CommandLineRPC(argc, argv);
2582 }
2583 }
2584 catch (std::exception& e) {
2585 PrintException(&e, "main()");
2586 } catch (...) {
2587 PrintException(NULL, "main()");
2588 }
2589 return 0;
2590}
2591#endif