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 const CBlockIndex* pindexLastProofOfWork = GetLastBlockIndex(pindexBest, false);
220 int nShift = (pindexLastProofOfWork->nBits >> 24) & 0xff;
223 (double)0x0000ffff / (double)(pindexLastProofOfWork->nBits & 0x00ffffff);
239 Value getdifficulty(const Array& params, bool fHelp)
241 if (fHelp || params.size() != 0)
244 "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.");
246 return GetDifficulty();
250 Value getgenerate(const Array& params, bool fHelp)
252 if (fHelp || params.size() != 0)
255 "Returns true or false.");
257 return (bool)fGenerateBitcoins;
261 Value setgenerate(const Array& params, bool fHelp)
263 if (fHelp || params.size() < 1 || params.size() > 2)
265 "setgenerate <generate> [genproclimit]\n"
266 "<generate> is true or false to turn generation on or off.\n"
267 "Generation is limited to [genproclimit] processors, -1 is unlimited.");
269 bool fGenerate = true;
270 if (params.size() > 0)
271 fGenerate = params[0].get_bool();
273 if (params.size() > 1)
275 int nGenProcLimit = params[1].get_int();
276 fLimitProcessors = (nGenProcLimit != -1);
277 WriteSetting("fLimitProcessors", fLimitProcessors);
278 if (nGenProcLimit != -1)
279 WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
280 if (nGenProcLimit == 0)
284 GenerateBitcoins(fGenerate, pwalletMain);
289 Value gethashespersec(const Array& params, bool fHelp)
291 if (fHelp || params.size() != 0)
294 "Returns a recent hashes per second performance measurement while generating.");
296 if (GetTimeMillis() - nHPSTimerStart > 8000)
297 return (boost::int64_t)0;
298 return (boost::int64_t)dHashesPerSec;
302 Value getinfo(const Array& params, bool fHelp)
304 if (fHelp || params.size() != 0)
307 "Returns an object containing various state info.");
310 obj.push_back(Pair("version", FormatFullVersion()));
311 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
312 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
313 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
314 obj.push_back(Pair("blocks", (int)nBestHeight));
315 obj.push_back(Pair("connections", (int)vNodes.size()));
316 obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
317 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
318 obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
319 obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
320 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
321 obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
322 obj.push_back(Pair("testnet", fTestNet));
323 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
324 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
325 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
326 if (pwalletMain->IsCrypted())
327 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
328 obj.push_back(Pair("errors", GetWarnings("statusbar")));
333 Value getnewaddress(const Array& params, bool fHelp)
335 if (fHelp || params.size() > 1)
337 "getnewaddress [account]\n"
338 "Returns a new ppcoin address for receiving payments. "
339 "If [account] is specified (recommended), it is added to the address book "
340 "so payments received with the address will be credited to [account].");
342 // Parse the account first so we don't generate a key if there's an error
344 if (params.size() > 0)
345 strAccount = AccountFromValue(params[0]);
347 if (!pwalletMain->IsLocked())
348 pwalletMain->TopUpKeyPool();
350 // Generate a new key that is added to wallet
351 std::vector<unsigned char> newKey;
352 if (!pwalletMain->GetKeyFromPool(newKey, false))
353 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
354 CBitcoinAddress address(newKey);
356 pwalletMain->SetAddressBookName(address, strAccount);
358 return address.ToString();
362 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
364 CWalletDB walletdb(pwalletMain->strWalletFile);
367 walletdb.ReadAccount(strAccount, account);
369 bool bKeyUsed = false;
371 // Check if the current key has been used
372 if (!account.vchPubKey.empty())
374 CScript scriptPubKey;
375 scriptPubKey.SetBitcoinAddress(account.vchPubKey);
376 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
377 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
380 const CWalletTx& wtx = (*it).second;
381 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
382 if (txout.scriptPubKey == scriptPubKey)
387 // Generate a new key
388 if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
390 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
391 throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
393 pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
394 walletdb.WriteAccount(strAccount, account);
397 return CBitcoinAddress(account.vchPubKey);
400 Value getaccountaddress(const Array& params, bool fHelp)
402 if (fHelp || params.size() != 1)
404 "getaccountaddress <account>\n"
405 "Returns the current ppcoin address for receiving payments to this account.");
407 // Parse the account first so we don't generate a key if there's an error
408 string strAccount = AccountFromValue(params[0]);
412 ret = GetAccountAddress(strAccount).ToString();
419 Value setaccount(const Array& params, bool fHelp)
421 if (fHelp || params.size() < 1 || params.size() > 2)
423 "setaccount <ppcoinaddress> <account>\n"
424 "Sets the account associated with the given address.");
426 CBitcoinAddress address(params[0].get_str());
427 if (!address.IsValid())
428 throw JSONRPCError(-5, "Invalid ppcoin address");
432 if (params.size() > 1)
433 strAccount = AccountFromValue(params[1]);
435 // Detect when changing the account of an address that is the 'unused current key' of another account:
436 if (pwalletMain->mapAddressBook.count(address))
438 string strOldAccount = pwalletMain->mapAddressBook[address];
439 if (address == GetAccountAddress(strOldAccount))
440 GetAccountAddress(strOldAccount, true);
443 pwalletMain->SetAddressBookName(address, strAccount);
449 Value getaccount(const Array& params, bool fHelp)
451 if (fHelp || params.size() != 1)
453 "getaccount <ppcoinaddress>\n"
454 "Returns the account associated with the given address.");
456 CBitcoinAddress address(params[0].get_str());
457 if (!address.IsValid())
458 throw JSONRPCError(-5, "Invalid ppcoin address");
461 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
462 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
463 strAccount = (*mi).second;
468 Value getaddressesbyaccount(const Array& params, bool fHelp)
470 if (fHelp || params.size() != 1)
472 "getaddressesbyaccount <account>\n"
473 "Returns the list of addresses for the given account.");
475 string strAccount = AccountFromValue(params[0]);
477 // Find all addresses that have the given account
479 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
481 const CBitcoinAddress& address = item.first;
482 const string& strName = item.second;
483 if (strName == strAccount)
484 ret.push_back(address.ToString());
489 Value settxfee(const Array& params, bool fHelp)
491 if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
493 "settxfee <amount>\n"
494 "<amount> is a real and is rounded to 0.01 (cent)\n"
495 "Minimum and default transaction fee per KB is 1 cent");
497 nTransactionFee = AmountFromValue(params[0]);
498 nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
502 Value sendtoaddress(const Array& params, bool fHelp)
504 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
506 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
507 "<amount> is a real and is rounded to the nearest 0.000001\n"
508 "requires wallet passphrase to be set with walletpassphrase first");
509 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
511 "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
512 "<amount> is a real and is rounded to the nearest 0.000001");
514 CBitcoinAddress address(params[0].get_str());
515 if (!address.IsValid())
516 throw JSONRPCError(-5, "Invalid ppcoin address");
519 int64 nAmount = AmountFromValue(params[1]);
523 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
524 wtx.mapValue["comment"] = params[2].get_str();
525 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
526 wtx.mapValue["to"] = params[3].get_str();
528 if (pwalletMain->IsLocked())
529 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
531 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
533 throw JSONRPCError(-4, strError);
535 return wtx.GetHash().GetHex();
538 static const string strMessageMagic = "Bitcoin Signed Message:\n";
540 Value signmessage(const Array& params, bool fHelp)
542 if (fHelp || params.size() != 2)
544 "signmessage <ppcoinaddress> <message>\n"
545 "Sign a message with the private key of an address");
547 if (pwalletMain->IsLocked())
548 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
550 string strAddress = params[0].get_str();
551 string strMessage = params[1].get_str();
553 CBitcoinAddress addr(strAddress);
555 throw JSONRPCError(-3, "Invalid address");
558 if (!pwalletMain->GetKey(addr, key))
559 throw JSONRPCError(-4, "Private key not available");
561 CDataStream ss(SER_GETHASH);
562 ss << strMessageMagic;
565 vector<unsigned char> vchSig;
566 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
567 throw JSONRPCError(-5, "Sign failed");
569 return EncodeBase64(&vchSig[0], vchSig.size());
572 Value verifymessage(const Array& params, bool fHelp)
574 if (fHelp || params.size() != 3)
576 "verifymessage <ppcoinaddress> <signature> <message>\n"
577 "Verify a signed message");
579 string strAddress = params[0].get_str();
580 string strSign = params[1].get_str();
581 string strMessage = params[2].get_str();
583 CBitcoinAddress addr(strAddress);
585 throw JSONRPCError(-3, "Invalid address");
587 bool fInvalid = false;
588 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
591 throw JSONRPCError(-5, "Malformed base64 encoding");
593 CDataStream ss(SER_GETHASH);
594 ss << strMessageMagic;
598 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
601 return (key.GetAddress() == addr);
605 Value getreceivedbyaddress(const Array& params, bool fHelp)
607 if (fHelp || params.size() < 1 || params.size() > 2)
609 "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
610 "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
613 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
614 CScript scriptPubKey;
615 if (!address.IsValid())
616 throw JSONRPCError(-5, "Invalid ppcoin address");
617 scriptPubKey.SetBitcoinAddress(address);
618 if (!IsMine(*pwalletMain,scriptPubKey))
621 // Minimum confirmations
623 if (params.size() > 1)
624 nMinDepth = params[1].get_int();
628 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
630 const CWalletTx& wtx = (*it).second;
631 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
634 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
635 if (txout.scriptPubKey == scriptPubKey)
636 if (wtx.GetDepthInMainChain() >= nMinDepth)
637 nAmount += txout.nValue;
640 return ValueFromAmount(nAmount);
644 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
646 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
648 const CBitcoinAddress& address = item.first;
649 const string& strName = item.second;
650 if (strName == strAccount)
651 setAddress.insert(address);
656 Value getreceivedbyaccount(const Array& params, bool fHelp)
658 if (fHelp || params.size() < 1 || params.size() > 2)
660 "getreceivedbyaccount <account> [minconf=1]\n"
661 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
663 // Minimum confirmations
665 if (params.size() > 1)
666 nMinDepth = params[1].get_int();
668 // Get the set of pub keys that have the label
669 string strAccount = AccountFromValue(params[0]);
670 set<CBitcoinAddress> setAddress;
671 GetAccountAddresses(strAccount, setAddress);
675 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
677 const CWalletTx& wtx = (*it).second;
678 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
681 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
683 CBitcoinAddress address;
684 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address))
685 if (wtx.GetDepthInMainChain() >= nMinDepth)
686 nAmount += txout.nValue;
690 return (double)nAmount / (double)COIN;
694 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
698 // Tally wallet transactions
699 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
701 const CWalletTx& wtx = (*it).second;
705 int64 nGenerated, nReceived, nSent, nFee;
706 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
708 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
709 nBalance += nReceived;
710 nBalance += nGenerated - nSent - nFee;
713 // Tally internal accounting entries
714 nBalance += walletdb.GetAccountCreditDebit(strAccount);
719 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
721 CWalletDB walletdb(pwalletMain->strWalletFile);
722 return GetAccountBalance(walletdb, strAccount, nMinDepth);
726 Value getbalance(const Array& params, bool fHelp)
728 if (fHelp || params.size() > 2)
730 "getbalance [account] [minconf=1]\n"
731 "If [account] is not specified, returns the server's total available balance.\n"
732 "If [account] is specified, returns the balance in the account.");
734 if (params.size() == 0)
735 return ValueFromAmount(pwalletMain->GetBalance());
738 if (params.size() > 1)
739 nMinDepth = params[1].get_int();
741 if (params[0].get_str() == "*") {
742 // Calculate total balance a different way from GetBalance()
743 // (GetBalance() sums up all unspent TxOuts)
744 // getbalance and getbalance '*' should always return the same number.
746 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
748 const CWalletTx& wtx = (*it).second;
752 int64 allGeneratedImmature, allGeneratedMature, allFee;
753 allGeneratedImmature = allGeneratedMature = allFee = 0;
754 string strSentAccount;
755 list<pair<CBitcoinAddress, int64> > listReceived;
756 list<pair<CBitcoinAddress, int64> > listSent;
757 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
758 if (wtx.GetDepthInMainChain() >= nMinDepth)
759 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
760 nBalance += r.second;
761 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
762 nBalance -= r.second;
764 nBalance += allGeneratedMature;
766 return ValueFromAmount(nBalance);
769 string strAccount = AccountFromValue(params[0]);
771 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
773 return ValueFromAmount(nBalance);
777 Value movecmd(const Array& params, bool fHelp)
779 if (fHelp || params.size() < 3 || params.size() > 5)
781 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
782 "Move from one account in your wallet to another.");
784 string strFrom = AccountFromValue(params[0]);
785 string strTo = AccountFromValue(params[1]);
786 int64 nAmount = AmountFromValue(params[2]);
787 if (params.size() > 3)
788 // unused parameter, used to be nMinDepth, keep type-checking it though
789 (void)params[3].get_int();
791 if (params.size() > 4)
792 strComment = params[4].get_str();
794 CWalletDB walletdb(pwalletMain->strWalletFile);
797 int64 nNow = GetAdjustedTime();
800 CAccountingEntry debit;
801 debit.strAccount = strFrom;
802 debit.nCreditDebit = -nAmount;
804 debit.strOtherAccount = strTo;
805 debit.strComment = strComment;
806 walletdb.WriteAccountingEntry(debit);
809 CAccountingEntry credit;
810 credit.strAccount = strTo;
811 credit.nCreditDebit = nAmount;
813 credit.strOtherAccount = strFrom;
814 credit.strComment = strComment;
815 walletdb.WriteAccountingEntry(credit);
817 walletdb.TxnCommit();
823 Value sendfrom(const Array& params, bool fHelp)
825 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
827 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
828 "<amount> is a real and is rounded to the nearest 0.000001\n"
829 "requires wallet passphrase to be set with walletpassphrase first");
830 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
832 "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
833 "<amount> is a real and is rounded to the nearest 0.000001");
835 string strAccount = AccountFromValue(params[0]);
836 CBitcoinAddress address(params[1].get_str());
837 if (!address.IsValid())
838 throw JSONRPCError(-5, "Invalid ppcoin address");
839 int64 nAmount = AmountFromValue(params[2]);
841 if (params.size() > 3)
842 nMinDepth = params[3].get_int();
845 wtx.strFromAccount = strAccount;
846 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
847 wtx.mapValue["comment"] = params[4].get_str();
848 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
849 wtx.mapValue["to"] = params[5].get_str();
851 if (pwalletMain->IsLocked())
852 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
855 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
856 if (nAmount > nBalance)
857 throw JSONRPCError(-6, "Account has insufficient funds");
860 string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
862 throw JSONRPCError(-4, strError);
864 return wtx.GetHash().GetHex();
868 Value sendmany(const Array& params, bool fHelp)
870 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
872 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
873 "amounts are double-precision floating point numbers\n"
874 "requires wallet passphrase to be set with walletpassphrase first");
875 if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
877 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
878 "amounts are double-precision floating point numbers");
880 string strAccount = AccountFromValue(params[0]);
881 Object sendTo = params[1].get_obj();
883 if (params.size() > 2)
884 nMinDepth = params[2].get_int();
887 wtx.strFromAccount = strAccount;
888 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
889 wtx.mapValue["comment"] = params[3].get_str();
891 set<CBitcoinAddress> setAddress;
892 vector<pair<CScript, int64> > vecSend;
894 int64 totalAmount = 0;
895 BOOST_FOREACH(const Pair& s, sendTo)
897 CBitcoinAddress address(s.name_);
898 if (!address.IsValid())
899 throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
901 if (setAddress.count(address))
902 throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
903 setAddress.insert(address);
905 CScript scriptPubKey;
906 scriptPubKey.SetBitcoinAddress(address);
907 int64 nAmount = AmountFromValue(s.value_);
908 totalAmount += nAmount;
910 vecSend.push_back(make_pair(scriptPubKey, nAmount));
913 if (pwalletMain->IsLocked())
914 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
915 if (fWalletUnlockStakeOnly)
916 throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
919 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
920 if (totalAmount > nBalance)
921 throw JSONRPCError(-6, "Account has insufficient funds");
924 CReserveKey keyChange(pwalletMain);
925 int64 nFeeRequired = 0;
926 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
929 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
930 throw JSONRPCError(-6, "Insufficient funds");
931 throw JSONRPCError(-4, "Transaction creation failed");
933 if (!pwalletMain->CommitTransaction(wtx, keyChange))
934 throw JSONRPCError(-4, "Transaction commit failed");
936 return wtx.GetHash().GetHex();
951 Value ListReceived(const Array& params, bool fByAccounts)
953 // Minimum confirmations
955 if (params.size() > 0)
956 nMinDepth = params[0].get_int();
958 // Whether to include empty accounts
959 bool fIncludeEmpty = false;
960 if (params.size() > 1)
961 fIncludeEmpty = params[1].get_bool();
964 map<CBitcoinAddress, tallyitem> mapTally;
965 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
967 const CWalletTx& wtx = (*it).second;
968 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
971 int nDepth = wtx.GetDepthInMainChain();
972 if (nDepth < nMinDepth)
975 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
977 CBitcoinAddress address;
978 if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid())
981 tallyitem& item = mapTally[address];
982 item.nAmount += txout.nValue;
983 item.nConf = min(item.nConf, nDepth);
989 map<string, tallyitem> mapAccountTally;
990 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
992 const CBitcoinAddress& address = item.first;
993 const string& strAccount = item.second;
994 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
995 if (it == mapTally.end() && !fIncludeEmpty)
1000 if (it != mapTally.end())
1002 nAmount = (*it).second.nAmount;
1003 nConf = (*it).second.nConf;
1008 tallyitem& item = mapAccountTally[strAccount];
1009 item.nAmount += nAmount;
1010 item.nConf = min(item.nConf, nConf);
1015 obj.push_back(Pair("address", address.ToString()));
1016 obj.push_back(Pair("account", strAccount));
1017 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1018 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1025 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1027 int64 nAmount = (*it).second.nAmount;
1028 int nConf = (*it).second.nConf;
1030 obj.push_back(Pair("account", (*it).first));
1031 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1032 obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
1040 Value listreceivedbyaddress(const Array& params, bool fHelp)
1042 if (fHelp || params.size() > 2)
1043 throw runtime_error(
1044 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1045 "[minconf] is the minimum number of confirmations before payments are included.\n"
1046 "[includeempty] whether to include addresses that haven't received any payments.\n"
1047 "Returns an array of objects containing:\n"
1048 " \"address\" : receiving address\n"
1049 " \"account\" : the account of the receiving address\n"
1050 " \"amount\" : total amount received by the address\n"
1051 " \"confirmations\" : number of confirmations of the most recent transaction included");
1053 return ListReceived(params, false);
1056 Value listreceivedbyaccount(const Array& params, bool fHelp)
1058 if (fHelp || params.size() > 2)
1059 throw runtime_error(
1060 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1061 "[minconf] is the minimum number of confirmations before payments are included.\n"
1062 "[includeempty] whether to include accounts that haven't received any payments.\n"
1063 "Returns an array of objects containing:\n"
1064 " \"account\" : the account of the receiving addresses\n"
1065 " \"amount\" : total amount received by addresses with this account\n"
1066 " \"confirmations\" : number of confirmations of the most recent transaction included");
1068 return ListReceived(params, true);
1071 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1073 int64 nGeneratedImmature, nGeneratedMature, nFee;
1074 string strSentAccount;
1075 list<pair<CBitcoinAddress, int64> > listReceived;
1076 list<pair<CBitcoinAddress, int64> > listSent;
1077 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1079 bool fAllAccounts = (strAccount == string("*"));
1081 // Generated blocks assigned to account ""
1082 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1085 entry.push_back(Pair("account", string("")));
1086 if (nGeneratedImmature)
1088 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1089 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1093 entry.push_back(Pair("category", "generate"));
1094 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1097 WalletTxToJSON(wtx, entry);
1098 ret.push_back(entry);
1102 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1104 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1107 entry.push_back(Pair("account", strSentAccount));
1108 entry.push_back(Pair("address", s.first.ToString()));
1109 entry.push_back(Pair("category", "send"));
1110 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1111 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1113 WalletTxToJSON(wtx, entry);
1114 ret.push_back(entry);
1119 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1120 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1123 if (pwalletMain->mapAddressBook.count(r.first))
1124 account = pwalletMain->mapAddressBook[r.first];
1125 if (fAllAccounts || (account == strAccount))
1128 entry.push_back(Pair("account", account));
1129 entry.push_back(Pair("address", r.first.ToString()));
1130 entry.push_back(Pair("category", "receive"));
1131 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1133 WalletTxToJSON(wtx, entry);
1134 ret.push_back(entry);
1139 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1141 bool fAllAccounts = (strAccount == string("*"));
1143 if (fAllAccounts || acentry.strAccount == strAccount)
1146 entry.push_back(Pair("account", acentry.strAccount));
1147 entry.push_back(Pair("category", "move"));
1148 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1149 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1150 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1151 entry.push_back(Pair("comment", acentry.strComment));
1152 ret.push_back(entry);
1156 Value listtransactions(const Array& params, bool fHelp)
1158 if (fHelp || params.size() > 3)
1159 throw runtime_error(
1160 "listtransactions [account] [count=10] [from=0]\n"
1161 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1163 string strAccount = "*";
1164 if (params.size() > 0)
1165 strAccount = params[0].get_str();
1167 if (params.size() > 1)
1168 nCount = params[1].get_int();
1170 if (params.size() > 2)
1171 nFrom = params[2].get_int();
1174 CWalletDB walletdb(pwalletMain->strWalletFile);
1176 // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
1177 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
1178 typedef multimap<int64, TxPair > TxItems;
1181 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1183 CWalletTx* wtx = &((*it).second);
1184 txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0)));
1186 list<CAccountingEntry> acentries;
1187 walletdb.ListAccountCreditDebit(strAccount, acentries);
1188 BOOST_FOREACH(CAccountingEntry& entry, acentries)
1190 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
1193 // Now: iterate backwards until we have nCount items to return:
1194 TxItems::reverse_iterator it = txByTime.rbegin();
1195 if (txByTime.size() > nFrom) std::advance(it, nFrom);
1196 for (; it != txByTime.rend(); ++it)
1198 CWalletTx *const pwtx = (*it).second.first;
1200 ListTransactions(*pwtx, strAccount, 0, true, ret);
1201 CAccountingEntry *const pacentry = (*it).second.second;
1203 AcentryToJSON(*pacentry, strAccount, ret);
1205 if (ret.size() >= nCount) break;
1207 // ret is now newest to oldest
1209 // Make sure we return only last nCount items (sends-to-self might give us an extra):
1210 if (ret.size() > nCount)
1212 Array::iterator last = ret.begin();
1213 std::advance(last, nCount);
1214 ret.erase(last, ret.end());
1216 std::reverse(ret.begin(), ret.end()); // oldest to newest
1221 Value listaccounts(const Array& params, bool fHelp)
1223 if (fHelp || params.size() > 1)
1224 throw runtime_error(
1225 "listaccounts [minconf=1]\n"
1226 "Returns Object that has account names as keys, account balances as values.");
1229 if (params.size() > 0)
1230 nMinDepth = params[0].get_int();
1232 map<string, int64> mapAccountBalances;
1233 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
1234 if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
1235 mapAccountBalances[entry.second] = 0;
1238 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1240 const CWalletTx& wtx = (*it).second;
1241 int64 nGeneratedImmature, nGeneratedMature, nFee;
1242 string strSentAccount;
1243 list<pair<CBitcoinAddress, int64> > listReceived;
1244 list<pair<CBitcoinAddress, int64> > listSent;
1245 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1246 mapAccountBalances[strSentAccount] -= nFee;
1247 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
1248 mapAccountBalances[strSentAccount] -= s.second;
1249 if (wtx.GetDepthInMainChain() >= nMinDepth)
1251 mapAccountBalances[""] += nGeneratedMature;
1252 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
1253 if (pwalletMain->mapAddressBook.count(r.first))
1254 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1256 mapAccountBalances[""] += r.second;
1260 list<CAccountingEntry> acentries;
1261 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1262 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1263 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1266 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1267 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1272 Value listsinceblock(const Array& params, bool fHelp)
1275 throw runtime_error(
1276 "listsinceblock [blockid] [target-confirmations]\n"
1277 "Get all transactions in blocks since block [blockid], or all transactions if omitted");
1279 CBlockIndex *pindex = NULL;
1280 int target_confirms = 1;
1282 if (params.size() > 0)
1284 uint256 blockId = 0;
1286 blockId.SetHex(params[0].get_str());
1287 pindex = CBlockLocator(blockId).GetBlockIndex();
1290 if (params.size() > 1)
1292 target_confirms = params[1].get_int();
1294 if (target_confirms < 1)
1295 throw JSONRPCError(-8, "Invalid parameter");
1298 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1302 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1304 CWalletTx tx = (*it).second;
1306 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1307 ListTransactions(tx, "*", 0, true, transactions);
1312 if (target_confirms == 1)
1315 lastblock = hashBestChain;
1319 int target_height = pindexBest->nHeight + 1 - target_confirms;
1322 for (block = pindexBest;
1323 block && block->nHeight > target_height;
1324 block = block->pprev);
1326 lastblock = block ? block->GetBlockHash() : 0;
1330 ret.push_back(Pair("transactions", transactions));
1331 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1336 Value gettransaction(const Array& params, bool fHelp)
1338 if (fHelp || params.size() != 1)
1339 throw runtime_error(
1340 "gettransaction <txid>\n"
1341 "Get detailed information about <txid>");
1344 hash.SetHex(params[0].get_str());
1348 if (!pwalletMain->mapWallet.count(hash))
1349 throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
1350 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1352 int64 nCredit = wtx.GetCredit();
1353 int64 nDebit = wtx.GetDebit();
1354 int64 nNet = nCredit - nDebit;
1355 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1357 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1359 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1361 WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
1364 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1365 entry.push_back(Pair("details", details));
1371 Value backupwallet(const Array& params, bool fHelp)
1373 if (fHelp || params.size() != 1)
1374 throw runtime_error(
1375 "backupwallet <destination>\n"
1376 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1378 string strDest = params[0].get_str();
1379 BackupWallet(*pwalletMain, strDest);
1385 Value keypoolrefill(const Array& params, bool fHelp)
1387 if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1388 throw runtime_error(
1390 "Fills the keypool, requires wallet passphrase to be set.");
1391 if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
1392 throw runtime_error(
1394 "Fills the keypool.");
1396 if (pwalletMain->IsLocked())
1397 throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
1399 pwalletMain->TopUpKeyPool();
1401 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1402 throw JSONRPCError(-4, "Error refreshing keypool.");
1408 void ThreadTopUpKeyPool(void* parg)
1410 pwalletMain->TopUpKeyPool();
1413 void ThreadCleanWalletPassphrase(void* parg)
1415 int64 nMyWakeTime = GetTime() + *((int*)parg);
1417 if (nWalletUnlockTime == 0)
1419 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1421 nWalletUnlockTime = nMyWakeTime;
1424 while (GetTime() < nWalletUnlockTime)
1425 Sleep(GetTime() - nWalletUnlockTime);
1427 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1429 nWalletUnlockTime = 0;
1434 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1436 if (nWalletUnlockTime < nMyWakeTime)
1437 nWalletUnlockTime = nMyWakeTime;
1443 pwalletMain->Lock();
1448 Value walletpassphrase(const Array& params, bool fHelp)
1450 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1451 throw runtime_error(
1452 "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
1453 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1454 "stakeonly is optional true/false allowing only stake creation.");
1457 if (!pwalletMain->IsCrypted())
1458 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1460 if (!pwalletMain->IsLocked())
1461 throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1463 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1464 SecureString strWalletPass;
1465 strWalletPass.reserve(100);
1466 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1467 // Alternately, find a way to make params[0] mlock()'d to begin with.
1468 strWalletPass = params[0].get_str().c_str();
1470 if (strWalletPass.length() > 0)
1472 if (!pwalletMain->Unlock(strWalletPass))
1473 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1476 throw runtime_error(
1477 "walletpassphrase <passphrase> <timeout>\n"
1478 "Stores the wallet decryption key in memory for <timeout> seconds.");
1480 CreateThread(ThreadTopUpKeyPool, NULL);
1481 int* pnSleepTime = new int(params[1].get_int());
1482 CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
1484 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1485 if (params.size() > 2)
1486 fWalletUnlockStakeOnly = params[2].get_bool();
1488 fWalletUnlockStakeOnly = false;
1494 Value walletpassphrasechange(const Array& params, bool fHelp)
1496 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1497 throw runtime_error(
1498 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1499 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1502 if (!pwalletMain->IsCrypted())
1503 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1505 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1506 // Alternately, find a way to make params[0] mlock()'d to begin with.
1507 SecureString strOldWalletPass;
1508 strOldWalletPass.reserve(100);
1509 strOldWalletPass = params[0].get_str().c_str();
1511 SecureString strNewWalletPass;
1512 strNewWalletPass.reserve(100);
1513 strNewWalletPass = params[1].get_str().c_str();
1515 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1516 throw runtime_error(
1517 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1518 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1520 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1521 throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
1527 Value walletlock(const Array& params, bool fHelp)
1529 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1530 throw runtime_error(
1532 "Removes the wallet encryption key from memory, locking the wallet.\n"
1533 "After calling this method, you will need to call walletpassphrase again\n"
1534 "before being able to call any methods which require the wallet to be unlocked.");
1537 if (!pwalletMain->IsCrypted())
1538 throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
1540 pwalletMain->Lock();
1541 CRITICAL_BLOCK(cs_nWalletUnlockTime)
1543 nWalletUnlockTime = 0;
1550 Value encryptwallet(const Array& params, bool fHelp)
1552 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1553 throw runtime_error(
1554 "encryptwallet <passphrase>\n"
1555 "Encrypts the wallet with <passphrase>.");
1558 if (pwalletMain->IsCrypted())
1559 throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
1562 // shutting down via RPC while the GUI is running does not work (yet):
1563 throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
1566 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1567 // Alternately, find a way to make params[0] mlock()'d to begin with.
1568 SecureString strWalletPass;
1569 strWalletPass.reserve(100);
1570 strWalletPass = params[0].get_str().c_str();
1572 if (strWalletPass.length() < 1)
1573 throw runtime_error(
1574 "encryptwallet <passphrase>\n"
1575 "Encrypts the wallet with <passphrase>.");
1577 if (!pwalletMain->EncryptWallet(strWalletPass))
1578 throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
1580 // BDB seems to have a bad habit of writing old data into
1581 // slack space in .dat files; that is bad if the old data is
1582 // unencrypted private keys. So:
1583 CreateThread(Shutdown, NULL);
1584 return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
1588 Value validateaddress(const Array& params, bool fHelp)
1590 if (fHelp || params.size() != 1)
1591 throw runtime_error(
1592 "validateaddress <ppcoinaddress>\n"
1593 "Return information about <ppcoinaddress>.");
1595 CBitcoinAddress address(params[0].get_str());
1596 bool isValid = address.IsValid();
1599 ret.push_back(Pair("isvalid", isValid));
1602 // Call Hash160ToAddress() so we always return current ADDRESSVERSION
1603 // version of the address:
1604 string currentAddress = address.ToString();
1605 ret.push_back(Pair("address", currentAddress));
1606 ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
1607 if (pwalletMain->mapAddressBook.count(address))
1608 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1614 Value getwork(const Array& params, bool fHelp)
1616 if (fHelp || params.size() > 1)
1617 throw runtime_error(
1619 "If [data] is not specified, returns formatted hash data to work on:\n"
1620 " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
1621 " \"data\" : block data\n"
1622 " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
1623 " \"target\" : little endian hash target\n"
1624 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1627 throw JSONRPCError(-9, "PPCoin is not connected!");
1629 if (IsInitialBlockDownload())
1630 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1632 typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
1633 static mapNewBlock_t mapNewBlock;
1634 static vector<CBlock*> vNewBlock;
1635 static CReserveKey reservekey(pwalletMain);
1637 if (params.size() == 0)
1640 static unsigned int nTransactionsUpdatedLast;
1641 static CBlockIndex* pindexPrev;
1642 static int64 nStart;
1643 static CBlock* pblock;
1644 if (pindexPrev != pindexBest ||
1645 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
1647 if (pindexPrev != pindexBest)
1649 // Deallocate old blocks since they're obsolete now
1650 mapNewBlock.clear();
1651 BOOST_FOREACH(CBlock* pblock, vNewBlock)
1655 nTransactionsUpdatedLast = nTransactionsUpdated;
1656 pindexPrev = pindexBest;
1660 pblock = CreateNewBlock(pwalletMain, true);
1662 throw JSONRPCError(-7, "Out of memory");
1663 vNewBlock.push_back(pblock);
1667 pblock->nTime = max(pblock->GetBlockTime(), GetAdjustedTime());
1670 // Update nExtraNonce
1671 static unsigned int nExtraNonce = 0;
1672 IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
1675 mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
1677 // Prebuild hash buffers
1681 FormatHashBuffers(pblock, pmidstate, pdata, phash1);
1683 uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
1686 result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
1687 result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
1688 result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
1689 result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
1695 vector<unsigned char> vchData = ParseHex(params[0].get_str());
1696 if (vchData.size() != 128)
1697 throw JSONRPCError(-8, "Invalid parameter");
1698 CBlock* pdata = (CBlock*)&vchData[0];
1701 for (int i = 0; i < 128/4; i++)
1702 ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
1705 if (!mapNewBlock.count(pdata->hashMerkleRoot))
1707 CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
1709 pblock->nTime = pdata->nTime;
1710 pblock->nNonce = pdata->nNonce;
1711 pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
1712 pblock->hashMerkleRoot = pblock->BuildMerkleTree();
1713 if (!pblock->SignBlock(*pwalletMain))
1714 throw JSONRPCError(-100, "Unable to sign block");
1716 return CheckWork(pblock, *pwalletMain, reservekey);
1721 Value getmemorypool(const Array& params, bool fHelp)
1723 if (fHelp || params.size() > 1)
1724 throw runtime_error(
1725 "getmemorypool [data]\n"
1726 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1727 " \"version\" : block version\n"
1728 " \"previousblockhash\" : hash of current highest block\n"
1729 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1730 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1731 " \"time\" : timestamp appropriate for next block\n"
1732 " \"bits\" : compressed target of next block\n"
1733 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1735 if (params.size() == 0)
1738 throw JSONRPCError(-9, "PPCoin is not connected!");
1740 if (IsInitialBlockDownload())
1741 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1743 static CReserveKey reservekey(pwalletMain);
1746 static unsigned int nTransactionsUpdatedLast;
1747 static CBlockIndex* pindexPrev;
1748 static int64 nStart;
1749 static CBlock* pblock;
1750 if (pindexPrev != pindexBest ||
1751 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1753 nTransactionsUpdatedLast = nTransactionsUpdated;
1754 pindexPrev = pindexBest;
1760 pblock = CreateNewBlock(pwalletMain);
1762 throw JSONRPCError(-7, "Out of memory");
1766 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1770 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1771 if(tx.IsCoinBase() || tx.IsCoinStake())
1777 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1781 result.push_back(Pair("version", pblock->nVersion));
1782 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1783 result.push_back(Pair("transactions", transactions));
1784 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1785 result.push_back(Pair("time", (int64_t)pblock->nTime));
1791 uBits.nBits = htonl((int32_t)pblock->nBits);
1792 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1799 CDataStream ssBlock(ParseHex(params[0].get_str()));
1803 return ProcessBlock(NULL, &pblock);
1808 // ppcoin: get information of sync-checkpoint
1809 Value getcheckpoint(const Array& params, bool fHelp)
1811 if (fHelp || params.size() != 0)
1812 throw runtime_error(
1814 "Show info of synchronized checkpoint.\n");
1817 CBlockIndex* pindexCheckpoint;
1819 result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
1820 pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint];
1821 result.push_back(Pair("height", pindexCheckpoint->nHeight));
1822 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str()));
1828 // ppcoin: reserve balance from being staked for network protection
1829 Value reservebalance(const Array& params, bool fHelp)
1831 if (fHelp || params.size() > 2)
1832 throw runtime_error(
1833 "reservebalance [<reserve> [amount]]\n"
1834 "<reserve> is true or false to turn balance reserve on or off.\n"
1835 "<amount> is a real and rounded to cent.\n"
1836 "Set reserve amount not participating in network protection.\n"
1837 "If no parameters provided current setting is printed.\n");
1839 if (params.size() > 0)
1841 bool fReserve = params[0].get_bool();
1844 if (params.size() == 1)
1845 throw runtime_error("must provide amount to reserve balance.\n");
1846 int64 nAmount = AmountFromValue(params[1]);
1847 nAmount = (nAmount / CENT) * CENT; // round to cent
1849 throw runtime_error("amount cannot be negative.\n");
1850 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1854 if (params.size() > 1)
1855 throw runtime_error("cannot specify amount to turn off reserve.\n");
1856 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1861 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1862 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1867 // ppcoin: check wallet integrity
1868 Value checkwallet(const Array& params, bool fHelp)
1870 if (fHelp || params.size() > 0)
1871 throw runtime_error(
1873 "Check wallet for integrity.\n");
1876 int64 nBalanceInQuestion;
1877 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1880 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1881 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1888 // ppcoin: repair wallet
1889 Value repairwallet(const Array& params, bool fHelp)
1891 if (fHelp || params.size() > 0)
1892 throw runtime_error(
1894 "Repair wallet if checkwallet reports any problem.\n");
1897 int64 nBalanceInQuestion;
1898 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1900 if (nMismatchSpent == 0)
1902 result.push_back(Pair("wallet check passed", true));
1906 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1907 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1912 // ppcoin: make a public-private key pair
1913 Value makekeypair(const Array& params, bool fHelp)
1915 if (fHelp || params.size() > 1)
1916 throw runtime_error(
1917 "makekeypair [prefix]\n"
1918 "Make a public/private key pair.\n"
1919 "[prefix] is optional preferred prefix for the public key.\n");
1921 string strPrefix = "";
1922 if (params.size() > 0)
1923 strPrefix = params[0].get_str();
1931 } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
1933 if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()))
1936 CPrivKey vchPrivKey = key.GetPrivKey();
1938 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1939 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1943 extern CCriticalSection cs_mapAlerts;
1944 extern map<uint256, CAlert> mapAlerts;
1946 // ppcoin: send alert.
1947 // There is a known deadlock situation with ThreadMessageHandler
1948 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1949 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1950 Value sendalert(const Array& params, bool fHelp)
1952 if (fHelp || params.size() < 5)
1953 throw runtime_error(
1954 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1955 "<message> is the alert text message\n"
1956 "<privatekey> is hex string of alert master private key\n"
1957 "<minver> is the minimum applicable client version\n"
1958 "<maxver> is the maximum applicable client version\n"
1959 "<id> is the alert id\n"
1960 "[cancelupto] cancels all alert id's up to this number\n"
1961 "Returns true or false.");
1966 alert.strStatusBar = params[0].get_str();
1967 alert.nMinVer = params[2].get_int();
1968 alert.nMaxVer = params[3].get_int();
1969 alert.nID = params[4].get_int();
1970 if (params.size() > 5)
1971 alert.nCancel = params[5].get_int();
1972 alert.nVersion = VERSION;
1973 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1974 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1975 alert.nPriority = 1;
1978 sMsg << (CUnsignedAlert)alert;
1979 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1981 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1982 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1983 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1984 throw runtime_error(
1985 "Unable to sign alert, check private key?\n");
1986 if(!alert.ProcessAlert())
1987 throw runtime_error(
1988 "Failed to process alert.\n");
1990 CRITICAL_BLOCK(cs_vNodes)
1991 BOOST_FOREACH(CNode* pnode, vNodes)
1992 alert.RelayTo(pnode);
1995 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1996 result.push_back(Pair("nVersion", alert.nVersion));
1997 result.push_back(Pair("nMinVer", alert.nMinVer));
1998 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1999 result.push_back(Pair("nID", alert.nID));
2000 if (alert.nCancel > 0)
2001 result.push_back(Pair("nCancel", alert.nCancel));
2005 // ppcoin: send checkpoint
2006 Value sendcheckpoint(const Array& params, bool fHelp)
2008 if (fHelp || params.size() > 2 || params.size() < 1 )
2009 throw runtime_error(
2010 "sendcheckpoint <privatekey> [checkpointhash]\n"
2011 "<privatekey> is hex string of checkpoint master private key\n"
2012 "<checkpointhash> is the hash of checkpoint block\n");
2014 CSyncCheckpoint checkpoint;
2017 // TODO: omit checkpointhash parameter
2018 if (params.size() > 1)
2020 checkpoint.hashCheckpoint = uint256(params[1].get_str());
2021 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
2022 throw runtime_error(
2023 "Provided checkpoint block is not on main chain\n");
2027 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
2028 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
2029 throw runtime_error(
2030 "Unable to select a more recent sync-checkpoint");
2034 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2035 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2037 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2038 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2039 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2040 throw runtime_error(
2041 "Unable to sign checkpoint, check private key?\n");
2043 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2044 throw runtime_error(
2045 "Failed to process checkpoint.\n");
2047 CRITICAL_BLOCK(cs_vNodes)
2048 BOOST_FOREACH(CNode* pnode, vNodes)
2049 checkpoint.RelayTo(pnode);
2052 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2053 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2054 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
2063 pair<string, rpcfn_type> pCallTable[] =
2065 make_pair("help", &help),
2066 make_pair("stop", &stop),
2067 make_pair("getblockcount", &getblockcount),
2068 make_pair("getblocknumber", &getblocknumber),
2069 make_pair("getconnectioncount", &getconnectioncount),
2070 make_pair("getdifficulty", &getdifficulty),
2071 make_pair("getgenerate", &getgenerate),
2072 make_pair("setgenerate", &setgenerate),
2073 make_pair("gethashespersec", &gethashespersec),
2074 make_pair("getinfo", &getinfo),
2075 make_pair("getnewaddress", &getnewaddress),
2076 make_pair("getaccountaddress", &getaccountaddress),
2077 make_pair("setaccount", &setaccount),
2078 make_pair("getaccount", &getaccount),
2079 make_pair("getaddressesbyaccount", &getaddressesbyaccount),
2080 make_pair("sendtoaddress", &sendtoaddress),
2081 make_pair("getreceivedbyaddress", &getreceivedbyaddress),
2082 make_pair("getreceivedbyaccount", &getreceivedbyaccount),
2083 make_pair("listreceivedbyaddress", &listreceivedbyaddress),
2084 make_pair("listreceivedbyaccount", &listreceivedbyaccount),
2085 make_pair("backupwallet", &backupwallet),
2086 make_pair("keypoolrefill", &keypoolrefill),
2087 make_pair("walletpassphrase", &walletpassphrase),
2088 make_pair("walletpassphrasechange", &walletpassphrasechange),
2089 make_pair("walletlock", &walletlock),
2090 make_pair("encryptwallet", &encryptwallet),
2091 make_pair("validateaddress", &validateaddress),
2092 make_pair("getbalance", &getbalance),
2093 make_pair("move", &movecmd),
2094 make_pair("sendfrom", &sendfrom),
2095 make_pair("sendmany", &sendmany),
2096 make_pair("gettransaction", &gettransaction),
2097 make_pair("listtransactions", &listtransactions),
2098 make_pair("signmessage", &signmessage),
2099 make_pair("verifymessage", &verifymessage),
2100 make_pair("getwork", &getwork),
2101 make_pair("listaccounts", &listaccounts),
2102 make_pair("settxfee", &settxfee),
2103 make_pair("getmemorypool", &getmemorypool),
2104 make_pair("listsinceblock", &listsinceblock),
2105 make_pair("getcheckpoint", &getcheckpoint),
2106 make_pair("reservebalance", &reservebalance),
2107 make_pair("checkwallet", &checkwallet),
2108 make_pair("repairwallet", &repairwallet),
2109 make_pair("makekeypair", &makekeypair),
2110 make_pair("sendalert", &sendalert),
2111 make_pair("sendcheckpoint", &sendcheckpoint),
2113 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2115 string pAllowInSafeMode[] =
2120 "getblocknumber", // deprecated
2121 "getconnectioncount",
2128 "getaccountaddress",
2130 "getaddressesbyaccount",
2140 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2148 // This ain't Apache. We're just using HTTP header for the length field
2149 // and to be compatible with other JSON-RPC implementations.
2152 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2155 s << "POST / HTTP/1.1\r\n"
2156 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2157 << "Host: 127.0.0.1\r\n"
2158 << "Content-Type: application/json\r\n"
2159 << "Content-Length: " << strMsg.size() << "\r\n"
2160 << "Connection: close\r\n"
2161 << "Accept: application/json\r\n";
2162 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2163 s << item.first << ": " << item.second << "\r\n";
2164 s << "\r\n" << strMsg;
2169 string rfc1123Time()
2174 struct tm* now_gmt = gmtime(&now);
2175 string locale(setlocale(LC_TIME, NULL));
2176 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2177 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2178 setlocale(LC_TIME, locale.c_str());
2179 return string(buffer);
2182 static string HTTPReply(int nStatus, const string& strMsg)
2185 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2187 "Server: ppcoin-json-rpc/%s\r\n"
2188 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2189 "Content-Type: text/html\r\n"
2190 "Content-Length: 296\r\n"
2192 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2193 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2196 "<TITLE>Error</TITLE>\r\n"
2197 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2199 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2200 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2201 const char *cStatus;
2202 if (nStatus == 200) cStatus = "OK";
2203 else if (nStatus == 400) cStatus = "Bad Request";
2204 else if (nStatus == 403) cStatus = "Forbidden";
2205 else if (nStatus == 404) cStatus = "Not Found";
2206 else if (nStatus == 500) cStatus = "Internal Server Error";
2209 "HTTP/1.1 %d %s\r\n"
2211 "Connection: close\r\n"
2212 "Content-Length: %d\r\n"
2213 "Content-Type: application/json\r\n"
2214 "Server: ppcoin-json-rpc/%s\r\n"
2219 rfc1123Time().c_str(),
2221 FormatFullVersion().c_str(),
2225 int ReadHTTPStatus(std::basic_istream<char>& stream)
2228 getline(stream, str);
2229 vector<string> vWords;
2230 boost::split(vWords, str, boost::is_any_of(" "));
2231 if (vWords.size() < 2)
2233 return atoi(vWords[1].c_str());
2236 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2242 std::getline(stream, str);
2243 if (str.empty() || str == "\r")
2245 string::size_type nColon = str.find(":");
2246 if (nColon != string::npos)
2248 string strHeader = str.substr(0, nColon);
2249 boost::trim(strHeader);
2250 boost::to_lower(strHeader);
2251 string strValue = str.substr(nColon+1);
2252 boost::trim(strValue);
2253 mapHeadersRet[strHeader] = strValue;
2254 if (strHeader == "content-length")
2255 nLen = atoi(strValue.c_str());
2261 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2263 mapHeadersRet.clear();
2267 int nStatus = ReadHTTPStatus(stream);
2270 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2271 if (nLen < 0 || nLen > MAX_SIZE)
2277 vector<char> vch(nLen);
2278 stream.read(&vch[0], nLen);
2279 strMessageRet = string(vch.begin(), vch.end());
2285 bool HTTPAuthorized(map<string, string>& mapHeaders)
2287 string strAuth = mapHeaders["authorization"];
2288 if (strAuth.substr(0,6) != "Basic ")
2290 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2291 string strUserPass = DecodeBase64(strUserPass64);
2292 return strUserPass == strRPCUserColonPass;
2296 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2297 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2298 // unspecified (HTTP errors and contents of 'error').
2300 // 1.0 spec: http://json-rpc.org/wiki/specification
2301 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2302 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2305 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2308 request.push_back(Pair("method", strMethod));
2309 request.push_back(Pair("params", params));
2310 request.push_back(Pair("id", id));
2311 return write_string(Value(request), false) + "\n";
2314 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2317 if (error.type() != null_type)
2318 reply.push_back(Pair("result", Value::null));
2320 reply.push_back(Pair("result", result));
2321 reply.push_back(Pair("error", error));
2322 reply.push_back(Pair("id", id));
2323 return write_string(Value(reply), false) + "\n";
2326 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2328 // Send error reply from json-rpc error object
2330 int code = find_value(objError, "code").get_int();
2331 if (code == -32600) nStatus = 400;
2332 else if (code == -32601) nStatus = 404;
2333 string strReply = JSONRPCReply(Value::null, objError, id);
2334 stream << HTTPReply(nStatus, strReply) << std::flush;
2337 bool ClientAllowed(const string& strAddress)
2339 if (strAddress == asio::ip::address_v4::loopback().to_string())
2341 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2342 BOOST_FOREACH(string strAllow, vAllow)
2343 if (WildcardMatch(strAddress, strAllow))
2350 // IOStream device that speaks SSL but can also speak non-SSL
2352 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2354 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2356 fUseSSL = fUseSSLIn;
2357 fNeedHandshake = fUseSSLIn;
2360 void handshake(ssl::stream_base::handshake_type role)
2362 if (!fNeedHandshake) return;
2363 fNeedHandshake = false;
2364 stream.handshake(role);
2366 std::streamsize read(char* s, std::streamsize n)
2368 handshake(ssl::stream_base::server); // HTTPS servers read first
2369 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2370 return stream.next_layer().read_some(asio::buffer(s, n));
2372 std::streamsize write(const char* s, std::streamsize n)
2374 handshake(ssl::stream_base::client); // HTTPS clients write first
2375 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2376 return asio::write(stream.next_layer(), asio::buffer(s, n));
2378 bool connect(const std::string& server, const std::string& port)
2380 ip::tcp::resolver resolver(stream.get_io_service());
2381 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2382 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2383 ip::tcp::resolver::iterator end;
2384 boost::system::error_code error = asio::error::host_not_found;
2385 while (error && endpoint_iterator != end)
2387 stream.lowest_layer().close();
2388 stream.lowest_layer().connect(*endpoint_iterator++, error);
2396 bool fNeedHandshake;
2402 void ThreadRPCServer(void* parg)
2404 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2407 vnThreadsRunning[4]++;
2408 ThreadRPCServer2(parg);
2409 vnThreadsRunning[4]--;
2411 catch (std::exception& e) {
2412 vnThreadsRunning[4]--;
2413 PrintException(&e, "ThreadRPCServer()");
2415 vnThreadsRunning[4]--;
2416 PrintException(NULL, "ThreadRPCServer()");
2418 printf("ThreadRPCServer exiting\n");
2421 void ThreadRPCServer2(void* parg)
2423 printf("ThreadRPCServer started\n");
2425 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2426 if (strRPCUserColonPass == ":")
2428 string strWhatAmI = "To use ppcoind";
2429 if (mapArgs.count("-server"))
2430 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2431 else if (mapArgs.count("-daemon"))
2432 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2434 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2435 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2437 GetConfigFile().c_str());
2439 CreateThread(Shutdown, NULL);
2444 bool fUseSSL = GetBoolArg("-rpcssl");
2445 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2447 asio::io_service io_service;
2448 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2449 ip::tcp::acceptor acceptor(io_service, endpoint);
2451 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2454 ssl::context context(io_service, ssl::context::sslv23);
2457 context.set_options(ssl::context::no_sslv2);
2458 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2459 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2460 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2461 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2462 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2463 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2464 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2465 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2467 string ciphers = GetArg("-rpcsslciphers",
2468 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2469 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2473 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2478 // Accept connection
2480 SSLStream sslStream(io_service, context);
2481 SSLIOStreamDevice d(sslStream, fUseSSL);
2482 iostreams::stream<SSLIOStreamDevice> stream(d);
2484 ip::tcp::iostream stream;
2487 ip::tcp::endpoint peer;
2488 vnThreadsRunning[4]--;
2490 acceptor.accept(sslStream.lowest_layer(), peer);
2492 acceptor.accept(*stream.rdbuf(), peer);
2494 vnThreadsRunning[4]++;
2498 // Restrict callers by IP
2499 if (!ClientAllowed(peer.address().to_string()))
2501 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2503 stream << HTTPReply(403, "") << std::flush;
2507 map<string, string> mapHeaders;
2510 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2511 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2514 printf("ThreadRPCServer ReadHTTP timeout\n");
2518 // Check authorization
2519 if (mapHeaders.count("authorization") == 0)
2521 stream << HTTPReply(401, "") << std::flush;
2524 if (!HTTPAuthorized(mapHeaders))
2526 // Deter brute-forcing short passwords
2527 if (mapArgs["-rpcpassword"].size() < 15)
2530 stream << HTTPReply(401, "") << std::flush;
2531 printf("ThreadRPCServer incorrect password attempt\n");
2535 Value id = Value::null;
2540 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2541 throw JSONRPCError(-32700, "Parse error");
2542 const Object& request = valRequest.get_obj();
2544 // Parse id now so errors from here on will have the id
2545 id = find_value(request, "id");
2548 Value valMethod = find_value(request, "method");
2549 if (valMethod.type() == null_type)
2550 throw JSONRPCError(-32600, "Missing method");
2551 if (valMethod.type() != str_type)
2552 throw JSONRPCError(-32600, "Method must be a string");
2553 string strMethod = valMethod.get_str();
2554 if (strMethod != "getwork" && strMethod != "getmemorypool")
2555 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2558 Value valParams = find_value(request, "params");
2560 if (valParams.type() == array_type)
2561 params = valParams.get_array();
2562 else if (valParams.type() == null_type)
2565 throw JSONRPCError(-32600, "Params must be an array");
2568 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2569 if (mi == mapCallTable.end())
2570 throw JSONRPCError(-32601, "Method not found");
2572 // Observe safe mode
2573 string strWarning = GetWarnings("rpc");
2574 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2575 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2581 CRITICAL_BLOCK(cs_main)
2582 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2583 result = (*(*mi).second)(params, false);
2586 string strReply = JSONRPCReply(result, Value::null, id);
2587 stream << HTTPReply(200, strReply) << std::flush;
2589 catch (std::exception& e)
2591 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2594 catch (Object& objError)
2596 ErrorReply(stream, objError, id);
2598 catch (std::exception& e)
2600 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2608 Object CallRPC(const string& strMethod, const Array& params)
2610 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2611 throw runtime_error(strprintf(
2612 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2613 "If the file does not exist, create it with owner-readable-only file permissions."),
2614 GetConfigFile().c_str()));
2616 // Connect to localhost
2617 bool fUseSSL = GetBoolArg("-rpcssl");
2619 asio::io_service io_service;
2620 ssl::context context(io_service, ssl::context::sslv23);
2621 context.set_options(ssl::context::no_sslv2);
2622 SSLStream sslStream(io_service, context);
2623 SSLIOStreamDevice d(sslStream, fUseSSL);
2624 iostreams::stream<SSLIOStreamDevice> stream(d);
2625 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2626 throw runtime_error("couldn't connect to server");
2629 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2631 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2633 throw runtime_error("couldn't connect to server");
2637 // HTTP basic authentication
2638 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2639 map<string, string> mapRequestHeaders;
2640 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2643 string strRequest = JSONRPCRequest(strMethod, params, 1);
2644 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2645 stream << strPost << std::flush;
2648 map<string, string> mapHeaders;
2650 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2652 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2653 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2654 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2655 else if (strReply.empty())
2656 throw runtime_error("no response from server");
2660 if (!read_string(strReply, valReply))
2661 throw runtime_error("couldn't parse reply from server");
2662 const Object& reply = valReply.get_obj();
2664 throw runtime_error("expected reply to have result, error and id properties");
2672 template<typename T>
2673 void ConvertTo(Value& value)
2675 if (value.type() == str_type)
2677 // reinterpret string as unquoted json value
2679 if (!read_string(value.get_str(), value2))
2680 throw runtime_error("type mismatch");
2681 value = value2.get_value<T>();
2685 value = value.get_value<T>();
2689 int CommandLineRPC(int argc, char *argv[])
2696 while (argc > 1 && IsSwitchChar(argv[1][0]))
2704 throw runtime_error("too few parameters");
2705 string strMethod = argv[1];
2707 // Parameters default to strings
2709 for (int i = 2; i < argc; i++)
2710 params.push_back(argv[i]);
2711 int n = params.size();
2714 // Special case non-string parameter types
2716 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2717 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2718 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2719 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2720 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2721 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2722 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2723 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2724 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2725 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2726 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2727 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2728 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2729 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2730 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2731 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2732 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2733 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2734 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2735 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2736 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2737 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2738 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2739 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2740 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2741 if (strMethod == "sendmany" && n > 1)
2743 string s = params[1].get_str();
2745 if (!read_string(s, v) || v.type() != obj_type)
2746 throw runtime_error("type mismatch");
2747 params[1] = v.get_obj();
2749 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2750 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2751 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2754 Object reply = CallRPC(strMethod, params);
2757 const Value& result = find_value(reply, "result");
2758 const Value& error = find_value(reply, "error");
2760 if (error.type() != null_type)
2763 strPrint = "error: " + write_string(error, false);
2764 int code = find_value(error.get_obj(), "code").get_int();
2770 if (result.type() == null_type)
2772 else if (result.type() == str_type)
2773 strPrint = result.get_str();
2775 strPrint = write_string(result, true);
2778 catch (std::exception& e)
2780 strPrint = string("error: ") + e.what();
2785 PrintException(NULL, "CommandLineRPC()");
2790 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2799 int main(int argc, char *argv[])
2802 // Turn off microsoft heap dump noise
2803 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2804 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2806 setbuf(stdin, NULL);
2807 setbuf(stdout, NULL);
2808 setbuf(stderr, NULL);
2812 if (argc >= 2 && string(argv[1]) == "-server")
2814 printf("server ready\n");
2815 ThreadRPCServer(NULL);
2819 return CommandLineRPC(argc, argv);
2822 catch (std::exception& e) {
2823 PrintException(&e, "main()");
2825 PrintException(NULL, "main()");