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 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2032 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2034 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2035 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2036 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2037 throw runtime_error(
2038 "Unable to sign checkpoint, check private key?\n");
2040 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2041 throw runtime_error(
2042 "Failed to process checkpoint.\n");
2044 CRITICAL_BLOCK(cs_vNodes)
2045 BOOST_FOREACH(CNode* pnode, vNodes)
2046 checkpoint.RelayTo(pnode);
2056 pair<string, rpcfn_type> pCallTable[] =
2058 make_pair("help", &help),
2059 make_pair("stop", &stop),
2060 make_pair("getblockcount", &getblockcount),
2061 make_pair("getblocknumber", &getblocknumber),
2062 make_pair("getconnectioncount", &getconnectioncount),
2063 make_pair("getdifficulty", &getdifficulty),
2064 make_pair("getgenerate", &getgenerate),
2065 make_pair("setgenerate", &setgenerate),
2066 make_pair("gethashespersec", &gethashespersec),
2067 make_pair("getinfo", &getinfo),
2068 make_pair("getnewaddress", &getnewaddress),
2069 make_pair("getaccountaddress", &getaccountaddress),
2070 make_pair("setaccount", &setaccount),
2071 make_pair("getaccount", &getaccount),
2072 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2073 make_pair("sendtoaddress", &sendtoaddress),
2074 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2075 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2076 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2077 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2078 make_pair("backupwallet", &backupwallet),
2079 make_pair("keypoolrefill", &keypoolrefill),
2080 make_pair("walletpassphrase", &walletpassphrase),
2081 make_pair("walletpassphrasechange", &walletpassphrasechange),
2082 make_pair("walletlock", &walletlock),
2083 make_pair("encryptwallet", &encryptwallet),
2084 make_pair("validateaddress", &validateaddress),
2085 make_pair("getbalance", &getbalance),
2086 make_pair("move", &movecmd),
2087 make_pair("sendfrom", &sendfrom),
2088 make_pair("sendmany", &sendmany),
2089 make_pair("gettransaction", &gettransaction),
2090 make_pair("listtransactions", &listtransactions),
2091 make_pair("signmessage", &signmessage),
2092 make_pair("verifymessage", &verifymessage),
2093 make_pair("getwork", &getwork),
2094 make_pair("listaccounts", &listaccounts),
2095 make_pair("settxfee", &settxfee),
2096 make_pair("getmemorypool", &getmemorypool),
2097 make_pair("listsinceblock", &listsinceblock),
2098 make_pair("resetcheckpoint", &resetcheckpoint),
2099 make_pair("getbranchpoint", &getbranchpoint),
2100 make_pair("reservebalance", &reservebalance),
2101 make_pair("checkwallet", &checkwallet),
2102 make_pair("repairwallet", &repairwallet),
2103 make_pair("makekeypair", &makekeypair),
2104 make_pair("sendalert", &sendalert),
2105 make_pair("sendcheckpoint", &sendcheckpoint),
2107 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2109 string pAllowInSafeMode[] =
2114 "getblocknumber", // deprecated
2115 "getconnectioncount",
2122 "getaccountaddress",
2124 "getaddressesbyaccount",
2135 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2143 // This ain't Apache. We're just using HTTP header for the length field
2144 // and to be compatible with other JSON-RPC implementations.
2147 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2150 s << "POST / HTTP/1.1\r\n"
2151 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2152 << "Host: 127.0.0.1\r\n"
2153 << "Content-Type: application/json\r\n"
2154 << "Content-Length: " << strMsg.size() << "\r\n"
2155 << "Connection: close\r\n"
2156 << "Accept: application/json\r\n";
2157 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2158 s << item.first << ": " << item.second << "\r\n";
2159 s << "\r\n" << strMsg;
2164 string rfc1123Time()
2169 struct tm* now_gmt = gmtime(&now);
2170 string locale(setlocale(LC_TIME, NULL));
2171 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2172 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2173 setlocale(LC_TIME, locale.c_str());
2174 return string(buffer);
2177 static string HTTPReply(int nStatus, const string& strMsg)
2180 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2182 "Server: ppcoin-json-rpc/%s\r\n"
2183 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2184 "Content-Type: text/html\r\n"
2185 "Content-Length: 296\r\n"
2187 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2188 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2191 "<TITLE>Error</TITLE>\r\n"
2192 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2194 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2195 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2196 const char *cStatus;
2197 if (nStatus == 200) cStatus = "OK";
2198 else if (nStatus == 400) cStatus = "Bad Request";
2199 else if (nStatus == 403) cStatus = "Forbidden";
2200 else if (nStatus == 404) cStatus = "Not Found";
2201 else if (nStatus == 500) cStatus = "Internal Server Error";
2204 "HTTP/1.1 %d %s\r\n"
2206 "Connection: close\r\n"
2207 "Content-Length: %d\r\n"
2208 "Content-Type: application/json\r\n"
2209 "Server: ppcoin-json-rpc/%s\r\n"
2214 rfc1123Time().c_str(),
2216 FormatFullVersion().c_str(),
2220 int ReadHTTPStatus(std::basic_istream<char>& stream)
2223 getline(stream, str);
2224 vector<string> vWords;
2225 boost::split(vWords, str, boost::is_any_of(" "));
2226 if (vWords.size() < 2)
2228 return atoi(vWords[1].c_str());
2231 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2237 std::getline(stream, str);
2238 if (str.empty() || str == "\r")
2240 string::size_type nColon = str.find(":");
2241 if (nColon != string::npos)
2243 string strHeader = str.substr(0, nColon);
2244 boost::trim(strHeader);
2245 boost::to_lower(strHeader);
2246 string strValue = str.substr(nColon+1);
2247 boost::trim(strValue);
2248 mapHeadersRet[strHeader] = strValue;
2249 if (strHeader == "content-length")
2250 nLen = atoi(strValue.c_str());
2256 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2258 mapHeadersRet.clear();
2262 int nStatus = ReadHTTPStatus(stream);
2265 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2266 if (nLen < 0 || nLen > MAX_SIZE)
2272 vector<char> vch(nLen);
2273 stream.read(&vch[0], nLen);
2274 strMessageRet = string(vch.begin(), vch.end());
2280 bool HTTPAuthorized(map<string, string>& mapHeaders)
2282 string strAuth = mapHeaders["authorization"];
2283 if (strAuth.substr(0,6) != "Basic ")
2285 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2286 string strUserPass = DecodeBase64(strUserPass64);
2287 return strUserPass == strRPCUserColonPass;
2291 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2292 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2293 // unspecified (HTTP errors and contents of 'error').
2295 // 1.0 spec: http://json-rpc.org/wiki/specification
2296 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2297 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2300 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2303 request.push_back(Pair("method", strMethod));
2304 request.push_back(Pair("params", params));
2305 request.push_back(Pair("id", id));
2306 return write_string(Value(request), false) + "\n";
2309 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2312 if (error.type() != null_type)
2313 reply.push_back(Pair("result", Value::null));
2315 reply.push_back(Pair("result", result));
2316 reply.push_back(Pair("error", error));
2317 reply.push_back(Pair("id", id));
2318 return write_string(Value(reply), false) + "\n";
2321 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2323 // Send error reply from json-rpc error object
2325 int code = find_value(objError, "code").get_int();
2326 if (code == -32600) nStatus = 400;
2327 else if (code == -32601) nStatus = 404;
2328 string strReply = JSONRPCReply(Value::null, objError, id);
2329 stream << HTTPReply(nStatus, strReply) << std::flush;
2332 bool ClientAllowed(const string& strAddress)
2334 if (strAddress == asio::ip::address_v4::loopback().to_string())
2336 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2337 BOOST_FOREACH(string strAllow, vAllow)
2338 if (WildcardMatch(strAddress, strAllow))
2345 // IOStream device that speaks SSL but can also speak non-SSL
2347 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2349 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2351 fUseSSL = fUseSSLIn;
2352 fNeedHandshake = fUseSSLIn;
2355 void handshake(ssl::stream_base::handshake_type role)
2357 if (!fNeedHandshake) return;
2358 fNeedHandshake = false;
2359 stream.handshake(role);
2361 std::streamsize read(char* s, std::streamsize n)
2363 handshake(ssl::stream_base::server); // HTTPS servers read first
2364 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2365 return stream.next_layer().read_some(asio::buffer(s, n));
2367 std::streamsize write(const char* s, std::streamsize n)
2369 handshake(ssl::stream_base::client); // HTTPS clients write first
2370 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2371 return asio::write(stream.next_layer(), asio::buffer(s, n));
2373 bool connect(const std::string& server, const std::string& port)
2375 ip::tcp::resolver resolver(stream.get_io_service());
2376 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2377 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2378 ip::tcp::resolver::iterator end;
2379 boost::system::error_code error = asio::error::host_not_found;
2380 while (error && endpoint_iterator != end)
2382 stream.lowest_layer().close();
2383 stream.lowest_layer().connect(*endpoint_iterator++, error);
2391 bool fNeedHandshake;
2397 void ThreadRPCServer(void* parg)
2399 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2402 vnThreadsRunning[4]++;
2403 ThreadRPCServer2(parg);
2404 vnThreadsRunning[4]--;
2406 catch (std::exception& e) {
2407 vnThreadsRunning[4]--;
2408 PrintException(&e, "ThreadRPCServer()");
2410 vnThreadsRunning[4]--;
2411 PrintException(NULL, "ThreadRPCServer()");
2413 printf("ThreadRPCServer exiting\n");
2416 void ThreadRPCServer2(void* parg)
2418 printf("ThreadRPCServer started\n");
2420 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2421 if (strRPCUserColonPass == ":")
2423 string strWhatAmI = "To use ppcoind";
2424 if (mapArgs.count("-server"))
2425 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2426 else if (mapArgs.count("-daemon"))
2427 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2429 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2430 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2432 GetConfigFile().c_str());
2434 CreateThread(Shutdown, NULL);
2439 bool fUseSSL = GetBoolArg("-rpcssl");
2440 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2442 asio::io_service io_service;
2443 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2444 ip::tcp::acceptor acceptor(io_service, endpoint);
2446 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2449 ssl::context context(io_service, ssl::context::sslv23);
2452 context.set_options(ssl::context::no_sslv2);
2453 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2454 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2455 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2456 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2457 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2458 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2459 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2460 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2462 string ciphers = GetArg("-rpcsslciphers",
2463 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2464 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2468 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2473 // Accept connection
2475 SSLStream sslStream(io_service, context);
2476 SSLIOStreamDevice d(sslStream, fUseSSL);
2477 iostreams::stream<SSLIOStreamDevice> stream(d);
2479 ip::tcp::iostream stream;
2482 ip::tcp::endpoint peer;
2483 vnThreadsRunning[4]--;
2485 acceptor.accept(sslStream.lowest_layer(), peer);
2487 acceptor.accept(*stream.rdbuf(), peer);
2489 vnThreadsRunning[4]++;
2493 // Restrict callers by IP
2494 if (!ClientAllowed(peer.address().to_string()))
2496 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2498 stream << HTTPReply(403, "") << std::flush;
2502 map<string, string> mapHeaders;
2505 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2506 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2509 printf("ThreadRPCServer ReadHTTP timeout\n");
2513 // Check authorization
2514 if (mapHeaders.count("authorization") == 0)
2516 stream << HTTPReply(401, "") << std::flush;
2519 if (!HTTPAuthorized(mapHeaders))
2521 // Deter brute-forcing short passwords
2522 if (mapArgs["-rpcpassword"].size() < 15)
2525 stream << HTTPReply(401, "") << std::flush;
2526 printf("ThreadRPCServer incorrect password attempt\n");
2530 Value id = Value::null;
2535 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2536 throw JSONRPCError(-32700, "Parse error");
2537 const Object& request = valRequest.get_obj();
2539 // Parse id now so errors from here on will have the id
2540 id = find_value(request, "id");
2543 Value valMethod = find_value(request, "method");
2544 if (valMethod.type() == null_type)
2545 throw JSONRPCError(-32600, "Missing method");
2546 if (valMethod.type() != str_type)
2547 throw JSONRPCError(-32600, "Method must be a string");
2548 string strMethod = valMethod.get_str();
2549 if (strMethod != "getwork" && strMethod != "getmemorypool")
2550 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2553 Value valParams = find_value(request, "params");
2555 if (valParams.type() == array_type)
2556 params = valParams.get_array();
2557 else if (valParams.type() == null_type)
2560 throw JSONRPCError(-32600, "Params must be an array");
2563 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2564 if (mi == mapCallTable.end())
2565 throw JSONRPCError(-32601, "Method not found");
2567 // Observe safe mode
2568 string strWarning = GetWarnings("rpc");
2569 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2570 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2576 CRITICAL_BLOCK(cs_main)
2577 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2578 result = (*(*mi).second)(params, false);
2581 string strReply = JSONRPCReply(result, Value::null, id);
2582 stream << HTTPReply(200, strReply) << std::flush;
2584 catch (std::exception& e)
2586 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2589 catch (Object& objError)
2591 ErrorReply(stream, objError, id);
2593 catch (std::exception& e)
2595 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2603 Object CallRPC(const string& strMethod, const Array& params)
2605 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2606 throw runtime_error(strprintf(
2607 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2608 "If the file does not exist, create it with owner-readable-only file permissions."),
2609 GetConfigFile().c_str()));
2611 // Connect to localhost
2612 bool fUseSSL = GetBoolArg("-rpcssl");
2614 asio::io_service io_service;
2615 ssl::context context(io_service, ssl::context::sslv23);
2616 context.set_options(ssl::context::no_sslv2);
2617 SSLStream sslStream(io_service, context);
2618 SSLIOStreamDevice d(sslStream, fUseSSL);
2619 iostreams::stream<SSLIOStreamDevice> stream(d);
2620 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2621 throw runtime_error("couldn't connect to server");
2624 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2626 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2628 throw runtime_error("couldn't connect to server");
2632 // HTTP basic authentication
2633 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2634 map<string, string> mapRequestHeaders;
2635 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2638 string strRequest = JSONRPCRequest(strMethod, params, 1);
2639 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2640 stream << strPost << std::flush;
2643 map<string, string> mapHeaders;
2645 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2647 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2648 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2649 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2650 else if (strReply.empty())
2651 throw runtime_error("no response from server");
2655 if (!read_string(strReply, valReply))
2656 throw runtime_error("couldn't parse reply from server");
2657 const Object& reply = valReply.get_obj();
2659 throw runtime_error("expected reply to have result, error and id properties");
2667 template<typename T>
2668 void ConvertTo(Value& value)
2670 if (value.type() == str_type)
2672 // reinterpret string as unquoted json value
2674 if (!read_string(value.get_str(), value2))
2675 throw runtime_error("type mismatch");
2676 value = value2.get_value<T>();
2680 value = value.get_value<T>();
2684 int CommandLineRPC(int argc, char *argv[])
2691 while (argc > 1 && IsSwitchChar(argv[1][0]))
2699 throw runtime_error("too few parameters");
2700 string strMethod = argv[1];
2702 // Parameters default to strings
2704 for (int i = 2; i < argc; i++)
2705 params.push_back(argv[i]);
2706 int n = params.size();
2709 // Special case non-string parameter types
2711 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2712 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2713 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2714 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2715 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2716 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2717 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2718 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2719 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2720 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2721 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2722 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2723 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2724 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2725 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2726 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2727 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2728 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2729 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2730 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2731 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2732 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2733 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2734 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2735 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2736 if (strMethod == "sendmany" && n > 1)
2738 string s = params[1].get_str();
2740 if (!read_string(s, v) || v.type() != obj_type)
2741 throw runtime_error("type mismatch");
2742 params[1] = v.get_obj();
2744 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2745 if (strMethod == "resetcheckpoint" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2746 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2747 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2750 Object reply = CallRPC(strMethod, params);
2753 const Value& result = find_value(reply, "result");
2754 const Value& error = find_value(reply, "error");
2756 if (error.type() != null_type)
2759 strPrint = "error: " + write_string(error, false);
2760 int code = find_value(error.get_obj(), "code").get_int();
2766 if (result.type() == null_type)
2768 else if (result.type() == str_type)
2769 strPrint = result.get_str();
2771 strPrint = write_string(result, true);
2774 catch (std::exception& e)
2776 strPrint = string("error: ") + e.what();
2781 PrintException(NULL, "CommandLineRPC()");
2786 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2795 int main(int argc, char *argv[])
2798 // Turn off microsoft heap dump noise
2799 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2800 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2802 setbuf(stdin, NULL);
2803 setbuf(stdout, NULL);
2804 setbuf(stderr, NULL);
2808 if (argc >= 2 && string(argv[1]) == "-server")
2810 printf("server ready\n");
2811 ThreadRPCServer(NULL);
2815 return CommandLineRPC(argc, argv);
2818 catch (std::exception& e) {
2819 PrintException(&e, "main()");
2821 PrintException(NULL, "main()");