Projects : bitcoin : bitcoin_dumpblock_no_losers

bitcoin/src/bitcoinrpc.cpp

Dir - Raw

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