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("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
312 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
313 obj.push_back(Pair("blocks", (int)nBestHeight));
314 obj.push_back(Pair("connections", (int)vNodes.size()));
315 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
316 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
317 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
318 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
319 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
320 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
321 obj.push_back(Pair("testnet", fTestNet));
322 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
323 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
324 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
325 if (pwalletMain->IsCrypted())
326 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
327 obj.push_back(Pair("errors", GetWarnings("statusbar")));
332 Value getnewaddress(const Array& params, bool fHelp)
334 if (fHelp || params.size() > 1)
336 "getnewaddress [account]\n"
337 "Returns a new ppcoin address for receiving payments. "
338 "If [account] is specified (recommended), it is added to the address book "
339 "so payments received with the address will be credited to [account].");
341 // Parse the account first so we don't generate a key if there's an error
343 if (params.size() > 0)
344 strAccount = AccountFromValue(params[0]);
346 if (!pwalletMain->IsLocked())
347 pwalletMain->TopUpKeyPool();
349 // Generate a new key that is added to wallet
350 std::vector<unsigned char> newKey;
351 if (!pwalletMain->GetKeyFromPool(newKey, false))
352 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
353 CBitcoinAddress address(newKey);
355 pwalletMain->SetAddressBookName(address, strAccount);
357 return address.ToString();
361 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
363 CWalletDB walletdb(pwalletMain->strWalletFile);
366 walletdb.ReadAccount(strAccount, account);
368 bool bKeyUsed = false;
370 // Check if the current key has been used
371 if (!account.vchPubKey.empty())
373 CScript scriptPubKey;
374 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
375 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
376 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
379 const CWalletTx& wtx = (*it).second;
380 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
381 if (txout.scriptPubKey == scriptPubKey)
386 // Generate a new key
387 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
389 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
390 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
392 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
393 walletdb.WriteAccount(strAccount, account);
396 return CBitcoinAddress(account.vchPubKey);
399 Value getaccountaddress(const Array& params, bool fHelp)
401 if (fHelp || params.size() != 1)
403 "getaccountaddress <account>\n"
404 "Returns the current ppcoin address for receiving payments to this account.");
406 // Parse the account first so we don't generate a key if there's an error
407 string strAccount = AccountFromValue(params[0]);
411 ret = GetAccountAddress(strAccount).ToString();
418 Value setaccount(const Array& params, bool fHelp)
420 if (fHelp || params.size() < 1 || params.size() > 2)
422 "setaccount <ppcoinaddress> <account>\n"
423 "Sets the account associated with the given address.");
425 CBitcoinAddress address(params[0].get_str());
426 if (!address.IsValid())
427 throw JSONRPCError(-5, "Invalid ppcoin address");
431 if (params.size() > 1)
432 strAccount = AccountFromValue(params[1]);
434 // Detect when changing the account of an address that is the 'unused current key' of another account:
435 if (pwalletMain->mapAddressBook.count(address))
437 string strOldAccount = pwalletMain->mapAddressBook[address];
438 if (address == GetAccountAddress(strOldAccount))
439 GetAccountAddress(strOldAccount, true);
442 pwalletMain->SetAddressBookName(address, strAccount);
448 Value getaccount(const Array& params, bool fHelp)
450 if (fHelp || params.size() != 1)
452 "getaccount <ppcoinaddress>\n"
453 "Returns the account associated with the given address.");
455 CBitcoinAddress address(params[0].get_str());
456 if (!address.IsValid())
457 throw JSONRPCError(-5, "Invalid ppcoin address");
460 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
461 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
462 strAccount = (*mi).second;
467 Value getaddressesbyaccount(const Array& params, bool fHelp)
469 if (fHelp || params.size() != 1)
471 "getaddressesbyaccount <account>\n"
472 "Returns the list of addresses for the given account.");
474 string strAccount = AccountFromValue(params[0]);
476 // Find all addresses that have the given account
478 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
480 const CBitcoinAddress& address = item.first;
481 const string& strName = item.second;
482 if (strName == strAccount)
483 ret.push_back(address.ToString());
488 Value settxfee(const Array& params, bool fHelp)
490 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
492 "settxfee <amount>\n"
493 "<amount> is a real and is rounded to 0.01 (cent)\n"
494 "Minimum and default transaction fee per KB is 1 cent");
496 nTransactionFee = AmountFromValue(params[0]);
497 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
501 Value sendtoaddress(const Array& params, bool fHelp)
503 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
505 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
506 "<amount> is a real and is rounded to the nearest 0.000001\n"
507 "requires wallet passphrase to be set with walletpassphrase first");
508 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
510 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
511 "<amount> is a real and is rounded to the nearest 0.000001");
513 CBitcoinAddress address(params[0].get_str());
514 if (!address.IsValid())
515 throw JSONRPCError(-5, "Invalid ppcoin address");
518 int64 nAmount = AmountFromValue(params[1]);
522 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
523 wtx.mapValue["comment"] = params[2].get_str();
524 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
525 wtx.mapValue["to"] = params[3].get_str();
527 if (pwalletMain->IsLocked())
528 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
530 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
532 throw JSONRPCError(-4, strError);
534 return wtx.GetHash().GetHex();
537 static const string strMessageMagic = "Bitcoin Signed Message:\n";
539 Value signmessage(const Array& params, bool fHelp)
541 if (fHelp || params.size() != 2)
543 "signmessage <ppcoinaddress> <message>\n"
544 "Sign a message with the private key of an address");
546 if (pwalletMain->IsLocked())
547 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
549 string strAddress = params[0].get_str();
550 string strMessage = params[1].get_str();
552 CBitcoinAddress addr(strAddress);
554 throw JSONRPCError(-3, "Invalid address");
557 if (!pwalletMain->GetKey(addr, key))
558 throw JSONRPCError(-4, "Private key not available");
560 CDataStream ss(SER_GETHASH);
561 ss << strMessageMagic;
564 vector<unsigned char> vchSig;
565 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
566 throw JSONRPCError(-5, "Sign failed");
568 return EncodeBase64(&vchSig[0], vchSig.size());
571 Value verifymessage(const Array& params, bool fHelp)
573 if (fHelp || params.size() != 3)
575 "verifymessage <ppcoinaddress> <signature> <message>\n"
576 "Verify a signed message");
578 string strAddress = params[0].get_str();
579 string strSign = params[1].get_str();
580 string strMessage = params[2].get_str();
582 CBitcoinAddress addr(strAddress);
584 throw JSONRPCError(-3, "Invalid address");
586 bool fInvalid = false;
587 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
590 throw JSONRPCError(-5, "Malformed base64 encoding");
592 CDataStream ss(SER_GETHASH);
593 ss << strMessageMagic;
597 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
600 return (key.GetAddress() == addr);
604 Value getreceivedbyaddress(const Array& params, bool fHelp)
606 if (fHelp || params.size() < 1 || params.size() > 2)
608 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
609 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
612 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
613 CScript scriptPubKey;
614 if (!address.IsValid())
615 throw JSONRPCError(-5, "Invalid ppcoin address");
616 scriptPubKey.SetBitcoinAddress(address);
617 if (!IsMine(*pwalletMain,scriptPubKey))
620 // Minimum confirmations
622 if (params.size() > 1)
623 nMinDepth = params[1].get_int();
627 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
629 const CWalletTx& wtx = (*it).second;
630 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
633 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
634 if (txout.scriptPubKey == scriptPubKey)
635 if (wtx.GetDepthInMainChain() >= nMinDepth)
636 nAmount += txout.nValue;
639 return ValueFromAmount(nAmount);
643 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
645 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
647 const CBitcoinAddress& address = item.first;
648 const string& strName = item.second;
649 if (strName == strAccount)
650 setAddress.insert(address);
655 Value getreceivedbyaccount(const Array& params, bool fHelp)
657 if (fHelp || params.size() < 1 || params.size() > 2)
659 "getreceivedbyaccount <account> [minconf=1]\n"
660 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
662 // Minimum confirmations
664 if (params.size() > 1)
665 nMinDepth = params[1].get_int();
667 // Get the set of pub keys that have the label
668 string strAccount = AccountFromValue(params[0]);
669 set<CBitcoinAddress> setAddress;
670 GetAccountAddresses(strAccount, setAddress);
674 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
676 const CWalletTx& wtx = (*it).second;
677 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
680 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
682 CBitcoinAddress address;
683 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
684 if (wtx.GetDepthInMainChain() >= nMinDepth)
685 nAmount += txout.nValue;
689 return (double)nAmount / (double)COIN;
693 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
697 // Tally wallet transactions
698 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
700 const CWalletTx& wtx = (*it).second;
704 int64 nGenerated, nReceived, nSent, nFee;
705 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
707 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
708 nBalance += nReceived;
709 nBalance += nGenerated - nSent - nFee;
712 // Tally internal accounting entries
713 nBalance += walletdb.GetAccountCreditDebit(strAccount);
718 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
720 CWalletDB walletdb(pwalletMain->strWalletFile);
721 return GetAccountBalance(walletdb, strAccount, nMinDepth);
725 Value getbalance(const Array& params, bool fHelp)
727 if (fHelp || params.size() > 2)
729 "getbalance [account] [minconf=1]\n"
730 "If [account] is not specified, returns the server's total available balance.\n"
731 "If [account] is specified, returns the balance in the account.");
733 if (params.size() == 0)
734 return ValueFromAmount(pwalletMain->GetBalance());
737 if (params.size() > 1)
738 nMinDepth = params[1].get_int();
740 if (params[0].get_str() == "*") {
741 // Calculate total balance a different way from GetBalance()
742 // (GetBalance() sums up all unspent TxOuts)
743 // getbalance and getbalance '*' should always return the same number.
745 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
747 const CWalletTx& wtx = (*it).second;
751 int64 allGeneratedImmature, allGeneratedMature, allFee;
752 allGeneratedImmature = allGeneratedMature = allFee = 0;
753 string strSentAccount;
754 list<pair<CBitcoinAddress, int64> > listReceived;
755 list<pair<CBitcoinAddress, int64> > listSent;
756 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
757 if (wtx.GetDepthInMainChain() >= nMinDepth)
758 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
759 nBalance += r.second;
760 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
761 nBalance -= r.second;
763 nBalance += allGeneratedMature;
765 return ValueFromAmount(nBalance);
768 string strAccount = AccountFromValue(params[0]);
770 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
772 return ValueFromAmount(nBalance);
776 Value movecmd(const Array& params, bool fHelp)
778 if (fHelp || params.size() < 3 || params.size() > 5)
780 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
781 "Move from one account in your wallet to another.");
783 string strFrom = AccountFromValue(params[0]);
784 string strTo = AccountFromValue(params[1]);
785 int64 nAmount = AmountFromValue(params[2]);
786 if (params.size() > 3)
787 // unused parameter, used to be nMinDepth, keep type-checking it though
788 (void)params[3].get_int();
790 if (params.size() > 4)
791 strComment = params[4].get_str();
793 CWalletDB walletdb(pwalletMain->strWalletFile);
796 int64 nNow = GetAdjustedTime();
799 CAccountingEntry debit;
800 debit.strAccount = strFrom;
801 debit.nCreditDebit = -nAmount;
803 debit.strOtherAccount = strTo;
804 debit.strComment = strComment;
805 walletdb.WriteAccountingEntry(debit);
808 CAccountingEntry credit;
809 credit.strAccount = strTo;
810 credit.nCreditDebit = nAmount;
812 credit.strOtherAccount = strFrom;
813 credit.strComment = strComment;
814 walletdb.WriteAccountingEntry(credit);
816 walletdb.TxnCommit();
822 Value sendfrom(const Array& params, bool fHelp)
824 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
826 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
827 "<amount> is a real and is rounded to the nearest 0.000001\n"
828 "requires wallet passphrase to be set with walletpassphrase first");
829 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
831 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
832 "<amount> is a real and is rounded to the nearest 0.000001");
834 string strAccount = AccountFromValue(params[0]);
835 CBitcoinAddress address(params[1].get_str());
836 if (!address.IsValid())
837 throw JSONRPCError(-5, "Invalid ppcoin address");
838 int64 nAmount = AmountFromValue(params[2]);
840 if (params.size() > 3)
841 nMinDepth = params[3].get_int();
844 wtx.strFromAccount = strAccount;
845 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
846 wtx.mapValue["comment"] = params[4].get_str();
847 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
848 wtx.mapValue["to"] = params[5].get_str();
850 if (pwalletMain->IsLocked())
851 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
854 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
855 if (nAmount > nBalance)
856 throw JSONRPCError(-6, "Account has insufficient funds");
859 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
861 throw JSONRPCError(-4, strError);
863 return wtx.GetHash().GetHex();
867 Value sendmany(const Array& params, bool fHelp)
869 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
871 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
872 "amounts are double-precision floating point numbers\n"
873 "requires wallet passphrase to be set with walletpassphrase first");
874 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
876 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
877 "amounts are double-precision floating point numbers");
879 string strAccount = AccountFromValue(params[0]);
880 Object sendTo = params[1].get_obj();
882 if (params.size() > 2)
883 nMinDepth = params[2].get_int();
886 wtx.strFromAccount = strAccount;
887 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
888 wtx.mapValue["comment"] = params[3].get_str();
890 set<CBitcoinAddress> setAddress;
891 vector<pair<CScript, int64> > vecSend;
893 int64 totalAmount = 0;
894 BOOST_FOREACH(const Pair& s, sendTo)
896 CBitcoinAddress address(s.name_);
897 if (!address.IsValid())
898 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
900 if (setAddress.count(address))
901 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
902 setAddress.insert(address);
904 CScript scriptPubKey;
905 scriptPubKey.SetBitcoinAddress(address);
906 int64 nAmount = AmountFromValue(s.value_);
907 totalAmount += nAmount;
909 vecSend.push_back(make_pair(scriptPubKey, nAmount));
912 if (pwalletMain->IsLocked())
913 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
914 if (fWalletUnlockStakeOnly)
915 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
918 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
919 if (totalAmount > nBalance)
920 throw JSONRPCError(-6, "Account has insufficient funds");
923 CReserveKey keyChange(pwalletMain);
924 int64 nFeeRequired = 0;
925 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
928 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
929 throw JSONRPCError(-6, "Insufficient funds");
930 throw JSONRPCError(-4, "Transaction creation failed");
932 if (!pwalletMain->CommitTransaction(wtx, keyChange))
933 throw JSONRPCError(-4, "Transaction commit failed");
935 return wtx.GetHash().GetHex();
950 Value ListReceived(const Array& params, bool fByAccounts)
952 // Minimum confirmations
954 if (params.size() > 0)
955 nMinDepth = params[0].get_int();
957 // Whether to include empty accounts
958 bool fIncludeEmpty = false;
959 if (params.size() > 1)
960 fIncludeEmpty = params[1].get_bool();
963 map<CBitcoinAddress, tallyitem> mapTally;
964 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
966 const CWalletTx& wtx = (*it).second;
967 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
970 int nDepth = wtx.GetDepthInMainChain();
971 if (nDepth < nMinDepth)
974 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
976 CBitcoinAddress address;
977 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
980 tallyitem& item = mapTally[address];
981 item.nAmount += txout.nValue;
982 item.nConf = min(item.nConf, nDepth);
988 map<string, tallyitem> mapAccountTally;
989 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
991 const CBitcoinAddress& address = item.first;
992 const string& strAccount = item.second;
993 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
994 if (it == mapTally.end() && !fIncludeEmpty)
999 if (it != mapTally.end())
1001 nAmount = (*it).second.nAmount;
1002 nConf = (*it).second.nConf;
1007 tallyitem& item = mapAccountTally[strAccount];
1008 item.nAmount += nAmount;
1009 item.nConf = min(item.nConf, nConf);
1014 obj.push_back(Pair("address", address.ToString()));
1015 obj.push_back(Pair("account", strAccount));
1016 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1017 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1024 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1026 int64 nAmount = (*it).second.nAmount;
1027 int nConf = (*it).second.nConf;
1029 obj.push_back(Pair("account", (*it).first));
1030 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1031 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1039 Value listreceivedbyaddress(const Array& params, bool fHelp)
1041 if (fHelp || params.size() > 2)
1042 throw runtime_error(
1043 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1044 "[minconf] is the minimum number of confirmations before payments are included.\n"
1045 "[includeempty] whether to include addresses that haven't received any payments.\n"
1046 "Returns an array of objects containing:\n"
1047 " \"address\" : receiving address\n"
1048 " \"account\" : the account of the receiving address\n"
1049 " \"amount\" : total amount received by the address\n"
1050 " \"confirmations\" : number of confirmations of the most recent transaction included");
1052 return ListReceived(params, false);
1055 Value listreceivedbyaccount(const Array& params, bool fHelp)
1057 if (fHelp || params.size() > 2)
1058 throw runtime_error(
1059 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1060 "[minconf] is the minimum number of confirmations before payments are included.\n"
1061 "[includeempty] whether to include accounts that haven't received any payments.\n"
1062 "Returns an array of objects containing:\n"
1063 " \"account\" : the account of the receiving addresses\n"
1064 " \"amount\" : total amount received by addresses with this account\n"
1065 " \"confirmations\" : number of confirmations of the most recent transaction included");
1067 return ListReceived(params, true);
1070 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1072 int64 nGeneratedImmature, nGeneratedMature, nFee;
1073 string strSentAccount;
1074 list<pair<CBitcoinAddress, int64> > listReceived;
1075 list<pair<CBitcoinAddress, int64> > listSent;
1076 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1078 bool fAllAccounts = (strAccount == string("*"));
1080 // Generated blocks assigned to account ""
1081 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1084 entry.push_back(Pair("account", string("")));
1085 if (nGeneratedImmature)
1087 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1088 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1092 entry.push_back(Pair("category", "generate"));
1093 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1096 WalletTxToJSON(wtx, entry);
1097 ret.push_back(entry);
1101 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1103 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1106 entry.push_back(Pair("account", strSentAccount));
1107 entry.push_back(Pair("address", s.first.ToString()));
1108 entry.push_back(Pair("category", "send"));
1109 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1110 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1112 WalletTxToJSON(wtx, entry);
1113 ret.push_back(entry);
1118 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1119 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1122 if (pwalletMain->mapAddressBook.count(r.first))
1123 account = pwalletMain->mapAddressBook[r.first];
1124 if (fAllAccounts || (account == strAccount))
1127 entry.push_back(Pair("account", account));
1128 entry.push_back(Pair("address", r.first.ToString()));
1129 entry.push_back(Pair("category", "receive"));
1130 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1132 WalletTxToJSON(wtx, entry);
1133 ret.push_back(entry);
1138 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1140 bool fAllAccounts = (strAccount == string("*"));
1142 if (fAllAccounts || acentry.strAccount == strAccount)
1145 entry.push_back(Pair("account", acentry.strAccount));
1146 entry.push_back(Pair("category", "move"));
1147 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1148 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1149 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1150 entry.push_back(Pair("comment", acentry.strComment));
1151 ret.push_back(entry);
1155 Value listtransactions(const Array& params, bool fHelp)
1157 if (fHelp || params.size() > 3)
1158 throw runtime_error(
1159 "listtransactions [account] [count=10] [from=0]\n"
1160 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1162 string strAccount = "*";
1163 if (params.size() > 0)
1164 strAccount = params[0].get_str();
1166 if (params.size() > 1)
1167 nCount = params[1].get_int();
1169 if (params.size() > 2)
1170 nFrom = params[2].get_int();
1173 CWalletDB walletdb(pwalletMain->strWalletFile);
1175 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1176 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1177 typedef multimap<int64, TxPair > TxItems;
1180 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1182 CWalletTx* wtx = &((*it).second);
1183 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1185 list<CAccountingEntry> acentries;
1186 walletdb.ListAccountCreditDebit(strAccount, acentries);
1187 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1189 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1192 // Now: iterate backwards until we have nCount items to return:
1193 TxItems::reverse_iterator it = txByTime.rbegin();
1194 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1195 for (; it != txByTime.rend(); ++it)
1197 CWalletTx *const pwtx = (*it).second.first;
1199 ListTransactions(*pwtx, strAccount, 0, true, ret);
1200 CAccountingEntry *const pacentry = (*it).second.second;
1202 AcentryToJSON(*pacentry, strAccount, ret);
1204 if (ret.size() >= nCount) break;
1206 // ret is now newest to oldest
1208 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1209 if (ret.size() > nCount)
1211 Array::iterator last = ret.begin();
1212 std::advance(last, nCount);
1213 ret.erase(last, ret.end());
1215 std::reverse(ret.begin(), ret.end()); // oldest to newest
1220 Value listaccounts(const Array& params, bool fHelp)
1222 if (fHelp || params.size() > 1)
1223 throw runtime_error(
1224 "listaccounts [minconf=1]\n"
1225 "Returns Object that has account names as keys, account balances as values.");
1228 if (params.size() > 0)
1229 nMinDepth = params[0].get_int();
1231 map<string, int64> mapAccountBalances;
1232 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1233 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1234 mapAccountBalances[entry.second] = 0;
1237 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1239 const CWalletTx& wtx = (*it).second;
1240 int64 nGeneratedImmature, nGeneratedMature, nFee;
1241 string strSentAccount;
1242 list<pair<CBitcoinAddress, int64> > listReceived;
1243 list<pair<CBitcoinAddress, int64> > listSent;
1244 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1245 mapAccountBalances[strSentAccount] -= nFee;
1246 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1247 mapAccountBalances[strSentAccount] -= s.second;
1248 if (wtx.GetDepthInMainChain() >= nMinDepth)
1250 mapAccountBalances[""] += nGeneratedMature;
1251 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1252 if (pwalletMain->mapAddressBook.count(r.first))
1253 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1255 mapAccountBalances[""] += r.second;
1259 list<CAccountingEntry> acentries;
1260 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1261 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1262 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1265 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1266 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1271 Value listsinceblock(const Array& params, bool fHelp)
1274 throw runtime_error(
1275 "listsinceblock [blockid] [target-confirmations]\n"
1276 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1278 CBlockIndex *pindex = NULL;
1279 int target_confirms = 1;
1281 if (params.size() > 0)
1283 uint256 blockId = 0;
1285 blockId.SetHex(params[0].get_str());
1286 pindex = CBlockLocator(blockId).GetBlockIndex();
1289 if (params.size() > 1)
1291 target_confirms = params[1].get_int();
1293 if (target_confirms < 1)
1294 throw JSONRPCError(-8, "Invalid parameter");
1297 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1301 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1303 CWalletTx tx = (*it).second;
1305 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1306 ListTransactions(tx, "*", 0, true, transactions);
1311 if (target_confirms == 1)
1314 lastblock = hashBestChain;
1318 int target_height = pindexBest->nHeight + 1 - target_confirms;
1321 for (block = pindexBest;
1322 block && block->nHeight > target_height;
1323 block = block->pprev);
1325 lastblock = block ? block->GetBlockHash() : 0;
1329 ret.push_back(Pair("transactions", transactions));
1330 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1335 Value gettransaction(const Array& params, bool fHelp)
1337 if (fHelp || params.size() != 1)
1338 throw runtime_error(
1339 "gettransaction <txid>\n"
1340 "Get detailed information about <txid>");
1343 hash.SetHex(params[0].get_str());
1347 if (!pwalletMain->mapWallet.count(hash))
1348 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1349 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1351 int64 nCredit = wtx.GetCredit();
1352 int64 nDebit = wtx.GetDebit();
1353 int64 nNet = nCredit - nDebit;
1354 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1356 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1358 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1360 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1363 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1364 entry.push_back(Pair("details", details));
1370 Value backupwallet(const Array& params, bool fHelp)
1372 if (fHelp || params.size() != 1)
1373 throw runtime_error(
1374 "backupwallet <destination>\n"
1375 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1377 string strDest = params[0].get_str();
1378 BackupWallet(*pwalletMain, strDest);
1384 Value keypoolrefill(const Array& params, bool fHelp)
1386 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1387 throw runtime_error(
1389 "Fills the keypool, requires wallet passphrase to be set.");
1390 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1391 throw runtime_error(
1393 "Fills the keypool.");
1395 if (pwalletMain->IsLocked())
1396 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1398 pwalletMain->TopUpKeyPool();
1400 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1401 throw JSONRPCError(-4, "Error refreshing keypool.");
1407 void ThreadTopUpKeyPool(void* parg)
1409 pwalletMain->TopUpKeyPool();
1412 void ThreadCleanWalletPassphrase(void* parg)
1414 int64 nMyWakeTime = GetTime() + *((int*)parg);
1416 if (nWalletUnlockTime == 0)
1418 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1420 nWalletUnlockTime = nMyWakeTime;
1423 while (GetTime() < nWalletUnlockTime)
1424 Sleep(GetTime() - nWalletUnlockTime);
1426 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1428 nWalletUnlockTime = 0;
1433 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1435 if (nWalletUnlockTime < nMyWakeTime)
1436 nWalletUnlockTime = nMyWakeTime;
1442 pwalletMain->Lock();
1447 Value walletpassphrase(const Array& params, bool fHelp)
1449 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1450 throw runtime_error(
1451 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1452 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1453 "stakeonly is optional true/false allowing only stake creation.");
1456 if (!pwalletMain->IsCrypted())
1457 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1459 if (!pwalletMain->IsLocked())
1460 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1462 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1463 SecureString strWalletPass;
1464 strWalletPass.reserve(100);
1465 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1466 // Alternately, find a way to make params[0] mlock()'d to begin with.
1467 strWalletPass = params[0].get_str().c_str();
1469 if (strWalletPass.length() > 0)
1471 if (!pwalletMain->Unlock(strWalletPass))
1472 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1475 throw runtime_error(
1476 "walletpassphrase <passphrase> <timeout>\n"
1477 "Stores the wallet decryption key in memory for <timeout> seconds.");
1479 CreateThread(ThreadTopUpKeyPool, NULL);
1480 int* pnSleepTime = new int(params[1].get_int());
1481 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1483 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1484 if (params.size() > 2)
1485 fWalletUnlockStakeOnly = params[2].get_bool();
1487 fWalletUnlockStakeOnly = false;
1493 Value walletpassphrasechange(const Array& params, bool fHelp)
1495 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1496 throw runtime_error(
1497 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1498 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1501 if (!pwalletMain->IsCrypted())
1502 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1504 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1505 // Alternately, find a way to make params[0] mlock()'d to begin with.
1506 SecureString strOldWalletPass;
1507 strOldWalletPass.reserve(100);
1508 strOldWalletPass = params[0].get_str().c_str();
1510 SecureString strNewWalletPass;
1511 strNewWalletPass.reserve(100);
1512 strNewWalletPass = params[1].get_str().c_str();
1514 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1515 throw runtime_error(
1516 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1517 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1519 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1520 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1526 Value walletlock(const Array& params, bool fHelp)
1528 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1529 throw runtime_error(
1531 "Removes the wallet encryption key from memory, locking the wallet.\n"
1532 "After calling this method, you will need to call walletpassphrase again\n"
1533 "before being able to call any methods which require the wallet to be unlocked.");
1536 if (!pwalletMain->IsCrypted())
1537 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1539 pwalletMain->Lock();
1540 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1542 nWalletUnlockTime = 0;
1549 Value encryptwallet(const Array& params, bool fHelp)
1551 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1552 throw runtime_error(
1553 "encryptwallet <passphrase>\n"
1554 "Encrypts the wallet with <passphrase>.");
1557 if (pwalletMain->IsCrypted())
1558 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1561 // shutting down via RPC while the GUI is running does not work (yet):
1562 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1565 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1566 // Alternately, find a way to make params[0] mlock()'d to begin with.
1567 SecureString strWalletPass;
1568 strWalletPass.reserve(100);
1569 strWalletPass = params[0].get_str().c_str();
1571 if (strWalletPass.length() < 1)
1572 throw runtime_error(
1573 "encryptwallet <passphrase>\n"
1574 "Encrypts the wallet with <passphrase>.");
1576 if (!pwalletMain->EncryptWallet(strWalletPass))
1577 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1579 // BDB seems to have a bad habit of writing old data into
1580 // slack space in .dat files; that is bad if the old data is
1581 // unencrypted private keys. So:
1582 CreateThread(Shutdown, NULL);
1583 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1587 Value validateaddress(const Array& params, bool fHelp)
1589 if (fHelp || params.size() != 1)
1590 throw runtime_error(
1591 "validateaddress <ppcoinaddress>\n"
1592 "Return information about <ppcoinaddress>.");
1594 CBitcoinAddress address(params[0].get_str());
1595 bool isValid = address.IsValid();
1598 ret.push_back(Pair("isvalid", isValid));
1601 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1602 // version of the address:
1603 string currentAddress = address.ToString();
1604 ret.push_back(Pair("address", currentAddress));
1605 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1606 if (pwalletMain->mapAddressBook.count(address))
1607 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1613 Value getwork(const Array& params, bool fHelp)
1615 if (fHelp || params.size() > 1)
1616 throw runtime_error(
1618 "If [data] is not specified, returns formatted hash data to work on:\n"
1619 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1620 " \"data\" : block data\n"
1621 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1622 " \"target\" : little endian hash target\n"
1623 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1626 throw JSONRPCError(-9, "PPCoin is not connected!");
1628 if (IsInitialBlockDownload())
1629 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1631 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1632 static mapNewBlock_t mapNewBlock;
1633 static vector<CBlock*> vNewBlock;
1634 static CReserveKey reservekey(pwalletMain);
1636 if (params.size() == 0)
1639 static unsigned int nTransactionsUpdatedLast;
1640 static CBlockIndex* pindexPrev;
1641 static int64 nStart;
1642 static CBlock* pblock;
1643 if (pindexPrev != pindexBest ||
1644 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1646 if (pindexPrev != pindexBest)
1648 // Deallocate old blocks since they're obsolete now
1649 mapNewBlock.clear();
1650 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1654 nTransactionsUpdatedLast = nTransactionsUpdated;
1655 pindexPrev = pindexBest;
1659 pblock = CreateNewBlock(pwalletMain);
1661 throw JSONRPCError(-7, "Out of memory");
1662 vNewBlock.push_back(pblock);
1666 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1669 // Update nExtraNonce
1670 static unsigned int nExtraNonce = 0;
1671 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1674 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1676 // Prebuild hash buffers
1680 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1682 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1685 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1686 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1687 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1688 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1694 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1695 if (vchData.size() != 128)
1696 throw JSONRPCError(-8, "Invalid parameter");
1697 CBlock* pdata = (CBlock*)&vchData[0];
1700 for (int i = 0; i < 128/4; i++)
1701 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1704 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1706 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1708 pblock->nTime = pdata->nTime;
1709 pblock->nNonce = pdata->nNonce;
1710 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1711 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1713 return CheckWork(pblock, *pwalletMain, reservekey);
1718 Value getmemorypool(const Array& params, bool fHelp)
1720 if (fHelp || params.size() > 1)
1721 throw runtime_error(
1722 "getmemorypool [data]\n"
1723 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1724 " \"version\" : block version\n"
1725 " \"previousblockhash\" : hash of current highest block\n"
1726 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1727 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1728 " \"time\" : timestamp appropriate for next block\n"
1729 " \"bits\" : compressed target of next block\n"
1730 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1732 if (params.size() == 0)
1735 throw JSONRPCError(-9, "PPCoin is not connected!");
1737 if (IsInitialBlockDownload())
1738 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1740 static CReserveKey reservekey(pwalletMain);
1743 static unsigned int nTransactionsUpdatedLast;
1744 static CBlockIndex* pindexPrev;
1745 static int64 nStart;
1746 static CBlock* pblock;
1747 if (pindexPrev != pindexBest ||
1748 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1750 nTransactionsUpdatedLast = nTransactionsUpdated;
1751 pindexPrev = pindexBest;
1757 pblock = CreateNewBlock(pwalletMain);
1759 throw JSONRPCError(-7, "Out of memory");
1763 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1767 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1768 if(tx.IsCoinBase() || tx.IsCoinStake())
1774 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1778 result.push_back(Pair("version", pblock->nVersion));
1779 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1780 result.push_back(Pair("transactions", transactions));
1781 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1782 result.push_back(Pair("time", (int64_t)pblock->nTime));
1788 uBits.nBits = htonl((int32_t)pblock->nBits);
1789 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1796 CDataStream ssBlock(ParseHex(params[0].get_str()));
1800 return ProcessBlock(NULL, &pblock);
1805 // ppcoin: reserve balance from being staked for network protection
1806 Value reservebalance(const Array& params, bool fHelp)
1808 if (fHelp || params.size() > 2)
1809 throw runtime_error(
1810 "reservebalance [<reserve> [amount]]\n"
1811 "<reserve> is true or false to turn balance reserve on or off.\n"
1812 "<amount> is a real and rounded to cent.\n"
1813 "Set reserve amount not participating in network protection.\n"
1814 "If no parameters provided current setting is printed.\n");
1816 if (params.size() > 0)
1818 bool fReserve = params[0].get_bool();
1821 if (params.size() == 1)
1822 throw runtime_error("must provide amount to reserve balance.\n");
1823 int64 nAmount = AmountFromValue(params[1]);
1824 nAmount = (nAmount / CENT) * CENT; // round to cent
1826 throw runtime_error("amount cannot be negative.\n");
1827 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1831 if (params.size() > 1)
1832 throw runtime_error("cannot specify amount to turn off reserve.\n");
1833 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1838 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1839 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1844 // ppcoin: check wallet integrity
1845 Value checkwallet(const Array& params, bool fHelp)
1847 if (fHelp || params.size() > 0)
1848 throw runtime_error(
1850 "Check wallet for integrity.\n");
1853 int64 nBalanceInQuestion;
1854 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1857 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1858 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1865 // ppcoin: repair wallet
1866 Value repairwallet(const Array& params, bool fHelp)
1868 if (fHelp || params.size() > 0)
1869 throw runtime_error(
1871 "Repair wallet if checkwallet reports any problem.\n");
1874 int64 nBalanceInQuestion;
1875 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1877 if (nMismatchSpent == 0)
1879 result.push_back(Pair("wallet check passed", true));
1883 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1884 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1889 // ppcoin: make a public-private key pair
1890 Value makekeypair(const Array& params, bool fHelp)
1894 CPrivKey vchPrivKey = key.GetPrivKey();
1897 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1898 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1902 extern CCriticalSection cs_mapAlerts;
1903 extern map<uint256, CAlert> mapAlerts;
1905 // ppcoin: send alert.
1906 // There is a known deadlock situation with ThreadMessageHandler
1907 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1908 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1909 Value sendalert(const Array& params, bool fHelp)
1911 if (fHelp || params.size() < 5)
1912 throw runtime_error(
1913 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1914 "<message> is the alert text message\n"
1915 "<privatekey> is hex string of alert master private key\n"
1916 "<minver> is the minimum applicable client version\n"
1917 "<maxver> is the maximum applicable client version\n"
1918 "<id> is the alert id\n"
1919 "[cancelupto] cancels all alert id's up to this number\n"
1920 "Returns true or false.");
1925 alert.strStatusBar = params[0].get_str();
1926 alert.nMinVer = params[2].get_int();
1927 alert.nMaxVer = params[3].get_int();
1928 alert.nID = params[4].get_int();
1929 if (params.size() > 5)
1930 alert.nCancel = params[5].get_int();
1931 alert.nVersion = VERSION;
1932 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1933 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1934 alert.nPriority = 1;
1937 sMsg << (CUnsignedAlert)alert;
1938 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1940 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1941 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1942 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1943 throw runtime_error(
1944 "Unable to sign alert, check private key?\n");
1945 if(!alert.ProcessAlert())
1946 throw runtime_error(
1947 "Failed to process alert.\n");
1949 CRITICAL_BLOCK(cs_vNodes)
1950 BOOST_FOREACH(CNode* pnode, vNodes)
1951 alert.RelayTo(pnode);
1954 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1955 result.push_back(Pair("nVersion", alert.nVersion));
1956 result.push_back(Pair("nMinVer", alert.nMinVer));
1957 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1958 result.push_back(Pair("nID", alert.nID));
1959 if (alert.nCancel > 0)
1960 result.push_back(Pair("nCancel", alert.nCancel));
1964 // ppcoin: send checkpoint
1965 Value sendcheckpoint(const Array& params, bool fHelp)
1967 if (fHelp || params.size() > 2 || params.size() < 1 )
1968 throw runtime_error(
1969 "sendcheckpoint <privatekey> [checkpointhash]\n"
1970 "<privatekey> is hex string of checkpoint master private key\n"
1971 "<checkpointhash> is the hash of checkpoint block\n");
1973 CSyncCheckpoint checkpoint;
1976 // TODO: omit checkpointhash parameter
1977 if (params.size() > 1)
1979 checkpoint.hashCheckpoint = uint256(params[1].get_str());
1980 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
1981 throw runtime_error(
1982 "Provided checkpoint block is not on main chain\n");
1986 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
1987 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
1988 throw runtime_error(
1989 "Unable to select a more recent sync-checkpoint");
1993 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
1994 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1996 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
1997 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1998 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
1999 throw runtime_error(
2000 "Unable to sign checkpoint, check private key?\n");
2002 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2003 throw runtime_error(
2004 "Failed to process checkpoint.\n");
2006 CRITICAL_BLOCK(cs_vNodes)
2007 BOOST_FOREACH(CNode* pnode, vNodes)
2008 checkpoint.RelayTo(pnode);
2011 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2012 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2013 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
2022 pair<string, rpcfn_type> pCallTable[] =
2024 make_pair("help", &help),
2025 make_pair("stop", &stop),
2026 make_pair("getblockcount", &getblockcount),
2027 make_pair("getblocknumber", &getblocknumber),
2028 make_pair("getconnectioncount", &getconnectioncount),
2029 make_pair("getdifficulty", &getdifficulty),
2030 make_pair("getgenerate", &getgenerate),
2031 make_pair("setgenerate", &setgenerate),
2032 make_pair("gethashespersec", &gethashespersec),
2033 make_pair("getinfo", &getinfo),
2034 make_pair("getnewaddress", &getnewaddress),
2035 make_pair("getaccountaddress", &getaccountaddress),
2036 make_pair("setaccount", &setaccount),
2037 make_pair("getaccount", &getaccount),
2038 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2039 make_pair("sendtoaddress", &sendtoaddress),
2040 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2041 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2042 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2043 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2044 make_pair("backupwallet", &backupwallet),
2045 make_pair("keypoolrefill", &keypoolrefill),
2046 make_pair("walletpassphrase", &walletpassphrase),
2047 make_pair("walletpassphrasechange", &walletpassphrasechange),
2048 make_pair("walletlock", &walletlock),
2049 make_pair("encryptwallet", &encryptwallet),
2050 make_pair("validateaddress", &validateaddress),
2051 make_pair("getbalance", &getbalance),
2052 make_pair("move", &movecmd),
2053 make_pair("sendfrom", &sendfrom),
2054 make_pair("sendmany", &sendmany),
2055 make_pair("gettransaction", &gettransaction),
2056 make_pair("listtransactions", &listtransactions),
2057 make_pair("signmessage", &signmessage),
2058 make_pair("verifymessage", &verifymessage),
2059 make_pair("getwork", &getwork),
2060 make_pair("listaccounts", &listaccounts),
2061 make_pair("settxfee", &settxfee),
2062 make_pair("getmemorypool", &getmemorypool),
2063 make_pair("listsinceblock", &listsinceblock),
2064 make_pair("reservebalance", &reservebalance),
2065 make_pair("checkwallet", &checkwallet),
2066 make_pair("repairwallet", &repairwallet),
2067 make_pair("makekeypair", &makekeypair),
2068 make_pair("sendalert", &sendalert),
2069 make_pair("sendcheckpoint", &sendcheckpoint),
2071 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2073 string pAllowInSafeMode[] =
2078 "getblocknumber", // deprecated
2079 "getconnectioncount",
2086 "getaccountaddress",
2088 "getaddressesbyaccount",
2097 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2105 // This ain't Apache. We're just using HTTP header for the length field
2106 // and to be compatible with other JSON-RPC implementations.
2109 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2112 s << "POST / HTTP/1.1\r\n"
2113 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2114 << "Host: 127.0.0.1\r\n"
2115 << "Content-Type: application/json\r\n"
2116 << "Content-Length: " << strMsg.size() << "\r\n"
2117 << "Connection: close\r\n"
2118 << "Accept: application/json\r\n";
2119 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2120 s << item.first << ": " << item.second << "\r\n";
2121 s << "\r\n" << strMsg;
2126 string rfc1123Time()
2131 struct tm* now_gmt = gmtime(&now);
2132 string locale(setlocale(LC_TIME, NULL));
2133 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2134 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2135 setlocale(LC_TIME, locale.c_str());
2136 return string(buffer);
2139 static string HTTPReply(int nStatus, const string& strMsg)
2142 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2144 "Server: ppcoin-json-rpc/%s\r\n"
2145 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2146 "Content-Type: text/html\r\n"
2147 "Content-Length: 296\r\n"
2149 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2150 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2153 "<TITLE>Error</TITLE>\r\n"
2154 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2156 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2157 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2158 const char *cStatus;
2159 if (nStatus == 200) cStatus = "OK";
2160 else if (nStatus == 400) cStatus = "Bad Request";
2161 else if (nStatus == 403) cStatus = "Forbidden";
2162 else if (nStatus == 404) cStatus = "Not Found";
2163 else if (nStatus == 500) cStatus = "Internal Server Error";
2166 "HTTP/1.1 %d %s\r\n"
2168 "Connection: close\r\n"
2169 "Content-Length: %d\r\n"
2170 "Content-Type: application/json\r\n"
2171 "Server: ppcoin-json-rpc/%s\r\n"
2176 rfc1123Time().c_str(),
2178 FormatFullVersion().c_str(),
2182 int ReadHTTPStatus(std::basic_istream<char>& stream)
2185 getline(stream, str);
2186 vector<string> vWords;
2187 boost::split(vWords, str, boost::is_any_of(" "));
2188 if (vWords.size() < 2)
2190 return atoi(vWords[1].c_str());
2193 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2199 std::getline(stream, str);
2200 if (str.empty() || str == "\r")
2202 string::size_type nColon = str.find(":");
2203 if (nColon != string::npos)
2205 string strHeader = str.substr(0, nColon);
2206 boost::trim(strHeader);
2207 boost::to_lower(strHeader);
2208 string strValue = str.substr(nColon+1);
2209 boost::trim(strValue);
2210 mapHeadersRet[strHeader] = strValue;
2211 if (strHeader == "content-length")
2212 nLen = atoi(strValue.c_str());
2218 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2220 mapHeadersRet.clear();
2224 int nStatus = ReadHTTPStatus(stream);
2227 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2228 if (nLen < 0 || nLen > MAX_SIZE)
2234 vector<char> vch(nLen);
2235 stream.read(&vch[0], nLen);
2236 strMessageRet = string(vch.begin(), vch.end());
2242 bool HTTPAuthorized(map<string, string>& mapHeaders)
2244 string strAuth = mapHeaders["authorization"];
2245 if (strAuth.substr(0,6) != "Basic ")
2247 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2248 string strUserPass = DecodeBase64(strUserPass64);
2249 return strUserPass == strRPCUserColonPass;
2253 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2254 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2255 // unspecified (HTTP errors and contents of 'error').
2257 // 1.0 spec: http://json-rpc.org/wiki/specification
2258 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2259 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2262 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2265 request.push_back(Pair("method", strMethod));
2266 request.push_back(Pair("params", params));
2267 request.push_back(Pair("id", id));
2268 return write_string(Value(request), false) + "\n";
2271 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2274 if (error.type() != null_type)
2275 reply.push_back(Pair("result", Value::null));
2277 reply.push_back(Pair("result", result));
2278 reply.push_back(Pair("error", error));
2279 reply.push_back(Pair("id", id));
2280 return write_string(Value(reply), false) + "\n";
2283 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2285 // Send error reply from json-rpc error object
2287 int code = find_value(objError, "code").get_int();
2288 if (code == -32600) nStatus = 400;
2289 else if (code == -32601) nStatus = 404;
2290 string strReply = JSONRPCReply(Value::null, objError, id);
2291 stream << HTTPReply(nStatus, strReply) << std::flush;
2294 bool ClientAllowed(const string& strAddress)
2296 if (strAddress == asio::ip::address_v4::loopback().to_string())
2298 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2299 BOOST_FOREACH(string strAllow, vAllow)
2300 if (WildcardMatch(strAddress, strAllow))
2307 // IOStream device that speaks SSL but can also speak non-SSL
2309 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2311 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2313 fUseSSL = fUseSSLIn;
2314 fNeedHandshake = fUseSSLIn;
2317 void handshake(ssl::stream_base::handshake_type role)
2319 if (!fNeedHandshake) return;
2320 fNeedHandshake = false;
2321 stream.handshake(role);
2323 std::streamsize read(char* s, std::streamsize n)
2325 handshake(ssl::stream_base::server); // HTTPS servers read first
2326 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2327 return stream.next_layer().read_some(asio::buffer(s, n));
2329 std::streamsize write(const char* s, std::streamsize n)
2331 handshake(ssl::stream_base::client); // HTTPS clients write first
2332 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2333 return asio::write(stream.next_layer(), asio::buffer(s, n));
2335 bool connect(const std::string& server, const std::string& port)
2337 ip::tcp::resolver resolver(stream.get_io_service());
2338 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2339 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2340 ip::tcp::resolver::iterator end;
2341 boost::system::error_code error = asio::error::host_not_found;
2342 while (error && endpoint_iterator != end)
2344 stream.lowest_layer().close();
2345 stream.lowest_layer().connect(*endpoint_iterator++, error);
2353 bool fNeedHandshake;
2359 void ThreadRPCServer(void* parg)
2361 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2364 vnThreadsRunning[4]++;
2365 ThreadRPCServer2(parg);
2366 vnThreadsRunning[4]--;
2368 catch (std::exception& e) {
2369 vnThreadsRunning[4]--;
2370 PrintException(&e, "ThreadRPCServer()");
2372 vnThreadsRunning[4]--;
2373 PrintException(NULL, "ThreadRPCServer()");
2375 printf("ThreadRPCServer exiting\n");
2378 void ThreadRPCServer2(void* parg)
2380 printf("ThreadRPCServer started\n");
2382 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2383 if (strRPCUserColonPass == ":")
2385 string strWhatAmI = "To use ppcoind";
2386 if (mapArgs.count("-server"))
2387 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2388 else if (mapArgs.count("-daemon"))
2389 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2391 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2392 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2394 GetConfigFile().c_str());
2396 CreateThread(Shutdown, NULL);
2401 bool fUseSSL = GetBoolArg("-rpcssl");
2402 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2404 asio::io_service io_service;
2405 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2406 ip::tcp::acceptor acceptor(io_service, endpoint);
2408 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2411 ssl::context context(io_service, ssl::context::sslv23);
2414 context.set_options(ssl::context::no_sslv2);
2415 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2416 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2417 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2418 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2419 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2420 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2421 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2422 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2424 string ciphers = GetArg("-rpcsslciphers",
2425 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2426 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2430 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2435 // Accept connection
2437 SSLStream sslStream(io_service, context);
2438 SSLIOStreamDevice d(sslStream, fUseSSL);
2439 iostreams::stream<SSLIOStreamDevice> stream(d);
2441 ip::tcp::iostream stream;
2444 ip::tcp::endpoint peer;
2445 vnThreadsRunning[4]--;
2447 acceptor.accept(sslStream.lowest_layer(), peer);
2449 acceptor.accept(*stream.rdbuf(), peer);
2451 vnThreadsRunning[4]++;
2455 // Restrict callers by IP
2456 if (!ClientAllowed(peer.address().to_string()))
2458 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2460 stream << HTTPReply(403, "") << std::flush;
2464 map<string, string> mapHeaders;
2467 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2468 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2471 printf("ThreadRPCServer ReadHTTP timeout\n");
2475 // Check authorization
2476 if (mapHeaders.count("authorization") == 0)
2478 stream << HTTPReply(401, "") << std::flush;
2481 if (!HTTPAuthorized(mapHeaders))
2483 // Deter brute-forcing short passwords
2484 if (mapArgs["-rpcpassword"].size() < 15)
2487 stream << HTTPReply(401, "") << std::flush;
2488 printf("ThreadRPCServer incorrect password attempt\n");
2492 Value id = Value::null;
2497 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2498 throw JSONRPCError(-32700, "Parse error");
2499 const Object& request = valRequest.get_obj();
2501 // Parse id now so errors from here on will have the id
2502 id = find_value(request, "id");
2505 Value valMethod = find_value(request, "method");
2506 if (valMethod.type() == null_type)
2507 throw JSONRPCError(-32600, "Missing method");
2508 if (valMethod.type() != str_type)
2509 throw JSONRPCError(-32600, "Method must be a string");
2510 string strMethod = valMethod.get_str();
2511 if (strMethod != "getwork" && strMethod != "getmemorypool")
2512 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2515 Value valParams = find_value(request, "params");
2517 if (valParams.type() == array_type)
2518 params = valParams.get_array();
2519 else if (valParams.type() == null_type)
2522 throw JSONRPCError(-32600, "Params must be an array");
2525 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2526 if (mi == mapCallTable.end())
2527 throw JSONRPCError(-32601, "Method not found");
2529 // Observe safe mode
2530 string strWarning = GetWarnings("rpc");
2531 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2532 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2538 CRITICAL_BLOCK(cs_main)
2539 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2540 result = (*(*mi).second)(params, false);
2543 string strReply = JSONRPCReply(result, Value::null, id);
2544 stream << HTTPReply(200, strReply) << std::flush;
2546 catch (std::exception& e)
2548 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2551 catch (Object& objError)
2553 ErrorReply(stream, objError, id);
2555 catch (std::exception& e)
2557 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2565 Object CallRPC(const string& strMethod, const Array& params)
2567 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2568 throw runtime_error(strprintf(
2569 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2570 "If the file does not exist, create it with owner-readable-only file permissions."),
2571 GetConfigFile().c_str()));
2573 // Connect to localhost
2574 bool fUseSSL = GetBoolArg("-rpcssl");
2576 asio::io_service io_service;
2577 ssl::context context(io_service, ssl::context::sslv23);
2578 context.set_options(ssl::context::no_sslv2);
2579 SSLStream sslStream(io_service, context);
2580 SSLIOStreamDevice d(sslStream, fUseSSL);
2581 iostreams::stream<SSLIOStreamDevice> stream(d);
2582 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2583 throw runtime_error("couldn't connect to server");
2586 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2588 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2590 throw runtime_error("couldn't connect to server");
2594 // HTTP basic authentication
2595 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2596 map<string, string> mapRequestHeaders;
2597 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2600 string strRequest = JSONRPCRequest(strMethod, params, 1);
2601 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2602 stream << strPost << std::flush;
2605 map<string, string> mapHeaders;
2607 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2609 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2610 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2611 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2612 else if (strReply.empty())
2613 throw runtime_error("no response from server");
2617 if (!read_string(strReply, valReply))
2618 throw runtime_error("couldn't parse reply from server");
2619 const Object& reply = valReply.get_obj();
2621 throw runtime_error("expected reply to have result, error and id properties");
2629 template<typename T>
2630 void ConvertTo(Value& value)
2632 if (value.type() == str_type)
2634 // reinterpret string as unquoted json value
2636 if (!read_string(value.get_str(), value2))
2637 throw runtime_error("type mismatch");
2638 value = value2.get_value<T>();
2642 value = value.get_value<T>();
2646 int CommandLineRPC(int argc, char *argv[])
2653 while (argc > 1 && IsSwitchChar(argv[1][0]))
2661 throw runtime_error("too few parameters");
2662 string strMethod = argv[1];
2664 // Parameters default to strings
2666 for (int i = 2; i < argc; i++)
2667 params.push_back(argv[i]);
2668 int n = params.size();
2671 // Special case non-string parameter types
2673 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2674 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2675 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2676 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2677 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2678 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2679 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2680 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2681 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2682 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2683 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2684 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2685 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2686 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2687 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2688 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2689 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2690 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2691 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2692 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2693 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2694 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2695 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2696 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2697 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2698 if (strMethod == "sendmany" && n > 1)
2700 string s = params[1].get_str();
2702 if (!read_string(s, v) || v.type() != obj_type)
2703 throw runtime_error("type mismatch");
2704 params[1] = v.get_obj();
2706 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2707 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2708 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2711 Object reply = CallRPC(strMethod, params);
2714 const Value& result = find_value(reply, "result");
2715 const Value& error = find_value(reply, "error");
2717 if (error.type() != null_type)
2720 strPrint = "error: " + write_string(error, false);
2721 int code = find_value(error.get_obj(), "code").get_int();
2727 if (result.type() == null_type)
2729 else if (result.type() == str_type)
2730 strPrint = result.get_str();
2732 strPrint = write_string(result, true);
2735 catch (std::exception& e)
2737 strPrint = string("error: ") + e.what();
2742 PrintException(NULL, "CommandLineRPC()");
2747 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2756 int main(int argc, char *argv[])
2759 // Turn off microsoft heap dump noise
2760 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2761 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2763 setbuf(stdin, NULL);
2764 setbuf(stdout, NULL);
2765 setbuf(stderr, NULL);
2769 if (argc >= 2 && string(argv[1]) == "-server")
2771 printf("server ready\n");
2772 ThreadRPCServer(NULL);
2776 return CommandLineRPC(argc, argv);
2779 catch (std::exception& e) {
2780 PrintException(&e, "main()");
2782 PrintException(NULL, "main()");