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: reset auto checkpoint
1806 Value resetcheckpoint(const Array& params, bool fHelp)
1808 if (fHelp || params.size() < 1 || params.size() > 1)
1809 throw runtime_error(
1810 "resetcheckpoint <checkpointheight>\n"
1811 "Reset automatic checkpoint to specified height.\n"
1812 "<checkpointheight> is the height of the new checkpoint block.\n");
1814 int nCheckpoint = params[0].get_int();
1815 if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight)
1816 throw runtime_error(
1817 "invalid checkpoint height.\n"
1819 if (nCheckpoint >= Checkpoints::nAutoCheckpoint)
1820 throw runtime_error(
1821 "new checkpoint must be earlier than current auto checkpoint.\n"
1823 if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint))
1824 throw runtime_error(
1825 "internal error - reset checkpoint failed.\n"
1832 // ppcoin: get branch point of alternative branch
1833 Value getbranchpoint(const Array& params, bool fHelp)
1835 if (fHelp || params.size() != 0)
1836 throw runtime_error(
1838 "Returns height of branch point of alternative branch.\n");
1841 if (Checkpoints::nBranchPoint > 0)
1842 result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint));
1844 result.push_back(Pair("branchpoint", "none"));
1845 result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint));
1850 // ppcoin: reserve balance from being staked for network protection
1851 Value reservebalance(const Array& params, bool fHelp)
1853 if (fHelp || params.size() > 2)
1854 throw runtime_error(
1855 "reservebalance [<reserve> [amount]]\n"
1856 "<reserve> is true or false to turn balance reserve on or off.\n"
1857 "<amount> is a real and rounded to cent.\n"
1858 "Set reserve amount not participating in network protection.\n"
1859 "If no parameters provided current setting is printed.\n");
1861 if (params.size() > 0)
1863 bool fReserve = params[0].get_bool();
1866 if (params.size() == 1)
1867 throw runtime_error("must provide amount to reserve balance.\n");
1868 int64 nAmount = AmountFromValue(params[1]);
1869 nAmount = (nAmount / CENT) * CENT; // round to cent
1871 throw runtime_error("amount cannot be negative.\n");
1872 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1876 if (params.size() > 1)
1877 throw runtime_error("cannot specify amount to turn off reserve.\n");
1878 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1883 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1884 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1889 // ppcoin: check wallet integrity
1890 Value checkwallet(const Array& params, bool fHelp)
1892 if (fHelp || params.size() > 0)
1893 throw runtime_error(
1895 "Check wallet for integrity.\n");
1898 int64 nBalanceInQuestion;
1899 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1902 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1903 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1910 // ppcoin: repair wallet
1911 Value repairwallet(const Array& params, bool fHelp)
1913 if (fHelp || params.size() > 0)
1914 throw runtime_error(
1916 "Repair wallet if checkwallet reports any problem.\n");
1919 int64 nBalanceInQuestion;
1920 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1922 if (nMismatchSpent == 0)
1924 result.push_back(Pair("wallet check passed", true));
1928 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1929 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1934 // ppcoin: make a public-private key pair
1935 Value makekeypair(const Array& params, bool fHelp)
1939 CPrivKey vchPrivKey = key.GetPrivKey();
1942 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1943 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1947 extern CCriticalSection cs_mapAlerts;
1948 extern map<uint256, CAlert> mapAlerts;
1950 // ppcoin: send alert.
1951 // There is a known deadlock situation with ThreadMessageHandler
1952 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1953 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1954 Value sendalert(const Array& params, bool fHelp)
1956 if (fHelp || params.size() < 5)
1957 throw runtime_error(
1958 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1959 "<message> is the alert text message\n"
1960 "<privatekey> is hex string of alert master private key\n"
1961 "<minver> is the minimum applicable client version\n"
1962 "<maxver> is the maximum applicable client version\n"
1963 "<id> is the alert id\n"
1964 "[cancelupto] cancels all alert id's up to this number\n"
1965 "Returns true or false.");
1970 alert.strStatusBar = params[0].get_str();
1971 alert.nMinVer = params[2].get_int();
1972 alert.nMaxVer = params[3].get_int();
1973 alert.nID = params[4].get_int();
1974 if (params.size() > 5)
1975 alert.nCancel = params[5].get_int();
1976 alert.nVersion = VERSION;
1977 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1978 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1979 alert.nPriority = 1;
1982 sMsg << (CUnsignedAlert)alert;
1983 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1985 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1986 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1987 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1988 throw runtime_error(
1989 "Unable to sign alert, check private key?\n");
1990 if(!alert.ProcessAlert())
1991 throw runtime_error(
1992 "Failed to process alert.\n");
1994 CRITICAL_BLOCK(cs_vNodes)
1995 BOOST_FOREACH(CNode* pnode, vNodes)
1996 alert.RelayTo(pnode);
1999 result.push_back(Pair("strStatusBar", alert.strStatusBar));
2000 result.push_back(Pair("nVersion", alert.nVersion));
2001 result.push_back(Pair("nMinVer", alert.nMinVer));
2002 result.push_back(Pair("nMaxVer", alert.nMaxVer));
2003 result.push_back(Pair("nID", alert.nID));
2004 if (alert.nCancel > 0)
2005 result.push_back(Pair("nCancel", alert.nCancel));
2009 // ppcoin: send checkpoint
2010 Value sendcheckpoint(const Array& params, bool fHelp)
2012 if (fHelp || params.size() > 2 || params.size() < 1 )
2013 throw runtime_error(
2014 "sendcheckpoint <privatekey> [checkpointhash]\n"
2015 "<privatekey> is hex string of checkpoint master private key\n"
2016 "<checkpointhash> is the hash of checkpoint block\n");
2018 CSyncCheckpoint checkpoint;
2021 // TODO: omit checkpointhash parameter
2022 if (params.size() > 1)
2024 checkpoint.hashCheckpoint = uint256(params[1].get_str());
2025 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
2026 throw runtime_error(
2027 "Provided checkpoint block is not on main chain\n");
2031 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
2032 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
2033 throw runtime_error(
2034 "Unable to select a more recent sync-checkpoint");
2038 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2039 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2041 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2042 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2043 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2044 throw runtime_error(
2045 "Unable to sign checkpoint, check private key?\n");
2047 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2048 throw runtime_error(
2049 "Failed to process checkpoint.\n");
2051 CRITICAL_BLOCK(cs_vNodes)
2052 BOOST_FOREACH(CNode* pnode, vNodes)
2053 checkpoint.RelayTo(pnode);
2056 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2057 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2058 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
2067 pair<string, rpcfn_type> pCallTable[] =
2069 make_pair("help", &help),
2070 make_pair("stop", &stop),
2071 make_pair("getblockcount", &getblockcount),
2072 make_pair("getblocknumber", &getblocknumber),
2073 make_pair("getconnectioncount", &getconnectioncount),
2074 make_pair("getdifficulty", &getdifficulty),
2075 make_pair("getgenerate", &getgenerate),
2076 make_pair("setgenerate", &setgenerate),
2077 make_pair("gethashespersec", &gethashespersec),
2078 make_pair("getinfo", &getinfo),
2079 make_pair("getnewaddress", &getnewaddress),
2080 make_pair("getaccountaddress", &getaccountaddress),
2081 make_pair("setaccount", &setaccount),
2082 make_pair("getaccount", &getaccount),
2083 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2084 make_pair("sendtoaddress", &sendtoaddress),
2085 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2086 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2087 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2088 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2089 make_pair("backupwallet", &backupwallet),
2090 make_pair("keypoolrefill", &keypoolrefill),
2091 make_pair("walletpassphrase", &walletpassphrase),
2092 make_pair("walletpassphrasechange", &walletpassphrasechange),
2093 make_pair("walletlock", &walletlock),
2094 make_pair("encryptwallet", &encryptwallet),
2095 make_pair("validateaddress", &validateaddress),
2096 make_pair("getbalance", &getbalance),
2097 make_pair("move", &movecmd),
2098 make_pair("sendfrom", &sendfrom),
2099 make_pair("sendmany", &sendmany),
2100 make_pair("gettransaction", &gettransaction),
2101 make_pair("listtransactions", &listtransactions),
2102 make_pair("signmessage", &signmessage),
2103 make_pair("verifymessage", &verifymessage),
2104 make_pair("getwork", &getwork),
2105 make_pair("listaccounts", &listaccounts),
2106 make_pair("settxfee", &settxfee),
2107 make_pair("getmemorypool", &getmemorypool),
2108 make_pair("listsinceblock", &listsinceblock),
2109 make_pair("resetcheckpoint", &resetcheckpoint),
2110 make_pair("getbranchpoint", &getbranchpoint),
2111 make_pair("reservebalance", &reservebalance),
2112 make_pair("checkwallet", &checkwallet),
2113 make_pair("repairwallet", &repairwallet),
2114 make_pair("makekeypair", &makekeypair),
2115 make_pair("sendalert", &sendalert),
2116 make_pair("sendcheckpoint", &sendcheckpoint),
2118 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2120 string pAllowInSafeMode[] =
2125 "getblocknumber", // deprecated
2126 "getconnectioncount",
2133 "getaccountaddress",
2135 "getaddressesbyaccount",
2146 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2154 // This ain't Apache. We're just using HTTP header for the length field
2155 // and to be compatible with other JSON-RPC implementations.
2158 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2161 s << "POST / HTTP/1.1\r\n"
2162 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2163 << "Host: 127.0.0.1\r\n"
2164 << "Content-Type: application/json\r\n"
2165 << "Content-Length: " << strMsg.size() << "\r\n"
2166 << "Connection: close\r\n"
2167 << "Accept: application/json\r\n";
2168 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2169 s << item.first << ": " << item.second << "\r\n";
2170 s << "\r\n" << strMsg;
2175 string rfc1123Time()
2180 struct tm* now_gmt = gmtime(&now);
2181 string locale(setlocale(LC_TIME, NULL));
2182 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2183 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2184 setlocale(LC_TIME, locale.c_str());
2185 return string(buffer);
2188 static string HTTPReply(int nStatus, const string& strMsg)
2191 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2193 "Server: ppcoin-json-rpc/%s\r\n"
2194 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2195 "Content-Type: text/html\r\n"
2196 "Content-Length: 296\r\n"
2198 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2199 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2202 "<TITLE>Error</TITLE>\r\n"
2203 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2205 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2206 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2207 const char *cStatus;
2208 if (nStatus == 200) cStatus = "OK";
2209 else if (nStatus == 400) cStatus = "Bad Request";
2210 else if (nStatus == 403) cStatus = "Forbidden";
2211 else if (nStatus == 404) cStatus = "Not Found";
2212 else if (nStatus == 500) cStatus = "Internal Server Error";
2215 "HTTP/1.1 %d %s\r\n"
2217 "Connection: close\r\n"
2218 "Content-Length: %d\r\n"
2219 "Content-Type: application/json\r\n"
2220 "Server: ppcoin-json-rpc/%s\r\n"
2225 rfc1123Time().c_str(),
2227 FormatFullVersion().c_str(),
2231 int ReadHTTPStatus(std::basic_istream<char>& stream)
2234 getline(stream, str);
2235 vector<string> vWords;
2236 boost::split(vWords, str, boost::is_any_of(" "));
2237 if (vWords.size() < 2)
2239 return atoi(vWords[1].c_str());
2242 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2248 std::getline(stream, str);
2249 if (str.empty() || str == "\r")
2251 string::size_type nColon = str.find(":");
2252 if (nColon != string::npos)
2254 string strHeader = str.substr(0, nColon);
2255 boost::trim(strHeader);
2256 boost::to_lower(strHeader);
2257 string strValue = str.substr(nColon+1);
2258 boost::trim(strValue);
2259 mapHeadersRet[strHeader] = strValue;
2260 if (strHeader == "content-length")
2261 nLen = atoi(strValue.c_str());
2267 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2269 mapHeadersRet.clear();
2273 int nStatus = ReadHTTPStatus(stream);
2276 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2277 if (nLen < 0 || nLen > MAX_SIZE)
2283 vector<char> vch(nLen);
2284 stream.read(&vch[0], nLen);
2285 strMessageRet = string(vch.begin(), vch.end());
2291 bool HTTPAuthorized(map<string, string>& mapHeaders)
2293 string strAuth = mapHeaders["authorization"];
2294 if (strAuth.substr(0,6) != "Basic ")
2296 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2297 string strUserPass = DecodeBase64(strUserPass64);
2298 return strUserPass == strRPCUserColonPass;
2302 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2303 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2304 // unspecified (HTTP errors and contents of 'error').
2306 // 1.0 spec: http://json-rpc.org/wiki/specification
2307 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2308 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2311 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2314 request.push_back(Pair("method", strMethod));
2315 request.push_back(Pair("params", params));
2316 request.push_back(Pair("id", id));
2317 return write_string(Value(request), false) + "\n";
2320 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2323 if (error.type() != null_type)
2324 reply.push_back(Pair("result", Value::null));
2326 reply.push_back(Pair("result", result));
2327 reply.push_back(Pair("error", error));
2328 reply.push_back(Pair("id", id));
2329 return write_string(Value(reply), false) + "\n";
2332 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2334 // Send error reply from json-rpc error object
2336 int code = find_value(objError, "code").get_int();
2337 if (code == -32600) nStatus = 400;
2338 else if (code == -32601) nStatus = 404;
2339 string strReply = JSONRPCReply(Value::null, objError, id);
2340 stream << HTTPReply(nStatus, strReply) << std::flush;
2343 bool ClientAllowed(const string& strAddress)
2345 if (strAddress == asio::ip::address_v4::loopback().to_string())
2347 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2348 BOOST_FOREACH(string strAllow, vAllow)
2349 if (WildcardMatch(strAddress, strAllow))
2356 // IOStream device that speaks SSL but can also speak non-SSL
2358 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2360 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2362 fUseSSL = fUseSSLIn;
2363 fNeedHandshake = fUseSSLIn;
2366 void handshake(ssl::stream_base::handshake_type role)
2368 if (!fNeedHandshake) return;
2369 fNeedHandshake = false;
2370 stream.handshake(role);
2372 std::streamsize read(char* s, std::streamsize n)
2374 handshake(ssl::stream_base::server); // HTTPS servers read first
2375 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2376 return stream.next_layer().read_some(asio::buffer(s, n));
2378 std::streamsize write(const char* s, std::streamsize n)
2380 handshake(ssl::stream_base::client); // HTTPS clients write first
2381 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2382 return asio::write(stream.next_layer(), asio::buffer(s, n));
2384 bool connect(const std::string& server, const std::string& port)
2386 ip::tcp::resolver resolver(stream.get_io_service());
2387 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2388 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2389 ip::tcp::resolver::iterator end;
2390 boost::system::error_code error = asio::error::host_not_found;
2391 while (error && endpoint_iterator != end)
2393 stream.lowest_layer().close();
2394 stream.lowest_layer().connect(*endpoint_iterator++, error);
2402 bool fNeedHandshake;
2408 void ThreadRPCServer(void* parg)
2410 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2413 vnThreadsRunning[4]++;
2414 ThreadRPCServer2(parg);
2415 vnThreadsRunning[4]--;
2417 catch (std::exception& e) {
2418 vnThreadsRunning[4]--;
2419 PrintException(&e, "ThreadRPCServer()");
2421 vnThreadsRunning[4]--;
2422 PrintException(NULL, "ThreadRPCServer()");
2424 printf("ThreadRPCServer exiting\n");
2427 void ThreadRPCServer2(void* parg)
2429 printf("ThreadRPCServer started\n");
2431 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2432 if (strRPCUserColonPass == ":")
2434 string strWhatAmI = "To use ppcoind";
2435 if (mapArgs.count("-server"))
2436 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2437 else if (mapArgs.count("-daemon"))
2438 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2440 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2441 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2443 GetConfigFile().c_str());
2445 CreateThread(Shutdown, NULL);
2450 bool fUseSSL = GetBoolArg("-rpcssl");
2451 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2453 asio::io_service io_service;
2454 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2455 ip::tcp::acceptor acceptor(io_service, endpoint);
2457 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2460 ssl::context context(io_service, ssl::context::sslv23);
2463 context.set_options(ssl::context::no_sslv2);
2464 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2465 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2466 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2467 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2468 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2469 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2470 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2471 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2473 string ciphers = GetArg("-rpcsslciphers",
2474 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2475 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2479 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2484 // Accept connection
2486 SSLStream sslStream(io_service, context);
2487 SSLIOStreamDevice d(sslStream, fUseSSL);
2488 iostreams::stream<SSLIOStreamDevice> stream(d);
2490 ip::tcp::iostream stream;
2493 ip::tcp::endpoint peer;
2494 vnThreadsRunning[4]--;
2496 acceptor.accept(sslStream.lowest_layer(), peer);
2498 acceptor.accept(*stream.rdbuf(), peer);
2500 vnThreadsRunning[4]++;
2504 // Restrict callers by IP
2505 if (!ClientAllowed(peer.address().to_string()))
2507 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2509 stream << HTTPReply(403, "") << std::flush;
2513 map<string, string> mapHeaders;
2516 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2517 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2520 printf("ThreadRPCServer ReadHTTP timeout\n");
2524 // Check authorization
2525 if (mapHeaders.count("authorization") == 0)
2527 stream << HTTPReply(401, "") << std::flush;
2530 if (!HTTPAuthorized(mapHeaders))
2532 // Deter brute-forcing short passwords
2533 if (mapArgs["-rpcpassword"].size() < 15)
2536 stream << HTTPReply(401, "") << std::flush;
2537 printf("ThreadRPCServer incorrect password attempt\n");
2541 Value id = Value::null;
2546 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2547 throw JSONRPCError(-32700, "Parse error");
2548 const Object& request = valRequest.get_obj();
2550 // Parse id now so errors from here on will have the id
2551 id = find_value(request, "id");
2554 Value valMethod = find_value(request, "method");
2555 if (valMethod.type() == null_type)
2556 throw JSONRPCError(-32600, "Missing method");
2557 if (valMethod.type() != str_type)
2558 throw JSONRPCError(-32600, "Method must be a string");
2559 string strMethod = valMethod.get_str();
2560 if (strMethod != "getwork" && strMethod != "getmemorypool")
2561 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2564 Value valParams = find_value(request, "params");
2566 if (valParams.type() == array_type)
2567 params = valParams.get_array();
2568 else if (valParams.type() == null_type)
2571 throw JSONRPCError(-32600, "Params must be an array");
2574 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2575 if (mi == mapCallTable.end())
2576 throw JSONRPCError(-32601, "Method not found");
2578 // Observe safe mode
2579 string strWarning = GetWarnings("rpc");
2580 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2581 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2587 CRITICAL_BLOCK(cs_main)
2588 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2589 result = (*(*mi).second)(params, false);
2592 string strReply = JSONRPCReply(result, Value::null, id);
2593 stream << HTTPReply(200, strReply) << std::flush;
2595 catch (std::exception& e)
2597 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2600 catch (Object& objError)
2602 ErrorReply(stream, objError, id);
2604 catch (std::exception& e)
2606 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2614 Object CallRPC(const string& strMethod, const Array& params)
2616 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2617 throw runtime_error(strprintf(
2618 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2619 "If the file does not exist, create it with owner-readable-only file permissions."),
2620 GetConfigFile().c_str()));
2622 // Connect to localhost
2623 bool fUseSSL = GetBoolArg("-rpcssl");
2625 asio::io_service io_service;
2626 ssl::context context(io_service, ssl::context::sslv23);
2627 context.set_options(ssl::context::no_sslv2);
2628 SSLStream sslStream(io_service, context);
2629 SSLIOStreamDevice d(sslStream, fUseSSL);
2630 iostreams::stream<SSLIOStreamDevice> stream(d);
2631 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2632 throw runtime_error("couldn't connect to server");
2635 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2637 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2639 throw runtime_error("couldn't connect to server");
2643 // HTTP basic authentication
2644 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2645 map<string, string> mapRequestHeaders;
2646 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2649 string strRequest = JSONRPCRequest(strMethod, params, 1);
2650 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2651 stream << strPost << std::flush;
2654 map<string, string> mapHeaders;
2656 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2658 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2659 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2660 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2661 else if (strReply.empty())
2662 throw runtime_error("no response from server");
2666 if (!read_string(strReply, valReply))
2667 throw runtime_error("couldn't parse reply from server");
2668 const Object& reply = valReply.get_obj();
2670 throw runtime_error("expected reply to have result, error and id properties");
2678 template<typename T>
2679 void ConvertTo(Value& value)
2681 if (value.type() == str_type)
2683 // reinterpret string as unquoted json value
2685 if (!read_string(value.get_str(), value2))
2686 throw runtime_error("type mismatch");
2687 value = value2.get_value<T>();
2691 value = value.get_value<T>();
2695 int CommandLineRPC(int argc, char *argv[])
2702 while (argc > 1 && IsSwitchChar(argv[1][0]))
2710 throw runtime_error("too few parameters");
2711 string strMethod = argv[1];
2713 // Parameters default to strings
2715 for (int i = 2; i < argc; i++)
2716 params.push_back(argv[i]);
2717 int n = params.size();
2720 // Special case non-string parameter types
2722 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2723 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2724 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2725 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2726 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2727 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2728 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2729 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2730 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2731 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2732 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2733 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2734 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2735 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2736 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2737 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2738 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2739 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2740 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2741 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2742 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2743 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2744 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2745 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2746 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2747 if (strMethod == "sendmany" && n > 1)
2749 string s = params[1].get_str();
2751 if (!read_string(s, v) || v.type() != obj_type)
2752 throw runtime_error("type mismatch");
2753 params[1] = v.get_obj();
2755 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2756 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2757 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2758 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2761 Object reply = CallRPC(strMethod, params);
2764 const Value& result = find_value(reply, "result");
2765 const Value& error = find_value(reply, "error");
2767 if (error.type() != null_type)
2770 strPrint = "error: " + write_string(error, false);
2771 int code = find_value(error.get_obj(), "code").get_int();
2777 if (result.type() == null_type)
2779 else if (result.type() == str_type)
2780 strPrint = result.get_str();
2782 strPrint = write_string(result, true);
2785 catch (std::exception& e)
2787 strPrint = string("error: ") + e.what();
2792 PrintException(NULL, "CommandLineRPC()");
2797 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2806 int main(int argc, char *argv[])
2809 // Turn off microsoft heap dump noise
2810 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2811 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2813 setbuf(stdin, NULL);
2814 setbuf(stdout, NULL);
2815 setbuf(stderr, NULL);
2819 if (argc >= 2 && string(argv[1]) == "-server")
2821 printf("server ready\n");
2822 ThreadRPCServer(NULL);
2826 return CommandLineRPC(argc, argv);
2829 catch (std::exception& e) {
2830 PrintException(&e, "main()");
2832 PrintException(NULL, "main()");