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