比特币源码解析(24) - 可执行程序 - Bitcoind

0x00 失踪人口回归

每隔一段时间都是说要写完,结果都是写了一篇就搁下了。。啪啪啪!打脸,嗯,加油写!

0x01 AppInitMain Step 7: load block chain 续(2)

CCoinsViewCache

在上一章中,我们分析到了init.cpp line 1515这里,到此为止整体的状态是我们已经从内存中读取了所有区块的信息,并且建立了所有区块的索引,按常理来讲,接下来可能就开始启动节点进行通信了,我们继续来看下面的代码。

// The on-disk coinsdb is now in a good state, create the cache
    pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));

    bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
    if (!is_coinsview_empty) {
     
        // LoadChainTip sets chainActive based on pcoinsTip's best block
        if (!LoadChainTip(chainparams)) {
     
            strLoadError = _("Error initializing block database");
            break;
        }
        assert(chainActive.Tip() != nullptr);
    }

首先创建CCoinsViewCache,并将新建的对象保存在指针pcoinsTip中,CCoinsViewCache对象声明在src/coin.h中,并在src/coin.cpp中进行实现。该类主要是维护一个CCoinsMap对象cacheCoins,并使用此对象对当前所有的COutPoint进行缓存,从而减少对CCoinsViewDB的直接操作。在这个当中,需要注意的一点是CCoinsViewCache对象中的base实际指向的是pcoinsdbview对象中的CCoinsView,所以该类中调用的方法例如Flush

bool CCoinsViewCache::Flush() {
     
    bool fOk = base->BatchWrite(cacheCoins, hashBlock);
    cacheCoins.clear();
    cachedCoinsUsage = 0;
    return fOk;
}

实际上base指向的BatchWrite调用的是pcoinsdbview对象中的BatchWrite,而并不是CCoinsView中的BatchWrite。这里继承关系稍微复杂一点,所以要注意指针实际指向的对象,避免理解错调用的函数。

设置完了缓存之后,接下来的两个参数fResetfReindexChainState我们在之前https://blog.csdn.net/pure_lady/article/details/78555973已经介绍过,这两个参数的关系是:fReset是重新建立整个block index,而fReindexChainState表示从block index中更新chain state;如果设置了fReset,那么fReindexChainState就不起作用,如果没有设置fReset,那么也可以单独设置fReindexChainState,使用当前的block index去更新chain state。然后接着使用GetBestBlock从db中读取当前最长链的区块头,如果最终的is_coinsview_emptyfalse,那么说明没有重新建立索引并且需要更新chainActive中的最新区块指向的位置,接着就需要调用LoadChainTip函数根据pcoinsTip中的GetBestBlock信息去设置chainActive中的tip。最后再通过assert去检查调用的结果。

RewindBlockIndex

if (!fReset) {
     
    // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
    // It both disconnects blocks based on chainActive, and drops block data in
    // mapBlockIndex based on lack of available witness data.
    uiInterface.InitMessage(_("Rewinding blocks..."));
    if (!RewindBlockIndex(chainparams)) {
     
        strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
        break;
    }
}

接下来判断如果没有重新建立区块索引,那么需要调用RewindBlockIndex去混滚交易索引,这个函数中主要是检查隔离见证,从顶部区块开始,移除所有启用了隔离见证但区块的结构却不符合隔离见证的要求的区块,如果回滚失败,那么就直接退出程序。

CheckBlocks

if (!is_coinsview_empty) {
     
    uiInterface.InitMessage(_("Verifying blocks..."));
    if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
     
        LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
                  MIN_BLOCKS_TO_KEEP);
    }

    CBlockIndex* tip = chainActive.Tip();
    RPCNotifyBlockChange(true, tip);
    if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
     
        strLoadError = _("The block database contains a block which appears to be from the future. "
                         "This may be due to your computer's date and time being set incorrectly. "
                         "Only rebuild the block database if you are sure that your computer's date and time are correct");
        break;
    }

    if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
     
        strLoadError = _("Corrupted block database detected");
        break;
    }
}

这部分是内部while循环的最后一步,主要是检查区块的完整性。首先判断如果启用了区块剪枝,并且-checkblocks设置的数量大于剪枝保留的最少区块数量MIN_BLOCKS_TO_KEEP,那么发出提醒,只能检查MIN_BLOCKS_TO_KEEP这么多个区块。接下来一个判断链顶部区块的时间和当前的系统时间对比,如果区块中包含的时间晚于当前系统时间两小时以上,那么说明当前的系统时钟没有设置正确,需要重新调整,并退出程序。最后根据-checkblocks参数检查数据库中存储的区块的有效性。

if (!fLoaded && !ShutdownRequested()) {
     
            // first suggest a reindex
            if (!fReset) {
     
                bool fRet = uiInterface.ThreadSafeQuestion(
                    strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"),
                    strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.",
                    "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
                if (fRet) {
     
                    fReindex = true;
                    AbortShutdown();
                } else {
     
                    LogPrintf("Aborted block database rebuild. Exiting.\n");
                    return false;
                }
            } else {
     
                return InitError(strLoadError);
            }
        }

最后判断如果没有加载成功,并且没有收到终止信号,如果开始没有设置过fReset,也就是重新加载区块索引,那么就先尝试重新建立区块索引,并重新返回最开始的while循环,重复一遍上述过程。

    // As LoadBlockIndex can take several minutes, it's possible the user
    // requested to kill the GUI during the last operation. If so, exit.
    // As the program has not fully started yet, Shutdown() is possibly overkill.
    if (ShutdownRequested()) {
     
        LogPrintf("Shutdown requested. Exiting.\n");
        return false;
    }

    fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
    CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
    // Allowed to fail as this file IS missing on first startup.
    if (!est_filein.IsNull())
        ::feeEstimator.Read(est_filein);
    fFeeEstimatesInitialized = true;

在第七步的最后,因为加载区块索引可能需要很长的时间,在这中间可能会收到终止的信号,但是在前面的循环当中可能会没有检测到,所以在循环结束之后立即进行检测,如果收到此信号,那么则立即退出程序。然后定义了一个文件路径指向~/.bitcoin/fee_estimates.dat这个文件,然后通过CBlockPolicyEstimator类型的对象feeEstimator中的Read函数来读取文件中心的信息,这个函数的实现位于fees.cpp line 924,这个操作相当于是将feeEstimator这个对象进行了持久化,保存到了文件当中,然后这里从文件中进行恢复。在后面用到这个对象中的其他函数时再来介绍其中的作用。第七步的所有步骤也就到此结束了。

你可能感兴趣的:(比特币源码分析,比特币,区块链,源代码)