Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "getchains" RPC command to monitor blockchain forks. #4536

Merged
merged 1 commit into from Aug 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions 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 ()
6 changes: 3 additions & 3 deletions src/main.cpp
Expand Up @@ -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))
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/main.h
Expand Up @@ -1068,7 +1068,7 @@ class CChain {
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. */
Expand Down
70 changes: 70 additions & 0 deletions src/rpcblockchain.cpp
Expand Up @@ -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<const CBlockIndex*, CompareBlocksByHeight> 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;
}
1 change: 1 addition & 0 deletions src/rpcserver.cpp
Expand Up @@ -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 },
Expand Down
1 change: 1 addition & 0 deletions src/rpcserver.h
Expand Up @@ -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