Projects : bitcoin : bitcoin_checkblocks_cleanup

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