1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Copyright (c) 2011 The PPCoin developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
12 #include <boost/asio.hpp>
13 #include <boost/iostreams/concepts.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #include <boost/algorithm/string.hpp>
17 #include <boost/asio/ssl.hpp>
18 #include <boost/filesystem.hpp>
19 #include <boost/filesystem/fstream.hpp>
20 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
22 #include "json/json_spirit_reader_template.h"
23 #include "json/json_spirit_writer_template.h"
24 #include "json/json_spirit_utils.h"
25 #define printf OutputDebugStringF
26 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
27 // precompiled in headers.h. The problem might be when the pch file goes over
28 // a certain size around 145MB. If we need access to json_spirit outside this
29 // file, we could use the compiled json_spirit option.
32 using namespace boost;
33 using namespace boost::asio;
34 using namespace json_spirit;
36 void ThreadRPCServer2(void* parg);
37 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
38 extern map<string, rpcfn_type> mapCallTable;
40 static std::string strRPCUserColonPass;
42 static int64 nWalletUnlockTime;
43 static CCriticalSection cs_nWalletUnlockTime;
46 Object JSONRPCError(int code, const string& message)
49 error.push_back(Pair("code", code));
50 error.push_back(Pair("message", message));
55 void PrintConsole(const std::string &format, ...)
58 int limit = sizeof(buffer);
60 va_start(arg_ptr, format);
61 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
63 if (ret < 0 || ret >= limit)
69 fprintf(stdout, "%s", buffer);
73 int64 AmountFromValue(const Value& value)
75 double dAmount = value.get_real();
76 if (dAmount <= 0.0 || dAmount > MAX_MONEY)
77 throw JSONRPCError(-3, "Invalid amount");
78 int64 nAmount = roundint64(dAmount * COIN);
79 if (!MoneyRange(nAmount))
80 throw JSONRPCError(-3, "Invalid amount");
84 Value ValueFromAmount(int64 amount)
86 return (double)amount / (double)COIN;
89 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
91 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
92 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
93 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
94 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
95 entry.push_back(Pair(item.first, item.second));
98 string AccountFromValue(const Value& value)
100 string strAccount = value.get_str();
101 if (strAccount == "*")
102 throw JSONRPCError(-11, "Invalid account name");
109 /// Note: This interface may still be subject to change.
113 Value help(const Array& params, bool fHelp)
115 if (fHelp || params.size() > 1)
118 "List commands, or get help for a command.");
121 if (params.size() > 0)
122 strCommand = params[0].get_str();
125 set<rpcfn_type> setDone;
126 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
128 string strMethod = (*mi).first;
129 // We already filter duplicates, but these deprecated screw up the sort order
130 if (strMethod == "getamountreceived" ||
131 strMethod == "getallreceived" ||
132 strMethod == "getblocknumber" || // deprecated
133 (strMethod.find("label") != string::npos))
135 if (strCommand != "" && strMethod != strCommand)
140 rpcfn_type pfn = (*mi).second;
141 if (setDone.insert(pfn).second)
142 (*pfn)(params, true);
144 catch (std::exception& e)
146 // Help text is returned in an exception
147 string strHelp = string(e.what());
148 if (strCommand == "")
149 if (strHelp.find('\n') != -1)
150 strHelp = strHelp.substr(0, strHelp.find('\n'));
151 strRet += strHelp + "\n";
155 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
156 strRet = strRet.substr(0,strRet.size()-1);
161 Value stop(const Array& params, bool fHelp)
163 if (fHelp || params.size() != 0)
166 "Stop ppcoin server.");
168 // Shutdown will take long enough that the response should get back
169 CreateThread(Shutdown, NULL);
170 return "ppcoin server stopping";
172 throw runtime_error("NYI: cannot shut down GUI with RPC command");
177 Value getblockcount(const Array& params, bool fHelp)
179 if (fHelp || params.size() != 0)
182 "Returns the number of blocks in the longest block chain.");
189 Value getblocknumber(const Array& params, bool fHelp)
191 if (fHelp || params.size() != 0)
194 "Deprecated. Use getblockcount.");
200 Value getconnectioncount(const Array& params, bool fHelp)
202 if (fHelp || params.size() != 0)
204 "getconnectioncount\n"
205 "Returns the number of connections to other nodes.");
207 return (int)vNodes.size();
211 double GetDifficulty()
213 // Floating point number that is a multiple of the minimum difficulty,
214 // minimum difficulty = 1.0.
216 if (pindexBest == NULL)
218 int nShift = (pindexBest->nBits >> 24) & 0xff;
221 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
237 Value getdifficulty(const Array& params, bool fHelp)
239 if (fHelp || params.size() != 0)
242 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
244 return GetDifficulty();
248 Value getgenerate(const Array& params, bool fHelp)
250 if (fHelp || params.size() != 0)
253 "Returns true or false.");
255 return (bool)fGenerateBitcoins;
259 Value setgenerate(const Array& params, bool fHelp)
261 if (fHelp || params.size() < 1 || params.size() > 2)
263 "setgenerate <generate> [genproclimit]\n"
264 "<generate> is true or false to turn generation on or off.\n"
265 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
267 bool fGenerate = true;
268 if (params.size() > 0)
269 fGenerate = params[0].get_bool();
271 if (params.size() > 1)
273 int nGenProcLimit = params[1].get_int();
274 fLimitProcessors = (nGenProcLimit != -1);
275 WriteSetting("fLimitProcessors", fLimitProcessors);
276 if (nGenProcLimit != -1)
277 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
278 if (nGenProcLimit == 0)
282 GenerateBitcoins(fGenerate, pwalletMain);
287 Value gethashespersec(const Array& params, bool fHelp)
289 if (fHelp || params.size() != 0)
292 "Returns a recent hashes per second performance measurement while generating.");
294 if (GetTimeMillis() - nHPSTimerStart > 8000)
295 return (boost::int64_t)0;
296 return (boost::int64_t)dHashesPerSec;
300 Value getinfo(const Array& params, bool fHelp)
302 if (fHelp || params.size() != 0)
305 "Returns an object containing various state info.");
308 obj.push_back(Pair("version", (int)VERSION));
309 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
310 obj.push_back(Pair("blocks", (int)nBestHeight));
311 obj.push_back(Pair("connections", (int)vNodes.size()));
312 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
313 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
314 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
315 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
316 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
317 obj.push_back(Pair("testnet", fTestNet));
318 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
319 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
320 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
321 if (pwalletMain->IsCrypted())
322 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
323 obj.push_back(Pair("errors", GetWarnings("statusbar")));
328 Value getnewaddress(const Array& params, bool fHelp)
330 if (fHelp || params.size() > 1)
332 "getnewaddress [account]\n"
333 "Returns a new ppcoin address for receiving payments. "
334 "If [account] is specified (recommended), it is added to the address book "
335 "so payments received with the address will be credited to [account].");
337 // Parse the account first so we don't generate a key if there's an error
339 if (params.size() > 0)
340 strAccount = AccountFromValue(params[0]);
342 if (!pwalletMain->IsLocked())
343 pwalletMain->TopUpKeyPool();
345 // Generate a new key that is added to wallet
346 std::vector<unsigned char> newKey;
347 if (!pwalletMain->GetKeyFromPool(newKey, false))
348 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
349 CBitcoinAddress address(newKey);
351 pwalletMain->SetAddressBookName(address, strAccount);
353 return address.ToString();
357 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
359 CWalletDB walletdb(pwalletMain->strWalletFile);
362 walletdb.ReadAccount(strAccount, account);
364 bool bKeyUsed = false;
366 // Check if the current key has been used
367 if (!account.vchPubKey.empty())
369 CScript scriptPubKey;
370 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
371 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
372 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
375 const CWalletTx& wtx = (*it).second;
376 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
377 if (txout.scriptPubKey == scriptPubKey)
382 // Generate a new key
383 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
385 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
386 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
388 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
389 walletdb.WriteAccount(strAccount, account);
392 return CBitcoinAddress(account.vchPubKey);
395 Value getaccountaddress(const Array& params, bool fHelp)
397 if (fHelp || params.size() != 1)
399 "getaccountaddress <account>\n"
400 "Returns the current ppcoin address for receiving payments to this account.");
402 // Parse the account first so we don't generate a key if there's an error
403 string strAccount = AccountFromValue(params[0]);
407 ret = GetAccountAddress(strAccount).ToString();
414 Value setaccount(const Array& params, bool fHelp)
416 if (fHelp || params.size() < 1 || params.size() > 2)
418 "setaccount <ppcoinaddress> <account>\n"
419 "Sets the account associated with the given address.");
421 CBitcoinAddress address(params[0].get_str());
422 if (!address.IsValid())
423 throw JSONRPCError(-5, "Invalid ppcoin address");
427 if (params.size() > 1)
428 strAccount = AccountFromValue(params[1]);
430 // Detect when changing the account of an address that is the 'unused current key' of another account:
431 if (pwalletMain->mapAddressBook.count(address))
433 string strOldAccount = pwalletMain->mapAddressBook[address];
434 if (address == GetAccountAddress(strOldAccount))
435 GetAccountAddress(strOldAccount, true);
438 pwalletMain->SetAddressBookName(address, strAccount);
444 Value getaccount(const Array& params, bool fHelp)
446 if (fHelp || params.size() != 1)
448 "getaccount <ppcoinaddress>\n"
449 "Returns the account associated with the given address.");
451 CBitcoinAddress address(params[0].get_str());
452 if (!address.IsValid())
453 throw JSONRPCError(-5, "Invalid ppcoin address");
456 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
457 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
458 strAccount = (*mi).second;
463 Value getaddressesbyaccount(const Array& params, bool fHelp)
465 if (fHelp || params.size() != 1)
467 "getaddressesbyaccount <account>\n"
468 "Returns the list of addresses for the given account.");
470 string strAccount = AccountFromValue(params[0]);
472 // Find all addresses that have the given account
474 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
476 const CBitcoinAddress& address = item.first;
477 const string& strName = item.second;
478 if (strName == strAccount)
479 ret.push_back(address.ToString());
484 Value settxfee(const Array& params, bool fHelp)
486 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
488 "settxfee <amount>\n"
489 "<amount> is a real and is rounded to 0.01 (cent)\n"
490 "Minimum and default transaction fee per KB is 1 cent");
492 nTransactionFee = AmountFromValue(params[0]);
493 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
497 Value sendtoaddress(const Array& params, bool fHelp)
499 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
501 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
502 "<amount> is a real and is rounded to the nearest 0.000001\n"
503 "requires wallet passphrase to be set with walletpassphrase first");
504 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
506 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
507 "<amount> is a real and is rounded to the nearest 0.000001");
509 CBitcoinAddress address(params[0].get_str());
510 if (!address.IsValid())
511 throw JSONRPCError(-5, "Invalid ppcoin address");
514 int64 nAmount = AmountFromValue(params[1]);
518 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
519 wtx.mapValue["comment"] = params[2].get_str();
520 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
521 wtx.mapValue["to"] = params[3].get_str();
523 if (pwalletMain->IsLocked())
524 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
526 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
528 throw JSONRPCError(-4, strError);
530 return wtx.GetHash().GetHex();
533 static const string strMessageMagic = "Bitcoin Signed Message:\n";
535 Value signmessage(const Array& params, bool fHelp)
537 if (fHelp || params.size() != 2)
539 "signmessage <ppcoinaddress> <message>\n"
540 "Sign a message with the private key of an address");
542 if (pwalletMain->IsLocked())
543 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
545 string strAddress = params[0].get_str();
546 string strMessage = params[1].get_str();
548 CBitcoinAddress addr(strAddress);
550 throw JSONRPCError(-3, "Invalid address");
553 if (!pwalletMain->GetKey(addr, key))
554 throw JSONRPCError(-4, "Private key not available");
556 CDataStream ss(SER_GETHASH);
557 ss << strMessageMagic;
560 vector<unsigned char> vchSig;
561 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
562 throw JSONRPCError(-5, "Sign failed");
564 return EncodeBase64(&vchSig[0], vchSig.size());
567 Value verifymessage(const Array& params, bool fHelp)
569 if (fHelp || params.size() != 3)
571 "verifymessage <ppcoinaddress> <signature> <message>\n"
572 "Verify a signed message");
574 string strAddress = params[0].get_str();
575 string strSign = params[1].get_str();
576 string strMessage = params[2].get_str();
578 CBitcoinAddress addr(strAddress);
580 throw JSONRPCError(-3, "Invalid address");
582 bool fInvalid = false;
583 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
586 throw JSONRPCError(-5, "Malformed base64 encoding");
588 CDataStream ss(SER_GETHASH);
589 ss << strMessageMagic;
593 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
596 return (key.GetAddress() == addr);
600 Value getreceivedbyaddress(const Array& params, bool fHelp)
602 if (fHelp || params.size() < 1 || params.size() > 2)
604 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
605 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
608 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
609 CScript scriptPubKey;
610 if (!address.IsValid())
611 throw JSONRPCError(-5, "Invalid ppcoin address");
612 scriptPubKey.SetBitcoinAddress(address);
613 if (!IsMine(*pwalletMain,scriptPubKey))
616 // Minimum confirmations
618 if (params.size() > 1)
619 nMinDepth = params[1].get_int();
623 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
625 const CWalletTx& wtx = (*it).second;
626 if (wtx.IsCoinBase() || !wtx.IsFinal())
629 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
630 if (txout.scriptPubKey == scriptPubKey)
631 if (wtx.GetDepthInMainChain() >= nMinDepth)
632 nAmount += txout.nValue;
635 return ValueFromAmount(nAmount);
639 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
641 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
643 const CBitcoinAddress& address = item.first;
644 const string& strName = item.second;
645 if (strName == strAccount)
646 setAddress.insert(address);
651 Value getreceivedbyaccount(const Array& params, bool fHelp)
653 if (fHelp || params.size() < 1 || params.size() > 2)
655 "getreceivedbyaccount <account> [minconf=1]\n"
656 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
658 // Minimum confirmations
660 if (params.size() > 1)
661 nMinDepth = params[1].get_int();
663 // Get the set of pub keys that have the label
664 string strAccount = AccountFromValue(params[0]);
665 set<CBitcoinAddress> setAddress;
666 GetAccountAddresses(strAccount, setAddress);
670 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
672 const CWalletTx& wtx = (*it).second;
673 if (wtx.IsCoinBase() || !wtx.IsFinal())
676 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
678 CBitcoinAddress address;
679 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
680 if (wtx.GetDepthInMainChain() >= nMinDepth)
681 nAmount += txout.nValue;
685 return (double)nAmount / (double)COIN;
689 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
693 // Tally wallet transactions
694 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
696 const CWalletTx& wtx = (*it).second;
700 int64 nGenerated, nReceived, nSent, nFee;
701 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
703 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
704 nBalance += nReceived;
705 nBalance += nGenerated - nSent - nFee;
708 // Tally internal accounting entries
709 nBalance += walletdb.GetAccountCreditDebit(strAccount);
714 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
716 CWalletDB walletdb(pwalletMain->strWalletFile);
717 return GetAccountBalance(walletdb, strAccount, nMinDepth);
721 Value getbalance(const Array& params, bool fHelp)
723 if (fHelp || params.size() > 2)
725 "getbalance [account] [minconf=1]\n"
726 "If [account] is not specified, returns the server's total available balance.\n"
727 "If [account] is specified, returns the balance in the account.");
729 if (params.size() == 0)
730 return ValueFromAmount(pwalletMain->GetBalance());
733 if (params.size() > 1)
734 nMinDepth = params[1].get_int();
736 if (params[0].get_str() == "*") {
737 // Calculate total balance a different way from GetBalance()
738 // (GetBalance() sums up all unspent TxOuts)
739 // getbalance and getbalance '*' should always return the same number.
741 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
743 const CWalletTx& wtx = (*it).second;
747 int64 allGeneratedImmature, allGeneratedMature, allFee;
748 allGeneratedImmature = allGeneratedMature = allFee = 0;
749 string strSentAccount;
750 list<pair<CBitcoinAddress, int64> > listReceived;
751 list<pair<CBitcoinAddress, int64> > listSent;
752 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
753 if (wtx.GetDepthInMainChain() >= nMinDepth)
754 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
755 nBalance += r.second;
756 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
757 nBalance -= r.second;
759 nBalance += allGeneratedMature;
761 return ValueFromAmount(nBalance);
764 string strAccount = AccountFromValue(params[0]);
766 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
768 return ValueFromAmount(nBalance);
772 Value movecmd(const Array& params, bool fHelp)
774 if (fHelp || params.size() < 3 || params.size() > 5)
776 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
777 "Move from one account in your wallet to another.");
779 string strFrom = AccountFromValue(params[0]);
780 string strTo = AccountFromValue(params[1]);
781 int64 nAmount = AmountFromValue(params[2]);
782 if (params.size() > 3)
783 // unused parameter, used to be nMinDepth, keep type-checking it though
784 (void)params[3].get_int();
786 if (params.size() > 4)
787 strComment = params[4].get_str();
789 CWalletDB walletdb(pwalletMain->strWalletFile);
792 int64 nNow = GetAdjustedTime();
795 CAccountingEntry debit;
796 debit.strAccount = strFrom;
797 debit.nCreditDebit = -nAmount;
799 debit.strOtherAccount = strTo;
800 debit.strComment = strComment;
801 walletdb.WriteAccountingEntry(debit);
804 CAccountingEntry credit;
805 credit.strAccount = strTo;
806 credit.nCreditDebit = nAmount;
808 credit.strOtherAccount = strFrom;
809 credit.strComment = strComment;
810 walletdb.WriteAccountingEntry(credit);
812 walletdb.TxnCommit();
818 Value sendfrom(const Array& params, bool fHelp)
820 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
822 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
823 "<amount> is a real and is rounded to the nearest 0.000001\n"
824 "requires wallet passphrase to be set with walletpassphrase first");
825 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
827 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
828 "<amount> is a real and is rounded to the nearest 0.000001");
830 string strAccount = AccountFromValue(params[0]);
831 CBitcoinAddress address(params[1].get_str());
832 if (!address.IsValid())
833 throw JSONRPCError(-5, "Invalid ppcoin address");
834 int64 nAmount = AmountFromValue(params[2]);
836 if (params.size() > 3)
837 nMinDepth = params[3].get_int();
840 wtx.strFromAccount = strAccount;
841 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
842 wtx.mapValue["comment"] = params[4].get_str();
843 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
844 wtx.mapValue["to"] = params[5].get_str();
846 if (pwalletMain->IsLocked())
847 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
850 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
851 if (nAmount > nBalance)
852 throw JSONRPCError(-6, "Account has insufficient funds");
855 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
857 throw JSONRPCError(-4, strError);
859 return wtx.GetHash().GetHex();
863 Value sendmany(const Array& params, bool fHelp)
865 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
867 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
868 "amounts are double-precision floating point numbers\n"
869 "requires wallet passphrase to be set with walletpassphrase first");
870 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
872 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
873 "amounts are double-precision floating point numbers");
875 string strAccount = AccountFromValue(params[0]);
876 Object sendTo = params[1].get_obj();
878 if (params.size() > 2)
879 nMinDepth = params[2].get_int();
882 wtx.strFromAccount = strAccount;
883 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
884 wtx.mapValue["comment"] = params[3].get_str();
886 set<CBitcoinAddress> setAddress;
887 vector<pair<CScript, int64> > vecSend;
889 int64 totalAmount = 0;
890 BOOST_FOREACH(const Pair& s, sendTo)
892 CBitcoinAddress address(s.name_);
893 if (!address.IsValid())
894 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
896 if (setAddress.count(address))
897 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
898 setAddress.insert(address);
900 CScript scriptPubKey;
901 scriptPubKey.SetBitcoinAddress(address);
902 int64 nAmount = AmountFromValue(s.value_);
903 totalAmount += nAmount;
905 vecSend.push_back(make_pair(scriptPubKey, nAmount));
908 if (pwalletMain->IsLocked())
909 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
912 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
913 if (totalAmount > nBalance)
914 throw JSONRPCError(-6, "Account has insufficient funds");
917 CReserveKey keyChange(pwalletMain);
918 int64 nFeeRequired = 0;
919 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
922 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
923 throw JSONRPCError(-6, "Insufficient funds");
924 throw JSONRPCError(-4, "Transaction creation failed");
926 if (!pwalletMain->CommitTransaction(wtx, keyChange))
927 throw JSONRPCError(-4, "Transaction commit failed");
929 return wtx.GetHash().GetHex();
944 Value ListReceived(const Array& params, bool fByAccounts)
946 // Minimum confirmations
948 if (params.size() > 0)
949 nMinDepth = params[0].get_int();
951 // Whether to include empty accounts
952 bool fIncludeEmpty = false;
953 if (params.size() > 1)
954 fIncludeEmpty = params[1].get_bool();
957 map<CBitcoinAddress, tallyitem> mapTally;
958 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
960 const CWalletTx& wtx = (*it).second;
961 if (wtx.IsCoinBase() || !wtx.IsFinal())
964 int nDepth = wtx.GetDepthInMainChain();
965 if (nDepth < nMinDepth)
968 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
970 CBitcoinAddress address;
971 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
974 tallyitem& item = mapTally[address];
975 item.nAmount += txout.nValue;
976 item.nConf = min(item.nConf, nDepth);
982 map<string, tallyitem> mapAccountTally;
983 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
985 const CBitcoinAddress& address = item.first;
986 const string& strAccount = item.second;
987 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
988 if (it == mapTally.end() && !fIncludeEmpty)
993 if (it != mapTally.end())
995 nAmount = (*it).second.nAmount;
996 nConf = (*it).second.nConf;
1001 tallyitem& item = mapAccountTally[strAccount];
1002 item.nAmount += nAmount;
1003 item.nConf = min(item.nConf, nConf);
1008 obj.push_back(Pair("address", address.ToString()));
1009 obj.push_back(Pair("account", strAccount));
1010 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1011 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1018 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1020 int64 nAmount = (*it).second.nAmount;
1021 int nConf = (*it).second.nConf;
1023 obj.push_back(Pair("account", (*it).first));
1024 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1025 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1033 Value listreceivedbyaddress(const Array& params, bool fHelp)
1035 if (fHelp || params.size() > 2)
1036 throw runtime_error(
1037 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1038 "[minconf] is the minimum number of confirmations before payments are included.\n"
1039 "[includeempty] whether to include addresses that haven't received any payments.\n"
1040 "Returns an array of objects containing:\n"
1041 " \"address\" : receiving address\n"
1042 " \"account\" : the account of the receiving address\n"
1043 " \"amount\" : total amount received by the address\n"
1044 " \"confirmations\" : number of confirmations of the most recent transaction included");
1046 return ListReceived(params, false);
1049 Value listreceivedbyaccount(const Array& params, bool fHelp)
1051 if (fHelp || params.size() > 2)
1052 throw runtime_error(
1053 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1054 "[minconf] is the minimum number of confirmations before payments are included.\n"
1055 "[includeempty] whether to include accounts that haven't received any payments.\n"
1056 "Returns an array of objects containing:\n"
1057 " \"account\" : the account of the receiving addresses\n"
1058 " \"amount\" : total amount received by addresses with this account\n"
1059 " \"confirmations\" : number of confirmations of the most recent transaction included");
1061 return ListReceived(params, true);
1064 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1066 int64 nGeneratedImmature, nGeneratedMature, nFee;
1067 string strSentAccount;
1068 list<pair<CBitcoinAddress, int64> > listReceived;
1069 list<pair<CBitcoinAddress, int64> > listSent;
1070 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1072 bool fAllAccounts = (strAccount == string("*"));
1074 // Generated blocks assigned to account ""
1075 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1078 entry.push_back(Pair("account", string("")));
1079 if (nGeneratedImmature)
1081 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1082 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1086 entry.push_back(Pair("category", "generate"));
1087 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1090 WalletTxToJSON(wtx, entry);
1091 ret.push_back(entry);
1095 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1097 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1100 entry.push_back(Pair("account", strSentAccount));
1101 entry.push_back(Pair("address", s.first.ToString()));
1102 entry.push_back(Pair("category", "send"));
1103 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1104 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1106 WalletTxToJSON(wtx, entry);
1107 ret.push_back(entry);
1112 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1113 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1116 if (pwalletMain->mapAddressBook.count(r.first))
1117 account = pwalletMain->mapAddressBook[r.first];
1118 if (fAllAccounts || (account == strAccount))
1121 entry.push_back(Pair("account", account));
1122 entry.push_back(Pair("address", r.first.ToString()));
1123 entry.push_back(Pair("category", "receive"));
1124 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1126 WalletTxToJSON(wtx, entry);
1127 ret.push_back(entry);
1132 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1134 bool fAllAccounts = (strAccount == string("*"));
1136 if (fAllAccounts || acentry.strAccount == strAccount)
1139 entry.push_back(Pair("account", acentry.strAccount));
1140 entry.push_back(Pair("category", "move"));
1141 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1142 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1143 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1144 entry.push_back(Pair("comment", acentry.strComment));
1145 ret.push_back(entry);
1149 Value listtransactions(const Array& params, bool fHelp)
1151 if (fHelp || params.size() > 3)
1152 throw runtime_error(
1153 "listtransactions [account] [count=10] [from=0]\n"
1154 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1156 string strAccount = "*";
1157 if (params.size() > 0)
1158 strAccount = params[0].get_str();
1160 if (params.size() > 1)
1161 nCount = params[1].get_int();
1163 if (params.size() > 2)
1164 nFrom = params[2].get_int();
1167 CWalletDB walletdb(pwalletMain->strWalletFile);
1169 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1170 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1171 typedef multimap<int64, TxPair > TxItems;
1174 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1176 CWalletTx* wtx = &((*it).second);
1177 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1179 list<CAccountingEntry> acentries;
1180 walletdb.ListAccountCreditDebit(strAccount, acentries);
1181 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1183 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1186 // Now: iterate backwards until we have nCount items to return:
1187 TxItems::reverse_iterator it = txByTime.rbegin();
1188 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1189 for (; it != txByTime.rend(); ++it)
1191 CWalletTx *const pwtx = (*it).second.first;
1193 ListTransactions(*pwtx, strAccount, 0, true, ret);
1194 CAccountingEntry *const pacentry = (*it).second.second;
1196 AcentryToJSON(*pacentry, strAccount, ret);
1198 if (ret.size() >= nCount) break;
1200 // ret is now newest to oldest
1202 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1203 if (ret.size() > nCount)
1205 Array::iterator last = ret.begin();
1206 std::advance(last, nCount);
1207 ret.erase(last, ret.end());
1209 std::reverse(ret.begin(), ret.end()); // oldest to newest
1214 Value listaccounts(const Array& params, bool fHelp)
1216 if (fHelp || params.size() > 1)
1217 throw runtime_error(
1218 "listaccounts [minconf=1]\n"
1219 "Returns Object that has account names as keys, account balances as values.");
1222 if (params.size() > 0)
1223 nMinDepth = params[0].get_int();
1225 map<string, int64> mapAccountBalances;
1226 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1227 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1228 mapAccountBalances[entry.second] = 0;
1231 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1233 const CWalletTx& wtx = (*it).second;
1234 int64 nGeneratedImmature, nGeneratedMature, nFee;
1235 string strSentAccount;
1236 list<pair<CBitcoinAddress, int64> > listReceived;
1237 list<pair<CBitcoinAddress, int64> > listSent;
1238 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1239 mapAccountBalances[strSentAccount] -= nFee;
1240 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1241 mapAccountBalances[strSentAccount] -= s.second;
1242 if (wtx.GetDepthInMainChain() >= nMinDepth)
1244 mapAccountBalances[""] += nGeneratedMature;
1245 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1246 if (pwalletMain->mapAddressBook.count(r.first))
1247 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1249 mapAccountBalances[""] += r.second;
1253 list<CAccountingEntry> acentries;
1254 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1255 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1256 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1259 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1260 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1265 Value listsinceblock(const Array& params, bool fHelp)
1268 throw runtime_error(
1269 "listsinceblock [blockid] [target-confirmations]\n"
1270 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1272 CBlockIndex *pindex = NULL;
1273 int target_confirms = 1;
1275 if (params.size() > 0)
1277 uint256 blockId = 0;
1279 blockId.SetHex(params[0].get_str());
1280 pindex = CBlockLocator(blockId).GetBlockIndex();
1283 if (params.size() > 1)
1285 target_confirms = params[1].get_int();
1287 if (target_confirms < 1)
1288 throw JSONRPCError(-8, "Invalid parameter");
1291 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1295 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1297 CWalletTx tx = (*it).second;
1299 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1300 ListTransactions(tx, "*", 0, true, transactions);
1305 if (target_confirms == 1)
1308 lastblock = hashBestChain;
1312 int target_height = pindexBest->nHeight + 1 - target_confirms;
1315 for (block = pindexBest;
1316 block && block->nHeight > target_height;
1317 block = block->pprev);
1319 lastblock = block ? block->GetBlockHash() : 0;
1323 ret.push_back(Pair("transactions", transactions));
1324 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1329 Value gettransaction(const Array& params, bool fHelp)
1331 if (fHelp || params.size() != 1)
1332 throw runtime_error(
1333 "gettransaction <txid>\n"
1334 "Get detailed information about <txid>");
1337 hash.SetHex(params[0].get_str());
1341 if (!pwalletMain->mapWallet.count(hash))
1342 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1343 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1345 int64 nCredit = wtx.GetCredit();
1346 int64 nDebit = wtx.GetDebit();
1347 int64 nNet = nCredit - nDebit;
1348 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1350 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1352 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1354 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1357 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1358 entry.push_back(Pair("details", details));
1364 Value backupwallet(const Array& params, bool fHelp)
1366 if (fHelp || params.size() != 1)
1367 throw runtime_error(
1368 "backupwallet <destination>\n"
1369 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1371 string strDest = params[0].get_str();
1372 BackupWallet(*pwalletMain, strDest);
1378 Value keypoolrefill(const Array& params, bool fHelp)
1380 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1381 throw runtime_error(
1383 "Fills the keypool, requires wallet passphrase to be set.");
1384 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1385 throw runtime_error(
1387 "Fills the keypool.");
1389 if (pwalletMain->IsLocked())
1390 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1392 pwalletMain->TopUpKeyPool();
1394 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1395 throw JSONRPCError(-4, "Error refreshing keypool.");
1401 void ThreadTopUpKeyPool(void* parg)
1403 pwalletMain->TopUpKeyPool();
1406 void ThreadCleanWalletPassphrase(void* parg)
1408 int64 nMyWakeTime = GetTime() + *((int*)parg);
1410 if (nWalletUnlockTime == 0)
1412 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1414 nWalletUnlockTime = nMyWakeTime;
1417 while (GetTime() < nWalletUnlockTime)
1418 Sleep(GetTime() - nWalletUnlockTime);
1420 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1422 nWalletUnlockTime = 0;
1427 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1429 if (nWalletUnlockTime < nMyWakeTime)
1430 nWalletUnlockTime = nMyWakeTime;
1436 pwalletMain->Lock();
1441 Value walletpassphrase(const Array& params, bool fHelp)
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.");
1449 if (!pwalletMain->IsCrypted())
1450 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1452 if (!pwalletMain->IsLocked())
1453 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
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();
1462 if (strWalletPass.length() > 0)
1464 if (!pwalletMain->Unlock(strWalletPass))
1465 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1468 throw runtime_error(
1469 "walletpassphrase <passphrase> <timeout>\n"
1470 "Stores the wallet decryption key in memory for <timeout> seconds.");
1472 CreateThread(ThreadTopUpKeyPool, NULL);
1473 int* pnSleepTime = new int(params[1].get_int());
1474 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1480 Value walletpassphrasechange(const Array& params, bool fHelp)
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>.");
1488 if (!pwalletMain->IsCrypted())
1489 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
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();
1497 SecureString strNewWalletPass;
1498 strNewWalletPass.reserve(100);
1499 strNewWalletPass = params[1].get_str().c_str();
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>.");
1506 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1507 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1513 Value walletlock(const Array& params, bool fHelp)
1515 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1516 throw runtime_error(
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.");
1523 if (!pwalletMain->IsCrypted())
1524 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1526 pwalletMain->Lock();
1527 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1529 nWalletUnlockTime = 0;
1536 Value encryptwallet(const Array& params, bool fHelp)
1538 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1539 throw runtime_error(
1540 "encryptwallet <passphrase>\n"
1541 "Encrypts the wallet with <passphrase>.");
1544 if (pwalletMain->IsCrypted())
1545 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1548 // shutting down via RPC while the GUI is running does not work (yet):
1549 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
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();
1558 if (strWalletPass.length() < 1)
1559 throw runtime_error(
1560 "encryptwallet <passphrase>\n"
1561 "Encrypts the wallet with <passphrase>.");
1563 if (!pwalletMain->EncryptWallet(strWalletPass))
1564 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
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; ppcoin server stopping, restart to run with encrypted wallet";
1574 Value validateaddress(const Array& params, bool fHelp)
1576 if (fHelp || params.size() != 1)
1577 throw runtime_error(
1578 "validateaddress <ppcoinaddress>\n"
1579 "Return information about <ppcoinaddress>.");
1581 CBitcoinAddress address(params[0].get_str());
1582 bool isValid = address.IsValid();
1585 ret.push_back(Pair("isvalid", isValid));
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]));
1600 Value getwork(const Array& params, bool fHelp)
1602 if (fHelp || params.size() > 1)
1603 throw runtime_error(
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.");
1613 throw JSONRPCError(-9, "PPCoin is not connected!");
1615 if (IsInitialBlockDownload())
1616 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1618 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1619 static mapNewBlock_t mapNewBlock;
1620 static vector<CBlock*> vNewBlock;
1621 static CReserveKey reservekey(pwalletMain);
1623 if (params.size() == 0)
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))
1633 if (pindexPrev != pindexBest)
1635 // Deallocate old blocks since they're obsolete now
1636 mapNewBlock.clear();
1637 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1641 nTransactionsUpdatedLast = nTransactionsUpdated;
1642 pindexPrev = pindexBest;
1646 pblock = CreateNewBlock(reservekey);
1648 throw JSONRPCError(-7, "Out of memory");
1649 vNewBlock.push_back(pblock);
1653 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1656 // Update nExtraNonce
1657 static unsigned int nExtraNonce = 0;
1658 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1661 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1663 // Prebuild hash buffers
1667 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1669 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
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))));
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];
1687 for (int i = 0; i < 128/4; i++)
1688 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1691 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1693 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
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();
1700 return CheckWork(pblock, *pwalletMain, reservekey);
1705 Value getmemorypool(const Array& params, bool fHelp)
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.");
1719 if (params.size() == 0)
1722 throw JSONRPCError(-9, "PPCoin is not connected!");
1724 if (IsInitialBlockDownload())
1725 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1727 static CReserveKey reservekey(pwalletMain);
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))
1737 nTransactionsUpdatedLast = nTransactionsUpdated;
1738 pindexPrev = pindexBest;
1744 pblock = CreateNewBlock(reservekey);
1746 throw JSONRPCError(-7, "Out of memory");
1750 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1754 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1761 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
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", (int64_t)pblock->vtx[0].vout[0].nValue));
1769 result.push_back(Pair("time", (int64_t)pblock->nTime));
1775 uBits.nBits = htonl((int32_t)pblock->nBits);
1776 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1783 CDataStream ssBlock(ParseHex(params[0].get_str()));
1787 return ProcessBlock(NULL, &pblock);
1805 pair<string, rpcfn_type> pCallTable[] =
1807 make_pair("help", &help),
1808 make_pair("stop", &stop),
1809 make_pair("getblockcount", &getblockcount),
1810 make_pair("getblocknumber", &getblocknumber),
1811 make_pair("getconnectioncount", &getconnectioncount),
1812 make_pair("getdifficulty", &getdifficulty),
1813 make_pair("getgenerate", &getgenerate),
1814 make_pair("setgenerate", &setgenerate),
1815 make_pair("gethashespersec", &gethashespersec),
1816 make_pair("getinfo", &getinfo),
1817 make_pair("getnewaddress", &getnewaddress),
1818 make_pair("getaccountaddress", &getaccountaddress),
1819 make_pair("setaccount", &setaccount),
1820 make_pair("getaccount", &getaccount),
1821 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1822 make_pair("sendtoaddress", &sendtoaddress),
1823 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1824 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1825 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1826 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1827 make_pair("backupwallet", &backupwallet),
1828 make_pair("keypoolrefill", &keypoolrefill),
1829 make_pair("walletpassphrase", &walletpassphrase),
1830 make_pair("walletpassphrasechange", &walletpassphrasechange),
1831 make_pair("walletlock", &walletlock),
1832 make_pair("encryptwallet", &encryptwallet),
1833 make_pair("validateaddress", &validateaddress),
1834 make_pair("getbalance", &getbalance),
1835 make_pair("move", &movecmd),
1836 make_pair("sendfrom", &sendfrom),
1837 make_pair("sendmany", &sendmany),
1838 make_pair("gettransaction", &gettransaction),
1839 make_pair("listtransactions", &listtransactions),
1840 make_pair("signmessage", &signmessage),
1841 make_pair("verifymessage", &verifymessage),
1842 make_pair("getwork", &getwork),
1843 make_pair("listaccounts", &listaccounts),
1844 make_pair("settxfee", &settxfee),
1845 make_pair("getmemorypool", &getmemorypool),
1846 make_pair("listsinceblock", &listsinceblock),
1848 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1850 string pAllowInSafeMode[] =
1855 "getblocknumber", // deprecated
1856 "getconnectioncount",
1863 "getaccountaddress",
1865 "getaddressesbyaccount",
1874 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1882 // This ain't Apache. We're just using HTTP header for the length field
1883 // and to be compatible with other JSON-RPC implementations.
1886 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1889 s << "POST / HTTP/1.1\r\n"
1890 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1891 << "Host: 127.0.0.1\r\n"
1892 << "Content-Type: application/json\r\n"
1893 << "Content-Length: " << strMsg.size() << "\r\n"
1894 << "Connection: close\r\n"
1895 << "Accept: application/json\r\n";
1896 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1897 s << item.first << ": " << item.second << "\r\n";
1898 s << "\r\n" << strMsg;
1903 string rfc1123Time()
1908 struct tm* now_gmt = gmtime(&now);
1909 string locale(setlocale(LC_TIME, NULL));
1910 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
1911 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
1912 setlocale(LC_TIME, locale.c_str());
1913 return string(buffer);
1916 static string HTTPReply(int nStatus, const string& strMsg)
1919 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
1921 "Server: ppcoin-json-rpc/%s\r\n"
1922 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
1923 "Content-Type: text/html\r\n"
1924 "Content-Length: 296\r\n"
1926 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
1927 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
1930 "<TITLE>Error</TITLE>\r\n"
1931 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
1933 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
1934 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
1935 const char *cStatus;
1936 if (nStatus == 200) cStatus = "OK";
1937 else if (nStatus == 400) cStatus = "Bad Request";
1938 else if (nStatus == 403) cStatus = "Forbidden";
1939 else if (nStatus == 404) cStatus = "Not Found";
1940 else if (nStatus == 500) cStatus = "Internal Server Error";
1943 "HTTP/1.1 %d %s\r\n"
1945 "Connection: close\r\n"
1946 "Content-Length: %d\r\n"
1947 "Content-Type: application/json\r\n"
1948 "Server: ppcoin-json-rpc/%s\r\n"
1953 rfc1123Time().c_str(),
1955 FormatFullVersion().c_str(),
1959 int ReadHTTPStatus(std::basic_istream<char>& stream)
1962 getline(stream, str);
1963 vector<string> vWords;
1964 boost::split(vWords, str, boost::is_any_of(" "));
1965 if (vWords.size() < 2)
1967 return atoi(vWords[1].c_str());
1970 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
1976 std::getline(stream, str);
1977 if (str.empty() || str == "\r")
1979 string::size_type nColon = str.find(":");
1980 if (nColon != string::npos)
1982 string strHeader = str.substr(0, nColon);
1983 boost::trim(strHeader);
1984 boost::to_lower(strHeader);
1985 string strValue = str.substr(nColon+1);
1986 boost::trim(strValue);
1987 mapHeadersRet[strHeader] = strValue;
1988 if (strHeader == "content-length")
1989 nLen = atoi(strValue.c_str());
1995 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
1997 mapHeadersRet.clear();
2001 int nStatus = ReadHTTPStatus(stream);
2004 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2005 if (nLen < 0 || nLen > MAX_SIZE)
2011 vector<char> vch(nLen);
2012 stream.read(&vch[0], nLen);
2013 strMessageRet = string(vch.begin(), vch.end());
2019 bool HTTPAuthorized(map<string, string>& mapHeaders)
2021 string strAuth = mapHeaders["authorization"];
2022 if (strAuth.substr(0,6) != "Basic ")
2024 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2025 string strUserPass = DecodeBase64(strUserPass64);
2026 return strUserPass == strRPCUserColonPass;
2030 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2031 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2032 // unspecified (HTTP errors and contents of 'error').
2034 // 1.0 spec: http://json-rpc.org/wiki/specification
2035 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2036 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2039 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2042 request.push_back(Pair("method", strMethod));
2043 request.push_back(Pair("params", params));
2044 request.push_back(Pair("id", id));
2045 return write_string(Value(request), false) + "\n";
2048 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2051 if (error.type() != null_type)
2052 reply.push_back(Pair("result", Value::null));
2054 reply.push_back(Pair("result", result));
2055 reply.push_back(Pair("error", error));
2056 reply.push_back(Pair("id", id));
2057 return write_string(Value(reply), false) + "\n";
2060 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2062 // Send error reply from json-rpc error object
2064 int code = find_value(objError, "code").get_int();
2065 if (code == -32600) nStatus = 400;
2066 else if (code == -32601) nStatus = 404;
2067 string strReply = JSONRPCReply(Value::null, objError, id);
2068 stream << HTTPReply(nStatus, strReply) << std::flush;
2071 bool ClientAllowed(const string& strAddress)
2073 if (strAddress == asio::ip::address_v4::loopback().to_string())
2075 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2076 BOOST_FOREACH(string strAllow, vAllow)
2077 if (WildcardMatch(strAddress, strAllow))
2084 // IOStream device that speaks SSL but can also speak non-SSL
2086 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2088 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2090 fUseSSL = fUseSSLIn;
2091 fNeedHandshake = fUseSSLIn;
2094 void handshake(ssl::stream_base::handshake_type role)
2096 if (!fNeedHandshake) return;
2097 fNeedHandshake = false;
2098 stream.handshake(role);
2100 std::streamsize read(char* s, std::streamsize n)
2102 handshake(ssl::stream_base::server); // HTTPS servers read first
2103 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2104 return stream.next_layer().read_some(asio::buffer(s, n));
2106 std::streamsize write(const char* s, std::streamsize n)
2108 handshake(ssl::stream_base::client); // HTTPS clients write first
2109 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2110 return asio::write(stream.next_layer(), asio::buffer(s, n));
2112 bool connect(const std::string& server, const std::string& port)
2114 ip::tcp::resolver resolver(stream.get_io_service());
2115 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2116 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2117 ip::tcp::resolver::iterator end;
2118 boost::system::error_code error = asio::error::host_not_found;
2119 while (error && endpoint_iterator != end)
2121 stream.lowest_layer().close();
2122 stream.lowest_layer().connect(*endpoint_iterator++, error);
2130 bool fNeedHandshake;
2136 void ThreadRPCServer(void* parg)
2138 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2141 vnThreadsRunning[4]++;
2142 ThreadRPCServer2(parg);
2143 vnThreadsRunning[4]--;
2145 catch (std::exception& e) {
2146 vnThreadsRunning[4]--;
2147 PrintException(&e, "ThreadRPCServer()");
2149 vnThreadsRunning[4]--;
2150 PrintException(NULL, "ThreadRPCServer()");
2152 printf("ThreadRPCServer exiting\n");
2155 void ThreadRPCServer2(void* parg)
2157 printf("ThreadRPCServer started\n");
2159 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2160 if (strRPCUserColonPass == ":")
2162 string strWhatAmI = "To use ppcoind";
2163 if (mapArgs.count("-server"))
2164 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2165 else if (mapArgs.count("-daemon"))
2166 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2168 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2169 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2171 GetConfigFile().c_str());
2173 CreateThread(Shutdown, NULL);
2178 bool fUseSSL = GetBoolArg("-rpcssl");
2179 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2181 asio::io_service io_service;
2182 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2183 ip::tcp::acceptor acceptor(io_service, endpoint);
2185 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2188 ssl::context context(io_service, ssl::context::sslv23);
2191 context.set_options(ssl::context::no_sslv2);
2192 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2193 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2194 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2195 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2196 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2197 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2198 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2199 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2201 string ciphers = GetArg("-rpcsslciphers",
2202 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2203 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2207 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2212 // Accept connection
2214 SSLStream sslStream(io_service, context);
2215 SSLIOStreamDevice d(sslStream, fUseSSL);
2216 iostreams::stream<SSLIOStreamDevice> stream(d);
2218 ip::tcp::iostream stream;
2221 ip::tcp::endpoint peer;
2222 vnThreadsRunning[4]--;
2224 acceptor.accept(sslStream.lowest_layer(), peer);
2226 acceptor.accept(*stream.rdbuf(), peer);
2228 vnThreadsRunning[4]++;
2232 // Restrict callers by IP
2233 if (!ClientAllowed(peer.address().to_string()))
2235 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2237 stream << HTTPReply(403, "") << std::flush;
2241 map<string, string> mapHeaders;
2244 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2245 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2248 printf("ThreadRPCServer ReadHTTP timeout\n");
2252 // Check authorization
2253 if (mapHeaders.count("authorization") == 0)
2255 stream << HTTPReply(401, "") << std::flush;
2258 if (!HTTPAuthorized(mapHeaders))
2260 // Deter brute-forcing short passwords
2261 if (mapArgs["-rpcpassword"].size() < 15)
2264 stream << HTTPReply(401, "") << std::flush;
2265 printf("ThreadRPCServer incorrect password attempt\n");
2269 Value id = Value::null;
2274 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2275 throw JSONRPCError(-32700, "Parse error");
2276 const Object& request = valRequest.get_obj();
2278 // Parse id now so errors from here on will have the id
2279 id = find_value(request, "id");
2282 Value valMethod = find_value(request, "method");
2283 if (valMethod.type() == null_type)
2284 throw JSONRPCError(-32600, "Missing method");
2285 if (valMethod.type() != str_type)
2286 throw JSONRPCError(-32600, "Method must be a string");
2287 string strMethod = valMethod.get_str();
2288 if (strMethod != "getwork" && strMethod != "getmemorypool")
2289 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2292 Value valParams = find_value(request, "params");
2294 if (valParams.type() == array_type)
2295 params = valParams.get_array();
2296 else if (valParams.type() == null_type)
2299 throw JSONRPCError(-32600, "Params must be an array");
2302 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2303 if (mi == mapCallTable.end())
2304 throw JSONRPCError(-32601, "Method not found");
2306 // Observe safe mode
2307 string strWarning = GetWarnings("rpc");
2308 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2309 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2315 CRITICAL_BLOCK(cs_main)
2316 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2317 result = (*(*mi).second)(params, false);
2320 string strReply = JSONRPCReply(result, Value::null, id);
2321 stream << HTTPReply(200, strReply) << std::flush;
2323 catch (std::exception& e)
2325 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2328 catch (Object& objError)
2330 ErrorReply(stream, objError, id);
2332 catch (std::exception& e)
2334 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2342 Object CallRPC(const string& strMethod, const Array& params)
2344 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2345 throw runtime_error(strprintf(
2346 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2347 "If the file does not exist, create it with owner-readable-only file permissions."),
2348 GetConfigFile().c_str()));
2350 // Connect to localhost
2351 bool fUseSSL = GetBoolArg("-rpcssl");
2353 asio::io_service io_service;
2354 ssl::context context(io_service, ssl::context::sslv23);
2355 context.set_options(ssl::context::no_sslv2);
2356 SSLStream sslStream(io_service, context);
2357 SSLIOStreamDevice d(sslStream, fUseSSL);
2358 iostreams::stream<SSLIOStreamDevice> stream(d);
2359 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2360 throw runtime_error("couldn't connect to server");
2363 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2365 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2367 throw runtime_error("couldn't connect to server");
2371 // HTTP basic authentication
2372 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2373 map<string, string> mapRequestHeaders;
2374 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2377 string strRequest = JSONRPCRequest(strMethod, params, 1);
2378 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2379 stream << strPost << std::flush;
2382 map<string, string> mapHeaders;
2384 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2386 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2387 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2388 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2389 else if (strReply.empty())
2390 throw runtime_error("no response from server");
2394 if (!read_string(strReply, valReply))
2395 throw runtime_error("couldn't parse reply from server");
2396 const Object& reply = valReply.get_obj();
2398 throw runtime_error("expected reply to have result, error and id properties");
2406 template<typename T>
2407 void ConvertTo(Value& value)
2409 if (value.type() == str_type)
2411 // reinterpret string as unquoted json value
2413 if (!read_string(value.get_str(), value2))
2414 throw runtime_error("type mismatch");
2415 value = value2.get_value<T>();
2419 value = value.get_value<T>();
2423 int CommandLineRPC(int argc, char *argv[])
2430 while (argc > 1 && IsSwitchChar(argv[1][0]))
2438 throw runtime_error("too few parameters");
2439 string strMethod = argv[1];
2441 // Parameters default to strings
2443 for (int i = 2; i < argc; i++)
2444 params.push_back(argv[i]);
2445 int n = params.size();
2448 // Special case non-string parameter types
2450 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2451 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2452 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2453 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2454 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2455 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2456 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2457 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2458 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2459 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2460 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2461 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2462 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2463 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2464 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2465 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2466 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2467 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2468 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2469 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2470 if (strMethod == "sendmany" && n > 1)
2472 string s = params[1].get_str();
2474 if (!read_string(s, v) || v.type() != obj_type)
2475 throw runtime_error("type mismatch");
2476 params[1] = v.get_obj();
2478 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2481 Object reply = CallRPC(strMethod, params);
2484 const Value& result = find_value(reply, "result");
2485 const Value& error = find_value(reply, "error");
2487 if (error.type() != null_type)
2490 strPrint = "error: " + write_string(error, false);
2491 int code = find_value(error.get_obj(), "code").get_int();
2497 if (result.type() == null_type)
2499 else if (result.type() == str_type)
2500 strPrint = result.get_str();
2502 strPrint = write_string(result, true);
2505 catch (std::exception& e)
2507 strPrint = string("error: ") + e.what();
2512 PrintException(NULL, "CommandLineRPC()");
2517 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2526 int main(int argc, char *argv[])
2529 // Turn off microsoft heap dump noise
2530 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2531 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2533 setbuf(stdin, NULL);
2534 setbuf(stdout, NULL);
2535 setbuf(stderr, NULL);
2539 if (argc >= 2 && string(argv[1]) == "-server")
2541 printf("server ready\n");
2542 ThreadRPCServer(NULL);
2546 return CommandLineRPC(argc, argv);
2549 catch (std::exception& e) {
2550 PrintException(&e, "main()");
2552 PrintException(NULL, "main()");