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", (int)VERSION));
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);
1662 throw JSONRPCError(-7, "Out of memory");
1663 vNewBlock.push_back(pblock);
1667 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, 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();
1714 return CheckWork(pblock, *pwalletMain, reservekey);
1719 Value getmemorypool(const Array& params, bool fHelp)
1721 if (fHelp || params.size() > 1)
1722 throw runtime_error(
1723 "getmemorypool [data]\n"
1724 "If [data] is not specified, returns data needed to construct a block to work on:\n"
1725 " \"version\" : block version\n"
1726 " \"previousblockhash\" : hash of current highest block\n"
1727 " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
1728 " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
1729 " \"time\" : timestamp appropriate for next block\n"
1730 " \"bits\" : compressed target of next block\n"
1731 "If [data] is specified, tries to solve the block and returns true if it was successful.");
1733 if (params.size() == 0)
1736 throw JSONRPCError(-9, "PPCoin is not connected!");
1738 if (IsInitialBlockDownload())
1739 throw JSONRPCError(-10, "PPCoin is downloading blocks...");
1741 static CReserveKey reservekey(pwalletMain);
1744 static unsigned int nTransactionsUpdatedLast;
1745 static CBlockIndex* pindexPrev;
1746 static int64 nStart;
1747 static CBlock* pblock;
1748 if (pindexPrev != pindexBest ||
1749 (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
1751 nTransactionsUpdatedLast = nTransactionsUpdated;
1752 pindexPrev = pindexBest;
1758 pblock = CreateNewBlock(pwalletMain);
1760 throw JSONRPCError(-7, "Out of memory");
1764 pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
1768 BOOST_FOREACH(CTransaction tx, pblock->vtx) {
1769 if(tx.IsCoinBase() || tx.IsCoinStake())
1775 transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
1779 result.push_back(Pair("version", pblock->nVersion));
1780 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
1781 result.push_back(Pair("transactions", transactions));
1782 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
1783 result.push_back(Pair("time", (int64_t)pblock->nTime));
1789 uBits.nBits = htonl((int32_t)pblock->nBits);
1790 result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
1797 CDataStream ssBlock(ParseHex(params[0].get_str()));
1801 return ProcessBlock(NULL, &pblock);
1806 // ppcoin: get information of sync-checkpoint
1807 Value getcheckpoint(const Array& params, bool fHelp)
1809 if (fHelp || params.size() != 0)
1810 throw runtime_error(
1812 "Show info of synchronized checkpoint.\n");
1815 CBlockIndex* pindexCheckpoint;
1817 result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
1818 pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint];
1819 result.push_back(Pair("height", pindexCheckpoint->nHeight));
1820 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str()));
1826 // ppcoin: reserve balance from being staked for network protection
1827 Value reservebalance(const Array& params, bool fHelp)
1829 if (fHelp || params.size() > 2)
1830 throw runtime_error(
1831 "reservebalance [<reserve> [amount]]\n"
1832 "<reserve> is true or false to turn balance reserve on or off.\n"
1833 "<amount> is a real and rounded to cent.\n"
1834 "Set reserve amount not participating in network protection.\n"
1835 "If no parameters provided current setting is printed.\n");
1837 if (params.size() > 0)
1839 bool fReserve = params[0].get_bool();
1842 if (params.size() == 1)
1843 throw runtime_error("must provide amount to reserve balance.\n");
1844 int64 nAmount = AmountFromValue(params[1]);
1845 nAmount = (nAmount / CENT) * CENT; // round to cent
1847 throw runtime_error("amount cannot be negative.\n");
1848 WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
1852 if (params.size() > 1)
1853 throw runtime_error("cannot specify amount to turn off reserve.\n");
1854 WriteSetting("nBalanceReserve", nBalanceReserve = 0);
1859 result.push_back(Pair("reserve", (nBalanceReserve > 0)));
1860 result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
1865 // ppcoin: check wallet integrity
1866 Value checkwallet(const Array& params, bool fHelp)
1868 if (fHelp || params.size() > 0)
1869 throw runtime_error(
1871 "Check wallet for integrity.\n");
1874 int64 nBalanceInQuestion;
1875 if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
1878 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1879 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1886 // ppcoin: repair wallet
1887 Value repairwallet(const Array& params, bool fHelp)
1889 if (fHelp || params.size() > 0)
1890 throw runtime_error(
1892 "Repair wallet if checkwallet reports any problem.\n");
1895 int64 nBalanceInQuestion;
1896 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1898 if (nMismatchSpent == 0)
1900 result.push_back(Pair("wallet check passed", true));
1904 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1905 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1910 // ppcoin: make a public-private key pair
1911 Value makekeypair(const Array& params, bool fHelp)
1913 if (fHelp || params.size() > 1)
1914 throw runtime_error(
1915 "makekeypair [prefix]\n"
1916 "Make a public/private key pair.\n"
1917 "[prefix] is optional preferred prefix for the public key.\n");
1919 string strPrefix = "";
1920 if (params.size() > 0)
1921 strPrefix = params[0].get_str();
1927 } while (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
1929 CPrivKey vchPrivKey = key.GetPrivKey();
1931 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1932 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
1936 extern CCriticalSection cs_mapAlerts;
1937 extern map<uint256, CAlert> mapAlerts;
1939 // ppcoin: send alert.
1940 // There is a known deadlock situation with ThreadMessageHandler
1941 // ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
1942 // ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
1943 Value sendalert(const Array& params, bool fHelp)
1945 if (fHelp || params.size() < 5)
1946 throw runtime_error(
1947 "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
1948 "<message> is the alert text message\n"
1949 "<privatekey> is hex string of alert master private key\n"
1950 "<minver> is the minimum applicable client version\n"
1951 "<maxver> is the maximum applicable client version\n"
1952 "<id> is the alert id\n"
1953 "[cancelupto] cancels all alert id's up to this number\n"
1954 "Returns true or false.");
1959 alert.strStatusBar = params[0].get_str();
1960 alert.nMinVer = params[2].get_int();
1961 alert.nMaxVer = params[3].get_int();
1962 alert.nID = params[4].get_int();
1963 if (params.size() > 5)
1964 alert.nCancel = params[5].get_int();
1965 alert.nVersion = VERSION;
1966 alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
1967 alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
1968 alert.nPriority = 1;
1971 sMsg << (CUnsignedAlert)alert;
1972 alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
1974 vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
1975 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
1976 if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
1977 throw runtime_error(
1978 "Unable to sign alert, check private key?\n");
1979 if(!alert.ProcessAlert())
1980 throw runtime_error(
1981 "Failed to process alert.\n");
1983 CRITICAL_BLOCK(cs_vNodes)
1984 BOOST_FOREACH(CNode* pnode, vNodes)
1985 alert.RelayTo(pnode);
1988 result.push_back(Pair("strStatusBar", alert.strStatusBar));
1989 result.push_back(Pair("nVersion", alert.nVersion));
1990 result.push_back(Pair("nMinVer", alert.nMinVer));
1991 result.push_back(Pair("nMaxVer", alert.nMaxVer));
1992 result.push_back(Pair("nID", alert.nID));
1993 if (alert.nCancel > 0)
1994 result.push_back(Pair("nCancel", alert.nCancel));
1998 // ppcoin: send checkpoint
1999 Value sendcheckpoint(const Array& params, bool fHelp)
2001 if (fHelp || params.size() > 2 || params.size() < 1 )
2002 throw runtime_error(
2003 "sendcheckpoint <privatekey> [checkpointhash]\n"
2004 "<privatekey> is hex string of checkpoint master private key\n"
2005 "<checkpointhash> is the hash of checkpoint block\n");
2007 CSyncCheckpoint checkpoint;
2010 // TODO: omit checkpointhash parameter
2011 if (params.size() > 1)
2013 checkpoint.hashCheckpoint = uint256(params[1].get_str());
2014 if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
2015 throw runtime_error(
2016 "Provided checkpoint block is not on main chain\n");
2020 checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
2021 if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
2022 throw runtime_error(
2023 "Unable to select a more recent sync-checkpoint");
2027 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
2028 checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
2030 vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
2031 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
2032 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
2033 throw runtime_error(
2034 "Unable to sign checkpoint, check private key?\n");
2036 if(!checkpoint.ProcessSyncCheckpoint(NULL))
2037 throw runtime_error(
2038 "Failed to process checkpoint.\n");
2040 CRITICAL_BLOCK(cs_vNodes)
2041 BOOST_FOREACH(CNode* pnode, vNodes)
2042 checkpoint.RelayTo(pnode);
2045 result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
2046 result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
2047 result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
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("getcheckpoint", &getcheckpoint),
2099 make_pair("reservebalance", &reservebalance),
2100 make_pair("checkwallet", &checkwallet),
2101 make_pair("repairwallet", &repairwallet),
2102 make_pair("makekeypair", &makekeypair),
2103 make_pair("sendalert", &sendalert),
2104 make_pair("sendcheckpoint", &sendcheckpoint),
2106 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
2108 string pAllowInSafeMode[] =
2113 "getblocknumber", // deprecated
2114 "getconnectioncount",
2121 "getaccountaddress",
2123 "getaddressesbyaccount",
2133 set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
2141 // This ain't Apache. We're just using HTTP header for the length field
2142 // and to be compatible with other JSON-RPC implementations.
2145 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
2148 s << "POST / HTTP/1.1\r\n"
2149 << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
2150 << "Host: 127.0.0.1\r\n"
2151 << "Content-Type: application/json\r\n"
2152 << "Content-Length: " << strMsg.size() << "\r\n"
2153 << "Connection: close\r\n"
2154 << "Accept: application/json\r\n";
2155 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
2156 s << item.first << ": " << item.second << "\r\n";
2157 s << "\r\n" << strMsg;
2162 string rfc1123Time()
2167 struct tm* now_gmt = gmtime(&now);
2168 string locale(setlocale(LC_TIME, NULL));
2169 setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
2170 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
2171 setlocale(LC_TIME, locale.c_str());
2172 return string(buffer);
2175 static string HTTPReply(int nStatus, const string& strMsg)
2178 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
2180 "Server: ppcoin-json-rpc/%s\r\n"
2181 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
2182 "Content-Type: text/html\r\n"
2183 "Content-Length: 296\r\n"
2185 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
2186 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
2189 "<TITLE>Error</TITLE>\r\n"
2190 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
2192 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
2193 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
2194 const char *cStatus;
2195 if (nStatus == 200) cStatus = "OK";
2196 else if (nStatus == 400) cStatus = "Bad Request";
2197 else if (nStatus == 403) cStatus = "Forbidden";
2198 else if (nStatus == 404) cStatus = "Not Found";
2199 else if (nStatus == 500) cStatus = "Internal Server Error";
2202 "HTTP/1.1 %d %s\r\n"
2204 "Connection: close\r\n"
2205 "Content-Length: %d\r\n"
2206 "Content-Type: application/json\r\n"
2207 "Server: ppcoin-json-rpc/%s\r\n"
2212 rfc1123Time().c_str(),
2214 FormatFullVersion().c_str(),
2218 int ReadHTTPStatus(std::basic_istream<char>& stream)
2221 getline(stream, str);
2222 vector<string> vWords;
2223 boost::split(vWords, str, boost::is_any_of(" "));
2224 if (vWords.size() < 2)
2226 return atoi(vWords[1].c_str());
2229 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
2235 std::getline(stream, str);
2236 if (str.empty() || str == "\r")
2238 string::size_type nColon = str.find(":");
2239 if (nColon != string::npos)
2241 string strHeader = str.substr(0, nColon);
2242 boost::trim(strHeader);
2243 boost::to_lower(strHeader);
2244 string strValue = str.substr(nColon+1);
2245 boost::trim(strValue);
2246 mapHeadersRet[strHeader] = strValue;
2247 if (strHeader == "content-length")
2248 nLen = atoi(strValue.c_str());
2254 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
2256 mapHeadersRet.clear();
2260 int nStatus = ReadHTTPStatus(stream);
2263 int nLen = ReadHTTPHeader(stream, mapHeadersRet);
2264 if (nLen < 0 || nLen > MAX_SIZE)
2270 vector<char> vch(nLen);
2271 stream.read(&vch[0], nLen);
2272 strMessageRet = string(vch.begin(), vch.end());
2278 bool HTTPAuthorized(map<string, string>& mapHeaders)
2280 string strAuth = mapHeaders["authorization"];
2281 if (strAuth.substr(0,6) != "Basic ")
2283 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
2284 string strUserPass = DecodeBase64(strUserPass64);
2285 return strUserPass == strRPCUserColonPass;
2289 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
2290 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
2291 // unspecified (HTTP errors and contents of 'error').
2293 // 1.0 spec: http://json-rpc.org/wiki/specification
2294 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
2295 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
2298 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
2301 request.push_back(Pair("method", strMethod));
2302 request.push_back(Pair("params", params));
2303 request.push_back(Pair("id", id));
2304 return write_string(Value(request), false) + "\n";
2307 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
2310 if (error.type() != null_type)
2311 reply.push_back(Pair("result", Value::null));
2313 reply.push_back(Pair("result", result));
2314 reply.push_back(Pair("error", error));
2315 reply.push_back(Pair("id", id));
2316 return write_string(Value(reply), false) + "\n";
2319 void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2321 // Send error reply from json-rpc error object
2323 int code = find_value(objError, "code").get_int();
2324 if (code == -32600) nStatus = 400;
2325 else if (code == -32601) nStatus = 404;
2326 string strReply = JSONRPCReply(Value::null, objError, id);
2327 stream << HTTPReply(nStatus, strReply) << std::flush;
2330 bool ClientAllowed(const string& strAddress)
2332 if (strAddress == asio::ip::address_v4::loopback().to_string())
2334 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
2335 BOOST_FOREACH(string strAllow, vAllow)
2336 if (WildcardMatch(strAddress, strAllow))
2343 // IOStream device that speaks SSL but can also speak non-SSL
2345 class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2347 SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
2349 fUseSSL = fUseSSLIn;
2350 fNeedHandshake = fUseSSLIn;
2353 void handshake(ssl::stream_base::handshake_type role)
2355 if (!fNeedHandshake) return;
2356 fNeedHandshake = false;
2357 stream.handshake(role);
2359 std::streamsize read(char* s, std::streamsize n)
2361 handshake(ssl::stream_base::server); // HTTPS servers read first
2362 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
2363 return stream.next_layer().read_some(asio::buffer(s, n));
2365 std::streamsize write(const char* s, std::streamsize n)
2367 handshake(ssl::stream_base::client); // HTTPS clients write first
2368 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
2369 return asio::write(stream.next_layer(), asio::buffer(s, n));
2371 bool connect(const std::string& server, const std::string& port)
2373 ip::tcp::resolver resolver(stream.get_io_service());
2374 ip::tcp::resolver::query query(server.c_str(), port.c_str());
2375 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
2376 ip::tcp::resolver::iterator end;
2377 boost::system::error_code error = asio::error::host_not_found;
2378 while (error && endpoint_iterator != end)
2380 stream.lowest_layer().close();
2381 stream.lowest_layer().connect(*endpoint_iterator++, error);
2389 bool fNeedHandshake;
2395 void ThreadRPCServer(void* parg)
2397 IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
2400 vnThreadsRunning[4]++;
2401 ThreadRPCServer2(parg);
2402 vnThreadsRunning[4]--;
2404 catch (std::exception& e) {
2405 vnThreadsRunning[4]--;
2406 PrintException(&e, "ThreadRPCServer()");
2408 vnThreadsRunning[4]--;
2409 PrintException(NULL, "ThreadRPCServer()");
2411 printf("ThreadRPCServer exiting\n");
2414 void ThreadRPCServer2(void* parg)
2416 printf("ThreadRPCServer started\n");
2418 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
2419 if (strRPCUserColonPass == ":")
2421 string strWhatAmI = "To use ppcoind";
2422 if (mapArgs.count("-server"))
2423 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
2424 else if (mapArgs.count("-daemon"))
2425 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
2427 _("Error: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n"
2428 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
2430 GetConfigFile().c_str());
2432 CreateThread(Shutdown, NULL);
2437 bool fUseSSL = GetBoolArg("-rpcssl");
2438 asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
2440 asio::io_service io_service;
2441 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
2442 ip::tcp::acceptor acceptor(io_service, endpoint);
2444 acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
2447 ssl::context context(io_service, ssl::context::sslv23);
2450 context.set_options(ssl::context::no_sslv2);
2451 filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
2452 if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
2453 if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
2454 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
2455 filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
2456 if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
2457 if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
2458 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
2460 string ciphers = GetArg("-rpcsslciphers",
2461 "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
2462 SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
2466 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2471 // Accept connection
2473 SSLStream sslStream(io_service, context);
2474 SSLIOStreamDevice d(sslStream, fUseSSL);
2475 iostreams::stream<SSLIOStreamDevice> stream(d);
2477 ip::tcp::iostream stream;
2480 ip::tcp::endpoint peer;
2481 vnThreadsRunning[4]--;
2483 acceptor.accept(sslStream.lowest_layer(), peer);
2485 acceptor.accept(*stream.rdbuf(), peer);
2487 vnThreadsRunning[4]++;
2491 // Restrict callers by IP
2492 if (!ClientAllowed(peer.address().to_string()))
2494 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2496 stream << HTTPReply(403, "") << std::flush;
2500 map<string, string> mapHeaders;
2503 boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest));
2504 if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30))))
2507 printf("ThreadRPCServer ReadHTTP timeout\n");
2511 // Check authorization
2512 if (mapHeaders.count("authorization") == 0)
2514 stream << HTTPReply(401, "") << std::flush;
2517 if (!HTTPAuthorized(mapHeaders))
2519 // Deter brute-forcing short passwords
2520 if (mapArgs["-rpcpassword"].size() < 15)
2523 stream << HTTPReply(401, "") << std::flush;
2524 printf("ThreadRPCServer incorrect password attempt\n");
2528 Value id = Value::null;
2533 if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type)
2534 throw JSONRPCError(-32700, "Parse error");
2535 const Object& request = valRequest.get_obj();
2537 // Parse id now so errors from here on will have the id
2538 id = find_value(request, "id");
2541 Value valMethod = find_value(request, "method");
2542 if (valMethod.type() == null_type)
2543 throw JSONRPCError(-32600, "Missing method");
2544 if (valMethod.type() != str_type)
2545 throw JSONRPCError(-32600, "Method must be a string");
2546 string strMethod = valMethod.get_str();
2547 if (strMethod != "getwork" && strMethod != "getmemorypool")
2548 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
2551 Value valParams = find_value(request, "params");
2553 if (valParams.type() == array_type)
2554 params = valParams.get_array();
2555 else if (valParams.type() == null_type)
2558 throw JSONRPCError(-32600, "Params must be an array");
2561 map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
2562 if (mi == mapCallTable.end())
2563 throw JSONRPCError(-32601, "Method not found");
2565 // Observe safe mode
2566 string strWarning = GetWarnings("rpc");
2567 if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
2568 throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
2574 CRITICAL_BLOCK(cs_main)
2575 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2576 result = (*(*mi).second)(params, false);
2579 string strReply = JSONRPCReply(result, Value::null, id);
2580 stream << HTTPReply(200, strReply) << std::flush;
2582 catch (std::exception& e)
2584 ErrorReply(stream, JSONRPCError(-1, e.what()), id);
2587 catch (Object& objError)
2589 ErrorReply(stream, objError, id);
2591 catch (std::exception& e)
2593 ErrorReply(stream, JSONRPCError(-32700, e.what()), id);
2601 Object CallRPC(const string& strMethod, const Array& params)
2603 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
2604 throw runtime_error(strprintf(
2605 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
2606 "If the file does not exist, create it with owner-readable-only file permissions."),
2607 GetConfigFile().c_str()));
2609 // Connect to localhost
2610 bool fUseSSL = GetBoolArg("-rpcssl");
2612 asio::io_service io_service;
2613 ssl::context context(io_service, ssl::context::sslv23);
2614 context.set_options(ssl::context::no_sslv2);
2615 SSLStream sslStream(io_service, context);
2616 SSLIOStreamDevice d(sslStream, fUseSSL);
2617 iostreams::stream<SSLIOStreamDevice> stream(d);
2618 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
2619 throw runtime_error("couldn't connect to server");
2622 throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries.");
2624 ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()));
2626 throw runtime_error("couldn't connect to server");
2630 // HTTP basic authentication
2631 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
2632 map<string, string> mapRequestHeaders;
2633 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
2636 string strRequest = JSONRPCRequest(strMethod, params, 1);
2637 string strPost = HTTPPost(strRequest, mapRequestHeaders);
2638 stream << strPost << std::flush;
2641 map<string, string> mapHeaders;
2643 int nStatus = ReadHTTP(stream, mapHeaders, strReply);
2645 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
2646 else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
2647 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
2648 else if (strReply.empty())
2649 throw runtime_error("no response from server");
2653 if (!read_string(strReply, valReply))
2654 throw runtime_error("couldn't parse reply from server");
2655 const Object& reply = valReply.get_obj();
2657 throw runtime_error("expected reply to have result, error and id properties");
2665 template<typename T>
2666 void ConvertTo(Value& value)
2668 if (value.type() == str_type)
2670 // reinterpret string as unquoted json value
2672 if (!read_string(value.get_str(), value2))
2673 throw runtime_error("type mismatch");
2674 value = value2.get_value<T>();
2678 value = value.get_value<T>();
2682 int CommandLineRPC(int argc, char *argv[])
2689 while (argc > 1 && IsSwitchChar(argv[1][0]))
2697 throw runtime_error("too few parameters");
2698 string strMethod = argv[1];
2700 // Parameters default to strings
2702 for (int i = 2; i < argc; i++)
2703 params.push_back(argv[i]);
2704 int n = params.size();
2707 // Special case non-string parameter types
2709 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
2710 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2711 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
2712 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
2713 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2714 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2715 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2716 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
2717 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2718 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
2719 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2720 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
2721 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2722 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
2723 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2724 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2725 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2726 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
2727 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2728 if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
2729 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
2730 if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2731 if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
2732 if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
2733 if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
2734 if (strMethod == "sendmany" && n > 1)
2736 string s = params[1].get_str();
2738 if (!read_string(s, v) || v.type() != obj_type)
2739 throw runtime_error("type mismatch");
2740 params[1] = v.get_obj();
2742 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
2743 if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
2744 if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
2747 Object reply = CallRPC(strMethod, params);
2750 const Value& result = find_value(reply, "result");
2751 const Value& error = find_value(reply, "error");
2753 if (error.type() != null_type)
2756 strPrint = "error: " + write_string(error, false);
2757 int code = find_value(error.get_obj(), "code").get_int();
2763 if (result.type() == null_type)
2765 else if (result.type() == str_type)
2766 strPrint = result.get_str();
2768 strPrint = write_string(result, true);
2771 catch (std::exception& e)
2773 strPrint = string("error: ") + e.what();
2778 PrintException(NULL, "CommandLineRPC()");
2783 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
2792 int main(int argc, char *argv[])
2795 // Turn off microsoft heap dump noise
2796 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2797 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2799 setbuf(stdin, NULL);
2800 setbuf(stdout, NULL);
2801 setbuf(stderr, NULL);
2805 if (argc >= 2 && string(argv[1]) == "-server")
2807 printf("server ready\n");
2808 ThreadRPCServer(NULL);
2812 return CommandLineRPC(argc, argv);
2815 catch (std::exception& e) {
2816 PrintException(&e, "main()");
2818 PrintException(NULL, "main()");