比特币源码学习0.13(八)

9.9 data directory maintenance

这一步主要是针对裁剪模式进行boost::signals2的裁剪

 // ********************************************************* Step 9: data directory maintenance

    // if pruning, unset the service bit and perform the initial blockstore prune
    // after any wallet rescanning has taken place.
    if (fPruneMode) {
        LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
        nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
        if (!fReindex) {
            uiInterface.InitMessage(_("Pruning blockstore..."));
            PruneAndFlush();
        }
    }
//关于隔离见证的软分叉合并处理
    if (Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
        // Only advertize witness capabilities if they have a reasonable start time.
        // This allows us to have the code merged without a defined softfork, by setting its
        // end time to 0.
        // Note that setting NODE_WITNESS is never required: the only downside from not
        // doing so is that after activation, no upgraded nodes will fetch from you.
        nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
        // Only care about others providing witness capabilities if there is a softfork
        // defined.
        nRelevantServices = ServiceFlags(nRelevantServices | NODE_WITNESS);
    }

9.10 import blocks

9.10.1 CheckDiskSpace

// ********************************************************* Step 10: import blocks

    if (!CheckDiskSpace())
        return false;

首先检查是否有足够的磁盘空间能存储即将到来的区块

//main.h
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Minimum disk space required - used in CheckDiskSpace() */
static const uint64_t nMinDiskSpace = 52428800;
//main.cpp
bool CheckDiskSpace(uint64_t nAdditionalBytes)
{
    uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available;

    // Check for nMinDiskSpace bytes (currently 50MB)
    if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
        return AbortNode("Disk space is low!", _("Error: Disk space is low!"));

    return true;
}

nAdditionalBytes默认值为0,当前目录下需要的最小空间目前定为50M


9.10.2 fHaveGenesis

 // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
    // No locking, as this happens before any background thread is started.
    if (chainActive.Tip() == NULL) {
        uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait);
    } else {
        fHaveGenesis = true;
    }

或者安装一个处理程序在genesis激活时通知我们,或者直接设置fHaveGenesis。不需要锁定,因为这在任何后台线程启动之前发生。
这里的chainActive是类CChain的实例。
岔开来看一下这个类

9.10.3 CChain

区块的内存索引链

/** An in-memory indexed chain of blocks. */
class CChain {
private:
    std::vector<CBlockIndex*> vChain;

public:
    /** Returns the index entry for the genesis block of this chain, or NULL if none. */
    //返回此链的genesis块(创世块)的索引条目,如果没有,则返回NULL
    CBlockIndex *Genesis() const {
        return vChain.size() > 0 ? vChain[0] : NULL;
    }

    /** Returns the index entry for the tip of this chain, or NULL if none. */
    //返回此链的tip的索引条目,如果没有则返回NULL,对于tip不是很理解,看代码应该是指最后此链的尾部长度
    CBlockIndex *Tip() const {
        return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL;
    }

    /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */
    //返回此链中特定高度的索引条目,如果不存在此高度,则返回NULL
    CBlockIndex *operator[](int nHeight) const {
        if (nHeight < 0 || nHeight >= (int)vChain.size())
            return NULL;
        return vChain[nHeight];
    }

    /** Compare two chains efficiently. */
    //比较两个链的有效性,代码实现是比较长度
    friend bool operator==(const CChain &a, const CChain &b) {
        return a.vChain.size() == b.vChain.size() &&
               a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
    }

    /** Efficiently check whether a block is present in this chain. */
    //检查这个区块在当前链是否有效
    bool Contains(const CBlockIndex *pindex) const {
        return (*this)[pindex->nHeight] == pindex;
    }

    /** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */
    CBlockIndex *Next(const CBlockIndex *pindex) const {
        if (Contains(pindex))
            return (*this)[pindex->nHeight + 1];
        else
            return NULL;
    }

    /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */
    //返回链中的最大高度。
    int Height() const {
        return vChain.size() - 1;
    }

    /** Set/initialize a chain with a given tip. */
    void SetTip(CBlockIndex *pindex);

    /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */
    CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const;

    /** Find the last common block between this chain and a block index entry. */
    //找到此链和块索引条目之间的最后一个公共块。
    const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
};

这个类是封装了对 std::vector<CBlockIndex*> vChain这个区块索引队列的一系列操作。
接下来返回代码部分,当链长度为0,也就是没有创世块则开启一个监听,否则就是已存在创世块。


9.10.4 loadblock

if (mapArgs.count("-blocknotify"))
        uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);

    std::vector<boost::filesystem::path> vImportFiles;
    if (mapArgs.count("-loadblock"))
    {
        BOOST_FOREACH(const std::string& strFile, mapMultiArgs["-loadblock"])
            vImportFiles.push_back(strFile);
    }

