X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fbitcoinrpc.cpp;h=b32b86eb3ddbbf16c217eff0e7e6e891dd480ac6;hp=f149dd2af0777a57517cfa711d8d4334315a94fb;hb=7124035567b03ca15027318b75f3a398ae7b4d89;hpb=65abc2081496fed54e01b6f3b43db2df6d52a665 diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index f149dd2..b32b86e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1,36 +1,32 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2013 The PPCoin developers -// Copyright (c) 2013 NovaCoin Developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "main.h" -#include "wallet.h" -#include "db.h" -#include "walletdb.h" -#include "net.h" #include "init.h" -#include "checkpoints.h" +#include "util.h" +#include "sync.h" #include "ui_interface.h" +#include "base58.h" #include "bitcoinrpc.h" +#include "db.h" #undef printf #include +#include +#include #include +#include #include #include #include #include -#include +#include #include -typedef boost::asio::ssl::stream SSLStream; +#include +#include #define printf OutputDebugStringF -// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are -// precompiled in headers.h. The problem might be when the pch file goes over -// a certain size around 145MB. If we need access to json_spirit outside this -// file, we could use the compiled json_spirit option. using namespace std; using namespace boost; @@ -41,11 +37,14 @@ void ThreadRPCServer2(void* parg); static std::string strRPCUserColonPass; -static int64 nWalletUnlockTime; -static CCriticalSection cs_nWalletUnlockTime; +const Object emptyobj; -extern Value dumpprivkey(const Array& params, bool fHelp); -extern Value importprivkey(const Array& params, bool fHelp); +void ThreadRPCServer3(void* parg); + +static inline unsigned short GetDefaultRPCPort() +{ + return GetBoolArg("-testnet", false) ? 18344 : 8344; +} Object JSONRPCError(int code, const string& message) { @@ -55,56 +54,63 @@ Object JSONRPCError(int code, const string& message) return error; } -double GetDifficulty(const CBlockIndex* blockindex = NULL) +void RPCTypeCheck(const Array& params, + const list& typesExpected, + bool fAllowNull) { - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - if (blockindex == NULL) + unsigned int i = 0; + BOOST_FOREACH(Value_type t, typesExpected) { - if (pindexBest == NULL) - return 1.0; - else - blockindex = GetLastBlockIndex(pindexBest, false); - } - - int nShift = (blockindex->nBits >> 24) & 0xff; - - double dDiff = - (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + if (params.size() <= i) + break; - while (nShift < 29) - { - dDiff *= 256.0; - nShift++; + const Value& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s, got %s", + Value_type_name[t], Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + i++; } - while (nShift > 29) +} + +void RPCTypeCheck(const Object& o, + const map& typesExpected, + bool fAllowNull) +{ + BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) { - dDiff /= 256.0; - nShift--; - } + const Value& v = find_value(o, t.first); + if (!fAllowNull && v.type() == null_type) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str())); - return dDiff; + if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s for %s, got %s", + Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + } } - -int64 AmountFromValue(const Value& value) +int64_t AmountFromValue(const Value& value) { double dAmount = value.get_real(); if (dAmount <= 0.0 || dAmount > MAX_MONEY) - throw JSONRPCError(-3, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + int64_t nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) - throw JSONRPCError(-3, "Invalid amount"); + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); return nAmount; } -Value ValueFromAmount(int64 amount) +Value ValueFromAmount(int64_t amount) { return (double)amount / (double)COIN; } -std::string -HexBits(unsigned int nBits) +std::string HexBits(unsigned int nBits) { union { int32_t nBits; @@ -114,123 +120,44 @@ HexBits(unsigned int nBits) return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); } -unsigned int BitsHex(std::string HexBits) -{ - union { - int32_t nBits; - char cBits[4]; - } uBits; - - vector vchBits = ParseHex(HexBits); - copy(vchBits.begin(), vchBits.begin() + 4, uBits.cBits); - uBits.nBits = htonl((int32_t)uBits.nBits); - return uBits.nBits; -} -void TxToJSON(const CTransaction &tx, Object& entry) -{ - entry.push_back(Pair("version", tx.nVersion)); - entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); - entry.push_back(Pair("size", (boost::int64_t)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); - Array vin; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - Object in; - if (tx.IsCoinBase()) - in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - else - { - Object prevout; - prevout.push_back(Pair("hash", txin.prevout.hash.GetHex())); - prevout.push_back(Pair("n", (boost::int64_t)txin.prevout.n)); - in.push_back(Pair("prevout", prevout)); - in.push_back(Pair("scriptSig", txin.scriptSig.ToString())); - } - in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); - vin.push_back(in); - } - entry.push_back(Pair("vin", vin)); - Array vout; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - Object out; - out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - out.push_back(Pair("scriptPubKey", txout.scriptPubKey.ToString())); - vout.push_back(out); - } - entry.push_back(Pair("vout", vout)); +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; } -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +uint256 ParseHashO(const Object& o, string strKey) { - int confirms = wtx.GetDepthInMainChain(); - entry.push_back(Pair("confirmations", confirms)); - if (confirms) - { - entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); - entry.push_back(Pair("blockindex", wtx.nIndex)); - } - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); - entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); + return ParseHashV(find_value(o, strKey), strKey); } -string AccountFromValue(const Value& value) +vector ParseHexV(const Value& v, string strName) { - string strAccount = value.get_str(); - if (strAccount == "*") - throw JSONRPCError(-11, "Invalid account name"); - return strAccount; + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); } -Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPrintTransactionDetail) +vector ParseHexO(const Object& o, string strKey) { - Object result; - result.push_back(Pair("hash", block.GetHash().GetHex())); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("time", DateTimeStrFormat(block.GetBlockTime()))); - result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); - result.push_back(Pair("bits", HexBits(block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("mint", ValueFromAmount(blockindex->nMint))); - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - if (blockindex->pnext) - result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); - result.push_back(Pair("flags", strprintf("%s%s", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work", blockindex->GeneratedStakeModifier()? " stake-modifier": ""))); - result.push_back(Pair("proofhash", blockindex->IsProofOfStake()? blockindex->hashProofOfStake.GetHex() : blockindex->GetBlockHash().GetHex())); - result.push_back(Pair("entropybit", (int)blockindex->GetStakeEntropyBit())); - result.push_back(Pair("modifier", strprintf("%016"PRI64x, blockindex->nStakeModifier))); - result.push_back(Pair("modifierchecksum", strprintf("%08x", blockindex->nStakeModifierChecksum))); - Array txinfo; - BOOST_FOREACH (const CTransaction& tx, block.vtx) - { - if (fPrintTransactionDetail) - { - Object entry; - - entry.push_back(Pair("txid", tx.GetHash().GetHex())); - TxToJSON(tx, entry); - entry.push_back(Pair("time", DateTimeStrFormat(tx.nTime))); - - txinfo.push_back(entry); - } - else - txinfo.push_back(tx.GetHash().GetHex()); - } - - result.push_back(Pair("tx", txinfo)); - result.push_back(Pair("signature", HexStr(block.vchBlockSig.begin(), block.vchBlockSig.end()))); - - return result; + return ParseHexV(find_value(o, strKey), strKey); } - /// /// Note: This interface may still be subject to change. /// @@ -244,12 +171,9 @@ string CRPCTable::help(string strCommand) const const CRPCCommand *pcmd = mi->second; string strMethod = mi->first; // 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)) + if (strMethod.find("label") != string::npos) continue; - if (strCommand != "" && strMethod != strCommand) + if (!strCommand.empty() && strMethod != strCommand) continue; try { @@ -262,13 +186,13 @@ string CRPCTable::help(string strCommand) const { // Help text is returned in an exception string strHelp = string(e.what()); - if (strCommand == "") + if (strCommand.empty()) if (strHelp.find('\n') != string::npos) strHelp = strHelp.substr(0, strHelp.find('\n')); strRet += strHelp + "\n"; } } - if (strRet == "") + if (strRet.empty()) strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); strRet = strRet.substr(0,strRet.size()-1); return strRet; @@ -291,2364 +215,16 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { - if (fHelp || params.size() != 0) - throw runtime_error( - "stop\n" - "Stop novacoin server."); - // Shutdown will take long enough that the response should get back - StartShutdown(); - return "novacoin server stopping"; -} - - -Value getblockcount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "Returns the number of blocks in the longest block chain."); - - return nBestHeight; -} - - -// deprecated -Value getblocknumber(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblocknumber\n" - "Deprecated. Use getblockcount."); - - return nBestHeight; -} - - -Value getconnectioncount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "Returns the number of connections to other nodes."); - - return (int)vNodes.size(); -} - - -Value getdifficulty(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "Returns difficulty as a multiple of the minimum difficulty."); - - Object obj; - obj.push_back(Pair("proof-of-work", GetDifficulty())); - obj.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true)))); - obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval)); - return obj; -} - - -Value getgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "Returns true or false."); - - return GetBoolArg("-gen"); -} - - -Value setgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate [genproclimit]\n" - " is true or false to turn generation on or off.\n" - "Generation is limited to [genproclimit] processors, -1 is unlimited."); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - if (params.size() > 1) - { - int nGenProcLimit = params[1].get_int(); - mapArgs["-genproclimit"] = itostr(nGenProcLimit); - if (nGenProcLimit == 0) - fGenerate = false; - } - mapArgs["-gen"] = (fGenerate ? "1" : "0"); - - GenerateBitcoins(fGenerate, pwalletMain); - return Value::null; -} - - -Value gethashespersec(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gethashespersec\n" - "Returns a recent hashes per second performance measurement while generating."); - - if (GetTimeMillis() - nHPSTimerStart > 8000) - return (boost::int64_t)0; - return (boost::int64_t)dHashesPerSec; -} - - -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info."); - - Object obj; - obj.push_back(Pair("version", FormatFullVersion())); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); - obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply))); - 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("difficulty", (double)GetDifficulty())); - 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 / 1000)); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - -Value getmininginfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmininginfo\n" - "Returns an object containing mining-related information."); - - Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); - obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", GetBoolArg("-gen"))); - obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", fTestNet)); - return obj; -} - - -Value getnewaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress [account]\n" - "Returns a new novacoin 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]."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); - - // Generate a new key that is added to wallet - 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); - - return address.ToString(); -} - - -CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - bool bKeyUsed = false; - - // Check if the current key has been used - if (!account.vchPubKey.empty()) - { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); - ++it) - { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - bKeyUsed = true; - } - } - - // Generate a new key - if (account.vchPubKey.empty() || bForceNew || bKeyUsed) - { - 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); - } - - return CBitcoinAddress(account.vchPubKey); -} - -Value getaccountaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \n" - "Returns the current novacoin 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; - - ret = GetAccountAddress(strAccount).ToString(); - - return ret; -} - - - -Value setaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \n" - "Sets the account associated with the given address."); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid novacoin address"); - - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwalletMain->mapAddressBook.count(address)) - { - string strOldAccount = pwalletMain->mapAddressBook[address]; - if (address == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } - - pwalletMain->SetAddressBookName(address, strAccount); - - return Value::null; -} - - -Value getaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \n" - "Returns the account associated with the given address."); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid novacoin address"); - - string strAccount; - map::iterator mi = pwalletMain->mapAddressBook.find(address); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - return strAccount; -} - - -Value getaddressesbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \n" - "Returns the list of addresses for the given account."); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - Array ret; - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) - { - 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 || AmountFromValue(params[0]) < MIN_TX_FEE) - throw runtime_error( - "settxfee \n" - " is a real and is rounded to 0.01 (cent)\n" - "Minimum and default transaction fee per KB is 1 cent"); - - nTransactionFee = AmountFromValue(params[0]); - nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent - return true; -} - -Value sendtoaddress(const Array& params, bool fHelp) -{ - 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\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"); - - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid novacoin address"); - - // Amount - int64 nAmount = AmountFromValue(params[1]); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - - // Wallet comments - CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); - - 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(); -} - -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, 0); - 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, 0); - ss << strMessageMagic; - ss << strMessage; - - CKey key; - if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) - return false; - - return (CBitcoinAddress(key.GetPubKey()) == 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."); - - // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); - CScript scriptPubKey; - if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid novacoin address"); - scriptPubKey.SetBitcoinAddress(address); - if (!IsMine(*pwalletMain,scriptPubKey)) - return (double)0.0; - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Tally - int64 nAmount = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - 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; - } - - return ValueFromAmount(nAmount); -} - - -void GetAccountAddresses(string strAccount, set& setAddress) -{ - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) - { - const CBitcoinAddress& address = item.first; - const string& strName = item.second; - if (strName == strAccount) - setAddress.insert(address); - } -} - - -Value getreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount [minconf=1]\n" - "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Get the set of pub keys assigned to account - string strAccount = AccountFromValue(params[0]); - set setAddress; - GetAccountAddresses(strAccount, setAddress); - - // Tally - int64 nAmount = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - CBitcoinAddress address; - if (ExtractAddress(txout.scriptPubKey, address) && pwalletMain->HaveKey(address) && setAddress.count(address)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return (double)nAmount / (double)COIN; -} - - -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) -{ - int64 nBalance = 0; - - // 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); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - - return nBalance; -} - -int64 GetAccountBalance(const string& strAccount, int nMinDepth) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth); -} - - -Value getbalance(const Array& params, bool fHelp) -{ - 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" - "If [account] is specified, returns the balance in the account."); - - if (params.size() == 0) - return ValueFromAmount(pwalletMain->GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' should always return the same number. - int64 nBalance = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived) - nBalance += r.second; - } - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent) - nBalance -= r.second; - nBalance -= allFee; - nBalance += allGeneratedMature; - } - return ValueFromAmount(nBalance); - } - - string strAccount = AccountFromValue(params[0]); - - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - - return ValueFromAmount(nBalance); -} - - -Value getpowreward(const Array& params, bool fHelp) -{ if (fHelp || params.size() > 1) throw runtime_error( - "getpowreward [nBits]\n" - "Returns PoW reward for block with provided difficulty."); - - if (params.size() == 0) - throw JSONRPCError(-200, "no bits provided"); - - std::string sBits = params[0].get_str(); - - if (sBits.length() != 8) - throw JSONRPCError(-201, "incorrect bits provided"); - - unsigned int nBits = BitsHex(sBits); - - return (int)GetProofOfWorkReward(nBits); -} - - - -Value movecmd(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move [minconf=1] [comment]\n" - "Move from one account in your wallet to another."); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); - if (params.size() > 3) - // 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(); - - CWalletDB walletdb(pwalletMain->strWalletFile); - if (!walletdb.TxnBegin()) - throw JSONRPCError(-20, "database error"); - - 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); - - if (!walletdb.TxnCommit()) - throw JSONRPCError(-20, "database error"); - - return true; -} - - -Value sendfrom(const Array& params, bool fHelp) -{ - 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\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]); - CBitcoinAddress address(params[1].get_str()); - if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid novacoin address"); - int64 nAmount = AmountFromValue(params[2]); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - if (pwalletMain->IsLocked()) - throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); - - // 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 (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"); - - string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); - int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); - - set setAddress; - vector > vecSend; - - int64 totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) - { - CBitcoinAddress address(s.name_); - if (!address.IsValid()) - throw JSONRPCError(-5, string("Invalid novacoin address:")+s.name_); - - if (setAddress.count(address)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_); - setAddress.insert(address); - - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(address); - int64 nAmount = AmountFromValue(s.value_); - if (nAmount < MIN_TXOUT_AMOUNT) - throw JSONRPCError(-101, "Send amount too small"); - totalAmount += nAmount; - - vecSend.push_back(make_pair(scriptPubKey, nAmount)); - } - - if (pwalletMain->IsLocked()) - throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); - if (fWalletUnlockMintOnly) - throw JSONRPCError(-13, "Error: Wallet unlocked for block minting 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) - { - 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(); -} - -Value addmultisigaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - { - string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" - "Add a nrequired-to-sign multisignature address to the wallet\"\n" - "each key is a bitcoin address or hex-encoded public key\n" - "If [account] is specified, assign address to [account]."; - throw runtime_error(msg); - } - - int nRequired = params[0].get_int(); - const Array& keys = params[1].get_array(); - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); - - // Gather public keys - if (nRequired < 1) - throw runtime_error("a multisignature address must require at least one key to redeem"); - if ((int)keys.size() < nRequired) - throw runtime_error( - strprintf("not enough keys supplied " - "(got %d keys, but need at least %d to redeem)", keys.size(), nRequired)); - std::vector pubkeys; - pubkeys.resize(keys.size()); - for (unsigned int i = 0; i < keys.size(); i++) - { - const std::string& ks = keys[i].get_str(); - - // Case 1: bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (address.IsValid()) - { - if (address.IsScript()) - throw runtime_error( - strprintf("%s is a pay-to-script address",ks.c_str())); - std::vector vchPubKey; - if (!pwalletMain->GetPubKey(address, vchPubKey)) - throw runtime_error( - strprintf("no full public key for address %s",ks.c_str())); - if (vchPubKey.empty() || !pubkeys[i].SetPubKey(vchPubKey)) - throw runtime_error(" Invalid public key: "+ks); - } - - // Case 2: hex public key - else if (IsHex(ks)) - { - vector vchPubKey = ParseHex(ks); - if (vchPubKey.empty() || !pubkeys[i].SetPubKey(vchPubKey)) - throw runtime_error(" Invalid public key: "+ks); - } - else - { - throw runtime_error(" Invalid public key: "+ks); - } - } - - // Construct using pay-to-script-hash: - CScript inner; - inner.SetMultisig(nRequired, pubkeys); - - uint160 scriptHash = Hash160(inner); - CScript scriptPubKey; - scriptPubKey.SetPayToScriptHash(inner); - pwalletMain->AddCScript(inner); - CBitcoinAddress address; - address.SetScriptHash160(scriptHash); - - pwalletMain->SetAddressBookName(address, strAccount); - return address.ToString(); -} - - -struct tallyitem -{ - int64 nAmount; - int nConf; - tallyitem() - { - nAmount = 0; - nConf = std::numeric_limits::max(); - } -}; - -Value ListReceived(const Array& params, bool fByAccounts) -{ - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - // Whether to include empty accounts - bool fIncludeEmpty = false; - if (params.size() > 1) - fIncludeEmpty = params[1].get_bool(); - - // Tally - map mapTally; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - - if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - CBitcoinAddress address; - if (!ExtractAddress(txout.scriptPubKey, address) || !pwalletMain->HaveKey(address) || !address.IsValid()) - continue; - - tallyitem& item = mapTally[address]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } - } - - // Reply - Array ret; - map mapAccountTally; - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) - { - 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 = std::numeric_limits::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", address.ToString())); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); - ret.push_back(obj); - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - int64 nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - Object obj; - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); - ret.push_back(obj); - } - } - - return ret; -} - -Value listreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaddress [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include addresses that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"address\" : receiving address\n" - " \"account\" : the account of the receiving address\n" - " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, false); -} - -Value listreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaccount [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include accounts that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"account\" : the account of the receiving addresses\n" - " \"amount\" : total amount received by addresses with this account\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, true); -} - -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) -{ - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - - bool fAllAccounts = (strAccount == string("*")); - - // Generated blocks assigned to account "" - if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) - { - Object entry; - entry.push_back(Pair("account", string(""))); - if (nGeneratedImmature) - { - entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); - } - else - { - entry.push_back(Pair("category", "generate")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); - } - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - - // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent) - { - Object entry; - entry.push_back(Pair("account", strSentAccount)); - 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))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - - // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived) - { - 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.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) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - Object entry; - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); - ret.push_back(entry); - } -} - -Value listtransactions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listtransactions [account] [count=10] [from=0]\n" - "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); - - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); - int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); - int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); - - if (nCount < 0) - throw JSONRPCError(-8, "Negative count"); - if (nFrom < 0) - throw JSONRPCError(-8, "Negative from"); - - Array ret; - CWalletDB walletdb(pwalletMain->strWalletFile); - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry - // would make this much faster for applications that do this a lot. - 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))); - } - - // iterate backwards until we have nCount items to return: - for (TxItems::reverse_iterator it = txByTime.rbegin(); 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+nFrom)) break; - } - // ret is newest to oldest - - if (nFrom > (int)ret.size()) - nFrom = ret.size(); - if ((nFrom + nCount) > (int)ret.size()) - nCount = ret.size() - nFrom; - Array::iterator first = ret.begin(); - std::advance(first, nFrom); - Array::iterator last = ret.begin(); - std::advance(last, nFrom+nCount); - - if (last != ret.end()) ret.erase(last, ret.end()); - if (first != ret.begin()) ret.erase(ret.begin(), first); - - std::reverse(ret.begin(), ret.end()); // Return oldest to newest - - return ret; -} - -Value listaccounts(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "listaccounts [minconf=1]\n" - "Returns Object that has account names as keys, account balances as values."); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - map mapAccountBalances; - 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) - { - 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) - { - 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; - } - } - - list acentries; - CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); - } - return ret; -} - -Value listsinceblock(const Array& params, bool fHelp) -{ - if (fHelp) - throw runtime_error( - "listsinceblock [blockhash] [target-confirmations]\n" - "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); - - CBlockIndex *pindex = NULL; - int target_confirms = 1; - + "stop \n" + " is true or false to detach the database or not for this stop only\n" + "Stop NovaCoin server (and possibly override the detachdb config value)."); + // Shutdown will take long enough that the response should get back 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) - { - 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) - throw runtime_error( - "gettransaction \n" - "Get detailed information about "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - - if (pwalletMain->mapWallet.count(hash)) - { - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - - TxToJSON(wtx, entry); - - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); - - WalletTxToJSON(wtx, entry); - - Array details; - ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); - entry.push_back(Pair("details", details)); - } - else - { - CTransaction tx; - uint256 hashBlock = 0; - if (GetTransaction(hash, tx, hashBlock)) - { - entry.push_back(Pair("txid", hash.GetHex())); - TxToJSON(tx, entry); - if (hashBlock == 0) - entry.push_back(Pair("confirmations", 0)); - else - { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); - entry.push_back(Pair("txntime", (boost::int64_t)tx.nTime)); - entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); - } - else - entry.push_back(Pair("confirmations", 0)); - } - } - } - else - throw JSONRPCError(-5, "No information available about transaction"); - } - - return entry; -} - - -Value backupwallet(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "backupwallet \n" - "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); - - string strDest = params[0].get_str(); - BackupWallet(*pwalletMain, strDest); - - return Value::null; -} - - -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 = GetTimeMillis() + *((int64*)parg) * 1000; - - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - if (nWalletUnlockTime == 0) - { - nWalletUnlockTime = nMyWakeTime; - - do - { - if (nWalletUnlockTime==0) - break; - int64 nToSleep = nWalletUnlockTime - GetTimeMillis(); - if (nToSleep <= 0) - break; - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - Sleep(nToSleep); - ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); - - } while(1); - - if (nWalletUnlockTime) - { - nWalletUnlockTime = 0; - pwalletMain->Lock(); - } - } - else - { - if (nWalletUnlockTime < nMyWakeTime) - nWalletUnlockTime = nMyWakeTime; - } - - LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - - delete (int64*)parg; -} - -Value walletpassphrase(const Array& params, bool fHelp) -{ - if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) - throw runtime_error( - "walletpassphrase [mintonly]\n" - "Stores the wallet decryption key in memory for seconds.\n" - "mintonly is optional true/false allowing only block minting."); - 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); - int64* pnSleepTime = new int64(params[1].get_int64()); - CreateThread(ThreadCleanWalletPassphrase, pnSleepTime); - - // ppcoin: if user OS account compromised prevent trivial sendmoney commands - if (params.size() > 2) - fWalletUnlockMintOnly = params[2].get_bool(); - else - fWalletUnlockMintOnly = 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."); - - { - LOCK(cs_nWalletUnlockTime); - pwalletMain->Lock(); - 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."); - - // 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: + bitdb.SetDetach(params[0].get_bool()); StartShutdown(); - return "wallet encrypted; novacoin 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 ."); - - CBitcoinAddress address(params[0].get_str()); - bool isValid = address.IsValid(); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - // Call Hash160ToAddress() so we always return current ADDRESSVERSION - // version of the address: - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - if (pwalletMain->HaveKey(address)) - { - ret.push_back(Pair("ismine", true)); - std::vector vchPubKey; - pwalletMain->GetPubKey(address, vchPubKey); - ret.push_back(Pair("pubkey", HexStr(vchPubKey))); - CKey key; - key.SetPubKey(vchPubKey); - ret.push_back(Pair("iscompressed", key.IsCompressed())); - } - else if (pwalletMain->HaveCScript(address.GetHash160())) - { - ret.push_back(Pair("isscript", true)); - CScript subscript; - pwalletMain->GetCScript(address.GetHash160(), subscript); - ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript))); - std::vector addresses; - txnouttype whichType; - int nRequired; - ExtractAddresses(subscript, whichType, addresses, nRequired); - ret.push_back(Pair("script", GetTxnOutputType(whichType))); - Array a; - BOOST_FOREACH(const CBitcoinAddress& addr, addresses) - a.push_back(addr.ToString()); - ret.push_back(Pair("addresses", a)); - if (whichType == TX_MULTISIG) - ret.push_back(Pair("sigsrequired", nRequired)); - } - else - ret.push_back(Pair("ismine", false)); - if (pwalletMain->mapAddressBook.count(address)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); - } - return ret; -} - -Value validatepubkey(const Array& params, bool fHelp) -{ - if (fHelp || !params.size() || params.size() > 2) - throw runtime_error( - "validatepubkey \n" - "Return information about ."); - - std::vector vchPubKey = ParseHex(params[0].get_str()); - bool isValid; - - if(vchPubKey.size() == 33) // Compressed key - isValid = (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03); - else if(vchPubKey.size() == 65) // Uncompressed key - isValid = vchPubKey[0] == 0x04; - else - isValid = false; - - CBitcoinAddress address(vchPubKey); - isValid = isValid ? address.IsValid() : false; - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - // Call Hash160ToAddress() so we always return current ADDRESSVERSION - // version of the address: - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - if (pwalletMain->HaveKey(address)) - { - ret.push_back(Pair("ismine", true)); - CKey key; - key.SetPubKey(vchPubKey); - ret.push_back(Pair("iscompressed", key.IsCompressed())); - } - else - ret.push_back(Pair("ismine", false)); - if (pwalletMain->mapAddressBook.count(address)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); - } - return ret; -} - - -Value getwork(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - 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 (DEPRECATED)\n" // deprecated - " \"data\" : block data\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, "NovaCoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "NovaCoin is downloading blocks..."); - - typedef map > mapNewBlock_t; - static mapNewBlock_t mapNewBlock; - static vector vNewBlock; - static CReserveKey reservekey(pwalletMain); - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlock* pblock; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != pindexBest) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) - delete pblock; - vNewBlock.clear(); - } - nTransactionsUpdatedLast = nTransactionsUpdated; - pindexPrev = pindexBest; - nStart = GetTime(); - - // Create new block - pblock = CreateNewBlock(pwalletMain); - if (!pblock) - throw JSONRPCError(-7, "Out of memory"); - vNewBlock.push_back(pblock); - } - - // Update nTime - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); - - // Prebuild hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - Object result; - 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)))); // deprecated - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(-8, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; 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; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - if (!pblock->SignBlock(*pwalletMain)) - throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); - - return CheckWork(pblock, *pwalletMain, reservekey); - } -} - -Value getblocktemplate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getblocktemplate [params]\n" - "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" - " \"coinbaseaux\" : data that should be included in coinbase\n" - " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" - " \"target\" : hash target\n" - " \"mintime\" : minimum timestamp appropriate for next block\n" - " \"curtime\" : current timestamp\n" - " \"mutable\" : list of ways the block template may be changed\n" - " \"noncerange\" : range of valid nonces\n" - " \"sigoplimit\" : limit of sigops in blocks\n" - " \"sizelimit\" : limit of block size\n" - " \"bits\" : compressed target of next block\n" - " \"height\" : height of the next block\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); - - std::string strMode = "template"; - if (params.size() > 0) - { - const Object& oparam = params[0].get_obj(); - const Value& modeval = find_value(oparam, "mode"); - if (modeval.type() == str_type) - strMode = modeval.get_str(); - else - throw JSONRPCError(-8, "Invalid mode"); - } - - if (strMode != "template") - throw JSONRPCError(-8, "Invalid mode"); - - if (vNodes.empty()) - throw JSONRPCError(-9, "NovaCoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "NovaCoin 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)) - { - // Clear pindexPrev so future calls make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; - nStart = GetTime(); - - // Create new block - if(pblock) - { - delete pblock; - pblock = NULL; - } - pblock = CreateNewBlock(pwalletMain); - if (!pblock) - throw JSONRPCError(-7, "Out of memory"); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - - // Update nTime - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - Array transactions; - map setTxIndex; - int i = 0; - CTxDB txdb("r"); - BOOST_FOREACH (CTransaction& tx, pblock->vtx) - { - uint256 txHash = tx.GetHash(); - setTxIndex[txHash] = i++; - - if (tx.IsCoinBase() || tx.IsCoinStake()) - continue; - - Object entry; - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << tx; - entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); - - entry.push_back(Pair("hash", txHash.GetHex())); - - MapPrevTx mapInputs; - map mapUnused; - bool fInvalid = false; - if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) - { - entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); - - Array deps; - BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) - { - if (setTxIndex.count(inp.first)) - deps.push_back(setTxIndex[inp.first]); - } - entry.push_back(Pair("depends", deps)); - - int64_t nSigOps = tx.GetLegacySigOpCount(); - nSigOps += tx.GetP2SHSigOpCount(mapInputs); - entry.push_back(Pair("sigops", nSigOps)); - } - - transactions.push_back(entry); - } - - Object aux; - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - static Array aMutable; - if (aMutable.empty()) - { - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); - } - - 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("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); - result.push_back(Pair("curtime", (int64_t)pblock->nTime)); - result.push_back(Pair("bits", HexBits(pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - - return result; -} - -Value submitblock(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "submitblock [optional-params-obj]\n" - "[optional-params-obj] parameter is currently ignored.\n" - "Attempts to submit new block to network.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); - - vector blockData(ParseHex(params[0].get_str())); - CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); - CBlock block; - try { - ssBlock >> block; - } - catch (std::exception &e) { - throw JSONRPCError(-22, "Block decode failed"); - } - - static CReserveKey reservekey(pwalletMain); - - if(!block.SignBlock(*pwalletMain)) - throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); - - bool fAccepted = CheckWork(&block, *pwalletMain, reservekey); - if (!fAccepted) - return "rejected"; - - return Value::null; -} - - -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" - " \"coinbaseflags\" : data that should be included in coinbase so support for new features can be judged\n" - " \"time\" : timestamp appropriate for next block\n" - " \"mintime\" : minimum timestamp appropriate for next block\n" - " \"curtime\" : current timestamp\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, "NovaCoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "NovaCoin 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->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - Array transactions; - BOOST_FOREACH(CTransaction tx, pblock->vtx) { - if(tx.IsCoinBase() || tx.IsCoinStake()) - continue; - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - 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("coinbaseflags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - result.push_back(Pair("time", (int64_t)pblock->nTime)); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("curtime", (int64_t)GetAdjustedTime())); - result.push_back(Pair("bits", HexBits(pblock->nBits))); - - return result; - } - else - { - // Parse parameters - CDataStream ssBlock(ParseHex(params[0].get_str()), SER_NETWORK, PROTOCOL_VERSION); - CBlock pblock; - ssBlock >> pblock; - - static CReserveKey reservekey(pwalletMain); - - if(!pblock.SignBlock(*pwalletMain)) - throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); - - return CheckWork(&pblock, *pwalletMain, reservekey); - } -} - -Value getnewpubkey(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewpubkey [account]\n" - "Returns new public key for coinbase generation."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); - - // Generate a new key that is added to wallet - std::vector newKey = pwalletMain->GenerateNewKey(true); - - if(!newKey.size()) - throw JSONRPCError(-12, "Error: Unable to create key"); - - CBitcoinAddress address(newKey); - pwalletMain->SetAddressBookName(address, strAccount); - - return HexStr(newKey.begin(), newKey.end()); -} - -Value getblockhash(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblockhash \n" - "Returns hash of block in best-block-chain at ."); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) - throw runtime_error("Block number out of range."); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; - while (pblockindex->nHeight > nHeight) - pblockindex = pblockindex->pprev; - return pblockindex->phashBlock->GetHex(); -} - -Value getblock(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblock [txinfo]\n" - "txinfo optional to print more detailed tx info\n" - "Returns details of a block with given block-hash."); - - std::string strHash = params[0].get_str(); - uint256 hash(strHash); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(-5, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex, true); - - return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); -} - -Value getblockbynumber(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblock [txinfo]\n" - "txinfo optional to print more detailed tx info\n" - "Returns details of a block with given block-number."); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) - throw runtime_error("Block number out of range."); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; - while (pblockindex->nHeight > nHeight) - pblockindex = pblockindex->pprev; - - uint256 hash = *pblockindex->phashBlock; - - pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex, true); - - return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); -} - -// ppcoin: get information of sync-checkpoint -Value getcheckpoint(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getcheckpoint\n" - "Show info of synchronized checkpoint.\n"); - - Object result; - CBlockIndex* pindexCheckpoint; - - result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); - pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; - result.push_back(Pair("height", pindexCheckpoint->nHeight)); - result.push_back(Pair("timestamp", DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str())); - if (mapArgs.count("-checkpointkey")) - result.push_back(Pair("checkpointmaster", true)); - - 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"); - mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str(); - } - else - { - if (params.size() > 1) - throw runtime_error("cannot specify amount to turn off reserve.\n"); - mapArgs["-reservebalance"] = "0"; - } - } - - Object result; - int64 nReserveBalance = 0; - if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) - throw runtime_error("invalid reserve balance amount\n"); - result.push_back(Pair("reserve", (nReserveBalance > 0))); - result.push_back(Pair("amount", ValueFromAmount(nReserveBalance))); - 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; - pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true); - 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 in question", ValueFromAmount(nBalanceInQuestion))); - } - return result; -} - - -// 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; -} - -// NovaCoin: resend unconfirmed wallet transactions -Value resendtx(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "resendtx\n" - "Re-send unconfirmed transactions.\n" - ); - - ResendWalletTransactions(); - - return Value::null; -} - - -// ppcoin: make a public-private key pair -Value makekeypair(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "makekeypair [prefix]\n" - "Make a public/private key pair.\n" - "[prefix] is optional preferred prefix for the public key.\n"); - - string strPrefix = ""; - if (params.size() > 0) - strPrefix = params[0].get_str(); - - CKey key; - int nCount = 0; - do - { - key.MakeNewKey(false); - nCount++; - } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())); - - if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())) - return Value::null; - - 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; -} - -extern CCriticalSection cs_mapAlerts; -extern map mapAlerts; - -// ppcoin: send alert. -// There is a known deadlock situation with ThreadMessageHandler -// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages() -// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage() -Value sendalert(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 6) - 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 internal client version\n" - " is the maximum applicable internal client version\n" - " is integer priority number\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.nPriority = params[4].get_int(); - alert.nID = params[5].get_int(); - if (params.size() > 6) - alert.nCancel = params[6].get_int(); - alert.nVersion = PROTOCOL_VERSION; - alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; - alert.nExpiration = GetAdjustedTime() + 365*24*60*60; - - CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); - 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"); - if(!alert.ProcessAlert()) - throw runtime_error( - "Failed to process alert.\n"); - // Relay alert - { - LOCK(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("nPriority", alert.nPriority)); - result.push_back(Pair("nID", alert.nID)); - if (alert.nCancel > 0) - result.push_back(Pair("nCancel", alert.nCancel)); - return result; + return "NovaCoin server stopping"; } @@ -2659,67 +235,99 @@ Value sendalert(const Array& params, bool fHelp) static const CRPCCommand vRPCCommands[] = -{ // name function safe mode? - // ------------------------ ----------------------- ---------- - { "help", &help, true }, - { "stop", &stop, true }, - { "getblockcount", &getblockcount, true }, - { "getblocknumber", &getblocknumber, true }, - { "getconnectioncount", &getconnectioncount, true }, - { "getdifficulty", &getdifficulty, true }, - { "getpowreward", &getpowreward, true }, - { "getgenerate", &getgenerate, true }, - { "setgenerate", &setgenerate, true }, - { "gethashespersec", &gethashespersec, true }, - { "getinfo", &getinfo, true }, - { "getmininginfo", &getmininginfo, true }, - { "getnewaddress", &getnewaddress, true }, - { "getnewpubkey", &getnewpubkey, true }, - { "getaccountaddress", &getaccountaddress, true }, - { "setaccount", &setaccount, true }, - { "getaccount", &getaccount, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true }, - { "sendtoaddress", &sendtoaddress, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false }, - { "backupwallet", &backupwallet, true }, - { "keypoolrefill", &keypoolrefill, true }, - { "walletpassphrase", &walletpassphrase, true }, - { "walletpassphrasechange", &walletpassphrasechange, false }, - { "walletlock", &walletlock, true }, - { "encryptwallet", &encryptwallet, false }, - { "validateaddress", &validateaddress, true }, - { "validatepubkey", &validatepubkey, true }, - { "getbalance", &getbalance, false }, - { "move", &movecmd, false }, - { "sendfrom", &sendfrom, false }, - { "sendmany", &sendmany, false }, - { "addmultisigaddress", &addmultisigaddress, false }, - { "getblock", &getblock, false }, - { "getblockhash", &getblockhash, false }, - { "getblockbynumber", &getblockbynumber, false }, - { "gettransaction", &gettransaction, false }, - { "listtransactions", &listtransactions, false }, - { "signmessage", &signmessage, false }, - { "verifymessage", &verifymessage, false }, - { "getwork", &getwork, true }, - { "listaccounts", &listaccounts, false }, - { "settxfee", &settxfee, false }, - { "getmemorypool", &getmemorypool, true }, - { "getblocktemplate", &getblocktemplate, true }, - { "submitblock", &submitblock, false }, - { "listsinceblock", &listsinceblock, false }, - { "dumpprivkey", &dumpprivkey, false }, - { "importprivkey", &importprivkey, false }, - { "getcheckpoint", &getcheckpoint, true }, - { "reservebalance", &reservebalance, false}, - { "checkwallet", &checkwallet, false}, - { "repairwallet", &repairwallet, false}, - { "resendtx", &resendtx, false}, - { "makekeypair", &makekeypair, false}, - { "sendalert", &sendalert, false}, +{ // name function safemd unlocked + // ------------------------ ----------------------- ------ -------- + { "help", &help, true, true }, + { "stop", &stop, true, true }, + { "getbestblockhash", &getbestblockhash, true, false }, + { "getblockcount", &getblockcount, true, false }, + { "getconnectioncount", &getconnectioncount, true, false }, + { "getaddrmaninfo", &getaddrmaninfo, true, false }, + { "getpeerinfo", &getpeerinfo, true, false }, + { "addnode", &addnode, true, true }, + { "getaddednodeinfo", &getaddednodeinfo, true, true }, + { "getdifficulty", &getdifficulty, true, false }, + { "getinfo", &getinfo, true, false }, + { "getsubsidy", &getsubsidy, true, false }, + { "getmininginfo", &getmininginfo, true, false }, + { "scaninput", &scaninput, true, true }, + { "getnewaddress", &getnewaddress, true, false }, + { "getnettotals", &getnettotals, true, true }, + { "ntptime", &ntptime, true, true }, + { "getaccountaddress", &getaccountaddress, true, false }, + { "setaccount", &setaccount, true, false }, + { "getaccount", &getaccount, false, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, + { "sendtoaddress", &sendtoaddress, false, false }, + { "mergecoins", &mergecoins, false, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, + { "backupwallet", &backupwallet, true, false }, + { "keypoolrefill", &keypoolrefill, true, false }, + { "keypoolreset", &keypoolreset, true, false }, + { "walletpassphrase", &walletpassphrase, true, false }, + { "walletpassphrasechange", &walletpassphrasechange, false, false }, + { "walletlock", &walletlock, true, false }, + { "encryptwallet", &encryptwallet, false, false }, + { "validateaddress", &validateaddress, true, false }, + { "getbalance", &getbalance, false, false }, + { "move", &movecmd, false, false }, + { "sendfrom", &sendfrom, false, false }, + { "sendmany", &sendmany, false, false }, + { "addmultisigaddress", &addmultisigaddress, false, false }, + { "addredeemscript", &addredeemscript, false, false }, + { "getrawmempool", &getrawmempool, true, false }, + { "getblock", &getblock, false, false }, + { "getblockbynumber", &getblockbynumber, false, false }, + { "dumpblock", &dumpblock, false, false }, + { "dumpblockbynumber", &dumpblockbynumber, false, false }, + { "getblockhash", &getblockhash, false, false }, + { "gettransaction", &gettransaction, false, false }, + { "listtransactions", &listtransactions, false, false }, + { "listaddressgroupings", &listaddressgroupings, false, false }, + { "signmessage", &signmessage, false, false }, + { "verifymessage", &verifymessage, false, false }, + { "getwork", &getwork, true, false }, + { "getworkex", &getworkex, true, false }, + { "listaccounts", &listaccounts, false, false }, + { "settxfee", &settxfee, false, false }, + { "getblocktemplate", &getblocktemplate, true, false }, + { "submitblock", &submitblock, false, false }, + { "listsinceblock", &listsinceblock, false, false }, + { "dumpprivkey", &dumpprivkey, false, false }, + { "dumppem", &dumppem, true, false }, + { "dumpwallet", &dumpwallet, true, false }, + { "importwallet", &importwallet, false, false }, + { "importprivkey", &importprivkey, false, false }, + { "importaddress", &importaddress, false, true }, + { "removeaddress", &removeaddress, false, true }, + { "listunspent", &listunspent, false, false }, + { "getrawtransaction", &getrawtransaction, false, false }, + { "createrawtransaction", &createrawtransaction, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false }, + { "createmultisig", &createmultisig, false, false }, + { "decodescript", &decodescript, false, false }, + { "signrawtransaction", &signrawtransaction, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false }, + { "getcheckpoint", &getcheckpoint, true, false }, + { "reservebalance", &reservebalance, false, true}, + { "checkwallet", &checkwallet, false, true}, + { "repairwallet", &repairwallet, false, true}, + { "resendwallettransactions", &resendwallettransactions, false, true}, + { "makekeypair", &makekeypair, false, true}, + { "newmalleablekey", &newmalleablekey, false, false}, + { "adjustmalleablekey", &adjustmalleablekey, false, false}, + { "adjustmalleablepubkey", &adjustmalleablepubkey, false, false}, + { "listmalleableviews", &listmalleableviews, false, false}, + { "dumpmalleablekey", &dumpmalleablekey, false, false}, + { "importmalleablekey", &importmalleablekey, true, false }, + { "encryptdata", &encryptdata, false, false }, + { "decryptdata", &decryptdata, false, false }, + { "encryptmessage", &encryptmessage, false, false }, + { "decryptmessage", &decryptmessage, false, false }, + { "sendalert", &sendalert, false, false}, }; CRPCTable::CRPCTable() @@ -2768,20 +376,12 @@ string HTTPPost(const string& strMsg, const map& mapRequestHeader string rfc1123Time() { - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); + return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); } -static string HTTPReply(int nStatus, const string& strMsg) +static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) { - if (nStatus == 401) + if (nStatus == HTTP_UNAUTHORIZED) return strprintf("HTTP/1.0 401 Authorization Required\r\n" "Date: %s\r\n" "Server: novacoin-json-rpc/%s\r\n" @@ -2799,17 +399,17 @@ static string HTTPReply(int nStatus, const string& strMsg) "

