aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp1514
-rw-r--r--src/rpc/client.cpp220
-rw-r--r--src/rpc/client.h22
-rw-r--r--src/rpc/mining.cpp1143
-rw-r--r--src/rpc/misc.cpp532
-rw-r--r--src/rpc/net.cpp626
-rw-r--r--src/rpc/protocol.cpp128
-rw-r--r--src/rpc/protocol.h94
-rw-r--r--src/rpc/rawtransaction.cpp950
-rw-r--r--src/rpc/register.h32
-rw-r--r--src/rpc/server.cpp568
-rw-r--r--src/rpc/server.h214
12 files changed, 6043 insertions, 0 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
new file mode 100644
index 000000000..6d3264130
--- /dev/null
+++ b/src/rpc/blockchain.cpp
@@ -0,0 +1,1514 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 "core_io.h"
+#include "validation.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 "hash.h"
+
+#include <stdint.h>
+
+#include <univalue.h>
+
+#include <boost/thread/thread.hpp> // boost::thread::interrupt
+
+#include <mutex>
+#include <condition_variable>
+using namespace std;
+
+struct CUpdatedBlock
+{
+ uint256 hash;
+ int height;
+};
+
+static std::mutex cs_blockchange;
+static std::condition_variable cond_blockchange;
+static CUpdatedBlock latestblock;
+
+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 AuxpowToJSON(const CAuxPow& auxpow)
+{
+ UniValue result(UniValue::VOBJ);
+
+ {
+ UniValue tx(UniValue::VOBJ);
+ tx.push_back(Pair("hex", EncodeHexTx(auxpow)));
+ TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx);
+ result.push_back(Pair("tx", tx));
+ }
+
+ result.push_back(Pair("index", auxpow.nIndex));
+ result.push_back(Pair("chainindex", auxpow.nChainIndex));
+
+ {
+ UniValue branch(UniValue::VARR);
+ BOOST_FOREACH(const uint256& node, auxpow.vMerkleBranch)
+ branch.push_back(node.GetHex());
+ result.push_back(Pair("merklebranch", branch));
+ }
+
+ {
+ UniValue branch(UniValue::VARR);
+ BOOST_FOREACH(const uint256& node, auxpow.vChainMerkleBranch)
+ branch.push_back(node.GetHex());
+ result.push_back(Pair("chainmerklebranch", branch));
+ }
+
+ CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION);
+ ssParent << auxpow.parentBlock;
+ const std::string strHex = HexStr(ssParent.begin(), ssParent.end());
+ result.push_back(Pair("parentblock", strHex));
+
+ return result;
+}
+
+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("versionHex", strprintf("%08x", 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", 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("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)));
+ result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
+ result.push_back(Pair("weight", (int)::GetBlockWeight(block)));
+ result.push_back(Pair("height", blockindex->nHeight));
+ result.push_back(Pair("version", block.nVersion));
+ result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
+ result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
+ UniValue txs(UniValue::VARR);
+ for(const auto& 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 (block.auxpow)
+ result.push_back(Pair("auxpow", AuxpowToJSON(*block.auxpow)));
+
+ 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getblockcount\n"
+ "\nReturns the number of blocks in the longest blockchain.\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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getbestblockhash\n"
+ "\nReturns the hash of the best (tip) block in the longest blockchain.\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();
+}
+
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
+{
+ if(pindex) {
+ std::lock_guard<std::mutex> lock(cs_blockchange);
+ latestblock.hash = pindex->GetBlockHash();
+ latestblock.height = pindex->nHeight;
+ }
+ cond_blockchange.notify_all();
+}
+
+UniValue waitfornewblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 1)
+ throw runtime_error(
+ "waitfornewblock (timeout)\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("waitfornewblock", "1000")
+ + HelpExampleRpc("waitfornewblock", "1000")
+ );
+ int timeout = 0;
+ if (request.params.size() > 0)
+ timeout = request.params[0].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ block = latestblock;
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ else
+ cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw runtime_error(
+ "waitforblock <blockhash> (timeout)\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. \"blockhash\" (required, string) Block hash to wait for.\n"
+ "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ );
+ int timeout = 0;
+
+ uint256 hash = uint256S(request.params[0].get_str());
+
+ if (request.params.size() > 1)
+ timeout = request.params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblockheight(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw runtime_error(
+ "waitforblockheight <height> (timeout)\n"
+ "\nWaits for (at least) block height and returns the height and hash\n"
+ "of the current tip.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. height (required, int) Block height to wait for (int)\n"
+ "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("waitforblockheight", "\"100\", 1000")
+ + HelpExampleRpc("waitforblockheight", "\"100\", 1000")
+ );
+ int timeout = 0;
+
+ int height = request.params[0].get_int();
+
+ if (request.params.size() > 1)
+ timeout = request.params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue getdifficulty(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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();
+}
+
+std::string EntryDescriptionString()
+{
+ return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\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) DEPRECATED. Priority when transaction entered pool\n"
+ " \"currentpriority\" : n, (numeric) DEPRECATED. Transaction priority now\n"
+ " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
+ " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n"
+ " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n"
+ " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n"
+ " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n"
+ " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n"
+ " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
+ " \"transactionid\", (string) parent transaction id\n"
+ " ... ]\n";
+}
+
+void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
+{
+ AssertLockHeld(mempool.cs);
+
+ 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()));
+ info.push_back(Pair("ancestorcount", e.GetCountWithAncestors()));
+ info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors()));
+ info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors()));
+ const CTransaction& tx = e.GetTx();
+ set<string> 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));
+}
+
+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);
+ entryToJSON(info, e);
+ o.push_back(Pair(hash.ToString(), info));
+ }
+ return o;
+ }
+ else
+ {
+ vector<uint256> vtxid;
+ mempool.queryHashes(vtxid);
+
+ UniValue a(UniValue::VARR);
+ BOOST_FOREACH(const uint256& hash, vtxid)
+ a.push_back(hash.ToString());
+
+ return a;
+ }
+}
+
+UniValue getrawmempool(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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"
+ "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\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"
+ + EntryDescriptionString()
+ + " }, ...\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getrawmempool", "true")
+ + HelpExampleRpc("getrawmempool", "true")
+ );
+
+ bool fVerbose = false;
+ if (request.params.size() > 0)
+ fVerbose = request.params[0].get_bool();
+
+ return mempoolToJSON(fVerbose);
+}
+
+UniValue getmempoolancestors(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw runtime_error(
+ "getmempoolancestors txid (verbose)\n"
+ "\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
+ "2. 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 strings)\n"
+ " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
+ " ,...\n"
+ "]\n"
+ "\nResult (for verbose=true):\n"
+ "{ (json object)\n"
+ " \"transactionid\" : { (json object)\n"
+ + EntryDescriptionString()
+ + " }, ...\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getmempoolancestors", "\"mytxid\"")
+ + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
+ );
+ }
+
+ bool fVerbose = false;
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
+
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
+
+ LOCK(mempool.cs);
+
+ CTxMemPool::txiter it = mempool.mapTx.find(hash);
+ if (it == mempool.mapTx.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
+ }
+
+ CTxMemPool::setEntries setAncestors;
+ uint64_t noLimit = std::numeric_limits<uint64_t>::max();
+ std::string dummy;
+ mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
+
+ if (!fVerbose) {
+ UniValue o(UniValue::VARR);
+ BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) {
+ o.push_back(ancestorIt->GetTx().GetHash().ToString());
+ }
+
+ return o;
+ } else {
+ UniValue o(UniValue::VOBJ);
+ BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) {
+ const CTxMemPoolEntry &e = *ancestorIt;
+ const uint256& _hash = e.GetTx().GetHash();
+ UniValue info(UniValue::VOBJ);
+ entryToJSON(info, e);
+ o.push_back(Pair(_hash.ToString(), info));
+ }
+ return o;
+ }
+}
+
+UniValue getmempooldescendants(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw runtime_error(
+ "getmempooldescendants txid (verbose)\n"
+ "\nIf txid is in the mempool, returns all in-mempool descendants.\n"
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
+ "2. 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 strings)\n"
+ " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
+ " ,...\n"
+ "]\n"
+ "\nResult (for verbose=true):\n"
+ "{ (json object)\n"
+ " \"transactionid\" : { (json object)\n"
+ + EntryDescriptionString()
+ + " }, ...\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getmempooldescendants", "\"mytxid\"")
+ + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
+ );
+ }
+
+ bool fVerbose = false;
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
+
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
+
+ LOCK(mempool.cs);
+
+ CTxMemPool::txiter it = mempool.mapTx.find(hash);
+ if (it == mempool.mapTx.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
+ }
+
+ CTxMemPool::setEntries setDescendants;
+ mempool.CalculateDescendants(it, setDescendants);
+ // CTxMemPool::CalculateDescendants will include the given tx
+ setDescendants.erase(it);
+
+ if (!fVerbose) {
+ UniValue o(UniValue::VARR);
+ BOOST_FOREACH(CTxMemPool::txiter descendantIt, setDescendants) {
+ o.push_back(descendantIt->GetTx().GetHash().ToString());
+ }
+
+ return o;
+ } else {
+ UniValue o(UniValue::VOBJ);
+ BOOST_FOREACH(CTxMemPool::txiter descendantIt, setDescendants) {
+ const CTxMemPoolEntry &e = *descendantIt;
+ const uint256& _hash = e.GetTx().GetHash();
+ UniValue info(UniValue::VOBJ);
+ entryToJSON(info, e);
+ o.push_back(Pair(_hash.ToString(), info));
+ }
+ return o;
+ }
+}
+
+UniValue getmempoolentry(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw runtime_error(
+ "getmempoolentry txid\n"
+ "\nReturns mempool data for given transaction\n"
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
+ "\nResult:\n"
+ "{ (json object)\n"
+ + EntryDescriptionString()
+ + "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
+ );
+ }
+
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
+
+ LOCK(mempool.cs);
+
+ CTxMemPool::txiter it = mempool.mapTx.find(hash);
+ if (it == mempool.mapTx.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
+ }
+
+ const CTxMemPoolEntry &e = *it;
+ UniValue info(UniValue::VOBJ);
+ entryToJSON(info, e);
+ return info;
+}
+
+UniValue getblockhash(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "getblockhash height\n"
+ "\nReturns hash of block in best-block-chain at height provided.\n"
+ "\nArguments:\n"
+ "1. height (numeric, required) The height index\n"
+ "\nResult:\n"
+ "\"hash\" (string) The block hash\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getblockhash", "1000")
+ + HelpExampleRpc("getblockhash", "1000")
+ );
+
+ LOCK(cs_main);
+
+ int nHeight = request.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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.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 <hash>.\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"
+ " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\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"
+ " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (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("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ );
+
+ LOCK(cs_main);
+
+ std::string strHash = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+
+ bool fVerbose = true;
+ if (request.params.size() > 1)
+ fVerbose = request.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(Params().GetConsensus(pblockindex->nHeight));
+ std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ return strHex;
+ }
+
+ return blockheaderToJSON(pblockindex);
+}
+
+UniValue getblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw runtime_error(
+ "getblock \"blockhash\" ( 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 <hash>.\n"
+ "\nArguments:\n"
+ "1. \"blockhash\" (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"
+ " \"strippedsize\" : n, (numeric) The block size excluding witness data\n"
+ " \"weight\" : n (numeric) The block weight as defined in BIP 141\n"
+ " \"height\" : n, (numeric) The block height or index\n"
+ " \"version\" : n, (numeric) The block version\n"
+ " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\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 = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+
+ bool fVerbose = true;
+ if (request.params.size() > 1)
+ fVerbose = request.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_MISC_ERROR, "Block not available (pruned data)");
+
+ if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus(pblockindex->nHeight)))
+ // Block not found on disk. This could be because we have the block
+ // header in our index but don't have the block (for example if a
+ // non-whitelisted node sends us an unrequested long chain of valid
+ // blocks, we add the headers to our index, but don't accept the
+ // block).
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
+
+ if (!fVerbose)
+ {
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
+ ssBlock << block;
+ std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ return strHex;
+ }
+
+ return blockToJSON(block, pblockindex);
+}
+
+struct CCoinsStats
+{
+ int nHeight;
+ uint256 hashBlock;
+ uint64_t nTransactions;
+ uint64_t nTransactionOutputs;
+ uint64_t nSerializedSize;
+ uint256 hashSerialized;
+ arith_uint256 nTotalAmount;
+
+ CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {}
+};
+
+//! Calculate statistics about the unspent transaction output set
+static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
+{
+ std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
+
+ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
+ stats.hashBlock = pcursor->GetBestBlock();
+ {
+ LOCK(cs_main);
+ stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight;
+ }
+ ss << stats.hashBlock;
+ arith_uint256 nTotalAmount = 0;
+ while (pcursor->Valid()) {
+ boost::this_thread::interruption_point();
+ uint256 key;
+ CCoins coins;
+ if (pcursor->GetKey(key) && pcursor->GetValue(coins)) {
+ stats.nTransactions++;
+ ss << key;
+ for (unsigned int i=0; i<coins.vout.size(); i++) {
+ const CTxOut &out = coins.vout[i];
+ if (!out.IsNull()) {
+ stats.nTransactionOutputs++;
+ ss << VARINT(i+1);
+ ss << out;
+ nTotalAmount += out.nValue;
+ }
+ }
+ stats.nSerializedSize += 32 + pcursor->GetValueSize();
+ ss << VARINT(0);
+ } else {
+ return error("%s: unable to read value", __func__);
+ }
+ pcursor->Next();
+ }
+ stats.hashSerialized = ss.GetHash();
+ stats.nTotalAmount = nTotalAmount;
+ return true;
+}
+
+UniValue pruneblockchain(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "pruneblockchain\n"
+ "\nArguments:\n"
+ "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
+ " to prune blocks whose block time is at least 2 hours older than the provided timestamp.\n"
+ "\nResult:\n"
+ "n (numeric) Height of the last block pruned.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("pruneblockchain", "1000")
+ + HelpExampleRpc("pruneblockchain", "1000"));
+
+ if (!fPruneMode)
+ throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
+
+ LOCK(cs_main);
+
+ int heightParam = request.params[0].get_int();
+ if (heightParam < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
+
+ // Height value more than a billion is too high to be a block height, and
+ // too low to be a block time (corresponds to timestamp from Sep 2001).
+ if (heightParam > 1000000000) {
+ // Add a 2 hour buffer to include blocks which might have had old timestamps
+ CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - 7200);
+ if (!pindex) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
+ }
+ heightParam = pindex->nHeight;
+ }
+
+ unsigned int height = (unsigned int) heightParam;
+ unsigned int chainHeight = (unsigned int) chainActive.Height();
+ if (chainHeight < Params().PruneAfterHeight())
+ throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
+ else if (height > chainHeight)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
+ else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
+ LogPrint("rpc", "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.");
+ height = chainHeight - MIN_BLOCKS_TO_KEEP;
+ }
+
+ PruneBlockFilesManual(height);
+ return uint64_t(height);
+}
+
+UniValue gettxoutsetinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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 (GetUTXOStats(pcoinsTip, 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)));
+ } else {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
+ }
+ return ret;
+}
+
+UniValue gettxout(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
+ throw runtime_error(
+ "gettxout \"txid\" n ( include_mempool )\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. include_mempool (boolean, optional) Whether to include the mempool\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 dogecoin addresses\n"
+ " \"address\" (string) dogecoin 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 = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+ int n = request.params[1].get_int();
+ bool fMempool = true;
+ if (request.params.size() > 2)
+ fMempool = request.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 JSONRPCRequest& request)
+{
+ int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL);
+ int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
+ if (request.fHelp || request.params.size() > 2)
+ throw runtime_error(
+ "verifychain ( checklevel nblocks )\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. nblocks (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 (request.params.size() > 0)
+ nCheckLevel = request.params[0].get_int();
+ if (request.params.size() > 1)
+ nCheckDepth = request.params[1].get_int();
+
+ return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth);
+}
+
+/** Implementation of IsSuperMajority with better feedback */
+static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
+{
+ UniValue rv(UniValue::VOBJ);
+ bool activated = false;
+ switch(version)
+ {
+ case 2:
+ activated = pindex->nHeight >= consensusParams.BIP34Height;
+ break;
+ case 3:
+ activated = pindex->nHeight >= consensusParams.BIP66Height;
+ break;
+ case 4:
+ int nFound = 0;
+ int nRequired = consensusParams.nMajorityRejectBlockOutdated;
+ CBlockIndex* pstart = pindex;
+ for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++)
+ {
+ if (pstart->GetBaseVersion() >= version)
+ ++nFound;
+ pstart = pstart->pprev;
+ }
+
+ activated = nFound >= nRequired;
+ rv.push_back(Pair("found", nFound));
+ rv.push_back(Pair("required", nRequired));
+ rv.push_back(Pair("window", consensusParams.nMajorityWindow));
+ break;
+ }
+ rv.push_back(Pair("status", activated));
+ 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("reject", SoftForkMajorityDesc(version, pindex, consensusParams)));
+ return rv;
+}
+
+static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+{
+ UniValue rv(UniValue::VOBJ);
+ const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
+ switch (thresholdState) {
+ case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break;
+ case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break;
+ case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break;
+ case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break;
+ case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break;
+ }
+ if (THRESHOLD_STARTED == thresholdState)
+ {
+ rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit));
+ }
+ rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
+ rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
+ rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
+ return rv;
+}
+
+void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+{
+ // Deployments with timeout value of 0 are hidden.
+ // A timeout value of 0 guarantees a softfork will never be activated.
+ // This is used when softfork codes are merged without specifying the deployment schedule.
+ if (consensusParams.vDeployments[id].nTimeout > 0)
+ bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id)));
+}
+
+UniValue getblockchaininfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getblockchaininfo\n"
+ "Returns an object containing various state info regarding blockchain 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"
+ " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\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) lowest-height complete block stored\n"
+ " \"softforks\": [ (array) status of softforks in progress\n"
+ " {\n"
+ " \"id\": \"xxxx\", (string) name of softfork\n"
+ " \"version\": xx, (numeric) block version\n"
+ " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
+ " \"status\": xx, (boolean) true if threshold reached\n"
+ " },\n"
+ " }, ...\n"
+ " ],\n"
+ " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
+ " \"xxxx\" : { (string) name of the softfork\n"
+ " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
+ " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
+ " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx (numeric) height of the first block to which the status applies\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", GuessVerificationProgress(Params().TxData(), chainActive.Tip())));
+ obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload()));
+ obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
+ obj.push_back(Pair("pruned", fPruneMode));
+
+ const Consensus::Params& consensusParams = Params().GetConsensus(0);
+ CBlockIndex* tip = chainActive.Tip();
+ UniValue softforks(UniValue::VARR);
+ UniValue bip9_softforks(UniValue::VOBJ);
+ softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
+ softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
+ softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
+ BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV);
+ BIP9SoftForkDescPushBack(bip9_softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT);
+ obj.push_back(Pair("softforks", softforks));
+ obj.push_back(Pair("bip9_softforks", bip9_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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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);
+
+ /*
+ * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which do not have another orphan building off of them.
+ * Algorithm:
+ * - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
+ * - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
+ * - add chainActive.Tip()
+ */
+ std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
+ std::set<const CBlockIndex*> setOrphans;
+ std::set<const CBlockIndex*> setPrevs;
+
+ BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
+ {
+ if (!chainActive.Contains(item.second)) {
+ setOrphans.insert(item.second);
+ setPrevs.insert(item.second->pprev);
+ }
+ }
+
+ for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it)
+ {
+ if (setPrevs.erase(*it) == 0) {
+ setTips.insert(*it);
+ }
+ }
+
+ // 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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 virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\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 preciousblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "preciousblock \"blockhash\"\n"
+ "\nTreats a block as if it were received before others with the same work.\n"
+ "\nA later preciousblock call can override the effect of an earlier one.\n"
+ "\nThe effects of preciousblock are not retained across restarts.\n"
+ "\nArguments:\n"
+ "1. \"blockhash\" (string, required) the hash of the block to mark as precious\n"
+ "\nResult:\n"
+ "\nExamples:\n"
+ + HelpExampleCli("preciousblock", "\"blockhash\"")
+ + HelpExampleRpc("preciousblock", "\"blockhash\"")
+ );
+
+ std::string strHash = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+ CBlockIndex* pblockindex;
+
+ {
+ LOCK(cs_main);
+ if (mapBlockIndex.count(hash) == 0)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+
+ pblockindex = mapBlockIndex[hash];
+ }
+
+ CValidationState state;
+ PreciousBlock(state, Params(), pblockindex);
+
+ if (!state.IsValid()) {
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ }
+
+ return NullUniValue;
+}
+
+UniValue invalidateblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "invalidateblock \"blockhash\"\n"
+ "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
+ "\nArguments:\n"
+ "1. \"blockhash\" (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 = request.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(), pblockindex);
+ }
+
+ if (state.IsValid()) {
+ ActivateBestChain(state, Params());
+ }
+
+ if (!state.IsValid()) {
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ }
+
+ return NullUniValue;
+}
+
+UniValue reconsiderblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "reconsiderblock \"blockhash\"\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. \"blockhash\" (string, required) the hash of the block to reconsider\n"
+ "\nResult:\n"
+ "\nExamples:\n"
+ + HelpExampleCli("reconsiderblock", "\"blockhash\"")
+ + HelpExampleRpc("reconsiderblock", "\"blockhash\"")
+ );
+
+ std::string strHash = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+
+ {
+ LOCK(cs_main);
+ if (mapBlockIndex.count(hash) == 0)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+
+ CBlockIndex* pblockindex = mapBlockIndex[hash];
+ ResetBlockFailureFlags(pblockindex);
+ }
+
+ CValidationState state;
+ ActivateBestChain(state, Params());
+
+ if (!state.IsValid()) {
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ }
+
+ return NullUniValue;
+}
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafe argNames
+ // --------------------- ------------------------ ----------------------- ------ ----------
+ { "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} },
+ { "blockchain", "getbestblockhash", &getbestblockhash, true, {} },
+ { "blockchain", "getblockcount", &getblockcount, true, {} },
+ { "blockchain", "getblock", &getblock, true, {"blockhash","verbose"} },
+ { "blockchain", "getblockhash", &getblockhash, true, {"height"} },
+ { "blockchain", "getblockheader", &getblockheader, true, {"blockhash","verbose"} },
+ { "blockchain", "getchaintips", &getchaintips, true, {} },
+ { "blockchain", "getdifficulty", &getdifficulty, true, {} },
+ { "blockchain", "getmempoolancestors", &getmempoolancestors, true, {"txid","verbose"} },
+ { "blockchain", "getmempooldescendants", &getmempooldescendants, true, {"txid","verbose"} },
+ { "blockchain", "getmempoolentry", &getmempoolentry, true, {"txid"} },
+ { "blockchain", "getmempoolinfo", &getmempoolinfo, true, {} },
+ { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} },
+ { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} },
+ { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} },
+ { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} },
+ { "blockchain", "verifychain", &verifychain, true, {"checklevel","nblocks"} },
+
+ { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} },
+
+ /* Not shown in help */
+ { "hidden", "invalidateblock", &invalidateblock, true, {"blockhash"} },
+ { "hidden", "reconsiderblock", &reconsiderblock, true, {"blockhash"} },
+ { "hidden", "waitfornewblock", &waitfornewblock, true, {"timeout"} },
+ { "hidden", "waitforblock", &waitforblock, true, {"blockhash","timeout"} },
+ { "hidden", "waitforblockheight", &waitforblockheight, true, {"height","timeout"} },
+};
+
+void RegisterBlockchainRPCCommands(CRPCTable &t)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
new file mode 100644
index 000000000..56d149591
--- /dev/null
+++ b/src/rpc/client.cpp
@@ -0,0 +1,220 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 <set>
+#include <stdint.h>
+
+#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
+#include <univalue.h>
+
+using namespace std;
+
+class CRPCConvertParam
+{
+public:
+ std::string methodName; //!< method whose params want conversion
+ int paramIdx; //!< 0-based idx of param to convert
+ std::string paramName; //!< parameter name
+};
+
+/**
+ * Specifiy a (method, idx, name) here if the argument is a non-string RPC
+ * argument and needs to be converted from JSON.
+ *
+ * @note Parameter indexes start from 0.
+ */
+static const CRPCConvertParam vRPCConvertParams[] =
+{
+ { "setmocktime", 0, "timestamp" },
+ { "generate", 0, "nblocks" },
+ { "generate", 1, "maxtries" },
+ { "generatetoaddress", 0, "nblocks" },
+ { "generatetoaddress", 2, "maxtries" },
+ { "getnetworkhashps", 0, "nblocks" },
+ { "getnetworkhashps", 1, "height" },
+ { "sendtoaddress", 1, "amount" },
+ { "sendtoaddress", 4, "subtractfeefromamount" },
+ { "settxfee", 0, "amount" },
+ { "getreceivedbyaddress", 1, "minconf" },
+ { "getreceivedbyaccount", 1, "minconf" },
+ { "listreceivedbyaddress", 0, "minconf" },
+ { "listreceivedbyaddress", 1, "include_empty" },
+ { "listreceivedbyaddress", 2, "include_watchonly" },
+ { "listreceivedbyaccount", 0, "minconf" },
+ { "listreceivedbyaccount", 1, "include_empty" },
+ { "listreceivedbyaccount", 2, "include_watchonly" },
+ { "getbalance", 1, "minconf" },
+ { "getbalance", 2, "include_watchonly" },
+ { "getblockhash", 0, "height" },
+ { "waitforblockheight", 0, "height" },
+ { "waitforblockheight", 1, "timeout" },
+ { "waitforblock", 1, "timeout" },
+ { "waitfornewblock", 0, "timeout" },
+ { "move", 2, "amount" },
+ { "move", 3, "minconf" },
+ { "sendfrom", 2, "amount" },
+ { "sendfrom", 3, "minconf" },
+ { "listtransactions", 1, "count" },
+ { "listtransactions", 2, "skip" },
+ { "listtransactions", 3, "include_watchonly" },
+ { "listaccounts", 0, "minconf" },
+ { "listaccounts", 1, "include_watchonly" },
+ { "walletpassphrase", 1, "timeout" },
+ { "getblocktemplate", 0, "template_request" },
+ { "listsinceblock", 1, "target_confirmations" },
+ { "listsinceblock", 2, "include_watchonly" },
+ { "sendmany", 1, "amounts" },
+ { "sendmany", 2, "minconf" },
+ { "sendmany", 4, "subtractfeefrom" },
+ { "addmultisigaddress", 0, "nrequired" },
+ { "addmultisigaddress", 1, "keys" },
+ { "createmultisig", 0, "nrequired" },
+ { "createmultisig", 1, "keys" },
+ { "listunspent", 0, "minconf" },
+ { "listunspent", 1, "maxconf" },
+ { "listunspent", 2, "addresses" },
+ { "getblock", 1, "verbose" },
+ { "getblockheader", 1, "verbose" },
+ { "gettransaction", 1, "include_watchonly" },
+ { "getrawtransaction", 1, "verbose" },
+ { "createrawtransaction", 0, "inputs" },
+ { "createrawtransaction", 1, "outputs" },
+ { "createrawtransaction", 2, "locktime" },
+ { "signrawtransaction", 1, "prevtxs" },
+ { "signrawtransaction", 2, "privkeys" },
+ { "sendrawtransaction", 1, "allowhighfees" },
+ { "fundrawtransaction", 1, "options" },
+ { "gettxout", 1, "n" },
+ { "gettxout", 2, "include_mempool" },
+ { "gettxoutproof", 0, "txids" },
+ { "lockunspent", 0, "unlock" },
+ { "lockunspent", 1, "transactions" },
+ { "importprivkey", 2, "rescan" },
+ { "importaddress", 2, "rescan" },
+ { "importaddress", 3, "p2sh" },
+ { "importpubkey", 2, "rescan" },
+ { "importmulti", 0, "requests" },
+ { "importmulti", 1, "options" },
+ { "verifychain", 0, "checklevel" },
+ { "verifychain", 1, "nblocks" },
+ { "pruneblockchain", 0, "height" },
+ { "keypoolrefill", 0, "newsize" },
+ { "getrawmempool", 0, "verbose" },
+ { "estimatefee", 0, "nblocks" },
+ { "estimatepriority", 0, "nblocks" },
+ { "estimatesmartfee", 0, "nblocks" },
+ { "estimatesmartpriority", 0, "nblocks" },
+ { "prioritisetransaction", 1, "priority_delta" },
+ { "prioritisetransaction", 2, "fee_delta" },
+ { "setban", 2, "bantime" },
+ { "setban", 3, "absolute" },
+ { "setnetworkactive", 0, "state" },
+ { "getmempoolancestors", 1, "verbose" },
+ { "getmempooldescendants", 1, "verbose" },
+ { "bumpfee", 1, "options" },
+ // Echo with conversion (For testing only)
+ { "echojson", 0, "arg0" },
+ { "echojson", 1, "arg1" },
+ { "echojson", 2, "arg2" },
+ { "echojson", 3, "arg3" },
+ { "echojson", 4, "arg4" },
+ { "echojson", 5, "arg5" },
+ { "echojson", 6, "arg6" },
+ { "echojson", 7, "arg7" },
+ { "echojson", 8, "arg8" },
+ { "echojson", 9, "arg9" },
+};
+
+class CRPCConvertTable
+{
+private:
+ std::set<std::pair<std::string, int>> members;
+ std::set<std::pair<std::string, std::string>> membersByName;
+
+public:
+ CRPCConvertTable();
+
+ bool convert(const std::string& method, int idx) {
+ return (members.count(std::make_pair(method, idx)) > 0);
+ }
+ bool convert(const std::string& method, const std::string& name) {
+ return (membersByName.count(std::make_pair(method, name)) > 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));
+ membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName,
+ vRPCConvertParams[i].paramName));
+ }
+}
+
+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];
+}
+
+UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &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;
+}
+
+UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams)
+{
+ UniValue params(UniValue::VOBJ);
+
+ for (const std::string &s: strParams) {
+ size_t pos = s.find("=");
+ if (pos == std::string::npos) {
+ throw(std::runtime_error("No '=' in named argument '"+s+"', this needs to be present for every argument (even if it is empty)"));
+ }
+
+ std::string name = s.substr(0, pos);
+ std::string value = s.substr(pos+1);
+
+ if (!rpcCvtTable.convert(strMethod, name)) {
+ // insert string value directly
+ params.pushKV(name, value);
+ } else {
+ // parse string as JSON, insert bool/number/object/etc. value
+ params.pushKV(name, ParseNonRFCJSONValue(value));
+ }
+ }
+
+ return params;
+}
diff --git a/src/rpc/client.h b/src/rpc/client.h
new file mode 100644
index 000000000..e7cf035d8
--- /dev/null
+++ b/src/rpc/client.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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.h>
+
+/** Convert positional arguments to command-specific RPC representation */
+UniValue RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams);
+
+/** Convert named arguments to command-specific RPC representation */
+UniValue RPCConvertNamedValues(const std::string& strMethod, const std::vector<std::string>& 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..e0c6708d2
--- /dev/null
+++ b/src/rpc/mining.cpp
@@ -0,0 +1,1143 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 "amount.h"
+#include "chain.h"
+#include "chainparams.h"
+#include "consensus/consensus.h"
+#include "consensus/params.h"
+#include "consensus/validation.h"
+#include "core_io.h"
+#include "init.h"
+#include "validation.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 <memory>
+#include <stdint.h>
+
+#include <boost/assign/list_of.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <univalue.h>
+
+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(pb->nHeight).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 workDiff.getdouble() / timeDiff;
+}
+
+UniValue getnetworkhashps(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 2)
+ throw runtime_error(
+ "getnetworkhashps ( nblocks 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. nblocks (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(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1);
+}
+
+UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+{
+ // Dogecoin: Never mine witness tx
+ const bool fMineWitnessTx = false;
+ static const int nInnerLoopCount = 0x10000;
+ int nHeightStart = 0;
+ int nHeightEnd = 0;
+ int nHeight = 0;
+
+ { // 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)
+ {
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript, fMineWitnessTx));
+ 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);
+ }
+ // Dogecoin: Don't mine Aux blocks in regtest
+ //CAuxPow::initAuxPow(*pblock);
+ //CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock;
+ while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetPoWHash(), pblock->nBits, Params().GetConsensus(nHeight))) {
+ ++pblock->nNonce;
+ --nMaxTries;
+ }
+ if (nMaxTries == 0) {
+ break;
+ }
+ if (pblock->nNonce == nInnerLoopCount) {
+ continue;
+ }
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ if (!ProcessNewBlock(Params(), shared_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 if the script came from the wallet
+ if (keepScript)
+ {
+ coinbaseScript->KeepScript();
+ }
+ }
+ return blockHashes;
+}
+
+UniValue generate(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw runtime_error(
+ "generate nblocks ( maxtries )\n"
+ "\nMine up to nblocks blocks immediately (before the RPC call returns)\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
+ "2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
+ "\nResult:\n"
+ "[ blockhashes ] (array) hashes of blocks generated\n"
+ "\nExamples:\n"
+ "\nGenerate 11 blocks\n"
+ + HelpExampleCli("generate", "11")
+ );
+
+ int nGenerate = request.params[0].get_int();
+ uint64_t nMaxTries = 1000000;
+ if (request.params.size() > 1) {
+ nMaxTries = request.params[1].get_int();
+ }
+
+ boost::shared_ptr<CReserveScript> 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)");
+
+ return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true);
+}
+
+UniValue generatetoaddress(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
+ throw runtime_error(
+ "generatetoaddress nblocks address (maxtries)\n"
+ "\nMine blocks immediately to a specified address (before the RPC call returns)\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, required) How many blocks are generated immediately.\n"
+ "2. address (string, required) The address to send the newly generated dogecoin to.\n"
+ "3. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
+ "\nResult:\n"
+ "[ blockhashes ] (array) hashes of blocks generated\n"
+ "\nExamples:\n"
+ "\nGenerate 11 blocks to myaddress\n"
+ + HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
+ );
+
+ int nGenerate = request.params[0].get_int();
+ uint64_t nMaxTries = 1000000;
+ if (request.params.size() > 2) {
+ nMaxTries = request.params[2].get_int();
+ }
+
+ CBitcoinAddress address(request.params[1].get_str());
+ if (!address.IsValid())
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
+
+ boost::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript());
+ coinbaseScript->reserveScript = GetScriptForDestination(address.Get());
+
+ return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
+}
+
+UniValue getmininginfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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"
+ " \"currentblockweight\": nnn, (numeric) The last block weight\n"
+ " \"currentblocktx\": nnn, (numeric) The last block transaction\n"
+ " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
+ " \"errors\": \"...\" (string) Current errors\n"
+ " \"networkhashps\": nnn, (numeric) The network hashes per second\n"
+ " \"pooledtx\": n (numeric) The size of the mempool\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("currentblockweight", (uint64_t)nLastBlockWeight));
+ 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("networkhashps", getnetworkhashps(request)));
+ obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
+ obj.push_back(Pair("chain", Params().NetworkIDString()));
+ return obj;
+}
+
+
+// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
+UniValue prioritisetransaction(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 3)
+ throw runtime_error(
+ "prioritisetransaction <txid> <priority delta> <fee delta>\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(request.params[0].get_str(), "txid");
+ CAmount nAmount = request.params[2].get_int64();
+
+ mempool.PrioritiseTransaction(hash, request.params[0].get_str(), request.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?";
+}
+
+std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
+ const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ std::string s = vbinfo.name;
+ if (!vbinfo.gbt_force) {
+ s.insert(s.begin(), '!');
+ }
+ return s;
+}
+
+UniValue getblocktemplate(const JSONRPCRequest& request)
+{
+ // Dogecoin: Never mine witness tx
+ const bool fMineWitnessTx = false;
+ if (request.fHelp || request.params.size() > 1)
+ throw runtime_error(
+ "getblocktemplate ( TemplateRequest )\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"
+ "For full specification, see BIPs 22, 23, 9, and 145:\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
+
+ "\nArguments:\n"
+ "1. template_request (json object, optional) A json object in the following spec\n"
+ " {\n"
+ " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), 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"
+ " \"rules\":[ (array, optional) A list of strings\n"
+ " \"support\" (string) client side supported softfork deployment\n"
+ " ,...\n"
+ " ]\n"
+ " }\n"
+ "\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"version\" : n, (numeric) The preferred block version\n"
+ " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n"
+ " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n"
+ " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n"
+ " ,...\n"
+ " },\n"
+ " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\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"
+ " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n"
+ " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\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 SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
+ " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\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\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\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"
+ " \"weightlimit\" : n, (numeric) limit of block weight\n"
+ " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"bits\" : \"xxxxxxxx\", (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;
+ std::set<std::string> setClientRules;
+ int64_t nMaxVersionPreVB = -1;
+ if (request.params.size() > 0)
+ {
+ const UniValue& oparam = request.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);
+ }
+
+ const UniValue& aClientRules = find_value(oparam, "rules");
+ if (aClientRules.isArray()) {
+ for (unsigned int i = 0; i < aClientRules.size(); ++i) {
+ const UniValue& v = aClientRules[i];
+ setClientRules.insert(v.get_str());
+ }
+ } else {
+ // NOTE: It is important that this NOT be read if versionbits is supported
+ const UniValue& uvMaxVersion = find_value(oparam, "maxversion");
+ if (uvMaxVersion.isNum()) {
+ nMaxVersionPreVB = uvMaxVersion.get_int64();
+ }
+ }
+ }
+
+ if (strMode != "template")
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Dogecoin is not connected!");
+
+ if (IsInitialBlockDownload())
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Dogecoin 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: <hashBestChain><nTransactionsUpdatedLast>
+ 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<boost::mutex> 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?
+ }
+
+ const struct BIP9DeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
+ // If the caller is indicating segwit support, then allow CreateNewBlock()
+ // to select witness transactions, after segwit activates (otherwise
+ // don't).
+ bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end();
+
+ // Update block
+ static CBlockIndex* pindexPrev;
+ static int64_t nStart;
+ static std::unique_ptr<CBlockTemplate> pblocktemplate;
+ // Cache whether the last invocation was with segwit support, to avoid returning
+ // a segwit-block to a non-segwit caller.
+ static bool fLastTemplateSupportsSegwit = true;
+ if (pindexPrev != chainActive.Tip() ||
+ (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) ||
+ fLastTemplateSupportsSegwit != fSupportsSegwit)
+ {
+ // Clear pindexPrev so future calls make a new block, despite any failures from here on
+ pindexPrev = nullptr;
+
+ // Store the pindexBest used before CreateNewBlock, to avoid races
+ nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+ CBlockIndex* pindexPrevNew = chainActive.Tip();
+ nStart = GetTime();
+ fLastTemplateSupportsSegwit = fSupportsSegwit;
+
+ // Create new block
+ CScript scriptDummy = CScript() << OP_TRUE;
+ pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fMineWitnessTx);
+ 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
+ const Consensus::Params& consensusParams = Params().GetConsensus(pindexPrev->nHeight + 1);
+
+ // Update nTime
+ UpdateTime(pblock, consensusParams, pindexPrev);
+ pblock->nNonce = 0;
+
+ // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
+ const bool fPreSegWit = (THRESHOLD_ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
+
+ UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
+
+ UniValue transactions(UniValue::VARR);
+ map<uint256, int64_t> setTxIndex;
+ int i = 0;
+ for (const auto& it : pblock->vtx) {
+ const CTransaction& tx = *it;
+ 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("txid", txHash.GetHex()));
+ entry.push_back(Pair("hash", tx.GetWitnessHash().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]));
+ int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
+ if (fPreSegWit) {
+ assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
+ nTxSigOps /= WITNESS_SCALE_FACTOR;
+ }
+ entry.push_back(Pair("sigops", nTxSigOps));
+ entry.push_back(Pair("weight", GetTransactionWeight(tx)));
+
+ 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);
+
+ UniValue aMutable(UniValue::VARR);
+ aMutable.push_back("time");
+ aMutable.push_back("transactions");
+ aMutable.push_back("prevblock");
+
+ UniValue result(UniValue::VOBJ);
+ result.push_back(Pair("capabilities", aCaps));
+
+ UniValue aRules(UniValue::VARR);
+ UniValue vbavailable(UniValue::VOBJ);
+ for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
+ Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
+ ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
+ switch (state) {
+ case THRESHOLD_DEFINED:
+ case THRESHOLD_FAILED:
+ // Not exposed to GBT at all
+ break;
+ case THRESHOLD_LOCKED_IN:
+ // Ensure bit is set in block version
+ pblock->nVersion |= VersionBitsMask(consensusParams, pos);
+ // FALL THROUGH to get vbavailable set...
+ case THRESHOLD_STARTED:
+ {
+ const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit));
+ if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
+ if (!vbinfo.gbt_force) {
+ // If the client doesn't support this, don't indicate it in the [default] version
+ pblock->nVersion &= ~VersionBitsMask(consensusParams, pos);
+ }
+ }
+ break;
+ }
+ case THRESHOLD_ACTIVE:
+ {
+ // Add to rules only
+ const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
+ aRules.push_back(gbt_vb_name(pos));
+ if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
+ // Not supported by the client; make sure it's safe to proceed
+ if (!vbinfo.gbt_force) {
+ // If we do anything other than throw an exception here, be sure version/force isn't sent to old clients
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name));
+ }
+ }
+ break;
+ }
+ }
+ }
+ result.push_back(Pair("version", pblock->nVersion));
+ result.push_back(Pair("rules", aRules));
+ result.push_back(Pair("vbavailable", vbavailable));
+ result.push_back(Pair("vbrequired", int(0)));
+
+ if (nMaxVersionPreVB >= 2) {
+ // If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here
+ // Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks
+ // This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated
+ // Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated
+ aMutable.push_back("version/force");
+ }
+
+ 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"));
+ int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST;
+ if (fPreSegWit) {
+ assert(nSigOpLimit % WITNESS_SCALE_FACTOR == 0);
+ nSigOpLimit /= WITNESS_SCALE_FACTOR;
+ }
+ result.push_back(Pair("sigoplimit", nSigOpLimit));
+ if (fPreSegWit) {
+ result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_BASE_SIZE));
+ } else {
+ result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SERIALIZED_SIZE));
+ result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT));
+ }
+ 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)));
+
+ if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) {
+ result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())));
+ }
+
+ 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.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. \"parameters\" (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\"")
+ );
+
+ std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
+ CBlock& block = *blockptr;
+ if (!DecodeHexBlk(block, request.params[0].get_str()))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
+
+ if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase");
+ }
+
+ 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;
+ }
+ }
+
+ {
+ LOCK(cs_main);
+ BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
+ if (mi != mapBlockIndex.end()) {
+ int nHeight = chainActive.Height() + 1;
+ UpdateUncommittedBlockStructures(block, mi->second, Params().GetConsensus(nHeight));
+ }
+ }
+
+ submitblock_StateCatcher sc(block.GetHash());
+ RegisterValidationInterface(&sc);
+ bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL);
+ UnregisterValidationInterface(&sc);
+ if (fBlockPresent)
+ {
+ if (fAccepted && !sc.found)
+ return "duplicate-inconclusive";
+ return "duplicate";
+ }
+ if (!sc.found)
+ return "inconclusive";
+ return BIP22ValidationResult(sc.state);
+}
+
+UniValue estimatefee(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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. Uses virtual transaction size of transaction\n"
+ "as defined in BIP 141 (witness data is discounted).\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, required)\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"
+ "-1 is always returned for nblocks == 1 as it is impossible to calculate\n"
+ "a fee that is high enough to get reliably included in the next block.\n"
+ "\nExample:\n"
+ + HelpExampleCli("estimatefee", "6")
+ );
+
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = request.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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "estimatepriority nblocks\n"
+ "\nDEPRECATED. Estimates the approximate priority a zero-fee transaction needs to begin\n"
+ "confirmation within nblocks blocks.\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric, required)\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(request.params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = request.params[0].get_int();
+ if (nBlocks < 1)
+ nBlocks = 1;
+
+ return mempool.estimatePriority(nBlocks);
+}
+
+UniValue estimatesmartfee(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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. Uses virtual transaction size as defined\n"
+ "in BIP 141 (witness data is discounted).\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(request.params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = request.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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "estimatesmartpriority nblocks\n"
+ "\nDEPRECATED. WARNING: 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, required)\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(request.params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = request.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;
+}
+
+/* ************************************************************************** */
+/* Merge mining. */
+
+UniValue getauxblockbip22(const JSONRPCRequest& request)
+{
+ if (request.fHelp
+ || (request.params.size() != 0 && request.params.size() != 2))
+ throw std::runtime_error(
+ "getauxblock (hash auxpow)\n"
+ "\nCreate or submit a merge-mined block.\n"
+ "\nWithout arguments, create a new block and return information\n"
+ "required to merge-mine it. With arguments, submit a solved\n"
+ "auxpow for a previously returned block.\n"
+ "\nArguments:\n"
+ "1. hash (string, optional) hash of the block to submit\n"
+ "2. auxpow (string, optional) serialised auxpow found\n"
+ "\nResult (without arguments):\n"
+ "{\n"
+ " \"hash\" (string) hash of the created block\n"
+ " \"chainid\" (numeric) chain ID for this block\n"
+ " \"previousblockhash\" (string) hash of the previous block\n"
+ " \"coinbasevalue\" (numeric) value of the block's coinbase\n"
+ " \"bits\" (string) compressed target of the block\n"
+ " \"height\" (numeric) height of the block\n"
+ " \"target\" (string) target in reversed byte order\n"
+ "}\n"
+ "\nResult (with arguments):\n"
+ "xxxxx (boolean) whether the submitted block was correct\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getauxblock", "")
+ + HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"")
+ + HelpExampleRpc("getauxblock", "")
+ );
+
+ boost::shared_ptr<CReserveScript> 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.size())
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && !Params().MineBlocksOnDemand())
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Dogecoin is not connected!");
+
+ if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand())
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
+ "Dogecoin is downloading blocks...");
+
+ /* This should never fail, since the chain is already
+ past the point of merge-mining start. Check nevertheless. */
+ {
+ LOCK(cs_main);
+ if (Params().GetConsensus(chainActive.Height() + 1).fAllowLegacyBlocks)
+ throw std::runtime_error("getauxblock method is not yet available");
+ }
+
+ /* The variables below are used to keep track of created and not yet
+ submitted auxpow blocks. Lock them to be sure even for multiple
+ RPC threads running in parallel. */
+ static CCriticalSection cs_auxblockCache;
+ LOCK(cs_auxblockCache);
+ static std::map<uint256, CBlock*> mapNewBlock;
+ static std::vector<std::unique_ptr<CBlockTemplate>> vNewBlockTemplate;
+
+ /* Create a new block? */
+ if (request.params.size() == 0)
+ {
+ static unsigned nTransactionsUpdatedLast;
+ static const CBlockIndex* pindexPrev = nullptr;
+ static uint64_t nStart;
+ static CBlock* pblock = nullptr;
+ static unsigned nExtraNonce = 0;
+
+ // Update block
+ // Dogecoin: Never mine witness tx
+ const bool fMineWitnessTx = false;
+ {
+ LOCK(cs_main);
+ if (pindexPrev != chainActive.Tip()
+ || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast
+ && GetTime() - nStart > 60))
+ {
+ if (pindexPrev != chainActive.Tip())
+ {
+ // Clear old blocks since they're obsolete now.
+ mapNewBlock.clear();
+ vNewBlockTemplate.clear();
+ pblock = nullptr;
+ }
+
+ // Create new block with nonce = 0 and extraNonce = 1
+ std::unique_ptr<CBlockTemplate> newBlock(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript, fMineWitnessTx));
+ if (!newBlock)
+ throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory");
+
+ // Update state only when CreateNewBlock succeeded
+ nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+ pindexPrev = chainActive.Tip();
+ nStart = GetTime();
+
+ // Finalise it by setting the version and building the merkle root
+ IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce);
+ newBlock->block.SetAuxpowFlag(true);
+
+ // Save
+ pblock = &newBlock->block;
+ mapNewBlock[pblock->GetHash()] = pblock;
+ vNewBlockTemplate.push_back(std::move(newBlock));
+ }
+ }
+
+ arith_uint256 target;
+ bool fNegative, fOverflow;
+ target.SetCompact(pblock->nBits, &fNegative, &fOverflow);
+ if (fNegative || fOverflow || target == 0)
+ throw std::runtime_error("invalid difficulty bits in block");
+
+ UniValue result(UniValue::VOBJ);
+ result.push_back(Pair("hash", pblock->GetHash().GetHex()));
+ result.push_back(Pair("chainid", pblock->GetChainId()));
+ result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
+ result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue));
+ result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
+ result.push_back(Pair("height", static_cast<int64_t> (pindexPrev->nHeight + 1)));
+ result.push_back(Pair("target", HexStr(BEGIN(target), END(target))));
+
+ return result;
+ }
+
+ /* Submit a block instead. Note that this need not lock cs_main,
+ since ProcessNewBlock below locks it instead. */
+
+ assert(request.params.size() == 2);
+ uint256 hash;
+ hash.SetHex(request.params[0].get_str());
+
+ const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash);
+ if (mit == mapNewBlock.end())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown");
+ CBlock& block = *mit->second;
+
+ const std::vector<unsigned char> vchAuxPow
+ = ParseHex(request.params[1].get_str());
+ CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
+ CAuxPow pow;
+ ss >> pow;
+ block.SetAuxpow(new CAuxPow(pow));
+ assert(block.GetHash() == hash);
+
+ submitblock_StateCatcher sc(block.GetHash());
+ RegisterValidationInterface(&sc);
+ std::shared_ptr<const CBlock> shared_block
+ = std::make_shared<const CBlock>(block);
+ bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr);
+ UnregisterValidationInterface(&sc);
+
+ if (fAccepted)
+ coinbaseScript->KeepScript();
+
+ return BIP22ValidationResult(sc.state);
+}
+
+UniValue getauxblock(const JSONRPCRequest& request)
+{
+ const UniValue response = getauxblockbip22(request);
+
+ // this is a request for a new blocktemplate: return response
+ if (request.params.size() == 0)
+ return response;
+
+ // this is a new block submission: return bool
+ return response.isNull();
+}
+
+/* ************************************************************************** */
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafeMode
+ // --------------------- ------------------------ ----------------------- ----------
+ { "mining", "getnetworkhashps", &getnetworkhashps, true, {"nblocks","height"} },
+ { "mining", "getmininginfo", &getmininginfo, true, {} },
+ { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} },
+ { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} },
+ { "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} },
+ { "mining", "getauxblock", &getauxblock, true, {"hash", "auxpow"} },
+
+ { "generating", "generate", &generate, true, {"nblocks","maxtries"} },
+ { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} },
+
+ { "util", "estimatefee", &estimatefee, true, {"nblocks"} },
+ { "util", "estimatepriority", &estimatepriority, true, {"nblocks"} },
+ { "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks"} },
+ { "util", "estimatesmartpriority", &estimatesmartpriority, true, {"nblocks"} },
+};
+
+void RegisterMiningRPCCommands(CRPCTable &t)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
new file mode 100644
index 000000000..dd0b8b817
--- /dev/null
+++ b/src/rpc/misc.cpp
@@ -0,0 +1,532 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 "validation.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 <stdint.h>
+
+#include <boost/assign/list_of.hpp>
+
+#include <univalue.h>
+
+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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getinfo\n"
+ "\nDEPRECATED. 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 dogecoin 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 Unix 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()));
+ if(g_connman)
+ obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
+ obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())));
+ obj.push_back(Pair("difficulty", (double)GetDifficulty()));
+ obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET));
+#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<UniValue>
+{
+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<CTxDestination> 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "validateaddress \"address\"\n"
+ "\nReturn information about the given dogecoin address.\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The dogecoin 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\" : \"address\", (string) The dogecoin 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"
+ " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
+ " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("validateaddress", "\"DTaXouBvXCDfViRZzSCaVNQBAyt1D9zThT\"")
+ + HelpExampleRpc("validateaddress", "\"DTaXouBvXCDfViRZzSCaVNQBAyt1D9zThT\"")
+ );
+
+#ifdef ENABLE_WALLET
+ LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
+#else
+ LOCK(cs_main);
+#endif
+
+ CBitcoinAddress address(request.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));
+ CKeyID keyID;
+ if (pwalletMain) {
+ const auto& meta = pwalletMain->mapKeyMetadata;
+ auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end();
+ if (it == meta.end()) {
+ it = meta.find(CScriptID(scriptPubKey));
+ }
+ if (it != meta.end()) {
+ ret.push_back(Pair("timestamp", it->second.nCreateTime));
+ if (!it->second.hdKeypath.empty()) {
+ ret.push_back(Pair("hdkeypath", it->second.hdKeypath));
+ ret.push_back(Pair("hdmasterkeyid", it->second.hdMasterKeyID.GetHex()));
+ }
+ }
+ }
+#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<CPubKey> 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.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 dogecoin addresses or hex-encoded public keys\n"
+ " [\n"
+ " \"key\" (string) dogecoin 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 \"[\\\"DB1Y8APJPE9K1kfYeuGPcbtyK7uruTNFa9\\\",\\\"DB9yDzihrJJBZ7mEUuGRAz7bJbh5jQJexj\\\"]\"") +
+ "\nAs a json rpc call\n"
+ + HelpExampleRpc("createmultisig", "2, \"[\\\"DB1Y8APJPE9K1kfYeuGPcbtyK7uruTNFa9\\\",\\\"DB9yDzihrJJBZ7mEUuGRAz7bJbh5jQJexj\\\"]\"")
+ ;
+ throw runtime_error(msg);
+ }
+
+ // Construct using pay-to-script-hash:
+ CScript inner = _createmultisig_redeemScript(request.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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 3)
+ throw runtime_error(
+ "verifymessage \"address\" \"signature\" \"message\"\n"
+ "\nVerify a signed message\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The dogecoin 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", "\"DH9fPpKHLiP5eaAD3pXxxUZmPktGNGTFp6\" \"my message\"") +
+ "\nVerify the signature\n"
+ + HelpExampleCli("verifymessage", "\"DH9fPpKHLiP5eaAD3pXxxUZmPktGNGTFp6\" \"signature\" \"my message\"") +
+ "\nAs json rpc\n"
+ + HelpExampleRpc("verifymessage", "\"DH9fPpKHLiP5eaAD3pXxxUZmPktGNGTFp6\", \"signature\", \"my message\"")
+ );
+
+ LOCK(cs_main);
+
+ string strAddress = request.params[0].get_str();
+ string strSign = request.params[1].get_str();
+ string strMessage = request.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<unsigned char> 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 signmessagewithprivkey(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 2)
+ throw runtime_error(
+ "signmessagewithprivkey \"privkey\" \"message\"\n"
+ "\nSign a message with the private key of an address\n"
+ "\nArguments:\n"
+ "1. \"privkey\" (string, required) The private key to sign the message with.\n"
+ "2. \"message\" (string, required) The message to create a signature of.\n"
+ "\nResult:\n"
+ "\"signature\" (string) The signature of the message encoded in base 64\n"
+ "\nExamples:\n"
+ "\nCreate the signature\n"
+ + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
+ "\nVerify the signature\n"
+ + HelpExampleCli("verifymessage", "\"DH9fPpKHLiP5eaAD3pXxxUZmPktGNGTFp6\" \"signature\" \"my message\"") +
+ "\nAs json rpc\n"
+ + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
+ );
+
+ string strPrivkey = request.params[0].get_str();
+ string strMessage = request.params[1].get_str();
+
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(strPrivkey);
+ 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");
+
+ CHashWriter ss(SER_GETHASH, 0);
+ ss << strMessageMagic;
+ ss << strMessage;
+
+ vector<unsigned char> vchSig;
+ if (!key.SignCompact(ss.GetHash(), vchSig))
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
+
+ return EncodeBase64(&vchSig[0], vchSig.size());
+}
+
+UniValue setmocktime(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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");
+
+ // For now, don't change mocktime if we're in the middle of validation, as
+ // this could have an effect on mempool time-based eviction, as well as
+ // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
+ // TODO: figure out the right way to synchronize around mocktime, and
+ // ensure all callsites of GetTime() are accessing this safely.
+ LOCK(cs_main);
+
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
+ SetMockTime(request.params[0].get_int64());
+
+ return NullUniValue;
+}
+
+static UniValue RPCLockedMemoryInfo()
+{
+ LockedPool::Stats stats = LockedPoolManager::Instance().stats();
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("used", uint64_t(stats.used)));
+ obj.push_back(Pair("free", uint64_t(stats.free)));
+ obj.push_back(Pair("total", uint64_t(stats.total)));
+ obj.push_back(Pair("locked", uint64_t(stats.locked)));
+ obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
+ obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free)));
+ return obj;
+}
+
+UniValue getmemoryinfo(const JSONRPCRequest& request)
+{
+ /* Please, avoid using the word "pool" here in the RPC interface or help,
+ * as users will undoubtedly confuse it with the other "memory pool"
+ */
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getmemoryinfo\n"
+ "Returns an object containing information about memory usage.\n"
+ "\nResult:\n"
+ "{\n"
+ " \"locked\": { (json object) Information about locked memory manager\n"
+ " \"used\": xxxxx, (numeric) Number of bytes used\n"
+ " \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
+ " \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
+ " \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
+ " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
+ " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
+ " }\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getmemoryinfo", "")
+ + HelpExampleRpc("getmemoryinfo", "")
+ );
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
+ return obj;
+}
+
+UniValue echo(const JSONRPCRequest& request)
+{
+ if (request.fHelp)
+ throw runtime_error(
+ "echo|echojson \"message\" ...\n"
+ "\nSimply echo back the input arguments. This command is for testing.\n"
+ "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in"
+ "dogecoin-cli and the GUI. There is no server-side difference."
+ );
+
+ return request.params;
+}
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafeMode
+ // --------------------- ------------------------ ----------------------- ----------
+ { "control", "getinfo", &getinfo, true, {} }, /* uses wallet if enabled */
+ { "control", "getmemoryinfo", &getmemoryinfo, true, {} },
+ { "util", "validateaddress", &validateaddress, true, {"address"} }, /* uses wallet if enabled */
+ { "util", "createmultisig", &createmultisig, true, {"nrequired","keys"} },
+ { "util", "verifymessage", &verifymessage, true, {"address","signature","message"} },
+ { "util", "signmessagewithprivkey", &signmessagewithprivkey, true, {"privkey","message"} },
+
+ /* Not shown in help */
+ { "hidden", "setmocktime", &setmocktime, true, {"timestamp"}},
+ { "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+};
+
+void RegisterMiscRPCCommands(CRPCTable &t)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
new file mode 100644
index 000000000..5bc3b7224
--- /dev/null
+++ b/src/rpc/net.cpp
@@ -0,0 +1,626 @@
+// Copyright (c) 2009-2016 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 "validation.h"
+#include "net.h"
+#include "net_processing.h"
+#include "netbase.h"
+#include "policy/policy.h"
+#include "protocol.h"
+#include "sync.h"
+#include "timedata.h"
+#include "ui_interface.h"
+#include "util.h"
+#include "utilstrencodings.h"
+#include "version.h"
+
+#include <boost/foreach.hpp>
+
+#include <univalue.h>
+
+using namespace std;
+
+UniValue getconnectioncount(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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", "")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+}
+
+UniValue ping(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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", "")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ // Request that each node send a ping during next message processing pass
+ g_connman->ForEachNode([](CNode* pnode) {
+ pnode->fPingQueued = true;
+ });
+ return NullUniValue;
+}
+
+UniValue getpeerinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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 (if available)\n"
+ " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n"
+ " \"pingwait\": n, (numeric) ping wait (if non-zero)\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"
+ " \"addnode\": true|false, (boolean) Whether connection was due to addnode and is using an addnode slot\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"
+ " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\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", "")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ vector<CNodeStats> vstats;
+ g_connman->GetNodeStats(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));
+ if (stats.dPingTime > 0.0)
+ obj.push_back(Pair("pingtime", stats.dPingTime));
+ if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6)
+ obj.push_back(Pair("minping", stats.dMinPing));
+ 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 modifying 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("addnode", stats.fAddnode));
+ 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 JSONRPCRequest& request)
+{
+ string strCommand;
+ if (request.params.size() == 2)
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.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:22556\" \"onetry\"")
+ + HelpExampleRpc("addnode", "\"192.168.0.6:22556\", \"onetry\"")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ string strNode = request.params[0].get_str();
+
+ if (strCommand == "onetry")
+ {
+ CAddress addr;
+ g_connman->OpenNetworkConnection(addr, false, NULL, strNode.c_str());
+ return NullUniValue;
+ }
+
+ if (strCommand == "add")
+ {
+ if(!g_connman->AddNode(strNode))
+ throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
+ }
+ else if(strCommand == "remove")
+ {
+ if(!g_connman->RemoveAddedNode(strNode))
+ throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
+ }
+
+ return NullUniValue;
+}
+
+UniValue disconnectnode(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "disconnectnode \"address\" \n"
+ "\nImmediately disconnects from the specified node.\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The IP address/port of the node\n"
+ "\nExamples:\n"
+ + HelpExampleCli("disconnectnode", "\"192.168.0.6:22556\"")
+ + HelpExampleRpc("disconnectnode", "\"192.168.0.6:22556\"")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ bool ret = g_connman->DisconnectNode(request.params[0].get_str());
+ if (!ret)
+ throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
+
+ return NullUniValue;
+}
+
+UniValue getaddednodeinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 1)
+ throw runtime_error(
+ "getaddednodeinfo ( \"node\" )\n"
+ "\nReturns information about the given added node, or all added nodes\n"
+ "(note that onetry addnodes are not listed here)\n"
+ "\nArguments:\n"
+ "1. \"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 or name (as provided to addnode)\n"
+ " \"connected\" : true|false, (boolean) If connected\n"
+ " \"addresses\" : [ (list of objects) Only when connected = true\n"
+ " {\n"
+ " \"address\" : \"192.168.0.201:22556\", (string) The dogecoin server IP and port we're connected to\n"
+ " \"connected\" : \"outbound\" (string) connection, inbound or outbound\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\"")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ std::vector<AddedNodeInfo> vInfo = g_connman->GetAddedNodeInfo();
+
+ if (request.params.size() == 1) {
+ bool found = false;
+ for (const AddedNodeInfo& info : vInfo) {
+ if (info.strAddedNode == request.params[0].get_str()) {
+ vInfo.assign(1, info);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
+ }
+ }
+
+ UniValue ret(UniValue::VARR);
+
+ for (const AddedNodeInfo& info : vInfo) {
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("addednode", info.strAddedNode));
+ obj.push_back(Pair("connected", info.fConnected));
+ UniValue addresses(UniValue::VARR);
+ if (info.fConnected) {
+ UniValue address(UniValue::VOBJ);
+ address.push_back(Pair("address", info.resolvedAddress.ToString()));
+ address.push_back(Pair("connected", info.fInbound ? "inbound" : "outbound"));
+ addresses.push_back(address);
+ }
+ obj.push_back(Pair("addresses", addresses));
+ ret.push_back(obj);
+ }
+
+ return ret;
+}
+
+UniValue getnettotals(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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) Current UNIX time in milliseconds\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", "")
+ );
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv()));
+ obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent()));
+ obj.push_back(Pair("timemillis", GetTimeMillis()));
+
+ UniValue outboundLimit(UniValue::VOBJ);
+ outboundLimit.push_back(Pair("timeframe", g_connman->GetMaxOutboundTimeframe()));
+ outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget()));
+ outboundLimit.push_back(Pair("target_reached", g_connman->OutboundTargetReached(false)));
+ outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true)));
+ outboundLimit.push_back(Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()));
+ outboundLimit.push_back(Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()));
+ obj.push_back(Pair("uploadtarget", outboundLimit));
+ return obj;
+}
+
+static UniValue GetNetworksInfo()
+{
+ UniValue networks(UniValue::VARR);
+ for(int n=0; n<NET_MAX; ++n)
+ {
+ enum Network network = static_cast<enum Network>(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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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"
+ " \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n"
+ " \"timeoffset\": xxxxx, (numeric) the time offset\n"
+ " \"connections\": xxxxx, (numeric) the number of connections\n"
+ " \"networkactive\": true|false, (bool) whether p2p networking is enabled\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"
+ " \"proxy_randomize_credentials\": true|false, (string) Whether randomized credentials are used\n"
+ " }\n"
+ " ,...\n"
+ " ],\n"
+ " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n"
+ " \"incrementalfee\": x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement 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\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));
+ if(g_connman)
+ obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
+ obj.push_back(Pair("localrelay", fRelayTxes));
+ obj.push_back(Pair("timeoffset", GetTimeOffset()));
+ if (g_connman) {
+ obj.push_back(Pair("networkactive", g_connman->GetNetworkActive()));
+ obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
+ }
+ obj.push_back(Pair("networks", GetNetworksInfo()));
+ obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
+ obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.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 JSONRPCRequest& request)
+{
+ string strCommand;
+ if (request.params.size() >= 2)
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.params.size() < 2 ||
+ (strCommand != "add" && strCommand != "remove"))
+ throw runtime_error(
+ "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n"
+ "\nAttempts add or remove a IP/Subnet from the banned list.\n"
+ "\nArguments:\n"
+ "1. \"subnet\" (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")
+ );
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ CSubNet subNet;
+ CNetAddr netAddr;
+ bool isSubnet = false;
+
+ if (request.params[0].get_str().find("/") != string::npos)
+ isSubnet = true;
+
+ if (!isSubnet) {
+ CNetAddr resolved;
+ LookupHost(request.params[0].get_str().c_str(), resolved, false);
+ netAddr = resolved;
+ }
+ else
+ LookupSubNet(request.params[0].get_str().c_str(), subNet);
+
+ if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
+ throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet");
+
+ if (strCommand == "add")
+ {
+ if (isSubnet ? g_connman->IsBanned(subNet) : g_connman->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 (request.params.size() >= 3 && !request.params[2].isNull())
+ banTime = request.params[2].get_int64();
+
+ bool absolute = false;
+ if (request.params.size() == 4 && request.params[3].isTrue())
+ absolute = true;
+
+ isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
+ }
+ else if(strCommand == "remove")
+ {
+ if (!( isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr) ))
+ throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned.");
+ }
+ return NullUniValue;
+}
+
+UniValue listbanned(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "listbanned\n"
+ "\nList all banned IPs/Subnets.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("listbanned", "")
+ + HelpExampleRpc("listbanned", "")
+ );
+
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ banmap_t banMap;
+ g_connman->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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "clearbanned\n"
+ "\nClear all banned IPs.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("clearbanned", "")
+ + HelpExampleRpc("clearbanned", "")
+ );
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ g_connman->ClearBanned();
+
+ return NullUniValue;
+}
+
+UniValue setnetworkactive(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw runtime_error(
+ "setnetworkactive true|false\n"
+ "\nDisable/enable all p2p network activity.\n"
+ "\nArguments:\n"
+ "1. \"state\" (boolean, required) true to enable networking, false to disable\n"
+ );
+ }
+
+ if (!g_connman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+
+ g_connman->SetNetworkActive(request.params[0].get_bool());
+
+ return g_connman->GetNetworkActive();
+}
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafeMode
+ // --------------------- ------------------------ ----------------------- ----------
+ { "network", "getconnectioncount", &getconnectioncount, true, {} },
+ { "network", "ping", &ping, true, {} },
+ { "network", "getpeerinfo", &getpeerinfo, true, {} },
+ { "network", "addnode", &addnode, true, {"node","command"} },
+ { "network", "disconnectnode", &disconnectnode, true, {"address"} },
+ { "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} },
+ { "network", "getnettotals", &getnettotals, true, {} },
+ { "network", "getnetworkinfo", &getnetworkinfo, true, {} },
+ { "network", "setban", &setban, true, {"subnet", "command", "bantime", "absolute"} },
+ { "network", "listbanned", &listbanned, true, {} },
+ { "network", "clearbanned", &clearbanned, true, {} },
+ { "network", "setnetworkactive", &setnetworkactive, true, {"state"} },
+};
+
+void RegisterNetRPCCommands(CRPCTable &t)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
new file mode 100644
index 000000000..dc710d939
--- /dev/null
+++ b/src/rpc/protocol.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 <stdint.h>
+#include <fstream>
+
+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
+ */
+
+UniValue JSONRPCRequestObj(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;
+}
+
+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)
+{
+ const size_t COOKIE_SIZE = 32;
+ unsigned char rand_pwd[COOKIE_SIZE];
+ GetRandBytes(rand_pwd, COOKIE_SIZE);
+ std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd, rand_pwd+COOKIE_SIZE);
+
+ /** 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..47e56e712
--- /dev/null
+++ b/src/rpc/protocol.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 <list>
+#include <map>
+#include <stdint.h>
+#include <string>
+#include <boost/filesystem.hpp>
+
+#include <univalue.h>
+
+//! 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
+ RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found
+
+ //! 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
+};
+
+UniValue JSONRPCRequestObj(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..0c3817729
--- /dev/null
+++ b/src/rpc/rawtransaction.cpp
@@ -0,0 +1,950 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 "validation.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 <stdint.h>
+
+#include <boost/assign/list_of.hpp>
+
+#include <univalue.h>
+
+using namespace std;
+
+void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
+{
+ txnouttype type;
+ vector<CTxDestination> 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("hash", tx.GetWitnessHash().GetHex()));
+ entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
+ entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx)));
+ entry.push_back(Pair("version", tx.nVersion));
+ entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
+
+ UniValue vin(UniValue::VARR);
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ const CTxIn& txin = tx.vin[i];
+ 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));
+ }
+ if (tx.HasWitness()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) {
+ std::vector<unsigned char> item = tx.vin[i].scriptWitness.stack[j];
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
+ }
+ in.push_back(Pair("txinwitness", txinwitness));
+ }
+ 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw runtime_error(
+ "getrawtransaction \"txid\" ( verbose )\n"
+
+ "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
+ "enabled, it also works for blockchain transactions.\n"
+ "DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
+
+ "\nReturn the raw transaction data.\n"
+ "\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
+ "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n"
+
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id\n"
+ "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
+
+ "\nResult (if verbose is not set or set to false):\n"
+ "\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
+
+ "\nResult (if verbose is set to true):\n"
+ "{\n"
+ " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
+ " \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
+ " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
+ " \"size\" : n, (numeric) The serialized transaction size\n"
+ " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\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"
+ " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\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"
+ " \"address\" (string) dogecoin 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\" true")
+ + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
+ );
+
+ LOCK(cs_main);
+
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
+
+ // Accept either a bool (true) or a num (>=1) to indicate verbose output.
+ bool fVerbose = false;
+ if (request.params.size() > 1) {
+ if (request.params[1].isNum()) {
+ if (request.params[1].get_int() != 0) {
+ fVerbose = true;
+ }
+ }
+ else if(request.params[1].isBool()) {
+ if(request.params[1].isTrue()) {
+ fVerbose = true;
+ }
+ }
+ else {
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean.");
+ }
+ }
+
+ CTransactionRef tx;
+ uint256 hashBlock;
+ // Dogecoin: Is this the best value for consensus height?
+ if (!GetTransaction(hash, tx, Params().GetConsensus(0), hashBlock, true))
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction"
+ : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") +
+ ". Use gettransaction for wallet transactions.");
+
+ string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
+
+ if (!fVerbose)
+ return strHex;
+
+ UniValue result(UniValue::VOBJ);
+ result.push_back(Pair("hex", strHex));
+ TxToJSON(*tx, hashBlock, result);
+ return result;
+}
+
+UniValue gettxoutproof(const JSONRPCRequest& request)
+{
+ if (request.fHelp || (request.params.size() != 1 && request.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 manually (by blockhash).\n"
+ "\nArguments:\n"
+ "1. \"txids\" (string) A json array of txids to filter\n"
+ " [\n"
+ " \"txid\" (string) A transaction hash\n"
+ " ,...\n"
+ " ]\n"
+ "2. \"blockhash\" (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<uint256> setTxids;
+ uint256 oneTxid;
+ UniValue txids = request.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 (request.params.size() > 1)
+ {
+ hashBlock = uint256S(request.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)
+ {
+ CTransactionRef tx;
+ if (!GetTransaction(oneTxid, tx, Params().GetConsensus(0), 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(pblockindex->nHeight)))
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
+
+ unsigned int ntxFound = 0;
+ for (const auto& 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 | SERIALIZE_TRANSACTION_NO_WITNESS);
+ CMerkleBlock mb(block, setTxids);
+ ssMB << mb;
+ std::string strHex = HexStr(ssMB.begin(), ssMB.end());
+ return strHex;
+}
+
+UniValue verifytxoutproof(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.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(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
+ CMerkleBlock merkleBlock;
+ ssMB >> merkleBlock;
+
+ UniValue res(UniValue::VARR);
+
+ vector<uint256> vMatch;
+ vector<unsigned int> vIndex;
+ if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.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. \"inputs\" (array, 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"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (object, required) a json object with outputs\n"
+ " {\n"
+ " \"address\": x.xxx, (numeric or string, required) The key is the dogecoin 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(request.params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
+ if (request.params[0].isNull() || request.params[1].isNull())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+
+ UniValue inputs = request.params[0].get_array();
+ UniValue sendTo = request.params[1].get_obj();
+
+ CMutableTransaction rawTx;
+
+ if (request.params.size() > 2 && !request.params[2].isNull()) {
+ int64_t nLockTime = request.params[2].get_int64();
+ if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::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<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max());
+
+ // set the sequence number if passed in the parameters object
+ const UniValue& sequenceObj = find_value(o, "sequence");
+ if (sequenceObj.isNum()) {
+ int64_t seqNr64 = sequenceObj.get_int64();
+ if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
+ else
+ nSequence = (uint32_t)seqNr64;
+ }
+
+ CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
+
+ rawTx.vin.push_back(in);
+ }
+
+ set<CBitcoinAddress> setAddress;
+ vector<string> addrList = sendTo.getKeys();
+ BOOST_FOREACH(const string& name_, addrList) {
+
+ if (name_ == "data") {
+ std::vector<unsigned char> 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 Dogecoin 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "decoderawtransaction \"hexstring\"\n"
+ "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
+
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The transaction hex string\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"txid\" : \"id\", (string) The transaction id\n"
+ " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
+ " \"size\" : n, (numeric) The transaction size\n"
+ " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\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"
+ " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\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"
+ " \"D731rRTrFydjJdZCKNzfB5go229p59GUGD\" (string) dogecoin address\n"
+ " ,...\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " ,...\n"
+ " ],\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ + HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
+ );
+
+ LOCK(cs_main);
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
+
+ CMutableTransaction mtx;
+
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+
+ UniValue result(UniValue::VOBJ);
+ TxToJSON(CTransaction(std::move(mtx)), uint256(), result);
+
+ return result;
+}
+
+UniValue decodescript(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "decodescript \"hexstring\"\n"
+ "\nDecode a hex-encoded script.\n"
+ "\nArguments:\n"
+ "1. \"hexstring\" (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) dogecoin address\n"
+ " ,...\n"
+ " ],\n"
+ " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("decodescript", "\"hexstring\"")
+ + HelpExampleRpc("decodescript", "\"hexstring\"")
+ );
+
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
+
+ UniValue r(UniValue::VOBJ);
+ CScript script;
+ if (request.params[0].get_str().size() > 0){
+ vector<unsigned char> scriptData(ParseHexV(request.params[0], "argument"));
+ script = CScript(scriptData.begin(), scriptData.end());
+ } else {
+ // Empty scripts are valid
+ }
+ ScriptPubKeyToJSON(script, r, false);
+
+ UniValue type;
+ type = find_value(r, "type");
+
+ if (type.isStr() && type.get_str() != "scripthash") {
+ // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
+ // don't return the address for a P2SH of the P2SH.
+ 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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.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 or P2WSH) redeem script\n"
+ " \"amount\": value (numeric, required) The amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"privkeys\" (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(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
+
+ vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
+ CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
+ vector<CMutableTransaction> 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 (request.params.size() > 2 && !request.params[2].isNull()) {
+ fGivenKeys = true;
+ UniValue keys = request.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 (request.params.size() > 1 && !request.params[1].isNull()) {
+ UniValue prevTxs = request.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,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ {"scriptPubKey", UniValueType(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<unsigned char> 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;
+ if (prevOut.exists("amount")) {
+ coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
+ }
+ }
+
+ // 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() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ RPCTypeCheckObj(prevOut,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ {"scriptPubKey", UniValueType(UniValue::VSTR)},
+ {"redeemScript", UniValueType(UniValue::VSTR)},
+ });
+ UniValue v = find_value(prevOut, "redeemScript");
+ if (!v.isNull()) {
+ vector<unsigned char> 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 (request.params.size() > 3 && !request.params[3].isNull()) {
+ static map<string, int> 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 = request.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);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mergedTx);
+ // 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;
+ const CAmount& amount = coins->vout[txin.prevout.n].nValue;
+
+ SignatureData sigdata;
+ // Only sign SIGHASH_SINGLE if there's a corresponding output:
+ if (!fHashSingle || (i < mergedTx.vout.size()))
+ ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
+
+ // ... and merge in other signatures:
+ BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
+ if (txv.vin.size() > i) {
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
+ }
+ }
+
+ UpdateTransaction(mergedTx, i, sigdata);
+
+ ScriptError serror = SCRIPT_ERR_OK;
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &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 JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.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(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
+
+ // parse hex string from parameter
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str()))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
+ const uint256& hashTx = tx->GetHash();
+
+ bool fLimitFree = false;
+ CAmount nMaxRawTxFee = maxTxFee;
+ if (request.params.size() > 1 && request.params[1].get_bool())
+ nMaxRawTxFee = 0;
+
+ 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, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) {
+ 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");
+ }
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ CInv inv(MSG_TX, hashTx);
+ g_connman->ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
+ return hashTx.GetHex();
+}
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafeMode
+ // --------------------- ------------------------ ----------------------- ----------
+ { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} },
+ { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime"} },
+ { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} },
+ { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} },
+ { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
+
+ { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} },
+ { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} },
+};
+
+void RegisterRawTransactionRPCCommands(CRPCTable &t)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}
diff --git a/src/rpc/register.h b/src/rpc/register.h
new file mode 100644
index 000000000..49aee2365
--- /dev/null
+++ b/src/rpc/register.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2009-2016 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_RPCREGISTER_H
+#define BITCOIN_RPCREGISTER_H
+
+/** These are in one header file to avoid creating tons of single-function
+ * headers for everything under src/rpc/ */
+class CRPCTable;
+
+/** Register block chain RPC commands */
+void RegisterBlockchainRPCCommands(CRPCTable &tableRPC);
+/** Register P2P networking RPC commands */
+void RegisterNetRPCCommands(CRPCTable &tableRPC);
+/** Register miscellaneous RPC commands */
+void RegisterMiscRPCCommands(CRPCTable &tableRPC);
+/** Register mining RPC commands */
+void RegisterMiningRPCCommands(CRPCTable &tableRPC);
+/** Register raw transaction RPC commands */
+void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC);
+
+static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
+{
+ RegisterBlockchainRPCCommands(t);
+ RegisterNetRPCCommands(t);
+ RegisterMiscRPCCommands(t);
+ RegisterMiningRPCCommands(t);
+ RegisterRawTransactionRPCCommands(t);
+}
+
+#endif
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
new file mode 100644
index 000000000..6ab5f834f
--- /dev/null
+++ b/src/rpc/server.cpp
@@ -0,0 +1,568 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 <univalue.h>
+
+#include <boost/bind.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2/signal.hpp>
+#include <boost/thread.hpp>
+#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
+
+#include <memory> // for unique_ptr
+#include <unordered_map>
+
+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. */
+static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
+
+static struct CRPCSignals
+{
+ boost::signals2::signal<void ()> Started;
+ boost::signals2::signal<void ()> Stopped;
+ boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
+ boost::signals2::signal<void (const CRPCCommand&)> PostCommand;
+} g_rpcSignals;
+
+void RPCServer::OnStarted(boost::function<void ()> slot)
+{
+ g_rpcSignals.Started.connect(slot);
+}
+
+void RPCServer::OnStopped(boost::function<void ()> slot)
+{
+ g_rpcSignals.Stopped.connect(slot);
+}
+
+void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot)
+{
+ g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
+}
+
+void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
+{
+ g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
+}
+
+void RPCTypeCheck(const UniValue& params,
+ const list<UniValue::VType>& typesExpected,
+ bool fAllowNull)
+{
+ unsigned int i = 0;
+ BOOST_FOREACH(UniValue::VType t, typesExpected)
+ {
+ if (params.size() <= i)
+ break;
+
+ const UniValue& v = params[i];
+ if (!(fAllowNull && v.isNull())) {
+ RPCTypeCheckArgument(v, t);
+ }
+ i++;
+ }
+}
+
+void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected)
+{
+ if (value.type() != typeExpected) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type())));
+ }
+}
+
+void RPCTypeCheckObj(const UniValue& o,
+ const map<string, UniValueType>& typesExpected,
+ bool fAllowNull,
+ bool fStrict)
+{
+ for (const auto& 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 (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
+ string err = strprintf("Expected type %s for %s, got %s",
+ uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+
+ if (fStrict)
+ {
+ BOOST_FOREACH(const string& k, o.getKeys())
+ {
+ if (typesExpected.count(k) == 0)
+ {
+ string err = strprintf("Unexpected key %s", k);
+ 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));
+}
+
+UniValue ValueFromAmount(const arith_uint256& amount)
+{
+ bool sign = amount < 0;
+ arith_uint256 n_abs = (sign ? -amount : amount);
+ arith_uint256 quotient = n_abs / COIN;
+ arith_uint256 remainder = n_abs - (quotient * COIN);
+ return UniValue(UniValue::VNUM,
+ strprintf("%s%d.%08d", sign ? "-" : "", (int64_t)quotient.getdouble(), (int64_t)remainder.getdouble()));
+}
+
+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+"')");
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
+ uint256 result;
+ result.SetHex(strHex);
+ return result;
+}
+uint256 ParseHashO(const UniValue& o, string strKey)
+{
+ return ParseHashV(find_value(o, strKey), strKey);
+}
+vector<unsigned char> 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<unsigned char> 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<rpcfn_type> setDone;
+ vector<pair<string, const CRPCCommand*> > vCommands;
+
+ for (map<string, const CRPCCommand*>::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
+ {
+ JSONRPCRequest jreq;
+ jreq.fHelp = true;
+ rpcfn_type pfn = pcmd->actor;
+ if (setDone.insert(pfn).second)
+ (*pfn)(jreq);
+ }
+ 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 JSONRPCRequest& jsonRequest)
+{
+ if (jsonRequest.fHelp || jsonRequest.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 (jsonRequest.params.size() > 0)
+ strCommand = jsonRequest.params[0].get_str();
+
+ return tableRPC.help(strCommand);
+}
+
+
+UniValue stop(const JSONRPCRequest& jsonRequest)
+{
+ // Accept the deprecated and ignored 'detach' boolean argument
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
+ throw runtime_error(
+ "stop\n"
+ "\nStop Dogecoin server.");
+ // Event loop will exit after current HTTP requests have been handled, so
+ // this reply will get back to the client.
+ StartShutdown();
+ return "Dogecoin server stopping";
+}
+
+/**
+ * Call Table
+ */
+static const CRPCCommand vRPCCommands[] =
+{ // category name actor (function) okSafe argNames
+ // --------------------- ------------------------ ----------------------- ------ ----------
+ /* Overall control/query calls */
+ { "control", "help", &help, true, {"command"} },
+ { "control", "stop", &stop, 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<string, const CRPCCommand*>::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<string, const CRPCCommand*>::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();
+ DeleteAuthCookie();
+ 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 JSONRPCRequest::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() || valParams.isObject())
+ params = valParams;
+ else if (valParams.isNull())
+ params = UniValue(UniValue::VARR);
+ else
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
+}
+
+static UniValue JSONRPCExecOne(const UniValue& req)
+{
+ UniValue rpc_result(UniValue::VOBJ);
+
+ JSONRPCRequest jreq;
+ try {
+ jreq.parse(req);
+
+ UniValue result = tableRPC.execute(jreq);
+ 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";
+}
+
+/**
+ * Process named arguments into a vector of positional arguments, based on the
+ * passed-in specification for the RPC call's arguments.
+ */
+static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
+{
+ JSONRPCRequest out = in;
+ out.params = UniValue(UniValue::VARR);
+ // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
+ // there is an unknown one.
+ const std::vector<std::string>& keys = in.params.getKeys();
+ const std::vector<UniValue>& values = in.params.getValues();
+ std::unordered_map<std::string, const UniValue*> argsIn;
+ for (size_t i=0; i<keys.size(); ++i) {
+ argsIn[keys[i]] = &values[i];
+ }
+ // Process expected parameters.
+ int hole = 0;
+ for (const std::string &argName: argNames) {
+ auto fr = argsIn.find(argName);
+ if (fr != argsIn.end()) {
+ for (int i = 0; i < hole; ++i) {
+ // Fill hole between specified parameters with JSON nulls,
+ // but not at the end (for backwards compatibility with calls
+ // that act based on number of specified parameters).
+ out.params.push_back(UniValue());
+ }
+ hole = 0;
+ out.params.push_back(*fr->second);
+ argsIn.erase(fr);
+ } else {
+ hole += 1;
+ }
+ }
+ // If there are still arguments in the argsIn map, this is an error.
+ if (!argsIn.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
+ }
+ // Return request with named arguments transformed to positional arguments
+ return out;
+}
+
+UniValue CRPCTable::execute(const JSONRPCRequest &request) const
+{
+ // Return immediately if in warmup
+ {
+ LOCK(cs_rpcWarmup);
+ if (fRPCInWarmup)
+ throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
+ }
+
+ // Find method
+ const CRPCCommand *pcmd = tableRPC[request.strMethod];
+ if (!pcmd)
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
+
+ g_rpcSignals.PreCommand(*pcmd);
+
+ try
+ {
+ // Execute, convert arguments to array if necessary
+ if (request.params.isObject()) {
+ return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
+ } else {
+ return pcmd->actor(request);
+ }
+ }
+ catch (const std::exception& e)
+ {
+ throw JSONRPCError(RPC_MISC_ERROR, e.what());
+ }
+
+ g_rpcSignals.PostCommand(*pcmd);
+}
+
+std::vector<std::string> CRPCTable::listCommands() const
+{
+ std::vector<std::string> commandList;
+ typedef std::map<std::string, const CRPCCommand*> commandMap;
+
+ std::transform( mapCommands.begin(), mapCommands.end(),
+ std::back_inserter(commandList),
+ boost::bind(&commandMap::value_type::first,_1) );
+ return commandList;
+}
+
+std::string HelpExampleCli(const std::string& methodname, const std::string& args)
+{
+ return "> dogecoin-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<void(void)> 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.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
+}
+
+int RPCSerializationFlags()
+{
+ int flag = 0;
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
+ flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
+ return flag;
+}
+
+CRPCTable tableRPC;
diff --git a/src/rpc/server.h b/src/rpc/server.h
new file mode 100644
index 000000000..2ea9326ac
--- /dev/null
+++ b/src/rpc/server.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 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 "arith_uint256.h"
+#include "rpc/protocol.h"
+#include "uint256.h"
+
+#include <list>
+#include <map>
+#include <stdint.h>
+#include <string>
+
+#include <boost/function.hpp>
+
+#include <univalue.h>
+
+static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1;
+
+class CRPCCommand;
+
+namespace RPCServer
+{
+ void OnStarted(boost::function<void ()> slot);
+ void OnStopped(boost::function<void ()> slot);
+ void OnPreCommand(boost::function<void (const CRPCCommand&)> slot);
+ void OnPostCommand(boost::function<void (const CRPCCommand&)> slot);
+}
+
+class CBlockIndex;
+class CNetAddr;
+
+/** Wrapper for UniValue::VType, which includes typeAny:
+ * Used to denote don't care type. Only used by RPCTypeCheckObj */
+struct UniValueType {
+ UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
+ UniValueType() : typeAny(true) {}
+ bool typeAny;
+ UniValue::VType type;
+};
+
+class JSONRPCRequest
+{
+public:
+ UniValue id;
+ std::string strMethod;
+ UniValue params;
+ bool fHelp;
+ std::string URI;
+ std::string authUser;
+
+ JSONRPCRequest() { id = NullUniValue; params = NullUniValue; fHelp = false; }
+ 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.
+ */
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false);
+
+/**
+ * Type-check one argument; throws JSONRPCError if wrong type given.
+ */
+void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected);
+
+/*
+ Check for expected keys/value types in an Object.
+*/
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull = false,
+ bool fStrict = 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<void(void)>& 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 <name> (if any).
+ */
+void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds);
+
+typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
+
+class CRPCCommand
+{
+public:
+ std::string category;
+ std::string name;
+ rpcfn_type actor;
+ bool okSafeMode;
+ std::vector<std::string> argNames;
+};
+
+/**
+ * Bitcoin RPC command dispatcher.
+ */
+class CRPCTable
+{
+private:
+ std::map<std::string, const CRPCCommand*> mapCommands;
+public:
+ CRPCTable();
+ const CRPCCommand* operator[](const std::string& name) const;
+ std::string help(const std::string& name) const;
+
+ /**
+ * Execute a method.
+ * @param request The JSONRPCRequest to execute
+ * @returns Result of the call.
+ * @throws an exception (UniValue) when an error happens.
+ */
+ UniValue execute(const JSONRPCRequest &request) const;
+
+ /**
+ * Returns a list of registered commands
+ * @returns List of registered commands.
+ */
+ std::vector<std::string> listCommands() 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<unsigned char> ParseHexV(const UniValue& v, std::string strName);
+extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
+
+extern int64_t nWalletUnlockTime;
+extern CAmount AmountFromValue(const UniValue& value);
+extern UniValue ValueFromAmount(const CAmount& amount);
+extern UniValue ValueFromAmount(const arith_uint256& 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();
+
+bool StartRPC();
+void InterruptRPC();
+void StopRPC();
+std::string JSONRPCExecBatch(const UniValue& vReq);
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
+
+// Retrieves any serialization flags requested in command line argument
+int RPCSerializationFlags();
+
+#endif // BITCOIN_RPCSERVER_H