-blocknotify:Execute command when the best block changes (%s in cmd is replaced by block hash)
-loadblock:Imports blocks from external blk000??.dat file on startup

如果设置了-loadblock,则从指定的外部blk000??.dat文件导入数据块
这段代码首先定义了一个boost::filesystem::path的数组,关于boost::filesystem,参考https://www.boost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htm

boost::filesystem :这个广泛使用的库提供了安全、可移植且易用的 C++ 接口,用于执行文件系统操作。可以从 Boost 站点免费下载此库。
boost::filesystem::path:A possibly empty sequence of names. Each element in the sequence, except the last, names a directory which contains the next element. The last element may name either a directory or file. The first element is closest to the root of the directory tree, the last element is farthest from the root.

通过-loadblock参数获取要导入的blk000??.dat文件,并存入上面的数组中,通过建立一个新的线程来加载区块数据。这个线程执行ThreadImport,并且把数组vImportFiles作为参数传入。


9.10.5 ThreadImport

开启区块导入的线程

 threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));

在bitcoin/doc/developer-notes.md中有关于线程的介绍,其他的线程之后再介绍,我们主要关注ThreadImport

Threads


  • ThreadScriptCheck : Verifies block scripts.
  • ThreadImport : Loads blocks from blk*.dat files or bootstrap.dat.
  • StartNode : Starts other threads.
  • ThreadDNSAddressSeed : Loads addresses of peers from the DNS.
  • ThreadMapPort : Universal plug-and-play startup/shutdown
  • ThreadSocketHandler : Sends/Receives data from peers on port 8333.
  • ThreadOpenAddedConnections : Opens network connections to added nodes.
  • ThreadOpenConnections : Initiates new connections to peers.
  • ThreadMessageHandler : Higher-level message handling (sending and receiving).
  • DumpAddresses : Dumps IP addresses of nodes to peers.dat.
  • ThreadFlushWalletDB : Close the wallet.dat file if it hasn’t been used in 500ms.
  • ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them.
  • BitcoinMiner : Generates bitcoins (if wallet is enabled).
  • Shutdown : Does an orderly shutdown of everything.

这个线程的作用是从blk*.dat文件或者bootstrap.dat中导入数据块。下面来看这个线程

//init.cpp
void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
{
    const CChainParams& chainparams = Params();
    RenameThread("bitcoin-loadblk");//1.给当前线程重命名
    CImportingNow imp;

    // -reindex 2.将所有区块文件从0重新加载一遍
    if (fReindex) {
        int nFile = 0;
        while (true) {
            CDiskBlockPos pos(nFile, 0);
            if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk")))
                break; // No block files left to reindex
            FILE *file = OpenBlockFile(pos, true);
            if (!file)
                break; // This error is logged in OpenBlockFile
            LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
            LoadExternalBlockFile(chainparams, file, &pos);
            nFile++;
        }
        pblocktree->WriteReindexing(false);
        fReindex = false;
        LogPrintf("Reindexing finished\n");
        // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
        InitBlockIndex(chainparams);//3.为防止没有创世区块导致中断,需要重新初始化
    }

    // hardcoded $DATADIR/bootstrap.dat 4.加载bootstrap.dat文件
    boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
    if (boost::filesystem::exists(pathBootstrap)) {
        FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
        if (file) {
            boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
            LogPrintf("Importing bootstrap.dat...\n");
            LoadExternalBlockFile(chainparams, file);
            RenameOver(pathBootstrap, pathBootstrapOld);
        } else {
            LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string());
        }
    }

    // -loadblock= 5.遍历传入的区块文件数组,加载区块数据
    BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) {
        FILE *file = fopen(path.string().c_str(), "rb");
        if (file) {
            LogPrintf("Importing blocks file %s...\n", path.string());
            LoadExternalBlockFile(chainparams, file);
        } else {
            LogPrintf("Warning: Could not open blocks file %s\n", path.string());
        }
    }

    // scan for better chains in the block chain database, that are not yet connected in the active best chain
    //6.扫描区块链中更好的链
    CValidationState state;
    if (!ActivateBestChain(state, chainparams)) {
        LogPrintf("Failed to connect best block");
        StartShutdown();
    }

    if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
        LogPrintf("Stopping after block import\n");
        StartShutdown();
    }
}

-stopafterblockimport:Stop running after importing blocks from disk (default: %u)

9.10.5.1 ActivateBestChain

上述代码第六步通过调用ActivateBestChain函数查找并激活最好的区块链,传入的参数是CValidationState类的对象

