同步,是区块链中非常重要的流程。区块链作为一种安全共享的去中心化的数据账本,每个加入到链中的节点都需要维护一份账本数据,当一个新节点加入到区块链中或者有节点掉线重新连接后,首要的任务就是要将链上的账本数据同步到本地账本中,否则节点功能可能无法正常开启,诸如共识节点无法参与共识等。

本文主要介绍长安链中同步模块如何工作以及长安链在同步模块的实现中做了哪些考虑,方便大家更好的学习和使用长安链。

同步模块架构

长安链的同步模块采用事件驱动的架构方式。各功能间通过work queue相互连接,work queue可以比作一个流水线,各功能宛如流水线上的工人,这种流水线的工作方式能够提高同步的并行化从而提升同步效率;同步时采取将同步请求发送到在处理最少请求节点的原则,请求尽量均匀地分配给对端节点,以避免一些节点处理压力过大而其他节点空闲的状况。具体的架构图如下。




长安链 java invokeContract 长安链是什么_数据


图1

同步模块各详细单元介绍如下:

  1. event loop 负责收集和分发事件到工作队列;
  2. 定时器,维护着诸如定时请求其他节点状态,定时探测同步的请求是否超时未获取到应答,检测是否有区块已经被同步但是缓存未被清理等定时器;
  3. 网络,同步模块接收来自网络的消息,如区块同步请求,区块同步应答,节点状态等网络事件;
  4. work queue, 内部维护着一个优先队列,主要负责事件/任务缓存和分发,是各模块的连接纽带;
  5. scheduler,同步模块的主要同步逻辑在这里执行,其内部会维护各个对端节点的状态,当前区块同步的状态(哪个区块需要同步,区块的同步请求发送到了哪个节点,同步时间等)。实现的主要功能有接收其他节点的状态并标识是否开启同步流程,进行区块同步并标识各个区块的同步状态,对超时请求进行重试,对完成的请求进行清理并推进下一步同步等;
  6. processor, 同步模块会将同步过来的区块数据放在处理器中缓存,处理器会顺序性地进行区块的验证和提交,并将验证结果通过event loop发往work queue供scheduler消费来推动本地当前块高向前。

同步流程

长安链的同步流程可以分为3步,节点加入→开始同步→验证并提交区块;接下我们来详细介绍每一步的实现。

1. 节点加入

区块同步,能让区块链节点的数据状态保持在最新,区块链状态的新旧最重要的标识是区块高度。本案中有三个节点Node1、Node2、Node3其中Node2是后加入节点。

当Node2加入链时,其首先会主动向链中的其他节点发送获取节点状态(区块高度)请求,并在本地维护其他各节点的状态数据,在节点的整个生命周期中,一方面相互之间会定时(默认5s,可配置)地向彼此发送获取状态请求,另一方面在节点每订阅到3个新块就会向其他节点同步一次自己的状态。Node2获取到Node1和Node3的区块高度后,会跟自己本地高度做对比,如若发现有节点高度比自己要高,则开始准备区块同步。



长安链 java invokeContract 长安链是什么_数据_02


 图 2-1-1

长安链 java invokeContract 长安链是什么_区块链_03

图 2-1-2

此时Node2本地区块高度为7,Node1和Node3发送的区块高度都为10,于是Node2发现自己的区块落后于其他节点,于是在本地标记要同步的区块(如图2-1-2,newblock代表此块高需要被同步),然后开启同步。

2. 开始同步

 长安链中同步分为两种模式:

• 普通同步:即本地节点向对端节点发起的获取区块的请求,只要求对端节点返回普通区块的数据,在本地节点拿到区块数据后,需要通过虚拟机将区块中的交易重新执行,然后验证执行结果跟区块中的结果是否一致来验证区块的有效性。

• 快速同步,本地节点向对端节点发起的获取区块的请求,除了要求对端节点返回区块数据外还要附带读写集信息,本地节点验证的时候,验证读写集相关信息而不需要再次通过虚拟机将交易重新执行,效率相对要高。

 //请求结构

type BlockSyncReq struct {

BlockHeight uint64   //请求的起始块高

BatchSize   uint64  //连续请求几个块

WithRwset   bool    //是否请求读写集

}

//响应结构

type BlockWithRWSet struct {

// block data

Block *common.Block //块数据

// transaction read/write set of blocks

TxRWSets []*common.TxRWSet //块的读写集

// contract event info

ContractEvents []*common.ContractEvent 

}

同步的时候使用哪种模式可以通过配置文件进行配置,目前默认是普通同步模式,配置见第4小节。

长安链 java invokeContract 长安链是什么_数据_04

长安链 java invokeContract 长安链是什么_同步请求_05

 图 2-2-1

节点会在本地维护一个块高到节点ID的映射(如图2-2-1)来反映块高的同步请求被发送到了哪个节点。节点在同步区块时会根据这个映射数据来判断下一个区块同步请求应该发送到哪个节点,遵循的原则是应当尽量将区块请求发送到在处理请求最少的节点上。

 Node2会根据上述原则分别向Node1和Node3发起同步区块请求(图2-2-2),在同步区块8时,因为Node1和Node3都没有正在处理的请求,所以选择Node1和Node3都一样,此时按照节点ID排序选择了Node1,并将块高8和Node1的映射数据暂存。当同步块高9的时候,根据映射数据发现自己发往Node1的请求数为1(记录了块8发送到了Node1),Node3为0,则Node1选择将块9的同步请求发送给Node3,请求块10时,Node1和Node2请求数都为1,则选择Node1进行同步。这种策略的前提是Node1和Node3都达到了请求的块高,如果Node1节点的自身块高为8,此时Node2只会将块9和块10的同步请求都发往Node3。

长安链 java invokeContract 长安链是什么_数据_06

图 2-2-2

 最终会维护如图2-2-3中的数据,其中pendingBlock代表此块高已经发送请求但还没处理完成。

长安链 java invokeContract 长安链是什么_数据_07

图 2-2-3

对端节点(Node1,Node3)在接收到区块请求后,从自己本地数据库中取出相应的区块数据,如果请求中要求读写集则附带上读写集信息,然后组装成响应结构返回给发起同步请求节点。

发起同步请求的节点收到响应的区块数据后,会将此块高对应的状态由pendingBlock转为receivedBlock,而后发送给processor去处理,具体处理流程见下小节。

对于发起同步请求的节点,在检测超时定时器的推动下(默认1s推动一次,可配置),如果发向某个对端节点的块请求时间到当前时间超过了配置的超时时间(默认5s可配置),则判定此请求失败,此时会将对应节点的状态数据从本地删除(图2-2-4),并把此块高对应的状态由pendingBlock重新置回newBlock,然后等待重试,重试时因为之前节点状态已不在本地,所以重试的请求将会被调度到其他的节点。

长安链 java invokeContract 长安链是什么_数据_08

长安链 java invokeContract 长安链是什么_区块链_09

图 2-2-4

在长安链实际的使用中,节点发起一次同步请求默认是一次一个块的请求,支持用户进行自行配置,可以在一次请求中同步多个块的数据,默认同时被同步的区块的最大数量为1024,用户亦可自行配置,配置详情见第4小节。

3. 验证并提交区块

节点同步下来的数据会被processor临时缓存起来,processor会根据本地块高从缓存中获取对应的块数据进行验证,普通同步模式下,验证器需要vm执行交易生成读写集,再进行后续校验,如果启用快速同步模式,同步的块中带有了读写集信息,则省去了vm执行流程。如果验证器验证区块合法,则将区块提交到本地账本,processor将本地缓存的区块数据从临时缓存中移除。

 同时processor会将区块的验证结果通过异步方式发送给scheduler,schduler在获取到这个结果后,作如下处理

• 验证成功,schduler将缓存的区块高度向前推进;

• 验证失败,scheduler会将此块高对应的节点ID取出(图2-2-3中蓝色部分数据),并根据节点ID将节点状态数据删除[1],如图2-2-4,然后将此块高的状态重新置回newblock等待被重新同步。



长安链 java invokeContract 长安链是什么_同步请求_10


图2-3-1

经过以上请求-验证-提交环节往复推进,最终本地区块高度同其他对端节点的最高块高持平,同步结束,本地账本中的区块数据同链上的账本数据保持一致。

4.同步相关的配置

配置在chainmaker.yml文件中

sync:

block_pool_size: 1024 //同时被处理的区块最大数量

batch_Size_from_one_node: 1 //一次请求中可以连续请求几

req_time_threshold: 5 //请求超时时间

liveness_tick:1 // 检测请求超时定时器的推动时间

node_status_tick: 5 //节点请求其他节点状态间隔

node:

fast_sync:

enabled: true //开启快速同步模式

总      结

以上内容梳理了长安链目前同步的流程和机制。同步在长安链中作为基础且重要的功能,以事件驱动方式运行,又引进了诸多性能提升措施,但对于性能的精益求精需要我们在接下来的工作中进一步挖掘同步模块中值得优化的空间。