今天把以太坊 eth/handle.go处理newblockMsg消息的流程基本看完了,记录一下。
以太坊 eth/handle.go处理newblockMsg消息的主要流程:
Fetcher负责验证区块并插入区块,如果符合条件,还会重组主链区块。
如果收到的远程peer节点发来的区块TD-区块难度(即上个区块的TD) 大于 之前保存的peer最新的TD,则证明中间还差了一些区块没有同步,并且远程peer节点发来的区块TD-区块难度(即上个区块的TD) 大于 本地最新区块的TD,则调用downloader进行数据同步。
《1》pm.fetcher.Enqueue(p.id, request.Block)
1.1 Equeu会进入fetcher的loop函数里面的case op := <-f.inject:处理
propBroadcastInMeter.Mark(1)
f.enqueue(op.origin, op.block) 注意这个方法的enqueue的e是小写的。
这个方法会检查区块号是否和本地区块号相差太远,如果相差的距离小于maxUncleDist或者大于maxQueueDist,则forgetHash然后退出。
最后会给queue队列push一个block,然后在loop方法里会循环取出block,如果检查通过,就调用 f.insert(op.origin, op.block)方法
1.2 f.insert(op.origin, op.block)方法。
1. 检验区块,如果父hash在本地区块链,并且header通过验证的话没就立刻把区块广播出去。
go f.broadcastBlock(block, true)
2.然后通过在newProtocolManager时给fetcher注入的type chainInsertFn func(types.Blocks)方法,
调用blockchain.InsertChain(blocks)把区块插入主链中。InsertChain()会调用insertChain(),最终会调用
status, err := bc.WriteBlockWithState(block, receipts, state),该方法里如果发现区块td( >=0的情况判断) 符合条件,会调用reorg()方法重组主链。如果status是主链(CanonStatTy),则将会PostChainEvents()发送ChainEvent消息,如果status是侧链(SideStatTy),则会发送ChainSideEvent消息。
1.3 .如果执行insertChain导入成功,会调用newProtocolManager时给fetcher注入的blockBroadcasterFn
(实际上是protocolManager的BroadcastBlock方法)再次广播区块,这次只广播区块hash。
1.4 fetcher方法内所有内容执行完成后,就发送一个done的信号:defer func() { f.done <- hash }()
于是又跳回fetcher.loop方法里的case hash := <-f.done:去处理。主要会执行forgetHash和forgetBlock
f.forgetHash(hash)
f.forgetBlock(hash)
《2》go pm.synchronise(p)
2.1 如果收到peer发来的区块td 比 之前peer发来的最大td 还要小,则直接退出不处理。(这一点设计真贴心。。)
2.2 如果大于之前peer的最大Td,则调用downloader开始同步数据
接下里将会进行downloader的Synchronise方法,会再调用synchronise方法
1. 从peerSet里的peerConnection取出key为p.id的con连接对象,如果没有该ID的对象,则直接退出。
如果有,则调用d.syncWithPeer(p, hash, td)方法。
2. d.syncWithPeer(p, hash, td)方法
(1)调用d.fetchHeight(p)获取远程peer的高度
(2)调用d.findAncestor(p, height)获取本地节点和远程节点的共同祖先
(3)初始化下载队列,d.queue.Prepare(origin+1, d.mode)
(4)注入4个函数到fetchers:fetchers := []func() error{
func() error { return d.fetchHeaders(p, origin+1, pivot) }, // Headers are always retrieved
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
func() error { return d.processHeaders(origin+1, pivot, td) },
} 如果是全同步模式,还要注入:fetchers = append(fetchers, d.processFullSyncContent)
(5)调用d.spawnSync(fetchers)分别开始同步区块头,区块体和收据,并逐一进行处理。
2.3 把当前主链的最新区块hash广播给所有peers