1、兄弟连Go语言+区块链技术培训以太坊源码分析35ethfetcher源码分析兄弟连Go语言+区块链技术培训以太坊源码分析(35)eth-fetcher源码分析fetcher包含基于块通知的同步。当我们接收到NewBlockHashesMsg消息得时候,我们只收到了很多Block的hash值。 需要通过hash值来同步区块,然后更新本地区块链。 fetcher就提供了这样的功能。数据结构/ announce is the hash notification of the availability of a new block in the/ network./ announce 是一个hash通
2、知,表示网络上有合适的新区块出现。type announce struct hash common.Hash / Hash of the block being announced /新区块的hash值number uint64 / Number of the block being announced (0 = unknown | old protocol) 区块的高度值,header *types.Header / Header of the block partially reassembled (new protocol)重新组装的区块头time time.Time / Timesta
3、mp of the announcementorigin string / Identifier of the peer originating the notificationfetchHeader headerRequesterFn / Fetcher function to retrieve the header of an announced block 获取区块头的函数指针, 里面包含了peer的信息。就是说找谁要这个区块头fetchBodies bodyRequesterFn / Fetcher function to retrieve the body of an announc
4、ed block 获取区块体的函数指针/ headerFilterTask represents a batch of headers needing fetcher filtering.type headerFilterTask struct peer string / The source peer of block headersheaders *types.Header / Collection of headers to filtertime time.Time / Arrival time of the headers/ headerFilterTask represents a
5、batch of block bodies (transactions and uncles)/ needing fetcher filtering.type bodyFilterTask struct peer string / The source peer of block bodiestransactions *types.Transaction / Collection of transactions per block bodiesuncles *types.Header / Collection of uncles per block bodiestime time.Time /
6、 Arrival time of the blocks contents/ inject represents a schedules import operation./ 当节点收到NewBlockMsg的消息时候,会插入一个区块type inject struct origin stringblock *types.Block/ Fetcher is responsible for accumulating block announcements from various peers/ and scheduling them for retrieval.type Fetcher struc
7、t / Various event channelsnotify chan *announce/announce的通道,inject chan *inject/inject的通道blockFilter chan chan *types.Block /通道的通道?headerFilter chan chan *headerFilterTaskbodyFilter chan chan *bodyFilterTaskdone chan common.Hashquit chan struct/ Announce statesannounces mapstringint / Per peer annou
8、nce counts to prevent memory exhaustion key是peer的名字, value是announce的count, 为了避免内存占用太大。announced mapcommon.Hash*announce / Announced blocks, scheduled for fetching 等待调度fetching的announcefetching mapcommon.Hash*announce / Announced blocks, currently fetching 正在fetching的announcefetched mapcommon.Hash*an
9、nounce / Blocks with headers fetched, scheduled for body retrieval / 已经获取区块头的,等待获取区块bodycompleting mapcommon.Hash*announce / Blocks with headers, currently body-completing /头和体都已经获取完成的announce/ Block cachequeue *prque.Prque / Queue containing the import operations (block number sorted) /包含了import操作的
10、队列(按照区块号排列)queues mapstringint / Per peer block counts to prevent memory exhaustion key是peer,value是block数量。 避免内存消耗太多。queued mapcommon.Hash*inject / Set of already queued blocks (to dedup imports) 已经放入队列的区块。 为了去重。/ Callbacks 依赖了一些回调函数。getBlock blockRetrievalFn / Retrieves a block from the local chain
11、verifyHeader headerVerifierFn / Checks if a blocks headers have a valid proof of workbroadcastBlock blockBroadcasterFn / Broadcasts a block to connected peerschainHeight chainHeightFn / Retrieves the current chains heightinsertChain chainInsertFn / Injects a batch of blocks into the chaindropPeer pe
12、erDropFn / Drops a peer for misbehaving/ Testing hooks 仅供测试使用。announceChangeHook func(common.Hash, bool) / Method to call upon adding or deleting a hash from the announce listqueueChangeHook func(common.Hash, bool) / Method to call upon adding or deleting a block from the import queuefetchingHook fu
13、nc(common.Hash) / Method to call upon starting a block (eth/61) or header (eth/62) fetchcompletingHook func(common.Hash) / Method to call upon starting a block body fetch (eth/62)importedHook func(*types.Block) / Method to call upon successful block import (both eth/61 and eth/62)启动fetcher, 直接启动了一个g
14、oroutine来处理。 这个函数有点长。 后续再分析。/ Start boots up the announcement based synchroniser, accepting and processing/ hash notifications and block fetches until termination requested.func (f *Fetcher) Start() go f.loop()loop函数函数太长。 我先帖一个省略版本的出来。fetcher通过四个map(announced,fetching,fetched,completing )记录了announce
15、的状态(等待fetch,正在fetch,fetch完头等待fetch body, fetch完成)。 loop其实通过定时器和各种消息来对各种map里面的announce进行状态转换。/ Loop is the main fetcher loop, checking and processing various notification/ events.func (f *Fetcher) loop() / Iterate the block fetching until a quit is requestedfetchTimer := time.NewTimer(0) /fetch的定时器。c
16、ompleteTimer := time.NewTimer(0) / compelte的定时器。for / Clean up any expired block fetches/ 如果fetching的时间超过5秒,那么放弃掉这个fetchingfor hash, announce := range f.fetching if time.Since(announce.time) fetchTimeout f.forgetHash(hash)/ Import any queued blocks that could potentially fit/ 这个fetcher.queue里面缓存了已经完
17、成fetch的block等待按照顺序插入到本地的区块链中/fetcher.queue是一个优先级队列。 优先级别就是他们的区块号的负数,这样区块数小的排在最前面。height := f.chainHeight()for !f.queue.Empty() /op := f.queue.PopItem().(*inject)if f.queueChangeHook != nil f.queueChangeHook(op.block.Hash(), false)/ If too high up the chain or phase, continue laternumber := op.block.
18、NumberU64()if number height+1 /当前的区块的高度太高,还不能importf.queue.Push(op, -float32(op.block.NumberU64()if f.queueChangeHook != nil f.queueChangeHook(op.block.Hash(), true)break/ Otherwise if fresh and still unknown, try and importhash := op.block.Hash()if number+maxUncleDist height | f.getBlock(hash) != n
19、il / 区块的高度太低 低于当前的height-maxUncleDist/ 或者区块已经被import了f.forgetBlock(hash)continue/ 插入区块f.insert(op.origin, op.block)/ Wait for an outside event to occurselect case -f.quit:/ Fetcher terminating, abort all operationsreturncase notification := -f.notify: /在接收到NewBlockHashesMsg的时候,对于本地区块链还没有的区块的hash值会调用
20、fetcher的Notify方法发送到notify通道。.case op := -f.inject: / 在接收到NewBlockMsg的时候会调用fetcher的Enqueue方法,这个方法会把当前接收到的区块发送到inject通道。.f.enqueue(op.origin, op.block)case hash := -f.done: /当完成一个区块的import的时候会发送该区块的hash值到done通道。.case -fetchTimer.C: / fetchTimer定时器,定期对需要fetch的区块头进行fetch.case -completeTimer.C: / complet
21、eTimer定时器定期对需要fetch的区块体进行fetch.case filter := -f.headerFilter: /当接收到BlockHeadersMsg的消息的时候(接收到一些区块头),会把这些消息投递到headerFilter队列。 这边会把属于fetcher请求的数据留下,其他的会返回出来,给其他系统使用。.case filter := -f.bodyFilter: /当接收到BlockBodiesMsg消息的时候,会把这些消息投递给bodyFilter队列。这边会把属于fetcher请求的数据留下,其他的会返回出来,给其他系统使用。.# 区块头的过滤流程# FilterHe
22、aders请求FilterHeaders方法在接收到BlockHeadersMsg的时候被调用。这个方法首先投递了一个channel filter到headerFilter。 然后往filter投递了一个headerFilterTask的任务。然后阻塞等待filter队列返回消息。/ FilterHeaders extracts all the headers that were explicitly requested by the fetcher,/ returning those that should be handled differently.func (f *Fetcher) F
23、ilterHeaders(peer string, headers *types.Header, time time.Time) *types.Header log.Trace(Filtering headers, peer, peer, headers, len(headers)/ Send the filter channel to the fetcherfilter := make(chan *headerFilterTask)select case f.headerFilter - filter:case -f.quit:return nil/ Request the filterin
24、g of the header listselect case filter - &headerFilterTaskpeer: peer, headers: headers, time: time:case -f.quit:return nil/ Retrieve the headers remaining after filteringselect case task := -filter:return task.headerscase -f.quit:return nil# headerFilter的处理这个处理在loop()的goroutine中。case filter := -f.headerFilter:/ Headers arrived from a remote peer. Extract those that were explicitly/ requested by the fetcher, and return everything else so its delivered/ to other parts of the system.var task *headerFilterTaskselect case task = -filter:case -f.quit:returnheaderFilter
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1