diff options
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r-- | src/rpc/blockchain.cpp | 155 |
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]); +} |