diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py new file mode 100755 index 0000000000..a83c499743 --- /dev/null +++ b/qa/rpc-tests/getchaintips.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Exercise the getchaintips API. + +# Since the test framework does not generate orphan blocks, we can +# unfortunately not check for them! + +from test_framework import BitcoinTestFramework +from util import assert_equal + +class GetChainTipsTest (BitcoinTestFramework): + + def run_test (self, nodes): + res = nodes[0].getchaintips () + assert_equal (len (res), 1) + res = res[0] + assert_equal (res['branchlen'], 0) + assert_equal (res['height'], 200) + +if __name__ == '__main__': + GetChainTipsTest ().main () diff --git a/src/main.cpp b/src/main.cpp index 353cde0bd7..cf9318fedf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -444,7 +444,7 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { return Genesis(); } -CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const { +const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { if (pindex->nHeight > Height()) pindex = pindex->GetAncestor(Height()); while (pindex && !Contains(pindex)) @@ -2067,8 +2067,8 @@ static CBlockIndex* FindMostWorkChain() { static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork) { AssertLockHeld(cs_main); bool fInvalidFound = false; - CBlockIndex *pindexOldTip = chainActive.Tip(); - CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + const CBlockIndex *pindexOldTip = chainActive.Tip(); + const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); // Disconnect active blocks which are no longer in the best chain. while (chainActive.Tip() && chainActive.Tip() != pindexFork) { diff --git a/src/main.h b/src/main.h index 48ec86f6a8..a27020459a 100644 --- a/src/main.h +++ b/src/main.h @@ -1068,7 +1068,7 @@ public: CBlockIndex *FindFork(const CBlockLocator &locator) const; /** Find the last common block between this chain and a block index entry. */ - CBlockIndex *FindFork(CBlockIndex *pindex) const; + const CBlockIndex *FindFork(const CBlockIndex *pindex) const; }; /** The currently-connected chain of blocks. */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 253693e624..1e5198b85c 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -461,3 +461,73 @@ Value getblockchaininfo(const Array& params, bool fHelp) obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); 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; + } +}; + +Value getchaintips(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getchaintips\n" + "Return information about all known tips in the block tree," + " including the main chain as well as orphaned branches.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"height\": xxxx, (numeric) height of the chain tip\n" + " \"hash\": \"xxxx\", (string) block hash of the tip\n" + " \"branchlen\": 0 (numeric) zero for main chain\n" + " },\n" + " {\n" + " \"height\": xxxx,\n" + " \"hash\": \"xxxx\",\n" + " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getchaintips", "") + + HelpExampleRpc("getchaintips", "") + ); + + /* Build up a list of chain tips. We start with the list of all + known blocks, and successively remove blocks that appear as pprev + of another block. */ + std::set setTips; + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + setTips.insert(item.second); + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + const CBlockIndex* pprev = item.second->pprev; + if (pprev) + setTips.erase(pprev); + } + + /* Construct the output array. */ + Array res; + BOOST_FOREACH(const CBlockIndex* block, setTips) + { + Object obj; + 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)); + + res.push_back(obj); + } + + return res; +} diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 5deb6a4e08..716a7fba6a 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -235,6 +235,7 @@ static const CRPCCommand vRPCCommands[] = { "getblockcount", &getblockcount, true, false, false }, { "getblock", &getblock, true, false, false }, { "getblockhash", &getblockhash, true, false, false }, + { "getchaintips", &getchaintips, true, false, false }, { "getdifficulty", &getdifficulty, true, false, false }, { "getrawmempool", &getrawmempool, true, false, false }, { "gettxout", &gettxout, true, false, false }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 31badadd6d..176852ca8f 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -205,5 +205,6 @@ extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp) extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp); #endif