X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fbitcoinrpc.cpp;h=21963c7e194e40e79f2d956f998d63c591cff336;hb=ec238d8a644d11b08d17fdb81a31851c07df3dac;hp=644ad92297e6fed83520ddb063ec140f52900e8f;hpb=8fe2308b34c444f74fed453133e0ada34ed9dd23;p=novacoin.git diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 644ad92..21963c7 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1,12 +1,14 @@ // Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" -#include "cryptopp/sha.h" #include "db.h" #include "net.h" #include "init.h" +#include "checkpoints.h" #undef printf #include #include @@ -36,6 +38,11 @@ void ThreadRPCServer2(void* parg); typedef Value(*rpcfn_type)(const Array& params, bool fHelp); extern map mapCallTable; +static std::string strRPCUserColonPass; + +static int64 nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; + Object JSONRPCError(int code, const string& message) { @@ -60,18 +67,14 @@ void PrintConsole(const std::string &format, ...) buffer[limit-1] = 0; } printf("%s", buffer); -#if defined(__WXMSW__) && defined(GUI) - MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); -#else fprintf(stdout, "%s", buffer); -#endif } int64 AmountFromValue(const Value& value) { double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) + if (dAmount <= 0.0 || dAmount > MAX_MONEY) throw JSONRPCError(-3, "Invalid amount"); int64 nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) @@ -127,6 +130,7 @@ Value help(const Array& params, bool fHelp) // We already filter duplicates, but these deprecated screw up the sort order if (strMethod == "getamountreceived" || strMethod == "getallreceived" || + strMethod == "getblocknumber" || // deprecated (strMethod.find("label") != string::npos)) continue; if (strCommand != "" && strMethod != strCommand) @@ -160,11 +164,14 @@ Value stop(const Array& params, bool fHelp) if (fHelp || params.size() != 0) throw runtime_error( "stop\n" - "Stop bitcoin server."); - + "Stop ppcoin server."); +#ifndef QT_GUI // Shutdown will take long enough that the response should get back CreateThread(Shutdown, NULL); - return "bitcoin server stopping"; + return "ppcoin server stopping"; +#else + throw runtime_error("NYI: cannot shut down GUI with RPC command"); +#endif } @@ -179,12 +186,13 @@ Value getblockcount(const Array& params, bool fHelp) } +// deprecated Value getblocknumber(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( "getblocknumber\n" - "Returns the block number of the latest block in the longest block chain."); + "Deprecated. Use getblockcount."); return nBestHeight; } @@ -300,16 +308,21 @@ Value getinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("version", (int)VERSION)); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP())); obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize())); obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } @@ -320,7 +333,7 @@ Value getnewaddress(const Array& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress [account]\n" - "Returns a new bitcoin address for receiving payments. " + "Returns a new ppcoin address for receiving payments. " "If [account] is specified (recommended), it is added to the address book " "so payments received with the address will be credited to [account]."); @@ -329,25 +342,30 @@ Value getnewaddress(const Array& params, bool fHelp) if (params.size() > 0) strAccount = AccountFromValue(params[0]); + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + std::vector newKey; + if (!pwalletMain->GetKeyFromPool(newKey, false)) + throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first"); + CBitcoinAddress address(newKey); + + pwalletMain->SetAddressBookName(address, strAccount); - pwalletMain->SetAddressBookName(strAddress, strAccount); - return strAddress; + return address.ToString(); } -// requires cs_main, cs_mapWallet locks -string GetAccountAddress(string strAccount, bool bForceNew=false) +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) { - string strAddress; - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); CAccount account; walletdb.ReadAccount(strAccount, account); + bool bKeyUsed = false; + // Check if the current key has been used if (!account.vchPubKey.empty()) { @@ -360,23 +378,21 @@ string GetAccountAddress(string strAccount, bool bForceNew=false) const CWalletTx& wtx = (*it).second; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (txout.scriptPubKey == scriptPubKey) - account.vchPubKey.clear(); + bKeyUsed = true; } } // Generate a new key - if (account.vchPubKey.empty() || bForceNew) + if (account.vchPubKey.empty() || bForceNew || bKeyUsed) { - account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); - string strAddress = PubKeyToAddress(account.vchPubKey); - pwalletMain->SetAddressBookName(strAddress, strAccount); + if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) + throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first"); + + pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount); walletdb.WriteAccount(strAccount, account); } - walletdb.TxnCommit(); - strAddress = PubKeyToAddress(account.vchPubKey); - - return strAddress; + return CBitcoinAddress(account.vchPubKey); } Value getaccountaddress(const Array& params, bool fHelp) @@ -384,18 +400,14 @@ Value getaccountaddress(const Array& params, bool fHelp) if (fHelp || params.size() != 1) throw runtime_error( "getaccountaddress \n" - "Returns the current bitcoin address for receiving payments to this account."); + "Returns the current ppcoin address for receiving payments to this account."); // Parse the account first so we don't generate a key if there's an error string strAccount = AccountFromValue(params[0]); Value ret; - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - ret = GetAccountAddress(strAccount); - } + ret = GetAccountAddress(strAccount).ToString(); return ret; } @@ -406,14 +418,12 @@ Value setaccount(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "setaccount \n" + "setaccount \n" "Sets the account associated with the given address."); - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - if (!isValid) - throw JSONRPCError(-5, "Invalid bitcoin address"); + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(-5, "Invalid ppcoin address"); string strAccount; @@ -421,19 +431,15 @@ Value setaccount(const Array& params, bool fHelp) strAccount = AccountFromValue(params[1]); // Detect when changing the account of an address that is the 'unused current key' of another account: - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + if (pwalletMain->mapAddressBook.count(address)) { - if (pwalletMain->mapAddressBook.count(strAddress)) - { - string strOldAccount = pwalletMain->mapAddressBook[strAddress]; - if (strAddress == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } + string strOldAccount = pwalletMain->mapAddressBook[address]; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); } - pwalletMain->SetAddressBookName(strAddress, strAccount); + pwalletMain->SetAddressBookName(address, strAccount); + return Value::null; } @@ -442,18 +448,17 @@ Value getaccount(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "getaccount \n" + "getaccount \n" "Returns the account associated with the given address."); - string strAddress = params[0].get_str(); + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(-5, "Invalid ppcoin address"); string strAccount; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - } + map::iterator mi = pwalletMain->mapAddressBook.find(address); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; return strAccount; } @@ -469,48 +474,44 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) // Find all addresses that have the given account Array ret; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only adding valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - ret.push_back(strAddress); - } - } + const CBitcoinAddress& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + ret.push_back(address.ToString()); } return ret; } Value settxfee(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 1) + if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE) throw runtime_error( "settxfee \n" - " is a real and is rounded to the nearest 0.00000001"); - - // Amount - int64 nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + " is a real and is rounded to 0.01 (cent)\n" + "Minimum and default transaction fee per KB is 1 cent"); - nTransactionFee = nAmount; + nTransactionFee = AmountFromValue(params[0]); + nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent return true; } Value sendtoaddress(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001"); - string strAddress = params[0].get_str(); + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(-5, "Invalid ppcoin address"); // Amount int64 nAmount = AmountFromValue(params[1]); @@ -522,29 +523,96 @@ Value sendtoaddress(const Array& params, bool fHelp) if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); - CRITICAL_BLOCK(cs_main) - { - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); return wtx.GetHash().GetHex(); } +static const string strMessageMagic = "Bitcoin Signed Message:\n"; + +Value signmessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessage \n" + "Sign a message with the private key of an address"); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + string strAddress = params[0].get_str(); + string strMessage = params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(-3, "Invalid address"); + + CKey key; + if (!pwalletMain->GetKey(addr, key)) + throw JSONRPCError(-4, "Private key not available"); + + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + throw JSONRPCError(-5, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +Value verifymessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \n" + "Verify a signed message"); + + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(-3, "Invalid address"); + + bool fInvalid = false; + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(-5, "Malformed base64 encoding"); + + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << strMessage; + + CKey key; + if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + return false; + + return (key.GetAddress() == addr); +} + Value getreceivedbyaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); // Bitcoin address - string strAddress = params[0].get_str(); + CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, "Invalid bitcoin address"); + if (!address.IsValid()) + throw JSONRPCError(-5, "Invalid ppcoin address"); + scriptPubKey.SetBitcoinAddress(address); if (!IsMine(*pwalletMain,scriptPubKey)) return (double)0.0; @@ -555,42 +623,30 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) // Tally int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) + continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; } return ValueFromAmount(nAmount); } -void GetAccountPubKeys(string strAccount, set& setPubKey) +void GetAccountAddresses(string strAccount, set& setAddress) { - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only counting our own valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - if (IsMine(*pwalletMain,scriptPubKey)) - setPubKey.insert(scriptPubKey); - } - } + const CBitcoinAddress& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + setAddress.insert(address); } } @@ -609,23 +665,23 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) // Get the set of pub keys that have the label string strAccount = AccountFromValue(params[0]); - set setPubKey; - GetAccountPubKeys(strAccount, setPubKey); + set setAddress; + GetAccountAddresses(strAccount, setAddress); // Tally int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) + continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (setPubKey.count(txout.scriptPubKey)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CBitcoinAddress address; + if (ExtractAddress(txout.scriptPubKey, pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; } } @@ -636,27 +692,25 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) { int64 nBalance = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Tally wallet transactions - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; - } + int64 nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; } + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + return nBalance; } @@ -669,7 +723,7 @@ int64 GetAccountBalance(const string& strAccount, int nMinDepth) Value getbalance(const Array& params, bool fHelp) { - if (fHelp || params.size() < 0 || params.size() > 2) + if (fHelp || params.size() > 2) throw runtime_error( "getbalance [account] [minconf=1]\n" "If [account] is not specified, returns the server's total available balance.\n" @@ -696,13 +750,13 @@ Value getbalance(const Array& params, bool fHelp) int64 allGeneratedImmature, allGeneratedMature, allFee; allGeneratedImmature = allGeneratedMature = allFee = 0; string strSentAccount; - list > listReceived; - list > listSent; + list > listReceived; + list > listSent; wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); if (wtx.GetDepthInMainChain() >= nMinDepth) - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived) nBalance += r.second; - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent) nBalance -= r.second; nBalance -= allFee; nBalance += allGeneratedMature; @@ -728,53 +782,58 @@ Value movecmd(const Array& params, bool fHelp) string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; if (params.size() > 3) - nMinDepth = params[3].get_int(); + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)params[3].get_int(); string strComment; if (params.size() > 4) strComment = params[4].get_str(); - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); - - int64 nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - walletdb.TxnCommit(); - } + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.TxnBegin(); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + walletdb.TxnCommit(); + return true; } Value sendfrom(const Array& params, bool fHelp) { - if (fHelp || params.size() < 3 || params.size() > 6) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6)) throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6)) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001"); string strAccount = AccountFromValue(params[0]); - string strAddress = params[1].get_str(); + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(-5, "Invalid ppcoin address"); int64 nAmount = AmountFromValue(params[2]); int nMinDepth = 1; if (params.size() > 3) @@ -787,26 +846,31 @@ Value sendfrom(const Array& params, bool fHelp) if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); - // Send - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); return wtx.GetHash().GetHex(); } + Value sendmany(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) throw runtime_error( "sendmany {address:amount,...} [minconf=1] [comment]\n" "amounts are double-precision floating point numbers"); @@ -822,49 +886,50 @@ Value sendmany(const Array& params, bool fHelp) if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["comment"] = params[3].get_str(); - set setAddress; + set setAddress; vector > vecSend; int64 totalAmount = 0; BOOST_FOREACH(const Pair& s, sendTo) { - uint160 hash160; - string strAddress = s.name_; + CBitcoinAddress address(s.name_); + if (!address.IsValid()) + throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_); - if (setAddress.count(strAddress)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); - setAddress.insert(strAddress); + if (setAddress.count(address)) + throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_); + setAddress.insert(address); CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); + scriptPubKey.SetBitcoinAddress(address); int64 nAmount = AmountFromValue(s.value_); totalAmount += nAmount; vecSend.push_back(make_pair(scriptPubKey, nAmount)); } - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (fWalletUnlockStakeOnly) + throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only."); + + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + int64 nFeeRequired = 0; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (totalAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; - bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - if (!fCreated) - { - if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); - } - if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); + if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); } + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); return wtx.GetHash().GetHex(); } @@ -894,73 +959,62 @@ Value ListReceived(const Array& params, bool fByAccounts) fIncludeEmpty = params[1].get_bool(); // Tally - map mapTally; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + map mapTally; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) + continue; - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CBitcoinAddress address; + if (!ExtractAddress(txout.scriptPubKey, pwalletMain, address) || !address.IsValid()) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - // Only counting our own bitcoin addresses and not ip addresses - uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); - if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine - continue; - - tallyitem& item = mapTally[hash160]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); } } // Reply Array ret; map mapAccountTally; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strAccount = item.second; - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - continue; - map::iterator it = mapTally.find(hash160); - if (it == mapTally.end() && !fIncludeEmpty) - continue; + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; - int64 nAmount = 0; - int nConf = INT_MAX; - if (it != mapTally.end()) - { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - } + int64 nAmount = 0; + int nConf = INT_MAX; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - } - else - { - Object obj; - obj.push_back(Pair("address", strAddress)); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("label", strAccount)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); } } @@ -972,7 +1026,6 @@ Value ListReceived(const Array& params, bool fByAccounts) int nConf = (*it).second.nConf; Object obj; obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("label", (*it).first)); // deprecated obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); ret.push_back(obj); @@ -1017,8 +1070,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { int64 nGeneratedImmature, nGeneratedMature, nFee; string strSentAccount; - list > listReceived; - list > listSent; + list > listReceived; + list > listSent; wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); bool fAllAccounts = (strAccount == string("*")); @@ -1046,11 +1099,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Sent if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) { - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent) { Object entry; entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", s.first)); + entry.push_back(Pair("address", s.first.ToString())); entry.push_back(Pair("category", "send")); entry.push_back(Pair("amount", ValueFromAmount(-s.second))); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); @@ -1062,27 +1115,23 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Received if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived) { - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + string account; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) { - string account; - if (pwalletMain->mapAddressBook.count(r.first)) - account = pwalletMain->mapAddressBook[r.first]; - if (fAllAccounts || (account == strAccount)) - { - Object entry; - entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", r.first)); - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(r.second))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", r.first.ToString())); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); } } - } void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) @@ -1122,40 +1171,38 @@ Value listtransactions(const Array& params, bool fHelp) Array ret; CWalletDB walletdb(pwalletMain->strWalletFile); - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - walletdb.ListAccountCreditDebit(strAccount, acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } - // Now: iterate backwards until we have nCount items to return: - TxItems::reverse_iterator it = txByTime.rbegin(); - for (std::advance(it, nFrom); it != txByTime.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); - - if (ret.size() >= nCount) break; - } - // ret is now newest to oldest + // Now: iterate backwards until we have nCount items to return: + TxItems::reverse_iterator it = txByTime.rbegin(); + if (txByTime.size() > nFrom) std::advance(it, nFrom); + for (; it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; } + // ret is now newest to oldest // Make sure we return only last nCount items (sends-to-self might give us an extra): if (ret.size() > nCount) @@ -1181,35 +1228,30 @@ Value listaccounts(const Array& params, bool fHelp) nMinDepth = params[0].get_int(); map mapAccountBalances; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { - uint160 hash160; - if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me - mapAccountBalances[entry.second] = 0; - } + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) { + if (pwalletMain->HaveKey(entry.first)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) { - const CWalletTx& wtx = (*it).second; - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - mapAccountBalances[""] += nGeneratedMature; - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - if (pwalletMain->mapAddressBook.count(r.first)) - mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; - else - mapAccountBalances[""] += r.second; - } + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; } } @@ -1225,6 +1267,70 @@ Value listaccounts(const Array& params, bool fHelp) return ret; } +Value listsinceblock(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listsinceblock [blockid] [target-confirmations]\n" + "Get all transactions in blocks since block [blockid], or all transactions if omitted"); + + CBlockIndex *pindex = NULL; + int target_confirms = 1; + + if (params.size() > 0) + { + uint256 blockId = 0; + + blockId.SetHex(params[0].get_str()); + pindex = CBlockLocator(blockId).GetBlockIndex(); + } + + if (params.size() > 1) + { + target_confirms = params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(-8, "Invalid parameter"); + } + + int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + + Array transactions; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions); + } + + uint256 lastblock; + + if (target_confirms == 1) + { + printf("oops!\n"); + lastblock = hashBestChain; + } + else + { + int target_height = pindexBest->nHeight + 1 - target_confirms; + + CBlockIndex *block; + for (block = pindexBest; + block && block->nHeight > target_height; + block = block->pprev); + + lastblock = block ? block->GetBlockHash() : 0; + } + + Object ret; + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + Value gettransaction(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1236,27 +1342,25 @@ Value gettransaction(const Array& params, bool fHelp) hash.SetHex(params[0].get_str()); Object entry; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - WalletTxToJSON(pwalletMain->mapWallet[hash], entry); + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); - Array details; - ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); - entry.push_back(Pair("details", details)); - } + WalletTxToJSON(pwalletMain->mapWallet[hash], entry); + + Array details; + ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); + entry.push_back(Pair("details", details)); return entry; } @@ -1276,16 +1380,218 @@ Value backupwallet(const Array& params, bool fHelp) } +Value keypoolrefill(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0)) + throw runtime_error( + "keypoolrefill\n" + "Fills the keypool, requires wallet passphrase to be set."); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0)) + throw runtime_error( + "keypoolrefill\n" + "Fills the keypool."); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + pwalletMain->TopUpKeyPool(); + + if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100)) + throw JSONRPCError(-4, "Error refreshing keypool."); + + return Value::null; +} + + +void ThreadTopUpKeyPool(void* parg) +{ + pwalletMain->TopUpKeyPool(); +} + +void ThreadCleanWalletPassphrase(void* parg) +{ + int64 nMyWakeTime = GetTime() + *((int*)parg); + + if (nWalletUnlockTime == 0) + { + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + nWalletUnlockTime = nMyWakeTime; + } + + while (GetTime() < nWalletUnlockTime) + Sleep(GetTime() - nWalletUnlockTime); + + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + nWalletUnlockTime = 0; + } + } + else + { + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + if (nWalletUnlockTime < nMyWakeTime) + nWalletUnlockTime = nMyWakeTime; + } + free(parg); + return; + } + + pwalletMain->Lock(); + + delete (int*)parg; +} + +Value walletpassphrase(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) + throw runtime_error( + "walletpassphrase [stakeonly]\n" + "Stores the wallet decryption key in memory for seconds.\n" + "stakeonly is optional true/false allowing only stake creation."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + if (!pwalletMain->IsLocked()) + throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings."); + + // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + + CreateThread(ThreadTopUpKeyPool, NULL); + int* pnSleepTime = new int(params[1].get_int()); + CreateThread(ThreadCleanWalletPassphrase, pnSleepTime); + + // ppcoin: if user OS account compromised prevent trivial sendmoney commands + if (params.size() > 2) + fWalletUnlockStakeOnly = params[2].get_bool(); + else + fWalletUnlockStakeOnly = false; + + return Value::null; +} + + +Value walletpassphrasechange(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + + return Value::null; +} + + +Value walletlock(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) + throw runtime_error( + "walletlock\n" + "Removes the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called."); + + pwalletMain->Lock(); + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + nWalletUnlockTime = 0; + } + + return Value::null; +} + + +Value encryptwallet(const Array& params, bool fHelp) +{ + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + if (fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called."); + +#ifdef QT_GUI + // shutting down via RPC while the GUI is running does not work (yet): + throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command"); +#endif + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(-16, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + CreateThread(Shutdown, NULL); + return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet"; +} + + Value validateaddress(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "validateaddress \n" - "Return information about ."); + "validateaddress \n" + "Return information about ."); - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); Object ret; ret.push_back(Pair("isvalid", isValid)); @@ -1293,14 +1599,11 @@ Value validateaddress(const Array& params, bool fHelp) { // Call Hash160ToAddress() so we always return current ADDRESSVERSION // version of the address: - string currentAddress = Hash160ToAddress(hash160); + string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - if (pwalletMain->mapAddressBook.count(currentAddress)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); - } + ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0))); + if (pwalletMain->mapAddressBook.count(address)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); } return ret; } @@ -1312,19 +1615,20 @@ Value getwork(const Array& params, bool fHelp) throw runtime_error( "getwork [data]\n" "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash\n" + " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated " \"target\" : little endian hash target\n" "If [data] is specified, tries to solve the block and returns true if it was successful."); if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); + throw JSONRPCError(-9, "PPCoin is not connected!"); if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + throw JSONRPCError(-10, "PPCoin is downloading blocks..."); - static map > mapNewBlock; + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; static vector vNewBlock; static CReserveKey reservekey(pwalletMain); @@ -1351,7 +1655,7 @@ Value getwork(const Array& params, bool fHelp) nStart = GetTime(); // Create new block - pblock = CreateNewBlock(reservekey); + pblock = CreateNewBlock(pwalletMain); if (!pblock) throw JSONRPCError(-7, "Out of memory"); vNewBlock.push_back(pblock); @@ -1363,11 +1667,10 @@ Value getwork(const Array& params, bool fHelp) // Update nExtraNonce static unsigned int nExtraNonce = 0; - static int64 nPrevTime = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); // Prebuild hash buffers char pmidstate[32]; @@ -1378,9 +1681,9 @@ Value getwork(const Array& params, bool fHelp) uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); return result; } @@ -1394,17 +1697,16 @@ Value getwork(const Array& params, bool fHelp) // Byte reverse for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); // Get saved block if (!mapNewBlock.count(pdata->hashMerkleRoot)) return false; CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; pblock->nTime = pdata->nTime; pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); return CheckWork(pblock, *pwalletMain, reservekey); @@ -1412,14 +1714,290 @@ Value getwork(const Array& params, bool fHelp) } +Value getmemorypool(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getmemorypool [data]\n" + "If [data] is not specified, returns data needed to construct a block to work on:\n" + " \"version\" : block version\n" + " \"previousblockhash\" : hash of current highest block\n" + " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" + " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" + " \"time\" : timestamp appropriate for next block\n" + " \"bits\" : compressed target of next block\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + if (params.size() == 0) + { + if (vNodes.empty()) + throw JSONRPCError(-9, "PPCoin is not connected!"); + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "PPCoin is downloading blocks..."); + static CReserveKey reservekey(pwalletMain); + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + { + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + // Create new block + if(pblock) + delete pblock; + pblock = CreateNewBlock(pwalletMain); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + } + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + Array transactions; + BOOST_FOREACH(CTransaction tx, pblock->vtx) { + if(tx.IsCoinBase() || tx.IsCoinStake()) + continue; + + CDataStream ssTx; + ssTx << tx; + + transactions.push_back(HexStr(ssTx.begin(), ssTx.end())); + } + + Object result; + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("time", (int64_t)pblock->nTime)); + + union { + int32_t nBits; + char cBits[4]; + } uBits; + uBits.nBits = htonl((int32_t)pblock->nBits); + result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits)))); + + return result; + } + else + { + // Parse parameters + CDataStream ssBlock(ParseHex(params[0].get_str())); + CBlock pblock; + ssBlock >> pblock; + + return ProcessBlock(NULL, &pblock); + } +} + + +// ppcoin: reset auto checkpoint +Value resetcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "resetcheckpoint \n" + "Reset automatic checkpoint to specified height.\n" + " is the height of the new checkpoint block.\n"); + + int nCheckpoint = params[0].get_int(); + if (nCheckpoint <= 0 || nCheckpoint >= nBestHeight) + throw runtime_error( + "invalid checkpoint height.\n" + ); + if (nCheckpoint >= Checkpoints::nAutoCheckpoint) + throw runtime_error( + "new checkpoint must be earlier than current auto checkpoint.\n" + ); + if (!Checkpoints::ResetAutoCheckpoint(nCheckpoint)) + throw runtime_error( + "internal error - reset checkpoint failed.\n" + ); + + return Value::null; +} + + +// ppcoin: get branch point of alternative branch +Value getbranchpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbranchpoint\n" + "Returns height of branch point of alternative branch.\n"); + + Object result; + if (Checkpoints::nBranchPoint > 0) + result.push_back(Pair("branchpoint", Checkpoints::nBranchPoint)); + else + result.push_back(Pair("branchpoint", "none")); + result.push_back(Pair("checkpoint", Checkpoints::nAutoCheckpoint)); + return result; +} + + +// ppcoin: reserve balance from being staked for network protection +Value reservebalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "reservebalance [ [amount]]\n" + " is true or false to turn balance reserve on or off.\n" + " is a real and rounded to cent.\n" + "Set reserve amount not participating in network protection.\n" + "If no parameters provided current setting is printed.\n"); + + if (params.size() > 0) + { + bool fReserve = params[0].get_bool(); + if (fReserve) + { + if (params.size() == 1) + throw runtime_error("must provide amount to reserve balance.\n"); + int64 nAmount = AmountFromValue(params[1]); + nAmount = (nAmount / CENT) * CENT; // round to cent + if (nAmount < 0) + throw runtime_error("amount cannot be negative.\n"); + WriteSetting("nBalanceReserve", nBalanceReserve = nAmount); + } + else + { + if (params.size() > 1) + throw runtime_error("cannot specify amount to turn off reserve.\n"); + WriteSetting("nBalanceReserve", nBalanceReserve = 0); + } + } + Object result; + result.push_back(Pair("reserve", (nBalanceReserve > 0))); + result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve))); + return result; +} + + +// ppcoin: check wallet integrity +Value checkwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "checkwallet\n" + "Check wallet for integrity.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion)) + { + Object result; + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion))); + return result; + } + return Value::null; +} + + +// ppcoin: repair wallet +Value repairwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "repairwallet\n" + "Repair wallet if checkwallet reports any problem.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion); + Object result; + if (nMismatchSpent == 0) + { + result.push_back(Pair("wallet check passed", true)); + } + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + +extern CCriticalSection cs_mapAlerts; +extern map mapAlerts; + +// ppcoin: send alert +Value sendalert(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 5) + throw runtime_error( + "sendalert [cancelupto]\n" + " is the alert text message\n" + " is hex string of alert master private key\n" + " is the minimum applicable client version\n" + " is the maximum applicable client version\n" + " is the alert id\n" + "[cancelupto] cancels all alert id's up to this number\n" + "Returns true or false."); + + CAlert alert; + CKey key; + + alert.strStatusBar = params[0].get_str(); + alert.nMinVer = params[2].get_int(); + alert.nMaxVer = params[3].get_int(); + alert.nID = params[4].get_int(); + if (params.size() > 5) + alert.nCancel = params[5].get_int(); + alert.nVersion = VERSION; + alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; + alert.nExpiration = GetAdjustedTime() + 365*24*60*60; + alert.nPriority = 1; + + CDataStream sMsg; + sMsg << (CUnsignedAlert)alert; + alert.vchMsg = vector(sMsg.begin(), sMsg.end()); + + vector vchPrivKey = ParseHex(params[1].get_str()); + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + throw runtime_error( + "Unable to sign alert, check private key?\n"); + alert.ProcessAlert(); + // Relay alert + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + + Object result; + result.push_back(Pair("strStatusBar", alert.strStatusBar)); + result.push_back(Pair("nVersion", alert.nVersion)); + result.push_back(Pair("nMinVer", alert.nMinVer)); + result.push_back(Pair("nMaxVer", alert.nMaxVer)); + result.push_back(Pair("nID", alert.nID)); + if (alert.nCancel > 0) + result.push_back(Pair("nCancel", alert.nCancel)); + return result; +} +Value makekeypair(const Array& params, bool fHelp) +{ + CKey key; + key.MakeNewKey(); + CPrivKey vchPrivKey = key.GetPrivKey(); + + Object result; + result.push_back(Pair("PrivateKey", HexStr(vchPrivKey.begin(), vchPrivKey.end()))); + result.push_back(Pair("PublicKey", HexStr(key.GetPubKey()))); + return result; +} // // Call Table @@ -1427,44 +2005,53 @@ Value getwork(const Array& params, bool fHelp) pair pCallTable[] = { - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("setlabel", &setaccount), // deprecated - make_pair("getaccount", &getaccount), - make_pair("getlabel", &getaccount), // deprecated - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress - make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated - make_pair("backupwallet", &backupwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("getaccount", &getaccount), + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("backupwallet", &backupwallet), + make_pair("keypoolrefill", &keypoolrefill), + make_pair("walletpassphrase", &walletpassphrase), + make_pair("walletpassphrasechange", &walletpassphrasechange), + make_pair("walletlock", &walletlock), + make_pair("encryptwallet", &encryptwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("signmessage", &signmessage), + make_pair("verifymessage", &verifymessage), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), + make_pair("getmemorypool", &getmemorypool), + make_pair("listsinceblock", &listsinceblock), + make_pair("resetcheckpoint", &resetcheckpoint), + make_pair("getbranchpoint", &getbranchpoint), + make_pair("reservebalance", &reservebalance), + make_pair("checkwallet", &checkwallet), + make_pair("repairwallet", &repairwallet), + make_pair("sendalert", &sendalert), + make_pair("makekeypair", &makekeypair), }; map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); @@ -1473,7 +2060,7 @@ string pAllowInSafeMode[] = "help", "stop", "getblockcount", - "getblocknumber", + "getblocknumber", // deprecated "getconnectioncount", "getdifficulty", "getgenerate", @@ -1482,14 +2069,17 @@ string pAllowInSafeMode[] = "getinfo", "getnewaddress", "getaccountaddress", - "setlabel", "getaccount", - "getlabel", // deprecated "getaddressesbyaccount", - "getaddressesbylabel", // deprecated "backupwallet", + "keypoolrefill", + "walletpassphrase", + "walletlock", "validateaddress", "getwork", + "getmemorypool", + "resetcheckpoint", + "getbranchpoint", }; set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); @@ -1507,10 +2097,11 @@ string HTTPPost(const string& strMsg, const map& mapRequestHeader { ostringstream s; s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n" << "Host: 127.0.0.1\r\n" << "Content-Type: application/json\r\n" << "Content-Length: " << strMsg.size() << "\r\n" + << "Connection: close\r\n" << "Accept: application/json\r\n"; BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) s << item.first << ": " << item.second << "\r\n"; @@ -1532,12 +2123,12 @@ string rfc1123Time() return string(buffer); } -string HTTPReply(int nStatus, const string& strMsg) +static string HTTPReply(int nStatus, const string& strMsg) { if (nStatus == 401) return strprintf("HTTP/1.0 401 Authorization Required\r\n" "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" + "Server: ppcoin-json-rpc/%s\r\n" "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" "Content-Type: text/html\r\n" "Content-Length: 296\r\n" @@ -1551,22 +2142,24 @@ string HTTPReply(int nStatus, const string& strMsg) "\r\n" "

