Neo2的区块同步机制
在Neo2中,同步区块的过程比较复杂。
主要分为两个步骤:
● 同步区块头
● 同步区块
如下图所示,是Neo2同步机制流程图。
同步区块头对应1、2两步:
1. 当本地节点发现本地的区块链高度低于远程节点的高度时,会发送GetHeaders(header hash)消息,其中header hash为本地高度最高的区块头对应的hash。
2. 远程节点收到请求后,会从header hash对应的header开始,返回指定数量的Headers到本地节点(默认最大Headers请求数为2000)。
同步完区块头后便会开始同步区块,该过程对应图中3-7步。
3. 同步完区块头后,会找到本地高度最高的区块对应的hash。
4. 根据找到的hash向远程节点发送GetBlock(hash)消息。
5. 远程节点收到请求后,获取开始区块对应的hash以及请求的区块数量count(默认最大请求数为500)。然后从该hash开始,依次获取后面的count个区块对应的hash值,并将所有hash放入一个hash[]。获取完成后将hash[]作为一个Inv消息返回给本地节点。
6. 本地节点收到Inv消息后,会对Inv里的hash[]进行过滤,并将过滤后的hash[]放入GetData请求中,发送给远程节点。
7. 远程节点收到GetData(hash[])请求,会获取hash[]中的每个hash对应的区块,并将区块发送给本地节点。
Neo2区块同步机制存在的问题
Neo2区块同步机制过程比较复杂,主要有以下问题:
● 区块不分叉
先同步区块头再同步区块的模式在可能分叉的区块链中具有显著的优势,因为区块头同步非常快并且可以快速选出更长的链。但是Neo使用的共识算法决定了其区块链不会分叉。因此,该同步模式已经不适用于Neo了。如果我们不再使用该模式,则可以节省区块头同步的网络开销。
● GetBlocks命令效率低,且名称具有误导性
当前的GetBlocks命令效率很低,并且名称具有误导性。从名称来看,我们将收到区块,但实际上,我们会收到一个包含哈希数组的Inv消息。然后对Inv里的hash[]进行过滤,最后使用GetData(hash[])命令才可以获取到对应区块。
Neo3的新区块同步机制设计
根据Neo2区块同步机制中存在的一些问题,我们在Neo3中对区块同步机制做了如下的改进:
1. 删除了区块头同步的机制,改为直接使用区块高度同步区块。
2. 添加了GetBlockData(IndexStart,count)消息,可以直接获取从IndexStart开始的count个区块,不用再通过Inv消息进行获取,减少了网络通信开销。
3. 增加了SyncManager,对区块同步任务进行管理。
如下图所示,是Neo3区块同步机制的流程图。
主要分为以下几个步骤:
1. 首先通过PingPong消息与远程节点相互同步区块链高度。
2. 如果本地区块链高度低于远程节点高度,则发送GetBlockData消息获取缺少的区块。
3. 远程节点将请求区块依次发送给本地节点。
4. SyncManger记录收到的区块Index。
5. 将收到的区块交给本地节点处理。
6. 本地验证区块的合法性。如果是合法区块,则发送Persisted Block Index给SyncManager,该块同步流程结束。
7. 如果是非法区块,则发送Invalid Block Index给SyncManager,SyncManager将会重新选择其他节点分配任务。并对发送非法区块的节点做标记。
Neo3的新区块同步机制测试及结论
针对Neo2和Neo3的区块同步机制,我们在不同情况下对区块同步速度做了如下测试:
安装StatesDumper 情况下,从0高度开始,P2P区块同步时间(单位:秒)提升了大约7倍。
结论
Neo3的新区块同步机制有效减少了在区块同步中的网络通信开销,极大提升了P2P区块同步的速度。同时也为节点健康程度检测打下了基础。
注:以上方案还在审核阶段,不能代表最终设计方案。