401 Unauthorized.

\r\n" "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); 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"; + if (nStatus == HTTP_OK) cStatus = "OK"; + else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; + else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; + else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; + else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) 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" + "Connection: %s\r\n" + "Content-Length: %" PRIszu "\r\n" "Content-Type: application/json\r\n" "Server: novacoin-json-rpc/%s\r\n" "\r\n" @@ -2817,26 +417,32 @@ static string HTTPReply(int nStatus, const string& strMsg) nStatus, cStatus, rfc1123Time().c_str(), + keepalive ? "keep-alive" : "close", strMsg.size(), FormatFullVersion().c_str(), strMsg.c_str()); } -int ReadHTTPStatus(std::basic_istream& stream) +int ReadHTTPStatus(std::basic_istream& stream, int &proto) { string str; getline(stream, str); vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); + istringstream iss(str); + copy(istream_iterator(iss), istream_iterator(), back_inserter(vWords)); if (vWords.size() < 2) - return 500; + return HTTP_INTERNAL_SERVER_ERROR; + proto = 0; + const char *ver = strstr(str.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); return atoi(vWords[1].c_str()); } int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) { int nLen = 0; - loop + for ( ; ; ) { string str; std::getline(stream, str); @@ -2861,15 +467,16 @@ int ReadHTTPHeader(std::basic_istream& stream, map& mapHea int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) { mapHeadersRet.clear(); - strMessageRet = ""; + strMessageRet.clear(); // Read status - int nStatus = ReadHTTPStatus(stream); + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); // Read header int nLen = ReadHTTPHeader(stream, mapHeadersRet); if (nLen < 0 || nLen > (int)MAX_SIZE) - return 500; + return HTTP_INTERNAL_SERVER_ERROR; // Read message if (nLen > 0) @@ -2879,6 +486,16 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe strMessageRet = string(vch.begin(), vch.end()); } + string sConHdr = mapHeadersRet["connection"]; + + if ((sConHdr != "close") && (sConHdr != "keep-alive")) + { + if (nProto >= 1) + mapHeadersRet["connection"] = "keep-alive"; + else + mapHeadersRet["connection"] = "close"; + } + return nStatus; } @@ -2889,7 +506,7 @@ bool HTTPAuthorized(map& mapHeaders) return false; string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); string strUserPass = DecodeBase64(strUserPass64); - return strUserPass == strRPCUserColonPass; + return TimingResistantEqual(strUserPass, strRPCUserColonPass); } // @@ -2911,7 +528,7 @@ string JSONRPCRequest(const string& strMethod, const Array& params, const Value& return write_string(Value(request), false) + "\n"; } -string JSONRPCReply(const Value& result, const Value& error, const Value& id) +Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) { Object reply; if (error.type() != null_type) @@ -2920,24 +537,42 @@ string JSONRPCReply(const Value& result, const Value& error, const Value& id) reply.push_back(Pair("result", result)); reply.push_back(Pair("error", error)); reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply = JSONRPCReplyObj(result, error, id); return write_string(Value(reply), false) + "\n"; } void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) { // Send error reply from json-rpc error object - int nStatus = 500; + int nStatus = HTTP_INTERNAL_SERVER_ERROR; int code = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; + if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; + else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; + stream << HTTPReply(nStatus, strReply, false) << std::flush; } -bool ClientAllowed(const string& strAddress) +bool ClientAllowed(const boost::asio::ip::address& address) { - if (strAddress == asio::ip::address_v4::loopback().to_string()) + // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses + if (address.is_v6() + && (address.to_v6().is_v4_compatible() + || address.to_v6().is_v4_mapped())) + return ClientAllowed(address.to_v6().to_v4()); + + if (address == asio::ip::address_v4::loopback() + || address == asio::ip::address_v6::loopback() + || (address.is_v4() + // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) + && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) return true; + + const string strAddress = address.to_string(); const vector& vAllow = mapMultiArgs["-rpcallowip"]; BOOST_FOREACH(string strAllow, vAllow) if (WildcardMatch(strAddress, strAllow)) @@ -2948,9 +583,10 @@ bool ClientAllowed(const string& strAddress) // // IOStream device that speaks SSL but can also speak non-SSL // +template class SSLIOStreamDevice : public iostreams::device { public: - SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) + SSLIOStreamDevice(asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) { fUseSSL = fUseSSLIn; fNeedHandshake = fUseSSLIn; @@ -2994,26 +630,152 @@ public: private: bool fNeedHandshake; bool fUseSSL; - SSLStream& stream; + SSLIOStreamDevice& operator=(SSLIOStreamDevice const&); + asio::ssl::stream& stream; +}; + +class AcceptedConnection +{ +public: + virtual ~AcceptedConnection() {} + + virtual std::iostream& stream() = 0; + virtual std::string peer_address_to_string() const = 0; + virtual void close() = 0; +}; + +template +class AcceptedConnectionImpl : public AcceptedConnection +{ +public: + AcceptedConnectionImpl( + asio::io_service& io_service, + ssl::context &context, + bool fUseSSL) : + sslStream(io_service, context), + _d(sslStream, fUseSSL), + _stream(_d) + { + } + + virtual std::iostream& stream() + { + return _stream; + } + + virtual std::string peer_address_to_string() const + { + return peer.address().to_string(); + } + + virtual void close() + { + _stream.close(); + } + + typename Protocol::endpoint peer; + asio::ssl::stream sslStream; + +private: + SSLIOStreamDevice _d; + iostreams::stream< SSLIOStreamDevice > _stream; }; void ThreadRPCServer(void* parg) { - IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); + // Make this thread recognisable as the RPC listener + RenameThread("novacoin-rpclist"); + try { - vnThreadsRunning[THREAD_RPCSERVER]++; + vnThreadsRunning[THREAD_RPCLISTENER]++; ThreadRPCServer2(parg); - vnThreadsRunning[THREAD_RPCSERVER]--; + vnThreadsRunning[THREAD_RPCLISTENER]--; } catch (std::exception& e) { - vnThreadsRunning[THREAD_RPCSERVER]--; + vnThreadsRunning[THREAD_RPCLISTENER]--; PrintException(&e, "ThreadRPCServer()"); } catch (...) { - vnThreadsRunning[THREAD_RPCSERVER]--; + vnThreadsRunning[THREAD_RPCLISTENER]--; PrintException(NULL, "ThreadRPCServer()"); } - printf("ThreadRPCServer exiting\n"); + printf("ThreadRPCServer exited\n"); +} + +// Forward declaration required for RPCListen +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error); + +/** + * Sets up I/O resources to accept and handle a new connection. + */ +template +static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL) +{ + // Accept connection + AcceptedConnectionImpl* conn = new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL); + + acceptor->async_accept( + conn->sslStream.lowest_layer(), + conn->peer, + boost::bind(&RPCAcceptHandler, + acceptor, + boost::ref(context), + fUseSSL, + conn, + boost::asio::placeholders::error)); +} + +/** + * Accept and handle incoming connection. + */ +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error) +{ + vnThreadsRunning[THREAD_RPCLISTENER]++; + + // Immediately start accepting new connections, except when we're cancelled or our socket is closed. + if (error != asio::error::operation_aborted + && acceptor->is_open()) + RPCListen(acceptor, context, fUseSSL); + + AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); + + // TODO: Actually handle errors + if (error) + { + delete conn; + } + + // Restrict callers by IP. It is important to + // do this before starting client thread, to filter out + // certain DoS and misbehaving clients. + else if (tcp_conn + && !ClientAllowed(tcp_conn->peer.address())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; + delete conn; + } + + // start HTTP client thread + else if (!NewThread(ThreadRPCServer3, conn)) { + printf("Failed to create RPC server client thread\n"); + delete conn; + } + + vnThreadsRunning[THREAD_RPCLISTENER]--; } void ThreadRPCServer2(void* parg) @@ -3021,7 +783,7 @@ void ThreadRPCServer2(void* parg) printf("ThreadRPCServer started\n"); strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (mapArgs["-rpcpassword"] == "") + if (mapArgs["-rpcpassword"].empty()) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); @@ -3030,41 +792,24 @@ void ThreadRPCServer2(void* parg) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); else if (mapArgs.count("-daemon")) strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - ThreadSafeMessageBox(strprintf( + uiInterface.ThreadSafeMessageBox(strprintf( _("%s, you must set a rpcpassword in the configuration file:\n %s\n" "It is recommended you use the following random password:\n" - "rpcuser=novarpc\n" + "rpcuser=novacoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), GetConfigFile().string().c_str(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), - _("Error"), wxOK | wxMODAL); + _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); StartShutdown(); return; } - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + const bool fUseSSL = GetBoolArg("-rpcssl"); asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT)); - ip::tcp::acceptor acceptor(io_service); - try - { - acceptor.open(endpoint.protocol()); - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor.bind(endpoint); - acceptor.listen(socket_base::max_connections); - } - catch(boost::system::system_error &e) - { - ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()), - _("Error"), wxOK | wxMODAL); - StartShutdown(); - return; - } ssl::context context(io_service, ssl::context::sslv23); if (fUseSSL) @@ -3085,137 +830,290 @@ void ThreadRPCServer2(void* parg) SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); } - loop + // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets + const bool loopback = !mapArgs.count("-rpcallowip"); + asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); + boost::system::error_code v6_only_error; + boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); + + boost::signals2::signal StopRequests; + + bool fListening = false; + std::string strerr; + try { - // Accept connection - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); - - ip::tcp::endpoint peer; - vnThreadsRunning[THREAD_RPCSERVER]--; - acceptor.accept(sslStream.lowest_layer(), peer); - vnThreadsRunning[4]++; - if (fShutdown) - return; + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + + // Try making the socket dual IPv6/IPv4 (if listening on the "any" address) + acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); + + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, context, fUseSSL); + // Cancel outstanding listen-requests for this acceptor when shutting down + StopRequests.connect(signals2::slot( + static_cast(&ip::tcp::acceptor::close), acceptor.get()) + .track(acceptor)); + + fListening = true; + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); + } - // Restrict callers by IP - if (!ClientAllowed(peer.address().to_string())) + try { + // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately + if (!fListening || loopback || v6_only_error) { - // 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; + bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); + endpoint.address(bindAddress); + + acceptor.reset(new ip::tcp::acceptor(io_service)); + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, context, fUseSSL); + // Cancel outstanding listen-requests for this acceptor when shutting down + StopRequests.connect(signals2::slot( + static_cast(&ip::tcp::acceptor::close), acceptor.get()) + .track(acceptor)); + + fListening = true; } + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what()); + } + + if (!fListening) { + uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + StartShutdown(); + return; + } + + vnThreadsRunning[THREAD_RPCLISTENER]--; + while (!fShutdown) + io_service.run_one(); + vnThreadsRunning[THREAD_RPCLISTENER]++; + StopRequests(); +} + +class JSONRequest +{ +public: + Value id; + string strMethod; + Array params; + + JSONRequest() { id = Value::null; } + void parse(const Value& valRequest); +}; + +void JSONRequest::parse(const Value& valRequest) +{ + // Parse request + if (valRequest.type() != obj_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getwork" && strMethod != "getblocktemplate") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); +} + +static Object JSONRPCExecOne(const Value& req) +{ + Object rpc_result; + + JSONRequest jreq; + try { + jreq.parse(req); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + } + catch (Object& objError) + { + rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + } + catch (std::exception& e) + { + rpc_result = JSONRPCReplyObj(Value::null, + JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + } + + return rpc_result; +} + +static string JSONRPCExecBatch(const Array& vReq) +{ + Array ret; + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return write_string(Value(ret), false) + "\n"; +} + +static CCriticalSection cs_THREAD_RPCHANDLER; + +void ThreadRPCServer3(void* parg) +{ + // Make this thread recognisable as the RPC handler + RenameThread("novacoin-rpchand"); + { + LOCK(cs_THREAD_RPCHANDLER); + vnThreadsRunning[THREAD_RPCHANDLER]++; + } + AcceptedConnection *conn = (AcceptedConnection *) parg; + + bool fRun = true; + for ( ; ; ) + { + if (fShutdown || !fRun) + { + conn->close(); + delete conn; + { + LOCK(cs_THREAD_RPCHANDLER); + --vnThreadsRunning[THREAD_RPCHANDLER]; + } + return; + } map mapHeaders; string strRequest; - boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); - if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) - { // Timed out: - acceptor.cancel(); - printf("ThreadRPCServer ReadHTTP timeout\n"); - continue; - } + ReadHTTP(conn->stream(), mapHeaders, strRequest); // Check authorization if (mapHeaders.count("authorization") == 0) { - stream << HTTPReply(401, "") << std::flush; - continue; + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; } if (!HTTPAuthorized(mapHeaders)) { - printf("ThreadRPCServer incorrect password attempt from %s\n",peer.address().to_string().c_str()); + printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); /* Deter brute-forcing short passwords. If this results in a DOS the user really shouldn't have their RPC port exposed.*/ if (mapArgs["-rpcpassword"].size() < 20) Sleep(250); - stream << HTTPReply(401, "") << std::flush; - continue; + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; } + if (mapHeaders["connection"] == "close") + fRun = false; - Value id = Value::null; + JSONRequest jreq; try { // Parse request Value valRequest; - if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) - throw JSONRPCError(-32700, "Parse error"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(-32600, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(-32600, "Method must be a string"); - string strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getmemorypool") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - Array params; - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(-32600, "Params must be an array"); + if (!read_string(strRequest, valRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - // Find method - const CRPCCommand *pcmd = tableRPC[strMethod]; - if (!pcmd) - throw JSONRPCError(-32601, "Method not found"); + string strReply; - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && - !pcmd->okSafeMode) - throw JSONRPCError(-2, string("Safe mode: ") + strWarning); + // singleton request + if (valRequest.type() == obj_type) { + jreq.parse(valRequest); - try - { - // Execute - Value result; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - result = pcmd->actor(params, false); - } + Value result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply - string strReply = JSONRPCReply(result, Value::null, id); - stream << HTTPReply(200, strReply) << std::flush; - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-1, e.what()), id); - } + strReply = JSONRPCReply(result, Value::null, jreq.id); + + // array of requests + } else if (valRequest.type() == array_type) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + + conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; } catch (Object& objError) { - ErrorReply(stream, objError, id); + ErrorReply(conn->stream(), objError, jreq.id); + break; } catch (std::exception& e) { - ErrorReply(stream, JSONRPCError(-32700, e.what()), id); + ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + break; } } + + delete conn; + { + LOCK(cs_THREAD_RPCHANDLER); + vnThreadsRunning[THREAD_RPCHANDLER]--; + } } +json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const +{ + // Find method + const CRPCCommand *pcmd = tableRPC[strMethod]; + if (!pcmd) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (!strWarning.empty() && !GetBoolArg("-disablesafemode") && + !pcmd->okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result; + { + if (pcmd->unlocked) + result = pcmd->actor(params, false); + else { + LOCK2(cs_main, pwalletMain->cs_wallet); + result = pcmd->actor(params, false); + } + } + return result; + } + catch (std::exception& e) + { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } +} Object CallRPC(const string& strMethod, const Array& params) { - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + if (mapArgs["-rpcuser"].empty() && mapArgs["-rpcpassword"].empty()) throw runtime_error(strprintf( _("You must set rpcpassword= in the configuration file:\n%s\n" "If the file does not exist, create it with owner-readable-only file permissions."), @@ -3226,10 +1124,10 @@ Object CallRPC(const string& strMethod, const Array& params) asio::io_service io_service; ssl::context context(io_service, ssl::context::sslv23); context.set_options(ssl::context::no_sslv2); - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()))) + asio::ssl::stream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice > stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) throw runtime_error("couldn't connect to server"); // HTTP basic authentication @@ -3246,9 +1144,9 @@ Object CallRPC(const string& strMethod, const Array& params) map mapHeaders; string strReply; int nStatus = ReadHTTP(stream, mapHeaders, strReply); - if (nStatus == 401) + if (nStatus == HTTP_UNAUTHORIZED) throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) + else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); else if (strReply.empty()) throw runtime_error("no response from server"); @@ -3268,15 +1166,19 @@ Object CallRPC(const string& strMethod, const Array& params) template -void ConvertTo(Value& value) +void ConvertTo(Value& value, bool fAllowNull=false) { + if (fAllowNull && value.type() == null_type) + return; if (value.type() == str_type) { // reinterpret string as unquoted json value Value value2; - if (!read_string(value.get_str(), value2)) - throw runtime_error("type mismatch"); - value = value2.get_value(); + string strJSON = value.get_str(); + if (!read_string(strJSON, value2)) + throw runtime_error(string("Error parsing JSON:")+strJSON); + ConvertTo(value2, fAllowNull); + value = value2; } else { @@ -3284,6 +1186,81 @@ void ConvertTo(Value& value) } } +// Convert strings to command-specific RPC representation +Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + Array params; + BOOST_FOREACH(const std::string ¶m, strParams) + params.push_back(param); + + size_t n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "stop" && n > 0) ConvertTo(params[0]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 0) ConvertTo(params[0]); + if (strMethod == "mergecoins" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 2) ConvertTo(params[2]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && 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 == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "dumpblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockbynumber" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + 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 == "getblocktemplate" && n > 0) ConvertTo(params[0]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + + if (strMethod == "scaninput" && n > 0) ConvertTo(params[0]); + + 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 == "sendalert" && n > 6) ConvertTo(params[6]); + + if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); + if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); + if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo(params[1]); + if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); + if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "keypoolrefill" && n > 0) ConvertTo(params[0]); + if (strMethod == "keypoolreset" && n > 0) ConvertTo(params[0]); + if (strMethod == "importaddress" && n > 2) ConvertTo(params[2]); + if (strMethod == "importprivkey" && n > 2) ConvertTo(params[2]); + + return params; +} + int CommandLineRPC(int argc, char *argv[]) { string strPrint; @@ -3303,64 +1280,8 @@ int CommandLineRPC(int argc, char *argv[]) string strMethod = argv[1]; // Parameters default to strings - Array params; - for (int i = 2; i < argc; i++) - params.push_back(argv[i]); - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - 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 == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && 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 == "getbalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); - if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); - if (strMethod == "getblockbynumber" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); - if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - 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 == "sendalert" && n > 6) ConvertTo(params[6]); - if (strMethod == "sendmany" && n > 1) - { - string s = params[1].get_str(); - Value v; - if (!read_string(s, v) || v.type() != obj_type) - throw runtime_error("type mismatch"); - params[1] = v.get_obj(); - } - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); - if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "addmultisigaddress" && n > 1) - { - string s = params[1].get_str(); - Value v; - if (!read_string(s, v) || v.type() != array_type) - throw runtime_error("type mismatch "+s); - params[1] = v.get_array(); - } + std::vector strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); // Execute Object reply = CallRPC(strMethod, params); @@ -3380,7 +1301,7 @@ int CommandLineRPC(int argc, char *argv[]) { // Result if (result.type() == null_type) - strPrint = ""; + strPrint.clear(); else if (result.type() == str_type) strPrint = result.get_str(); else @@ -3397,7 +1318,7 @@ int CommandLineRPC(int argc, char *argv[]) PrintException(NULL, "CommandLineRPC()"); } - if (strPrint != "") + if (!strPrint.empty()) { fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); } @@ -3411,7 +1332,7 @@ int CommandLineRPC(int argc, char *argv[]) int main(int argc, char *argv[]) { #ifdef _MSC_VER - // Turn off microsoft heap dump noise + // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); #endif