继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

以太坊之工作流程

紫衣仙女
关注TA
已关注
手记 231
粉丝 71
获赞 334

这篇跟上一篇的区别是同步数据完成之后,以太坊的工作流程是怎样的

初始化

eth/handler.go

  1. func (pm *ProtocolManager) Start(maxPeers int) {  

  2.     pm.maxPeers = maxPeers  

  3.   

  4.     // broadcast transactions  

  5.     pm.txCh = make(chan core.TxPreEvent, txChanSize)  

  6.     pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh)  

  7.     go pm.txBroadcastLoop()  

  8.   

  9.     // broadcast mined blocks  

  10.     pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{})  

  11.     go pm.minedBroadcastLoop()  

  12.   

  13.     // start sync handlers  

  14.     go pm.syncer()  

  15.     go pm.txsyncLoop()  

  16. }  

初始化Start的时候会新建4个goroutine:

txBroadcastLoop:  当收到新的tx时,会将它发送到连接的Peer

MinedBroadcastLoop:当挖出新的区块或者收到新的区块时,会将其发送到连接的Peer

syncer:有新的Peer连接时,会新建goroutine从td最高的Peer同步区块信息

txsyncLoop:当监听到txsyncCh有数据时(新连接peer时会把自身的txpool写到这个channel),新建goroutine发送tx记录到连接的Peer

广播交易

订阅tx消息

上面Start()的时候订阅了txCh消息


  1. func (pool *TxPool) SubscribeTxPreEvent(ch chan<- TxPreEvent) event.Subscription {  

  2.     return pool.scope.Track(pool.txFeed.Subscribe(ch))  

  3. }  

当自身节点收到了客户端的交易或者收到其他节点发来的交易加到txpool中时,Send订阅消息:core/tx_pool.go


  1. func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {  

  2.     // We've directly injected a replacement transaction, notify subsystems  

  3.     go pool.txFeed.Send(TxPreEvent{tx})  

  4.   

  5. }  

这样txBroadcastLoop中就读到了tx:


  1. func (self *ProtocolManager) txBroadcastLoop() {  

  2.     for {  

  3.         select {  

  4.         case event := <-self.txCh:  

  5.             self.BroadcastTx(event.Tx.Hash(), event.Tx)  

  6.   

  7.         // Err() channel will be closed when unsubscribing.  

  8.         case <-self.txSub.Err():  

  9.             return  

  10.         }  

  11.     }  

  12. }  

然后BroadCastTx就是发送TxMsg到所有连接的Peer,对端Peer收到Tx后加入自己的Txpool中

这样就达到交易扩散的目的

广播区块

上面Start()的时候订阅了NewMinedBlockEvent


  1. pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{})  

这是event.TypeMux自带的功能

当打包新区块成功时,会Post这个event:miner/worker.go


  1. func (self *worker) wait() {  

  2.     for {  

  3.         mustCommitNewWork := true  

  4.         for result := range self.recv {  

  5.             atomic.AddInt32(&self.atWork, -1)  

  6.   

  7.             if result == nil {  

  8.                 continue  

  9.             }  

  10.             block := result.Block  

  11.             work := result.Work  

  12.   

  13.             // Update the block hash in all logs since it is now available and not when the  

  14.             // receipt/log of individual transactions were created.  

  15.             for _, r := range work.receipts {  

  16.                 for _, l := range r.Logs {  

  17.                     l.BlockHash = block.Hash()  

  18.                 }  

  19.             }  

  20.             for _, log := range work.state.Logs() {  

  21.                 log.BlockHash = block.Hash()  

  22.             }  

  23.             stat, err := self.chain.WriteBlockWithState(block, work.receipts, work.state)  

  24.             if err != nil {  

  25.                 log.Error("Failed writing block to chain", "err", err)  

  26.                 continue  

  27.             }  

  28.             // check if canon block and write transactions  

  29.             if stat == core.CanonStatTy {  

  30.                 // implicit by posting ChainHeadEvent  

  31.                 mustCommitNewWork = false  

  32.             }  

  33.             // Broadcast the block and announce chain insertion event  

  34.             self.mux.Post(core.NewMinedBlockEvent{Block: block})  

  35.             var (  

  36.                 events []interface{}  

  37.                 logs   = work.state.Logs()  

  38.             )  

  39.             events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})  

  40.             if stat == core.CanonStatTy {  

  41.                 events = append(events, core.ChainHeadEvent{Block: block})  

  42.             }  

  43.             self.chain.PostChainEvents(events, logs)  

  44.   

  45.             // Insert the block into the set of pending ones to wait for confirmations  

  46.             self.unconfirmed.Insert(block.NumberU64(), block.Hash())  

  47.   

  48.             if mustCommitNewWork {  

  49.                 self.commitNewWork()  

  50.             }  

  51.         }  

  52.     }  

  53. }  

1 收到挖矿产生的block后WriteBlockWithState()写入本地leveldb

2 Post NewMinedBlockEvent


  1. func (self *ProtocolManager) minedBroadcastLoop() {  

  2.     // automatically stops if unsubscribe  

  3.     for obj := range self.minedBlockSub.Chan() {  

  4.         switch ev := obj.Data.(type) {  

  5.         case core.NewMinedBlockEvent:  

  6.             self.BroadcastBlock(ev.Block, true)  // First propagate block to peers  

  7.             self.BroadcastBlock(ev.Block, false) // Only then announce to the rest  

  8.         }  

  9.     }  

  10. }  

这里收到新的区块后,调用BroadcastBlock广播区块,看代码是调用了两次,一次第二个参数为true,一次为fasle


  1. func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {  

  2.     hash := block.Hash()  

  3.     peers := pm.peers.PeersWithoutBlock(hash)  

  4.   

  5.     // If propagation is requested, send to a subset of the peer  

  6.     if propagate {  

  7.         // Calculate the TD of the block (it's not imported yet, so block.Td is not valid)  

  8.         var td *big.Int  

  9.         if parent := pm.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil {  

  10.             td = new(big.Int).Add(block.Difficulty(), pm.blockchain.GetTd(block.ParentHash(), block.NumberU64()-1))  

  11.         } else {  

  12.             log.Error("Propagating dangling block", "number", block.Number(), "hash", hash)  

  13.             return  

  14.         }  

  15.         // Send the block to a subset of our peers  

  16.         transfer := peers[:int(math.Sqrt(float64(len(peers))))]  

  17.         for _, peer := range transfer {  

  18.             peer.SendNewBlock(block, td)  

  19.         }  

  20.         log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))  

  21.         return  

  22.     }  

  23.     // Otherwise if the block is indeed in out own chain, announce it  

  24.     if pm.blockchain.HasBlock(hash, block.NumberU64()) {  

  25.         for _, peer := range peers {  

  26.             peer.SendNewBlockHashes([]common.Hash{hash}, []uint64{block.NumberU64()})  

  27.         }  

  28.         log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))  

  29.     }  

  30. }  

第二个参数:

true:从节点中找开平方根个节点广播整个区块

false:所有节点广播区块的hash;其他节点收到区块的hash,再发送消息获取区块

这么做的目的是减少本节点的瞬时网络负载,免得一次广播太多的block造成自己的网络拥堵

这样就实现了区块的扩散


原文出处

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP