一、BlockChain的insertChain方法
1.1 前言
本章节介绍BlockChain模块插入一个新区块的流程。一个新区块的来源有两种可能性,第一种可能性是本节点挖矿成功,要调用BlockChain模块向本地区块链上插入,第二种可能性是节点从网络上的其他节点收到一个区块,调用BlockChain模块插入本地区块链。将一个区块插入区块链是调用BlockChain的insertChain函数,本章节详细介绍insertChain流程。
1.2 insertChain函数
inertChain函数是功能是将一组区块批量插入区块链,inesrtChain函数会检查这一组区块是否是首尾相接。检查无误后会校验区块头和区块体,然后还需要校验状态是不是正确。最后将区块插入区块链,需要注意的是能插入区块链不一定能插入规范链,在插入的时候会具体判断是否能插入规范链,如果不能插入规范链就是一条分叉。
上面代码的第2步,使用了bc.engine.VerifyHeaders函数去验证区块的区块头,这个函数传入了两个切片:
这两个切片的长度是相同的,第一个切片传入的是这组区块的区块头 ,第二组切片指定要验证哪些区块头,如果某个区块要验证,则在seals切片上对应位置置为true。我们可以看到代码里面把所有位置都置为了true,表示要验证headers切片里的所有区块头。
另外bc.engine.VerifyHeaders是异步检查,调完之后会直接返回,继续往下执行。它会返回两个管道abort和result,第一个管道可以命令VerifyHeaders函数停止验证,第二个管道是可以从其中等待验证结果,VerifyHeaders函数会保证验证结果返回的顺序和我们传入的headers切片的顺序相同。所以第3步验证区块头的时候重新用for循环遍历chain切片时,for循环的第一次执行,result管道返回的必然是chain中第一个区块的区块头的验证结果。
第6步处理ErrFutureBlock错误返回值时,如果待插入的区块在数据库中已经存在,说明它是有可能是一个分叉,如果它的区块高度比当前规范链的头区块要大,那么就重新在插入一下,因为有可能这个区块的所在的分叉的总难度比当前规范链大,如果真是这样的话,需要把这条分叉升级为规范链,重新插入的过程会检查是否是规范链,如果是就会升级。
第7步,当收到一个区块它的时间戳大于当前时间15秒,小于30秒,节点不会将这个区块丢弃,而是将这个区块放入到futureBlocks列表,我们在上一章节讲到NewBlockChain函数最后会启动一个go程定时来检查这些区块能不能插入到区块链,如果能插,就会再次调用insertChain来插入。
第10步是验证区块的状态,验证区块的状态流程是基于父区块的世界状态去执行待插入区块的里的所有交易,生成新的世界状态,然后调用bc.Validator().ValidateState去验证新的状态是不是正确,其中重要的一个环节就是比较新生成的状态树树根和区块头中的状态树树根是否相同。
第11步调用WriteBlockWithState将区块写入区块链,这个函数会去判断这个区块写入的是一个分叉还是规范链,当然这个插入的区块有可能将一个原来的分叉升级为规范链,原来的规范链变成一条分叉。
1.3 总结
本章节主要介绍insertChain的流程,从上面的分析可以看出insertChain函数里面主要是实现了对区块的校验,包括区块头和区块体,校验通过之后会调用WriteBlockWithState函数将区块写入区块链,而真正写入的过程在WriteBlockWithState中。下一章节我们分析WriteBlockWithState函数流程。
-END-
领取专属 10元无门槛券
私享最新 技术干货