1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Copyright (c) 2011-2012 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.
11 #include "checkpoints.h"
13 #include <boost/asio.hpp>
14 #include <boost/iostreams/concepts.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #include <boost/algorithm/string.hpp>
18 #include <boost/asio/ssl.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/filesystem/fstream.hpp>
21 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
23 #include "json/json_spirit_reader_template.h"
24 #include "json/json_spirit_writer_template.h"
25 #include "json/json_spirit_utils.h"
26 #define printf OutputDebugStringF
27 // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
28 // precompiled in headers.h. The problem might be when the pch file goes over
29 // a certain size around 145MB. If we need access to json_spirit outside this
30 // file, we could use the compiled json_spirit option.
33 using namespace boost;
34 using namespace boost::asio;
35 using namespace json_spirit;
37 void ThreadRPCServer2(void* parg);
38 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
39 extern map<string, rpcfn_type> mapCallTable;
41 static std::string strRPCUserColonPass;
43 static int64 nWalletUnlockTime;
44 static CCriticalSection cs_nWalletUnlockTime;
47 Object JSONRPCError(int code, const string& message)
50 error.push_back(Pair("code", code));
51 error.push_back(Pair("message", message));
56 void PrintConsole(const std::string &format, ...)
59 int limit = sizeof(buffer);
61 va_start(arg_ptr, format);
62 int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
64 if (ret < 0 || ret >= limit)
70 fprintf(stdout, "%s", buffer);
74 int64 AmountFromValue(const Value& value)
76 double dAmount = value.get_real();
77 if (dAmount <= 0.0 || dAmount > MAX_MONEY)
78 throw JSONRPCError(-3, "Invalid amount");
79 int64 nAmount = roundint64(dAmount * COIN);
80 if (!MoneyRange(nAmount))
81 throw JSONRPCError(-3, "Invalid amount");
85 Value ValueFromAmount(int64 amount)
87 return (double)amount / (double)COIN;
90 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
92 entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain()));
93 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
94 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
95 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
96 entry.push_back(Pair(item.first, item.second));
99 string AccountFromValue(const Value& value)
101 string strAccount = value.get_str();
102 if (strAccount == "*")
103 throw JSONRPCError(-11, "Invalid account name");
110 /// Note: This interface may still be subject to change.
114 Value help(const Array& params, bool fHelp)
116 if (fHelp || params.size() > 1)
119 "List commands, or get help for a command.");
122 if (params.size() > 0)
123 strCommand = params[0].get_str();
126 set<rpcfn_type> setDone;
127 for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
129 string strMethod = (*mi).first;
130 // We already filter duplicates, but these deprecated screw up the sort order
131 if (strMethod == "getamountreceived" ||
132 strMethod == "getallreceived" ||
133 strMethod == "getblocknumber" || // deprecated
134 (strMethod.find("label") != string::npos))
136 if (strCommand != "" && strMethod != strCommand)
141 rpcfn_type pfn = (*mi).second;
142 if (setDone.insert(pfn).second)
143 (*pfn)(params, true);
145 catch (std::exception& e)
147 // Help text is returned in an exception
148 string strHelp = string(e.what());
149 if (strCommand == "")
150 if (strHelp.find('\n') != -1)
151 strHelp = strHelp.substr(0, strHelp.find('\n'));
152 strRet += strHelp + "\n";
156 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
157 strRet = strRet.substr(0,strRet.size()-1);
162 Value stop(const Array& params, bool fHelp)
164 if (fHelp || params.size() != 0)
167 "Stop ppcoin server.");
169 // Shutdown will take long enough that the response should get back
170 CreateThread(Shutdown, NULL);
171 return "ppcoin server stopping";
173 throw runtime_error("NYI: cannot shut down GUI with RPC command");
178 Value getblockcount(const Array& params, bool fHelp)
180 if (fHelp || params.size() != 0)
183 "Returns the number of blocks in the longest block chain.");
190 Value getblocknumber(const Array& params, bool fHelp)
192 if (fHelp || params.size() != 0)
195 "Deprecated. Use getblockcount.");
201 Value getconnectioncount(const Array& params, bool fHelp)
203 if (fHelp || params.size() != 0)
205 "getconnectioncount\n"
206 "Returns the number of connections to other nodes.");
208 return (int)vNodes.size();
212 double GetDifficulty()
214 // Floating point number that is a multiple of the minimum difficulty,
215 // minimum difficulty = 1.0.
217 if (pindexBest == NULL)
219 int nShift = (pindexBest->nBits >> 24) & 0xff;
222 (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff);
238 Value getdifficulty(const Array& params, bool fHelp)
240 if (fHelp || params.size() != 0)
243 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
245 return GetDifficulty();
249 Value getgenerate(const Array& params, bool fHelp)
251 if (fHelp || params.size() != 0)
254 "Returns true or false.");
256 return (bool)fGenerateBitcoins;
260 Value setgenerate(const Array& params, bool fHelp)
262 if (fHelp || params.size() < 1 || params.size() > 2)
264 "setgenerate <generate> [genproclimit]\n"
265 "<generate> is true or false to turn generation on or off.\n"
266 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
268 bool fGenerate = true;
269 if (params.size() > 0)
270 fGenerate = params[0].get_bool();
272 if (params.size() > 1)
274 int nGenProcLimit = params[1].get_int();
275 fLimitProcessors = (nGenProcLimit != -1);
276 WriteSetting("fLimitProcessors", fLimitProcessors);
277 if (nGenProcLimit != -1)
278 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
279 if (nGenProcLimit == 0)
283 GenerateBitcoins(fGenerate, pwalletMain);
288 Value gethashespersec(const Array& params, bool fHelp)
290 if (fHelp || params.size() != 0)
293 "Returns a recent hashes per second performance measurement while generating.");
295 if (GetTimeMillis() - nHPSTimerStart > 8000)
296 return (boost::int64_t)0;
297 return (boost::int64_t)dHashesPerSec;
301 Value getinfo(const Array& params, bool fHelp)
303 if (fHelp || params.size() != 0)
306 "Returns an object containing various state info.");
309 obj.push_back(Pair("version", (int)VERSION));
310 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
311 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
312 obj.push_back(Pair("blocks", (int)nBestHeight));
313 obj.push_back(Pair("connections", (int)vNodes.size()));
314 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
315 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
316 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
317 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
318 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
319 obj.push_back(Pair("testnet", fTestNet));
320 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
321 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
322 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
323 if (pwalletMain->IsCrypted())
324 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
325 obj.push_back(Pair("errors", GetWarnings("statusbar")));
330 Value getnewaddress(const Array& params, bool fHelp)
332 if (fHelp || params.size() > 1)
334 "getnewaddress [account]\n"
335 "Returns a new ppcoin address for receiving payments. "
336 "If [account] is specified (recommended), it is added to the address book "
337 "so payments received with the address will be credited to [account].");
339 // Parse the account first so we don't generate a key if there's an error
341 if (params.size() > 0)
342 strAccount = AccountFromValue(params[0]);
344 if (!pwalletMain->IsLocked())
345 pwalletMain->TopUpKeyPool();
347 // Generate a new key that is added to wallet
348 std::vector<unsigned char> newKey;
349 if (!pwalletMain->GetKeyFromPool(newKey, false))
350 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
351 CBitcoinAddress address(newKey);
353 pwalletMain->SetAddressBookName(address, strAccount);
355 return address.ToString();
359 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
361 CWalletDB walletdb(pwalletMain->strWalletFile);
364 walletdb.ReadAccount(strAccount, account);
366 bool bKeyUsed = false;
368 // Check if the current key has been used
369 if (!account.vchPubKey.empty())
371 CScript scriptPubKey;
372 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
373 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
374 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
377 const CWalletTx& wtx = (*it).second;
378 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
379 if (txout.scriptPubKey == scriptPubKey)
384 // Generate a new key
385 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
387 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
388 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
390 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
391 walletdb.WriteAccount(strAccount, account);
394 return CBitcoinAddress(account.vchPubKey);
397 Value getaccountaddress(const Array& params, bool fHelp)
399 if (fHelp || params.size() != 1)
401 "getaccountaddress <account>\n"
402 "Returns the current ppcoin address for receiving payments to this account.");
404 // Parse the account first so we don't generate a key if there's an error
405 string strAccount = AccountFromValue(params[0]);
409 ret = GetAccountAddress(strAccount).ToString();
416 Value setaccount(const Array& params, bool fHelp)
418 if (fHelp || params.size() < 1 || params.size() > 2)
420 "setaccount <ppcoinaddress> <account>\n"
421 "Sets the account associated with the given address.");
423 CBitcoinAddress address(params[0].get_str());
424 if (!address.IsValid())
425 throw JSONRPCError(-5, "Invalid ppcoin address");
429 if (params.size() > 1)
430 strAccount = AccountFromValue(params[1]);
432 // Detect when changing the account of an address that is the 'unused current key' of another account:
433 if (pwalletMain->mapAddressBook.count(address))
435 string strOldAccount = pwalletMain->mapAddressBook[address];
436 if (address == GetAccountAddress(strOldAccount))
437 GetAccountAddress(strOldAccount, true);
440 pwalletMain->SetAddressBookName(address, strAccount);
446 Value getaccount(const Array& params, bool fHelp)
448 if (fHelp || params.size() != 1)
450 "getaccount <ppcoinaddress>\n"
451 "Returns the account associated with the given address.");
453 CBitcoinAddress address(params[0].get_str());
454 if (!address.IsValid())
455 throw JSONRPCError(-5, "Invalid ppcoin address");
458 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
459 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
460 strAccount = (*mi).second;
465 Value getaddressesbyaccount(const Array& params, bool fHelp)
467 if (fHelp || params.size() != 1)
469 "getaddressesbyaccount <account>\n"
470 "Returns the list of addresses for the given account.");
472 string strAccount = AccountFromValue(params[0]);
474 // Find all addresses that have the given account
476 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
478 const CBitcoinAddress& address = item.first;
479 const string& strName = item.second;
480 if (strName == strAccount)
481 ret.push_back(address.ToString());
486 Value settxfee(const Array& params, bool fHelp)
488 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
490 "settxfee <amount>\n"
491 "<amount> is a real and is rounded to 0.01 (cent)\n"
492 "Minimum and default transaction fee per KB is 1 cent");
494 nTransactionFee = AmountFromValue(params[0]);
495 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
499 Value sendtoaddress(const Array& params, bool fHelp)
501 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
503 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
504 "<amount> is a real and is rounded to the nearest 0.000001\n"
505 "requires wallet passphrase to be set with walletpassphrase first");
506 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
508 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
509 "<amount> is a real and is rounded to the nearest 0.000001");
511 CBitcoinAddress address(params[0].get_str());
512 if (!address.IsValid())
513 throw JSONRPCError(-5, "Invalid ppcoin address");
516 int64 nAmount = AmountFromValue(params[1]);
520 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
521 wtx.mapValue["comment"] = params[2].get_str();
522 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
523 wtx.mapValue["to"] = params[3].get_str();
525 if (pwalletMain->IsLocked())
526 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
528 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
530 throw JSONRPCError(-4, strError);
532 return wtx.GetHash().GetHex();
535 static const string strMessageMagic = "Bitcoin Signed Message:\n";
537 Value signmessage(const Array& params, bool fHelp)
539 if (fHelp || params.size() != 2)
541 "signmessage <ppcoinaddress> <message>\n"
542 "Sign a message with the private key of an address");
544 if (pwalletMain->IsLocked())
545 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
547 string strAddress = params[0].get_str();
548 string strMessage = params[1].get_str();
550 CBitcoinAddress addr(strAddress);
552 throw JSONRPCError(-3, "Invalid address");
555 if (!pwalletMain->GetKey(addr, key))
556 throw JSONRPCError(-4, "Private key not available");
558 CDataStream ss(SER_GETHASH);
559 ss << strMessageMagic;
562 vector<unsigned char> vchSig;
563 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
564 throw JSONRPCError(-5, "Sign failed");
566 return EncodeBase64(&vchSig[0], vchSig.size());
569 Value verifymessage(const Array& params, bool fHelp)
571 if (fHelp || params.size() != 3)
573 "verifymessage <ppcoinaddress> <signature> <message>\n"
574 "Verify a signed message");
576 string strAddress = params[0].get_str();
577 string strSign = params[1].get_str();
578 string strMessage = params[2].get_str();
580 CBitcoinAddress addr(strAddress);
582 throw JSONRPCError(-3, "Invalid address");
584 bool fInvalid = false;
585 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
588 throw JSONRPCError(-5, "Malformed base64 encoding");
590 CDataStream ss(SER_GETHASH);
591 ss << strMessageMagic;
595 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
598 return (key.GetAddress() == addr);
602 Value getreceivedbyaddress(const Array& params, bool fHelp)
604 if (fHelp || params.size() < 1 || params.size() > 2)
606 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
607 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
610 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
611 CScript scriptPubKey;
612 if (!address.IsValid())
613 throw JSONRPCError(-5, "Invalid ppcoin address");
614 scriptPubKey.SetBitcoinAddress(address);
615 if (!IsMine(*pwalletMain,scriptPubKey))
618 // Minimum confirmations
620 if (params.size() > 1)
621 nMinDepth = params[1].get_int();
625 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
627 const CWalletTx& wtx = (*it).second;
628 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
631 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
632 if (txout.scriptPubKey == scriptPubKey)
633 if (wtx.GetDepthInMainChain() >= nMinDepth)
634 nAmount += txout.nValue;
637 return ValueFromAmount(nAmount);
641 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
643 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
645 const CBitcoinAddress& address = item.first;
646 const string& strName = item.second;
647 if (strName == strAccount)
648 setAddress.insert(address);
653 Value getreceivedbyaccount(const Array& params, bool fHelp)
655 if (fHelp || params.size() < 1 || params.size() > 2)
657 "getreceivedbyaccount <account> [minconf=1]\n"
658 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
660 // Minimum confirmations
662 if (params.size() > 1)
663 nMinDepth = params[1].get_int();
665 // Get the set of pub keys that have the label
666 string strAccount = AccountFromValue(params[0]);
667 set<CBitcoinAddress> setAddress;
668 GetAccountAddresses(strAccount, setAddress);
672 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
674 const CWalletTx& wtx = (*it).second;
675 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
678 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
680 CBitcoinAddress address;
681 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
682 if (wtx.GetDepthInMainChain() >= nMinDepth)
683 nAmount += txout.nValue;
687 return (double)nAmount / (double)COIN;
691 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
695 // Tally wallet transactions
696 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
698 const CWalletTx& wtx = (*it).second;
702 int64 nGenerated, nReceived, nSent, nFee;
703 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
705 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
706 nBalance += nReceived;
707 nBalance += nGenerated - nSent - nFee;
710 // Tally internal accounting entries
711 nBalance += walletdb.GetAccountCreditDebit(strAccount);
716 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
718 CWalletDB walletdb(pwalletMain->strWalletFile);
719 return GetAccountBalance(walletdb, strAccount, nMinDepth);
723 Value getbalance(const Array& params, bool fHelp)
725 if (fHelp || params.size() > 2)
727 "getbalance [account] [minconf=1]\n"
728 "If [account] is not specified, returns the server's total available balance.\n"
729 "If [account] is specified, returns the balance in the account.");
731 if (params.size() == 0)
732 return ValueFromAmount(pwalletMain->GetBalance());
735 if (params.size() > 1)
736 nMinDepth = params[1].get_int();
738 if (params[0].get_str() == "*") {
739 // Calculate total balance a different way from GetBalance()
740 // (GetBalance() sums up all unspent TxOuts)
741 // getbalance and getbalance '*' should always return the same number.
743 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
745 const CWalletTx& wtx = (*it).second;
749 int64 allGeneratedImmature, allGeneratedMature, allFee;
750 allGeneratedImmature = allGeneratedMature = allFee = 0;
751 string strSentAccount;
752 list<pair<CBitcoinAddress, int64> > listReceived;
753 list<pair<CBitcoinAddress, int64> > listSent;
754 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
755 if (wtx.GetDepthInMainChain() >= nMinDepth)
756 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
757 nBalance += r.second;
758 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
759 nBalance -= r.second;
761 nBalance += allGeneratedMature;
763 return ValueFromAmount(nBalance);
766 string strAccount = AccountFromValue(params[0]);
768 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
770 return ValueFromAmount(nBalance);
774 Value movecmd(const Array& params, bool fHelp)
776 if (fHelp || params.size() < 3 || params.size() > 5)
778 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
779 "Move from one account in your wallet to another.");
781 string strFrom = AccountFromValue(params[0]);
782 string strTo = AccountFromValue(params[1]);
783 int64 nAmount = AmountFromValue(params[2]);
784 if (params.size() > 3)
785 // unused parameter, used to be nMinDepth, keep type-checking it though
786 (void)params[3].get_int();
788 if (params.size() > 4)
789 strComment = params[4].get_str();
791 CWalletDB walletdb(pwalletMain->strWalletFile);
794 int64 nNow = GetAdjustedTime();
797 CAccountingEntry debit;
798 debit.strAccount = strFrom;
799 debit.nCreditDebit = -nAmount;
801 debit.strOtherAccount = strTo;
802 debit.strComment = strComment;
803 walletdb.WriteAccountingEntry(debit);
806 CAccountingEntry credit;
807 credit.strAccount = strTo;
808 credit.nCreditDebit = nAmount;
810 credit.strOtherAccount = strFrom;
811 credit.strComment = strComment;
812 walletdb.WriteAccountingEntry(credit);
814 walletdb.TxnCommit();
820 Value sendfrom(const Array& params, bool fHelp)
822 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
824 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
825 "<amount> is a real and is rounded to the nearest 0.000001\n"
826 "requires wallet passphrase to be set with walletpassphrase first");
827 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
829 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
830 "<amount> is a real and is rounded to the nearest 0.000001");
832 string strAccount = AccountFromValue(params[0]);
833 CBitcoinAddress address(params[1].get_str());
834 if (!address.IsValid())
835 throw JSONRPCError(-5, "Invalid ppcoin address");
836 int64 nAmount = AmountFromValue(params[2]);
838 if (params.size() > 3)
839 nMinDepth = params[3].get_int();
842 wtx.strFromAccount = strAccount;
843 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
844 wtx.mapValue["comment"] = params[4].get_str();
845 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
846 wtx.mapValue["to"] = params[5].get_str();
848 if (pwalletMain->IsLocked())
849 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
852 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
853 if (nAmount > nBalance)
854 throw JSONRPCError(-6, "Account has insufficient funds");
857 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
859 throw JSONRPCError(-4, strError);
861 return wtx.GetHash().GetHex();
865 Value sendmany(const Array& params, bool fHelp)
867 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
869 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
870 "amounts are double-precision floating point numbers\n"
871 "requires wallet passphrase to be set with walletpassphrase first");
872 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
874 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
875 "amounts are double-precision floating point numbers");
877 string strAccount = AccountFromValue(params[0]);
878 Object sendTo = params[1].get_obj();
880 if (params.size() > 2)
881 nMinDepth = params[2].get_int();
884 wtx.strFromAccount = strAccount;
885 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
886 wtx.mapValue["comment"] = params[3].get_str();
888 set<CBitcoinAddress> setAddress;
889 vector<pair<CScript, int64> > vecSend;
891 int64 totalAmount = 0;
892 BOOST_FOREACH(const Pair& s, sendTo)
894 CBitcoinAddress address(s.name_);
895 if (!address.IsValid())
896 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
898 if (setAddress.count(address))
899 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
900 setAddress.insert(address);
902 CScript scriptPubKey;
903 scriptPubKey.SetBitcoinAddress(address);
904 int64 nAmount = AmountFromValue(s.value_);
905 totalAmount += nAmount;
907 vecSend.push_back(make_pair(scriptPubKey, nAmount));
910 if (pwalletMain->IsLocked())
911 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
912 if (fWalletUnlockStakeOnly)
913 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
916 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
917 if (totalAmount > nBalance)
918 throw JSONRPCError(-6, "Account has insufficient funds");
921 CReserveKey keyChange(pwalletMain);
922 int64 nFeeRequired = 0;
923 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
926 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
927 throw JSONRPCError(-6, "Insufficient funds");
928 throw JSONRPCError(-4, "Transaction creation failed");
930 if (!pwalletMain->CommitTransaction(wtx, keyChange))
931 throw JSONRPCError(-4, "Transaction commit failed");
933 return wtx.GetHash().GetHex();
948 Value ListReceived(const Array& params, bool fByAccounts)
950 // Minimum confirmations
952 if (params.size() > 0)
953 nMinDepth = params[0].get_int();
955 // Whether to include empty accounts
956 bool fIncludeEmpty = false;
957 if (params.size() > 1)
958 fIncludeEmpty = params[1].get_bool();
961 map<CBitcoinAddress, tallyitem> mapTally;
962 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
964 const CWalletTx& wtx = (*it).second;
965 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
968 int nDepth = wtx.GetDepthInMainChain();
969 if (nDepth < nMinDepth)
972 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
974 CBitcoinAddress address;
975 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
978 tallyitem& item = mapTally[address];
979 item.nAmount += txout.nValue;
980 item.nConf = min(item.nConf, nDepth);
986 map<string, tallyitem> mapAccountTally;
987 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
989 const CBitcoinAddress& address = item.first;
990 const string& strAccount = item.second;
991 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
992 if (it == mapTally.end() && !fIncludeEmpty)
997 if (it != mapTally.end())
999 nAmount = (*it).second.nAmount;
1000 nConf = (*it).second.nConf;
1005 tallyitem& item = mapAccountTally[strAccount];
1006 item.nAmount += nAmount;
1007 item.nConf = min(item.nConf, nConf);
1012 obj.push_back(Pair("address", address.ToString()));
1013 obj.push_back(Pair("account", strAccount));
1014 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1015 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1022 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1024 int64 nAmount = (*it).second.nAmount;
1025 int nConf = (*it).second.nConf;
1027 obj.push_back(Pair("account", (*it).first));
1028 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1029 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1037 Value listreceivedbyaddress(const Array& params, bool fHelp)
1039 if (fHelp || params.size() > 2)
1040 throw runtime_error(
1041 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1042 "[minconf] is the minimum number of confirmations before payments are included.\n"
1043 "[includeempty] whether to include addresses that haven't received any payments.\n"
1044 "Returns an array of objects containing:\n"
1045 " \"address\" : receiving address\n"
1046 " \"account\" : the account of the receiving address\n"
1047 " \"amount\" : total amount received by the address\n"
1048 " \"confirmations\" : number of confirmations of the most recent transaction included");
1050 return ListReceived(params, false);
1053 Value listreceivedbyaccount(const Array& params, bool fHelp)
1055 if (fHelp || params.size() > 2)
1056 throw runtime_error(
1057 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1058 "[minconf] is the minimum number of confirmations before payments are included.\n"
1059 "[includeempty] whether to include accounts that haven't received any payments.\n"
1060 "Returns an array of objects containing:\n"
1061 " \"account\" : the account of the receiving addresses\n"
1062 " \"amount\" : total amount received by addresses with this account\n"
1063 " \"confirmations\" : number of confirmations of the most recent transaction included");
1065 return ListReceived(params, true);
1068 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1070 int64 nGeneratedImmature, nGeneratedMature, nFee;
1071 string strSentAccount;
1072 list<pair<CBitcoinAddress, int64> > listReceived;
1073 list<pair<CBitcoinAddress, int64> > listSent;
1074 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1076 bool fAllAccounts = (strAccount == string("*"));
1078 // Generated blocks assigned to account ""
1079 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1082 entry.push_back(Pair("account", string("")));
1083 if (nGeneratedImmature)
1085 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1086 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1090 entry.push_back(Pair("category", "generate"));
1091 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1094 WalletTxToJSON(wtx, entry);
1095 ret.push_back(entry);
1099 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1101 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1104 entry.push_back(Pair("account", strSentAccount));
1105 entry.push_back(Pair("address", s.first.ToString()));
1106 entry.push_back(Pair("category", "send"));
1107 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1108 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1110 WalletTxToJSON(wtx, entry);
1111 ret.push_back(entry);
1116 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1117 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1120 if (pwalletMain->mapAddressBook.count(r.first))
1121 account = pwalletMain->mapAddressBook[r.first];
1122 if (fAllAccounts || (account == strAccount))
1125 entry.push_back(Pair("account", account));
1126 entry.push_back(Pair("address", r.first.ToString()));
1127 entry.push_back(Pair("category", "receive"));
1128 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1130 WalletTxToJSON(wtx, entry);
1131 ret.push_back(entry);
1136 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1138 bool fAllAccounts = (strAccount == string("*"));
1140 if (fAllAccounts || acentry.strAccount == strAccount)
1143 entry.push_back(Pair("account", acentry.strAccount));
1144 entry.push_back(Pair("category", "move"));
1145 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1146 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1147 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1148 entry.push_back(Pair("comment", acentry.strComment));
1149 ret.push_back(entry);
1153 Value listtransactions(const Array& params, bool fHelp)
1155 if (fHelp || params.size() > 3)
1156 throw runtime_error(
1157 "listtransactions [account] [count=10] [from=0]\n"
1158 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1160 string strAccount = "*";
1161 if (params.size() > 0)
1162 strAccount = params[0].get_str();
1164 if (params.size() > 1)
1165 nCount = params[1].get_int();
1167 if (params.size() > 2)
1168 nFrom = params[2].get_int();
1171 CWalletDB walletdb(pwalletMain->strWalletFile);
1173 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1174 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1175 typedef multimap<int64, TxPair > TxItems;
1178 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1180 CWalletTx* wtx = &((*it).second);
1181 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1183 list<CAccountingEntry> acentries;
1184 walletdb.ListAccountCreditDebit(strAccount, acentries);
1185 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1187 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1190 // Now: iterate backwards until we have nCount items to return:
1191 TxItems::reverse_iterator it = txByTime.rbegin();
1192 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1193 for (; it != txByTime.rend(); ++it)
1195 CWalletTx *const pwtx = (*it).second.first;
1197 ListTransactions(*pwtx, strAccount, 0, true, ret);
1198 CAccountingEntry *const pacentry = (*it).second.second;
1200 AcentryToJSON(*pacentry, strAccount, ret);
1202 if (ret.size() >= nCount) break;
1204 // ret is now newest to oldest
1206 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1207 if (ret.size() > nCount)
1209 Array::iterator last = ret.begin();
1210 std::advance(last, nCount);
1211 ret.erase(last, ret.end());
1213 std::reverse(ret.begin(), ret.end()); // oldest to newest
1218 Value listaccounts(const Array& params, bool fHelp)
1220 if (fHelp || params.size() > 1)
1221 throw runtime_error(
1222 "listaccounts [minconf=1]\n"
1223 "Returns Object that has account names as keys, account balances as values.");
1226 if (params.size() > 0)
1227 nMinDepth = params[0].get_int();
1229 map<string, int64> mapAccountBalances;
1230 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1231 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1232 mapAccountBalances[entry.second] = 0;
1235 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1237 const CWalletTx& wtx = (*it).second;
1238 int64 nGeneratedImmature, nGeneratedMature, nFee;
1239 string strSentAccount;
1240 list<pair<CBitcoinAddress, int64> > listReceived;
1241 list<pair<CBitcoinAddress, int64> > listSent;
1242 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1243 mapAccountBalances[strSentAccount] -= nFee;
1244 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1245 mapAccountBalances[strSentAccount] -= s.second;
1246 if (wtx.GetDepthInMainChain() >= nMinDepth)
1248 mapAccountBalances[""] += nGeneratedMature;
1249 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1250 if (pwalletMain->mapAddressBook.count(r.first))
1251 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1253 mapAccountBalances[""] += r.second;
1257 list<CAccountingEntry> acentries;
1258 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1259 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1260 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1263 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1264 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1269 Value listsinceblock(const Array& params, bool fHelp)
1272 throw runtime_error(
1273 "listsinceblock [blockid] [target-confirmations]\n"
1274 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1276 CBlockIndex *pindex = NULL;
1277 int target_confirms = 1;
1279 if (params.size() > 0)
1281 uint256 blockId = 0;
1283 blockId.SetHex(params[0].get_str());
1284 pindex = CBlockLocator(blockId).GetBlockIndex();
1287 if (params.size() > 1)
1289 target_confirms = params[1].get_int();
1291 if (target_confirms < 1)
1292 throw JSONRPCError(-8, "Invalid parameter");
1295 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1299 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1301 CWalletTx tx = (*it).second;
1303 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1304 ListTransactions(tx, "*", 0, true, transactions);
1309 if (target_confirms == 1)
1312 lastblock = hashBestChain;
1316 int target_height = pindexBest->nHeight + 1 - target_confirms;
1319 for (block = pindexBest;
1320 block && block->nHeight > target_height;
1321 block = block->pprev);
1323 lastblock = block ? block->GetBlockHash() : 0;
1327 ret.push_back(Pair("transactions", transactions));
1328 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1333 Value gettransaction(const Array& params, bool fHelp)
1335 if (fHelp || params.size() != 1)
1336 throw runtime_error(
1337 "gettransaction <txid>\n"
1338 "Get detailed information about <txid>");
1341 hash.SetHex(params[0].get_str());
1345 if (!pwalletMain->mapWallet.count(hash))
1346 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1347 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1349 int64 nCredit = wtx.GetCredit();
1350 int64 nDebit = wtx.GetDebit();
1351 int64 nNet = nCredit - nDebit;
1352 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1354 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1356 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1358 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1361 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1362 entry.push_back(Pair("details", details));
1368 Value backupwallet(const Array& params, bool fHelp)
1370 if (fHelp || params.size() != 1)
1371 throw runtime_error(
1372 "backupwallet <destination>\n"
1373 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1375 string strDest = params[0].get_str();
1376 BackupWallet(*pwalletMain, strDest);
1382 Value keypoolrefill(const Array& params, bool fHelp)
1384 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1385 throw runtime_error(
1387 "Fills the keypool, requires wallet passphrase to be set.");
1388 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1389 throw runtime_error(
1391 "Fills the keypool.");
1393 if (pwalletMain->IsLocked())
1394 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1396 pwalletMain->TopUpKeyPool();
1398 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1399 throw JSONRPCError(-4, "Error refreshing keypool.");
1405 void ThreadTopUpKeyPool(void* parg)
1407 pwalletMain->TopUpKeyPool();
1410 void ThreadCleanWalletPassphrase(void* parg)
1412 int64 nMyWakeTime = GetTime() + *((int*)parg);
1414 if (nWalletUnlockTime == 0)
1416 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1418 nWalletUnlockTime = nMyWakeTime;
1421 while (GetTime() < nWalletUnlockTime)
1422 Sleep(GetTime() - nWalletUnlockTime);
1424 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1426 nWalletUnlockTime = 0;
1431 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1433 if (nWalletUnlockTime < nMyWakeTime)
1434 nWalletUnlockTime = nMyWakeTime;
1440 pwalletMain->Lock();
1445 Value walletpassphrase(const Array& params, bool fHelp)
1447 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1448 throw runtime_error(
1449 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1450 "Stores the wallet decryption key in memory for <timeout> seconds."
1451 "stakeonly is an optional true/false allowing only stake creation.");
1454 if (!pwalletMain->IsCrypted())
1455 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1457 if (!pwalletMain->IsLocked())
1458 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
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();
1467 if (strWalletPass.length() > 0)
1469 if (!pwalletMain->Unlock(strWalletPass))
1470 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1473 throw runtime_error(
1474 "walletpassphrase <passphrase> <timeout>\n"
1475 "Stores the wallet decryption key in memory for <timeout> seconds.");
1477 CreateThread(ThreadTopUpKeyPool, NULL);
1478 int* pnSleepTime = new int(params[1].get_int());
1479 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1481 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1482 if (params.size() > 2)
1483 fWalletUnlockStakeOnly = params[2].get_bool();
1485 fWalletUnlockStakeOnly = false;
1491 Value walletpassphrasechange(const Array& params, bool fHelp)
1493 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1494 throw runtime_error(
1495 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1496 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1499 if (!pwalletMain->IsCrypted())
1500 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1502 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1503 // Alternately, find a way to make params[0] mlock()'d to begin with.
1504 SecureString strOldWalletPass;
1505 strOldWalletPass.reserve(100);
1506 strOldWalletPass = params[0].get_str().c_str();
1508 SecureString strNewWalletPass;
1509 strNewWalletPass.reserve(100);
1510 strNewWalletPass = params[1].get_str().c_str();
1512 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1513 throw runtime_error(
1514 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1515 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1517 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1518 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1524 Value walletlock(const Array& params, bool fHelp)
1526 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1527 throw runtime_error(
1529 "Removes the wallet encryption key from memory, locking the wallet.\n"
1530 "After calling this method, you will need to call walletpassphrase again\n"
1531 "before being able to call any methods which require the wallet to be unlocked.");
1534 if (!pwalletMain->IsCrypted())
1535 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1537 pwalletMain->Lock();
1538 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1540 nWalletUnlockTime = 0;
1547 Value encryptwallet(const Array& params, bool fHelp)
1549 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1550 throw runtime_error(
1551 "encryptwallet <passphrase>\n"
1552 "Encrypts the wallet with <passphrase>.");
1555 if (pwalletMain->IsCrypted())
1556 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1559 // shutting down via RPC while the GUI is running does not work (yet):
1560 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1563 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1564 // Alternately, find a way to make params[0] mlock()'d to begin with.
1565 SecureString strWalletPass;
1566 strWalletPass.reserve(100);
1567 strWalletPass = params[0].get_str().c_str();
1569 if (strWalletPass.length() < 1)
1570 throw runtime_error(
1571 "encryptwallet <passphrase>\n"
1572 "Encrypts the wallet with <passphrase>.");
1574 if (!pwalletMain->EncryptWallet(strWalletPass))
1575 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1577 // BDB seems to have a bad habit of writing old data into
1578 // slack space in .dat files; that is bad if the old data is
1579 // unencrypted private keys. So:
1580 CreateThread(Shutdown, NULL);
1581 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1585 Value validateaddress(const Array& params, bool fHelp)
1587 if (fHelp || params.size() != 1)
1588 throw runtime_error(
1589 "validateaddress <ppcoinaddress>\n"
1590 "Return information about <ppcoinaddress>.");
1592 CBitcoinAddress address(params[0].get_str());
1593 bool isValid = address.IsValid();
1596 ret.push_back(Pair("isvalid", isValid));
1599 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1600 // version of the address:
1601 string currentAddress = address.ToString();
1602 ret.push_back(Pair("address", currentAddress));
1603 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1604 if (pwalletMain->mapAddressBook.count(address))
1605 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1611 Value getwork(const Array& params, bool fHelp)
1613 if (fHelp || params.size() > 1)
1614 throw runtime_error(
1616 "If [data] is not specified, returns formatted hash data to work on:\n"
1617 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1618 " \"data\" : block data\n"
1619 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1620 " \"target\" : little endian hash target\n"
1621 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1624 throw JSONRPCError(-9, "PPCoin is not connected!");
1626 if (IsInitialBlockDownload())
1627 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1629 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1630 static mapNewBlock_t mapNewBlock;
1631 static vector<CBlock*> vNewBlock;
1632 static CReserveKey reservekey(pwalletMain);
1634 if (params.size() == 0)
1637 static unsigned int nTransactionsUpdatedLast;
1638 static CBlockIndex* pindexPrev;
1639 static int64 nStart;
1640 static CBlock* pblock;
1641 if (pindexPrev != pindexBest ||
1642 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1644 if (pindexPrev != pindexBest)
1646 // Deallocate old blocks since they're obsolete now
1647 mapNewBlock.clear();
1648 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1652 nTransactionsUpdatedLast = nTransactionsUpdated;
1653 pindexPrev = pindexBest;
1657 pblock = CreateNewBlock(pwalletMain);
1659 throw JSONRPCError(-7, "Out of memory");
1660 vNewBlock.push_back(pblock);
1664 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1667 // Update nExtraNonce
1668 static unsigned int nExtraNonce = 0;
1669 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1672 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1674 // Prebuild hash buffers
1678 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1680 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1683 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1684 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1685 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1686 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1692 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1693 if (vchData.size() != 128)
1694 throw JSONRPCError(-8, "Invalid parameter");
1695 CBlock* pdata = (CBlock*)&vchData[0];
1698 for (int i = 0; i < 128/4; i++)
1699 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1702 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1704 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1706 pblock->nTime = pdata->nTime;
1707 pblock->nNonce = pdata->nNonce;
1708 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1709 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1711 return CheckWork(pblock, *pwalletMain, reservekey);
1716 Value getmemorypool(const Array& params, bool fHelp)
1718 if (fHelp || params.size() > 1)
1719 throw runtime_error(
1720 "getmemorypool [data]\n"
1721 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1722 " \"version\" : block version\n"
1723 " \"previousblockhash\" : hash of current highest block\n"
1724 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1725 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1726 " \"time\" : timestamp appropriate for next block\n"
1727 " \"bits\" : compressed target of next block\n"
1728 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1730 if (params.size() == 0)
1733 throw JSONRPCError(-9, "PPCoin is not connected!");
1735 if (IsInitialBlockDownload())
1736 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1738 static CReserveKey reservekey(pwalletMain);
1741 static unsigned int nTransactionsUpdatedLast;
1742 static CBlockIndex* pindexPrev;
1743 static int64 nStart;
1744 static CBlock* pblock;
1745 if (pindexPrev != pindexBest ||
1746 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1748 nTransactionsUpdatedLast = nTransactionsUpdated;
1749 pindexPrev = pindexBest;
1755 pblock = CreateNewBlock(pwalletMain);
1757 throw JSONRPCError(-7, "Out of memory");
1761 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1765 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1766 if(tx.IsCoinBase() || tx.IsCoinStake())
1772 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1776 result.push_back(Pair("version", pblock->nVersion));
1777 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1778 result.push_back(Pair("transactions", transactions));
1779 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1780 result.push_back(Pair("time", (int64_t)pblock->nTime));
1786 uBits.nBits = htonl((int32_t)pblock->nBits);
1787 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1794 CDataStream ssBlock(ParseHex(params[0].get_str()));
1798 return ProcessBlock(NULL, &pblock);
1803 // ppcoin: reset auto checkpoint
1804 Value resetcheckpoint(const Array& params, bool fHelp)
1806 if (fHelp || params.size() < 1 || params.size() > 1)
1807 throw runtime_error(
1808 "resetcheckpoint <checkpointheight>\n"
1809 "Reset automatic checkpoint to specified height.\n"
1810 "<checkpointheight> is the height of the new checkpoint block.\n");
1812 int nCheckpoint = params[0].get_int();
1813 if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1814 throw runtime_error(
1815 "invalid checkpoint height.\n"
1817 if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1818 throw runtime_error(
1819 "new checkpoint must be earlier than current auto checkpoint.\n"
1821 if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1822 throw runtime_error(
1823 "internal error - reset checkpoint failed.\n"
1830 // ppcoin: get branch point of alternative branch
1831 Value getbranchpoint(const Array& params, bool fHelp)
1833 if (fHelp || params.size() != 0)
1834 throw runtime_error(
1836 "Returns height of branch point of alternative branch.\n");
1839 if (Checkpoints::nBranchPoint > 0)
1840 result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1842 result.push_back(Pair("branchpoint", "none"));
1843 result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1848 // ppcoin: reserve balance from being staked for network protection
1849 Value reservebalance(const Array& params, bool fHelp)
1851 if (fHelp || params.size() > 2)
1852 throw runtime_error(
1853 "reservebalance [<reserve> [amount]]\n"
1854 "<reserve> is true or false to turn balance reserve on or off.\n"
1855 "<amount> is a real and rounded to cent.\n"
1856 "Set reserve amount not participating in network protection.\n"
1857 "If no parameters provided current setting is printed.\n");
1859 if (params.size() > 0)
1861 bool fReserve = params[0].get_bool();
1864 if (params.size() == 1)
1865 throw runtime_error("must provide amount to reserve balance.\n");
1866 int64 nAmount = AmountFromValue(params[1]);
1867 nAmount = (nAmount / CENT) * CENT; // round to cent
1869 throw runtime_error("amount cannot be negative.\n");
1870 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1874 if (params.size() > 1)
1875 throw runtime_error("cannot specify amount to turn off reserve.\n");
1876 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1881 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1882 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1895 pair<string, rpcfn_type> pCallTable[] =
1897 make_pair("help", &help),
1898 make_pair("stop", &stop),
1899 make_pair("getblockcount", &getblockcount),
1900 make_pair("getblocknumber", &getblocknumber),
1901 make_pair("getconnectioncount", &getconnectioncount),
1902 make_pair("getdifficulty", &getdifficulty),
1903 make_pair("getgenerate", &getgenerate),
1904 make_pair("setgenerate", &setgenerate),
1905 make_pair("gethashespersec", &gethashespersec),
1906 make_pair("getinfo", &getinfo),
1907 make_pair("getnewaddress", &getnewaddress),
1908 make_pair("getaccountaddress", &getaccountaddress),
1909 make_pair("setaccount", &setaccount),
1910 make_pair("getaccount", &getaccount),
1911 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1912 make_pair("sendtoaddress", &sendtoaddress),
1913 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1914 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1915 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1916 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1917 make_pair("backupwallet", &backupwallet),
1918 make_pair("keypoolrefill", &keypoolrefill),
1919 make_pair("walletpassphrase", &walletpassphrase),
1920 make_pair("walletpassphrasechange", &walletpassphrasechange),
1921 make_pair("walletlock", &walletlock),
1922 make_pair("encryptwallet", &encryptwallet),
1923 make_pair("validateaddress", &validateaddress),
1924 make_pair("getbalance", &getbalance),
1925 make_pair("move", &movecmd),
1926 make_pair("sendfrom", &sendfrom),
1927 make_pair("sendmany", &sendmany),
1928 make_pair("gettransaction", &gettransaction),
1929 make_pair("listtransactions", &listtransactions),
1930 make_pair("signmessage", &signmessage),
1931 make_pair("verifymessage", &verifymessage),
1932 make_pair("getwork", &getwork),
1933 make_pair("listaccounts", &listaccounts),
1934 make_pair("settxfee", &settxfee),
1935 make_pair("getmemorypool", &getmemorypool),
1936 make_pair("listsinceblock", &listsinceblock),
1937 make_pair("resetcheckpoint", &resetcheckpoint),
1938 make_pair("getbranchpoint", &getbranchpoint),
1939 make_pair("reservebalance", &reservebalance),
1941 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1943 string pAllowInSafeMode[] =
1948 "getblocknumber", // deprecated
1949 "getconnectioncount",
1956 "getaccountaddress",
1958 "getaddressesbyaccount",
1969 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1977 // This ain't Apache. We're just using HTTP header for the length field
1978 // and to be compatible with other JSON-RPC implementations.
1981 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1984 s << "POST / HTTP/1.1\r\n"
1985 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1986 << "Host: 127.0.0.1\r\n"
1987 << "Content-Type: application/json\r\n"
1988 << "Content-Length: " << strMsg.size() << "\r\n"
1989 << "Connection: close\r\n"
1990 << "Accept: application/json\r\n";
1991 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1992 s << item.first << ": " << item.second << "\r\n";
1993 s << "\r\n" << strMsg;
1998 string rfc1123Time()
2003 struct tm* now_gmt = gmtime(&now);
2004 string locale(setlocale(LC_TIME, NULL));
2005 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2006 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2007 setlocale(LC_TIME, locale.c_str());
2008 return string(buffer);
2011 static string HTTPReply(int nStatus, const string& strMsg)
2014 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2016 "Server: ppcoin-json-rpc/%s\r\n"
2017 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2018 "Content-Type: text/html\r\n"
2019 "Content-Length: 296\r\n"
2021 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2022 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2025 "<TITLE>Error</TITLE>\r\n"
2026 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2028 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2029 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2030 const char *cStatus;
2031 if (nStatus == 200) cStatus = "OK";
2032 else if (nStatus == 400) cStatus = "Bad Request";
2033 else if (nStatus == 403) cStatus = "Forbidden";
2034 else if (nStatus == 404) cStatus = "Not Found";
2035 else if (nStatus == 500) cStatus = "Internal Server Error";
2038 "HTTP/1.1 %d %s\r\n"
2040 "Connection: close\r\n"
2041 "Content-Length: %d\r\n"
2042 "Content-Type: application/json\r\n"
2043 "Server: ppcoin-json-rpc/%s\r\n"
2048 rfc1123Time().c_str(),
2050 FormatFullVersion().c_str(),
2054 int ReadHTTPStatus(std::basic_istream<char>& stream)
2057 getline(stream, str);
2058 vector<string> vWords;
2059 boost::split(vWords, str, boost::is_any_of(" "));
2060 if (vWords.size() < 2)
2062 return atoi(vWords[1].c_str());
2065 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2071 std::getline(stream, str);
2072 if (str.empty() || str == "\r")
2074 string::size_type nColon = str.find(":");
2075 if (nColon != string::npos)
2077 string strHeader = str.substr(0, nColon);
2078 boost::trim(strHeader);
2079 boost::to_lower(strHeader);
2080 string strValue = str.substr(nColon+1);
2081 boost::trim(strValue);
2082 mapHeadersRet[strHeader] = strValue;
2083 if (strHeader == "content-length")
2084 nLen = atoi(strValue.c_str());
2090 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2092 mapHeadersRet.clear();
2096 int nStatus = ReadHTTPStatus(stream);
2099 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2100 if (nLen < 0 || nLen > MAX_SIZE)
2106 vector<char> vch(nLen);
2107 stream.read(&vch[0], nLen);
2108 strMessageRet = string(vch.begin(), vch.end());
2114 bool HTTPAuthorized(map<string, string>& mapHeaders)
2116 string strAuth = mapHeaders["authorization"];
2117 if (strAuth.substr(0,6) != "Basic ")
2119 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2120 string strUserPass = DecodeBase64(strUserPass64);
2121 return strUserPass == strRPCUserColonPass;
2125 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2126 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2127 // unspecified (HTTP errors and contents of 'error').
2129 // 1.0 spec: http://json-rpc.org/wiki/specification
2130 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2131 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2134 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2137 request.push_back(Pair("method", strMethod));
2138 request.push_back(Pair("params", params));
2139 request.push_back(Pair("id", id));
2140 return write_string(Value(request), false) + "\n";
2143 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2146 if (error.type() != null_type)
2147 reply.push_back(Pair("result", Value::null));
2149 reply.push_back(Pair("result", result));
2150 reply.push_back(Pair("error", error));
2151 reply.push_back(Pair("id", id));
2152 return write_string(Value(reply), false) + "\n";
2155 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2157 // Send error reply from json-rpc error object
2159 int code = find_value(objError, "code").get_int();
2160 if (code == -32600) nStatus = 400;
2161 else if (code == -32601) nStatus = 404;
2162 string strReply = JSONRPCReply(Value::null, objError, id);
2163 stream << HTTPReply(nStatus, strReply) << std::flush;
2166 bool ClientAllowed(const string& strAddress)
2168 if (strAddress == asio::ip::address_v4::loopback().to_string())
2170 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2171 BOOST_FOREACH(string strAllow, vAllow)
2172 if (WildcardMatch(strAddress, strAllow))
2179 // IOStream device that speaks SSL but can also speak non-SSL
2181 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2183 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2185 fUseSSL = fUseSSLIn;
2186 fNeedHandshake = fUseSSLIn;
2189 void handshake(ssl::stream_base::handshake_type role)
2191 if (!fNeedHandshake) return;
2192 fNeedHandshake = false;
2193 stream.handshake(role);
2195 std::streamsize read(char* s, std::streamsize n)
2197 handshake(ssl::stream_base::server); // HTTPS servers read first
2198 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2199 return stream.next_layer().read_some(asio::buffer(s, n));
2201 std::streamsize write(const char* s, std::streamsize n)
2203 handshake(ssl::stream_base::client); // HTTPS clients write first
2204 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2205 return asio::write(stream.next_layer(), asio::buffer(s, n));
2207 bool connect(const std::string& server, const std::string& port)
2209 ip::tcp::resolver resolver(stream.get_io_service());
2210 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2211 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2212 ip::tcp::resolver::iterator end;
2213 boost::system::error_code error = asio::error::host_not_found;
2214 while (error && endpoint_iterator != end)
2216 stream.lowest_layer().close();
2217 stream.lowest_layer().connect(*endpoint_iterator++, error);
2225 bool fNeedHandshake;
2231 void ThreadRPCServer(void* parg)
2233 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2236 vnThreadsRunning[4]++;
2237 ThreadRPCServer2(parg);
2238 vnThreadsRunning[4]--;
2240 catch (std::exception& e) {
2241 vnThreadsRunning[4]--;
2242 PrintException(&e, "ThreadRPCServer()");
2244 vnThreadsRunning[4]--;
2245 PrintException(NULL, "ThreadRPCServer()");
2247 printf("ThreadRPCServer exiting\n");
2250 void ThreadRPCServer2(void* parg)
2252 printf("ThreadRPCServer started\n");
2254 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2255 if (strRPCUserColonPass == ":")
2257 string strWhatAmI = "To use ppcoind";
2258 if (mapArgs.count("-server"))
2259 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2260 else if (mapArgs.count("-daemon"))
2261 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2263 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2264 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2266 GetConfigFile().c_str());
2268 CreateThread(Shutdown, NULL);
2273 bool fUseSSL = GetBoolArg("-rpcssl");
2274 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2276 asio::io_service io_service;
2277 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2278 ip::tcp::acceptor acceptor(io_service, endpoint);
2280 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2283 ssl::context context(io_service, ssl::context::sslv23);
2286 context.set_options(ssl::context::no_sslv2);
2287 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2288 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2289 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2290 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2291 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2292 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2293 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2294 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2296 string ciphers = GetArg("-rpcsslciphers",
2297 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2298 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2302 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2307 // Accept connection
2309 SSLStream sslStream(io_service, context);
2310 SSLIOStreamDevice d(sslStream, fUseSSL);
2311 iostreams::stream<SSLIOStreamDevice> stream(d);
2313 ip::tcp::iostream stream;
2316 ip::tcp::endpoint peer;
2317 vnThreadsRunning[4]--;
2319 acceptor.accept(sslStream.lowest_layer(), peer);
2321 acceptor.accept(*stream.rdbuf(), peer);
2323 vnThreadsRunning[4]++;
2327 // Restrict callers by IP
2328 if (!ClientAllowed(peer.address().to_string()))
2330 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2332 stream << HTTPReply(403, "") << std::flush;
2336 map<string, string> mapHeaders;
2339 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2340 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2343 printf("ThreadRPCServer ReadHTTP timeout\n");
2347 // Check authorization
2348 if (mapHeaders.count("authorization") == 0)
2350 stream << HTTPReply(401, "") << std::flush;
2353 if (!HTTPAuthorized(mapHeaders))
2355 // Deter brute-forcing short passwords
2356 if (mapArgs["-rpcpassword"].size() < 15)
2359 stream << HTTPReply(401, "") << std::flush;
2360 printf("ThreadRPCServer incorrect password attempt\n");
2364 Value id = Value::null;
2369 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2370 throw JSONRPCError(-32700, "Parse error");
2371 const Object& request = valRequest.get_obj();
2373 // Parse id now so errors from here on will have the id
2374 id = find_value(request, "id");
2377 Value valMethod = find_value(request, "method");
2378 if (valMethod.type() == null_type)
2379 throw JSONRPCError(-32600, "Missing method");
2380 if (valMethod.type() != str_type)
2381 throw JSONRPCError(-32600, "Method must be a string");
2382 string strMethod = valMethod.get_str();
2383 if (strMethod != "getwork" && strMethod != "getmemorypool")
2384 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2387 Value valParams = find_value(request, "params");
2389 if (valParams.type() == array_type)
2390 params = valParams.get_array();
2391 else if (valParams.type() == null_type)
2394 throw JSONRPCError(-32600, "Params must be an array");
2397 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2398 if (mi == mapCallTable.end())
2399 throw JSONRPCError(-32601, "Method not found");
2401 // Observe safe mode
2402 string strWarning = GetWarnings("rpc");
2403 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2404 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2410 CRITICAL_BLOCK(cs_main)
2411 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2412 result = (*(*mi).second)(params, false);
2415 string strReply = JSONRPCReply(result, Value::null, id);
2416 stream << HTTPReply(200, strReply) << std::flush;
2418 catch (std::exception& e)
2420 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2423 catch (Object& objError)
2425 ErrorReply(stream, objError, id);
2427 catch (std::exception& e)
2429 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2437 Object CallRPC(const string& strMethod, const Array& params)
2439 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2440 throw runtime_error(strprintf(
2441 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2442 "If the file does not exist, create it with owner-readable-only file permissions."),
2443 GetConfigFile().c_str()));
2445 // Connect to localhost
2446 bool fUseSSL = GetBoolArg("-rpcssl");
2448 asio::io_service io_service;
2449 ssl::context context(io_service, ssl::context::sslv23);
2450 context.set_options(ssl::context::no_sslv2);
2451 SSLStream sslStream(io_service, context);
2452 SSLIOStreamDevice d(sslStream, fUseSSL);
2453 iostreams::stream<SSLIOStreamDevice> stream(d);
2454 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2455 throw runtime_error("couldn't connect to server");
2458 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2460 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2462 throw runtime_error("couldn't connect to server");
2466 // HTTP basic authentication
2467 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2468 map<string, string> mapRequestHeaders;
2469 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2472 string strRequest = JSONRPCRequest(strMethod, params, 1);
2473 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2474 stream << strPost << std::flush;
2477 map<string, string> mapHeaders;
2479 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2481 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2482 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2483 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2484 else if (strReply.empty())
2485 throw runtime_error("no response from server");
2489 if (!read_string(strReply, valReply))
2490 throw runtime_error("couldn't parse reply from server");
2491 const Object& reply = valReply.get_obj();
2493 throw runtime_error("expected reply to have result, error and id properties");
2501 template<typename T>
2502 void ConvertTo(Value& value)
2504 if (value.type() == str_type)
2506 // reinterpret string as unquoted json value
2508 if (!read_string(value.get_str(), value2))
2509 throw runtime_error("type mismatch");
2510 value = value2.get_value<T>();
2514 value = value.get_value<T>();
2518 int CommandLineRPC(int argc, char *argv[])
2525 while (argc > 1 && IsSwitchChar(argv[1][0]))
2533 throw runtime_error("too few parameters");
2534 string strMethod = argv[1];
2536 // Parameters default to strings
2538 for (int i = 2; i < argc; i++)
2539 params.push_back(argv[i]);
2540 int n = params.size();
2543 // Special case non-string parameter types
2545 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2546 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2547 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2548 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2549 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2550 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2551 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2552 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2553 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2554 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2555 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2556 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2557 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2558 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2559 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2560 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2561 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2562 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2563 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2564 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2565 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2566 if (strMethod == "sendmany" && n > 1)
2568 string s = params[1].get_str();
2570 if (!read_string(s, v) || v.type() != obj_type)
2571 throw runtime_error("type mismatch");
2572 params[1] = v.get_obj();
2574 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2575 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2576 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2577 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2580 Object reply = CallRPC(strMethod, params);
2583 const Value& result = find_value(reply, "result");
2584 const Value& error = find_value(reply, "error");
2586 if (error.type() != null_type)
2589 strPrint = "error: " + write_string(error, false);
2590 int code = find_value(error.get_obj(), "code").get_int();
2596 if (result.type() == null_type)
2598 else if (result.type() == str_type)
2599 strPrint = result.get_str();
2601 strPrint = write_string(result, true);
2604 catch (std::exception& e)
2606 strPrint = string("error: ") + e.what();
2611 PrintException(NULL, "CommandLineRPC()");
2616 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2625 int main(int argc, char *argv[])
2628 // Turn off microsoft heap dump noise
2629 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2630 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2632 setbuf(stdin, NULL);
2633 setbuf(stdout, NULL);
2634 setbuf(stderr, NULL);
2638 if (argc >= 2 && string(argv[1]) == "-server")
2640 printf("server ready\n");
2641 ThreadRPCServer(NULL);
2645 return CommandLineRPC(argc, argv);
2648 catch (std::exception& e) {
2649 PrintException(&e, "main()");
2651 PrintException(NULL, "main()");