From a0eaff8a1d18ebba33cdea4cd1efaddeb55519e7 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 15 Jan 2016 11:55:17 +1100 Subject: move rpc* to rpc/ --- src/Makefile.am | 22 +- src/bitcoin-cli.cpp | 4 +- src/bitcoind.cpp | 3 +- src/httprpc.cpp | 4 +- src/httpserver.cpp | 2 +- src/init.cpp | 2 +- src/qt/bitcoin.cpp | 2 +- src/qt/rpcconsole.cpp | 4 +- src/rest.cpp | 2 +- src/rpc/blockchain.cpp | 891 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/client.cpp | 163 ++++++++ src/rpc/client.h | 17 + src/rpc/mining.cpp | 800 +++++++++++++++++++++++++++++++++++++ src/rpc/misc.cpp | 398 +++++++++++++++++++ src/rpc/net.cpp | 628 +++++++++++++++++++++++++++++ src/rpc/protocol.cpp | 127 ++++++ src/rpc/protocol.h | 93 +++++ src/rpc/rawtransaction.cpp | 841 +++++++++++++++++++++++++++++++++++++++ src/rpc/server.cpp | 539 +++++++++++++++++++++++++ src/rpc/server.h | 243 ++++++++++++ src/rpcblockchain.cpp | 891 ------------------------------------------ src/rpcclient.cpp | 164 -------- src/rpcclient.h | 17 - src/rpcmining.cpp | 800 ------------------------------------- src/rpcmisc.cpp | 398 ------------------- src/rpcnet.cpp | 628 ----------------------------- src/rpcprotocol.cpp | 127 ------ src/rpcprotocol.h | 93 ----- src/rpcrawtransaction.cpp | 841 --------------------------------------- src/rpcserver.cpp | 539 ------------------------- src/rpcserver.h | 243 ------------ src/test/rpc_tests.cpp | 6 +- src/test/rpc_wallet_tests.cpp | 4 +- src/wallet/rpcdump.cpp | 2 +- src/wallet/rpcwallet.cpp | 72 ++-- 35 files changed, 4804 insertions(+), 4806 deletions(-) create mode 100644 src/rpc/blockchain.cpp create mode 100644 src/rpc/client.cpp create mode 100644 src/rpc/client.h create mode 100644 src/rpc/mining.cpp create mode 100644 src/rpc/misc.cpp create mode 100644 src/rpc/net.cpp create mode 100644 src/rpc/protocol.cpp create mode 100644 src/rpc/protocol.h create mode 100644 src/rpc/rawtransaction.cpp create mode 100644 src/rpc/server.cpp create mode 100644 src/rpc/server.h delete mode 100644 src/rpcblockchain.cpp delete mode 100644 src/rpcclient.cpp delete mode 100644 src/rpcclient.h delete mode 100644 src/rpcmining.cpp delete mode 100644 src/rpcmisc.cpp delete mode 100644 src/rpcnet.cpp delete mode 100644 src/rpcprotocol.cpp delete mode 100644 src/rpcprotocol.h delete mode 100644 src/rpcrawtransaction.cpp delete mode 100644 src/rpcserver.cpp delete mode 100644 src/rpcserver.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 948d12424..9a6b4fd66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -131,9 +131,9 @@ BITCOIN_CORE_H = \ pubkey.h \ random.h \ reverselock.h \ - rpcclient.h \ - rpcprotocol.h \ - rpcserver.h \ + rpc/client.h \ + rpc/protocol.h \ + rpc/server.h \ scheduler.h \ script/interpreter.h \ script/script.h \ @@ -203,12 +203,12 @@ libbitcoin_server_a_SOURCES = \ policy/policy.cpp \ pow.cpp \ rest.cpp \ - rpcblockchain.cpp \ - rpcmining.cpp \ - rpcmisc.cpp \ - rpcnet.cpp \ - rpcrawtransaction.cpp \ - rpcserver.cpp \ + rpc/blockchain.cpp \ + rpc/mining.cpp \ + rpc/misc.cpp \ + rpc/net.cpp \ + rpc/rawtransaction.cpp \ + rpc/server.cpp \ script/sigcache.cpp \ timedata.cpp \ torcontrol.cpp \ @@ -304,7 +304,7 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ random.cpp \ - rpcprotocol.cpp \ + rpc/protocol.cpp \ support/cleanse.cpp \ sync.cpp \ uint256.cpp \ @@ -322,7 +322,7 @@ endif libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ - rpcclient.cpp \ + rpc/client.cpp \ $(BITCOIN_CORE_H) nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index fb2052108..d1d534b05 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -5,8 +5,8 @@ #include "chainparamsbase.h" #include "clientversion.h" -#include "rpcclient.h" -#include "rpcprotocol.h" +#include "rpc/client.h" +#include "rpc/protocol.h" #include "util.h" #include "utilstrencodings.h" diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 3b6608c95..d1fd56d17 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -5,14 +5,13 @@ #include "chainparams.h" #include "clientversion.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "init.h" #include "noui.h" #include "scheduler.h" #include "util.h" #include "httpserver.h" #include "httprpc.h" -#include "rpcserver.h" #include #include diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 1466dc0cb..e7c28238b 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -3,8 +3,8 @@ #include "base58.h" #include "chainparams.h" #include "httpserver.h" -#include "rpcprotocol.h" -#include "rpcserver.h" +#include "rpc/protocol.h" +#include "rpc/server.h" #include "random.h" #include "sync.h" #include "util.h" diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 91518d7c5..ce1accb04 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -8,7 +8,7 @@ #include "compat.h" #include "util.h" #include "netbase.h" -#include "rpcprotocol.h" // For HTTP status codes +#include "rpc/protocol.h" // For HTTP status codes #include "sync.h" #include "ui_interface.h" diff --git a/src/init.cpp b/src/init.cpp index 282ede55c..fada53536 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -23,7 +23,7 @@ #include "miner.h" #include "net.h" #include "policy/policy.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "script/standard.h" #include "script/sigcache.h" #include "scheduler.h" diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index dcf752cc3..b33df45fb 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -26,7 +26,7 @@ #endif #include "init.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "scheduler.h" #include "ui_interface.h" #include "util.h" diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 7178bc00e..2658e867c 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -12,8 +12,8 @@ #include "bantablemodel.h" #include "chainparams.h" -#include "rpcserver.h" -#include "rpcclient.h" +#include "rpc/server.h" +#include "rpc/client.h" #include "util.h" #include diff --git a/src/rest.cpp b/src/rest.cpp index ad884dac1..ebdccc940 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -9,7 +9,7 @@ #include "primitives/transaction.h" #include "main.h" #include "httpserver.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "streams.h" #include "sync.h" #include "txmempool.h" diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp new file mode 100644 index 000000000..de6bda4ea --- /dev/null +++ b/src/rpc/blockchain.cpp @@ -0,0 +1,891 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "coins.h" +#include "consensus/validation.h" +#include "main.h" +#include "policy/policy.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "streams.h" +#include "sync.h" +#include "txmempool.h" +#include "util.h" +#include "utilstrencodings.h" + +#include + +#include + +using namespace std; + +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); + +double GetDifficulty(const CBlockIndex* blockindex) +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (blockindex == NULL) + { + if (chainActive.Tip() == NULL) + return 1.0; + else + blockindex = chainActive.Tip(); + } + + int nShift = (blockindex->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + +UniValue blockheaderToJSON(const CBlockIndex* blockindex) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); + int confirmations = -1; + // Only report confirmations if the block is on the main chain + if (chainActive.Contains(blockindex)) + confirmations = chainActive.Height() - blockindex->nHeight + 1; + result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", blockindex->nVersion)); + result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); + result.push_back(Pair("time", (int64_t)blockindex->nTime)); + result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); + result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce)); + result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + CBlockIndex *pnext = chainActive.Next(blockindex); + if (pnext) + result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + return result; +} + +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hash", block.GetHash().GetHex())); + int confirmations = -1; + // Only report confirmations if the block is on the main chain + if (chainActive.Contains(blockindex)) + confirmations = chainActive.Height() - blockindex->nHeight + 1; + result.push_back(Pair("confirmations", confirmations)); + 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())); + UniValue txs(UniValue::VARR); + BOOST_FOREACH(const CTransaction&tx, block.vtx) + { + if(txDetails) + { + UniValue objTx(UniValue::VOBJ); + TxToJSON(tx, uint256(), objTx); + txs.push_back(objTx); + } + else + txs.push_back(tx.GetHash().GetHex()); + } + result.push_back(Pair("tx", txs)); + result.push_back(Pair("time", block.GetBlockTime())); + result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); + result.push_back(Pair("nonce", (uint64_t)block.nNonce)); + result.push_back(Pair("bits", strprintf("%08x", block.nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + CBlockIndex *pnext = chainActive.Next(blockindex); + if (pnext) + result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + return result; +} + +UniValue getblockcount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "\nReturns the number of blocks in the longest block chain.\n" + "\nResult:\n" + "n (numeric) The current block count\n" + "\nExamples:\n" + + HelpExampleCli("getblockcount", "") + + HelpExampleRpc("getblockcount", "") + ); + + LOCK(cs_main); + return chainActive.Height(); +} + +UniValue getbestblockhash(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbestblockhash\n" + "\nReturns the hash of the best (tip) block in the longest block chain.\n" + "\nResult\n" + "\"hex\" (string) the block hash hex encoded\n" + "\nExamples\n" + + HelpExampleCli("getbestblockhash", "") + + HelpExampleRpc("getbestblockhash", "") + ); + + LOCK(cs_main); + return chainActive.Tip()->GetBlockHash().GetHex(); +} + +UniValue getdifficulty(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + "\nResult:\n" + "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + "\nExamples:\n" + + HelpExampleCli("getdifficulty", "") + + HelpExampleRpc("getdifficulty", "") + ); + + LOCK(cs_main); + return GetDifficulty(); +} + +UniValue mempoolToJSON(bool fVerbose = false) +{ + if (fVerbose) + { + LOCK(mempool.cs); + UniValue o(UniValue::VOBJ); + BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) + { + const uint256& hash = e.GetTx().GetHash(); + UniValue info(UniValue::VOBJ); + info.push_back(Pair("size", (int)e.GetTxSize())); + info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); + info.push_back(Pair("time", e.GetTime())); + info.push_back(Pair("height", (int)e.GetHeight())); + info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); + info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); + info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); + const CTransaction& tx = e.GetTx(); + set setDepends; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mempool.exists(txin.prevout.hash)) + setDepends.insert(txin.prevout.hash.ToString()); + } + + UniValue depends(UniValue::VARR); + BOOST_FOREACH(const string& dep, setDepends) + { + depends.push_back(dep); + } + + info.push_back(Pair("depends", depends)); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } + else + { + vector vtxid; + mempool.queryHashes(vtxid); + + UniValue a(UniValue::VARR); + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); + + return a; + } +} + +UniValue getrawmempool(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getrawmempool ( verbose )\n" + "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" + "\nArguments:\n" + "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult: (for verbose = false):\n" + "[ (json array of string)\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + "]\n" + "\nResult: (for verbose = true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" + " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" + " \"height\" : n, (numeric) block height when transaction entered pool\n" + " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" + " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " }, ...\n" + "}\n" + "\nExamples\n" + + HelpExampleCli("getrawmempool", "true") + + HelpExampleRpc("getrawmempool", "true") + ); + + LOCK(cs_main); + + bool fVerbose = false; + if (params.size() > 0) + fVerbose = params[0].get_bool(); + + return mempoolToJSON(fVerbose); +} + +UniValue getblockhash(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getblockhash index\n" + "\nReturns hash of block in best-block-chain at index provided.\n" + "\nArguments:\n" + "1. index (numeric, required) The block index\n" + "\nResult:\n" + "\"hash\" (string) The block hash\n" + "\nExamples:\n" + + HelpExampleCli("getblockhash", "1000") + + HelpExampleRpc("getblockhash", "1000") + ); + + LOCK(cs_main); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > chainActive.Height()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + + CBlockIndex* pblockindex = chainActive[nHeight]; + return pblockindex->GetBlockHash().GetHex(); +} + +UniValue getblockheader(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblockheader \"hash\" ( verbose )\n" + "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" + "If verbose is true, returns an Object with information about blockheader .\n" + "\nArguments:\n" + "1. \"hash\" (string, required) The block hash\n" + "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + "\nResult (for verbose = true):\n" + "{\n" + " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" + " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" + " \"height\" : n, (numeric) The block height or index\n" + " \"version\" : n, (numeric) The block version\n" + " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" + " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"nonce\" : n, (numeric) The nonce\n" + " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" + " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" + " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" + "}\n" + "\nResult (for verbose=false):\n" + "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" + "\nExamples:\n" + + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + ); + + LOCK(cs_main); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + + bool fVerbose = true; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (!fVerbose) + { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << pblockindex->GetBlockHeader(); + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } + + return blockheaderToJSON(pblockindex); +} + +UniValue getblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblock \"hash\" ( verbose )\n" + "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n" + "If verbose is true, returns an Object with information about block .\n" + "\nArguments:\n" + "1. \"hash\" (string, required) The block hash\n" + "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + "\nResult (for verbose = true):\n" + "{\n" + " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" + " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" + " \"size\" : n, (numeric) The block size\n" + " \"height\" : n, (numeric) The block height or index\n" + " \"version\" : n, (numeric) The block version\n" + " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" + " \"tx\" : [ (array of string) The transaction ids\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + " ],\n" + " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"nonce\" : n, (numeric) The nonce\n" + " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" + " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" + " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" + "}\n" + "\nResult (for verbose=false):\n" + "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" + "\nExamples:\n" + + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + ); + + LOCK(cs_main); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + + bool fVerbose = true; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + if (!fVerbose) + { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } + + return blockToJSON(block, pblockindex); +} + +UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gettxoutsetinfo\n" + "\nReturns statistics about the unspent transaction output set.\n" + "Note this call may take some time.\n" + "\nResult:\n" + "{\n" + " \"height\":n, (numeric) The current block height (index)\n" + " \"bestblock\": \"hex\", (string) the best block hash hex\n" + " \"transactions\": n, (numeric) The number of transactions\n" + " \"txouts\": n, (numeric) The number of output transactions\n" + " \"bytes_serialized\": n, (numeric) The serialized size\n" + " \"hash_serialized\": \"hash\", (string) The serialized hash\n" + " \"total_amount\": x.xxx (numeric) The total amount\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("gettxoutsetinfo", "") + + HelpExampleRpc("gettxoutsetinfo", "") + ); + + UniValue ret(UniValue::VOBJ); + + CCoinsStats stats; + FlushStateToDisk(); + if (pcoinsTip->GetStats(stats)) { + ret.push_back(Pair("height", (int64_t)stats.nHeight)); + ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); + ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); + ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); + ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); + ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); + ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + } + return ret; +} + +UniValue gettxout(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "gettxout \"txid\" n ( includemempool )\n" + "\nReturns details about an unspent transaction output.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. n (numeric, required) vout number\n" + "3. includemempool (boolean, optional) Whether to include the mem pool\n" + "\nResult:\n" + "{\n" + " \"bestblock\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"code\", (string) \n" + " \"hex\" : \"hex\", (string) \n" + " \"reqSigs\" : n, (numeric) Number of required signatures\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" + " \"addresses\" : [ (array of string) array of bitcoin addresses\n" + " \"bitcoinaddress\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " },\n" + " \"version\" : n, (numeric) The version\n" + " \"coinbase\" : true|false (boolean) Coinbase or not\n" + "}\n" + + "\nExamples:\n" + "\nGet unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nView the details\n" + + HelpExampleCli("gettxout", "\"txid\" 1") + + "\nAs a json rpc call\n" + + HelpExampleRpc("gettxout", "\"txid\", 1") + ); + + LOCK(cs_main); + + UniValue ret(UniValue::VOBJ); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + int n = params[1].get_int(); + bool fMempool = true; + if (params.size() > 2) + fMempool = params[2].get_bool(); + + CCoins coins; + if (fMempool) { + LOCK(mempool.cs); + CCoinsViewMemPool view(pcoinsTip, mempool); + if (!view.GetCoins(hash, coins)) + return NullUniValue; + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } else { + if (!pcoinsTip->GetCoins(hash, coins)) + return NullUniValue; + } + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + return NullUniValue; + + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); + if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) + ret.push_back(Pair("confirmations", 0)); + else + ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); + ret.push_back(Pair("scriptPubKey", o)); + ret.push_back(Pair("version", coins.nVersion)); + ret.push_back(Pair("coinbase", coins.fCoinBase)); + + return ret; +} + +UniValue verifychain(const UniValue& params, bool fHelp) +{ + int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL); + int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); + if (fHelp || params.size() > 2) + throw runtime_error( + "verifychain ( checklevel numblocks )\n" + "\nVerifies blockchain database.\n" + "\nArguments:\n" + "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n" + "2. numblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n" + "\nResult:\n" + "true|false (boolean) Verified or not\n" + "\nExamples:\n" + + HelpExampleCli("verifychain", "") + + HelpExampleRpc("verifychain", "") + ); + + LOCK(cs_main); + + if (params.size() > 0) + nCheckLevel = params[0].get_int(); + if (params.size() > 1) + nCheckDepth = params[1].get_int(); + + return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); +} + +/** Implementation of IsSuperMajority with better feedback */ +static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams) +{ + int nFound = 0; + CBlockIndex* pstart = pindex; + for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++) + { + if (pstart->nVersion >= minVersion) + ++nFound; + pstart = pstart->pprev; + } + + UniValue rv(UniValue::VOBJ); + rv.push_back(Pair("status", nFound >= nRequired)); + rv.push_back(Pair("found", nFound)); + rv.push_back(Pair("required", nRequired)); + rv.push_back(Pair("window", consensusParams.nMajorityWindow)); + return rv; +} + +static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + UniValue rv(UniValue::VOBJ); + rv.push_back(Pair("id", name)); + rv.push_back(Pair("version", version)); + rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))); + rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams))); + return rv; +} + +UniValue getblockchaininfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockchaininfo\n" + "Returns an object containing various state info regarding block chain processing.\n" + "\nResult:\n" + "{\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" + " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" + " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" + " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" + " \"pruneheight\": xxxxxx, (numeric) heighest block available\n" + " \"softforks\": [ (array) status of softforks in progress\n" + " {\n" + " \"id\": \"xxxx\", (string) name of softfork\n" + " \"version\": xx, (numeric) block version\n" + " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n" + " \"status\": xx, (boolean) true if threshold reached\n" + " \"found\": xx, (numeric) number of blocks with the new version found\n" + " \"required\": xx, (numeric) number of blocks required to trigger\n" + " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n" + " },\n" + " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" + " }, ...\n" + " ]\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getblockchaininfo", "") + + HelpExampleRpc("getblockchaininfo", "") + ); + + LOCK(cs_main); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); + obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); + obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); + obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("pruned", fPruneMode)); + + const Consensus::Params& consensusParams = Params().GetConsensus(); + CBlockIndex* tip = chainActive.Tip(); + UniValue softforks(UniValue::VARR); + softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); + softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); + softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); + obj.push_back(Pair("softforks", softforks)); + + if (fPruneMode) + { + CBlockIndex *block = chainActive.Tip(); + while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) + block = block->pprev; + + obj.push_back(Pair("pruneheight", block->nHeight)); + } + return obj; +} + +/** Comparison function for sorting the getchaintips heads. */ +struct CompareBlocksByHeight +{ + bool operator()(const CBlockIndex* a, const CBlockIndex* b) const + { + /* Make sure that unequal blocks with the same height do not compare + equal. Use the pointers themselves to make a distinction. */ + + if (a->nHeight != b->nHeight) + return (a->nHeight > b->nHeight); + + return a < b; + } +}; + +UniValue getchaintips(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getchaintips\n" + "Return information about all known tips in the block tree," + " including the main chain as well as orphaned branches.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"height\": xxxx, (numeric) height of the chain tip\n" + " \"hash\": \"xxxx\", (string) block hash of the tip\n" + " \"branchlen\": 0 (numeric) zero for main chain\n" + " \"status\": \"active\" (string) \"active\" for the main chain\n" + " },\n" + " {\n" + " \"height\": xxxx,\n" + " \"hash\": \"xxxx\",\n" + " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" + " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" + " }\n" + "]\n" + "Possible values for status:\n" + "1. \"invalid\" This branch contains at least one invalid block\n" + "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" + "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" + "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" + "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" + "\nExamples:\n" + + HelpExampleCli("getchaintips", "") + + HelpExampleRpc("getchaintips", "") + ); + + LOCK(cs_main); + + /* Build up a list of chain tips. We start with the list of all + known blocks, and successively remove blocks that appear as pprev + of another block. */ + std::set setTips; + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + setTips.insert(item.second); + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + const CBlockIndex* pprev = item.second->pprev; + if (pprev) + setTips.erase(pprev); + } + + // Always report the currently active tip. + setTips.insert(chainActive.Tip()); + + /* Construct the output array. */ + UniValue res(UniValue::VARR); + BOOST_FOREACH(const CBlockIndex* block, setTips) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("height", block->nHeight)); + obj.push_back(Pair("hash", block->phashBlock->GetHex())); + + const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; + obj.push_back(Pair("branchlen", branchLen)); + + string status; + if (chainActive.Contains(block)) { + // This block is part of the currently active chain. + status = "active"; + } else if (block->nStatus & BLOCK_FAILED_MASK) { + // This block or one of its ancestors is invalid. + status = "invalid"; + } else if (block->nChainTx == 0) { + // This block cannot be connected because full block data for it or one of its parents is missing. + status = "headers-only"; + } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { + // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. + status = "valid-fork"; + } else if (block->IsValid(BLOCK_VALID_TREE)) { + // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. + status = "valid-headers"; + } else { + // No clue. + status = "unknown"; + } + obj.push_back(Pair("status", status)); + + res.push_back(obj); + } + + return res; +} + +UniValue mempoolInfoToJSON() +{ + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("size", (int64_t) mempool.size())); + ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); + ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); + size_t maxmempool = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + ret.push_back(Pair("maxmempool", (int64_t) maxmempool)); + ret.push_back(Pair("mempoolminfee", ValueFromAmount(mempool.GetMinFee(maxmempool).GetFeePerK()))); + + return ret; +} + +UniValue getmempoolinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmempoolinfo\n" + "\nReturns details on the active state of the TX memory pool.\n" + "\nResult:\n" + "{\n" + " \"size\": xxxxx, (numeric) Current tx count\n" + " \"bytes\": xxxxx, (numeric) Sum of all tx sizes\n" + " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" + " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" + " \"mempoolminfee\": xxxxx (numeric) Minimum fee for tx to be accepted\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmempoolinfo", "") + + HelpExampleRpc("getmempoolinfo", "") + ); + + return mempoolInfoToJSON(); +} + +UniValue invalidateblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "invalidateblock \"hash\"\n" + "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" + "\nArguments:\n" + "1. hash (string, required) the hash of the block to mark as invalid\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("invalidateblock", "\"blockhash\"") + + HelpExampleRpc("invalidateblock", "\"blockhash\"") + ); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + CValidationState state; + + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockindex = mapBlockIndex[hash]; + InvalidateBlock(state, Params().GetConsensus(), pblockindex); + } + + if (state.IsValid()) { + ActivateBestChain(state, Params()); + } + + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; +} + +UniValue reconsiderblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "reconsiderblock \"hash\"\n" + "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" + "This can be used to undo the effects of invalidateblock.\n" + "\nArguments:\n" + "1. hash (string, required) the hash of the block to reconsider\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("reconsiderblock", "\"blockhash\"") + + HelpExampleRpc("reconsiderblock", "\"blockhash\"") + ); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + CValidationState state; + + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockindex = mapBlockIndex[hash]; + ReconsiderBlock(state, pblockindex); + } + + if (state.IsValid()) { + ActivateBestChain(state, Params()); + } + + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; +} diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp new file mode 100644 index 000000000..b0e9b6f15 --- /dev/null +++ b/src/rpc/client.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/client.h" +#include "rpc/protocol.h" +#include "util.h" + +#include +#include + +#include // for to_lower() +#include + +using namespace std; + +class CRPCConvertParam +{ +public: + std::string methodName; //! method whose params want conversion + int paramIdx; //! 0-based idx of param to convert +}; + +static const CRPCConvertParam vRPCConvertParams[] = +{ + { "stop", 0 }, + { "setmocktime", 0 }, + { "getaddednodeinfo", 0 }, + { "setgenerate", 0 }, + { "setgenerate", 1 }, + { "generate", 0 }, + { "getnetworkhashps", 0 }, + { "getnetworkhashps", 1 }, + { "sendtoaddress", 1 }, + { "sendtoaddress", 4 }, + { "settxfee", 0 }, + { "getreceivedbyaddress", 1 }, + { "getreceivedbyaccount", 1 }, + { "listreceivedbyaddress", 0 }, + { "listreceivedbyaddress", 1 }, + { "listreceivedbyaddress", 2 }, + { "listreceivedbyaccount", 0 }, + { "listreceivedbyaccount", 1 }, + { "listreceivedbyaccount", 2 }, + { "getbalance", 1 }, + { "getbalance", 2 }, + { "getblockhash", 0 }, + { "move", 2 }, + { "move", 3 }, + { "sendfrom", 2 }, + { "sendfrom", 3 }, + { "listtransactions", 1 }, + { "listtransactions", 2 }, + { "listtransactions", 3 }, + { "listaccounts", 0 }, + { "listaccounts", 1 }, + { "walletpassphrase", 1 }, + { "getblocktemplate", 0 }, + { "listsinceblock", 1 }, + { "listsinceblock", 2 }, + { "sendmany", 1 }, + { "sendmany", 2 }, + { "sendmany", 4 }, + { "addmultisigaddress", 0 }, + { "addmultisigaddress", 1 }, + { "createmultisig", 0 }, + { "createmultisig", 1 }, + { "listunspent", 0 }, + { "listunspent", 1 }, + { "listunspent", 2 }, + { "getblock", 1 }, + { "getblockheader", 1 }, + { "gettransaction", 1 }, + { "getrawtransaction", 1 }, + { "createrawtransaction", 0 }, + { "createrawtransaction", 1 }, + { "createrawtransaction", 2 }, + { "signrawtransaction", 1 }, + { "signrawtransaction", 2 }, + { "sendrawtransaction", 1 }, + { "fundrawtransaction", 1 }, + { "gettxout", 1 }, + { "gettxout", 2 }, + { "gettxoutproof", 0 }, + { "lockunspent", 0 }, + { "lockunspent", 1 }, + { "importprivkey", 2 }, + { "importaddress", 2 }, + { "importaddress", 3 }, + { "importpubkey", 2 }, + { "verifychain", 0 }, + { "verifychain", 1 }, + { "keypoolrefill", 0 }, + { "getrawmempool", 0 }, + { "estimatefee", 0 }, + { "estimatepriority", 0 }, + { "estimatesmartfee", 0 }, + { "estimatesmartpriority", 0 }, + { "prioritisetransaction", 1 }, + { "prioritisetransaction", 2 }, + { "setban", 2 }, + { "setban", 3 }, +}; + +class CRPCConvertTable +{ +private: + std::set > members; + +public: + CRPCConvertTable(); + + bool convert(const std::string& method, int idx) { + return (members.count(std::make_pair(method, idx)) > 0); + } +}; + +CRPCConvertTable::CRPCConvertTable() +{ + const unsigned int n_elem = + (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); + + for (unsigned int i = 0; i < n_elem; i++) { + members.insert(std::make_pair(vRPCConvertParams[i].methodName, + vRPCConvertParams[i].paramIdx)); + } +} + +static CRPCConvertTable rpcCvtTable; + +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal) +{ + UniValue jVal; + if (!jVal.read(std::string("[")+strVal+std::string("]")) || + !jVal.isArray() || jVal.size()!=1) + throw runtime_error(string("Error parsing JSON:")+strVal); + return jVal[0]; +} + +/** Convert strings to command-specific RPC representation */ +UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + UniValue params(UniValue::VARR); + + for (unsigned int idx = 0; idx < strParams.size(); idx++) { + const std::string& strVal = strParams[idx]; + + if (!rpcCvtTable.convert(strMethod, idx)) { + // insert string value directly + params.push_back(strVal); + } else { + // parse string as JSON, insert bool/number/object/etc. value + params.push_back(ParseNonRFCJSONValue(strVal)); + } + } + + return params; +} + diff --git a/src/rpc/client.h b/src/rpc/client.h new file mode 100644 index 000000000..ae015860b --- /dev/null +++ b/src/rpc/client.h @@ -0,0 +1,17 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPCCLIENT_H +#define BITCOIN_RPCCLIENT_H + +#include + +UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal); + +#endif // BITCOIN_RPCCLIENT_H diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp new file mode 100644 index 000000000..996b7f9cb --- /dev/null +++ b/src/rpc/mining.cpp @@ -0,0 +1,800 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "consensus/consensus.h" +#include "consensus/validation.h" +#include "core_io.h" +#include "init.h" +#include "main.h" +#include "miner.h" +#include "net.h" +#include "pow.h" +#include "rpc/server.h" +#include "txmempool.h" +#include "util.h" +#include "utilstrencodings.h" +#include "validationinterface.h" + +#include + +#include +#include + +#include + +using namespace std; + +/** + * Return average network hashes per second based on the last 'lookup' blocks, + * or from the last difficulty change if 'lookup' is nonpositive. + * If 'height' is nonnegative, compute the estimate at the time when a given block was found. + */ +UniValue GetNetworkHashPS(int lookup, int height) { + CBlockIndex *pb = chainActive.Tip(); + + if (height >= 0 && height < chainActive.Height()) + pb = chainActive[height]; + + if (pb == NULL || !pb->nHeight) + return 0; + + // If lookup is -1, then use blocks since last difficulty change. + if (lookup <= 0) + lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1; + + // If lookup is larger than chain, then set it to chain length. + if (lookup > pb->nHeight) + lookup = pb->nHeight; + + CBlockIndex *pb0 = pb; + int64_t minTime = pb0->GetBlockTime(); + int64_t maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64_t time = pb0->GetBlockTime(); + minTime = std::min(time, minTime); + maxTime = std::max(time, maxTime); + } + + // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. + if (minTime == maxTime) + return 0; + + arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork; + int64_t timeDiff = maxTime - minTime; + + return (int64_t)(workDiff.getdouble() / timeDiff); +} + +UniValue getnetworkhashps(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworkhashps ( blocks height )\n" + "\nReturns the estimated network hashes per second based on the last n blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" + "\nArguments:\n" + "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n" + "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" + "\nResult:\n" + "x (numeric) Hashes per second estimated\n" + "\nExamples:\n" + + HelpExampleCli("getnetworkhashps", "") + + HelpExampleRpc("getnetworkhashps", "") + ); + + LOCK(cs_main); + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + +UniValue getgenerate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "\nReturn if the server is set to generate coins or not. The default is false.\n" + "It is set with the command line argument -gen (or " + std::string(BITCOIN_CONF_FILENAME) + " setting gen)\n" + "It can also be set with the setgenerate call.\n" + "\nResult\n" + "true|false (boolean) If the server is set to generate coins or not\n" + "\nExamples:\n" + + HelpExampleCli("getgenerate", "") + + HelpExampleRpc("getgenerate", "") + ); + + LOCK(cs_main); + return GetBoolArg("-gen", DEFAULT_GENERATE); +} + +UniValue generate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "generate numblocks\n" + "\nMine blocks immediately (before the RPC call returns)\n" + "\nNote: this function can only be used on the regtest network\n" + "\nArguments:\n" + "1. numblocks (numeric, required) How many blocks are generated immediately.\n" + "\nResult\n" + "[ blockhashes ] (array) hashes of blocks generated\n" + "\nExamples:\n" + "\nGenerate 11 blocks\n" + + HelpExampleCli("generate", "11") + ); + + if (!Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be used on regtest"); + + int nHeightStart = 0; + int nHeightEnd = 0; + int nHeight = 0; + int nGenerate = params[0].get_int(); + + boost::shared_ptr coinbaseScript; + GetMainSignals().ScriptForMining(coinbaseScript); + + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + //throw an error if no script was provided + if (coinbaseScript->reserveScript.empty()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); + + { // Don't keep cs_main locked + LOCK(cs_main); + nHeightStart = chainActive.Height(); + nHeight = nHeightStart; + nHeightEnd = nHeightStart+nGenerate; + } + unsigned int nExtraNonce = 0; + UniValue blockHashes(UniValue::VARR); + while (nHeight < nHeightEnd) + { + auto_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); + if (!pblocktemplate.get()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); + CBlock *pblock = &pblocktemplate->block; + { + LOCK(cs_main); + IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); + } + while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { + // Yes, there is a chance every nonce could fail to satisfy the -regtest + // target -- 1 in 2^(2^32). That ain't gonna happen. + ++pblock->nNonce; + } + CValidationState state; + if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); + ++nHeight; + blockHashes.push_back(pblock->GetHash().GetHex()); + + //mark script as important because it was used at least for one coinbase output + coinbaseScript->KeepScript(); + } + return blockHashes; +} + +UniValue setgenerate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate generate ( genproclimit )\n" + "\nSet 'generate' true or false to turn generation on or off.\n" + "Generation is limited to 'genproclimit' processors, -1 is unlimited.\n" + "See the getgenerate call for the current setting.\n" + "\nArguments:\n" + "1. generate (boolean, required) Set to true to turn on generation, off to turn off.\n" + "2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n" + "\nExamples:\n" + "\nSet the generation on with a limit of one processor\n" + + HelpExampleCli("setgenerate", "true 1") + + "\nCheck the setting\n" + + HelpExampleCli("getgenerate", "") + + "\nTurn off generation\n" + + HelpExampleCli("setgenerate", "false") + + "\nUsing json rpc\n" + + HelpExampleRpc("setgenerate", "true, 1") + ); + + if (Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Use the generate method instead of setgenerate on this network"); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + int nGenProcLimit = GetArg("-genproclimit", DEFAULT_GENERATE_THREADS); + if (params.size() > 1) + { + nGenProcLimit = params[1].get_int(); + if (nGenProcLimit == 0) + fGenerate = false; + } + + mapArgs["-gen"] = (fGenerate ? "1" : "0"); + mapArgs ["-genproclimit"] = itostr(nGenProcLimit); + GenerateBitcoins(fGenerate, nGenProcLimit, Params()); + + return NullUniValue; +} + +UniValue getmininginfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmininginfo\n" + "\nReturns a json object containing mining-related information." + "\nResult:\n" + "{\n" + " \"blocks\": nnn, (numeric) The current block\n" + " \"currentblocksize\": nnn, (numeric) The last block size\n" + " \"currentblocktx\": nnn, (numeric) The last block transaction\n" + " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" + " \"errors\": \"...\" (string) Current errors\n" + " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" + " \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n" + " \"pooledtx\": n (numeric) The size of the mem pool\n" + " \"testnet\": true|false (boolean) If using testnet or not\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmininginfo", "") + + HelpExampleRpc("getmininginfo", "") + ); + + + LOCK(cs_main); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + 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("genproclimit", (int)GetArg("-genproclimit", DEFAULT_GENERATE_THREADS))); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("generate", getgenerate(params, false))); + return obj; +} + + +// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts +UniValue prioritisetransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "prioritisetransaction \n" + "Accepts the transaction into mined blocks at a higher (or lower) priority\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id.\n" + "2. priority delta (numeric, required) The priority to add or subtract.\n" + " The transaction selection algorithm considers the tx as it would have a higher priority.\n" + " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n" + "3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" + " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" + " considers the transaction as it would have paid a higher (or lower) fee.\n" + "\nResult\n" + "true (boolean) Returns true\n" + "\nExamples:\n" + + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashStr(params[0].get_str(), "txid"); + CAmount nAmount = params[2].get_int64(); + + mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); + return true; +} + + +// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller +static UniValue BIP22ValidationResult(const CValidationState& state) +{ + if (state.IsValid()) + return NullUniValue; + + std::string strRejectReason = state.GetRejectReason(); + if (state.IsError()) + throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); + if (state.IsInvalid()) + { + if (strRejectReason.empty()) + return "rejected"; + return strRejectReason; + } + // Should be impossible + return "valid?"; +} + +UniValue getblocktemplate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getblocktemplate ( \"jsonrequestobject\" )\n" + "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" + "It returns data needed to construct a block to work on.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + + "\nArguments:\n" + "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" + " {\n" + " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" + " \"capabilities\":[ (array, optional) A list of strings\n" + " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" + " ,...\n" + " ]\n" + " }\n" + "\n" + + "\nResult:\n" + "{\n" + " \"version\" : n, (numeric) The block version\n" + " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" + " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" + " {\n" + " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" + " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n" + " \"depends\" : [ (array) array of numbers \n" + " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" + " ,...\n" + " ],\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" + " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" + " }\n" + " ,...\n" + " ],\n" + " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" + " \"flags\" : \"flags\" (string) \n" + " },\n" + " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" + " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" + " \"target\" : \"xxxx\", (string) The hash target\n" + " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" + " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" + " ,...\n" + " ],\n" + " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n" + " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" + " \"sizelimit\" : n, (numeric) limit of block size\n" + " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + " \"bits\" : \"xxx\", (string) compressed target of next block\n" + " \"height\" : n (numeric) The height of the next block\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getblocktemplate", "") + + HelpExampleRpc("getblocktemplate", "") + ); + + LOCK(cs_main); + + std::string strMode = "template"; + UniValue lpval = NullUniValue; + if (params.size() > 0) + { + const UniValue& oparam = params[0].get_obj(); + const UniValue& modeval = find_value(oparam, "mode"); + if (modeval.isStr()) + strMode = modeval.get_str(); + else if (modeval.isNull()) + { + /* Do nothing */ + } + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + lpval = find_value(oparam, "longpollid"); + + if (strMode == "proposal") + { + const UniValue& dataval = find_value(oparam, "data"); + if (!dataval.isStr()) + throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); + + CBlock block; + if (!DecodeHexBlk(block, dataval.get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + uint256 hash = block.GetHash(); + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) { + CBlockIndex *pindex = mi->second; + if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) + return "duplicate"; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return "duplicate-invalid"; + return "duplicate-inconclusive"; + } + + CBlockIndex* const pindexPrev = chainActive.Tip(); + // TestBlockValidity only supports blocks built on the current Tip + if (block.hashPrevBlock != pindexPrev->GetBlockHash()) + return "inconclusive-not-best-prevblk"; + CValidationState state; + TestBlockValidity(state, Params(), block, pindexPrev, false, true); + return BIP22ValidationResult(state); + } + } + + if (strMode != "template") + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); + + static unsigned int nTransactionsUpdatedLast; + + if (!lpval.isNull()) + { + // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions + uint256 hashWatchedChain; + boost::system_time checktxtime; + unsigned int nTransactionsUpdatedLastLP; + + if (lpval.isStr()) + { + // Format: + std::string lpstr = lpval.get_str(); + + hashWatchedChain.SetHex(lpstr.substr(0, 64)); + nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); + } + else + { + // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier + hashWatchedChain = chainActive.Tip()->GetBlockHash(); + nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; + } + + // Release the wallet and main lock while waiting + LEAVE_CRITICAL_SECTION(cs_main); + { + checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); + + boost::unique_lock lock(csBestBlock); + while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) + { + if (!cvBlockChange.timed_wait(lock, checktxtime)) + { + // Timeout: Check transactions for update + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) + break; + checktxtime += boost::posix_time::seconds(10); + } + } + } + ENTER_CRITICAL_SECTION(cs_main); + + if (!IsRPCRunning()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); + // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? + } + + // Update block + static CBlockIndex* pindexPrev; + static int64_t nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != 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 = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); + nStart = GetTime(); + + // Create new block + if(pblocktemplate) + { + delete pblocktemplate; + pblocktemplate = NULL; + } + CScript scriptDummy = CScript() << OP_TRUE; + pblocktemplate = CreateNewBlock(Params(), scriptDummy); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + UpdateTime(pblock, Params().GetConsensus(), pindexPrev); + pblock->nNonce = 0; + + UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); + + UniValue transactions(UniValue::VARR); + map setTxIndex; + int i = 0; + BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase()) + continue; + + UniValue entry(UniValue::VOBJ); + + entry.push_back(Pair("data", EncodeHexTx(tx))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + UniValue deps(UniValue::VARR); + BOOST_FOREACH (const CTxIn &in, tx.vin) + { + if (setTxIndex.count(in.prevout.hash)) + deps.push_back(setTxIndex[in.prevout.hash]); + } + entry.push_back(Pair("depends", deps)); + + int index_in_template = i - 1; + entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); + entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); + + transactions.push_back(entry); + } + + UniValue aux(UniValue::VOBJ); + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + + static UniValue aMutable(UniValue::VARR); + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("capabilities", aCaps)); + 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("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); + 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", pblock->GetBlockTime())); + result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + + return result; +} + +class submitblock_StateCatcher : public CValidationInterface +{ +public: + uint256 hash; + bool found; + CValidationState state; + + submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}; + +protected: + virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) { + if (block.GetHash() != hash) + return; + found = true; + state = stateIn; + }; +}; + +UniValue submitblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" + "\nAttempts to submit new block to network.\n" + "The 'jsonparametersobject' parameter is currently ignored.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + + "\nArguments\n" + "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" + "2. \"jsonparametersobject\" (string, optional) object of optional parameters\n" + " {\n" + " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n" + " }\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("submitblock", "\"mydata\"") + + HelpExampleRpc("submitblock", "\"mydata\"") + ); + + CBlock block; + if (!DecodeHexBlk(block, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + uint256 hash = block.GetHash(); + bool fBlockPresent = false; + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) { + CBlockIndex *pindex = mi->second; + if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) + return "duplicate"; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return "duplicate-invalid"; + // Otherwise, we might only have the header - process the block before returning + fBlockPresent = true; + } + } + + CValidationState state; + submitblock_StateCatcher sc(block.GetHash()); + RegisterValidationInterface(&sc); + bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); + UnregisterValidationInterface(&sc); + if (fBlockPresent) + { + if (fAccepted && !sc.found) + return "duplicate-inconclusive"; + return "duplicate"; + } + if (fAccepted) + { + if (!sc.found) + return "inconclusive"; + state = sc.state; + } + return BIP22ValidationResult(state); +} + +UniValue estimatefee(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatefee nblocks\n" + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within nblocks blocks.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "n (numeric) estimated fee-per-kilobyte\n" + "\n" + "A negative value is returned if not enough transactions and blocks\n" + "have been observed to make an estimate.\n" + "\nExample:\n" + + HelpExampleCli("estimatefee", "6") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + if (nBlocks < 1) + nBlocks = 1; + + CFeeRate feeRate = mempool.estimateFee(nBlocks); + if (feeRate == CFeeRate(0)) + return -1.0; + + return ValueFromAmount(feeRate.GetFeePerK()); +} + +UniValue estimatepriority(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatepriority nblocks\n" + "\nEstimates the approximate priority a zero-fee transaction needs to begin\n" + "confirmation within nblocks blocks.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "n (numeric) estimated priority\n" + "\n" + "A negative value is returned if not enough transactions and blocks\n" + "have been observed to make an estimate.\n" + "\nExample:\n" + + HelpExampleCli("estimatepriority", "6") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + if (nBlocks < 1) + nBlocks = 1; + + return mempool.estimatePriority(nBlocks); +} + +UniValue estimatesmartfee(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatesmartfee nblocks\n" + "\nWARNING: This interface is unstable and may disappear or change!\n" + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within nblocks blocks if possible and return the number of blocks\n" + "for which the estimate is valid.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "{\n" + " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" + " \"blocks\" : n (numeric) block number where estimate was found\n" + "}\n" + "\n" + "A negative value is returned if not enough transactions and blocks\n" + "have been observed to make an estimate for any number of blocks.\n" + "However it will not return a value below the mempool reject fee.\n" + "\nExample:\n" + + HelpExampleCli("estimatesmartfee", "6") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + + UniValue result(UniValue::VOBJ); + int answerFound; + CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound); + result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); + result.push_back(Pair("blocks", answerFound)); + return result; +} + +UniValue estimatesmartpriority(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatesmartpriority nblocks\n" + "\nWARNING: This interface is unstable and may disappear or change!\n" + "\nEstimates the approximate priority a zero-fee transaction needs to begin\n" + "confirmation within nblocks blocks if possible and return the number of blocks\n" + "for which the estimate is valid.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "{\n" + " \"priority\" : x.x, (numeric) estimated priority\n" + " \"blocks\" : n (numeric) block number where estimate was found\n" + "}\n" + "\n" + "A negative value is returned if not enough transactions and blocks\n" + "have been observed to make an estimate for any number of blocks.\n" + "However if the mempool reject fee is set it will return 1e9 * MAX_MONEY.\n" + "\nExample:\n" + + HelpExampleCli("estimatesmartpriority", "6") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + + UniValue result(UniValue::VOBJ); + int answerFound; + double priority = mempool.estimateSmartPriority(nBlocks, &answerFound); + result.push_back(Pair("priority", priority)); + result.push_back(Pair("blocks", answerFound)); + return result; +} diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp new file mode 100644 index 000000000..0aab9c304 --- /dev/null +++ b/src/rpc/misc.cpp @@ -0,0 +1,398 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "clientversion.h" +#include "init.h" +#include "main.h" +#include "net.h" +#include "netbase.h" +#include "rpc/server.h" +#include "timedata.h" +#include "util.h" +#include "utilstrencodings.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#include "wallet/walletdb.h" +#endif + +#include + +#include + +#include + +using namespace std; + +/** + * @note Do not add or change anything in the information returned by this + * method. `getinfo` exists for backwards-compatibility only. It combines + * information from wildly different sources in the program, which is a mess, + * and is thus planned to be deprecated eventually. + * + * Based on the source of the information, new information should be added to: + * - `getblockchaininfo`, + * - `getnetworkinfo` or + * - `getwalletinfo` + * + * Or alternatively, create a specific query method for the information. + **/ +UniValue getinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" + " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"errors\": \"...\" (string) any error messages\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getinfo", "") + + HelpExampleRpc("getinfo", "") + ); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("version", CLIENT_VERSION)); + obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } +#endif + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } + if (pwalletMain && pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); +#endif + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + +#ifdef ENABLE_WALLET +class DescribeAddressVisitor : public boost::static_visitor +{ +public: + UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } + + UniValue operator()(const CKeyID &keyID) const { + UniValue obj(UniValue::VOBJ); + CPubKey vchPubKey; + obj.push_back(Pair("isscript", false)); + if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) { + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + } + return obj; + } + + UniValue operator()(const CScriptID &scriptID) const { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("isscript", true)); + if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + UniValue a(UniValue::VARR); + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + } + return obj; + } +}; +#endif + +UniValue validateaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \"bitcoinaddress\"\n" + "\nReturn information about the given bitcoin address.\n" + "\nArguments:\n" + "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n" + "\nResult:\n" + "{\n" + " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" + " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n" + " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"ismine\" : true|false, (boolean) If the address is yours or not\n" + " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" + " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" + " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + ); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); + + CScript scriptPubKey = GetScriptForDestination(dest); + ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + +#ifdef ENABLE_WALLET + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); + ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); + UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + ret.pushKVs(detail); + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); +#endif + } + return ret; +} + +/** + * Used by addmultisigaddress / createmultisig: + */ +CScript _createmultisig_redeemScript(const UniValue& params) +{ + int nRequired = params[0].get_int(); + const UniValue& keys = params[1].get_array(); + + // 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 %u keys, but need at least %d to redeem)", keys.size(), nRequired)); + if (keys.size() > 16) + throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); +#ifdef ENABLE_WALLET + // Case 1: Bitcoin address and we have full public key: + CBitcoinAddress address(ks); + if (pwalletMain && address.IsValid()) + { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key",ks)); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s",ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + + // Case 2: hex public key + else +#endif + if (IsHex(ks)) + { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + else + { + throw runtime_error(" Invalid public key: "+ks); + } + } + CScript result = GetScriptForMultisig(nRequired, pubkeys); + + if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) + throw runtime_error( + strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)); + + return result; +} + +UniValue createmultisig(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) + { + string msg = "createmultisig nrequired [\"key\",...]\n" + "\nCreates a multi-signature address with n signature of m keys required.\n" + "It returns a json object with the address and redeemScript.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n" + " [\n" + " \"key\" (string) bitcoin address or hex-encoded public key\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "{\n" + " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" + " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" + "}\n" + + "\nExamples:\n" + "\nCreate a multisig address from 2 addresses\n" + + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + ; + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig_redeemScript(params); + CScriptID innerID(inner); + CBitcoinAddress address(innerID); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + +UniValue verifymessage(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" + "\nVerify a signed message\n" + "\nArguments:\n" + "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the signature.\n" + "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" + "3. \"message\" (string, required) The message that was signed.\n" + "\nResult:\n" + "true|false (boolean) If the signature is verified or not.\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + "\nVerify the signature\n" + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") + ); + + LOCK(cs_main); + + 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(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + bool fInvalid = false; + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkey; + if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + return false; + + return (pubkey.GetID() == keyID); +} + +UniValue setmocktime(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "setmocktime timestamp\n" + "\nSet the local time to given timestamp (-regtest only)\n" + "\nArguments:\n" + "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n" + " Pass 0 to go back to using the system time." + ); + + if (!Params().MineBlocksOnDemand()) + throw runtime_error("setmocktime for regression testing (-regtest mode) only"); + + // cs_vNodes is locked and node send/receive times are updated + // atomically with the time change to prevent peers from being + // disconnected because we think we haven't communicated with them + // in a long time. + LOCK2(cs_main, cs_vNodes); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + SetMockTime(params[0].get_int64()); + + uint64_t t = GetTime(); + BOOST_FOREACH(CNode* pnode, vNodes) { + pnode->nLastSend = pnode->nLastRecv = t; + } + + return NullUniValue; +} diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp new file mode 100644 index 000000000..065214a98 --- /dev/null +++ b/src/rpc/net.cpp @@ -0,0 +1,628 @@ +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/server.h" + +#include "chainparams.h" +#include "clientversion.h" +#include "main.h" +#include "net.h" +#include "netbase.h" +#include "protocol.h" +#include "sync.h" +#include "timedata.h" +#include "ui_interface.h" +#include "util.h" +#include "utilstrencodings.h" +#include "version.h" + +#include + +#include + +using namespace std; + +UniValue getconnectioncount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "\nReturns the number of connections to other nodes.\n" + "\nResult:\n" + "n (numeric) The connection count\n" + "\nExamples:\n" + + HelpExampleCli("getconnectioncount", "") + + HelpExampleRpc("getconnectioncount", "") + ); + + LOCK2(cs_main, cs_vNodes); + + return (int)vNodes.size(); +} + +UniValue ping(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "ping\n" + "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n" + "\nExamples:\n" + + HelpExampleCli("ping", "") + + HelpExampleRpc("ping", "") + ); + + // Request that each node send a ping during next message processing pass + LOCK2(cs_main, cs_vNodes); + + BOOST_FOREACH(CNode* pNode, vNodes) { + pNode->fPingQueued = true; + } + + return NullUniValue; +} + +static void CopyNodeStats(std::vector& vstats) +{ + vstats.clear(); + + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + BOOST_FOREACH(CNode* pnode, vNodes) { + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); + } +} + +UniValue getpeerinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpeerinfo\n" + "\nReturns data about each connected network node as a json array of objects.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"id\": n, (numeric) Peer index\n" + " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" + " \"addrlocal\":\"ip:port\", (string) local address\n" + " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" + " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" + " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" + " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" + " \"bytessent\": n, (numeric) The total bytes sent\n" + " \"bytesrecv\": n, (numeric) The total bytes received\n" + " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" + " \"pingtime\": n, (numeric) ping time\n" + " \"minping\": n, (numeric) minimum observed ping time\n" + " \"pingwait\": n, (numeric) ping wait\n" + " \"version\": v, (numeric) The peer version, such as 7001\n" + " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" + " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" + " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" + " \"banscore\": n, (numeric) The ban score\n" + " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" + " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" + " \"inflight\": [\n" + " n, (numeric) The heights of blocks we're currently asking from this peer\n" + " ...\n" + " ]\n" + " \"bytessent_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" + " ...\n" + " }\n" + " \"bytesrecv_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" + " ...\n" + " }\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getpeerinfo", "") + + HelpExampleRpc("getpeerinfo", "") + ); + + LOCK(cs_main); + + vector vstats; + CopyNodeStats(vstats); + + UniValue ret(UniValue::VARR); + + BOOST_FOREACH(const CNodeStats& stats, vstats) { + UniValue obj(UniValue::VOBJ); + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); + obj.push_back(Pair("id", stats.nodeid)); + obj.push_back(Pair("addr", stats.addrName)); + if (!(stats.addrLocal.empty())) + obj.push_back(Pair("addrlocal", stats.addrLocal)); + obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); + obj.push_back(Pair("relaytxes", stats.fRelayTxes)); + obj.push_back(Pair("lastsend", stats.nLastSend)); + obj.push_back(Pair("lastrecv", stats.nLastRecv)); + obj.push_back(Pair("bytessent", stats.nSendBytes)); + obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); + obj.push_back(Pair("conntime", stats.nTimeConnected)); + obj.push_back(Pair("timeoffset", stats.nTimeOffset)); + obj.push_back(Pair("pingtime", stats.dPingTime)); + obj.push_back(Pair("minping", stats.dPingMin)); + if (stats.dPingWait > 0.0) + obj.push_back(Pair("pingwait", stats.dPingWait)); + obj.push_back(Pair("version", stats.nVersion)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifiying the JSON output by putting special characters in + // their ver message. + obj.push_back(Pair("subver", stats.cleanSubVer)); + obj.push_back(Pair("inbound", stats.fInbound)); + obj.push_back(Pair("startingheight", stats.nStartingHeight)); + if (fStateStats) { + obj.push_back(Pair("banscore", statestats.nMisbehavior)); + obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); + obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); + UniValue heights(UniValue::VARR); + BOOST_FOREACH(int height, statestats.vHeightInFlight) { + heights.push_back(height); + } + obj.push_back(Pair("inflight", heights)); + } + obj.push_back(Pair("whitelisted", stats.fWhitelisted)); + + UniValue sendPerMsgCmd(UniValue::VOBJ); + BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapSendBytesPerMsgCmd) { + if (i.second > 0) + sendPerMsgCmd.push_back(Pair(i.first, i.second)); + } + obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd)); + + UniValue recvPerMsgCmd(UniValue::VOBJ); + BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapRecvBytesPerMsgCmd) { + if (i.second > 0) + recvPerMsgCmd.push_back(Pair(i.first, i.second)); + } + obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd)); + + ret.push_back(obj); + } + + return ret; +} + +UniValue addnode(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() == 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() != 2 || + (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "addnode \"node\" \"add|remove|onetry\"\n" + "\nAttempts add or remove a node from the addnode list.\n" + "Or try a connection to a node once.\n" + "\nArguments:\n" + "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" + "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" + "\nExamples:\n" + + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"") + ); + + string strNode = params[0].get_str(); + + if (strCommand == "onetry") + { + CAddress addr; + OpenNetworkConnection(addr, NULL, strNode.c_str()); + return NullUniValue; + } + + LOCK(cs_vAddedNodes); + vector::iterator it = vAddedNodes.begin(); + for(; it != vAddedNodes.end(); it++) + if (strNode == *it) + break; + + if (strCommand == "add") + { + if (it != vAddedNodes.end()) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); + vAddedNodes.push_back(strNode); + } + else if(strCommand == "remove") + { + if (it == vAddedNodes.end()) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); + vAddedNodes.erase(it); + } + + return NullUniValue; +} + +UniValue disconnectnode(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "disconnectnode \"node\" \n" + "\nImmediately disconnects from the specified node.\n" + "\nArguments:\n" + "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" + "\nExamples:\n" + + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + ); + + CNode* pNode = FindNode(params[0].get_str()); + if (pNode == NULL) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); + + pNode->fDisconnect = true; + + return NullUniValue; +} + +UniValue getaddednodeinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getaddednodeinfo dns ( \"node\" )\n" + "\nReturns information about the given added node, or all added nodes\n" + "(note that onetry addnodes are not listed here)\n" + "If dns is false, only a list of added nodes will be provided,\n" + "otherwise connected information will also be available.\n" + "\nArguments:\n" + "1. dns (boolean, required) If false, only a list of added nodes will be provided, otherwise connected information will also be available.\n" + "2. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"addednode\" : \"192.168.0.201\", (string) The node ip address\n" + " \"connected\" : true|false, (boolean) If connected\n" + " \"addresses\" : [\n" + " {\n" + " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server host and port\n" + " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" + " }\n" + " ,...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddednodeinfo", "true") + + HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"") + + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"") + ); + + bool fDns = params[0].get_bool(); + + list laddedNodes(0); + if (params.size() == 1) + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) + laddedNodes.push_back(strAddNode); + } + else + { + string strNode = params[1].get_str(); + LOCK(cs_vAddedNodes); + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) { + if (strAddNode == strNode) + { + laddedNodes.push_back(strAddNode); + break; + } + } + if (laddedNodes.size() == 0) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); + } + + UniValue ret(UniValue::VARR); + if (!fDns) + { + BOOST_FOREACH (const std::string& strAddNode, laddedNodes) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", strAddNode)); + ret.push_back(obj); + } + return ret; + } + + list > > laddedAddreses(0); + BOOST_FOREACH(const std::string& strAddNode, laddedNodes) { + vector vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) + laddedAddreses.push_back(make_pair(strAddNode, vservNode)); + else + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", strAddNode)); + obj.push_back(Pair("connected", false)); + UniValue addresses(UniValue::VARR); + obj.push_back(Pair("addresses", addresses)); + } + } + + LOCK(cs_vNodes); + for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", it->first)); + + UniValue addresses(UniValue::VARR); + bool fConnected = false; + BOOST_FOREACH(const CService& addrNode, it->second) { + bool fFound = false; + UniValue node(UniValue::VOBJ); + node.push_back(Pair("address", addrNode.ToString())); + BOOST_FOREACH(CNode* pnode, vNodes) { + if (pnode->addr == addrNode) + { + fFound = true; + fConnected = true; + node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); + break; + } + } + if (!fFound) + node.push_back(Pair("connected", "false")); + addresses.push_back(node); + } + obj.push_back(Pair("connected", fConnected)); + obj.push_back(Pair("addresses", addresses)); + ret.push_back(obj); + } + + return ret; +} + +UniValue getnettotals(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "\nReturns information about network traffic, including bytes in, bytes out,\n" + "and current time.\n" + "\nResult:\n" + "{\n" + " \"totalbytesrecv\": n, (numeric) Total bytes received\n" + " \"totalbytessent\": n, (numeric) Total bytes sent\n" + " \"timemillis\": t, (numeric) Total cpu time\n" + " \"uploadtarget\":\n" + " {\n" + " \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n" + " \"target\": n, (numeric) Target in bytes\n" + " \"target_reached\": true|false, (boolean) True if target is reached\n" + " \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n" + " \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n" + " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" + " }\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getnettotals", "") + + HelpExampleRpc("getnettotals", "") + ); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); + obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); + obj.push_back(Pair("timemillis", GetTimeMillis())); + + UniValue outboundLimit(UniValue::VOBJ); + outboundLimit.push_back(Pair("timeframe", CNode::GetMaxOutboundTimeframe())); + outboundLimit.push_back(Pair("target", CNode::GetMaxOutboundTarget())); + outboundLimit.push_back(Pair("target_reached", CNode::OutboundTargetReached(false))); + outboundLimit.push_back(Pair("serve_historical_blocks", !CNode::OutboundTargetReached(true))); + outboundLimit.push_back(Pair("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft())); + outboundLimit.push_back(Pair("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle())); + obj.push_back(Pair("uploadtarget", outboundLimit)); + return obj; +} + +static UniValue GetNetworksInfo() +{ + UniValue networks(UniValue::VARR); + for(int n=0; n(n); + if(network == NET_UNROUTABLE) + continue; + proxyType proxy; + UniValue obj(UniValue::VOBJ); + GetProxy(network, proxy); + obj.push_back(Pair("name", GetNetworkName(network))); + obj.push_back(Pair("limited", IsLimited(network))); + obj.push_back(Pair("reachable", IsReachable(network))); + obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); + obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); + networks.push_back(obj); + } + return networks; +} + +UniValue getnetworkinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getnetworkinfo\n" + "Returns an object containing various state info regarding P2P networking.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"networks\": [ (array) information per network\n" + " {\n" + " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" + " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" + " \"reachable\": true|false, (boolean) is the network reachable?\n" + " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" + " }\n" + " ,...\n" + " ],\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"localaddresses\": [ (array) list of local addresses\n" + " {\n" + " \"address\": \"xxxx\", (string) network address\n" + " \"port\": xxx, (numeric) network port\n" + " \"score\": xxx (numeric) relative score\n" + " }\n" + " ,...\n" + " ]\n" + " \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getnetworkinfo", "") + + HelpExampleRpc("getnetworkinfo", "") + ); + + LOCK(cs_main); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("version", CLIENT_VERSION)); + obj.push_back(Pair("subversion", strSubVersion)); + obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); + obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); + obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("networks", GetNetworksInfo())); + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + UniValue localAddresses(UniValue::VARR); + { + LOCK(cs_mapLocalHost); + BOOST_FOREACH(const PAIRTYPE(CNetAddr, LocalServiceInfo) &item, mapLocalHost) + { + UniValue rec(UniValue::VOBJ); + rec.push_back(Pair("address", item.first.ToString())); + rec.push_back(Pair("port", item.second.nPort)); + rec.push_back(Pair("score", item.second.nScore)); + localAddresses.push_back(rec); + } + } + obj.push_back(Pair("localaddresses", localAddresses)); + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + return obj; +} + +UniValue setban(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() >= 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() < 2 || + (strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n" + "\nAttempts add or remove a IP/Subnet from the banned list.\n" + "\nArguments:\n" + "1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n" + "2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n" + "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" + "4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + "\nExamples:\n" + + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400") + ); + + CSubNet subNet; + CNetAddr netAddr; + bool isSubnet = false; + + if (params[0].get_str().find("/") != string::npos) + isSubnet = true; + + if (!isSubnet) + netAddr = CNetAddr(params[0].get_str()); + else + subNet = CSubNet(params[0].get_str()); + + if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); + + if (strCommand == "add") + { + if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr)) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); + + int64_t banTime = 0; //use standard bantime if not specified + if (params.size() >= 3 && !params[2].isNull()) + banTime = params[2].get_int64(); + + bool absolute = false; + if (params.size() == 4 && params[3].isTrue()) + absolute = true; + + isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); + + //disconnect possible nodes + while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) + bannedNode->fDisconnect = true; + } + else if(strCommand == "remove") + { + if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) + throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); + } + + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); + + return NullUniValue; +} + +UniValue listbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listbanned\n" + "\nList all banned IPs/Subnets.\n" + "\nExamples:\n" + + HelpExampleCli("listbanned", "") + + HelpExampleRpc("listbanned", "") + ); + + banmap_t banMap; + CNode::GetBanned(banMap); + + UniValue bannedAddresses(UniValue::VARR); + for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) + { + CBanEntry banEntry = (*it).second; + UniValue rec(UniValue::VOBJ); + rec.push_back(Pair("address", (*it).first.ToString())); + rec.push_back(Pair("banned_until", banEntry.nBanUntil)); + rec.push_back(Pair("ban_created", banEntry.nCreateTime)); + rec.push_back(Pair("ban_reason", banEntry.banReasonToString())); + + bannedAddresses.push_back(rec); + } + + return bannedAddresses; +} + +UniValue clearbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "clearbanned\n" + "\nClear all banned IPs.\n" + "\nExamples:\n" + + HelpExampleCli("clearbanned", "") + + HelpExampleRpc("clearbanned", "") + ); + + CNode::ClearBanned(); + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); + + return NullUniValue; +} diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp new file mode 100644 index 000000000..f5275062a --- /dev/null +++ b/src/rpc/protocol.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/protocol.h" + +#include "random.h" +#include "tinyformat.h" +#include "util.h" +#include "utilstrencodings.h" +#include "utiltime.h" +#include "version.h" + +#include +#include + +using namespace std; + +/** + * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, + * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were + * unspecified (HTTP errors and contents of 'error'). + * + * 1.0 spec: http://json-rpc.org/wiki/specification + * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html + */ + +string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) +{ + UniValue request(UniValue::VOBJ); + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return request.write() + "\n"; +} + +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) +{ + UniValue reply(UniValue::VOBJ); + if (!error.isNull()) + reply.push_back(Pair("result", NullUniValue)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id) +{ + UniValue reply = JSONRPCReplyObj(result, error, id); + return reply.write() + "\n"; +} + +UniValue JSONRPCError(int code, const string& message) +{ + UniValue error(UniValue::VOBJ); + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + +/** Username used when cookie authentication is in use (arbitrary, only for + * recognizability in debugging/logging purposes) + */ +static const std::string COOKIEAUTH_USER = "__cookie__"; +/** Default name for auth cookie file */ +static const std::string COOKIEAUTH_FILE = ".cookie"; + +boost::filesystem::path GetAuthCookieFile() +{ + boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + if (!path.is_complete()) path = GetDataDir() / path; + return path; +} + +bool GenerateAuthCookie(std::string *cookie_out) +{ + unsigned char rand_pwd[32]; + GetRandBytes(rand_pwd, 32); + std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32); + + /** the umask determines what permissions are used to create this file - + * these are set to 077 in init.cpp unless overridden with -sysperms. + */ + std::ofstream file; + boost::filesystem::path filepath = GetAuthCookieFile(); + file.open(filepath.string().c_str()); + if (!file.is_open()) { + LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); + return false; + } + file << cookie; + file.close(); + LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); + + if (cookie_out) + *cookie_out = cookie; + return true; +} + +bool GetAuthCookie(std::string *cookie_out) +{ + std::ifstream file; + std::string cookie; + boost::filesystem::path filepath = GetAuthCookieFile(); + file.open(filepath.string().c_str()); + if (!file.is_open()) + return false; + std::getline(file, cookie); + file.close(); + + if (cookie_out) + *cookie_out = cookie; + return true; +} + +void DeleteAuthCookie() +{ + try { + boost::filesystem::remove(GetAuthCookieFile()); + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what()); + } +} + diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h new file mode 100644 index 000000000..55d0aac68 --- /dev/null +++ b/src/rpc/protocol.h @@ -0,0 +1,93 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPCPROTOCOL_H +#define BITCOIN_RPCPROTOCOL_H + +#include +#include +#include +#include +#include + +#include + +//! HTTP status codes +enum HTTPStatusCode +{ + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_BAD_METHOD = 405, + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_SERVICE_UNAVAILABLE = 503, +}; + +//! Bitcoin RPC error codes +enum RPCErrorCode +{ + //! Standard JSON-RPC 2.0 errors + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602, + RPC_INTERNAL_ERROR = -32603, + RPC_PARSE_ERROR = -32700, + + //! General application defined errors + RPC_MISC_ERROR = -1, //! std::exception thrown in command handling + RPC_FORBIDDEN_BY_SAFE_MODE = -2, //! Server is in safe mode, and command is not allowed in safe mode + RPC_TYPE_ERROR = -3, //! Unexpected type was passed as parameter + RPC_INVALID_ADDRESS_OR_KEY = -5, //! Invalid address or key + RPC_OUT_OF_MEMORY = -7, //! Ran out of memory during operation + RPC_INVALID_PARAMETER = -8, //! Invalid, missing or duplicate parameter + RPC_DATABASE_ERROR = -20, //! Database error + RPC_DESERIALIZATION_ERROR = -22, //! Error parsing or validating structure in raw format + RPC_VERIFY_ERROR = -25, //! General error during transaction or block submission + RPC_VERIFY_REJECTED = -26, //! Transaction or block was rejected by network rules + RPC_VERIFY_ALREADY_IN_CHAIN = -27, //! Transaction already in chain + RPC_IN_WARMUP = -28, //! Client still warming up + + //! Aliases for backward compatibility + RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, + RPC_TRANSACTION_REJECTED = RPC_VERIFY_REJECTED, + RPC_TRANSACTION_ALREADY_IN_CHAIN= RPC_VERIFY_ALREADY_IN_CHAIN, + + //! P2P client errors + RPC_CLIENT_NOT_CONNECTED = -9, //! Bitcoin is not connected + RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks + RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added + RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before + RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes + RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet + + //! Wallet errors + RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.) + RPC_WALLET_INSUFFICIENT_FUNDS = -6, //! Not enough funds in wallet or account + RPC_WALLET_INVALID_ACCOUNT_NAME = -11, //! Invalid account name + RPC_WALLET_KEYPOOL_RAN_OUT = -12, //! Keypool ran out, call keypoolrefill first + RPC_WALLET_UNLOCK_NEEDED = -13, //! Enter the wallet passphrase with walletpassphrase first + RPC_WALLET_PASSPHRASE_INCORRECT = -14, //! The wallet passphrase entered was incorrect + RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) + RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet + RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked +}; + +std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); +std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); +UniValue JSONRPCError(int code, const std::string& message); + +/** Get name of RPC authentication cookie file */ +boost::filesystem::path GetAuthCookieFile(); +/** Generate a new RPC authentication cookie and write it to disk */ +bool GenerateAuthCookie(std::string *cookie_out); +/** Read the RPC authentication cookie from disk */ +bool GetAuthCookie(std::string *cookie_out); +/** Delete RPC authentication cookie from disk */ +void DeleteAuthCookie(); + +#endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp new file mode 100644 index 000000000..2ec80f468 --- /dev/null +++ b/src/rpc/rawtransaction.cpp @@ -0,0 +1,841 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "chain.h" +#include "coins.h" +#include "consensus/validation.h" +#include "core_io.h" +#include "init.h" +#include "keystore.h" +#include "main.h" +#include "merkleblock.h" +#include "net.h" +#include "policy/policy.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" +#include "txmempool.h" +#include "uint256.h" +#include "utilstrencodings.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include + +#include + +#include + +using namespace std; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + out.push_back(Pair("type", GetTxnOutputType(type))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + UniValue a(UniValue::VARR); + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +{ + entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + UniValue vin(UniValue::VARR); + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + UniValue in(UniValue::VOBJ); + if (tx.IsCoinBase()) + in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else { + in.push_back(Pair("txid", txin.prevout.hash.GetHex())); + in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + UniValue o(UniValue::VOBJ); + o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); + o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.push_back(Pair("scriptSig", o)); + } + in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& txout = tx.vout[i]; + UniValue out(UniValue::VOBJ); + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("n", (int64_t)i)); + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + + if (!hashBlock.IsNull()) { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); + entry.push_back(Pair("time", pindex->GetBlockTime())); + entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +UniValue getrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getrawtransaction \"txid\" ( verbose )\n" + "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" + "or there is an unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option.\n" + "\nReturn the raw transaction data.\n" + "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" + "If verbose is non-zero, returns an Object with information about 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n" + + "\nResult (if verbose is not set or set to 0):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult (if verbose > 0):\n" + "{\n" + " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" + " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"size\" : n, (numeric) The transaction size\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) \n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"bitcoinaddress\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + " \"blockhash\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The confirmations\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getrawtransaction", "\"mytxid\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" 1") + + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + bool fVerbose = false; + if (params.size() > 1) + fVerbose = (params[1].get_int() != 0); + + CTransaction tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + + string strHex = EncodeHexTx(tx); + + if (!fVerbose) + return strHex; + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", strHex)); + TxToJSON(tx, hashBlock, result); + return result; +} + +UniValue gettxoutproof(const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 1 && params.size() != 2)) + throw runtime_error( + "gettxoutproof [\"txid\",...] ( blockhash )\n" + "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" + "\nNOTE: By default this function only works sometimes. This is when there is an\n" + "unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option or\n" + "specify the block in which the transaction is included in manually (by blockhash).\n" + "\nReturn the raw transaction data.\n" + "\nArguments:\n" + "1. \"txids\" (string) A json array of txids to filter\n" + " [\n" + " \"txid\" (string) A transaction hash\n" + " ,...\n" + " ]\n" + "2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n" + "\nResult:\n" + "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + ); + + set setTxids; + uint256 oneTxid; + UniValue txids = params[0].get_array(); + for (unsigned int idx = 0; idx < txids.size(); idx++) { + const UniValue& txid = txids[idx]; + if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); + uint256 hash(uint256S(txid.get_str())); + if (setTxids.count(hash)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); + setTxids.insert(hash); + oneTxid = hash; + } + + LOCK(cs_main); + + CBlockIndex* pblockindex = NULL; + + uint256 hashBlock; + if (params.size() > 1) + { + hashBlock = uint256S(params[1].get_str()); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + pblockindex = mapBlockIndex[hashBlock]; + } else { + CCoins coins; + if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) + pblockindex = chainActive[coins.nHeight]; + } + + if (pblockindex == NULL) + { + CTransaction tx; + if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); + pblockindex = mapBlockIndex[hashBlock]; + } + + CBlock block; + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + unsigned int ntxFound = 0; + BOOST_FOREACH(const CTransaction&tx, block.vtx) + if (setTxids.count(tx.GetHash())) + ntxFound++; + if (ntxFound != setTxids.size()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::string strHex = HexStr(ssMB.begin(), ssMB.end()); + return strHex; +} + +UniValue verifytxoutproof(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "verifytxoutproof \"proof\"\n" + "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" + "and throwing an RPC error if the block is not in our best chain\n" + "\nArguments:\n" + "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" + "\nResult:\n" + "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" + ); + + CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); + CMerkleBlock merkleBlock; + ssMB >> merkleBlock; + + UniValue res(UniValue::VARR); + + vector vMatch; + if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) + return res; + + LOCK(cs_main); + + if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); + + BOOST_FOREACH(const uint256& hash, vMatch) + res.push_back(hash.GetHex()); + return res; +} + +UniValue createrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" + "\nCreate a transaction spending the given inputs and creating new outputs.\n" + "Outputs can be addresses or data.\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network.\n" + + "\nArguments:\n" + "1. \"transactions\" (string, required) A json array of json objects\n" + " [\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n (numeric, required) The output number\n" + " }\n" + " ,...\n" + " ]\n" + "2. \"outputs\" (string, required) a json object with outputs\n" + " {\n" + " \"address\": x.xxx (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" + " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n" + " ...\n" + " }\n" + "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" + "\nResult:\n" + "\"transaction\" (string) hex string of the transaction\n" + + "\nExamples\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); + if (params[0].isNull() || params[1].isNull()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); + + UniValue inputs = params[0].get_array(); + UniValue sendTo = params[1].get_obj(); + + CMutableTransaction rawTx; + + if (params.size() > 2 && !params[2].isNull()) { + int64_t nLockTime = params[2].get_int64(); + if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); + rawTx.nLockTime = nLockTime; + } + + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); + + uint256 txid = ParseHashO(o, "txid"); + + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); + + rawTx.vin.push_back(in); + } + + set setAddress; + vector addrList = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, addrList) { + + if (name_ == "data") { + std::vector data = ParseHexV(sendTo[name_].getValStr(),"Data"); + + CTxOut out(0, CScript() << OP_RETURN << data); + rawTx.vout.push_back(out); + } else { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + } + + return EncodeHexTx(rawTx); +} + +UniValue decoderawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decoderawtransaction \"hexstring\"\n" + "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" + + "\nArguments:\n" + "1. \"hex\" (string, required) The transaction hex string\n" + + "\nResult:\n" + "{\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"size\" : n, (numeric) The transaction size\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) The output number\n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") + ); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + CTransaction tx; + + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + UniValue result(UniValue::VOBJ); + TxToJSON(tx, uint256(), result); + + return result; +} + +UniValue decodescript(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decodescript \"hex\"\n" + "\nDecode a hex-encoded script.\n" + "\nArguments:\n" + "1. \"hex\" (string) the hex encoded script\n" + "\nResult:\n" + "{\n" + " \"asm\":\"asm\", (string) Script public key\n" + " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"type\":\"type\", (string) The output type\n" + " \"reqSigs\": n, (numeric) The required signatures\n" + " \"addresses\": [ (json array of string)\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ],\n" + " \"p2sh\",\"address\" (string) script address\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("decodescript", "\"hexstring\"") + + HelpExampleRpc("decodescript", "\"hexstring\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + UniValue r(UniValue::VOBJ); + CScript script; + if (params[0].get_str().size() > 0){ + vector scriptData(ParseHexV(params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + return r; +} + +/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ +static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) +{ + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txin.prevout.hash.ToString())); + entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); + entry.push_back(Pair("error", strMessage)); + vErrorsRet.push_back(entry); +} + +UniValue signrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "The third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + + HelpRequiringPassphrase() + "\n" +#endif + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings, or 'null' if none provided)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + HelpExampleRpc("signrawtransaction", "\"myhex\"") + ); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); + + vector txData(ParseHexV(params[0], "argument 1")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) { + try { + CMutableTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CMutableTransaction mergedTx(txVariants[0]); + + // Fetch previous transactions (inputs): + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(&viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { + const uint256& prevHash = txin.prevout.hash; + CCoins coins; + view.AccessCoins(prevHash); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && !params[2].isNull()) { + fGivenKeys = true; + UniValue keys = params[2].get_array(); + for (unsigned int idx = 0; idx < keys.size(); idx++) { + UniValue k = keys[idx]; + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + tempKeystore.AddKey(key); + } + } +#ifdef ENABLE_WALLET + else if (pwalletMain) + EnsureWalletIsUnlocked(); +#endif + + // Add previous txouts given in the RPC call: + if (params.size() > 1 && !params[1].isNull()) { + UniValue prevTxs = params[1].get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + const UniValue& p = prevTxs[idx]; + if (!p.isObject()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + UniValue prevOut = p.get_obj(); + + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); + + uint256 txid = ParseHashO(prevOut, "txid"); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + vector pkData(ParseHexO(prevOut, "scriptPubKey")); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ + ScriptToAsmStr(scriptPubKey); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut+1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; // we don't know the actual output value + } + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); + UniValue v = find_value(prevOut, "redeemScript"); + if (!v.isNull()) { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + +#ifdef ENABLE_WALLET + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif + + int nHashType = SIGHASH_ALL; + if (params.size() > 3 && !params[3].isNull()) { + static map mapSigHashValues = + boost::assign::map_list_of + (string("ALL"), int(SIGHASH_ALL)) + (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) + (string("NONE"), int(SIGHASH_NONE)) + (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) + (string("SINGLE"), int(SIGHASH_SINGLE)) + (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) + ; + string strHashType = params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Script verification errors + UniValue vErrors(UniValue::VARR); + + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); + continue; + } + const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + + txin.scriptSig.clear(); + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + + // ... and merge in other signatures: + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { + txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + } + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } + } + bool fComplete = vErrors.empty(); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(mergedTx))); + result.push_back(Pair("complete", fComplete)); + if (!vErrors.empty()) { + result.push_back(Pair("errors", vErrors)); + } + + return result; +} + +UniValue sendrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "sendrawtransaction \"hexstring\" ( allowhighfees )\n" + "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" + "\nAlso see createrawtransaction and signrawtransaction calls.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" + "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + "\nResult:\n" + "\"hex\" (string) The transaction hash in hex\n" + "\nExamples:\n" + "\nCreate a transaction\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + + "Sign the transaction, and get back the hex\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + "\nSend the transaction (signed hex)\n" + + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") + ); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); + + // parse hex string from parameter + CTransaction tx; + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + uint256 hashTx = tx.GetHash(); + + bool fOverrideFees = false; + if (params.size() > 1) + fOverrideFees = params[1].get_bool(); + + CCoinsViewCache &view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hashTx); + bool fHaveMempool = mempool.exists(hashTx); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) { + if (state.IsInvalid()) { + throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + } else { + if (fMissingInputs) { + throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + } + throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + } + } + } else if (fHaveChain) { + throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + } + RelayTransaction(tx); + + return hashTx.GetHex(); +} diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp new file mode 100644 index 000000000..b2d4559cc --- /dev/null +++ b/src/rpc/server.cpp @@ -0,0 +1,539 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/server.h" + +#include "base58.h" +#include "init.h" +#include "random.h" +#include "sync.h" +#include "ui_interface.h" +#include "util.h" +#include "utilstrencodings.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for to_upper() + +using namespace RPCServer; +using namespace std; + +static bool fRPCRunning = false; +static bool fRPCInWarmup = true; +static std::string rpcWarmupStatus("RPC server started"); +static CCriticalSection cs_rpcWarmup; +/* Timer-creating functions */ +static RPCTimerInterface* timerInterface = NULL; +/* Map of name to timer. + * @note Can be changed to std::unique_ptr when C++11 */ +static std::map > deadlineTimers; + +static struct CRPCSignals +{ + boost::signals2::signal Started; + boost::signals2::signal Stopped; + boost::signals2::signal PreCommand; + boost::signals2::signal PostCommand; +} g_rpcSignals; + +void RPCServer::OnStarted(boost::function slot) +{ + g_rpcSignals.Started.connect(slot); +} + +void RPCServer::OnStopped(boost::function slot) +{ + g_rpcSignals.Stopped.connect(slot); +} + +void RPCServer::OnPreCommand(boost::function slot) +{ + g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); +} + +void RPCServer::OnPostCommand(boost::function slot) +{ + g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); +} + +void RPCTypeCheck(const UniValue& params, + const list& typesExpected, + bool fAllowNull) +{ + unsigned int i = 0; + BOOST_FOREACH(UniValue::VType t, typesExpected) + { + if (params.size() <= i) + break; + + const UniValue& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.isNull())))) + { + string err = strprintf("Expected type %s, got %s", + uvTypeName(t), uvTypeName(v.type())); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + i++; + } +} + +void RPCTypeCheckObj(const UniValue& o, + const map& typesExpected, + bool fAllowNull) +{ + BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) + { + const UniValue& v = find_value(o, t.first); + if (!fAllowNull && v.isNull()) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); + + if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) + { + string err = strprintf("Expected type %s for %s, got %s", + uvTypeName(t.second), t.first, uvTypeName(v.type())); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + } +} + +CAmount AmountFromValue(const UniValue& value) +{ + if (!value.isNum() && !value.isStr()) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); + CAmount amount; + if (!ParseFixedPoint(value.getValStr(), 8, &amount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + if (!MoneyRange(amount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); + return amount; +} + +UniValue ValueFromAmount(const CAmount& amount) +{ + bool sign = amount < 0; + int64_t n_abs = (sign ? -amount : amount); + int64_t quotient = n_abs / COIN; + int64_t remainder = n_abs % COIN; + return UniValue(UniValue::VNUM, + strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); +} + +uint256 ParseHashV(const UniValue& v, string strName) +{ + string strHex; + if (v.isStr()) + 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; +} +uint256 ParseHashO(const UniValue& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector ParseHexV(const UniValue& v, string strName) +{ + string strHex; + if (v.isStr()) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector ParseHexO(const UniValue& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + +/** + * Note: This interface may still be subject to change. + */ + +std::string CRPCTable::help(const std::string& strCommand) const +{ + string strRet; + string category; + set setDone; + vector > vCommands; + + for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) + vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); + sort(vCommands.begin(), vCommands.end()); + + BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands) + { + const CRPCCommand *pcmd = command.second; + string strMethod = pcmd->name; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod.find("label") != string::npos) + continue; + if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) + continue; + try + { + UniValue params; + rpcfn_type pfn = pcmd->actor; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (const std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + { + if (strHelp.find('\n') != string::npos) + strHelp = strHelp.substr(0, strHelp.find('\n')); + + if (category != pcmd->category) + { + if (!category.empty()) + strRet += "\n"; + category = pcmd->category; + string firstLetter = category.substr(0,1); + boost::to_upper(firstLetter); + strRet += "== " + firstLetter + category.substr(1) + " ==\n"; + } + } + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + +UniValue help(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help ( \"command\" )\n" + "\nList all commands, or get help for a specified command.\n" + "\nArguments:\n" + "1. \"command\" (string, optional) The command to get help on\n" + "\nResult:\n" + "\"text\" (string) The help text\n" + ); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + return tableRPC.help(strCommand); +} + + +UniValue stop(const UniValue& params, bool fHelp) +{ + // Accept the deprecated and ignored 'detach' boolean argument + if (fHelp || params.size() > 1) + throw runtime_error( + "stop\n" + "\nStop Bitcoin server."); + // Event loop will exit after current HTTP requests have been handled, so + // this reply will get back to the client. + StartShutdown(); + return "Bitcoin server stopping"; +} + +/** + * Call Table + */ +static const CRPCCommand vRPCCommands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + /* Overall control/query calls */ + { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ + { "control", "help", &help, true }, + { "control", "stop", &stop, true }, + + /* P2P networking */ + { "network", "getnetworkinfo", &getnetworkinfo, true }, + { "network", "addnode", &addnode, true }, + { "network", "disconnectnode", &disconnectnode, true }, + { "network", "getaddednodeinfo", &getaddednodeinfo, true }, + { "network", "getconnectioncount", &getconnectioncount, true }, + { "network", "getnettotals", &getnettotals, true }, + { "network", "getpeerinfo", &getpeerinfo, true }, + { "network", "ping", &ping, true }, + { "network", "setban", &setban, true }, + { "network", "listbanned", &listbanned, true }, + { "network", "clearbanned", &clearbanned, true }, + + /* Block chain and UTXO */ + { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, + { "blockchain", "getbestblockhash", &getbestblockhash, true }, + { "blockchain", "getblockcount", &getblockcount, true }, + { "blockchain", "getblock", &getblock, true }, + { "blockchain", "getblockhash", &getblockhash, true }, + { "blockchain", "getblockheader", &getblockheader, true }, + { "blockchain", "getchaintips", &getchaintips, true }, + { "blockchain", "getdifficulty", &getdifficulty, true }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, + { "blockchain", "getrawmempool", &getrawmempool, true }, + { "blockchain", "gettxout", &gettxout, true }, + { "blockchain", "gettxoutproof", &gettxoutproof, true }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, + { "blockchain", "verifychain", &verifychain, true }, + + /* Mining */ + { "mining", "getblocktemplate", &getblocktemplate, true }, + { "mining", "getmininginfo", &getmininginfo, true }, + { "mining", "getnetworkhashps", &getnetworkhashps, true }, + { "mining", "prioritisetransaction", &prioritisetransaction, true }, + { "mining", "submitblock", &submitblock, true }, + + /* Coin generation */ + { "generating", "getgenerate", &getgenerate, true }, + { "generating", "setgenerate", &setgenerate, true }, + { "generating", "generate", &generate, true }, + + /* Raw transactions */ + { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, + { "rawtransactions", "decodescript", &decodescript, true }, + { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ + + /* Utility functions */ + { "util", "createmultisig", &createmultisig, true }, + { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ + { "util", "verifymessage", &verifymessage, true }, + { "util", "estimatefee", &estimatefee, true }, + { "util", "estimatepriority", &estimatepriority, true }, + { "util", "estimatesmartfee", &estimatesmartfee, true }, + { "util", "estimatesmartpriority", &estimatesmartpriority, true }, + + /* Not shown in help */ + { "hidden", "invalidateblock", &invalidateblock, true }, + { "hidden", "reconsiderblock", &reconsiderblock, true }, + { "hidden", "setmocktime", &setmocktime, true }, +}; + +CRPCTable::CRPCTable() +{ + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + { + const CRPCCommand *pcmd; + + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } +} + +const CRPCCommand *CRPCTable::operator[](const std::string &name) const +{ + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} + +bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd) +{ + if (IsRPCRunning()) + return false; + + // don't allow overwriting for now + map::const_iterator it = mapCommands.find(name); + if (it != mapCommands.end()) + return false; + + mapCommands[name] = pcmd; + return true; +} + +bool StartRPC() +{ + LogPrint("rpc", "Starting RPC\n"); + fRPCRunning = true; + g_rpcSignals.Started(); + return true; +} + +void InterruptRPC() +{ + LogPrint("rpc", "Interrupting RPC\n"); + // Interrupt e.g. running longpolls + fRPCRunning = false; +} + +void StopRPC() +{ + LogPrint("rpc", "Stopping RPC\n"); + deadlineTimers.clear(); + g_rpcSignals.Stopped(); +} + +bool IsRPCRunning() +{ + return fRPCRunning; +} + +void SetRPCWarmupStatus(const std::string& newStatus) +{ + LOCK(cs_rpcWarmup); + rpcWarmupStatus = newStatus; +} + +void SetRPCWarmupFinished() +{ + LOCK(cs_rpcWarmup); + assert(fRPCInWarmup); + fRPCInWarmup = false; +} + +bool RPCIsInWarmup(std::string *outStatus) +{ + LOCK(cs_rpcWarmup); + if (outStatus) + *outStatus = rpcWarmupStatus; + return fRPCInWarmup; +} + +void JSONRequest::parse(const UniValue& valRequest) +{ + // Parse request + if (!valRequest.isObject()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + const UniValue& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + UniValue valMethod = find_value(request, "method"); + if (valMethod.isNull()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); + if (!valMethod.isStr()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getblocktemplate") + LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); + + // Parse params + UniValue valParams = find_value(request, "params"); + if (valParams.isArray()) + params = valParams.get_array(); + else if (valParams.isNull()) + params = UniValue(UniValue::VARR); + else + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); +} + +static UniValue JSONRPCExecOne(const UniValue& req) +{ + UniValue rpc_result(UniValue::VOBJ); + + JSONRequest jreq; + try { + jreq.parse(req); + + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); + } + catch (const UniValue& objError) + { + rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); + } + catch (const std::exception& e) + { + rpc_result = JSONRPCReplyObj(NullUniValue, + JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + } + + return rpc_result; +} + +std::string JSONRPCExecBatch(const UniValue& vReq) +{ + UniValue ret(UniValue::VARR); + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return ret.write() + "\n"; +} + +UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const +{ + // Return immediately if in warmup + { + LOCK(cs_rpcWarmup); + if (fRPCInWarmup) + throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); + } + + // Find method + const CRPCCommand *pcmd = tableRPC[strMethod]; + if (!pcmd) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + + g_rpcSignals.PreCommand(*pcmd); + + try + { + // Execute + return pcmd->actor(params, false); + } + catch (const std::exception& e) + { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } + + g_rpcSignals.PostCommand(*pcmd); +} + +std::string HelpExampleCli(const std::string& methodname, const std::string& args) +{ + return "> bitcoin-cli " + methodname + " " + args + "\n"; +} + +std::string HelpExampleRpc(const std::string& methodname, const std::string& args) +{ + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; +} + +void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) +{ + if (!timerInterface) + timerInterface = iface; +} + +void RPCSetTimerInterface(RPCTimerInterface *iface) +{ + timerInterface = iface; +} + +void RPCUnsetTimerInterface(RPCTimerInterface *iface) +{ + if (timerInterface == iface) + timerInterface = NULL; +} + +void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds) +{ + if (!timerInterface) + throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); + deadlineTimers.erase(name); + LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); + deadlineTimers.insert(std::make_pair(name, boost::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); +} + +CRPCTable tableRPC; diff --git a/src/rpc/server.h b/src/rpc/server.h new file mode 100644 index 000000000..38a237d62 --- /dev/null +++ b/src/rpc/server.h @@ -0,0 +1,243 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPCSERVER_H +#define BITCOIN_RPCSERVER_H + +#include "amount.h" +#include "rpc/protocol.h" +#include "uint256.h" + +#include +#include +#include +#include + +#include + +#include + +class CRPCCommand; + +namespace RPCServer +{ + void OnStarted(boost::function slot); + void OnStopped(boost::function slot); + void OnPreCommand(boost::function slot); + void OnPostCommand(boost::function slot); +} + +class CBlockIndex; +class CNetAddr; + +class JSONRequest +{ +public: + UniValue id; + std::string strMethod; + UniValue params; + + JSONRequest() { id = NullUniValue; } + void parse(const UniValue& valRequest); +}; + +/** Query whether RPC is running */ +bool IsRPCRunning(); + +/** + * Set the RPC warmup status. When this is done, all RPC calls will error out + * immediately with RPC_IN_WARMUP. + */ +void SetRPCWarmupStatus(const std::string& newStatus); +/* Mark warmup as done. RPC calls will be processed from now on. */ +void SetRPCWarmupFinished(); + +/* returns the current warmup state. */ +bool RPCIsInWarmup(std::string *statusOut); + +/** + * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that + * the right number of arguments are passed, just that any passed are the correct type. + * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); + */ +void RPCTypeCheck(const UniValue& params, + const std::list& typesExpected, bool fAllowNull=false); + +/* + Check for expected keys/value types in an Object. + Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); +*/ +void RPCTypeCheckObj(const UniValue& o, + const std::map& typesExpected, bool fAllowNull=false); + +/** Opaque base class for timers returned by NewTimerFunc. + * This provides no methods at the moment, but makes sure that delete + * cleans up the whole state. + */ +class RPCTimerBase +{ +public: + virtual ~RPCTimerBase() {} +}; + +/** + * RPC timer "driver". + */ +class RPCTimerInterface +{ +public: + virtual ~RPCTimerInterface() {} + /** Implementation name */ + virtual const char *Name() = 0; + /** Factory function for timers. + * RPC will call the function to create a timer that will call func in *millis* milliseconds. + * @note As the RPC mechanism is backend-neutral, it can use different implementations of timers. + * This is needed to cope with the case in which there is no HTTP server, but + * only GUI RPC console, and to break the dependency of pcserver on httprpc. + */ + virtual RPCTimerBase* NewTimer(boost::function& func, int64_t millis) = 0; +}; + +/** Set the factory function for timers */ +void RPCSetTimerInterface(RPCTimerInterface *iface); +/** Set the factory function for timer, but only, if unset */ +void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface); +/** Unset factory function for timers */ +void RPCUnsetTimerInterface(RPCTimerInterface *iface); + +/** + * Run func nSeconds from now. + * Overrides previous timer (if any). + */ +void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds); + +typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp); + +class CRPCCommand +{ +public: + std::string category; + std::string name; + rpcfn_type actor; + bool okSafeMode; +}; + +/** + * Bitcoin RPC command dispatcher. + */ +class CRPCTable +{ +private: + std::map mapCommands; +public: + CRPCTable(); + const CRPCCommand* operator[](const std::string& name) const; + std::string help(const std::string& name) const; + + /** + * Execute a method. + * @param method Method to execute + * @param params UniValue Array of arguments (JSON objects) + * @returns Result of the call. + * @throws an exception (UniValue) when an error happens. + */ + UniValue execute(const std::string &method, const UniValue ¶ms) const; + + + /** + * Appends a CRPCCommand to the dispatch table. + * Returns false if RPC server is already running (dump concurrency protection). + * Commands cannot be overwritten (returns false). + */ + bool appendCommand(const std::string& name, const CRPCCommand* pcmd); +}; + +extern CRPCTable tableRPC; + +/** + * Utilities: convert hex-encoded Values + * (throws error if not hex). + */ +extern uint256 ParseHashV(const UniValue& v, std::string strName); +extern uint256 ParseHashO(const UniValue& o, std::string strKey); +extern std::vector ParseHexV(const UniValue& v, std::string strName); +extern std::vector ParseHexO(const UniValue& o, std::string strKey); + +extern int64_t nWalletUnlockTime; +extern CAmount AmountFromValue(const UniValue& value); +extern UniValue ValueFromAmount(const CAmount& amount); +extern double GetDifficulty(const CBlockIndex* blockindex = NULL); +extern std::string HelpRequiringPassphrase(); +extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); +extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); + +extern void EnsureWalletIsUnlocked(); + +extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp +extern UniValue getpeerinfo(const UniValue& params, bool fHelp); +extern UniValue ping(const UniValue& params, bool fHelp); +extern UniValue addnode(const UniValue& params, bool fHelp); +extern UniValue disconnectnode(const UniValue& params, bool fHelp); +extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); +extern UniValue getnettotals(const UniValue& params, bool fHelp); +extern UniValue setban(const UniValue& params, bool fHelp); +extern UniValue listbanned(const UniValue& params, bool fHelp); +extern UniValue clearbanned(const UniValue& params, bool fHelp); + +extern UniValue getgenerate(const UniValue& params, bool fHelp); // in rpcmining.cpp +extern UniValue setgenerate(const UniValue& params, bool fHelp); +extern UniValue generate(const UniValue& params, bool fHelp); +extern UniValue getnetworkhashps(const UniValue& params, bool fHelp); +extern UniValue getmininginfo(const UniValue& params, bool fHelp); +extern UniValue prioritisetransaction(const UniValue& params, bool fHelp); +extern UniValue getblocktemplate(const UniValue& params, bool fHelp); +extern UniValue submitblock(const UniValue& params, bool fHelp); +extern UniValue estimatefee(const UniValue& params, bool fHelp); +extern UniValue estimatepriority(const UniValue& params, bool fHelp); +extern UniValue estimatesmartfee(const UniValue& params, bool fHelp); +extern UniValue estimatesmartpriority(const UniValue& params, bool fHelp); + +extern UniValue verifymessage(const UniValue& params, bool fHelp); +extern UniValue createmultisig(const UniValue& params, bool fHelp); +extern UniValue validateaddress(const UniValue& params, bool fHelp); +extern UniValue getinfo(const UniValue& params, bool fHelp); +extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); +extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); +extern UniValue setmocktime(const UniValue& params, bool fHelp); + +extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp +extern UniValue listunspent(const UniValue& params, bool fHelp); +extern UniValue lockunspent(const UniValue& params, bool fHelp); +extern UniValue listlockunspent(const UniValue& params, bool fHelp); +extern UniValue createrawtransaction(const UniValue& params, bool fHelp); +extern UniValue decoderawtransaction(const UniValue& params, bool fHelp); +extern UniValue decodescript(const UniValue& params, bool fHelp); +extern UniValue signrawtransaction(const UniValue& params, bool fHelp); +extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); +extern UniValue gettxoutproof(const UniValue& params, bool fHelp); +extern UniValue verifytxoutproof(const UniValue& params, bool fHelp); + +extern UniValue getblockcount(const UniValue& params, bool fHelp); // in rpcblockchain.cpp +extern UniValue getbestblockhash(const UniValue& params, bool fHelp); +extern UniValue getdifficulty(const UniValue& params, bool fHelp); +extern UniValue settxfee(const UniValue& params, bool fHelp); +extern UniValue getmempoolinfo(const UniValue& params, bool fHelp); +extern UniValue getrawmempool(const UniValue& params, bool fHelp); +extern UniValue getblockhash(const UniValue& params, bool fHelp); +extern UniValue getblockheader(const UniValue& params, bool fHelp); +extern UniValue getblock(const UniValue& params, bool fHelp); +extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp); +extern UniValue gettxout(const UniValue& params, bool fHelp); +extern UniValue verifychain(const UniValue& params, bool fHelp); +extern UniValue getchaintips(const UniValue& params, bool fHelp); +extern UniValue invalidateblock(const UniValue& params, bool fHelp); +extern UniValue reconsiderblock(const UniValue& params, bool fHelp); + +bool StartRPC(); +void InterruptRPC(); +void StopRPC(); +std::string JSONRPCExecBatch(const UniValue& vReq); + +#endif // BITCOIN_RPCSERVER_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp deleted file mode 100644 index 954441d15..000000000 --- a/src/rpcblockchain.cpp +++ /dev/null @@ -1,891 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "coins.h" -#include "consensus/validation.h" -#include "main.h" -#include "policy/policy.h" -#include "primitives/transaction.h" -#include "rpcserver.h" -#include "streams.h" -#include "sync.h" -#include "txmempool.h" -#include "util.h" -#include "utilstrencodings.h" - -#include - -#include - -using namespace std; - -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); -void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); - -double GetDifficulty(const CBlockIndex* blockindex) -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - if (blockindex == NULL) - { - if (chainActive.Tip() == NULL) - return 1.0; - else - blockindex = chainActive.Tip(); - } - - int nShift = (blockindex->nBits >> 24) & 0xff; - - double dDiff = - (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); - - while (nShift < 29) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > 29) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -UniValue blockheaderToJSON(const CBlockIndex* blockindex) -{ - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) - confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", blockindex->nVersion)); - result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); - result.push_back(Pair("time", (int64_t)blockindex->nTime)); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce)); - result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) -{ - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) - confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - 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())); - UniValue txs(UniValue::VARR); - BOOST_FOREACH(const CTransaction&tx, block.vtx) - { - if(txDetails) - { - UniValue objTx(UniValue::VOBJ); - TxToJSON(tx, uint256(), objTx); - txs.push_back(objTx); - } - else - txs.push_back(tx.GetHash().GetHex()); - } - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", (uint64_t)block.nNonce)); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - - if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = chainActive.Next(blockindex); - if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); - return result; -} - -UniValue getblockcount(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "\nReturns the number of blocks in the longest block chain.\n" - "\nResult:\n" - "n (numeric) The current block count\n" - "\nExamples:\n" - + HelpExampleCli("getblockcount", "") - + HelpExampleRpc("getblockcount", "") - ); - - LOCK(cs_main); - return chainActive.Height(); -} - -UniValue getbestblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getbestblockhash\n" - "\nReturns the hash of the best (tip) block in the longest block chain.\n" - "\nResult\n" - "\"hex\" (string) the block hash hex encoded\n" - "\nExamples\n" - + HelpExampleCli("getbestblockhash", "") - + HelpExampleRpc("getbestblockhash", "") - ); - - LOCK(cs_main); - return chainActive.Tip()->GetBlockHash().GetHex(); -} - -UniValue getdifficulty(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nResult:\n" - "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nExamples:\n" - + HelpExampleCli("getdifficulty", "") - + HelpExampleRpc("getdifficulty", "") - ); - - LOCK(cs_main); - return GetDifficulty(); -} - -UniValue mempoolToJSON(bool fVerbose = false) -{ - if (fVerbose) - { - LOCK(mempool.cs); - UniValue o(UniValue::VOBJ); - BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) - { - const uint256& hash = e.GetTx().GetHash(); - UniValue info(UniValue::VOBJ); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); - info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); - info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); - info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); - const CTransaction& tx = e.GetTx(); - set setDepends; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (mempool.exists(txin.prevout.hash)) - setDepends.insert(txin.prevout.hash.ToString()); - } - - UniValue depends(UniValue::VARR); - BOOST_FOREACH(const string& dep, setDepends) - { - depends.push_back(dep); - } - - info.push_back(Pair("depends", depends)); - o.push_back(Pair(hash.ToString(), info)); - } - return o; - } - else - { - vector vtxid; - mempool.queryHashes(vtxid); - - UniValue a(UniValue::VARR); - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); - - return a; - } -} - -UniValue getrawmempool(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getrawmempool ( verbose )\n" - "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nArguments:\n" - "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" - "\nResult: (for verbose = false):\n" - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" - " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" - " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " }, ...\n" - "}\n" - "\nExamples\n" - + HelpExampleCli("getrawmempool", "true") - + HelpExampleRpc("getrawmempool", "true") - ); - - LOCK(cs_main); - - bool fVerbose = false; - if (params.size() > 0) - fVerbose = params[0].get_bool(); - - return mempoolToJSON(fVerbose); -} - -UniValue getblockhash(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getblockhash index\n" - "\nReturns hash of block in best-block-chain at index provided.\n" - "\nArguments:\n" - "1. index (numeric, required) The block index\n" - "\nResult:\n" - "\"hash\" (string) The block hash\n" - "\nExamples:\n" - + HelpExampleCli("getblockhash", "1000") - + HelpExampleRpc("getblockhash", "1000") - ); - - LOCK(cs_main); - - int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > chainActive.Height()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - - CBlockIndex* pblockindex = chainActive[nHeight]; - return pblockindex->GetBlockHash().GetHex(); -} - -UniValue getblockheader(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblockheader \"hash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" - "If verbose is true, returns an Object with information about blockheader .\n" - "\nArguments:\n" - "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" - " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockheaderToJSON(pblockindex); -} - -UniValue getblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getblock \"hash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n" - "If verbose is true, returns an Object with information about block .\n" - "\nArguments:\n" - "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"size\" : n, (numeric) The block size\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"tx\" : [ (array of string) The transaction ids\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - " ],\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); - - LOCK(cs_main); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - - bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); - - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - - if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << block; - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; - } - - return blockToJSON(block, pblockindex); -} - -UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gettxoutsetinfo\n" - "\nReturns statistics about the unspent transaction output set.\n" - "Note this call may take some time.\n" - "\nResult:\n" - "{\n" - " \"height\":n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) the best block hash hex\n" - " \"transactions\": n, (numeric) The number of transactions\n" - " \"txouts\": n, (numeric) The number of output transactions\n" - " \"bytes_serialized\": n, (numeric) The serialized size\n" - " \"hash_serialized\": \"hash\", (string) The serialized hash\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("gettxoutsetinfo", "") - + HelpExampleRpc("gettxoutsetinfo", "") - ); - - UniValue ret(UniValue::VOBJ); - - CCoinsStats stats; - FlushStateToDisk(); - if (pcoinsTip->GetStats(stats)) { - ret.push_back(Pair("height", (int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); - ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); - } - return ret; -} - -UniValue gettxout(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - throw runtime_error( - "gettxout \"txid\" n ( includemempool )\n" - "\nReturns details about an unspent transaction output.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. n (numeric, required) vout number\n" - "3. includemempool (boolean, optional) Whether to include the mem pool\n" - "\nResult:\n" - "{\n" - " \"bestblock\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"code\", (string) \n" - " \"hex\" : \"hex\", (string) \n" - " \"reqSigs\" : n, (numeric) Number of required signatures\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of bitcoin addresses\n" - " \"bitcoinaddress\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " },\n" - " \"version\" : n, (numeric) The version\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" - "}\n" - - "\nExamples:\n" - "\nGet unspent transactions\n" - + HelpExampleCli("listunspent", "") + - "\nView the details\n" - + HelpExampleCli("gettxout", "\"txid\" 1") + - "\nAs a json rpc call\n" - + HelpExampleRpc("gettxout", "\"txid\", 1") - ); - - LOCK(cs_main); - - UniValue ret(UniValue::VOBJ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - int n = params[1].get_int(); - bool fMempool = true; - if (params.size() > 2) - fMempool = params[2].get_bool(); - - CCoins coins; - if (fMempool) { - LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip, mempool); - if (!view.GetCoins(hash, coins)) - return NullUniValue; - mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } else { - if (!pcoinsTip->GetCoins(hash, coins)) - return NullUniValue; - } - if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) - return NullUniValue; - - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - CBlockIndex *pindex = it->second; - ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); - if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) - ret.push_back(Pair("confirmations", 0)); - else - ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); - ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); - UniValue o(UniValue::VOBJ); - ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("version", coins.nVersion)); - ret.push_back(Pair("coinbase", coins.fCoinBase)); - - return ret; -} - -UniValue verifychain(const UniValue& params, bool fHelp) -{ - int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL); - int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); - if (fHelp || params.size() > 2) - throw runtime_error( - "verifychain ( checklevel numblocks )\n" - "\nVerifies blockchain database.\n" - "\nArguments:\n" - "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n" - "2. numblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n" - "\nResult:\n" - "true|false (boolean) Verified or not\n" - "\nExamples:\n" - + HelpExampleCli("verifychain", "") - + HelpExampleRpc("verifychain", "") - ); - - LOCK(cs_main); - - if (params.size() > 0) - nCheckLevel = params[0].get_int(); - if (params.size() > 1) - nCheckDepth = params[1].get_int(); - - return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); -} - -/** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams) -{ - int nFound = 0; - CBlockIndex* pstart = pindex; - for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++) - { - if (pstart->nVersion >= minVersion) - ++nFound; - pstart = pstart->pprev; - } - - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("status", nFound >= nRequired)); - rv.push_back(Pair("found", nFound)); - rv.push_back(Pair("required", nRequired)); - rv.push_back(Pair("window", consensusParams.nMajorityWindow)); - return rv; -} - -static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) -{ - UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("version", version)); - rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))); - rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams))); - return rv; -} - -UniValue getblockchaininfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockchaininfo\n" - "Returns an object containing various state info regarding block chain processing.\n" - "\nResult:\n" - "{\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" - " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) heighest block available\n" - " \"softforks\": [ (array) status of softforks in progress\n" - " {\n" - " \"id\": \"xxxx\", (string) name of softfork\n" - " \"version\": xx, (numeric) block version\n" - " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n" - " \"status\": xx, (boolean) true if threshold reached\n" - " \"found\": xx, (numeric) number of blocks with the new version found\n" - " \"required\": xx, (numeric) number of blocks required to trigger\n" - " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n" - " },\n" - " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" - " }, ...\n" - " ]\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getblockchaininfo", "") - + HelpExampleRpc("getblockchaininfo", "") - ); - - LOCK(cs_main); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); - obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); - obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); - obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); - obj.push_back(Pair("pruned", fPruneMode)); - - const Consensus::Params& consensusParams = Params().GetConsensus(); - CBlockIndex* tip = chainActive.Tip(); - UniValue softforks(UniValue::VARR); - softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - obj.push_back(Pair("softforks", softforks)); - - if (fPruneMode) - { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } - return obj; -} - -/** Comparison function for sorting the getchaintips heads. */ -struct CompareBlocksByHeight -{ - bool operator()(const CBlockIndex* a, const CBlockIndex* b) const - { - /* Make sure that unequal blocks with the same height do not compare - equal. Use the pointers themselves to make a distinction. */ - - if (a->nHeight != b->nHeight) - return (a->nHeight > b->nHeight); - - return a < b; - } -}; - -UniValue getchaintips(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getchaintips\n" - "Return information about all known tips in the block tree," - " including the main chain as well as orphaned branches.\n" - "\nResult:\n" - "[\n" - " {\n" - " \"height\": xxxx, (numeric) height of the chain tip\n" - " \"hash\": \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\": 0 (numeric) zero for main chain\n" - " \"status\": \"active\" (string) \"active\" for the main chain\n" - " },\n" - " {\n" - " \"height\": xxxx,\n" - " \"hash\": \"xxxx\",\n" - " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" - " }\n" - "]\n" - "Possible values for status:\n" - "1. \"invalid\" This branch contains at least one invalid block\n" - "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" - "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" - "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" - "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" - "\nExamples:\n" - + HelpExampleCli("getchaintips", "") - + HelpExampleRpc("getchaintips", "") - ); - - LOCK(cs_main); - - /* Build up a list of chain tips. We start with the list of all - known blocks, and successively remove blocks that appear as pprev - of another block. */ - std::set setTips; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - setTips.insert(item.second); - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - const CBlockIndex* pprev = item.second->pprev; - if (pprev) - setTips.erase(pprev); - } - - // Always report the currently active tip. - setTips.insert(chainActive.Tip()); - - /* Construct the output array. */ - UniValue res(UniValue::VARR); - BOOST_FOREACH(const CBlockIndex* block, setTips) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->nHeight)); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); - - const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; - obj.push_back(Pair("branchlen", branchLen)); - - string status; - if (chainActive.Contains(block)) { - // This block is part of the currently active chain. - status = "active"; - } else if (block->nStatus & BLOCK_FAILED_MASK) { - // This block or one of its ancestors is invalid. - status = "invalid"; - } else if (block->nChainTx == 0) { - // This block cannot be connected because full block data for it or one of its parents is missing. - status = "headers-only"; - } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { - // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. - status = "valid-fork"; - } else if (block->IsValid(BLOCK_VALID_TREE)) { - // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. - status = "valid-headers"; - } else { - // No clue. - status = "unknown"; - } - obj.push_back(Pair("status", status)); - - res.push_back(obj); - } - - return res; -} - -UniValue mempoolInfoToJSON() -{ - UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); - size_t maxmempool = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; - ret.push_back(Pair("maxmempool", (int64_t) maxmempool)); - ret.push_back(Pair("mempoolminfee", ValueFromAmount(mempool.GetMinFee(maxmempool).GetFeePerK()))); - - return ret; -} - -UniValue getmempoolinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmempoolinfo\n" - "\nReturns details on the active state of the TX memory pool.\n" - "\nResult:\n" - "{\n" - " \"size\": xxxxx, (numeric) Current tx count\n" - " \"bytes\": xxxxx, (numeric) Sum of all tx sizes\n" - " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" - " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"mempoolminfee\": xxxxx (numeric) Minimum fee for tx to be accepted\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolinfo", "") - + HelpExampleRpc("getmempoolinfo", "") - ); - - return mempoolInfoToJSON(); -} - -UniValue invalidateblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "invalidateblock \"hash\"\n" - "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to mark as invalid\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("invalidateblock", "\"blockhash\"") - + HelpExampleRpc("invalidateblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - InvalidateBlock(state, Params().GetConsensus(), pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state, Params()); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} - -UniValue reconsiderblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "reconsiderblock \"hash\"\n" - "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" - "This can be used to undo the effects of invalidateblock.\n" - "\nArguments:\n" - "1. hash (string, required) the hash of the block to reconsider\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("reconsiderblock", "\"blockhash\"") - + HelpExampleRpc("reconsiderblock", "\"blockhash\"") - ); - - std::string strHash = params[0].get_str(); - uint256 hash(uint256S(strHash)); - CValidationState state; - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReconsiderBlock(state, pblockindex); - } - - if (state.IsValid()) { - ActivateBestChain(state, Params()); - } - - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } - - return NullUniValue; -} diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp deleted file mode 100644 index 047158023..000000000 --- a/src/rpcclient.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "rpcclient.h" - -#include "rpcprotocol.h" -#include "util.h" - -#include -#include - -#include // for to_lower() -#include - -using namespace std; - -class CRPCConvertParam -{ -public: - std::string methodName; //! method whose params want conversion - int paramIdx; //! 0-based idx of param to convert -}; - -static const CRPCConvertParam vRPCConvertParams[] = -{ - { "stop", 0 }, - { "setmocktime", 0 }, - { "getaddednodeinfo", 0 }, - { "setgenerate", 0 }, - { "setgenerate", 1 }, - { "generate", 0 }, - { "getnetworkhashps", 0 }, - { "getnetworkhashps", 1 }, - { "sendtoaddress", 1 }, - { "sendtoaddress", 4 }, - { "settxfee", 0 }, - { "getreceivedbyaddress", 1 }, - { "getreceivedbyaccount", 1 }, - { "listreceivedbyaddress", 0 }, - { "listreceivedbyaddress", 1 }, - { "listreceivedbyaddress", 2 }, - { "listreceivedbyaccount", 0 }, - { "listreceivedbyaccount", 1 }, - { "listreceivedbyaccount", 2 }, - { "getbalance", 1 }, - { "getbalance", 2 }, - { "getblockhash", 0 }, - { "move", 2 }, - { "move", 3 }, - { "sendfrom", 2 }, - { "sendfrom", 3 }, - { "listtransactions", 1 }, - { "listtransactions", 2 }, - { "listtransactions", 3 }, - { "listaccounts", 0 }, - { "listaccounts", 1 }, - { "walletpassphrase", 1 }, - { "getblocktemplate", 0 }, - { "listsinceblock", 1 }, - { "listsinceblock", 2 }, - { "sendmany", 1 }, - { "sendmany", 2 }, - { "sendmany", 4 }, - { "addmultisigaddress", 0 }, - { "addmultisigaddress", 1 }, - { "createmultisig", 0 }, - { "createmultisig", 1 }, - { "listunspent", 0 }, - { "listunspent", 1 }, - { "listunspent", 2 }, - { "getblock", 1 }, - { "getblockheader", 1 }, - { "gettransaction", 1 }, - { "getrawtransaction", 1 }, - { "createrawtransaction", 0 }, - { "createrawtransaction", 1 }, - { "createrawtransaction", 2 }, - { "signrawtransaction", 1 }, - { "signrawtransaction", 2 }, - { "sendrawtransaction", 1 }, - { "fundrawtransaction", 1 }, - { "gettxout", 1 }, - { "gettxout", 2 }, - { "gettxoutproof", 0 }, - { "lockunspent", 0 }, - { "lockunspent", 1 }, - { "importprivkey", 2 }, - { "importaddress", 2 }, - { "importaddress", 3 }, - { "importpubkey", 2 }, - { "verifychain", 0 }, - { "verifychain", 1 }, - { "keypoolrefill", 0 }, - { "getrawmempool", 0 }, - { "estimatefee", 0 }, - { "estimatepriority", 0 }, - { "estimatesmartfee", 0 }, - { "estimatesmartpriority", 0 }, - { "prioritisetransaction", 1 }, - { "prioritisetransaction", 2 }, - { "setban", 2 }, - { "setban", 3 }, -}; - -class CRPCConvertTable -{ -private: - std::set > members; - -public: - CRPCConvertTable(); - - bool convert(const std::string& method, int idx) { - return (members.count(std::make_pair(method, idx)) > 0); - } -}; - -CRPCConvertTable::CRPCConvertTable() -{ - const unsigned int n_elem = - (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); - - for (unsigned int i = 0; i < n_elem; i++) { - members.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramIdx)); - } -} - -static CRPCConvertTable rpcCvtTable; - -/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) - * as well as objects and arrays. - */ -UniValue ParseNonRFCJSONValue(const std::string& strVal) -{ - UniValue jVal; - if (!jVal.read(std::string("[")+strVal+std::string("]")) || - !jVal.isArray() || jVal.size()!=1) - throw runtime_error(string("Error parsing JSON:")+strVal); - return jVal[0]; -} - -/** Convert strings to command-specific RPC representation */ -UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) -{ - UniValue params(UniValue::VARR); - - for (unsigned int idx = 0; idx < strParams.size(); idx++) { - const std::string& strVal = strParams[idx]; - - if (!rpcCvtTable.convert(strMethod, idx)) { - // insert string value directly - params.push_back(strVal); - } else { - // parse string as JSON, insert bool/number/object/etc. value - params.push_back(ParseNonRFCJSONValue(strVal)); - } - } - - return params; -} - diff --git a/src/rpcclient.h b/src/rpcclient.h deleted file mode 100644 index ae015860b..000000000 --- a/src/rpcclient.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPCCLIENT_H -#define BITCOIN_RPCCLIENT_H - -#include - -UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); -/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) - * as well as objects and arrays. - */ -UniValue ParseNonRFCJSONValue(const std::string& strVal); - -#endif // BITCOIN_RPCCLIENT_H diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp deleted file mode 100644 index 958c817d6..000000000 --- a/src/rpcmining.cpp +++ /dev/null @@ -1,800 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "consensus/consensus.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "init.h" -#include "main.h" -#include "miner.h" -#include "net.h" -#include "pow.h" -#include "rpcserver.h" -#include "txmempool.h" -#include "util.h" -#include "utilstrencodings.h" -#include "validationinterface.h" - -#include - -#include -#include - -#include - -using namespace std; - -/** - * Return average network hashes per second based on the last 'lookup' blocks, - * or from the last difficulty change if 'lookup' is nonpositive. - * If 'height' is nonnegative, compute the estimate at the time when a given block was found. - */ -UniValue GetNetworkHashPS(int lookup, int height) { - CBlockIndex *pb = chainActive.Tip(); - - if (height >= 0 && height < chainActive.Height()) - pb = chainActive[height]; - - if (pb == NULL || !pb->nHeight) - return 0; - - // If lookup is -1, then use blocks since last difficulty change. - if (lookup <= 0) - lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1; - - // If lookup is larger than chain, then set it to chain length. - if (lookup > pb->nHeight) - lookup = pb->nHeight; - - CBlockIndex *pb0 = pb; - int64_t minTime = pb0->GetBlockTime(); - int64_t maxTime = minTime; - for (int i = 0; i < lookup; i++) { - pb0 = pb0->pprev; - int64_t time = pb0->GetBlockTime(); - minTime = std::min(time, minTime); - maxTime = std::max(time, maxTime); - } - - // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. - if (minTime == maxTime) - return 0; - - arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork; - int64_t timeDiff = maxTime - minTime; - - return (int64_t)(workDiff.getdouble() / timeDiff); -} - -UniValue getnetworkhashps(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "getnetworkhashps ( blocks height )\n" - "\nReturns the estimated network hashes per second based on the last n blocks.\n" - "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" - "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" - "\nArguments:\n" - "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n" - "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" - "\nResult:\n" - "x (numeric) Hashes per second estimated\n" - "\nExamples:\n" - + HelpExampleCli("getnetworkhashps", "") - + HelpExampleRpc("getnetworkhashps", "") - ); - - LOCK(cs_main); - return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); -} - -UniValue getgenerate(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "\nReturn if the server is set to generate coins or not. The default is false.\n" - "It is set with the command line argument -gen (or " + std::string(BITCOIN_CONF_FILENAME) + " setting gen)\n" - "It can also be set with the setgenerate call.\n" - "\nResult\n" - "true|false (boolean) If the server is set to generate coins or not\n" - "\nExamples:\n" - + HelpExampleCli("getgenerate", "") - + HelpExampleRpc("getgenerate", "") - ); - - LOCK(cs_main); - return GetBoolArg("-gen", DEFAULT_GENERATE); -} - -UniValue generate(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "generate numblocks\n" - "\nMine blocks immediately (before the RPC call returns)\n" - "\nNote: this function can only be used on the regtest network\n" - "\nArguments:\n" - "1. numblocks (numeric, required) How many blocks are generated immediately.\n" - "\nResult\n" - "[ blockhashes ] (array) hashes of blocks generated\n" - "\nExamples:\n" - "\nGenerate 11 blocks\n" - + HelpExampleCli("generate", "11") - ); - - if (!Params().MineBlocksOnDemand()) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be used on regtest"); - - int nHeightStart = 0; - int nHeightEnd = 0; - int nHeight = 0; - int nGenerate = params[0].get_int(); - - boost::shared_ptr coinbaseScript; - GetMainSignals().ScriptForMining(coinbaseScript); - - // If the keypool is exhausted, no script is returned at all. Catch this. - if (!coinbaseScript) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - - //throw an error if no script was provided - if (coinbaseScript->reserveScript.empty()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); - - { // Don't keep cs_main locked - LOCK(cs_main); - nHeightStart = chainActive.Height(); - nHeight = nHeightStart; - nHeightEnd = nHeightStart+nGenerate; - } - unsigned int nExtraNonce = 0; - UniValue blockHashes(UniValue::VARR); - while (nHeight < nHeightEnd) - { - auto_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); - if (!pblocktemplate.get()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); - CBlock *pblock = &pblocktemplate->block; - { - LOCK(cs_main); - IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); - } - while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - // Yes, there is a chance every nonce could fail to satisfy the -regtest - // target -- 1 in 2^(2^32). That ain't gonna happen. - ++pblock->nNonce; - } - CValidationState state; - if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); - ++nHeight; - blockHashes.push_back(pblock->GetHash().GetHex()); - - //mark script as important because it was used at least for one coinbase output - coinbaseScript->KeepScript(); - } - return blockHashes; -} - -UniValue setgenerate(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate generate ( genproclimit )\n" - "\nSet 'generate' true or false to turn generation on or off.\n" - "Generation is limited to 'genproclimit' processors, -1 is unlimited.\n" - "See the getgenerate call for the current setting.\n" - "\nArguments:\n" - "1. generate (boolean, required) Set to true to turn on generation, off to turn off.\n" - "2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n" - "\nExamples:\n" - "\nSet the generation on with a limit of one processor\n" - + HelpExampleCli("setgenerate", "true 1") + - "\nCheck the setting\n" - + HelpExampleCli("getgenerate", "") + - "\nTurn off generation\n" - + HelpExampleCli("setgenerate", "false") + - "\nUsing json rpc\n" - + HelpExampleRpc("setgenerate", "true, 1") - ); - - if (Params().MineBlocksOnDemand()) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Use the generate method instead of setgenerate on this network"); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - int nGenProcLimit = GetArg("-genproclimit", DEFAULT_GENERATE_THREADS); - if (params.size() > 1) - { - nGenProcLimit = params[1].get_int(); - if (nGenProcLimit == 0) - fGenerate = false; - } - - mapArgs["-gen"] = (fGenerate ? "1" : "0"); - mapArgs ["-genproclimit"] = itostr(nGenProcLimit); - GenerateBitcoins(fGenerate, nGenProcLimit, Params()); - - return NullUniValue; -} - -UniValue getmininginfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getmininginfo\n" - "\nReturns a json object containing mining-related information." - "\nResult:\n" - "{\n" - " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblocksize\": nnn, (numeric) The last block size\n" - " \"currentblocktx\": nnn, (numeric) The last block transaction\n" - " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" - " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" - " \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n" - " \"pooledtx\": n (numeric) The size of the mem pool\n" - " \"testnet\": true|false (boolean) If using testnet or not\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getmininginfo", "") - + HelpExampleRpc("getmininginfo", "") - ); - - - LOCK(cs_main); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - 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("genproclimit", (int)GetArg("-genproclimit", DEFAULT_GENERATE_THREADS))); - obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("generate", getgenerate(params, false))); - return obj; -} - - -// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts -UniValue prioritisetransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 3) - throw runtime_error( - "prioritisetransaction \n" - "Accepts the transaction into mined blocks at a higher (or lower) priority\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id.\n" - "2. priority delta (numeric, required) The priority to add or subtract.\n" - " The transaction selection algorithm considers the tx as it would have a higher priority.\n" - " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n" - "3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" - " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" - " considers the transaction as it would have paid a higher (or lower) fee.\n" - "\nResult\n" - "true (boolean) Returns true\n" - "\nExamples:\n" - + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") - + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") - ); - - LOCK(cs_main); - - uint256 hash = ParseHashStr(params[0].get_str(), "txid"); - CAmount nAmount = params[2].get_int64(); - - mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); - return true; -} - - -// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller -static UniValue BIP22ValidationResult(const CValidationState& state) -{ - if (state.IsValid()) - return NullUniValue; - - std::string strRejectReason = state.GetRejectReason(); - if (state.IsError()) - throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); - if (state.IsInvalid()) - { - if (strRejectReason.empty()) - return "rejected"; - return strRejectReason; - } - // Should be impossible - return "valid?"; -} - -UniValue getblocktemplate(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getblocktemplate ( \"jsonrequestobject\" )\n" - "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" - "It returns data needed to construct a block to work on.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" - - "\nArguments:\n" - "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" - " {\n" - " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" - " \"capabilities\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" - " ,...\n" - " ]\n" - " }\n" - "\n" - - "\nResult:\n" - "{\n" - " \"version\" : n, (numeric) The block version\n" - " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" - " {\n" - " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n" - " \"depends\" : [ (array) array of numbers \n" - " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" - " ,...\n" - " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" - " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" - " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" - " }\n" - " ,...\n" - " ],\n" - " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" - " \"flags\" : \"flags\" (string) \n" - " },\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" - " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" - " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" - " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" - " ,...\n" - " ],\n" - " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n" - " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" - " \"sizelimit\" : n, (numeric) limit of block size\n" - " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" - " \"bits\" : \"xxx\", (string) compressed target of next block\n" - " \"height\" : n (numeric) The height of the next block\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("getblocktemplate", "") - + HelpExampleRpc("getblocktemplate", "") - ); - - LOCK(cs_main); - - std::string strMode = "template"; - UniValue lpval = NullUniValue; - if (params.size() > 0) - { - const UniValue& oparam = params[0].get_obj(); - const UniValue& modeval = find_value(oparam, "mode"); - if (modeval.isStr()) - strMode = modeval.get_str(); - else if (modeval.isNull()) - { - /* Do nothing */ - } - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - lpval = find_value(oparam, "longpollid"); - - if (strMode == "proposal") - { - const UniValue& dataval = find_value(oparam, "data"); - if (!dataval.isStr()) - throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); - - CBlock block; - if (!DecodeHexBlk(block, dataval.get_str())) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - - uint256 hash = block.GetHash(); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; - if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) - return "duplicate"; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return "duplicate-invalid"; - return "duplicate-inconclusive"; - } - - CBlockIndex* const pindexPrev = chainActive.Tip(); - // TestBlockValidity only supports blocks built on the current Tip - if (block.hashPrevBlock != pindexPrev->GetBlockHash()) - return "inconclusive-not-best-prevblk"; - CValidationState state; - TestBlockValidity(state, Params(), block, pindexPrev, false, true); - return BIP22ValidationResult(state); - } - } - - if (strMode != "template") - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); - - static unsigned int nTransactionsUpdatedLast; - - if (!lpval.isNull()) - { - // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions - uint256 hashWatchedChain; - boost::system_time checktxtime; - unsigned int nTransactionsUpdatedLastLP; - - if (lpval.isStr()) - { - // Format: - std::string lpstr = lpval.get_str(); - - hashWatchedChain.SetHex(lpstr.substr(0, 64)); - nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); - } - else - { - // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier - hashWatchedChain = chainActive.Tip()->GetBlockHash(); - nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; - } - - // Release the wallet and main lock while waiting - LEAVE_CRITICAL_SECTION(cs_main); - { - checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); - - boost::unique_lock lock(csBestBlock); - while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) - { - if (!cvBlockChange.timed_wait(lock, checktxtime)) - { - // Timeout: Check transactions for update - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) - break; - checktxtime += boost::posix_time::seconds(10); - } - } - } - ENTER_CRITICAL_SECTION(cs_main); - - if (!IsRPCRunning()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); - // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? - } - - // Update block - static CBlockIndex* pindexPrev; - static int64_t nStart; - static CBlockTemplate* pblocktemplate; - if (pindexPrev != chainActive.Tip() || - (mempool.GetTransactionsUpdated() != 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 = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrevNew = chainActive.Tip(); - nStart = GetTime(); - - // Create new block - if(pblocktemplate) - { - delete pblocktemplate; - pblocktemplate = NULL; - } - CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = CreateNewBlock(Params(), scriptDummy); - if (!pblocktemplate) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - CBlock* pblock = &pblocktemplate->block; // pointer for convenience - - // Update nTime - UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - pblock->nNonce = 0; - - UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); - - UniValue transactions(UniValue::VARR); - map setTxIndex; - int i = 0; - BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { - uint256 txHash = tx.GetHash(); - setTxIndex[txHash] = i++; - - if (tx.IsCoinBase()) - continue; - - UniValue entry(UniValue::VOBJ); - - entry.push_back(Pair("data", EncodeHexTx(tx))); - - entry.push_back(Pair("hash", txHash.GetHex())); - - UniValue deps(UniValue::VARR); - BOOST_FOREACH (const CTxIn &in, tx.vin) - { - if (setTxIndex.count(in.prevout.hash)) - deps.push_back(setTxIndex[in.prevout.hash]); - } - entry.push_back(Pair("depends", deps)); - - int index_in_template = i - 1; - entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); - entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); - - transactions.push_back(entry); - } - - UniValue aux(UniValue::VOBJ); - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - - arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - - static UniValue aMutable(UniValue::VARR); - if (aMutable.empty()) - { - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); - } - - UniValue result(UniValue::VOBJ); - result.push_back(Pair("capabilities", aCaps)); - 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("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); - 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", pblock->GetBlockTime())); - result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - - return result; -} - -class submitblock_StateCatcher : public CValidationInterface -{ -public: - uint256 hash; - bool found; - CValidationState state; - - submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}; - -protected: - virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) { - if (block.GetHash() != hash) - return; - found = true; - state = stateIn; - }; -}; - -UniValue submitblock(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" - "\nAttempts to submit new block to network.\n" - "The 'jsonparametersobject' parameter is currently ignored.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" - - "\nArguments\n" - "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" - "2. \"jsonparametersobject\" (string, optional) object of optional parameters\n" - " {\n" - " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n" - " }\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("submitblock", "\"mydata\"") - + HelpExampleRpc("submitblock", "\"mydata\"") - ); - - CBlock block; - if (!DecodeHexBlk(block, params[0].get_str())) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - - uint256 hash = block.GetHash(); - bool fBlockPresent = false; - { - LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; - if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) - return "duplicate"; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return "duplicate-invalid"; - // Otherwise, we might only have the header - process the block before returning - fBlockPresent = true; - } - } - - CValidationState state; - submitblock_StateCatcher sc(block.GetHash()); - RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); - UnregisterValidationInterface(&sc); - if (fBlockPresent) - { - if (fAccepted && !sc.found) - return "duplicate-inconclusive"; - return "duplicate"; - } - if (fAccepted) - { - if (!sc.found) - return "inconclusive"; - state = sc.state; - } - return BIP22ValidationResult(state); -} - -UniValue estimatefee(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatefee nblocks\n" - "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within nblocks blocks.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "n (numeric) estimated fee-per-kilobyte\n" - "\n" - "A negative value is returned if not enough transactions and blocks\n" - "have been observed to make an estimate.\n" - "\nExample:\n" - + HelpExampleCli("estimatefee", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].get_int(); - if (nBlocks < 1) - nBlocks = 1; - - CFeeRate feeRate = mempool.estimateFee(nBlocks); - if (feeRate == CFeeRate(0)) - return -1.0; - - return ValueFromAmount(feeRate.GetFeePerK()); -} - -UniValue estimatepriority(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatepriority nblocks\n" - "\nEstimates the approximate priority a zero-fee transaction needs to begin\n" - "confirmation within nblocks blocks.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "n (numeric) estimated priority\n" - "\n" - "A negative value is returned if not enough transactions and blocks\n" - "have been observed to make an estimate.\n" - "\nExample:\n" - + HelpExampleCli("estimatepriority", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].get_int(); - if (nBlocks < 1) - nBlocks = 1; - - return mempool.estimatePriority(nBlocks); -} - -UniValue estimatesmartfee(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatesmartfee nblocks\n" - "\nWARNING: This interface is unstable and may disappear or change!\n" - "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within nblocks blocks if possible and return the number of blocks\n" - "for which the estimate is valid.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "{\n" - " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" - " \"blocks\" : n (numeric) block number where estimate was found\n" - "}\n" - "\n" - "A negative value is returned if not enough transactions and blocks\n" - "have been observed to make an estimate for any number of blocks.\n" - "However it will not return a value below the mempool reject fee.\n" - "\nExample:\n" - + HelpExampleCli("estimatesmartfee", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].get_int(); - - UniValue result(UniValue::VOBJ); - int answerFound; - CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound); - result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); - result.push_back(Pair("blocks", answerFound)); - return result; -} - -UniValue estimatesmartpriority(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatesmartpriority nblocks\n" - "\nWARNING: This interface is unstable and may disappear or change!\n" - "\nEstimates the approximate priority a zero-fee transaction needs to begin\n" - "confirmation within nblocks blocks if possible and return the number of blocks\n" - "for which the estimate is valid.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "{\n" - " \"priority\" : x.x, (numeric) estimated priority\n" - " \"blocks\" : n (numeric) block number where estimate was found\n" - "}\n" - "\n" - "A negative value is returned if not enough transactions and blocks\n" - "have been observed to make an estimate for any number of blocks.\n" - "However if the mempool reject fee is set it will return 1e9 * MAX_MONEY.\n" - "\nExample:\n" - + HelpExampleCli("estimatesmartpriority", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].get_int(); - - UniValue result(UniValue::VOBJ); - int answerFound; - double priority = mempool.estimateSmartPriority(nBlocks, &answerFound); - result.push_back(Pair("priority", priority)); - result.push_back(Pair("blocks", answerFound)); - return result; -} diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp deleted file mode 100644 index 9871c3fcc..000000000 --- a/src/rpcmisc.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "base58.h" -#include "clientversion.h" -#include "init.h" -#include "main.h" -#include "net.h" -#include "netbase.h" -#include "rpcserver.h" -#include "timedata.h" -#include "util.h" -#include "utilstrencodings.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#include "wallet/walletdb.h" -#endif - -#include - -#include - -#include - -using namespace std; - -/** - * @note Do not add or change anything in the information returned by this - * method. `getinfo` exists for backwards-compatibility only. It combines - * information from wildly different sources in the program, which is a mess, - * and is thus planned to be deprecated eventually. - * - * Based on the source of the information, new information should be added to: - * - `getblockchaininfo`, - * - `getnetworkinfo` or - * - `getwalletinfo` - * - * Or alternatively, create a specific query method for the information. - **/ -UniValue getinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info.\n" - "\nResult:\n" - "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": \"...\" (string) any error messages\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getinfo", "") - + HelpExampleRpc("getinfo", "") - ); - -#ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); -#else - LOCK(cs_main); -#endif - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); -#ifdef ENABLE_WALLET - if (pwalletMain) { - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - } -#endif - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); -#ifdef ENABLE_WALLET - if (pwalletMain) { - obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); - } - if (pwalletMain && pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); -#endif - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - -#ifdef ENABLE_WALLET -class DescribeAddressVisitor : public boost::static_visitor -{ -public: - UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } - - UniValue operator()(const CKeyID &keyID) const { - UniValue obj(UniValue::VOBJ); - CPubKey vchPubKey; - obj.push_back(Pair("isscript", false)); - if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) { - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); - } - return obj; - } - - UniValue operator()(const CScriptID &scriptID) const { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.push_back(Pair("isscript", true)); - if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { - std::vector addresses; - txnouttype whichType; - int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.push_back(Pair("script", GetTxnOutputType(whichType))); - obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); - UniValue a(UniValue::VARR); - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - obj.push_back(Pair("addresses", a)); - if (whichType == TX_MULTISIG) - obj.push_back(Pair("sigsrequired", nRequired)); - } - return obj; - } -}; -#endif - -UniValue validateaddress(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \"bitcoinaddress\"\n" - "\nReturn information about the given bitcoin address.\n" - "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n" - "\nResult:\n" - "{\n" - " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" - " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n" - " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" - " \"ismine\" : true|false, (boolean) If the address is yours or not\n" - " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" - " \"isscript\" : true|false, (boolean) If the key is a script\n" - " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" - " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - ); - -#ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); -#else - LOCK(cs_main); -#endif - - CBitcoinAddress address(params[0].get_str()); - bool isValid = address.IsValid(); - - UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - -#ifdef ENABLE_WALLET - isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; - ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); - ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); - ret.pushKVs(detail); - if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); -#endif - } - return ret; -} - -/** - * Used by addmultisigaddress / createmultisig: - */ -CScript _createmultisig_redeemScript(const UniValue& params) -{ - int nRequired = params[0].get_int(); - const UniValue& keys = params[1].get_array(); - - // 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 %u keys, but need at least %d to redeem)", keys.size(), nRequired)); - if (keys.size() > 16) - throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); - std::vector pubkeys; - pubkeys.resize(keys.size()); - for (unsigned int i = 0; i < keys.size(); i++) - { - const std::string& ks = keys[i].get_str(); -#ifdef ENABLE_WALLET - // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (pwalletMain && address.IsValid()) - { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw runtime_error( - strprintf("%s does not refer to a key",ks)); - CPubKey vchPubKey; - if (!pwalletMain->GetPubKey(keyID, vchPubKey)) - throw runtime_error( - strprintf("no full public key for address %s",ks)); - if (!vchPubKey.IsFullyValid()) - throw runtime_error(" Invalid public key: "+ks); - pubkeys[i] = vchPubKey; - } - - // Case 2: hex public key - else -#endif - if (IsHex(ks)) - { - CPubKey vchPubKey(ParseHex(ks)); - if (!vchPubKey.IsFullyValid()) - throw runtime_error(" Invalid public key: "+ks); - pubkeys[i] = vchPubKey; - } - else - { - throw runtime_error(" Invalid public key: "+ks); - } - } - CScript result = GetScriptForMultisig(nRequired, pubkeys); - - if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) - throw runtime_error( - strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)); - - return result; -} - -UniValue createmultisig(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 2) - { - string msg = "createmultisig nrequired [\"key\",...]\n" - "\nCreates a multi-signature address with n signature of m keys required.\n" - "It returns a json object with the address and redeemScript.\n" - - "\nArguments:\n" - "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n" - " [\n" - " \"key\" (string) bitcoin address or hex-encoded public key\n" - " ,...\n" - " ]\n" - - "\nResult:\n" - "{\n" - " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" - " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" - "}\n" - - "\nExamples:\n" - "\nCreate a multisig address from 2 addresses\n" - + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") - ; - throw runtime_error(msg); - } - - // Construct using pay-to-script-hash: - CScript inner = _createmultisig_redeemScript(params); - CScriptID innerID(inner); - CBitcoinAddress address(innerID); - - UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", address.ToString())); - result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); - - return result; -} - -UniValue verifymessage(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 3) - throw runtime_error( - "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" - "\nVerify a signed message\n" - "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the signature.\n" - "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" - "3. \"message\" (string, required) The message that was signed.\n" - "\nResult:\n" - "true|false (boolean) If the signature is verified or not.\n" - "\nExamples:\n" - "\nUnlock the wallet for 30 seconds\n" - + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + - "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + - "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + - "\nAs json rpc\n" - + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") - ); - - LOCK(cs_main); - - 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(RPC_TYPE_ERROR, "Invalid address"); - - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); - - bool fInvalid = false; - vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); - - if (fInvalid) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; - - CPubKey pubkey; - if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) - return false; - - return (pubkey.GetID() == keyID); -} - -UniValue setmocktime(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "setmocktime timestamp\n" - "\nSet the local time to given timestamp (-regtest only)\n" - "\nArguments:\n" - "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n" - " Pass 0 to go back to using the system time." - ); - - if (!Params().MineBlocksOnDemand()) - throw runtime_error("setmocktime for regression testing (-regtest mode) only"); - - // cs_vNodes is locked and node send/receive times are updated - // atomically with the time change to prevent peers from being - // disconnected because we think we haven't communicated with them - // in a long time. - LOCK2(cs_main, cs_vNodes); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - SetMockTime(params[0].get_int64()); - - uint64_t t = GetTime(); - BOOST_FOREACH(CNode* pnode, vNodes) { - pnode->nLastSend = pnode->nLastRecv = t; - } - - return NullUniValue; -} diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp deleted file mode 100644 index b61e7c5f1..000000000 --- a/src/rpcnet.cpp +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "rpcserver.h" - -#include "chainparams.h" -#include "clientversion.h" -#include "main.h" -#include "net.h" -#include "netbase.h" -#include "protocol.h" -#include "sync.h" -#include "timedata.h" -#include "ui_interface.h" -#include "util.h" -#include "utilstrencodings.h" -#include "version.h" - -#include - -#include - -using namespace std; - -UniValue getconnectioncount(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "\nReturns the number of connections to other nodes.\n" - "\nResult:\n" - "n (numeric) The connection count\n" - "\nExamples:\n" - + HelpExampleCli("getconnectioncount", "") - + HelpExampleRpc("getconnectioncount", "") - ); - - LOCK2(cs_main, cs_vNodes); - - return (int)vNodes.size(); -} - -UniValue ping(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "ping\n" - "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" - "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" - "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n" - "\nExamples:\n" - + HelpExampleCli("ping", "") - + HelpExampleRpc("ping", "") - ); - - // Request that each node send a ping during next message processing pass - LOCK2(cs_main, cs_vNodes); - - BOOST_FOREACH(CNode* pNode, vNodes) { - pNode->fPingQueued = true; - } - - return NullUniValue; -} - -static void CopyNodeStats(std::vector& vstats) -{ - vstats.clear(); - - LOCK(cs_vNodes); - vstats.reserve(vNodes.size()); - BOOST_FOREACH(CNode* pnode, vNodes) { - CNodeStats stats; - pnode->copyStats(stats); - vstats.push_back(stats); - } -} - -UniValue getpeerinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getpeerinfo\n" - "\nReturns data about each connected network node as a json array of objects.\n" - "\nResult:\n" - "[\n" - " {\n" - " \"id\": n, (numeric) Peer index\n" - " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" - " \"addrlocal\":\"ip:port\", (string) local address\n" - " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" - " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" - " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" - " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" - " \"bytessent\": n, (numeric) The total bytes sent\n" - " \"bytesrecv\": n, (numeric) The total bytes received\n" - " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" - " \"pingtime\": n, (numeric) ping time\n" - " \"minping\": n, (numeric) minimum observed ping time\n" - " \"pingwait\": n, (numeric) ping wait\n" - " \"version\": v, (numeric) The peer version, such as 7001\n" - " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" - " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" - " \"banscore\": n, (numeric) The ban score\n" - " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" - " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" - " \"inflight\": [\n" - " n, (numeric) The heights of blocks we're currently asking from this peer\n" - " ...\n" - " ]\n" - " \"bytessent_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" - " ...\n" - " }\n" - " \"bytesrecv_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" - " ...\n" - " }\n" - " }\n" - " ,...\n" - "]\n" - "\nExamples:\n" - + HelpExampleCli("getpeerinfo", "") - + HelpExampleRpc("getpeerinfo", "") - ); - - LOCK(cs_main); - - vector vstats; - CopyNodeStats(vstats); - - UniValue ret(UniValue::VARR); - - BOOST_FOREACH(const CNodeStats& stats, vstats) { - UniValue obj(UniValue::VOBJ); - CNodeStateStats statestats; - bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); - obj.push_back(Pair("id", stats.nodeid)); - obj.push_back(Pair("addr", stats.addrName)); - if (!(stats.addrLocal.empty())) - obj.push_back(Pair("addrlocal", stats.addrLocal)); - obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); - obj.push_back(Pair("relaytxes", stats.fRelayTxes)); - obj.push_back(Pair("lastsend", stats.nLastSend)); - obj.push_back(Pair("lastrecv", stats.nLastRecv)); - obj.push_back(Pair("bytessent", stats.nSendBytes)); - obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); - obj.push_back(Pair("conntime", stats.nTimeConnected)); - obj.push_back(Pair("timeoffset", stats.nTimeOffset)); - obj.push_back(Pair("pingtime", stats.dPingTime)); - obj.push_back(Pair("minping", stats.dPingMin)); - if (stats.dPingWait > 0.0) - obj.push_back(Pair("pingwait", stats.dPingWait)); - obj.push_back(Pair("version", stats.nVersion)); - // Use the sanitized form of subver here, to avoid tricksy remote peers from - // corrupting or modifiying the JSON output by putting special characters in - // their ver message. - obj.push_back(Pair("subver", stats.cleanSubVer)); - obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("startingheight", stats.nStartingHeight)); - if (fStateStats) { - obj.push_back(Pair("banscore", statestats.nMisbehavior)); - obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); - obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); - UniValue heights(UniValue::VARR); - BOOST_FOREACH(int height, statestats.vHeightInFlight) { - heights.push_back(height); - } - obj.push_back(Pair("inflight", heights)); - } - obj.push_back(Pair("whitelisted", stats.fWhitelisted)); - - UniValue sendPerMsgCmd(UniValue::VOBJ); - BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapSendBytesPerMsgCmd) { - if (i.second > 0) - sendPerMsgCmd.push_back(Pair(i.first, i.second)); - } - obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd)); - - UniValue recvPerMsgCmd(UniValue::VOBJ); - BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapRecvBytesPerMsgCmd) { - if (i.second > 0) - recvPerMsgCmd.push_back(Pair(i.first, i.second)); - } - obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd)); - - ret.push_back(obj); - } - - return ret; -} - -UniValue addnode(const UniValue& params, bool fHelp) -{ - string strCommand; - if (params.size() == 2) - strCommand = params[1].get_str(); - if (fHelp || params.size() != 2 || - (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) - throw runtime_error( - "addnode \"node\" \"add|remove|onetry\"\n" - "\nAttempts add or remove a node from the addnode list.\n" - "Or try a connection to a node once.\n" - "\nArguments:\n" - "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" - "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" - "\nExamples:\n" - + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") - + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"") - ); - - string strNode = params[0].get_str(); - - if (strCommand == "onetry") - { - CAddress addr; - OpenNetworkConnection(addr, NULL, strNode.c_str()); - return NullUniValue; - } - - LOCK(cs_vAddedNodes); - vector::iterator it = vAddedNodes.begin(); - for(; it != vAddedNodes.end(); it++) - if (strNode == *it) - break; - - if (strCommand == "add") - { - if (it != vAddedNodes.end()) - throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); - vAddedNodes.push_back(strNode); - } - else if(strCommand == "remove") - { - if (it == vAddedNodes.end()) - throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); - vAddedNodes.erase(it); - } - - return NullUniValue; -} - -UniValue disconnectnode(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "disconnectnode \"node\" \n" - "\nImmediately disconnects from the specified node.\n" - "\nArguments:\n" - "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" - "\nExamples:\n" - + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") - + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") - ); - - CNode* pNode = FindNode(params[0].get_str()); - if (pNode == NULL) - throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); - - pNode->fDisconnect = true; - - return NullUniValue; -} - -UniValue getaddednodeinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getaddednodeinfo dns ( \"node\" )\n" - "\nReturns information about the given added node, or all added nodes\n" - "(note that onetry addnodes are not listed here)\n" - "If dns is false, only a list of added nodes will be provided,\n" - "otherwise connected information will also be available.\n" - "\nArguments:\n" - "1. dns (boolean, required) If false, only a list of added nodes will be provided, otherwise connected information will also be available.\n" - "2. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n" - "\nResult:\n" - "[\n" - " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node ip address\n" - " \"connected\" : true|false, (boolean) If connected\n" - " \"addresses\" : [\n" - " {\n" - " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server host and port\n" - " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" - " }\n" - " ,...\n" - " ]\n" - " }\n" - " ,...\n" - "]\n" - "\nExamples:\n" - + HelpExampleCli("getaddednodeinfo", "true") - + HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"") - + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"") - ); - - bool fDns = params[0].get_bool(); - - list laddedNodes(0); - if (params.size() == 1) - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) - laddedNodes.push_back(strAddNode); - } - else - { - string strNode = params[1].get_str(); - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) { - if (strAddNode == strNode) - { - laddedNodes.push_back(strAddNode); - break; - } - } - if (laddedNodes.size() == 0) - throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); - } - - UniValue ret(UniValue::VARR); - if (!fDns) - { - BOOST_FOREACH (const std::string& strAddNode, laddedNodes) { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); - ret.push_back(obj); - } - return ret; - } - - list > > laddedAddreses(0); - BOOST_FOREACH(const std::string& strAddNode, laddedNodes) { - vector vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) - laddedAddreses.push_back(make_pair(strAddNode, vservNode)); - else - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); - obj.push_back(Pair("connected", false)); - UniValue addresses(UniValue::VARR); - obj.push_back(Pair("addresses", addresses)); - } - } - - LOCK(cs_vNodes); - for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", it->first)); - - UniValue addresses(UniValue::VARR); - bool fConnected = false; - BOOST_FOREACH(const CService& addrNode, it->second) { - bool fFound = false; - UniValue node(UniValue::VOBJ); - node.push_back(Pair("address", addrNode.ToString())); - BOOST_FOREACH(CNode* pnode, vNodes) { - if (pnode->addr == addrNode) - { - fFound = true; - fConnected = true; - node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); - break; - } - } - if (!fFound) - node.push_back(Pair("connected", "false")); - addresses.push_back(node); - } - obj.push_back(Pair("connected", fConnected)); - obj.push_back(Pair("addresses", addresses)); - ret.push_back(obj); - } - - return ret; -} - -UniValue getnettotals(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 0) - throw runtime_error( - "getnettotals\n" - "\nReturns information about network traffic, including bytes in, bytes out,\n" - "and current time.\n" - "\nResult:\n" - "{\n" - " \"totalbytesrecv\": n, (numeric) Total bytes received\n" - " \"totalbytessent\": n, (numeric) Total bytes sent\n" - " \"timemillis\": t, (numeric) Total cpu time\n" - " \"uploadtarget\":\n" - " {\n" - " \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n" - " \"target\": n, (numeric) Target in bytes\n" - " \"target_reached\": true|false, (boolean) True if target is reached\n" - " \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n" - " \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n" - " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" - " }\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getnettotals", "") - + HelpExampleRpc("getnettotals", "") - ); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); - obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); - obj.push_back(Pair("timemillis", GetTimeMillis())); - - UniValue outboundLimit(UniValue::VOBJ); - outboundLimit.push_back(Pair("timeframe", CNode::GetMaxOutboundTimeframe())); - outboundLimit.push_back(Pair("target", CNode::GetMaxOutboundTarget())); - outboundLimit.push_back(Pair("target_reached", CNode::OutboundTargetReached(false))); - outboundLimit.push_back(Pair("serve_historical_blocks", !CNode::OutboundTargetReached(true))); - outboundLimit.push_back(Pair("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft())); - outboundLimit.push_back(Pair("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle())); - obj.push_back(Pair("uploadtarget", outboundLimit)); - return obj; -} - -static UniValue GetNetworksInfo() -{ - UniValue networks(UniValue::VARR); - for(int n=0; n(n); - if(network == NET_UNROUTABLE) - continue; - proxyType proxy; - UniValue obj(UniValue::VOBJ); - GetProxy(network, proxy); - obj.push_back(Pair("name", GetNetworkName(network))); - obj.push_back(Pair("limited", IsLimited(network))); - obj.push_back(Pair("reachable", IsReachable(network))); - obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); - obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); - networks.push_back(obj); - } - return networks; -} - -UniValue getnetworkinfo(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getnetworkinfo\n" - "Returns an object containing various state info regarding P2P networking.\n" - "\nResult:\n" - "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"networks\": [ (array) information per network\n" - " {\n" - " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" - " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" - " \"reachable\": true|false, (boolean) is the network reachable?\n" - " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" - " }\n" - " ,...\n" - " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" - " \"localaddresses\": [ (array) list of local addresses\n" - " {\n" - " \"address\": \"xxxx\", (string) network address\n" - " \"port\": xxx, (numeric) network port\n" - " \"score\": xxx (numeric) relative score\n" - " }\n" - " ,...\n" - " ]\n" - " \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getnetworkinfo", "") - + HelpExampleRpc("getnetworkinfo", "") - ); - - LOCK(cs_main); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", strSubVersion)); - obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); - obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("networks", GetNetworksInfo())); - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - UniValue localAddresses(UniValue::VARR); - { - LOCK(cs_mapLocalHost); - BOOST_FOREACH(const PAIRTYPE(CNetAddr, LocalServiceInfo) &item, mapLocalHost) - { - UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", item.first.ToString())); - rec.push_back(Pair("port", item.second.nPort)); - rec.push_back(Pair("score", item.second.nScore)); - localAddresses.push_back(rec); - } - } - obj.push_back(Pair("localaddresses", localAddresses)); - obj.push_back(Pair("warnings", GetWarnings("statusbar"))); - return obj; -} - -UniValue setban(const UniValue& params, bool fHelp) -{ - string strCommand; - if (params.size() >= 2) - strCommand = params[1].get_str(); - if (fHelp || params.size() < 2 || - (strCommand != "add" && strCommand != "remove")) - throw runtime_error( - "setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n" - "\nAttempts add or remove a IP/Subnet from the banned list.\n" - "\nArguments:\n" - "1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n" - "2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n" - "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" - "4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" - "\nExamples:\n" - + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") - + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") - + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400") - ); - - CSubNet subNet; - CNetAddr netAddr; - bool isSubnet = false; - - if (params[0].get_str().find("/") != string::npos) - isSubnet = true; - - if (!isSubnet) - netAddr = CNetAddr(params[0].get_str()); - else - subNet = CSubNet(params[0].get_str()); - - if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) - throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); - - if (strCommand == "add") - { - if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr)) - throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); - - int64_t banTime = 0; //use standard bantime if not specified - if (params.size() >= 3 && !params[2].isNull()) - banTime = params[2].get_int64(); - - bool absolute = false; - if (params.size() == 4 && params[3].isTrue()) - absolute = true; - - isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); - - //disconnect possible nodes - while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) - bannedNode->fDisconnect = true; - } - else if(strCommand == "remove") - { - if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) - throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); - } - - DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); - - return NullUniValue; -} - -UniValue listbanned(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "listbanned\n" - "\nList all banned IPs/Subnets.\n" - "\nExamples:\n" - + HelpExampleCli("listbanned", "") - + HelpExampleRpc("listbanned", "") - ); - - banmap_t banMap; - CNode::GetBanned(banMap); - - UniValue bannedAddresses(UniValue::VARR); - for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) - { - CBanEntry banEntry = (*it).second; - UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", (*it).first.ToString())); - rec.push_back(Pair("banned_until", banEntry.nBanUntil)); - rec.push_back(Pair("ban_created", banEntry.nCreateTime)); - rec.push_back(Pair("ban_reason", banEntry.banReasonToString())); - - bannedAddresses.push_back(rec); - } - - return bannedAddresses; -} - -UniValue clearbanned(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "clearbanned\n" - "\nClear all banned IPs.\n" - "\nExamples:\n" - + HelpExampleCli("clearbanned", "") - + HelpExampleRpc("clearbanned", "") - ); - - CNode::ClearBanned(); - DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); - - return NullUniValue; -} diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp deleted file mode 100644 index b7605545d..000000000 --- a/src/rpcprotocol.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "rpcprotocol.h" - -#include "random.h" -#include "tinyformat.h" -#include "util.h" -#include "utilstrencodings.h" -#include "utiltime.h" -#include "version.h" - -#include -#include - -using namespace std; - -/** - * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, - * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were - * unspecified (HTTP errors and contents of 'error'). - * - * 1.0 spec: http://json-rpc.org/wiki/specification - * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html - */ - -string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) -{ - UniValue request(UniValue::VOBJ); - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return request.write() + "\n"; -} - -UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) -{ - UniValue reply(UniValue::VOBJ); - if (!error.isNull()) - reply.push_back(Pair("result", NullUniValue)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return reply; -} - -string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id) -{ - UniValue reply = JSONRPCReplyObj(result, error, id); - return reply.write() + "\n"; -} - -UniValue JSONRPCError(int code, const string& message) -{ - UniValue error(UniValue::VOBJ); - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - -/** Username used when cookie authentication is in use (arbitrary, only for - * recognizability in debugging/logging purposes) - */ -static const std::string COOKIEAUTH_USER = "__cookie__"; -/** Default name for auth cookie file */ -static const std::string COOKIEAUTH_FILE = ".cookie"; - -boost::filesystem::path GetAuthCookieFile() -{ - boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); - if (!path.is_complete()) path = GetDataDir() / path; - return path; -} - -bool GenerateAuthCookie(std::string *cookie_out) -{ - unsigned char rand_pwd[32]; - GetRandBytes(rand_pwd, 32); - std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32); - - /** the umask determines what permissions are used to create this file - - * these are set to 077 in init.cpp unless overridden with -sysperms. - */ - std::ofstream file; - boost::filesystem::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); - if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); - return false; - } - file << cookie; - file.close(); - LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); - - if (cookie_out) - *cookie_out = cookie; - return true; -} - -bool GetAuthCookie(std::string *cookie_out) -{ - std::ifstream file; - std::string cookie; - boost::filesystem::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); - if (!file.is_open()) - return false; - std::getline(file, cookie); - file.close(); - - if (cookie_out) - *cookie_out = cookie; - return true; -} - -void DeleteAuthCookie() -{ - try { - boost::filesystem::remove(GetAuthCookieFile()); - } catch (const boost::filesystem::filesystem_error& e) { - LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what()); - } -} - diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h deleted file mode 100644 index 55d0aac68..000000000 --- a/src/rpcprotocol.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPCPROTOCOL_H -#define BITCOIN_RPCPROTOCOL_H - -#include -#include -#include -#include -#include - -#include - -//! HTTP status codes -enum HTTPStatusCode -{ - HTTP_OK = 200, - HTTP_BAD_REQUEST = 400, - HTTP_UNAUTHORIZED = 401, - HTTP_FORBIDDEN = 403, - HTTP_NOT_FOUND = 404, - HTTP_BAD_METHOD = 405, - HTTP_INTERNAL_SERVER_ERROR = 500, - HTTP_SERVICE_UNAVAILABLE = 503, -}; - -//! Bitcoin RPC error codes -enum RPCErrorCode -{ - //! Standard JSON-RPC 2.0 errors - RPC_INVALID_REQUEST = -32600, - RPC_METHOD_NOT_FOUND = -32601, - RPC_INVALID_PARAMS = -32602, - RPC_INTERNAL_ERROR = -32603, - RPC_PARSE_ERROR = -32700, - - //! General application defined errors - RPC_MISC_ERROR = -1, //! std::exception thrown in command handling - RPC_FORBIDDEN_BY_SAFE_MODE = -2, //! Server is in safe mode, and command is not allowed in safe mode - RPC_TYPE_ERROR = -3, //! Unexpected type was passed as parameter - RPC_INVALID_ADDRESS_OR_KEY = -5, //! Invalid address or key - RPC_OUT_OF_MEMORY = -7, //! Ran out of memory during operation - RPC_INVALID_PARAMETER = -8, //! Invalid, missing or duplicate parameter - RPC_DATABASE_ERROR = -20, //! Database error - RPC_DESERIALIZATION_ERROR = -22, //! Error parsing or validating structure in raw format - RPC_VERIFY_ERROR = -25, //! General error during transaction or block submission - RPC_VERIFY_REJECTED = -26, //! Transaction or block was rejected by network rules - RPC_VERIFY_ALREADY_IN_CHAIN = -27, //! Transaction already in chain - RPC_IN_WARMUP = -28, //! Client still warming up - - //! Aliases for backward compatibility - RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, - RPC_TRANSACTION_REJECTED = RPC_VERIFY_REJECTED, - RPC_TRANSACTION_ALREADY_IN_CHAIN= RPC_VERIFY_ALREADY_IN_CHAIN, - - //! P2P client errors - RPC_CLIENT_NOT_CONNECTED = -9, //! Bitcoin is not connected - RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks - RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added - RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before - RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes - RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet - - //! Wallet errors - RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.) - RPC_WALLET_INSUFFICIENT_FUNDS = -6, //! Not enough funds in wallet or account - RPC_WALLET_INVALID_ACCOUNT_NAME = -11, //! Invalid account name - RPC_WALLET_KEYPOOL_RAN_OUT = -12, //! Keypool ran out, call keypoolrefill first - RPC_WALLET_UNLOCK_NEEDED = -13, //! Enter the wallet passphrase with walletpassphrase first - RPC_WALLET_PASSPHRASE_INCORRECT = -14, //! The wallet passphrase entered was incorrect - RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) - RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet - RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked -}; - -std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); -UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); -std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); -UniValue JSONRPCError(int code, const std::string& message); - -/** Get name of RPC authentication cookie file */ -boost::filesystem::path GetAuthCookieFile(); -/** Generate a new RPC authentication cookie and write it to disk */ -bool GenerateAuthCookie(std::string *cookie_out); -/** Read the RPC authentication cookie from disk */ -bool GetAuthCookie(std::string *cookie_out); -/** Delete RPC authentication cookie from disk */ -void DeleteAuthCookie(); - -#endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp deleted file mode 100644 index 64bf569ba..000000000 --- a/src/rpcrawtransaction.cpp +++ /dev/null @@ -1,841 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "base58.h" -#include "chain.h" -#include "coins.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "init.h" -#include "keystore.h" -#include "main.h" -#include "merkleblock.h" -#include "net.h" -#include "policy/policy.h" -#include "primitives/transaction.h" -#include "rpcserver.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/standard.h" -#include "txmempool.h" -#include "uint256.h" -#include "utilstrencodings.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#endif - -#include - -#include - -#include - -using namespace std; - -void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) -{ - txnouttype type; - vector addresses; - int nRequired; - - out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); - if (fIncludeHex) - out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - - if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { - out.push_back(Pair("type", GetTxnOutputType(type))); - return; - } - - out.push_back(Pair("reqSigs", nRequired)); - out.push_back(Pair("type", GetTxnOutputType(type))); - - UniValue a(UniValue::VARR); - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - out.push_back(Pair("addresses", a)); -} - -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) -{ - entry.push_back(Pair("txid", tx.GetHash().GetHex())); - entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); - entry.push_back(Pair("version", tx.nVersion)); - entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); - UniValue vin(UniValue::VARR); - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - UniValue in(UniValue::VOBJ); - if (tx.IsCoinBase()) - in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - else { - in.push_back(Pair("txid", txin.prevout.hash.GetHex())); - in.push_back(Pair("vout", (int64_t)txin.prevout.n)); - UniValue o(UniValue::VOBJ); - o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); - o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - in.push_back(Pair("scriptSig", o)); - } - in.push_back(Pair("sequence", (int64_t)txin.nSequence)); - vin.push_back(in); - } - entry.push_back(Pair("vin", vin)); - UniValue vout(UniValue::VARR); - for (unsigned int i = 0; i < tx.vout.size(); i++) { - const CTxOut& txout = tx.vout[i]; - UniValue out(UniValue::VOBJ); - out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - out.push_back(Pair("n", (int64_t)i)); - UniValue o(UniValue::VOBJ); - ScriptPubKeyToJSON(txout.scriptPubKey, o, true); - out.push_back(Pair("scriptPubKey", o)); - vout.push_back(out); - } - entry.push_back(Pair("vout", vout)); - - if (!hashBlock.IsNull()) { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); - entry.push_back(Pair("time", pindex->GetBlockTime())); - entry.push_back(Pair("blocktime", pindex->GetBlockTime())); - } - else - entry.push_back(Pair("confirmations", 0)); - } - } -} - -UniValue getrawtransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getrawtransaction \"txid\" ( verbose )\n" - "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" - "or there is an unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option.\n" - "\nReturn the raw transaction data.\n" - "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" - "If verbose is non-zero, returns an Object with information about 'txid'.\n" - - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n" - - "\nResult (if verbose is not set or set to 0):\n" - "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" - - "\nResult (if verbose > 0):\n" - "{\n" - " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" - " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" - " \"size\" : n, (numeric) The transaction size\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) \n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" - " },\n" - " \"sequence\": n (numeric) The script sequence number\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"bitcoinaddress\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - " \"blockhash\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The confirmations\n" - " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("getrawtransaction", "\"mytxid\"") - + HelpExampleCli("getrawtransaction", "\"mytxid\" 1") - + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") - ); - - LOCK(cs_main); - - uint256 hash = ParseHashV(params[0], "parameter 1"); - - bool fVerbose = false; - if (params.size() > 1) - fVerbose = (params[1].get_int() != 0); - - CTransaction tx; - uint256 hashBlock; - if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); - - string strHex = EncodeHexTx(tx); - - if (!fVerbose) - return strHex; - - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", strHex)); - TxToJSON(tx, hashBlock, result); - return result; -} - -UniValue gettxoutproof(const UniValue& params, bool fHelp) -{ - if (fHelp || (params.size() != 1 && params.size() != 2)) - throw runtime_error( - "gettxoutproof [\"txid\",...] ( blockhash )\n" - "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" - "\nNOTE: By default this function only works sometimes. This is when there is an\n" - "unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option or\n" - "specify the block in which the transaction is included in manually (by blockhash).\n" - "\nReturn the raw transaction data.\n" - "\nArguments:\n" - "1. \"txids\" (string) A json array of txids to filter\n" - " [\n" - " \"txid\" (string) A transaction hash\n" - " ,...\n" - " ]\n" - "2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n" - "\nResult:\n" - "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" - ); - - set setTxids; - uint256 oneTxid; - UniValue txids = params[0].get_array(); - for (unsigned int idx = 0; idx < txids.size(); idx++) { - const UniValue& txid = txids[idx]; - if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); - uint256 hash(uint256S(txid.get_str())); - if (setTxids.count(hash)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); - setTxids.insert(hash); - oneTxid = hash; - } - - LOCK(cs_main); - - CBlockIndex* pblockindex = NULL; - - uint256 hashBlock; - if (params.size() > 1) - { - hashBlock = uint256S(params[1].get_str()); - if (!mapBlockIndex.count(hashBlock)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - pblockindex = mapBlockIndex[hashBlock]; - } else { - CCoins coins; - if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) - pblockindex = chainActive[coins.nHeight]; - } - - if (pblockindex == NULL) - { - CTransaction tx; - if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); - if (!mapBlockIndex.count(hashBlock)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); - pblockindex = mapBlockIndex[hashBlock]; - } - - CBlock block; - if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - unsigned int ntxFound = 0; - BOOST_FOREACH(const CTransaction&tx, block.vtx) - if (setTxids.count(tx.GetHash())) - ntxFound++; - if (ntxFound != setTxids.size()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); - - CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); - CMerkleBlock mb(block, setTxids); - ssMB << mb; - std::string strHex = HexStr(ssMB.begin(), ssMB.end()); - return strHex; -} - -UniValue verifytxoutproof(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "verifytxoutproof \"proof\"\n" - "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" - "and throwing an RPC error if the block is not in our best chain\n" - "\nArguments:\n" - "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" - "\nResult:\n" - "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" - ); - - CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); - CMerkleBlock merkleBlock; - ssMB >> merkleBlock; - - UniValue res(UniValue::VARR); - - vector vMatch; - if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) - return res; - - LOCK(cs_main); - - if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); - - BOOST_FOREACH(const uint256& hash, vMatch) - res.push_back(hash.GetHex()); - return res; -} - -UniValue createrawtransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 3) - throw runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" - "\nCreate a transaction spending the given inputs and creating new outputs.\n" - "Outputs can be addresses or data.\n" - "Returns hex-encoded raw transaction.\n" - "Note that the transaction's inputs are not signed, and\n" - "it is not stored in the wallet or transmitted to the network.\n" - - "\nArguments:\n" - "1. \"transactions\" (string, required) A json array of json objects\n" - " [\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n (numeric, required) The output number\n" - " }\n" - " ,...\n" - " ]\n" - "2. \"outputs\" (string, required) a json object with outputs\n" - " {\n" - " \"address\": x.xxx (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" - " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n" - " ...\n" - " }\n" - "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "\nResult:\n" - "\"transaction\" (string) hex string of the transaction\n" - - "\nExamples\n" - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") - + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") - + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); - if (params[0].isNull() || params[1].isNull()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); - - UniValue inputs = params[0].get_array(); - UniValue sendTo = params[1].get_obj(); - - CMutableTransaction rawTx; - - if (params.size() > 2 && !params[2].isNull()) { - int64_t nLockTime = params[2].get_int64(); - if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); - rawTx.nLockTime = nLockTime; - } - - for (unsigned int idx = 0; idx < inputs.size(); idx++) { - const UniValue& input = inputs[idx]; - const UniValue& o = input.get_obj(); - - uint256 txid = ParseHashO(o, "txid"); - - const UniValue& vout_v = find_value(o, "vout"); - if (!vout_v.isNum()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); - int nOutput = vout_v.get_int(); - if (nOutput < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - - uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); - CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); - - rawTx.vin.push_back(in); - } - - set setAddress; - vector addrList = sendTo.getKeys(); - BOOST_FOREACH(const string& name_, addrList) { - - if (name_ == "data") { - std::vector data = ParseHexV(sendTo[name_].getValStr(),"Data"); - - CTxOut out(0, CScript() << OP_RETURN << data); - rawTx.vout.push_back(out); - } else { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); - - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); - - CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(sendTo[name_]); - - CTxOut out(nAmount, scriptPubKey); - rawTx.vout.push_back(out); - } - } - - return EncodeHexTx(rawTx); -} - -UniValue decoderawtransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "decoderawtransaction \"hexstring\"\n" - "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" - - "\nArguments:\n" - "1. \"hex\" (string, required) The transaction hex string\n" - - "\nResult:\n" - "{\n" - " \"txid\" : \"id\", (string) The transaction id\n" - " \"size\" : n, (numeric) The transaction size\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) The output number\n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" - " },\n" - " \"sequence\": n (numeric) The script sequence number\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("decoderawtransaction", "\"hexstring\"") - + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") - ); - - LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); - - CTransaction tx; - - if (!DecodeHexTx(tx, params[0].get_str())) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - - UniValue result(UniValue::VOBJ); - TxToJSON(tx, uint256(), result); - - return result; -} - -UniValue decodescript(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "decodescript \"hex\"\n" - "\nDecode a hex-encoded script.\n" - "\nArguments:\n" - "1. \"hex\" (string) the hex encoded script\n" - "\nResult:\n" - "{\n" - " \"asm\":\"asm\", (string) Script public key\n" - " \"hex\":\"hex\", (string) hex encoded public key\n" - " \"type\":\"type\", (string) The output type\n" - " \"reqSigs\": n, (numeric) The required signatures\n" - " \"addresses\": [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ],\n" - " \"p2sh\",\"address\" (string) script address\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("decodescript", "\"hexstring\"") - + HelpExampleRpc("decodescript", "\"hexstring\"") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); - - UniValue r(UniValue::VOBJ); - CScript script; - if (params[0].get_str().size() > 0){ - vector scriptData(ParseHexV(params[0], "argument")); - script = CScript(scriptData.begin(), scriptData.end()); - } else { - // Empty scripts are valid - } - ScriptPubKeyToJSON(script, r, false); - - r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); - return r; -} - -/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ -static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) -{ - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txin.prevout.hash.ToString())); - entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); - entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); - entry.push_back(Pair("error", strMessage)); - vErrorsRet.push_back(entry); -} - -UniValue signrawtransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 4) - throw runtime_error( - "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" - "\nSign inputs for raw transaction (serialized, hex-encoded).\n" - "The second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - "The third optional argument (may be null) is an array of base58-encoded private\n" - "keys that, if given, will be the only keys used to sign the transaction.\n" -#ifdef ENABLE_WALLET - + HelpRequiringPassphrase() + "\n" -#endif - - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" - " [ (json array of json objects, or 'null' if none provided)\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" - " }\n" - " ,...\n" - " ]\n" - "3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n" - " [ (json array of strings, or 'null' if none provided)\n" - " \"privatekey\" (string) private key in base58-encoding\n" - " ,...\n" - " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" - " \"ALL\"\n" - " \"NONE\"\n" - " \"SINGLE\"\n" - " \"ALL|ANYONECANPAY\"\n" - " \"NONE|ANYONECANPAY\"\n" - " \"SINGLE|ANYONECANPAY\"\n" - - "\nResult:\n" - "{\n" - " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") - + HelpExampleRpc("signrawtransaction", "\"myhex\"") - ); - -#ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); -#else - LOCK(cs_main); -#endif - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); - - vector txData(ParseHexV(params[0], "argument 1")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - vector txVariants; - while (!ssData.empty()) { - try { - CMutableTransaction tx; - ssData >> tx; - txVariants.push_back(tx); - } - catch (const std::exception&) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - } - } - - if (txVariants.empty()) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); - - // mergedTx will end up with all the signatures; it - // starts as a clone of the rawtx: - CMutableTransaction mergedTx(txVariants[0]); - - // Fetch previous transactions (inputs): - CCoinsView viewDummy; - CCoinsViewCache view(&viewDummy); - { - LOCK(mempool.cs); - CCoinsViewCache &viewChain = *pcoinsTip; - CCoinsViewMemPool viewMempool(&viewChain, mempool); - view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - - BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { - const uint256& prevHash = txin.prevout.hash; - CCoins coins; - view.AccessCoins(prevHash); // this is certainly allowed to fail - } - - view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long - } - - bool fGivenKeys = false; - CBasicKeyStore tempKeystore; - if (params.size() > 2 && !params[2].isNull()) { - fGivenKeys = true; - UniValue keys = params[2].get_array(); - for (unsigned int idx = 0; idx < keys.size(); idx++) { - UniValue k = keys[idx]; - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(k.get_str()); - if (!fGood) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - CKey key = vchSecret.GetKey(); - if (!key.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); - tempKeystore.AddKey(key); - } - } -#ifdef ENABLE_WALLET - else if (pwalletMain) - EnsureWalletIsUnlocked(); -#endif - - // Add previous txouts given in the RPC call: - if (params.size() > 1 && !params[1].isNull()) { - UniValue prevTxs = params[1].get_array(); - for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { - const UniValue& p = prevTxs[idx]; - if (!p.isObject()) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); - - UniValue prevOut = p.get_obj(); - - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); - - uint256 txid = ParseHashO(prevOut, "txid"); - - int nOut = find_value(prevOut, "vout").get_int(); - if (nOut < 0) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); - - vector pkData(ParseHexO(prevOut, "scriptPubKey")); - CScript scriptPubKey(pkData.begin(), pkData.end()); - - { - CCoinsModifier coins = view.ModifyCoins(txid); - if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { - string err("Previous output scriptPubKey mismatch:\n"); - err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ - ScriptToAsmStr(scriptPubKey); - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); - } - if ((unsigned int)nOut >= coins->vout.size()) - coins->vout.resize(nOut+1); - coins->vout[nOut].scriptPubKey = scriptPubKey; - coins->vout[nOut].nValue = 0; // we don't know the actual output value - } - - // if redeemScript given and not using the local wallet (private keys - // given), add redeemScript to the tempKeystore so it can be signed: - if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); - UniValue v = find_value(prevOut, "redeemScript"); - if (!v.isNull()) { - vector rsData(ParseHexV(v, "redeemScript")); - CScript redeemScript(rsData.begin(), rsData.end()); - tempKeystore.AddCScript(redeemScript); - } - } - } - } - -#ifdef ENABLE_WALLET - const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); -#else - const CKeyStore& keystore = tempKeystore; -#endif - - int nHashType = SIGHASH_ALL; - if (params.size() > 3 && !params[3].isNull()) { - static map mapSigHashValues = - boost::assign::map_list_of - (string("ALL"), int(SIGHASH_ALL)) - (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) - (string("NONE"), int(SIGHASH_NONE)) - (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) - (string("SINGLE"), int(SIGHASH_SINGLE)) - (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) - ; - string strHashType = params[3].get_str(); - if (mapSigHashValues.count(strHashType)) - nHashType = mapSigHashValues[strHashType]; - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); - } - - bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); - - // Script verification errors - UniValue vErrors(UniValue::VARR); - - // Sign what we can: - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { - CTxIn& txin = mergedTx.vin[i]; - const CCoins* coins = view.AccessCoins(txin.prevout.hash); - if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); - continue; - } - const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; - - txin.scriptSig.clear(); - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); - - // ... and merge in other signatures: - BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); - } - ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); - } - } - bool fComplete = vErrors.empty(); - - UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(mergedTx))); - result.push_back(Pair("complete", fComplete)); - if (!vErrors.empty()) { - result.push_back(Pair("errors", vErrors)); - } - - return result; -} - -UniValue sendrawtransaction(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "sendrawtransaction \"hexstring\" ( allowhighfees )\n" - "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" - "\nAlso see createrawtransaction and signrawtransaction calls.\n" - "\nArguments:\n" - "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" - "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" - "\nResult:\n" - "\"hex\" (string) The transaction hash in hex\n" - "\nExamples:\n" - "\nCreate a transaction\n" - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + - "Sign the transaction, and get back the hex\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") + - "\nSend the transaction (signed hex)\n" - + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") - ); - - LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); - - // parse hex string from parameter - CTransaction tx; - if (!DecodeHexTx(tx, params[0].get_str())) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - uint256 hashTx = tx.GetHash(); - - bool fOverrideFees = false; - if (params.size() > 1) - fOverrideFees = params[1].get_bool(); - - CCoinsViewCache &view = *pcoinsTip; - const CCoins* existingCoins = view.AccessCoins(hashTx); - bool fHaveMempool = mempool.exists(hashTx); - bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; - if (!fHaveMempool && !fHaveChain) { - // push to local node and sync with wallets - CValidationState state; - bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) { - if (state.IsInvalid()) { - throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); - } else { - if (fMissingInputs) { - throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); - } - throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); - } - } - } else if (fHaveChain) { - throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); - } - RelayTransaction(tx); - - return hashTx.GetHex(); -} diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp deleted file mode 100644 index b638598f7..000000000 --- a/src/rpcserver.cpp +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "rpcserver.h" - -#include "base58.h" -#include "init.h" -#include "random.h" -#include "sync.h" -#include "ui_interface.h" -#include "util.h" -#include "utilstrencodings.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include // for to_upper() - -using namespace RPCServer; -using namespace std; - -static bool fRPCRunning = false; -static bool fRPCInWarmup = true; -static std::string rpcWarmupStatus("RPC server started"); -static CCriticalSection cs_rpcWarmup; -/* Timer-creating functions */ -static RPCTimerInterface* timerInterface = NULL; -/* Map of name to timer. - * @note Can be changed to std::unique_ptr when C++11 */ -static std::map > deadlineTimers; - -static struct CRPCSignals -{ - boost::signals2::signal Started; - boost::signals2::signal Stopped; - boost::signals2::signal PreCommand; - boost::signals2::signal PostCommand; -} g_rpcSignals; - -void RPCServer::OnStarted(boost::function slot) -{ - g_rpcSignals.Started.connect(slot); -} - -void RPCServer::OnStopped(boost::function slot) -{ - g_rpcSignals.Stopped.connect(slot); -} - -void RPCServer::OnPreCommand(boost::function slot) -{ - g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); -} - -void RPCServer::OnPostCommand(boost::function slot) -{ - g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); -} - -void RPCTypeCheck(const UniValue& params, - const list& typesExpected, - bool fAllowNull) -{ - unsigned int i = 0; - BOOST_FOREACH(UniValue::VType t, typesExpected) - { - if (params.size() <= i) - break; - - const UniValue& v = params[i]; - if (!((v.type() == t) || (fAllowNull && (v.isNull())))) - { - string err = strprintf("Expected type %s, got %s", - uvTypeName(t), uvTypeName(v.type())); - throw JSONRPCError(RPC_TYPE_ERROR, err); - } - i++; - } -} - -void RPCTypeCheckObj(const UniValue& o, - const map& typesExpected, - bool fAllowNull) -{ - BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) - { - const UniValue& v = find_value(o, t.first); - if (!fAllowNull && v.isNull()) - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - - if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) - { - string err = strprintf("Expected type %s for %s, got %s", - uvTypeName(t.second), t.first, uvTypeName(v.type())); - throw JSONRPCError(RPC_TYPE_ERROR, err); - } - } -} - -CAmount AmountFromValue(const UniValue& value) -{ - if (!value.isNum() && !value.isStr()) - throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); - CAmount amount; - if (!ParseFixedPoint(value.getValStr(), 8, &amount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - if (!MoneyRange(amount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); - return amount; -} - -UniValue ValueFromAmount(const CAmount& amount) -{ - bool sign = amount < 0; - int64_t n_abs = (sign ? -amount : amount); - int64_t quotient = n_abs / COIN; - int64_t remainder = n_abs % COIN; - return UniValue(UniValue::VNUM, - strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); -} - -uint256 ParseHashV(const UniValue& v, string strName) -{ - string strHex; - if (v.isStr()) - 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; -} -uint256 ParseHashO(const UniValue& o, string strKey) -{ - return ParseHashV(find_value(o, strKey), strKey); -} -vector ParseHexV(const UniValue& v, string strName) -{ - string strHex; - if (v.isStr()) - strHex = v.get_str(); - if (!IsHex(strHex)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - return ParseHex(strHex); -} -vector ParseHexO(const UniValue& o, string strKey) -{ - return ParseHexV(find_value(o, strKey), strKey); -} - -/** - * Note: This interface may still be subject to change. - */ - -std::string CRPCTable::help(const std::string& strCommand) const -{ - string strRet; - string category; - set setDone; - vector > vCommands; - - for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) - vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); - sort(vCommands.begin(), vCommands.end()); - - BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands) - { - const CRPCCommand *pcmd = command.second; - string strMethod = pcmd->name; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod.find("label") != string::npos) - continue; - if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) - continue; - try - { - UniValue params; - rpcfn_type pfn = pcmd->actor; - if (setDone.insert(pfn).second) - (*pfn)(params, true); - } - catch (const std::exception& e) - { - // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - { - if (strHelp.find('\n') != string::npos) - strHelp = strHelp.substr(0, strHelp.find('\n')); - - if (category != pcmd->category) - { - if (!category.empty()) - strRet += "\n"; - category = pcmd->category; - string firstLetter = category.substr(0,1); - boost::to_upper(firstLetter); - strRet += "== " + firstLetter + category.substr(1) + " ==\n"; - } - } - strRet += strHelp + "\n"; - } - } - if (strRet == "") - strRet = strprintf("help: unknown command: %s\n", strCommand); - strRet = strRet.substr(0,strRet.size()-1); - return strRet; -} - -UniValue help(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "help ( \"command\" )\n" - "\nList all commands, or get help for a specified command.\n" - "\nArguments:\n" - "1. \"command\" (string, optional) The command to get help on\n" - "\nResult:\n" - "\"text\" (string) The help text\n" - ); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - - return tableRPC.help(strCommand); -} - - -UniValue stop(const UniValue& params, bool fHelp) -{ - // Accept the deprecated and ignored 'detach' boolean argument - if (fHelp || params.size() > 1) - throw runtime_error( - "stop\n" - "\nStop Bitcoin server."); - // Event loop will exit after current HTTP requests have been handled, so - // this reply will get back to the client. - StartShutdown(); - return "Bitcoin server stopping"; -} - -/** - * Call Table - */ -static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - /* Overall control/query calls */ - { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ - { "control", "help", &help, true }, - { "control", "stop", &stop, true }, - - /* P2P networking */ - { "network", "getnetworkinfo", &getnetworkinfo, true }, - { "network", "addnode", &addnode, true }, - { "network", "disconnectnode", &disconnectnode, true }, - { "network", "getaddednodeinfo", &getaddednodeinfo, true }, - { "network", "getconnectioncount", &getconnectioncount, true }, - { "network", "getnettotals", &getnettotals, true }, - { "network", "getpeerinfo", &getpeerinfo, true }, - { "network", "ping", &ping, true }, - { "network", "setban", &setban, true }, - { "network", "listbanned", &listbanned, true }, - { "network", "clearbanned", &clearbanned, true }, - - /* Block chain and UTXO */ - { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, - { "blockchain", "getbestblockhash", &getbestblockhash, true }, - { "blockchain", "getblockcount", &getblockcount, true }, - { "blockchain", "getblock", &getblock, true }, - { "blockchain", "getblockhash", &getblockhash, true }, - { "blockchain", "getblockheader", &getblockheader, true }, - { "blockchain", "getchaintips", &getchaintips, true }, - { "blockchain", "getdifficulty", &getdifficulty, true }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, - { "blockchain", "getrawmempool", &getrawmempool, true }, - { "blockchain", "gettxout", &gettxout, true }, - { "blockchain", "gettxoutproof", &gettxoutproof, true }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, - { "blockchain", "verifychain", &verifychain, true }, - - /* Mining */ - { "mining", "getblocktemplate", &getblocktemplate, true }, - { "mining", "getmininginfo", &getmininginfo, true }, - { "mining", "getnetworkhashps", &getnetworkhashps, true }, - { "mining", "prioritisetransaction", &prioritisetransaction, true }, - { "mining", "submitblock", &submitblock, true }, - - /* Coin generation */ - { "generating", "getgenerate", &getgenerate, true }, - { "generating", "setgenerate", &setgenerate, true }, - { "generating", "generate", &generate, true }, - - /* Raw transactions */ - { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, - { "rawtransactions", "decodescript", &decodescript, true }, - { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ - - /* Utility functions */ - { "util", "createmultisig", &createmultisig, true }, - { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ - { "util", "verifymessage", &verifymessage, true }, - { "util", "estimatefee", &estimatefee, true }, - { "util", "estimatepriority", &estimatepriority, true }, - { "util", "estimatesmartfee", &estimatesmartfee, true }, - { "util", "estimatesmartpriority", &estimatesmartpriority, true }, - - /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true }, - { "hidden", "reconsiderblock", &reconsiderblock, true }, - { "hidden", "setmocktime", &setmocktime, true }, -}; - -CRPCTable::CRPCTable() -{ - unsigned int vcidx; - for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) - { - const CRPCCommand *pcmd; - - pcmd = &vRPCCommands[vcidx]; - mapCommands[pcmd->name] = pcmd; - } -} - -const CRPCCommand *CRPCTable::operator[](const std::string &name) const -{ - map::const_iterator it = mapCommands.find(name); - if (it == mapCommands.end()) - return NULL; - return (*it).second; -} - -bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd) -{ - if (IsRPCRunning()) - return false; - - // don't allow overwriting for now - map::const_iterator it = mapCommands.find(name); - if (it != mapCommands.end()) - return false; - - mapCommands[name] = pcmd; - return true; -} - -bool StartRPC() -{ - LogPrint("rpc", "Starting RPC\n"); - fRPCRunning = true; - g_rpcSignals.Started(); - return true; -} - -void InterruptRPC() -{ - LogPrint("rpc", "Interrupting RPC\n"); - // Interrupt e.g. running longpolls - fRPCRunning = false; -} - -void StopRPC() -{ - LogPrint("rpc", "Stopping RPC\n"); - deadlineTimers.clear(); - g_rpcSignals.Stopped(); -} - -bool IsRPCRunning() -{ - return fRPCRunning; -} - -void SetRPCWarmupStatus(const std::string& newStatus) -{ - LOCK(cs_rpcWarmup); - rpcWarmupStatus = newStatus; -} - -void SetRPCWarmupFinished() -{ - LOCK(cs_rpcWarmup); - assert(fRPCInWarmup); - fRPCInWarmup = false; -} - -bool RPCIsInWarmup(std::string *outStatus) -{ - LOCK(cs_rpcWarmup); - if (outStatus) - *outStatus = rpcWarmupStatus; - return fRPCInWarmup; -} - -void JSONRequest::parse(const UniValue& valRequest) -{ - // Parse request - if (!valRequest.isObject()) - throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); - const UniValue& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - UniValue valMethod = find_value(request, "method"); - if (valMethod.isNull()) - throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); - if (!valMethod.isStr()) - throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); - strMethod = valMethod.get_str(); - if (strMethod != "getblocktemplate") - LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); - - // Parse params - UniValue valParams = find_value(request, "params"); - if (valParams.isArray()) - params = valParams.get_array(); - else if (valParams.isNull()) - params = UniValue(UniValue::VARR); - else - throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); -} - -static UniValue JSONRPCExecOne(const UniValue& req) -{ - UniValue rpc_result(UniValue::VOBJ); - - JSONRequest jreq; - try { - jreq.parse(req); - - UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); - rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); - } - catch (const UniValue& objError) - { - rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); - } - catch (const std::exception& e) - { - rpc_result = JSONRPCReplyObj(NullUniValue, - JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); - } - - return rpc_result; -} - -std::string JSONRPCExecBatch(const UniValue& vReq) -{ - UniValue ret(UniValue::VARR); - for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) - ret.push_back(JSONRPCExecOne(vReq[reqIdx])); - - return ret.write() + "\n"; -} - -UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const -{ - // Return immediately if in warmup - { - LOCK(cs_rpcWarmup); - if (fRPCInWarmup) - throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); - } - - // Find method - const CRPCCommand *pcmd = tableRPC[strMethod]; - if (!pcmd) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); - - g_rpcSignals.PreCommand(*pcmd); - - try - { - // Execute - return pcmd->actor(params, false); - } - catch (const std::exception& e) - { - throw JSONRPCError(RPC_MISC_ERROR, e.what()); - } - - g_rpcSignals.PostCommand(*pcmd); -} - -std::string HelpExampleCli(const std::string& methodname, const std::string& args) -{ - return "> bitcoin-cli " + methodname + " " + args + "\n"; -} - -std::string HelpExampleRpc(const std::string& methodname, const std::string& args) -{ - return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " - "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; -} - -void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) -{ - if (!timerInterface) - timerInterface = iface; -} - -void RPCSetTimerInterface(RPCTimerInterface *iface) -{ - timerInterface = iface; -} - -void RPCUnsetTimerInterface(RPCTimerInterface *iface) -{ - if (timerInterface == iface) - timerInterface = NULL; -} - -void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds) -{ - if (!timerInterface) - throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); - deadlineTimers.erase(name); - LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); - deadlineTimers.insert(std::make_pair(name, boost::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); -} - -CRPCTable tableRPC; diff --git a/src/rpcserver.h b/src/rpcserver.h deleted file mode 100644 index 219d0422d..000000000 --- a/src/rpcserver.h +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPCSERVER_H -#define BITCOIN_RPCSERVER_H - -#include "amount.h" -#include "rpcprotocol.h" -#include "uint256.h" - -#include -#include -#include -#include - -#include - -#include - -class CRPCCommand; - -namespace RPCServer -{ - void OnStarted(boost::function slot); - void OnStopped(boost::function slot); - void OnPreCommand(boost::function slot); - void OnPostCommand(boost::function slot); -} - -class CBlockIndex; -class CNetAddr; - -class JSONRequest -{ -public: - UniValue id; - std::string strMethod; - UniValue params; - - JSONRequest() { id = NullUniValue; } - void parse(const UniValue& valRequest); -}; - -/** Query whether RPC is running */ -bool IsRPCRunning(); - -/** - * Set the RPC warmup status. When this is done, all RPC calls will error out - * immediately with RPC_IN_WARMUP. - */ -void SetRPCWarmupStatus(const std::string& newStatus); -/* Mark warmup as done. RPC calls will be processed from now on. */ -void SetRPCWarmupFinished(); - -/* returns the current warmup state. */ -bool RPCIsInWarmup(std::string *statusOut); - -/** - * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that - * the right number of arguments are passed, just that any passed are the correct type. - * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); - */ -void RPCTypeCheck(const UniValue& params, - const std::list& typesExpected, bool fAllowNull=false); - -/* - Check for expected keys/value types in an Object. - Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); -*/ -void RPCTypeCheckObj(const UniValue& o, - const std::map& typesExpected, bool fAllowNull=false); - -/** Opaque base class for timers returned by NewTimerFunc. - * This provides no methods at the moment, but makes sure that delete - * cleans up the whole state. - */ -class RPCTimerBase -{ -public: - virtual ~RPCTimerBase() {} -}; - -/** - * RPC timer "driver". - */ -class RPCTimerInterface -{ -public: - virtual ~RPCTimerInterface() {} - /** Implementation name */ - virtual const char *Name() = 0; - /** Factory function for timers. - * RPC will call the function to create a timer that will call func in *millis* milliseconds. - * @note As the RPC mechanism is backend-neutral, it can use different implementations of timers. - * This is needed to cope with the case in which there is no HTTP server, but - * only GUI RPC console, and to break the dependency of pcserver on httprpc. - */ - virtual RPCTimerBase* NewTimer(boost::function& func, int64_t millis) = 0; -}; - -/** Set the factory function for timers */ -void RPCSetTimerInterface(RPCTimerInterface *iface); -/** Set the factory function for timer, but only, if unset */ -void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface); -/** Unset factory function for timers */ -void RPCUnsetTimerInterface(RPCTimerInterface *iface); - -/** - * Run func nSeconds from now. - * Overrides previous timer (if any). - */ -void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds); - -typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp); - -class CRPCCommand -{ -public: - std::string category; - std::string name; - rpcfn_type actor; - bool okSafeMode; -}; - -/** - * Bitcoin RPC command dispatcher. - */ -class CRPCTable -{ -private: - std::map mapCommands; -public: - CRPCTable(); - const CRPCCommand* operator[](const std::string& name) const; - std::string help(const std::string& name) const; - - /** - * Execute a method. - * @param method Method to execute - * @param params UniValue Array of arguments (JSON objects) - * @returns Result of the call. - * @throws an exception (UniValue) when an error happens. - */ - UniValue execute(const std::string &method, const UniValue ¶ms) const; - - - /** - * Appends a CRPCCommand to the dispatch table. - * Returns false if RPC server is already running (dump concurrency protection). - * Commands cannot be overwritten (returns false). - */ - bool appendCommand(const std::string& name, const CRPCCommand* pcmd); -}; - -extern CRPCTable tableRPC; - -/** - * Utilities: convert hex-encoded Values - * (throws error if not hex). - */ -extern uint256 ParseHashV(const UniValue& v, std::string strName); -extern uint256 ParseHashO(const UniValue& o, std::string strKey); -extern std::vector ParseHexV(const UniValue& v, std::string strName); -extern std::vector ParseHexO(const UniValue& o, std::string strKey); - -extern int64_t nWalletUnlockTime; -extern CAmount AmountFromValue(const UniValue& value); -extern UniValue ValueFromAmount(const CAmount& amount); -extern double GetDifficulty(const CBlockIndex* blockindex = NULL); -extern std::string HelpRequiringPassphrase(); -extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); -extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); - -extern void EnsureWalletIsUnlocked(); - -extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp -extern UniValue getpeerinfo(const UniValue& params, bool fHelp); -extern UniValue ping(const UniValue& params, bool fHelp); -extern UniValue addnode(const UniValue& params, bool fHelp); -extern UniValue disconnectnode(const UniValue& params, bool fHelp); -extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); -extern UniValue getnettotals(const UniValue& params, bool fHelp); -extern UniValue setban(const UniValue& params, bool fHelp); -extern UniValue listbanned(const UniValue& params, bool fHelp); -extern UniValue clearbanned(const UniValue& params, bool fHelp); - -extern UniValue getgenerate(const UniValue& params, bool fHelp); // in rpcmining.cpp -extern UniValue setgenerate(const UniValue& params, bool fHelp); -extern UniValue generate(const UniValue& params, bool fHelp); -extern UniValue getnetworkhashps(const UniValue& params, bool fHelp); -extern UniValue getmininginfo(const UniValue& params, bool fHelp); -extern UniValue prioritisetransaction(const UniValue& params, bool fHelp); -extern UniValue getblocktemplate(const UniValue& params, bool fHelp); -extern UniValue submitblock(const UniValue& params, bool fHelp); -extern UniValue estimatefee(const UniValue& params, bool fHelp); -extern UniValue estimatepriority(const UniValue& params, bool fHelp); -extern UniValue estimatesmartfee(const UniValue& params, bool fHelp); -extern UniValue estimatesmartpriority(const UniValue& params, bool fHelp); - -extern UniValue verifymessage(const UniValue& params, bool fHelp); -extern UniValue createmultisig(const UniValue& params, bool fHelp); -extern UniValue validateaddress(const UniValue& params, bool fHelp); -extern UniValue getinfo(const UniValue& params, bool fHelp); -extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); -extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); -extern UniValue setmocktime(const UniValue& params, bool fHelp); - -extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp -extern UniValue listunspent(const UniValue& params, bool fHelp); -extern UniValue lockunspent(const UniValue& params, bool fHelp); -extern UniValue listlockunspent(const UniValue& params, bool fHelp); -extern UniValue createrawtransaction(const UniValue& params, bool fHelp); -extern UniValue decoderawtransaction(const UniValue& params, bool fHelp); -extern UniValue decodescript(const UniValue& params, bool fHelp); -extern UniValue signrawtransaction(const UniValue& params, bool fHelp); -extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); -extern UniValue gettxoutproof(const UniValue& params, bool fHelp); -extern UniValue verifytxoutproof(const UniValue& params, bool fHelp); - -extern UniValue getblockcount(const UniValue& params, bool fHelp); // in rpcblockchain.cpp -extern UniValue getbestblockhash(const UniValue& params, bool fHelp); -extern UniValue getdifficulty(const UniValue& params, bool fHelp); -extern UniValue settxfee(const UniValue& params, bool fHelp); -extern UniValue getmempoolinfo(const UniValue& params, bool fHelp); -extern UniValue getrawmempool(const UniValue& params, bool fHelp); -extern UniValue getblockhash(const UniValue& params, bool fHelp); -extern UniValue getblockheader(const UniValue& params, bool fHelp); -extern UniValue getblock(const UniValue& params, bool fHelp); -extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp); -extern UniValue gettxout(const UniValue& params, bool fHelp); -extern UniValue verifychain(const UniValue& params, bool fHelp); -extern UniValue getchaintips(const UniValue& params, bool fHelp); -extern UniValue invalidateblock(const UniValue& params, bool fHelp); -extern UniValue reconsiderblock(const UniValue& params, bool fHelp); - -bool StartRPC(); -void InterruptRPC(); -void StopRPC(); -std::string JSONRPCExecBatch(const UniValue& vReq); - -#endif // BITCOIN_RPCSERVER_H diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index cc52e3ea0..d6309ca38 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpcserver.h" -#include "rpcclient.h" +#include "rpc/server.h" +#include "rpc/client.h" #include "base58.h" #include "netbase.h" @@ -260,7 +260,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) adr = find_value(o1, "address"); banned_until = find_value(o1, "banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); - int64_t now = GetTime(); + int64_t now = GetTime(); BOOST_CHECK(banned_until.get_int64() > now); BOOST_CHECK(banned_until.get_int64()-now <= 200); diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 398372af3..3443be209 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpcserver.h" -#include "rpcclient.h" +#include "rpc/server.h" +#include "rpc/client.h" #include "base58.h" #include "main.h" diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index b025c3745..9ec28e7b9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -4,7 +4,7 @@ #include "base58.h" #include "chain.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "init.h" #include "main.h" #include "script/script.h" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e54293395..8a2d938ae 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -12,7 +12,7 @@ #include "net.h" #include "netbase.h" #include "policy/rbf.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "timedata.h" #include "util.h" #include "utilmoneystr.h" @@ -110,7 +110,7 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress ( \"account\" )\n" @@ -189,7 +189,7 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "getaccountaddress \"account\"\n" @@ -221,7 +221,7 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 1) throw runtime_error( "getrawchangeaddress\n" @@ -256,7 +256,7 @@ UniValue setaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "setaccount \"bitcoinaddress\" \"account\"\n" @@ -302,7 +302,7 @@ UniValue getaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "getaccount \"bitcoinaddress\"\n" @@ -334,7 +334,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "getaddressesbyaccount \"account\"\n" @@ -402,7 +402,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( "sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" @@ -460,7 +460,7 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp) throw runtime_error( "listaddressgroupings\n" @@ -511,7 +511,7 @@ UniValue signmessage(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 2) throw runtime_error( "signmessage \"bitcoinaddress\" \"message\"\n" @@ -567,7 +567,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n" @@ -625,7 +625,7 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getreceivedbyaccount \"account\" ( minconf )\n" @@ -714,7 +714,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 3) throw runtime_error( "getbalance ( \"account\" minconf includeWatchonly )\n" @@ -789,7 +789,7 @@ UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 0) throw runtime_error( "getunconfirmedbalance\n" @@ -805,7 +805,7 @@ UniValue movecmd(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 3 || params.size() > 5) throw runtime_error( "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" @@ -878,7 +878,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( "sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" @@ -942,7 +942,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" @@ -1056,7 +1056,7 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 2 || params.size() > 3) { string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" @@ -1239,7 +1239,7 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 3) throw runtime_error( "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" @@ -1277,7 +1277,7 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 3) throw runtime_error( "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" @@ -1412,7 +1412,7 @@ UniValue listtransactions(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 4) throw runtime_error( "listtransactions ( \"account\" count from includeWatchonly)\n" @@ -1538,7 +1538,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 2) throw runtime_error( "listaccounts ( minconf includeWatchonly)\n" @@ -1617,7 +1617,7 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp) throw runtime_error( "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" @@ -1709,7 +1709,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "gettransaction \"txid\" ( includeWatchonly )\n" @@ -1824,7 +1824,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 1) throw runtime_error( "backupwallet \"destination\"\n" @@ -1850,7 +1850,7 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 1) throw runtime_error( "keypoolrefill ( newsize )\n" @@ -1894,7 +1894,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( "walletpassphrase \"passphrase\" timeout\n" @@ -1954,7 +1954,7 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" @@ -2000,7 +2000,7 @@ UniValue walletlock(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) throw runtime_error( "walletlock\n" @@ -2039,7 +2039,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) throw runtime_error( "encryptwallet \"passphrase\"\n" @@ -2096,7 +2096,7 @@ UniValue lockunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" @@ -2180,7 +2180,7 @@ UniValue listlockunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 0) throw runtime_error( "listlockunspent\n" @@ -2229,7 +2229,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" @@ -2256,7 +2256,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 0) throw runtime_error( "getwalletinfo\n" @@ -2298,7 +2298,7 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() != 0) throw runtime_error( "resendwallettransactions\n" @@ -2323,7 +2323,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - + if (fHelp || params.size() > 3) throw runtime_error( "listunspent ( minconf maxconf [\"address\",...] )\n" -- cgit v1.2.3