Projects : bitcoin : bitcoin_rebranding

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
1942Value getblockindex(const Array &params, bool fHelp)
1943{
1944 if (fHelp || params.size() != 1)
1945 throw runtime_error(
1946 "getblockindex <blockhash>\n"
1947 "Returns a record from the block index, containing metadata on the given block:\n"
1948 " \"hash\" : block hash (hex string or null; should be the same as provided)\n"
1949 " \"previousblockhash\" : hash of the previous block in chain (hex string or null)\n"
1950 " \"nextblockhash\" : hash of the next block in chain (hex string or null)\n"
1951 " \"file\" : block file number on disk\n"
1952 " \"blockpos\" : byte position in block file (number)\n"
1953 " \"height\" : height of block in chain (number)\n"
1954 " \"chainwork\" : total chain work (bignum, as hex string)\n"
1955 " \"version\" : block version (number)\n"
1956 " \"merkleroot\" : merkle root (hex string)\n"
1957 " \"time\" : block time in seconds since epoch (number)\n"
1958 " \"bits\" : difficulty target in compact format (32-bit hex string)\n"
1959 " \"nonce\" : block nonce (number)"
1960 );
1961
1962 uint256 hashBlock = 0;
1963 hashBlock.SetHex(params[0].get_str());
1964
1965 if (mapBlockIndex.count(hashBlock) == 0)
1966 throw JSONRPCError(-5, "Block not found");
1967 CBlockIndex *pindex = mapBlockIndex[hashBlock];
1968 if (!pindex)
1969 throw runtime_error("BUG: null mapBlockIndex entry");
1970
1971 Object result;
1972 result.push_back(Pair("hash", pindex->phashBlock
1973 ? pindex->phashBlock->GetHex()
1974 : Value::null));
1975 result.push_back(Pair("previousblockhash", pindex->pprev && pindex->pprev->phashBlock
1976 ? pindex->pprev->phashBlock->GetHex()
1977 : Value::null));
1978 result.push_back(Pair("nextblockhash", pindex->pnext && pindex->pnext->phashBlock
1979 ? pindex->pnext->phashBlock->GetHex()
1980 : Value::null));
1981 result.push_back(Pair("file", (boost::int64_t)pindex->nFile));
1982 result.push_back(Pair("blockpos", (boost::int64_t)pindex->nBlockPos));
1983 result.push_back(Pair("height", pindex->nHeight));
1984 result.push_back(Pair("chainwork", pindex->bnChainWork.GetHex()));
1985 // block header
1986 result.push_back(Pair("version", pindex->nVersion));
1987 result.push_back(Pair("merkleroot", pindex->hashMerkleRoot.GetHex()));
1988 result.push_back(Pair("time", (boost::int64_t)pindex->nTime));
1989 result.push_back(Pair("bits", strprintf("%08x", pindex->nBits)));
1990 // Including the decoded difficulty target as a double might be nice here, as from GetDifficulty(), but I haven't figured out how that encoding works and whether such conversion is precise. -jfw
1991 result.push_back(Pair("nonce", (boost::int64_t)pindex->nNonce));
1992 return result;
1993}
1994
1995
1996//
1997// Call Table
1998//
1999
2000pair<string, rpcfn_type> pCallTable[] =
2001{
2002 make_pair("help", &help),
2003 make_pair("stop", &stop),
2004 make_pair("getblockcount", &getblockcount),
2005 make_pair("getblocknumber", &getblocknumber),
2006 make_pair("getconnectioncount", &getconnectioncount),
2007 make_pair("getdifficulty", &getdifficulty),
2008 make_pair("getgenerate", &getgenerate),
2009 make_pair("setgenerate", &setgenerate),
2010 make_pair("gethashespersec", &gethashespersec),
2011 make_pair("getinfo", &getinfo),
2012 make_pair("getnewaddress", &getnewaddress),
2013 make_pair("getaccountaddress", &getaccountaddress),
2014 make_pair("setaccount", &setaccount),
2015 make_pair("getaccount", &getaccount),
2016 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2017 make_pair("sendtoaddress", &sendtoaddress),
2018 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2019 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2020 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2021 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2022 make_pair("backupwallet", &backupwallet),
2023 make_pair("keypoolrefill", &keypoolrefill),
2024 make_pair("walletpassphrase", &walletpassphrase),
2025 make_pair("walletpassphrasechange", &walletpassphrasechange),
2026 make_pair("walletlock", &walletlock),
2027 make_pair("encryptwallet", &encryptwallet),
2028 make_pair("validateaddress", &validateaddress),
2029 make_pair("getbalance", &getbalance),
2030 make_pair("move", &movecmd),
2031 make_pair("sendfrom", &sendfrom),
2032 make_pair("sendmany", &sendmany),
2033 make_pair("gettransaction", &gettransaction),
2034 make_pair("listtransactions", &listtransactions),
2035 make_pair("signmessage", &signmessage),
2036 make_pair("verifymessage", &verifymessage),
2037 make_pair("getwork", &getwork),
2038 make_pair("listaccounts", &listaccounts),
2039 make_pair("settxfee", &settxfee),
2040 make_pair("setminrelaytxfee", &setminrelaytxfee),
2041 make_pair("getmemorypool", &getmemorypool),
2042 make_pair("listsinceblock", &listsinceblock),
2043 make_pair("dumpblock", &dumpblock),
2044 make_pair("eatblock", &eatblock),
2045 make_pair("importprivkey", &importprivkey),
2046 make_pair("dumpprivkey", &dumpprivkey),
2047 make_pair("getrawtransaction", &getrawtransaction),
2048 make_pair("sendrawtransaction", &sendrawtransaction),
2049 make_pair("getblockindex", &getblockindex),
2050};
2051map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2052
2053string pAllowInSafeMode[] =
2054{
2055 "help",
2056 "stop",
2057 "getblockcount",
2058 "getblocknumber", // deprecated
2059 "getconnectioncount",
2060 "getdifficulty",
2061 "getgenerate",
2062 "setgenerate",
2063 "gethashespersec",
2064 "getinfo",
2065 "getnewaddress",
2066 "getaccountaddress",
2067 "getaccount",
2068 "getaddressesbyaccount",
2069 "backupwallet",
2070 "keypoolrefill",
2071 "walletpassphrase",
2072 "walletlock",
2073 "validateaddress",
2074 "getwork",
2075 "getmemorypool",
2076 "dumpblock",
2077 "eatblock",
2078 "getblockindex",
2079};
2080set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2081
2082
2083
2084
2085//
2086// HTTP protocol
2087//
2088// This ain't Apache. We're just using HTTP header for the length field
2089// and to be compatible with other JSON-RPC implementations.
2090//
2091
2092string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2093{
2094 ostringstream s;
2095 s << "POST / HTTP/1.1\r\n"
2096 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2097 << "Host: 127.0.0.1\r\n"
2098 << "Content-Type: application/json\r\n"
2099 << "Content-Length: " << strMsg.size() << "\r\n"
2100 << "Connection: close\r\n"
2101 << "Accept: application/json\r\n";
2102 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2103 s << item.first << ": " << item.second << "\r\n";
2104 s << "\r\n" << strMsg;
2105
2106 return s.str();
2107}
2108
2109string rfc1123Time()
2110{
2111 char buffer[64];
2112 time_t now;
2113 time(&now);
2114 struct tm* now_gmt = gmtime(&now);
2115 string locale(setlocale(LC_TIME, NULL));
2116 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2117 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2118 setlocale(LC_TIME, locale.c_str());
2119 return string(buffer);
2120}
2121
2122static string HTTPReply(int nStatus, const string& strMsg)
2123{
2124 if (nStatus == 401)
2125 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2126 "Date: %s\r\n"
2127 "Server: bitcoin-json-rpc/%s\r\n"
2128 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2129 "Content-Type: text/html\r\n"
2130 "Content-Length: 296\r\n"
2131 "\r\n"
2132 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2133 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2134 "<HTML>\r\n"
2135 "<HEAD>\r\n"
2136 "<TITLE>Error</TITLE>\r\n"
2137 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2138 "</HEAD>\r\n"
2139 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2140 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2141 const char *cStatus;
2142 if (nStatus == 200) cStatus = "OK";
2143 else if (nStatus == 400) cStatus = "Bad Request";
2144 else if (nStatus == 403) cStatus = "Forbidden";
2145 else if (nStatus == 404) cStatus = "Not Found";
2146 else if (nStatus == 500) cStatus = "Internal Server Error";
2147 else cStatus = "";
2148 return strprintf(
2149 "HTTP/1.1 %d %s\r\n"
2150 "Date: %s\r\n"
2151 "Connection: close\r\n"
2152 "Content-Length: %d\r\n"
2153 "Content-Type: application/json\r\n"
2154 "Server: bitcoin-json-rpc/%s\r\n"
2155 "\r\n"
2156 "%s",
2157 nStatus,
2158 cStatus,
2159 rfc1123Time().c_str(),
2160 strMsg.size(),
2161 FormatFullVersion().c_str(),
2162 strMsg.c_str());
2163}
2164
2165int ReadHTTPStatus(std::basic_istream<char>& stream)
2166{
2167 string str;
2168 getline(stream, str);
2169 vector<string> vWords;
2170 boost::split(vWords, str, boost::is_any_of(" "));
2171 if (vWords.size() < 2)
2172 return 500;
2173 return atoi(vWords[1].c_str());
2174}
2175
2176int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2177{
2178 int nLen = 0;
2179 loop
2180 {
2181 string str;
2182 std::getline(stream, str);
2183 if (str.empty() || str == "\r")
2184 break;
2185 string::size_type nColon = str.find(":");
2186 if (nColon != string::npos)
2187 {
2188 string strHeader = str.substr(0, nColon);
2189 boost::trim(strHeader);
2190 boost::to_lower(strHeader);
2191 string strValue = str.substr(nColon+1);
2192 boost::trim(strValue);
2193 mapHeadersRet[strHeader] = strValue;
2194 if (strHeader == "content-length")
2195 nLen = atoi(strValue.c_str());
2196 }
2197 }
2198 return nLen;
2199}
2200
2201int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2202{
2203 mapHeadersRet.clear();
2204 strMessageRet = "";
2205
2206 // Read status
2207 int nStatus = ReadHTTPStatus(stream);
2208
2209 // Read header
2210 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2211 if (nLen < 0 || nLen > MAX_SIZE)
2212 return 500;
2213
2214 // Read message
2215 if (nLen > 0)
2216 {
2217 vector<char> vch(nLen);
2218 stream.read(&vch[0], nLen);
2219 strMessageRet = string(vch.begin(), vch.end());
2220 }
2221
2222 return nStatus;
2223}
2224
2225bool HTTPAuthorized(map<string, string>& mapHeaders)
2226{
2227 string strAuth = mapHeaders["authorization"];
2228 if (strAuth.substr(0,6) != "Basic ")
2229 return false;
2230 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2231 string strUserPass = DecodeBase64(strUserPass64);
2232 return strUserPass == strRPCUserColonPass;
2233}
2234
2235//
2236// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2237// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2238// unspecified (HTTP errors and contents of 'error').
2239//
2240// 1.0 spec: http://json-rpc.org/wiki/specification
2241// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2242// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2243//
2244
2245string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2246{
2247 Object request;
2248 request.push_back(Pair("method", strMethod));
2249 request.push_back(Pair("params", params));
2250 request.push_back(Pair("id", id));
2251 return write_string(Value(request), false) + "\n";
2252}
2253
2254string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2255{
2256 Object reply;
2257 if (error.type() != null_type)
2258 reply.push_back(Pair("result", Value::null));
2259 else
2260 reply.push_back(Pair("result", result));
2261 reply.push_back(Pair("error", error));
2262 reply.push_back(Pair("id", id));
2263 return write_string(Value(reply), false) + "\n";
2264}
2265
2266void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2267{
2268 // Send error reply from json-rpc error object
2269 int nStatus = 500;
2270 int code = find_value(objError, "code").get_int();
2271 if (code == -32600) nStatus = 400;
2272 else if (code == -32601) nStatus = 404;
2273 string strReply = JSONRPCReply(Value::null, objError, id);
2274 stream << HTTPReply(nStatus, strReply) << std::flush;
2275}
2276
2277bool ClientAllowed(const string& strAddress)
2278{
2279 if (strAddress == asio::ip::address_v4::loopback().to_string())
2280 return true;
2281 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2282 BOOST_FOREACH(string strAllow, vAllow)
2283 if (WildcardMatch(strAddress, strAllow))
2284 return true;
2285 return false;
2286}
2287
2288void ThreadRPCServer(void* parg)
2289{
2290 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2291 try
2292 {
2293 vnThreadsRunning[4]++;
2294 ThreadRPCServer2(parg);
2295 vnThreadsRunning[4]--;
2296 }
2297 catch (std::exception& e) {
2298 vnThreadsRunning[4]--;
2299 PrintException(&e, "ThreadRPCServer()");
2300 } catch (...) {
2301 vnThreadsRunning[4]--;
2302 PrintException(NULL, "ThreadRPCServer()");
2303 }
2304 printf("ThreadRPCServer exiting\n");
2305}
2306
2307void ThreadRPCServer2(void* parg)
2308{
2309 printf("ThreadRPCServer started\n");
2310
2311 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2312 if (strRPCUserColonPass == ":")
2313 {
2314 unsigned char rand_pwd[32];
2315 RAND_bytes(rand_pwd, 32);
2316 string strWhatAmI = "To use bitcoind";
2317 if (mapArgs.count("-server"))
2318 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2319 else if (mapArgs.count("-daemon"))
2320 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2321 PrintConsole(
2322 _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
2323 "It is recommended you use the following random password:\n"
2324 "rpcuser=bitcoinrpc\n"
2325 "rpcpassword=%s\n"
2326 "(you do not need to remember this password)\n"
2327 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2328 strWhatAmI.c_str(),
2329 GetConfigFile().c_str(),
2330 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
2331 CreateThread(Shutdown, NULL);
2332 return;
2333 }
2334
2335 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2336
2337 asio::io_service io_service;
2338 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
2339 ip::tcp::acceptor acceptor(io_service, endpoint);
2340
2341 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2342
2343 loop
2344 {
2345 // Accept connection
2346 ip::tcp::iostream stream;
2347
2348 ip::tcp::endpoint peer;
2349 vnThreadsRunning[4]--;
2350 acceptor.accept(*stream.rdbuf(), peer);
2351 vnThreadsRunning[4]++;
2352 if (fShutdown)
2353 return;
2354
2355 // Restrict callers by IP
2356 if (!ClientAllowed(peer.address().to_string()))
2357 {
2358 // snipsnipsnip
2359 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2360 //if (!fUseSSL)
2361 stream << HTTPReply(403, "") << std::flush;
2362 continue;
2363 }
2364
2365 map<string, string> mapHeaders;
2366 string strRequest;
2367
2368 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2369 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2370 { // Timed out:
2371 acceptor.cancel();
2372 printf("ThreadRPCServer ReadHTTP timeout\n");
2373 continue;
2374 }
2375
2376 // Check authorization
2377 if (mapHeaders.count("authorization") == 0)
2378 {
2379 stream << HTTPReply(401, "") << std::flush;
2380 continue;
2381 }
2382 if (!HTTPAuthorized(mapHeaders))
2383 {
2384 printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str());
2385 /* Deter brute-forcing short passwords.
2386 If this results in a DOS the user really
2387 shouldn't have their RPC port exposed.*/
2388 if (mapArgs["-rpcpassword"].size() < 20)
2389 Sleep(250);
2390
2391 stream << HTTPReply(401, "") << std::flush;
2392 continue;
2393 }
2394
2395 Value id = Value::null;
2396 try
2397 {
2398 // Parse request
2399 Value valRequest;
2400 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2401 throw JSONRPCError(-32700, "Parse error");
2402 const Object& request = valRequest.get_obj();
2403
2404 // Parse id now so errors from here on will have the id
2405 id = find_value(request, "id");
2406
2407 // Parse method
2408 Value valMethod = find_value(request, "method");
2409 if (valMethod.type() == null_type)
2410 throw JSONRPCError(-32600, "Missing method");
2411 if (valMethod.type() != str_type)
2412 throw JSONRPCError(-32600, "Method must be a string");
2413 string strMethod = valMethod.get_str();
2414 if (strMethod != "getwork" && strMethod != "getmemorypool")
2415 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2416
2417 // Parse params
2418 Value valParams = find_value(request, "params");
2419 Array params;
2420 if (valParams.type() == array_type)
2421 params = valParams.get_array();
2422 else if (valParams.type() == null_type)
2423 params = Array();
2424 else
2425 throw JSONRPCError(-32600, "Params must be an array");
2426
2427 // Find method
2428 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2429 if (mi == mapCallTable.end())
2430 throw JSONRPCError(-32601, "Method not found");
2431
2432 // Observe safe mode
2433 if (!fDisableSafeMode) {
2434 string strWarning = GetWarnings("rpc");
2435 if (strWarning != "" && !setAllowInSafeMode.count(strMethod))
2436 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2437 }
2438
2439 try
2440 {
2441 // Execute
2442 Value result;
2443 CRITICAL_BLOCK(cs_main)
2444 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2445 result = (*(*mi).second)(params, false);
2446
2447 // Send reply
2448 string strReply = JSONRPCReply(result, Value::null, id);
2449 stream << HTTPReply(200, strReply) << std::flush;
2450 }
2451 catch (std::exception& e)
2452 {
2453 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2454 }
2455 }
2456 catch (Object& objError)
2457 {
2458 ErrorReply(stream, objError, id);
2459 }
2460 catch (std::exception& e)
2461 {
2462 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2463 }
2464 }
2465}
2466
2467
2468
2469
2470Object CallRPC(const string& strMethod, const Array& params)
2471{
2472 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2473 throw runtime_error(strprintf(
2474 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2475 "If the file does not exist, create it with owner-readable-only file permissions."),
2476 GetConfigFile().c_str()));
2477
2478 // Connect to localhost
2479 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
2480 if (stream.fail())
2481 throw runtime_error("couldn't connect to server");
2482
2483 // HTTP basic authentication
2484 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2485 map<string, string> mapRequestHeaders;
2486 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2487
2488 // Send request
2489 string strRequest = JSONRPCRequest(strMethod, params, 1);
2490 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2491 stream << strPost << std::flush;
2492
2493 // Receive reply
2494 map<string, string> mapHeaders;
2495 string strReply;
2496 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2497 if (nStatus == 401)
2498 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2499 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2500 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2501 else if (strReply.empty())
2502 throw runtime_error("no response from server");
2503
2504 // Parse reply
2505 Value valReply;
2506 if (!read_string(strReply, valReply))
2507 throw runtime_error("couldn't parse reply from server");
2508 const Object& reply = valReply.get_obj();
2509 if (reply.empty())
2510 throw runtime_error("expected reply to have result, error and id properties");
2511
2512 return reply;
2513}
2514
2515
2516
2517
2518template<typename T>
2519void ConvertTo(Value& value)
2520{
2521 if (value.type() == str_type)
2522 {
2523 // reinterpret string as unquoted json value
2524 Value value2;
2525 if (!read_string(value.get_str(), value2))
2526 throw runtime_error("type mismatch");
2527 value = value2.get_value<T>();
2528 }
2529 else
2530 {
2531 value = value.get_value<T>();
2532 }
2533}
2534
2535int CommandLineRPC(int argc, char *argv[])
2536{
2537 string strPrint;
2538 int nRet = 0;
2539 try
2540 {
2541 // Skip switches
2542 while (argc > 1 && IsSwitchChar(argv[1][0]))
2543 {
2544 argc--;
2545 argv++;
2546 }
2547
2548 // Method
2549 if (argc < 2)
2550 throw runtime_error("too few parameters");
2551 string strMethod = argv[1];
2552
2553 // Parameters default to strings
2554 Array params;
2555 for (int i = 2; i < argc; i++)
2556 params.push_back(argv[i]);
2557 int n = params.size();
2558
2559 //
2560 // Special case non-string parameter types
2561 //
2562 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2563 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2564 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2565 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2566 if (strMethod == "setminrelaytxfee" && n > 0) ConvertTo<double>(params[0]);
2567 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2568 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2569 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2570 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2571 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2572 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2573 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2574 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2575 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2576 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2577 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2578 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2579 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2580 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2581 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2582 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2583 if (strMethod == "dumpblock" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2584 if (strMethod == "sendmany" && n > 1)
2585 {
2586 string s = params[1].get_str();
2587 Value v;
2588 if (!read_string(s, v) || v.type() != obj_type)
2589 throw runtime_error("type mismatch");
2590 params[1] = v.get_obj();
2591 }
2592 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2593
2594 // Execute
2595 Object reply = CallRPC(strMethod, params);
2596
2597 // Parse reply
2598 const Value& result = find_value(reply, "result");
2599 const Value& error = find_value(reply, "error");
2600
2601 if (error.type() != null_type)
2602 {
2603 // Error
2604 strPrint = "error: " + write_string(error, false);
2605 int code = find_value(error.get_obj(), "code").get_int();
2606 nRet = abs(code);
2607 }
2608 else
2609 {
2610 // Result
2611 if (result.type() == null_type)
2612 strPrint = "";
2613 else if (result.type() == str_type)
2614 strPrint = result.get_str();
2615 else
2616 strPrint = write_string(result, true);
2617 }
2618 }
2619 catch (std::exception& e)
2620 {
2621 strPrint = string("error: ") + e.what();
2622 nRet = 87;
2623 }
2624 catch (...)
2625 {
2626 PrintException(NULL, "CommandLineRPC()");
2627 }
2628
2629 if (strPrint != "")
2630 {
2631 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2632 }
2633 return nRet;
2634}
2635
2636
2637
2638
2639#ifdef TEST
2640int main(int argc, char *argv[])
2641{
2642 setbuf(stdin, NULL);
2643 setbuf(stdout, NULL);
2644 setbuf(stderr, NULL);
2645
2646 try
2647 {
2648 if (argc >= 2 && string(argv[1]) == "-server")
2649 {
2650 printf("server ready\n");
2651 ThreadRPCServer(NULL);
2652 }
2653 else
2654 {
2655 return CommandLineRPC(argc, argv);
2656 }
2657 }
2658 catch (std::exception& e) {
2659 PrintException(&e, "main()");
2660 } catch (...) {
2661 PrintException(NULL, "main()");
2662 }
2663 return 0;
2664}
2665#endif