比特币源码分析--深入理解区块链 13. 区块同步和IBD介绍

        当一个新的比特币节点加入到比特币的P2P网络时,首要的工作就是进行区块同步(从其它节点下载区块)。只有在同步区块数据后,该节点才能拥有比特币整个链的区块数据和具备完整的功能。同时,也是进行后续一系列操作必要前提。特别对于在节点中执行挖矿和交易转账操作操作更是必不可少。新加入的节点数据要跟主网的区块数据保持同步有两种途径:一是通过导入区块文件,该方式是将历史的区块数据打包成文件,然后在比特币客户端启动时通过设置特定的参数来导入文件,从而加快区块历史数据的导入。二是通过网络同步,它是最常用的方式也是本文要说明的。客户端在启动网络后将从对等节点下载区块并进行一些必要的检查,将合法的区块整理成一个链并保存在本地。

        区块数据同步使得比特币的分布式网络和分布式的存储更加可靠和安全,它能最大程度保护链上数据和交易的安全,防止黑客攻击和非人为因素的破坏,避免了中心化网络和存储带来的风险。

初始块下载( IBD)

        IBD(Initial Block Download)是从头开始构建完整的比特币区块链的过程。 当一个新节点建立并加入网络时,它会连接到其他节点并向它们请求区块。 新节点处理这些块并构建区块链,直到它赶上并与网络同步。节点在完成IBD后如有新的区块的生成时,会通过网络实时在节点之间同步,采用的跟IBD不完全一样的同步机制,本文主要讲述在IBD阶段如何实现区块的数据的下载。

        区块头(Block Header)和区块(Block)的内容并不完全相同。前者只包含了区块的基本数据,而后者除了包含区块头之外还包含交易(Transactions)详情。它们之间的关系如下类图:

区块的类图

        上述类图是区块的基本类,其中CBlockHeader和CBlock主要用于区块文件的持久化存储。而CBlockIndex和CDiskBlockIndex主要用于区块在内存中建立索引和基于LevelDB的数据存储。之所以要这样区分,是因为区块的数据量较大且还在不断增长中,当前的区块数量已超过70万个。为了方便查找和定位有必要在内存中维持一个索引,同时基于该索引用了高效的基于key-value的数据库LevelDB作为存储方式。为了方便管理,存于LevelDB中的数据包含了一些状态和区块其它属性数据,比如nStatus和nHeight、nDataPos,nUndPos等,这些数据都并不是区块必需的数据,它是在同步过程中计算而来,它们对于管理本本地的区块有重要的作用。而存于区块文件(Block Files)中的数据有区块头和交易信息。其中vtx是一个包含了交易数据CTransaction的数组。

        在区块同步过程中,区块头和区块分开进行。

同步流程

         区块的同步下载采用先同步区块头后同步区块数据的方式,在时间上两者交替进行。只有在本地拥有区块头的情况下才会同步区块的详细数据。流程如下:

区块基本同步流程

        上图是区块同步的简单流程图示,在SendMessages处理过程中,本地节点首先发送GETHEADERS协议给对等节点,对等节点响应HEADERS协议并传回区块头,然后通过ProcessHeadersMessage处理区块头。在收到区块头的基础上,本地节点继续发送GETDATA协议给对方,对方响应BLOCK协议并传回区块的详细数据(包括交易数据)。通过不断地GETHEADERS和GETDATA完成IBD。

区块头的同步流程:

区块头同步流程 

流程说明:

  1. net_processing.cpp 模块负责网络数据包的发送和接收,所有协议数据均在此模块发送和接收。
  2. 请求对方发送区块头的协议标记是NetMsgType::GETHEADERS。
  3. 触发发送NetMsgType::GETHEADERS协议数据的地方有SendMessages、ProcessMessages、ProcessHeadersMessage、ConsiderEvction。
  4. 对等节点响应 NetMsgType::HEADERS协议标记,在此解析区块头。
  5. 通过AcceptBlockHeader处理收到的区块头,并使用AddToBlockIndex将区块头加入本地的内存索引表。
  6. 在同步过程中,系统将定期将区块头索引信息通过WriteBlockIndexDB保存到LevelDB。
  7. 在系统退出时,通过ForceFlushStateToDisk强制将未保存的区块索引保存到LevelDB。

区块同步流程:

区块同步流程

流程说明:

  1. 从节点下载区块数据的协议标记是NetMsgType::GETDATA,对方的响应协议标记是NetMsgType::BLOCK。
  2. 从一个节点每次请求获得的区块数据最多是16个,通过函数FindNextBlocksToDownload计算从指定的节点获得从多少高度开始,多少个区块。
  3. 接收到区块后通过ProcessBlock处理收到的区块并对区块做一系列必要的检查。
  4. 通过SaveBlockToDisk函数将区块数据持久化到区块文件。
  5. 通过ReceivedBlockTransactions将区块标记为已接收数据,该操作是将区块作为一个完整区块的重要步骤,通过此操作将同步到本地的区块作为一个合法、有效且完整的区块。
  6. 通过ActiveBestChain将收到的区块加到的本地的当前网络的区块链,该操作将有效防止区块分叉和保证链的完整和连贯。
  7. 上述图中红色部分涉及到将区块数据持久化,其中WriteBlockToIndexDB在上面也有提到,它主要用于将区块索引加入到LevelDB。

区块文件的组织形式

        比特币客户端默认的区块文件保存在~/ .bitcoin/<network>/ blocks目录下,它以blkxxxxx.dat方式命令,其中xxxxx是5位数字序号从00000开始。每个文件最大为128Mb,当超过指定大小时序号加1。随着区块的增加文件数量也不断增加,为了能够通过区块索引快速定位区块文件,在同步区块的过程中,使用了如下的数据结构作为索引来实现快速定位区块文件。

区块索引文件

        CBlockFileInfo中的字段 nBlock指定文件序号,nHeighFirst、nHeightLast指出该文件的包含的区块的开始和结束高度,nTimeFirst、nTimeLast指出该文件区块的开始时间和结束时间。从而可以方便通过文件序号、区块高度和时间快速定位到指定文件,CBlockFileInfo在同步区块中的过程中保存在LevelDB中。

IBD状态判断

 判断当前节点是否处于IBD状态很重要,因为如果节点处于IBD状态它表明节点的数据初始化尚未完成,将导致很多操作无法进行,系统将通过判断下面任何一个条件是否成立来确定当前是否处在IBD状态:

  1. 正在导入区块或正在重建索引。
  2. 本地区块链条中最后的区块索引为空。
  3. 最后的区块的工作量少于系统规定的最小工作量。
  4. 当前时间与最后的区块时间相差大于1天。也就是24小时内没有区块更新。该时间可由用户设置。

猜你喜欢

转载自blog.csdn.net/dragon_trooquant/article/details/124891643