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  }