aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/blockchain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r--src/rpc/blockchain.cpp155
1 files changed, 144 insertions, 11 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index da57973dae..6960415e2e 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -18,11 +18,14 @@
#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
+
using namespace std;
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
@@ -70,6 +73,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
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()));
@@ -98,6 +102,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
result.push_back(Pair("height", blockindex->nHeight));
result.push_back(Pair("version", block.nVersion));
+ result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
UniValue txs(UniValue::VARR);
BOOST_FOREACH(const CTransaction&tx, block.vtx)
@@ -316,6 +321,7 @@ UniValue getblockheader(const UniValue& params, bool fHelp)
" \"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"
@@ -375,6 +381,7 @@ UniValue getblock(const UniValue& params, bool fHelp)
" \"size\" : n, (numeric) The block size\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"
@@ -428,6 +435,60 @@ UniValue getblock(const UniValue& params, bool fHelp)
return blockToJSON(block, pblockindex);
}
+struct CCoinsStats
+{
+ int nHeight;
+ uint256 hashBlock;
+ uint64_t nTransactions;
+ uint64_t nTransactionOutputs;
+ uint64_t nSerializedSize;
+ uint256 hashSerialized;
+ CAmount 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)
+{
+ boost::scoped_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;
+ CAmount 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 gettxoutsetinfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -454,7 +515,7 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp)
CCoinsStats stats;
FlushStateToDisk();
- if (pcoinsTip->GetStats(stats)) {
+ 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));
@@ -604,6 +665,26 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex*
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));
+ return rv;
+}
+
UniValue getblockchaininfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -634,7 +715,15 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" },\n"
" \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\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\", \"lockedin\", \"active\", \"failed\"\n"
+ " \"bit\": xx, (numeric) the bit, 0-28, in the block version field used to signal this soft fork\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"
+ " }\n"
+ " }\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "")
@@ -657,10 +746,13 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
const Consensus::Params& consensusParams = Params().GetConsensus();
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));
+ bip9_softforks.push_back(Pair("csv", BIP9SoftForkDesc(consensusParams, Consensus::DEPLOYMENT_CSV)));
obj.push_back(Pair("softforks", softforks));
+ obj.push_back(Pair("bip9_softforks", bip9_softforks));
if (fPruneMode)
{
@@ -723,17 +815,30 @@ UniValue getchaintips(const UniValue& params, bool fHelp)
LOCK(cs_main);
- /* Build up a list of chain tips. We start with the list of all
- known blocks, and successively remove blocks that appear as pprev
- of another block. */
+ /*
+ * 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)
- setTips.insert(item.second);
- BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
{
- const CBlockIndex* pprev = item.second->pprev;
- if (pprev)
- setTips.erase(pprev);
+ 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.
@@ -837,7 +942,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
CBlockIndex* pblockindex = mapBlockIndex[hash];
- InvalidateBlock(state, Params().GetConsensus(), pblockindex);
+ InvalidateBlock(state, Params(), pblockindex);
}
if (state.IsValid()) {
@@ -889,3 +994,31 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp)
return NullUniValue;
}
+
+static const CRPCCommand commands[] =
+{ // category name actor (function) okSafeMode
+ // --------------------- ------------------------ ----------------------- ----------
+ { "blockchain", "getblockchaininfo", &getblockchaininfo, true },
+ { "blockchain", "getbestblockhash", &getbestblockhash, true },
+ { "blockchain", "getblockcount", &getblockcount, true },
+ { "blockchain", "getblock", &getblock, true },
+ { "blockchain", "getblockhash", &getblockhash, true },
+ { "blockchain", "getblockheader", &getblockheader, true },
+ { "blockchain", "getchaintips", &getchaintips, true },
+ { "blockchain", "getdifficulty", &getdifficulty, true },
+ { "blockchain", "getmempoolinfo", &getmempoolinfo, true },
+ { "blockchain", "getrawmempool", &getrawmempool, true },
+ { "blockchain", "gettxout", &gettxout, true },
+ { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
+ { "blockchain", "verifychain", &verifychain, true },
+
+ /* Not shown in help */
+ { "hidden", "invalidateblock", &invalidateblock, true },
+ { "hidden", "reconsiderblock", &reconsiderblock, true },
+};
+
+void RegisterBlockchainRPCCommands(CRPCTable &tableRPC)
+{
+ for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
+ tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
+}