Projects : bitcoin : bitcoin_help_tuneups

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#include <boost/iostreams/concepts.hpp>
14#include <boost/iostreams/stream.hpp>
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 if (!fCanEat)
1806 throw runtime_error(
1807 "'eatblock' is only permitted if bitcoind was started with -caneat flag!");
1808
1809 // path to load block from
1810 string filename = params[0].get_str();
1811
1812 printf("Attempting to create block #%d from file %s\n", nBestHeight + 1, filename.c_str());
1813 CAutoFile filein = fopen(filename.c_str(), "rb");
1814 CBlock block;
1815 filein >> block;
1816 return ProcessBlock(NULL, &block); // note that 'true' even if it was rejected (bastard, etc)
1817} // ... but will return 'false' if we already have the block.
1818
1819
1820Value importprivkey(const Array& params, bool fHelp)
1821{
1822 if (fHelp || params.size() < 1 || params.size() > 2)
1823 throw runtime_error(
1824 "importprivkey <bitcoinprivkey> [label]\n"
1825 "Adds a private key (as returned by dumpprivkey) to your wallet.");
1826
1827 string strSecret = params[0].get_str();
1828 string strLabel = "";
1829 if (params.size() > 1)
1830 strLabel = params[1].get_str();
1831 CBitcoinSecret vchSecret;
1832 bool fGood = vchSecret.SetString(strSecret);
1833
1834 if (!fGood) throw JSONRPCError(-5,"Invalid private key");
1835
1836 CKey key;
1837 CSecret secret = vchSecret.GetSecret();
1838 key.SetSecret(secret);
1839 CBitcoinAddress vchAddress = CBitcoinAddress(key.GetPubKey());
1840
1841 CRITICAL_BLOCK(cs_main)
1842 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1843 {
1844 pwalletMain->MarkDirty();
1845 pwalletMain->SetAddressBookName(vchAddress, strLabel);
1846
1847 if (!pwalletMain->AddKey(key))
1848 throw JSONRPCError(-4,"Error adding key to wallet");
1849
1850 pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
1851 pwalletMain->ReacceptWalletTransactions();
1852 }
1853
1854 return Value::null;
1855}
1856
1857Value dumpprivkey(const Array& params, bool fHelp)
1858{
1859 if (fHelp || params.size() != 1)
1860 throw runtime_error(
1861 "dumpprivkey <bitcoinaddress>\n"
1862 "Reveals the private key corresponding to <bitcoinaddress>.");
1863
1864 string strAddress = params[0].get_str();
1865 CBitcoinAddress address;
1866 if (!address.SetString(strAddress))
1867 throw JSONRPCError(-5, "Invalid bitcoin address");
1868 CSecret vchSecret;
1869 if (!pwalletMain->GetSecret(address, vchSecret))
1870 throw JSONRPCError(-4,"Private key for address " + strAddress + " is not known");
1871 return CBitcoinSecret(vchSecret).ToString();
1872}
1873
1874Value getrawtransaction(const Array& params, bool fHelp)
1875{
1876 if (fHelp || params.size() != 1)
1877 throw runtime_error(
1878 "getrawtransaction <txid>\n"
1879 "Get hex serialization of <txid> from memory pool or database.");
1880
1881 uint256 hash;
1882 hash.SetHex(params[0].get_str());
1883
1884 CTransaction tx;
1885 if (!GetMempoolTx(hash, tx) && !CTxDB("r").ReadDiskTx(hash, tx))
1886 throw JSONRPCError(-5, "Transaction not found in memory pool or database.");
1887
1888 CDataStream ssTx;
1889 ssTx << tx;
1890 return HexStr(ssTx.begin(), ssTx.end());
1891}
1892
1893Value sendrawtransaction(const Array& params, bool fHelp)
1894{
1895 if (fHelp || params.size() < 1 || params.size() > 1)
1896 throw runtime_error(
1897 "sendrawtransaction <hex string>\n"
1898 "Submits raw transaction (serialized, hex-encoded) to local node and network.");
1899
1900 CDataStream ssData(ParseHex(params[0].get_str()));
1901 CTransaction tx;
1902 try
1903 {
1904 ssData >> tx;
1905 }
1906 catch (std::exception &e)
1907 {
1908 throw JSONRPCError(-22, "tx decode failed");
1909 }
1910
1911 uint256 hash = tx.GetHash();
1912 if (!MempoolContainsTx(hash))
1913 {
1914 if (!tx.AcceptToMemoryPool(true))
1915 {
1916 if (CTxDB("r").ContainsTx(hash))
1917 throw JSONRPCError(-27, "tx already included in block");
1918 throw JSONRPCError(-25, "tx rejected, see log for details");
1919 }
1920
1921 SyncWithWallets(tx, NULL, true);
1922 }
1923
1924 CInv inv(MSG_TX, hash);
1925 RelayMessage(inv, tx);
1926
1927 return hash.GetHex();
1928}
1929
1930
1931//
1932// Call Table
1933//
1934
1935pair<string, rpcfn_type> pCallTable[] =
1936{
1937 make_pair("help", &help),
1938 make_pair("stop", &stop),
1939 make_pair("getblockcount", &getblockcount),
1940 make_pair("getblocknumber", &getblocknumber),
1941 make_pair("getconnectioncount", &getconnectioncount),
1942 make_pair("getdifficulty", &getdifficulty),
1943 make_pair("getgenerate", &getgenerate),
1944 make_pair("setgenerate", &setgenerate),
1945 make_pair("gethashespersec", &gethashespersec),
1946 make_pair("getinfo", &getinfo),
1947 make_pair("getnewaddress", &getnewaddress),
1948 make_pair("getaccountaddress", &getaccountaddress),
1949 make_pair("setaccount", &setaccount),
1950 make_pair("getaccount", &getaccount),
1951 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1952 make_pair("sendtoaddress", &sendtoaddress),
1953 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1954 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1955 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1956 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1957 make_pair("backupwallet", &backupwallet),
1958 make_pair("keypoolrefill", &keypoolrefill),
1959 make_pair("walletpassphrase", &walletpassphrase),
1960 make_pair("walletpassphrasechange", &walletpassphrasechange),
1961 make_pair("walletlock", &walletlock),
1962 make_pair("encryptwallet", &encryptwallet),
1963 make_pair("validateaddress", &validateaddress),
1964 make_pair("getbalance", &getbalance),
1965 make_pair("move", &movecmd),
1966 make_pair("sendfrom", &sendfrom),
1967 make_pair("sendmany", &sendmany),
1968 make_pair("gettransaction", &gettransaction),
1969 make_pair("listtransactions", &listtransactions),
1970 make_pair("signmessage", &signmessage),
1971 make_pair("verifymessage", &verifymessage),
1972 make_pair("getwork", &getwork),
1973 make_pair("listaccounts", &listaccounts),
1974 make_pair("settxfee", &settxfee),
1975 make_pair("getmemorypool", &getmemorypool),
1976 make_pair("listsinceblock", &listsinceblock),
1977 make_pair("dumpblock", &dumpblock),
1978 make_pair("eatblock", &eatblock),
1979 make_pair("importprivkey", &importprivkey),
1980 make_pair("dumpprivkey", &dumpprivkey),
1981 make_pair("getrawtransaction", &getrawtransaction),
1982 make_pair("sendrawtransaction", &sendrawtransaction),
1983};
1984map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1985
1986string pAllowInSafeMode[] =
1987{
1988 "help",
1989 "stop",
1990 "getblockcount",
1991 "getblocknumber", // deprecated
1992 "getconnectioncount",
1993 "getdifficulty",
1994 "getgenerate",
1995 "setgenerate",
1996 "gethashespersec",
1997 "getinfo",
1998 "getnewaddress",
1999 "getaccountaddress",
2000 "getaccount",
2001 "getaddressesbyaccount",
2002 "backupwallet",
2003 "keypoolrefill",
2004 "walletpassphrase",
2005 "walletlock",
2006 "validateaddress",
2007 "getwork",
2008 "getmemorypool",
2009 "dumpblock",
2010};
2011set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2012
2013
2014
2015
2016//
2017// HTTP protocol
2018//
2019// This ain't Apache. We're just using HTTP header for the length field
2020// and to be compatible with other JSON-RPC implementations.
2021//
2022
2023string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2024{
2025 ostringstream s;
2026 s << "POST / HTTP/1.1\r\n"
2027 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2028 << "Host: 127.0.0.1\r\n"
2029 << "Content-Type: application/json\r\n"
2030 << "Content-Length: " << strMsg.size() << "\r\n"
2031 << "Connection: close\r\n"
2032 << "Accept: application/json\r\n";
2033 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2034 s << item.first << ": " << item.second << "\r\n";
2035 s << "\r\n" << strMsg;
2036
2037 return s.str();
2038}
2039
2040string rfc1123Time()
2041{
2042 char buffer[64];
2043 time_t now;
2044 time(&now);
2045 struct tm* now_gmt = gmtime(&now);
2046 string locale(setlocale(LC_TIME, NULL));
2047 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2048 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2049 setlocale(LC_TIME, locale.c_str());
2050 return string(buffer);
2051}
2052
2053static string HTTPReply(int nStatus, const string& strMsg)
2054{
2055 if (nStatus == 401)
2056 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2057 "Date: %s\r\n"
2058 "Server: bitcoin-json-rpc/%s\r\n"
2059 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2060 "Content-Type: text/html\r\n"
2061 "Content-Length: 296\r\n"
2062 "\r\n"
2063 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2064 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2065 "<HTML>\r\n"
2066 "<HEAD>\r\n"
2067 "<TITLE>Error</TITLE>\r\n"
2068 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2069 "</HEAD>\r\n"
2070 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2071 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2072 const char *cStatus;
2073 if (nStatus == 200) cStatus = "OK";
2074 else if (nStatus == 400) cStatus = "Bad Request";
2075 else if (nStatus == 403) cStatus = "Forbidden";
2076 else if (nStatus == 404) cStatus = "Not Found";
2077 else if (nStatus == 500) cStatus = "Internal Server Error";
2078 else cStatus = "";
2079 return strprintf(
2080 "HTTP/1.1 %d %s\r\n"
2081 "Date: %s\r\n"
2082 "Connection: close\r\n"
2083 "Content-Length: %d\r\n"
2084 "Content-Type: application/json\r\n"
2085 "Server: bitcoin-json-rpc/%s\r\n"
2086 "\r\n"
2087 "%s",
2088 nStatus,
2089 cStatus,
2090 rfc1123Time().c_str(),
2091 strMsg.size(),
2092 FormatFullVersion().c_str(),
2093 strMsg.c_str());
2094}
2095
2096int ReadHTTPStatus(std::basic_istream<char>& stream)
2097{
2098 string str;
2099 getline(stream, str);
2100 vector<string> vWords;
2101 boost::split(vWords, str, boost::is_any_of(" "));
2102 if (vWords.size() < 2)
2103 return 500;
2104 return atoi(vWords[1].c_str());
2105}
2106
2107int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2108{
2109 int nLen = 0;
2110 loop
2111 {
2112 string str;
2113 std::getline(stream, str);
2114 if (str.empty() || str == "\r")
2115 break;
2116 string::size_type nColon = str.find(":");
2117 if (nColon != string::npos)
2118 {
2119 string strHeader = str.substr(0, nColon);
2120 boost::trim(strHeader);
2121 boost::to_lower(strHeader);
2122 string strValue = str.substr(nColon+1);
2123 boost::trim(strValue);
2124 mapHeadersRet[strHeader] = strValue;
2125 if (strHeader == "content-length")
2126 nLen = atoi(strValue.c_str());
2127 }
2128 }
2129 return nLen;
2130}
2131
2132int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2133{
2134 mapHeadersRet.clear();
2135 strMessageRet = "";
2136
2137 // Read status
2138 int nStatus = ReadHTTPStatus(stream);
2139
2140 // Read header
2141 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2142 if (nLen < 0 || nLen > MAX_SIZE)
2143 return 500;
2144
2145 // Read message
2146 if (nLen > 0)
2147 {
2148 vector<char> vch(nLen);
2149 stream.read(&vch[0], nLen);
2150 strMessageRet = string(vch.begin(), vch.end());
2151 }
2152
2153 return nStatus;
2154}
2155
2156bool HTTPAuthorized(map<string, string>& mapHeaders)
2157{
2158 string strAuth = mapHeaders["authorization"];
2159 if (strAuth.substr(0,6) != "Basic ")
2160 return false;
2161 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2162 string strUserPass = DecodeBase64(strUserPass64);
2163 return strUserPass == strRPCUserColonPass;
2164}
2165
2166//
2167// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2168// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2169// unspecified (HTTP errors and contents of 'error').
2170//
2171// 1.0 spec: http://json-rpc.org/wiki/specification
2172// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2173// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2174//
2175
2176string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2177{
2178 Object request;
2179 request.push_back(Pair("method", strMethod));
2180 request.push_back(Pair("params", params));
2181 request.push_back(Pair("id", id));
2182 return write_string(Value(request), false) + "\n";
2183}
2184
2185string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2186{
2187 Object reply;
2188 if (error.type() != null_type)
2189 reply.push_back(Pair("result", Value::null));
2190 else
2191 reply.push_back(Pair("result", result));
2192 reply.push_back(Pair("error", error));
2193 reply.push_back(Pair("id", id));
2194 return write_string(Value(reply), false) + "\n";
2195}
2196
2197void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2198{
2199 // Send error reply from json-rpc error object
2200 int nStatus = 500;
2201 int code = find_value(objError, "code").get_int();
2202 if (code == -32600) nStatus = 400;
2203 else if (code == -32601) nStatus = 404;
2204 string strReply = JSONRPCReply(Value::null, objError, id);
2205 stream << HTTPReply(nStatus, strReply) << std::flush;
2206}
2207
2208bool ClientAllowed(const string& strAddress)
2209{
2210 if (strAddress == asio::ip::address_v4::loopback().to_string())
2211 return true;
2212 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2213 BOOST_FOREACH(string strAllow, vAllow)
2214 if (WildcardMatch(strAddress, strAllow))
2215 return true;
2216 return false;
2217}
2218
2219void ThreadRPCServer(void* parg)
2220{
2221 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2222 try
2223 {
2224 vnThreadsRunning[4]++;
2225 ThreadRPCServer2(parg);
2226 vnThreadsRunning[4]--;
2227 }
2228 catch (std::exception& e) {
2229 vnThreadsRunning[4]--;
2230 PrintException(&e, "ThreadRPCServer()");
2231 } catch (...) {
2232 vnThreadsRunning[4]--;
2233 PrintException(NULL, "ThreadRPCServer()");
2234 }
2235 printf("ThreadRPCServer exiting\n");
2236}
2237
2238void ThreadRPCServer2(void* parg)
2239{
2240 printf("ThreadRPCServer started\n");
2241
2242 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2243 if (strRPCUserColonPass == ":")
2244 {
2245 unsigned char rand_pwd[32];
2246 RAND_bytes(rand_pwd, 32);
2247 string strWhatAmI = "To use bitcoind";
2248 if (mapArgs.count("-server"))
2249 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2250 else if (mapArgs.count("-daemon"))
2251 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2252 PrintConsole(
2253 _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
2254 "It is recommended you use the following random password:\n"
2255 "rpcuser=bitcoinrpc\n"
2256 "rpcpassword=%s\n"
2257 "(you do not need to remember this password)\n"
2258 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2259 strWhatAmI.c_str(),
2260 GetConfigFile().c_str(),
2261 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
2262 CreateThread(Shutdown, NULL);
2263 return;
2264 }
2265
2266 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2267
2268 asio::io_service io_service;
2269 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
2270 ip::tcp::acceptor acceptor(io_service, endpoint);
2271
2272 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2273
2274 loop
2275 {
2276 // Accept connection
2277 ip::tcp::iostream stream;
2278
2279 ip::tcp::endpoint peer;
2280 vnThreadsRunning[4]--;
2281 acceptor.accept(*stream.rdbuf(), peer);
2282 vnThreadsRunning[4]++;
2283 if (fShutdown)
2284 return;
2285
2286 // Restrict callers by IP
2287 if (!ClientAllowed(peer.address().to_string()))
2288 {
2289 // snipsnipsnip
2290 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2291 //if (!fUseSSL)
2292 stream << HTTPReply(403, "") << std::flush;
2293 continue;
2294 }
2295
2296 map<string, string> mapHeaders;
2297 string strRequest;
2298
2299 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2300 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2301 { // Timed out:
2302 acceptor.cancel();
2303 printf("ThreadRPCServer ReadHTTP timeout\n");
2304 continue;
2305 }
2306
2307 // Check authorization
2308 if (mapHeaders.count("authorization") == 0)
2309 {
2310 stream << HTTPReply(401, "") << std::flush;
2311 continue;
2312 }
2313 if (!HTTPAuthorized(mapHeaders))
2314 {
2315 printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str());
2316 /* Deter brute-forcing short passwords.
2317 If this results in a DOS the user really
2318 shouldn't have their RPC port exposed.*/
2319 if (mapArgs["-rpcpassword"].size() < 20)
2320 Sleep(250);
2321
2322 stream << HTTPReply(401, "") << std::flush;
2323 continue;
2324 }
2325
2326 Value id = Value::null;
2327 try
2328 {
2329 // Parse request
2330 Value valRequest;
2331 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2332 throw JSONRPCError(-32700, "Parse error");
2333 const Object& request = valRequest.get_obj();
2334
2335 // Parse id now so errors from here on will have the id
2336 id = find_value(request, "id");
2337
2338 // Parse method
2339 Value valMethod = find_value(request, "method");
2340 if (valMethod.type() == null_type)
2341 throw JSONRPCError(-32600, "Missing method");
2342 if (valMethod.type() != str_type)
2343 throw JSONRPCError(-32600, "Method must be a string");
2344 string strMethod = valMethod.get_str();
2345 if (strMethod != "getwork" && strMethod != "getmemorypool")
2346 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2347
2348 // Parse params
2349 Value valParams = find_value(request, "params");
2350 Array params;
2351 if (valParams.type() == array_type)
2352 params = valParams.get_array();
2353 else if (valParams.type() == null_type)
2354 params = Array();
2355 else
2356 throw JSONRPCError(-32600, "Params must be an array");
2357
2358 // Find method
2359 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2360 if (mi == mapCallTable.end())
2361 throw JSONRPCError(-32601, "Method not found");
2362
2363 // Observe safe mode
2364 if (!fDisableSafeMode) {
2365 string strWarning = GetWarnings("rpc");
2366 if (strWarning != "" && !setAllowInSafeMode.count(strMethod))
2367 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2368 }
2369
2370 try
2371 {
2372 // Execute
2373 Value result;
2374 CRITICAL_BLOCK(cs_main)
2375 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2376 result = (*(*mi).second)(params, false);
2377
2378 // Send reply
2379 string strReply = JSONRPCReply(result, Value::null, id);
2380 stream << HTTPReply(200, strReply) << std::flush;
2381 }
2382 catch (std::exception& e)
2383 {
2384 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2385 }
2386 }
2387 catch (Object& objError)
2388 {
2389 ErrorReply(stream, objError, id);
2390 }
2391 catch (std::exception& e)
2392 {
2393 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2394 }
2395 }
2396}
2397
2398
2399
2400
2401Object CallRPC(const string& strMethod, const Array& params)
2402{
2403 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2404 throw runtime_error(strprintf(
2405 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2406 "If the file does not exist, create it with owner-readable-only file permissions."),
2407 GetConfigFile().c_str()));
2408
2409 // Connect to localhost
2410 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
2411 if (stream.fail())
2412 throw runtime_error("couldn't connect to server");
2413
2414 // HTTP basic authentication
2415 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2416 map<string, string> mapRequestHeaders;
2417 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2418
2419 // Send request
2420 string strRequest = JSONRPCRequest(strMethod, params, 1);
2421 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2422 stream << strPost << std::flush;
2423
2424 // Receive reply
2425 map<string, string> mapHeaders;
2426 string strReply;
2427 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2428 if (nStatus == 401)
2429 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2430 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2431 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2432 else if (strReply.empty())
2433 throw runtime_error("no response from server");
2434
2435 // Parse reply
2436 Value valReply;
2437 if (!read_string(strReply, valReply))
2438 throw runtime_error("couldn't parse reply from server");
2439 const Object& reply = valReply.get_obj();
2440 if (reply.empty())
2441 throw runtime_error("expected reply to have result, error and id properties");
2442
2443 return reply;
2444}
2445
2446
2447
2448
2449template<typename T>
2450void ConvertTo(Value& value)
2451{
2452 if (value.type() == str_type)
2453 {
2454 // reinterpret string as unquoted json value
2455 Value value2;
2456 if (!read_string(value.get_str(), value2))
2457 throw runtime_error("type mismatch");
2458 value = value2.get_value<T>();
2459 }
2460 else
2461 {
2462 value = value.get_value<T>();
2463 }
2464}
2465
2466int CommandLineRPC(int argc, char *argv[])
2467{
2468 string strPrint;
2469 int nRet = 0;
2470 try
2471 {
2472 // Skip switches
2473 while (argc > 1 && IsSwitchChar(argv[1][0]))
2474 {
2475 argc--;
2476 argv++;
2477 }
2478
2479 // Method
2480 if (argc < 2)
2481 throw runtime_error("too few parameters");
2482 string strMethod = argv[1];
2483
2484 // Parameters default to strings
2485 Array params;
2486 for (int i = 2; i < argc; i++)
2487 params.push_back(argv[i]);
2488 int n = params.size();
2489
2490 //
2491 // Special case non-string parameter types
2492 //
2493 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2494 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2495 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2496 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2497 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2498 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2499 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2500 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2501 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2502 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2503 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2504 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2505 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2506 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2507 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2508 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2509 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2510 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2511 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2512 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2513 if (strMethod == "dumpblock" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2514 if (strMethod == "sendmany" && n > 1)
2515 {
2516 string s = params[1].get_str();
2517 Value v;
2518 if (!read_string(s, v) || v.type() != obj_type)
2519 throw runtime_error("type mismatch");
2520 params[1] = v.get_obj();
2521 }
2522 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2523
2524 // Execute
2525 Object reply = CallRPC(strMethod, params);
2526
2527 // Parse reply
2528 const Value& result = find_value(reply, "result");
2529 const Value& error = find_value(reply, "error");
2530
2531 if (error.type() != null_type)
2532 {
2533 // Error
2534 strPrint = "error: " + write_string(error, false);
2535 int code = find_value(error.get_obj(), "code").get_int();
2536 nRet = abs(code);
2537 }
2538 else
2539 {
2540 // Result
2541 if (result.type() == null_type)
2542 strPrint = "";
2543 else if (result.type() == str_type)
2544 strPrint = result.get_str();
2545 else
2546 strPrint = write_string(result, true);
2547 }
2548 }
2549 catch (std::exception& e)
2550 {
2551 strPrint = string("error: ") + e.what();
2552 nRet = 87;
2553 }
2554 catch (...)
2555 {
2556 PrintException(NULL, "CommandLineRPC()");
2557 }
2558
2559 if (strPrint != "")
2560 {
2561 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2562 }
2563 return nRet;
2564}
2565
2566
2567
2568
2569#ifdef TEST
2570int main(int argc, char *argv[])
2571{
2572 setbuf(stdin, NULL);
2573 setbuf(stdout, NULL);
2574 setbuf(stderr, NULL);
2575
2576 try
2577 {
2578 if (argc >= 2 && string(argv[1]) == "-server")
2579 {
2580 printf("server ready\n");
2581 ThreadRPCServer(NULL);
2582 }
2583 else
2584 {
2585 return CommandLineRPC(argc, argv);
2586 }
2587 }
2588 catch (std::exception& e) {
2589 PrintException(&e, "main()");
2590 } catch (...) {
2591 PrintException(NULL, "main()");
2592 }
2593 return 0;
2594}
2595#endif