//src/consensus/validation.h
/** Capture information about block/transaction validation */
class CValidationState {
private:
    enum mode_state {
        MODE_VALID,   //!< everything ok
        MODE_INVALID, //!< network rule violation (DoS value may be set)
        MODE_ERROR,   //!< run-time error
    } mode;
    int nDoS;
    std::string strRejectReason;
    unsigned int chRejectCode;
    bool corruptionPossible;
    std::string strDebugMessage;
    public:
    CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
    ···

这个类是验证区块或交易有效性的类,这个类定义了三种状态,有效、无效、错误,默认设置为有效状态。接下来看ActivateBestChain函数

//main.h
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
//main.cpp
/**
 * Make the best chain active, in multiple steps. The result is either failure
 * or an activated best chain. pblock is either NULL or a pointer to a block
 * that is already loaded (to avoid loading it again from disk).
 */
 //通过多个步骤激活最佳链,要么成功激活要么失败,pblock是只想已加载块的指针或者为空,避免再次加载
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
    CBlockIndex *pindexMostWork = NULL;
    CBlockIndex *pindexNewTip = NULL;
    do {
        boost::this_thread::interruption_point();
        if (ShutdownRequested())
            break;
//1.先获得当前链的最高区块
        const CBlockIndex *pindexFork;
        bool fInitialDownload;
        int nNewHeight;
        {
            LOCK(cs_main);
            CBlockIndex *pindexOldTip = chainActive.Tip();
            if (pindexMostWork == NULL) {
            //2.查找工作量最多(也就是最长)的链
                pindexMostWork = FindMostWorkChain();
            }
//3.如果未找到或者长的链就是现在的链,则直接返回true
            // Whether we have anything to do at all.
            if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip())
                return true;
//4.尝试使pindexMostWork称为激活的块取得一些进展,从代码看是断开旧块连接,连接到新的块
            bool fInvalidFound = false;
            if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound))
                return false;
//5.擦出缓存,我们可能需要另外的分支
            if (fInvalidFound) {
                // Wipe cache, we may need another branch now.
                pindexMostWork = NULL;
            }
            pindexNewTip = chainActive.Tip();
            pindexFork = chainActive.FindFork(pindexOldTip);
            fInitialDownload = IsInitialBlockDownload();
            nNewHeight = chainActive.Height();
        }
        // When we reach this point, we switched to a new tip (stored in pindexNewTip).
//到这一步,我们已经切换到新的最高点(区块链的尾部)
        // Notifications/callbacks that can run without cs_main
        // Always notify the UI if a new block tip was connected
        if (pindexFork != pindexNewTip) {
            uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);

            if (!fInitialDownload) {
                // Find the hashes of all blocks that weren't previously in the best chain.
                std::vector<uint256> vHashes;
                CBlockIndex *pindexToAnnounce = pindexNewTip;
                while (pindexToAnnounce != pindexFork) {
                    vHashes.push_back(pindexToAnnounce->GetBlockHash());
                    pindexToAnnounce = pindexToAnnounce->pprev;
                    if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
                        // Limit announcements in case of a huge reorganization.
                        // Rely on the peer's synchronization mechanism in that case.
                        break;
                    }
                }
                // Relay inventory, but don't relay old inventory during initial block download.
                {
                    LOCK(cs_vNodes);
                    BOOST_FOREACH(CNode* pnode, vNodes) {
                        if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
                            BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
                                pnode->PushBlockHash(hash);
                            }
                        }
                    }
                }
                // Notify external listeners about the new tip.
                if (!vHashes.empty()) {
                    GetMainSignals().UpdatedBlockTip(pindexNewTip);
                }
            }
        }
    } while (pindexNewTip != pindexMostWork);//直到最新的链是最长的链退出循环
    CheckBlockIndex(chainparams.GetConsensus());

    // Write changes periodically to disk, after relay.
    if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
        return false;
    }

    return true;
}

9.10.6 GenesisWait

// Wait for genesis block to be processed
    {
        boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
        while (!fHaveGenesis) {
            condvar_GenesisWait.wait(lock);
        }
        uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
    }

如果创世块未被加载,则等待直到被加载,然后断开对其的监听。

/** New block has been accepted */
    boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip;

NotifyBlockTip用于监听新的区块被接收
来了解下boost::signals2

boost::signals2:signals2基于Boost的另一个库signals,实现了线程安全的观察者模式。在signals2中,观察者模式被称为信号/插槽(signals and slots),它是一种函数回调机制,一个信号关联了多个插槽,当信号发出时,所有关联它的插槽都会被调用。

至于uiInterface是类CClientUIInterface的对象,这个类封装了各种交互信号

//ui_interface.h class CClientUIInterface
···
 /** Show message box. */
    boost::signals2::signal<bool (const std::string& message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeMessageBox;

    /** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
    boost::signals2::signal<bool (const std::string& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeQuestion;

    /** Progress message during initialization. */
    boost::signals2::signal<void (const std::string &message)> InitMessage;

    /** Number of network connections changed. */
    boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;

    /**
     * Status bar alerts changed.
     */
     ···

猜你喜欢

转载自blog.csdn.net/m0_37847176/article/details/81744939