比特币 RPC 命令「verifychain」
$ bitcoin-cli help verifychain verifychain ( checklevel numblocks ) 验证区块链数据库。 参数: 1. checklevel(字符串,可选,0-4,默认为 3)区块验证有多彻底。 检查等级 0:从磁盘读区块数据到内存。 检查等级 1:验证区块有效性。 检查等级 2:验证撤销有效性。 检查等级 3:检查在内存中断开链尖区块连接的不一致性。 检查等级 4:尝试重连区块。 2. numblocks (字符串,可选,默认为 288,0 或负数表示全部)要检查的区块数。 结果: "true|false" (布尔型)验证与否 例子: > bitcoin-cli verifychain > curl --user myusername:userpassword --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "verifychain", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
源码剖析
verifychain
对应的函数在文件 rpcserver.h
中被引用。
extern UniValue verifychain(const UniValue& params, bool fHelp);
实现在文件 rpcblockchain.cpp
中。
UniValue verifychain(const UniValue& params, bool fHelp)
{
int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL); // 检查等级,默认 3
int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); // 检查块数,默认 288
if (fHelp || params.size() > 2)
throw runtime_error(
"verifychain ( checklevel numblocks )\n"
"\nVerifies blockchain database.\n"
"\nArguments:\n"
"1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n"
"2. numblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n"
"\nResult:\n"
"true|false (boolean) Verified or not\n"
"\nExamples:\n"
+ HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
); // 1. 帮助内容
LOCK(cs_main); // 上锁
if (params.size() > 0)
nCheckLevel = params[0].get_int(); // 获取指定的检查等级
if (params.size() > 1)
nCheckDepth = params[1].get_int(); // 获取指定得检查块数作为检查深度
return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); // 检查区块链数据库
}
1. 帮助内容
参考比特币 RPC 命令「getbestblockhash」1. 帮助内容。
基本流程:
- 设置默认检查等级和默认检查块数。
- 处理命令帮助和参数个数。
- 上锁。
- 获取用户指定的检查等级和检查块数(深度)。
- 检查区块链数据库。
第五步,创建了一个临时对象然后调用 CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth) 来验证区块链数据库。 该类定义在“main.h”文件中。
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB { // VerifyDB 的 RAII 包装器:验证区块和币数据库完整性
public:
CVerifyDB();
~CVerifyDB();
bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); // 验证数据库
};
验证函数实现在“main.cpp”文件中。
bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
LOCK(cs_main); // 上锁
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) // 激活的链尖非空 且 链尖的前一个区块哈希非空(即区块链高度至少为 1)
return true;
// Verify blocks in the best chain // 验证最佳链上的区块
if (nCheckDepth <= 0) // 检查深度若为负数
nCheckDepth = 1000000000; // suffices until the year 19000
if (nCheckDepth > chainActive.Height()) // 检查深度若大于当前激活链高度
nCheckDepth = chainActive.Height(); // 检查深度等于链高度
nCheckLevel = std::max(0, std::min(4, nCheckLevel)); // 检查等级,最高 4 级
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); // 记录检查信息
CCoinsViewCache coins(coinsview);
CBlockIndex* pindexState = chainActive.Tip(); // 获取链尖区块索引
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0; // 好的交易数
CValidationState state; // 用于保存区块的验证状态
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{ // 遍历区块链,从新到旧
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))));
if (pindex->nHeight < chainActive.Height()-nCheckDepth) // 若当前验证的区块数大于检查深度
break; // 跳出
CBlock block;
// check level 0: read from disk // 检查等级 0:从磁盘读
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) // 根据区块索引从磁盘读区块数据到内存 block
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity // 检查等级 1:验证区块有效性
if (nCheckLevel >= 1 && !CheckBlock(block, state)) // 检查区块状态
return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity // 检查等级 2:验证撤销有效性
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
CDiskBlockPos pos = pindex->GetUndoPos(); // 获取区块在磁盘上撤销的位置
if (!pos.IsNull()) {
if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash())) // 撤销从磁盘读,从历史文件读
return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks // 检查等级 3:检查在内存中断开链尖区块连接的不一致性
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { // 币的动态内存用量不能大于 5000 * 300 (1M 多)
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean)) // 断开链尖区块
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
pindexState = pindex->pprev; // 指向前一个区块(新的链尖区块)
if (!fClean) {
nGoodTransactions = 0;
pindexFailure = pindex;
} else
nGoodTransactions += block.vtx.size(); // 记录好的交易数
}
if (ShutdownRequested()) // 这里如果比特币核心被请求断开连接
return true; // 直接返回 true
}
if (pindexFailure) // 记录失败信息
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// check level 4: try reconnecting blocks // 检查等级 4:尝试重连区块
if (nCheckLevel >= 4) { // 最高等级 4
CBlockIndex *pindex = pindexState; // 获取当前区块索引
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))));
pindex = chainActive.Next(pindex); // 指向下一个区块索引
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) // 从磁盘中读区块数据
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins)) // 连接该区块
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); // 检验完成,没有不一致
return true;
}
未完成。