github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/eth/sync.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2015 Go Ethereum作者 10 //此文件是Go以太坊库的一部分。 11 // 12 //Go-Ethereum库是免费软件:您可以重新分发它和/或修改 13 //根据GNU发布的较低通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊图书馆的发行目的是希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU较低的通用公共许可证,了解更多详细信息。 21 // 22 //你应该收到一份GNU较低级别的公共许可证副本 23 //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package eth 26 27 import ( 28 "math/rand" 29 "sync/atomic" 30 "time" 31 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/core/types" 34 "github.com/ethereum/go-ethereum/eth/downloader" 35 "github.com/ethereum/go-ethereum/log" 36 "github.com/ethereum/go-ethereum/p2p/discover" 37 ) 38 39 const ( 40 forceSyncCycle = 10 * time.Second //强制同步的时间间隔,即使没有可用的对等机 41 minDesiredPeerCount = 5 //开始同步所需的对等机数量 42 43 //这是由TxSyncLoop发送的事务包的目标大小。 44 //如果单个事务超过此大小,包可能会大于此值。 45 txsyncPackSize = 100 * 1024 46 ) 47 48 type txsync struct { 49 p *peer 50 txs []*types.Transaction 51 } 52 53 //SyncTransactions开始将所有当前挂起的事务发送给给定的对等方。 54 func (pm *ProtocolManager) syncTransactions(p *peer) { 55 var txs types.Transactions 56 pending, _ := pm.txpool.Pending() 57 for _, batch := range pending { 58 txs = append(txs, batch...) 59 } 60 if len(txs) == 0 { 61 return 62 } 63 select { 64 case pm.txsyncCh <- &txsync{p, txs}: 65 case <-pm.quitSync: 66 } 67 } 68 69 //TxSyncLoop负责每个新事务的初始事务同步 70 //连接。当一个新的对等点出现时,我们会中继所有当前挂起的 71 //交易。为了尽量减少出口带宽的使用,我们发送 72 //一次将小数据包中的事务打包到一个对等机。 73 func (pm *ProtocolManager) txsyncLoop() { 74 var ( 75 pending = make(map[discover.NodeID]*txsync) 76 sending = false //发送是否处于活动状态 77 pack = new(txsync) //正在发送的包 78 done = make(chan error, 1) //发送的结果 79 ) 80 81 //发送从同步开始发送一组事务。 82 send := func(s *txsync) { 83 //用达到目标大小的事务填充包。 84 size := common.StorageSize(0) 85 pack.p = s.p 86 pack.txs = pack.txs[:0] 87 for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { 88 pack.txs = append(pack.txs, s.txs[i]) 89 size += s.txs[i].Size() 90 } 91 //删除将发送的事务。 92 s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] 93 if len(s.txs) == 0 { 94 delete(pending, s.p.ID()) 95 } 96 //在后台发送包。 97 s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size) 98 sending = true 99 go func() { done <- pack.p.SendTransactions(pack.txs) }() 100 } 101 102 //选择下一个挂起的同步。 103 pick := func() *txsync { 104 if len(pending) == 0 { 105 return nil 106 } 107 n := rand.Intn(len(pending)) + 1 108 for _, s := range pending { 109 if n--; n == 0 { 110 return s 111 } 112 } 113 return nil 114 } 115 116 for { 117 select { 118 case s := <-pm.txsyncCh: 119 pending[s.p.ID()] = s 120 if !sending { 121 send(s) 122 } 123 case err := <-done: 124 sending = false 125 //停止跟踪导致发送失败的对等机。 126 if err != nil { 127 pack.p.Log().Debug("Transaction send failed", "err", err) 128 delete(pending, pack.p.ID()) 129 } 130 //安排下一次发送。 131 if s := pick(); s != nil { 132 send(s) 133 } 134 case <-pm.quitSync: 135 return 136 } 137 } 138 } 139 140 //同步器负责定期与网络同步,两者都是 141 //下载哈希和块以及处理公告处理程序。 142 func (pm *ProtocolManager) syncer() { 143 //启动并确保清除同步机制 144 pm.fetcher.Start() 145 defer pm.fetcher.Stop() 146 defer pm.downloader.Terminate() 147 148 //等待不同事件触发同步操作 149 forceSync := time.NewTicker(forceSyncCycle) 150 defer forceSync.Stop() 151 152 for { 153 select { 154 case <-pm.newPeerCh: 155 //确保我们有同行可供选择,然后同步 156 if pm.peers.Len() < minDesiredPeerCount { 157 break 158 } 159 go pm.synchronise(pm.peers.BestPeer()) 160 161 case <-forceSync.C: 162 //即使没有足够的对等点,也强制同步 163 go pm.synchronise(pm.peers.BestPeer()) 164 165 case <-pm.noMorePeers: 166 return 167 } 168 } 169 } 170 171 //同步尝试同步我们的本地块链与远程对等。 172 func (pm *ProtocolManager) synchronise(peer *peer) { 173 //如果没有对等点,则短路 174 if peer == nil { 175 return 176 } 177 //确保同行的TD高于我们自己的TD 178 currentBlock := pm.blockchain.CurrentBlock() 179 td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) 180 181 pHead, pTd := peer.Head() 182 if pTd.Cmp(td) <= 0 { 183 return 184 } 185 //否则,尝试与下载程序同步 186 mode := downloader.FullSync 187 if atomic.LoadUint32(&pm.fastSync) == 1 { 188 //已显式请求并授予快速同步 189 mode = downloader.FastSync 190 } else if currentBlock.NumberU64() == 0 && pm.blockchain.CurrentFastBlock().NumberU64() > 0 { 191 //数据库似乎是空的,因为当前块是Genesis。然而快速 192 //块在前面,因此在某个点为该节点启用了快速同步。 193 //唯一可能发生这种情况的场景是用户手动(或通过 194 //坏块)将快速同步节点回滚到同步点以下。在这种情况下 195 //但是重新启用快速同步是安全的。 196 atomic.StoreUint32(&pm.fastSync, 1) 197 mode = downloader.FastSync 198 } 199 200 if mode == downloader.FastSync { 201 //确保我们正在同步的对等机的总难度更高。 202 if pm.blockchain.GetTdByHash(pm.blockchain.CurrentFastBlock().Hash()).Cmp(pTd) >= 0 { 203 return 204 } 205 } 206 207 //运行同步循环,并禁用快速同步(如果我们已通过透视图块) 208 if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { 209 return 210 } 211 if atomic.LoadUint32(&pm.fastSync) == 1 { 212 log.Info("Fast sync complete, auto disabling") 213 atomic.StoreUint32(&pm.fastSync, 0) 214 } 215 atomic.StoreUint32(&pm.acceptTxs, 1) //标记初始同步完成 216 if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { 217 //我们已经完成了一个同步循环,通知所有对等方新状态。这条路是 218 //在需要通知网关节点的星型拓扑网络中至关重要 219 //它的所有过时的新块的可用性对等。这次失败 220 //场景通常会出现在私人网络和黑客网络中, 221 //连接性能下降,但对主网来说也应该是健康的 222 //更可靠地更新对等点或本地TD状态。 223 go pm.BroadcastBlock(head, false) 224 } 225 }