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  }