比特币 RPC 命令「getchaintips」
$ bitcoin-cli help getchaintips getchaintips 返回有关区块树上所有已知尖端的信息,包括主链和孤儿分支。 结果: [ { "height": xxxx, (数字)链尖的高度 "hash": "xxxx", (字符串)链尖区块的哈希 "branchlen": 0 (数字)主链为 0 "status": "active"(字符串)主链为 "active" }, { "height": xxxx, "hash": "xxxx", "branchlen": 1 (数字)连接尖端到主链的分支长度 "status": "xxxx" (字符串)链的状态 (active, valid-fork, valid-headers, headers-only, invalid) } ] 状态的可能取值: 1. "invalid" 该分支至少包含一块无效区块 2. "headers-only" 该分支并非所有区块都有效,但区块头都有效 3. "valid-headers" 该分支所有区块都有效,但它们没有完全验证 4. "valid-fork" 该分支不是活跃链的一部分,但已完全验证 5. "active" 这是活跃主链的尖端,确定有效 例子: > bitcoin-cli getchaintips > curl --user myusername:mypassword --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getchaintips", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
源码剖析
getchaintips
对应的函数在文件 rpcserver.h
中被引用。
extern UniValue getchaintips(const UniValue& params, bool fHelp);
实现在文件 rpcblockchain.cpp
中。
UniValue getchaintips(const UniValue& 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"
" \"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", "")
); // 1. 帮助内容
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. */
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips; // 2. 构建一个链尖列表。
BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
setTips.insert(item.second);
BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
{ // 我们从所有已知区块的列表开始,依次移除作为另一个区块的 pprev 区块。
const CBlockIndex* pprev = item.second->pprev;
if (pprev)
setTips.erase(pprev);
}
// Always report the currently active tip.
setTips.insert(chainActive.Tip()); // 3. 始终报告当前活跃的链尖。
/* Construct the output array. */
UniValue res(UniValue::VARR); // 4. 构建输出数组。
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;
}
1. 帮助内容
参考比特币 RPC 命令「getbestblockhash」1. 帮助内容。
2. 构建一个链尖列表
函数对象比较器 CompareBlocksByHeight
定义在文件 rpcblockchain.cpp
中。
/** Comparison function for sorting the getchaintips heads. */
struct CompareBlocksByHeight // 用于排序函数 getchaintips 区块头的比较函数。
{
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;
}
};
确保高度相同的不同区块比较起来不想等。 使用指针自身来做区分。
3. 始终报告当前活跃的链尖
参考比特币 RPC 命令「getbestblockhash」2. 返回活跃的链尖区块哈希。
4. 构建输出数组并返回
计算分支长度,寻找分叉函数 chainActive.FindFork(block)
声明在文件 chain.h
的链类 CChain
中。
/** An in-memory indexed chain of blocks. */
class CChain { // 一个内存中已索引的区块链。
private:
...
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const; // 查找该链和一个区块索引项之间的最后一个公共区块。
};
实现在文件 chain.cpp
中。
const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
if (pindex == NULL) {
return NULL;
}
if (pindex->nHeight > Height()) // 若指定区块高度大于当前活跃的链高度
pindex = pindex->GetAncestor(Height()); // 获取活跃的链上祖先区块索引
while (pindex && !Contains(pindex)) // 向前查找该分支的交点
pindex = pindex->pprev;
return pindex;
}
获取祖先函数 pindex->GetAncestor(Height())
声明在文件 chain.h
的区块索引类 CBlockIndex
中。
/** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple
* candidates to be the next block. A blockindex may have multiple pprev pointing
* to it, but at most one of them can be part of the currently active branch.
*/
class CBlockIndex
{
...
//! Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height); //! 有效地找到这个区块的祖先。
const CBlockIndex* GetAncestor(int height) const;
};
实现在文件 chain.cpp
中。
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); } // 把一个数的二进制表示中的最低位的 '1' 转换为 '0'。
/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */
int static inline GetSkipHeight(int height) { // 使用 CBlockIndex::pskip 指针计算跳回的高度。
if (height < 2)
return 0;
// Determine which height to jump back to. Any number strictly lower than height is acceptable,
// but the following expression seems to perform well in simulations (max 110 steps to go back
// up to 2**18 blocks). // 确定跳回的高度。任何严格低于高度均可接受,但下面的表达式似乎在模拟中表现得很好(最多 110 步回到 2**18 个区块)。
return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); // height 奇前偶后
}
CBlockIndex* CBlockIndex::GetAncestor(int height)
{
if (height > nHeight || height < 0)
return NULL;
CBlockIndex* pindexWalk = this;
int heightWalk = nHeight;
while (heightWalk > height) {
int heightSkip = GetSkipHeight(heightWalk);
int heightSkipPrev = GetSkipHeight(heightWalk - 1);
if (pindexWalk->pskip != NULL &&
(heightSkip == height ||
(heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
heightSkipPrev >= height)))) {
// Only follow pskip if pprev->pskip isn't better than pskip->pprev.
pindexWalk = pindexWalk->pskip; // 只有当 pprev->pskip 不优于 pskip->pprev 时才遵循 pskip。
heightWalk = heightSkip;
} else {
pindexWalk = pindexWalk->pprev;
heightWalk--;
}
}
return pindexWalk;
}
const CBlockIndex* CBlockIndex::GetAncestor(int height) const
{
return const_cast<CBlockIndex*>(this)->GetAncestor(height); // 转调上面的重载函数
}