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.");
914 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
915 if (totalAmount > nBalance)
916 throw JSONRPCError(-6, "Account has insufficient funds");
919 CReserveKey keyChange(pwalletMain);
920 int64 nFeeRequired = 0;
921 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
924 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
925 throw JSONRPCError(-6, "Insufficient funds");
926 throw JSONRPCError(-4, "Transaction creation failed");
928 if (!pwalletMain->CommitTransaction(wtx, keyChange))
929 throw JSONRPCError(-4, "Transaction commit failed");
931 return wtx.GetHash().GetHex();
946 Value ListReceived(const Array& params, bool fByAccounts)
948 // Minimum confirmations
950 if (params.size() > 0)
951 nMinDepth = params[0].get_int();
953 // Whether to include empty accounts
954 bool fIncludeEmpty = false;
955 if (params.size() > 1)
956 fIncludeEmpty = params[1].get_bool();
959 map<CBitcoinAddress, tallyitem> mapTally;
960 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
962 const CWalletTx& wtx = (*it).second;
963 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
966 int nDepth = wtx.GetDepthInMainChain();
967 if (nDepth < nMinDepth)
970 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
972 CBitcoinAddress address;
973 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
976 tallyitem& item = mapTally[address];
977 item.nAmount += txout.nValue;
978 item.nConf = min(item.nConf, nDepth);
984 map<string, tallyitem> mapAccountTally;
985 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
987 const CBitcoinAddress& address = item.first;
988 const string& strAccount = item.second;
989 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
990 if (it == mapTally.end() && !fIncludeEmpty)
995 if (it != mapTally.end())
997 nAmount = (*it).second.nAmount;
998 nConf = (*it).second.nConf;
1003 tallyitem& item = mapAccountTally[strAccount];
1004 item.nAmount += nAmount;
1005 item.nConf = min(item.nConf, nConf);
1010 obj.push_back(Pair("address", address.ToString()));
1011 obj.push_back(Pair("account", strAccount));
1012 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1013 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1020 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1022 int64 nAmount = (*it).second.nAmount;
1023 int nConf = (*it).second.nConf;
1025 obj.push_back(Pair("account", (*it).first));
1026 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1027 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1035 Value listreceivedbyaddress(const Array& params, bool fHelp)
1037 if (fHelp || params.size() > 2)
1038 throw runtime_error(
1039 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1040 "[minconf] is the minimum number of confirmations before payments are included.\n"
1041 "[includeempty] whether to include addresses that haven't received any payments.\n"
1042 "Returns an array of objects containing:\n"
1043 " \"address\" : receiving address\n"
1044 " \"account\" : the account of the receiving address\n"
1045 " \"amount\" : total amount received by the address\n"
1046 " \"confirmations\" : number of confirmations of the most recent transaction included");
1048 return ListReceived(params, false);
1051 Value listreceivedbyaccount(const Array& params, bool fHelp)
1053 if (fHelp || params.size() > 2)
1054 throw runtime_error(
1055 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1056 "[minconf] is the minimum number of confirmations before payments are included.\n"
1057 "[includeempty] whether to include accounts that haven't received any payments.\n"
1058 "Returns an array of objects containing:\n"
1059 " \"account\" : the account of the receiving addresses\n"
1060 " \"amount\" : total amount received by addresses with this account\n"
1061 " \"confirmations\" : number of confirmations of the most recent transaction included");
1063 return ListReceived(params, true);
1066 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1068 int64 nGeneratedImmature, nGeneratedMature, nFee;
1069 string strSentAccount;
1070 list<pair<CBitcoinAddress, int64> > listReceived;
1071 list<pair<CBitcoinAddress, int64> > listSent;
1072 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1074 bool fAllAccounts = (strAccount == string("*"));
1076 // Generated blocks assigned to account ""
1077 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1080 entry.push_back(Pair("account", string("")));
1081 if (nGeneratedImmature)
1083 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1084 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1088 entry.push_back(Pair("category", "generate"));
1089 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1092 WalletTxToJSON(wtx, entry);
1093 ret.push_back(entry);
1097 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1099 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1102 entry.push_back(Pair("account", strSentAccount));
1103 entry.push_back(Pair("address", s.first.ToString()));
1104 entry.push_back(Pair("category", "send"));
1105 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1106 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1108 WalletTxToJSON(wtx, entry);
1109 ret.push_back(entry);
1114 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1115 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1118 if (pwalletMain->mapAddressBook.count(r.first))
1119 account = pwalletMain->mapAddressBook[r.first];
1120 if (fAllAccounts || (account == strAccount))
1123 entry.push_back(Pair("account", account));
1124 entry.push_back(Pair("address", r.first.ToString()));
1125 entry.push_back(Pair("category", "receive"));
1126 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1128 WalletTxToJSON(wtx, entry);
1129 ret.push_back(entry);
1134 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1136 bool fAllAccounts = (strAccount == string("*"));
1138 if (fAllAccounts || acentry.strAccount == strAccount)
1141 entry.push_back(Pair("account", acentry.strAccount));
1142 entry.push_back(Pair("category", "move"));
1143 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1144 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1145 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1146 entry.push_back(Pair("comment", acentry.strComment));
1147 ret.push_back(entry);
1151 Value listtransactions(const Array& params, bool fHelp)
1153 if (fHelp || params.size() > 3)
1154 throw runtime_error(
1155 "listtransactions [account] [count=10] [from=0]\n"
1156 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1158 string strAccount = "*";
1159 if (params.size() > 0)
1160 strAccount = params[0].get_str();
1162 if (params.size() > 1)
1163 nCount = params[1].get_int();
1165 if (params.size() > 2)
1166 nFrom = params[2].get_int();
1169 CWalletDB walletdb(pwalletMain->strWalletFile);
1171 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1172 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1173 typedef multimap<int64, TxPair > TxItems;
1176 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1178 CWalletTx* wtx = &((*it).second);
1179 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1181 list<CAccountingEntry> acentries;
1182 walletdb.ListAccountCreditDebit(strAccount, acentries);
1183 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1185 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1188 // Now: iterate backwards until we have nCount items to return:
1189 TxItems::reverse_iterator it = txByTime.rbegin();
1190 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1191 for (; it != txByTime.rend(); ++it)
1193 CWalletTx *const pwtx = (*it).second.first;
1195 ListTransactions(*pwtx, strAccount, 0, true, ret);
1196 CAccountingEntry *const pacentry = (*it).second.second;
1198 AcentryToJSON(*pacentry, strAccount, ret);
1200 if (ret.size() >= nCount) break;
1202 // ret is now newest to oldest
1204 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1205 if (ret.size() > nCount)
1207 Array::iterator last = ret.begin();
1208 std::advance(last, nCount);
1209 ret.erase(last, ret.end());
1211 std::reverse(ret.begin(), ret.end()); // oldest to newest
1216 Value listaccounts(const Array& params, bool fHelp)
1218 if (fHelp || params.size() > 1)
1219 throw runtime_error(
1220 "listaccounts [minconf=1]\n"
1221 "Returns Object that has account names as keys, account balances as values.");
1224 if (params.size() > 0)
1225 nMinDepth = params[0].get_int();
1227 map<string, int64> mapAccountBalances;
1228 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1229 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1230 mapAccountBalances[entry.second] = 0;
1233 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1235 const CWalletTx& wtx = (*it).second;
1236 int64 nGeneratedImmature, nGeneratedMature, nFee;
1237 string strSentAccount;
1238 list<pair<CBitcoinAddress, int64> > listReceived;
1239 list<pair<CBitcoinAddress, int64> > listSent;
1240 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1241 mapAccountBalances[strSentAccount] -= nFee;
1242 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1243 mapAccountBalances[strSentAccount] -= s.second;
1244 if (wtx.GetDepthInMainChain() >= nMinDepth)
1246 mapAccountBalances[""] += nGeneratedMature;
1247 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1248 if (pwalletMain->mapAddressBook.count(r.first))
1249 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1251 mapAccountBalances[""] += r.second;
1255 list<CAccountingEntry> acentries;
1256 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1257 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1258 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1261 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1262 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1267 Value listsinceblock(const Array& params, bool fHelp)
1270 throw runtime_error(
1271 "listsinceblock [blockid] [target-confirmations]\n"
1272 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1274 CBlockIndex *pindex = NULL;
1275 int target_confirms = 1;
1277 if (params.size() > 0)
1279 uint256 blockId = 0;
1281 blockId.SetHex(params[0].get_str());
1282 pindex = CBlockLocator(blockId).GetBlockIndex();
1285 if (params.size() > 1)
1287 target_confirms = params[1].get_int();
1289 if (target_confirms < 1)
1290 throw JSONRPCError(-8, "Invalid parameter");
1293 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1297 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1299 CWalletTx tx = (*it).second;
1301 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1302 ListTransactions(tx, "*", 0, true, transactions);
1307 if (target_confirms == 1)
1310 lastblock = hashBestChain;
1314 int target_height = pindexBest->nHeight + 1 - target_confirms;
1317 for (block = pindexBest;
1318 block && block->nHeight > target_height;
1319 block = block->pprev);
1321 lastblock = block ? block->GetBlockHash() : 0;
1325 ret.push_back(Pair("transactions", transactions));
1326 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1331 Value gettransaction(const Array& params, bool fHelp)
1333 if (fHelp || params.size() != 1)
1334 throw runtime_error(
1335 "gettransaction <txid>\n"
1336 "Get detailed information about <txid>");
1339 hash.SetHex(params[0].get_str());
1343 if (!pwalletMain->mapWallet.count(hash))
1344 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1345 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1347 int64 nCredit = wtx.GetCredit();
1348 int64 nDebit = wtx.GetDebit();
1349 int64 nNet = nCredit - nDebit;
1350 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1352 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1354 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1356 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1359 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1360 entry.push_back(Pair("details", details));
1366 Value backupwallet(const Array& params, bool fHelp)
1368 if (fHelp || params.size() != 1)
1369 throw runtime_error(
1370 "backupwallet <destination>\n"
1371 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1373 string strDest = params[0].get_str();
1374 BackupWallet(*pwalletMain, strDest);
1380 Value keypoolrefill(const Array& params, bool fHelp)
1382 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1383 throw runtime_error(
1385 "Fills the keypool, requires wallet passphrase to be set.");
1386 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1387 throw runtime_error(
1389 "Fills the keypool.");
1391 if (pwalletMain->IsLocked())
1392 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1394 pwalletMain->TopUpKeyPool();
1396 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1397 throw JSONRPCError(-4, "Error refreshing keypool.");
1403 void ThreadTopUpKeyPool(void* parg)
1405 pwalletMain->TopUpKeyPool();
1408 void ThreadCleanWalletPassphrase(void* parg)
1410 int64 nMyWakeTime = GetTime() + *((int*)parg);
1412 if (nWalletUnlockTime == 0)
1414 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1416 nWalletUnlockTime = nMyWakeTime;
1419 while (GetTime() < nWalletUnlockTime)
1420 Sleep(GetTime() - nWalletUnlockTime);
1422 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1424 nWalletUnlockTime = 0;
1429 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1431 if (nWalletUnlockTime < nMyWakeTime)
1432 nWalletUnlockTime = nMyWakeTime;
1438 pwalletMain->Lock();
1443 Value walletpassphrase(const Array& params, bool fHelp)
1445 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1446 throw runtime_error(
1447 "walletpassphrase <passphrase> <timeout>\n"
1448 "Stores the wallet decryption key in memory for <timeout> seconds.");
1451 if (!pwalletMain->IsCrypted())
1452 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1454 if (!pwalletMain->IsLocked())
1455 throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
1457 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1458 SecureString strWalletPass;
1459 strWalletPass.reserve(100);
1460 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1461 // Alternately, find a way to make params[0] mlock()'d to begin with.
1462 strWalletPass = params[0].get_str().c_str();
1464 if (strWalletPass.length() > 0)
1466 if (!pwalletMain->Unlock(strWalletPass))
1467 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1470 throw runtime_error(
1471 "walletpassphrase <passphrase> <timeout>\n"
1472 "Stores the wallet decryption key in memory for <timeout> seconds.");
1474 CreateThread(ThreadTopUpKeyPool, NULL);
1475 int* pnSleepTime = new int(params[1].get_int());
1476 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1482 Value walletpassphrasechange(const Array& params, bool fHelp)
1484 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1485 throw runtime_error(
1486 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1487 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1490 if (!pwalletMain->IsCrypted())
1491 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1493 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1494 // Alternately, find a way to make params[0] mlock()'d to begin with.
1495 SecureString strOldWalletPass;
1496 strOldWalletPass.reserve(100);
1497 strOldWalletPass = params[0].get_str().c_str();
1499 SecureString strNewWalletPass;
1500 strNewWalletPass.reserve(100);
1501 strNewWalletPass = params[1].get_str().c_str();
1503 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1504 throw runtime_error(
1505 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1506 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1508 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1509 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1515 Value walletlock(const Array& params, bool fHelp)
1517 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1518 throw runtime_error(
1520 "Removes the wallet encryption key from memory, locking the wallet.\n"
1521 "After calling this method, you will need to call walletpassphrase again\n"
1522 "before being able to call any methods which require the wallet to be unlocked.");
1525 if (!pwalletMain->IsCrypted())
1526 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1528 pwalletMain->Lock();
1529 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1531 nWalletUnlockTime = 0;
1538 Value encryptwallet(const Array& params, bool fHelp)
1540 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1541 throw runtime_error(
1542 "encryptwallet <passphrase>\n"
1543 "Encrypts the wallet with <passphrase>.");
1546 if (pwalletMain->IsCrypted())
1547 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1550 // shutting down via RPC while the GUI is running does not work (yet):
1551 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1554 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1555 // Alternately, find a way to make params[0] mlock()'d to begin with.
1556 SecureString strWalletPass;
1557 strWalletPass.reserve(100);
1558 strWalletPass = params[0].get_str().c_str();
1560 if (strWalletPass.length() < 1)
1561 throw runtime_error(
1562 "encryptwallet <passphrase>\n"
1563 "Encrypts the wallet with <passphrase>.");
1565 if (!pwalletMain->EncryptWallet(strWalletPass))
1566 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1568 // BDB seems to have a bad habit of writing old data into
1569 // slack space in .dat files; that is bad if the old data is
1570 // unencrypted private keys. So:
1571 CreateThread(Shutdown, NULL);
1572 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1576 Value validateaddress(const Array& params, bool fHelp)
1578 if (fHelp || params.size() != 1)
1579 throw runtime_error(
1580 "validateaddress <ppcoinaddress>\n"
1581 "Return information about <ppcoinaddress>.");
1583 CBitcoinAddress address(params[0].get_str());
1584 bool isValid = address.IsValid();
1587 ret.push_back(Pair("isvalid", isValid));
1590 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1591 // version of the address:
1592 string currentAddress = address.ToString();
1593 ret.push_back(Pair("address", currentAddress));
1594 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1595 if (pwalletMain->mapAddressBook.count(address))
1596 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1602 Value getwork(const Array& params, bool fHelp)
1604 if (fHelp || params.size() > 1)
1605 throw runtime_error(
1607 "If [data] is not specified, returns formatted hash data to work on:\n"
1608 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1609 " \"data\" : block data\n"
1610 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1611 " \"target\" : little endian hash target\n"
1612 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1615 throw JSONRPCError(-9, "PPCoin is not connected!");
1617 if (IsInitialBlockDownload())
1618 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1620 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1621 static mapNewBlock_t mapNewBlock;
1622 static vector<CBlock*> vNewBlock;
1623 static CReserveKey reservekey(pwalletMain);
1625 if (params.size() == 0)
1628 static unsigned int nTransactionsUpdatedLast;
1629 static CBlockIndex* pindexPrev;
1630 static int64 nStart;
1631 static CBlock* pblock;
1632 if (pindexPrev != pindexBest ||
1633 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1635 if (pindexPrev != pindexBest)
1637 // Deallocate old blocks since they're obsolete now
1638 mapNewBlock.clear();
1639 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1643 nTransactionsUpdatedLast = nTransactionsUpdated;
1644 pindexPrev = pindexBest;
1648 pblock = CreateNewBlock(pwalletMain);
1650 throw JSONRPCError(-7, "Out of memory");
1651 vNewBlock.push_back(pblock);
1655 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1658 // Update nExtraNonce
1659 static unsigned int nExtraNonce = 0;
1660 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1663 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1665 // Prebuild hash buffers
1669 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1671 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1674 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1675 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1676 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1677 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1683 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1684 if (vchData.size() != 128)
1685 throw JSONRPCError(-8, "Invalid parameter");
1686 CBlock* pdata = (CBlock*)&vchData[0];
1689 for (int i = 0; i < 128/4; i++)
1690 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1693 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1695 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1697 pblock->nTime = pdata->nTime;
1698 pblock->nNonce = pdata->nNonce;
1699 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1700 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1702 return CheckWork(pblock, *pwalletMain, reservekey);
1707 Value getmemorypool(const Array& params, bool fHelp)
1709 if (fHelp || params.size() > 1)
1710 throw runtime_error(
1711 "getmemorypool [data]\n"
1712 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1713 " \"version\" : block version\n"
1714 " \"previousblockhash\" : hash of current highest block\n"
1715 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1716 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1717 " \"time\" : timestamp appropriate for next block\n"
1718 " \"bits\" : compressed target of next block\n"
1719 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1721 if (params.size() == 0)
1724 throw JSONRPCError(-9, "PPCoin is not connected!");
1726 if (IsInitialBlockDownload())
1727 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1729 static CReserveKey reservekey(pwalletMain);
1732 static unsigned int nTransactionsUpdatedLast;
1733 static CBlockIndex* pindexPrev;
1734 static int64 nStart;
1735 static CBlock* pblock;
1736 if (pindexPrev != pindexBest ||
1737 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1739 nTransactionsUpdatedLast = nTransactionsUpdated;
1740 pindexPrev = pindexBest;
1746 pblock = CreateNewBlock(pwalletMain);
1748 throw JSONRPCError(-7, "Out of memory");
1752 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1756 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1757 if(tx.IsCoinBase() || tx.IsCoinStake())
1763 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1767 result.push_back(Pair("version", pblock->nVersion));
1768 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1769 result.push_back(Pair("transactions", transactions));
1770 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1771 result.push_back(Pair("time", (int64_t)pblock->nTime));
1777 uBits.nBits = htonl((int32_t)pblock->nBits);
1778 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1785 CDataStream ssBlock(ParseHex(params[0].get_str()));
1789 return ProcessBlock(NULL, &pblock);
1794 // ppcoin: reset auto checkpoint
1795 Value resetcheckpoint(const Array& params, bool fHelp)
1797 if (fHelp || params.size() < 1 || params.size() > 1)
1798 throw runtime_error(
1799 "resetcheckpoint <checkpointheight>\n"
1800 "Reset automatic checkpoint to specified height.\n"
1801 "<checkpointheight> is the height of the new checkpoint block.\n");
1803 int nCheckpoint = params[0].get_int();
1804 if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1805 throw runtime_error(
1806 "invalid checkpoint height.\n"
1808 if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1809 throw runtime_error(
1810 "new checkpoint must be earlier than current auto checkpoint.\n"
1812 if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1813 throw runtime_error(
1814 "internal error - reset checkpoint failed.\n"
1821 // ppcoin: get branch point of alternative branch
1822 Value getbranchpoint(const Array& params, bool fHelp)
1824 if (fHelp || params.size() != 0)
1825 throw runtime_error(
1827 "Returns height of branch point of alternative branch.\n");
1830 if (Checkpoints::nBranchPoint > 0)
1831 result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1833 result.push_back(Pair("branchpoint", "none"));
1834 result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1849 pair<string, rpcfn_type> pCallTable[] =
1851 make_pair("help", &help),
1852 make_pair("stop", &stop),
1853 make_pair("getblockcount", &getblockcount),
1854 make_pair("getblocknumber", &getblocknumber),
1855 make_pair("getconnectioncount", &getconnectioncount),
1856 make_pair("getdifficulty", &getdifficulty),
1857 make_pair("getgenerate", &getgenerate),
1858 make_pair("setgenerate", &setgenerate),
1859 make_pair("gethashespersec", &gethashespersec),
1860 make_pair("getinfo", &getinfo),
1861 make_pair("getnewaddress", &getnewaddress),
1862 make_pair("getaccountaddress", &getaccountaddress),
1863 make_pair("setaccount", &setaccount),
1864 make_pair("getaccount", &getaccount),
1865 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
1866 make_pair("sendtoaddress", &sendtoaddress),
1867 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
1868 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
1869 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
1870 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
1871 make_pair("backupwallet", &backupwallet),
1872 make_pair("keypoolrefill", &keypoolrefill),
1873 make_pair("walletpassphrase", &walletpassphrase),
1874 make_pair("walletpassphrasechange", &walletpassphrasechange),
1875 make_pair("walletlock", &walletlock),
1876 make_pair("encryptwallet", &encryptwallet),
1877 make_pair("validateaddress", &validateaddress),
1878 make_pair("getbalance", &getbalance),
1879 make_pair("move", &movecmd),
1880 make_pair("sendfrom", &sendfrom),
1881 make_pair("sendmany", &sendmany),
1882 make_pair("gettransaction", &gettransaction),
1883 make_pair("listtransactions", &listtransactions),
1884 make_pair("signmessage", &signmessage),
1885 make_pair("verifymessage", &verifymessage),
1886 make_pair("getwork", &getwork),
1887 make_pair("listaccounts", &listaccounts),
1888 make_pair("settxfee", &settxfee),
1889 make_pair("getmemorypool", &getmemorypool),
1890 make_pair("listsinceblock", &listsinceblock),
1891 make_pair("resetcheckpoint", &resetcheckpoint),
1892 make_pair("getbranchpoint", &getbranchpoint),
1894 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
1896 string pAllowInSafeMode[] =
1901 "getblocknumber", // deprecated
1902 "getconnectioncount",
1909 "getaccountaddress",
1911 "getaddressesbyaccount",
1922 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
1930 // This ain't Apache. We're just using HTTP header for the length field
1931 // and to be compatible with other JSON-RPC implementations.
1934 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
1937 s << "POST / HTTP/1.1\r\n"
1938 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
1939 << "Host: 127.0.0.1\r\n"
1940 << "Content-Type: application/json\r\n"
1941 << "Content-Length: " << strMsg.size() << "\r\n"
1942 << "Connection: close\r\n"
1943 << "Accept: application/json\r\n";
1944 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
1945 s << item.first << ": " << item.second << "\r\n";
1946 s << "\r\n" << strMsg;
1951 string rfc1123Time()
1956 struct tm* now_gmt = gmtime(&now);
1957 string locale(setlocale(LC_TIME, NULL));
1958 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
1959 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
1960 setlocale(LC_TIME, locale.c_str());
1961 return string(buffer);
1964 static string HTTPReply(int nStatus, const string& strMsg)
1967 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
1969 "Server: ppcoin-json-rpc/%s\r\n"
1970 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
1971 "Content-Type: text/html\r\n"
1972 "Content-Length: 296\r\n"
1974 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
1975 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
1978 "<TITLE>Error</TITLE>\r\n"
1979 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
1981 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
1982 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
1983 const char *cStatus;
1984 if (nStatus == 200) cStatus = "OK";
1985 else if (nStatus == 400) cStatus = "Bad Request";
1986 else if (nStatus == 403) cStatus = "Forbidden";
1987 else if (nStatus == 404) cStatus = "Not Found";
1988 else if (nStatus == 500) cStatus = "Internal Server Error";
1991 "HTTP/1.1 %d %s\r\n"
1993 "Connection: close\r\n"
1994 "Content-Length: %d\r\n"
1995 "Content-Type: application/json\r\n"
1996 "Server: ppcoin-json-rpc/%s\r\n"
2001 rfc1123Time().c_str(),
2003 FormatFullVersion().c_str(),
2007 int ReadHTTPStatus(std::basic_istream<char>& stream)
2010 getline(stream, str);
2011 vector<string> vWords;
2012 boost::split(vWords, str, boost::is_any_of(" "));
2013 if (vWords.size() < 2)
2015 return atoi(vWords[1].c_str());
2018 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2024 std::getline(stream, str);
2025 if (str.empty() || str == "\r")
2027 string::size_type nColon = str.find(":");
2028 if (nColon != string::npos)
2030 string strHeader = str.substr(0, nColon);
2031 boost::trim(strHeader);
2032 boost::to_lower(strHeader);
2033 string strValue = str.substr(nColon+1);
2034 boost::trim(strValue);
2035 mapHeadersRet[strHeader] = strValue;
2036 if (strHeader == "content-length")
2037 nLen = atoi(strValue.c_str());
2043 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2045 mapHeadersRet.clear();
2049 int nStatus = ReadHTTPStatus(stream);
2052 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2053 if (nLen < 0 || nLen > MAX_SIZE)
2059 vector<char> vch(nLen);
2060 stream.read(&vch[0], nLen);
2061 strMessageRet = string(vch.begin(), vch.end());
2067 bool HTTPAuthorized(map<string, string>& mapHeaders)
2069 string strAuth = mapHeaders["authorization"];
2070 if (strAuth.substr(0,6) != "Basic ")
2072 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2073 string strUserPass = DecodeBase64(strUserPass64);
2074 return strUserPass == strRPCUserColonPass;
2078 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2079 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2080 // unspecified (HTTP errors and contents of 'error').
2082 // 1.0 spec: http://json-rpc.org/wiki/specification
2083 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2084 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2087 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2090 request.push_back(Pair("method", strMethod));
2091 request.push_back(Pair("params", params));
2092 request.push_back(Pair("id", id));
2093 return write_string(Value(request), false) + "\n";
2096 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2099 if (error.type() != null_type)
2100 reply.push_back(Pair("result", Value::null));
2102 reply.push_back(Pair("result", result));
2103 reply.push_back(Pair("error", error));
2104 reply.push_back(Pair("id", id));
2105 return write_string(Value(reply), false) + "\n";
2108 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2110 // Send error reply from json-rpc error object
2112 int code = find_value(objError, "code").get_int();
2113 if (code == -32600) nStatus = 400;
2114 else if (code == -32601) nStatus = 404;
2115 string strReply = JSONRPCReply(Value::null, objError, id);
2116 stream << HTTPReply(nStatus, strReply) << std::flush;
2119 bool ClientAllowed(const string& strAddress)
2121 if (strAddress == asio::ip::address_v4::loopback().to_string())
2123 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2124 BOOST_FOREACH(string strAllow, vAllow)
2125 if (WildcardMatch(strAddress, strAllow))
2132 // IOStream device that speaks SSL but can also speak non-SSL
2134 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2136 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2138 fUseSSL = fUseSSLIn;
2139 fNeedHandshake = fUseSSLIn;
2142 void handshake(ssl::stream_base::handshake_type role)
2144 if (!fNeedHandshake) return;
2145 fNeedHandshake = false;
2146 stream.handshake(role);
2148 std::streamsize read(char* s, std::streamsize n)
2150 handshake(ssl::stream_base::server); // HTTPS servers read first
2151 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2152 return stream.next_layer().read_some(asio::buffer(s, n));
2154 std::streamsize write(const char* s, std::streamsize n)
2156 handshake(ssl::stream_base::client); // HTTPS clients write first
2157 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2158 return asio::write(stream.next_layer(), asio::buffer(s, n));
2160 bool connect(const std::string& server, const std::string& port)
2162 ip::tcp::resolver resolver(stream.get_io_service());
2163 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2164 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2165 ip::tcp::resolver::iterator end;
2166 boost::system::error_code error = asio::error::host_not_found;
2167 while (error && endpoint_iterator != end)
2169 stream.lowest_layer().close();
2170 stream.lowest_layer().connect(*endpoint_iterator++, error);
2178 bool fNeedHandshake;
2184 void ThreadRPCServer(void* parg)
2186 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2189 vnThreadsRunning[4]++;
2190 ThreadRPCServer2(parg);
2191 vnThreadsRunning[4]--;
2193 catch (std::exception& e) {
2194 vnThreadsRunning[4]--;
2195 PrintException(&e, "ThreadRPCServer()");
2197 vnThreadsRunning[4]--;
2198 PrintException(NULL, "ThreadRPCServer()");
2200 printf("ThreadRPCServer exiting\n");
2203 void ThreadRPCServer2(void* parg)
2205 printf("ThreadRPCServer started\n");
2207 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2208 if (strRPCUserColonPass == ":")
2210 string strWhatAmI = "To use ppcoind";
2211 if (mapArgs.count("-server"))
2212 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2213 else if (mapArgs.count("-daemon"))
2214 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2216 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2217 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2219 GetConfigFile().c_str());
2221 CreateThread(Shutdown, NULL);
2226 bool fUseSSL = GetBoolArg("-rpcssl");
2227 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2229 asio::io_service io_service;
2230 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2231 ip::tcp::acceptor acceptor(io_service, endpoint);
2233 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2236 ssl::context context(io_service, ssl::context::sslv23);
2239 context.set_options(ssl::context::no_sslv2);
2240 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2241 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2242 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2243 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2244 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2245 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2246 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2247 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2249 string ciphers = GetArg("-rpcsslciphers",
2250 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2251 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2255 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2260 // Accept connection
2262 SSLStream sslStream(io_service, context);
2263 SSLIOStreamDevice d(sslStream, fUseSSL);
2264 iostreams::stream<SSLIOStreamDevice> stream(d);
2266 ip::tcp::iostream stream;
2269 ip::tcp::endpoint peer;
2270 vnThreadsRunning[4]--;
2272 acceptor.accept(sslStream.lowest_layer(), peer);
2274 acceptor.accept(*stream.rdbuf(), peer);
2276 vnThreadsRunning[4]++;
2280 // Restrict callers by IP
2281 if (!ClientAllowed(peer.address().to_string()))
2283 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2285 stream << HTTPReply(403, "") << std::flush;
2289 map<string, string> mapHeaders;
2292 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2293 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2296 printf("ThreadRPCServer ReadHTTP timeout\n");
2300 // Check authorization
2301 if (mapHeaders.count("authorization") == 0)
2303 stream << HTTPReply(401, "") << std::flush;
2306 if (!HTTPAuthorized(mapHeaders))
2308 // Deter brute-forcing short passwords
2309 if (mapArgs["-rpcpassword"].size() < 15)
2312 stream << HTTPReply(401, "") << std::flush;
2313 printf("ThreadRPCServer incorrect password attempt\n");
2317 Value id = Value::null;
2322 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2323 throw JSONRPCError(-32700, "Parse error");
2324 const Object& request = valRequest.get_obj();
2326 // Parse id now so errors from here on will have the id
2327 id = find_value(request, "id");
2330 Value valMethod = find_value(request, "method");
2331 if (valMethod.type() == null_type)
2332 throw JSONRPCError(-32600, "Missing method");
2333 if (valMethod.type() != str_type)
2334 throw JSONRPCError(-32600, "Method must be a string");
2335 string strMethod = valMethod.get_str();
2336 if (strMethod != "getwork" && strMethod != "getmemorypool")
2337 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2340 Value valParams = find_value(request, "params");
2342 if (valParams.type() == array_type)
2343 params = valParams.get_array();
2344 else if (valParams.type() == null_type)
2347 throw JSONRPCError(-32600, "Params must be an array");
2350 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2351 if (mi == mapCallTable.end())
2352 throw JSONRPCError(-32601, "Method not found");
2354 // Observe safe mode
2355 string strWarning = GetWarnings("rpc");
2356 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2357 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2363 CRITICAL_BLOCK(cs_main)
2364 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2365 result = (*(*mi).second)(params, false);
2368 string strReply = JSONRPCReply(result, Value::null, id);
2369 stream << HTTPReply(200, strReply) << std::flush;
2371 catch (std::exception& e)
2373 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2376 catch (Object& objError)
2378 ErrorReply(stream, objError, id);
2380 catch (std::exception& e)
2382 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2390 Object CallRPC(const string& strMethod, const Array& params)
2392 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2393 throw runtime_error(strprintf(
2394 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2395 "If the file does not exist, create it with owner-readable-only file permissions."),
2396 GetConfigFile().c_str()));
2398 // Connect to localhost
2399 bool fUseSSL = GetBoolArg("-rpcssl");
2401 asio::io_service io_service;
2402 ssl::context context(io_service, ssl::context::sslv23);
2403 context.set_options(ssl::context::no_sslv2);
2404 SSLStream sslStream(io_service, context);
2405 SSLIOStreamDevice d(sslStream, fUseSSL);
2406 iostreams::stream<SSLIOStreamDevice> stream(d);
2407 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2408 throw runtime_error("couldn't connect to server");
2411 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2413 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2415 throw runtime_error("couldn't connect to server");
2419 // HTTP basic authentication
2420 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2421 map<string, string> mapRequestHeaders;
2422 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2425 string strRequest = JSONRPCRequest(strMethod, params, 1);
2426 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2427 stream << strPost << std::flush;
2430 map<string, string> mapHeaders;
2432 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2434 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2435 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2436 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2437 else if (strReply.empty())
2438 throw runtime_error("no response from server");
2442 if (!read_string(strReply, valReply))
2443 throw runtime_error("couldn't parse reply from server");
2444 const Object& reply = valReply.get_obj();
2446 throw runtime_error("expected reply to have result, error and id properties");
2454 template<typename T>
2455 void ConvertTo(Value& value)
2457 if (value.type() == str_type)
2459 // reinterpret string as unquoted json value
2461 if (!read_string(value.get_str(), value2))
2462 throw runtime_error("type mismatch");
2463 value = value2.get_value<T>();
2467 value = value.get_value<T>();
2471 int CommandLineRPC(int argc, char *argv[])
2478 while (argc > 1 && IsSwitchChar(argv[1][0]))
2486 throw runtime_error("too few parameters");
2487 string strMethod = argv[1];
2489 // Parameters default to strings
2491 for (int i = 2; i < argc; i++)
2492 params.push_back(argv[i]);
2493 int n = params.size();
2496 // Special case non-string parameter types
2498 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2499 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2500 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2501 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2502 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2503 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2504 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2505 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2506 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2507 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2508 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2509 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2510 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2511 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2512 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2513 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2514 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2515 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2516 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2517 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2518 if (strMethod == "sendmany" && n > 1)
2520 string s = params[1].get_str();
2522 if (!read_string(s, v) || v.type() != obj_type)
2523 throw runtime_error("type mismatch");
2524 params[1] = v.get_obj();
2526 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2527 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2530 Object reply = CallRPC(strMethod, params);
2533 const Value& result = find_value(reply, "result");
2534 const Value& error = find_value(reply, "error");
2536 if (error.type() != null_type)
2539 strPrint = "error: " + write_string(error, false);
2540 int code = find_value(error.get_obj(), "code").get_int();
2546 if (result.type() == null_type)
2548 else if (result.type() == str_type)
2549 strPrint = result.get_str();
2551 strPrint = write_string(result, true);
2554 catch (std::exception& e)
2556 strPrint = string("error: ") + e.what();
2561 PrintException(NULL, "CommandLineRPC()");
2566 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2575 int main(int argc, char *argv[])
2578 // Turn off microsoft heap dump noise
2579 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2580 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2582 setbuf(stdin, NULL);
2583 setbuf(stdout, NULL);
2584 setbuf(stderr, NULL);
2588 if (argc >= 2 && string(argv[1]) == "-server")
2590 printf("server ready\n");
2591 ThreadRPCServer(NULL);
2595 return CommandLineRPC(argc, argv);
2598 catch (std::exception& e) {
2599 PrintException(&e, "main()");
2601 PrintException(NULL, "main()");