401 Unauthorized.

\r\n" "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - string strStatus; - if (nStatus == 200) strStatus = "OK"; - else if (nStatus == 400) strStatus = "Bad Request"; - else if (nStatus == 404) strStatus = "Not Found"; - else if (nStatus == 500) strStatus = "Internal Server Error"; + const char *cStatus; + if (nStatus == 200) cStatus = "OK"; + else if (nStatus == 400) cStatus = "Bad Request"; + else if (nStatus == 403) cStatus = "Forbidden"; + else if (nStatus == 404) cStatus = "Not Found"; + else if (nStatus == 500) cStatus = "Internal Server Error"; + else cStatus = ""; return strprintf( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" + "Server: ppcoin-json-rpc/%s\r\n" "\r\n" "%s", nStatus, - strStatus.c_str(), + cStatus, rfc1123Time().c_str(), strMsg.size(), FormatFullVersion().c_str(), @@ -1633,43 +2226,6 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe return nStatus; } -string EncodeBase64(string s) -{ - BIO *b64, *bmem; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_write(b64, s.c_str(), s.size()); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - string result(bptr->data, bptr->length); - BIO_free_all(b64); - - return result; -} - -string DecodeBase64(string s) -{ - BIO *b64, *bmem; - - char* buffer = static_cast(calloc(s.size(), sizeof(char))); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); - - string result(buffer); - free(buffer); - return result; -} - bool HTTPAuthorized(map& mapHeaders) { string strAuth = mapHeaders["authorization"]; @@ -1677,12 +2233,7 @@ bool HTTPAuthorized(map& mapHeaders) return false; string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); string strUserPass = DecodeBase64(strUserPass64); - string::size_type nColon = strUserPass.find(":"); - if (nColon == string::npos) - return false; - string strUser = strUserPass.substr(0, nColon); - string strPassword = strUserPass.substr(nColon+1); - return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); + return strUserPass == strRPCUserColonPass; } // @@ -1815,19 +2366,22 @@ void ThreadRPCServer2(void* parg) { printf("ThreadRPCServer started\n"); - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if (strRPCUserColonPass == ":") { - string strWhatAmI = "To use bitcoind"; + string strWhatAmI = "To use ppcoind"; if (mapArgs.count("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); else if (mapArgs.count("-daemon")) strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); PrintConsole( - _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" + _("Error: %s, you must set rpcpassword=\nin the configuration file: %s\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), GetConfigFile().c_str()); +#ifndef QT_GUI CreateThread(Shutdown, NULL); +#endif return; } @@ -1835,7 +2389,7 @@ void ThreadRPCServer2(void* parg) asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT)); ip::tcp::acceptor acceptor(io_service, endpoint); acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); @@ -1860,7 +2414,7 @@ void ThreadRPCServer2(void* parg) } #else if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries."); #endif loop @@ -1887,7 +2441,12 @@ void ThreadRPCServer2(void* parg) // Restrict callers by IP if (!ClientAllowed(peer.address().to_string())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + stream << HTTPReply(403, "") << std::flush; continue; + } map mapHeaders; string strRequest; @@ -1936,7 +2495,7 @@ void ThreadRPCServer2(void* parg) if (valMethod.type() != str_type) throw JSONRPCError(-32600, "Method must be a string"); string strMethod = valMethod.get_str(); - if (strMethod != "getwork") + if (strMethod != "getwork" && strMethod != "getmemorypool") printf("ThreadRPCServer method=%s\n", strMethod.c_str()); // Parse params @@ -1962,7 +2521,10 @@ void ThreadRPCServer2(void* parg) try { // Execute - Value result = (*(*mi).second)(params, false); + Value result; + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_wallet) + result = (*(*mi).second)(params, false); // Send reply string strReply = JSONRPCReply(result, Value::null, id); @@ -2004,13 +2566,13 @@ Object CallRPC(const string& strMethod, const Array& params) SSLStream sslStream(io_service, context); SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()))) throw runtime_error("couldn't connect to server"); #else if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries."); - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); + ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())); if (stream.fail()) throw runtime_error("couldn't connect to server"); #endif @@ -2099,18 +2661,12 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); if (strMethod == "move" && n > 2) ConvertTo(params[2]); if (strMethod == "move" && n > 3) ConvertTo(params[3]); @@ -2119,6 +2675,13 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "walletpassphrase" && n > 2) ConvertTo(params[2]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendalert" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendalert" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendalert" && n > 4) ConvertTo(params[4]); + if (strMethod == "sendalert" && n > 5) ConvertTo(params[5]); if (strMethod == "sendmany" && n > 1) { string s = params[1].get_str(); @@ -2128,6 +2691,9 @@ int CommandLineRPC(int argc, char *argv[]) params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "resetcheckpoint" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); // Execute Object reply = CallRPC(strMethod, params); @@ -2135,7 +2701,6 @@ int CommandLineRPC(int argc, char *argv[]) // Parse reply const Value& result = find_value(reply, "result"); const Value& error = find_value(reply, "error"); - const Value& id = find_value(reply, "id"); if (error.type() != null_type) { @@ -2167,13 +2732,7 @@ int CommandLineRPC(int argc, char *argv[]) if (strPrint != "") { -#if defined(__WXMSW__) && defined(GUI) - // Windows GUI apps can't print to command line, - // so settle for a message box yuck - MyMessageBox(strPrint, "Bitcoin", wxOK); -#else fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); -#endif } return nRet; }