github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/eth/downloader/statesync.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 //版权所有2017 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 downloader 26 27 import ( 28 "fmt" 29 "hash" 30 "sync" 31 "time" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/state" 36 "github.com/ethereum/go-ethereum/crypto/sha3" 37 "github.com/ethereum/go-ethereum/ethdb" 38 "github.com/ethereum/go-ethereum/log" 39 "github.com/ethereum/go-ethereum/trie" 40 ) 41 42 //statereq表示一批状态获取请求,分组到 43 //单个数据检索网络包。 44 type stateReq struct { 45 items []common.Hash //要下载的状态项的哈希 46 tasks map[common.Hash]*stateTask //下载任务以跟踪以前的尝试 47 timeout time.Duration //完成此操作的最大往返时间 48 timer *time.Timer //RTT超时过期时要触发的计时器 49 peer *peerConnection //我们请求的同伴 50 response [][]byte //对等机的响应数据(超时为零) 51 dropped bool //标记对等机是否提前退出 52 } 53 54 //如果此请求超时,则返回timed out。 55 func (req *stateReq) timedOut() bool { 56 return req.response == nil 57 } 58 59 //StateSyncStats是状态检索期间要报告的进度统计信息的集合。 60 //同步到RPC请求并显示在用户日志中。 61 type stateSyncStats struct { 62 processed uint64 //处理的状态条目数 63 duplicate uint64 //两次下载的状态条目数 64 unexpected uint64 //接收到的非请求状态条目数 65 pending uint64 //仍挂起状态条目数 66 } 67 68 //SyncState开始使用给定的根哈希下载状态。 69 func (d *Downloader) syncState(root common.Hash) *stateSync { 70 s := newStateSync(d, root) 71 select { 72 case d.stateSyncStart <- s: 73 case <-d.quitCh: 74 s.err = errCancelStateFetch 75 close(s.done) 76 } 77 return s 78 } 79 80 //StateFetcher管理活动状态同步并接受请求 81 //代表它。 82 func (d *Downloader) stateFetcher() { 83 for { 84 select { 85 case s := <-d.stateSyncStart: 86 for next := s; next != nil; { 87 next = d.runStateSync(next) 88 } 89 case <-d.stateCh: 90 //不运行同步时忽略状态响应。 91 case <-d.quitCh: 92 return 93 } 94 } 95 } 96 97 //runstatesync运行状态同步,直到完成或另一个根目录为止。 98 //请求将哈希切换到。 99 func (d *Downloader) runStateSync(s *stateSync) *stateSync { 100 var ( 101 active = make(map[string]*stateReq) //当前飞行请求 102 finished []*stateReq //已完成或失败的请求 103 timeout = make(chan *stateReq) //活动请求超时 104 ) 105 defer func() { 106 //退出时取消活动请求计时器。还可以将对等机设置为空闲,以便 107 //可用于下一次同步。 108 for _, req := range active { 109 req.timer.Stop() 110 req.peer.SetNodeDataIdle(len(req.items)) 111 } 112 }() 113 //运行状态同步。 114 go s.run() 115 defer s.Cancel() 116 117 //倾听同伴离开事件以取消分配的任务 118 peerDrop := make(chan *peerConnection, 1024) 119 peerSub := s.d.peers.SubscribePeerDrops(peerDrop) 120 defer peerSub.Unsubscribe() 121 122 for { 123 //如果有第一个缓冲元素,则启用发送。 124 var ( 125 deliverReq *stateReq 126 deliverReqCh chan *stateReq 127 ) 128 if len(finished) > 0 { 129 deliverReq = finished[0] 130 deliverReqCh = s.deliver 131 } 132 133 select { 134 //StateSync生命周期: 135 case next := <-d.stateSyncStart: 136 return next 137 138 case <-s.done: 139 return nil 140 141 //将下一个完成的请求发送到当前同步: 142 case deliverReqCh <- deliverReq: 143 //移出第一个请求,但也为GC将空槽设置为零 144 copy(finished, finished[1:]) 145 finished[len(finished)-1] = nil 146 finished = finished[:len(finished)-1] 147 148 //处理传入状态包: 149 case pack := <-d.stateCh: 150 //放弃任何未请求的数据(或以前超时的数据) 151 req := active[pack.PeerId()] 152 if req == nil { 153 log.Debug("Unrequested node data", "peer", pack.PeerId(), "len", pack.Items()) 154 continue 155 } 156 //完成请求并排队等待处理 157 req.timer.Stop() 158 req.response = pack.(*statePack).states 159 160 finished = append(finished, req) 161 delete(active, pack.PeerId()) 162 163 //处理掉的对等连接: 164 case p := <-peerDrop: 165 //Skip if no request is currently pending 166 req := active[p.id] 167 if req == nil { 168 continue 169 } 170 //完成请求并排队等待处理 171 req.timer.Stop() 172 req.dropped = true 173 174 finished = append(finished, req) 175 delete(active, p.id) 176 177 //处理超时请求: 178 case req := <-timeout: 179 //如果对等机已经在请求其他东西,请忽略过时的超时。 180 //当超时和传递同时发生时,就会发生这种情况, 181 //导致两种途径触发。 182 if active[req.peer.id] != req { 183 continue 184 } 185 //将超时数据移回下载队列 186 finished = append(finished, req) 187 delete(active, req.peer.id) 188 189 //跟踪传出状态请求: 190 case req := <-d.trackStateReq: 191 //如果此对等机已经存在活动请求,则说明存在问题。在 192 //理论上,trie节点调度决不能将两个请求分配给同一个 193 //同龄人。然而,在实践中,对等端可能会收到一个请求,断开连接并 194 //在前一次超时前立即重新连接。在这种情况下,第一个 195 //请求永远不会得到满足,唉,我们不能悄悄地改写它,就像这样。 196 //导致有效的请求丢失,并同步卡住。 197 if old := active[req.peer.id]; old != nil { 198 log.Warn("Busy peer assigned new state fetch", "peer", old.peer.id) 199 200 //确保前一个不会被误丢 201 old.timer.Stop() 202 old.dropped = true 203 204 finished = append(finished, old) 205 } 206 //如果对等端停止,启动计时器通知同步循环。 207 req.timer = time.AfterFunc(req.timeout, func() { 208 select { 209 case timeout <- req: 210 case <-s.done: 211 //在不太可能发生的情况下,防止定时器泄漏。 212 //在退出runstatesync之前,计时器将被激发。 213 } 214 }) 215 active[req.peer.id] = req 216 } 217 } 218 } 219 220 //statesync计划下载特定state trie定义的请求 221 //通过给定的状态根。 222 type stateSync struct { 223 d *Downloader //用于访问和管理当前对等集的下载程序实例 224 225 sched *trie.Sync //状态trie-sync计划程序定义任务 226 keccak hash.Hash //KECCAK256哈希验证交付 227 tasks map[common.Hash]*stateTask //当前排队等待检索的任务集 228 229 numUncommitted int 230 bytesUncommitted int 231 232 deliver chan *stateReq //传递通道多路复用对等响应 233 cancel chan struct{} //发送终止请求信号的通道 234 cancelOnce sync.Once //确保Cancel只被调用一次 235 done chan struct{} //通道到信号终止完成 236 err error //同步期间发生的任何错误(在完成前设置) 237 } 238 239 //statetask表示单个trie节点下载任务,包含一组 240 //对等机已尝试从中检索以检测停止的同步并中止。 241 type stateTask struct { 242 attempts map[string]struct{} 243 } 244 245 //newstatesync创建新的状态trie下载计划程序。此方法不 246 //开始同步。用户需要调用run来启动。 247 func newStateSync(d *Downloader, root common.Hash) *stateSync { 248 return &stateSync{ 249 d: d, 250 sched: state.NewStateSync(root, d.stateDB), 251 keccak: sha3.NewKeccak256(), 252 tasks: make(map[common.Hash]*stateTask), 253 deliver: make(chan *stateReq), 254 cancel: make(chan struct{}), 255 done: make(chan struct{}), 256 } 257 } 258 259 //运行启动任务分配和响应处理循环,阻止直到 260 //它结束,并最终通知等待循环的任何Goroutines 261 //完成。 262 func (s *stateSync) run() { 263 s.err = s.loop() 264 close(s.done) 265 } 266 267 //等待块,直到同步完成或取消。 268 func (s *stateSync) Wait() error { 269 <-s.done 270 return s.err 271 } 272 273 //取消取消同步并等待其关闭。 274 func (s *stateSync) Cancel() error { 275 s.cancelOnce.Do(func() { close(s.cancel) }) 276 return s.Wait() 277 } 278 279 //循环是状态trie-sync的主事件循环。它负责 280 //向对等方分配新任务(包括将其发送给对等方)以及 281 //用于处理入站数据。注意,循环不直接 282 //从对等端接收数据,而不是在下载程序中缓冲这些数据, 283 //按这里异步。原因是将处理与数据接收分离 284 //超时。 285 func (s *stateSync) loop() (err error) { 286 //侦听新的对等事件以将任务分配给它们 287 newPeer := make(chan *peerConnection, 1024) 288 peerSub := s.d.peers.SubscribeNewPeers(newPeer) 289 defer peerSub.Unsubscribe() 290 defer func() { 291 cerr := s.commit(true) 292 if err == nil { 293 err = cerr 294 } 295 }() 296 297 //继续分配新任务,直到同步完成或中止 298 for s.sched.Pending() > 0 { 299 if err = s.commit(false); err != nil { 300 return err 301 } 302 s.assignTasks() 303 //分配的任务,等待发生什么 304 select { 305 case <-newPeer: 306 //新对等机已到达,请尝试分配它的下载任务 307 308 case <-s.cancel: 309 return errCancelStateFetch 310 311 case <-s.d.cancelCh: 312 return errCancelStateFetch 313 314 case req := <-s.deliver: 315 //响应、断开连接或超时触发,如果停止,则丢弃对等机 316 log.Trace("Received node data response", "peer", req.peer.id, "count", len(req.response), "dropped", req.dropped, "timeout", !req.dropped && req.timedOut()) 317 if len(req.items) <= 2 && !req.dropped && req.timedOut() { 318 //2项是最低要求,即使超时,我们也没有用 319 //现在这个人。 320 log.Warn("Stalling state sync, dropping peer", "peer", req.peer.id) 321 s.d.dropPeer(req.peer.id) 322 } 323 //处理所有接收到的Blob并检查是否存在过时的传递 324 if err = s.process(req); err != nil { 325 log.Warn("Node data write error", "err", err) 326 return err 327 } 328 req.peer.SetNodeDataIdle(len(req.response)) 329 } 330 } 331 return nil 332 } 333 334 func (s *stateSync) commit(force bool) error { 335 if !force && s.bytesUncommitted < ethdb.IdealBatchSize { 336 return nil 337 } 338 start := time.Now() 339 b := s.d.stateDB.NewBatch() 340 if written, err := s.sched.Commit(b); written == 0 || err != nil { 341 return err 342 } 343 if err := b.Write(); err != nil { 344 return fmt.Errorf("DB write error: %v", err) 345 } 346 s.updateStats(s.numUncommitted, 0, 0, time.Since(start)) 347 s.numUncommitted = 0 348 s.bytesUncommitted = 0 349 return nil 350 } 351 352 //assign tasks尝试将新任务分配给所有空闲对等端,或者从 353 //当前正在重试批处理,或者从TIE同步本身获取新数据。 354 func (s *stateSync) assignTasks() { 355 //遍历所有空闲对等点,并尝试为其分配状态获取 356 peers, _ := s.d.peers.NodeDataIdlePeers() 357 for _, p := range peers { 358 //分配一批与估计的延迟/带宽成比例的获取 359 cap := p.NodeDataCapacity(s.d.requestRTT()) 360 req := &stateReq{peer: p, timeout: s.d.requestTTL()} 361 s.fillTasks(cap, req) 362 363 //如果为对等机分配了要获取的任务,则发送网络请求 364 if len(req.items) > 0 { 365 req.peer.log.Trace("Requesting new batch of data", "type", "state", "count", len(req.items)) 366 select { 367 case s.d.trackStateReq <- req: 368 req.peer.FetchNodeData(req.items) 369 case <-s.cancel: 370 case <-s.d.cancelCh: 371 } 372 } 373 } 374 } 375 376 //filltasks用最多n个状态下载来填充给定的请求对象 377 //要发送到远程对等机的任务。 378 func (s *stateSync) fillTasks(n int, req *stateReq) { 379 //从调度程序重新填充可用任务。 380 if len(s.tasks) < n { 381 new := s.sched.Missing(n - len(s.tasks)) 382 for _, hash := range new { 383 s.tasks[hash] = &stateTask{make(map[string]struct{})} 384 } 385 } 386 //查找尚未使用请求的对等方尝试的任务。 387 req.items = make([]common.Hash, 0, n) 388 req.tasks = make(map[common.Hash]*stateTask, n) 389 for hash, t := range s.tasks { 390 //当我们收集到足够多的请求时停止 391 if len(req.items) == n { 392 break 393 } 394 //跳过我们已经尝试过的来自此对等方的任何请求 395 if _, ok := t.attempts[req.peer.id]; ok { 396 continue 397 } 398 //将请求分配给该对等方 399 t.attempts[req.peer.id] = struct{}{} 400 req.items = append(req.items, hash) 401 req.tasks[hash] = t 402 delete(s.tasks, hash) 403 } 404 } 405 406 //进程迭代一批已交付状态数据,并注入每个项 407 //进入运行状态同步,重新排队请求但没有的任何项目 408 //交付。 409 func (s *stateSync) process(req *stateReq) error { 410 //收集处理状态并在收到有效数据时更新进度 411 duplicate, unexpected := 0, 0 412 413 defer func(start time.Time) { 414 if duplicate > 0 || unexpected > 0 { 415 s.updateStats(0, duplicate, unexpected, time.Since(start)) 416 } 417 }(time.Now()) 418 419 //对所有传递的数据进行迭代,并逐个注入到trie中 420 progress := false 421 422 for _, blob := range req.response { 423 prog, hash, err := s.processNodeData(blob) 424 switch err { 425 case nil: 426 s.numUncommitted++ 427 s.bytesUncommitted += len(blob) 428 progress = progress || prog 429 case trie.ErrNotRequested: 430 unexpected++ 431 case trie.ErrAlreadyProcessed: 432 duplicate++ 433 default: 434 return fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err) 435 } 436 if _, ok := req.tasks[hash]; ok { 437 delete(req.tasks, hash) 438 } 439 } 440 //将未完成的任务放回重试队列 441 npeers := s.d.peers.Len() 442 for hash, task := range req.tasks { 443 //如果节点确实传递了某些内容,则丢失的项可能是由于协议 444 //限制或以前的超时+延迟传递。两种情况都应该允许 445 //要重试丢失项的节点(以避免单点暂停)。 446 if len(req.response) > 0 || req.timedOut() { 447 delete(task.attempts, req.peer.id) 448 } 449 //如果我们已经请求节点太多次,可能是恶意的 450 //在没有人拥有正确数据的地方同步。中止。 451 if len(task.attempts) >= npeers { 452 return fmt.Errorf("state node %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers) 453 } 454 //缺少项,请放入重试队列。 455 s.tasks[hash] = task 456 } 457 return nil 458 } 459 460 //processNodeData尝试插入从远程服务器传递的trie节点数据blob 461 //查看状态trie,返回是否编写了有用的内容或 462 //发生错误。 463 func (s *stateSync) processNodeData(blob []byte) (bool, common.Hash, error) { 464 res := trie.SyncResult{Data: blob} 465 s.keccak.Reset() 466 s.keccak.Write(blob) 467 s.keccak.Sum(res.Hash[:0]) 468 committed, _, err := s.sched.Process([]trie.SyncResult{res}) 469 return committed, res.Hash, err 470 } 471 472 //updateStats触发各种状态同步进度计数器并显示日志 473 //供用户查看的消息。 474 func (s *stateSync) updateStats(written, duplicate, unexpected int, duration time.Duration) { 475 s.d.syncStatsLock.Lock() 476 defer s.d.syncStatsLock.Unlock() 477 478 s.d.syncStatsState.pending = uint64(s.sched.Pending()) 479 s.d.syncStatsState.processed += uint64(written) 480 s.d.syncStatsState.duplicate += uint64(duplicate) 481 s.d.syncStatsState.unexpected += uint64(unexpected) 482 483 if written > 0 || duplicate > 0 || unexpected > 0 { 484 log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "retry", len(s.tasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected) 485 } 486 if written > 0 { 487 rawdb.WriteFastTrieProgress(s.d.stateDB, s.d.syncStatsState.processed) 488 } 489 }