github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/consensus/ethash/sealer.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:34</date>
    10  //</624450075187220480>
    11  
    12  
    13  package ethash
    14  
    15  import (
    16  	"bytes"
    17  	crand "crypto/rand"
    18  	"encoding/json"
    19  	"errors"
    20  	"math"
    21  	"math/big"
    22  	"math/rand"
    23  	"net/http"
    24  	"runtime"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/common/hexutil"
    30  	"github.com/ethereum/go-ethereum/consensus"
    31  	"github.com/ethereum/go-ethereum/core/types"
    32  	"github.com/ethereum/go-ethereum/log"
    33  )
    34  
    35  const (
    36  //StaleThreshold是可接受但有效的Ethash溶液的最大深度。
    37  	staleThreshold = 7
    38  )
    39  
    40  var (
    41  	errNoMiningWork      = errors.New("no mining work available yet")
    42  	errInvalidSealResult = errors.New("invalid or stale proof-of-work solution")
    43  )
    44  
    45  //
    46  //块的难度要求。
    47  func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
    48  //如果我们使用的是假战俘,只需立即返回一个0。
    49  	if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
    50  		header := block.Header()
    51  		header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
    52  		select {
    53  		case results <- block.WithSeal(header):
    54  		default:
    55  			log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
    56  		}
    57  		return nil
    58  	}
    59  //如果我们正在运行一个共享的POW,就委托密封它。
    60  	if ethash.shared != nil {
    61  		return ethash.shared.Seal(chain, block, results, stop)
    62  	}
    63  //创建一个运行程序及其所指向的多个搜索线程
    64  	abort := make(chan struct{})
    65  
    66  	ethash.lock.Lock()
    67  	threads := ethash.threads
    68  	if ethash.rand == nil {
    69  		seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
    70  		if err != nil {
    71  			ethash.lock.Unlock()
    72  			return err
    73  		}
    74  		ethash.rand = rand.New(rand.NewSource(seed.Int64()))
    75  	}
    76  	ethash.lock.Unlock()
    77  	if threads == 0 {
    78  		threads = runtime.NumCPU()
    79  	}
    80  	if threads < 0 {
    81  threads = 0 //允许禁用本地挖掘而不在本地/远程周围使用额外逻辑
    82  	}
    83  //将新工作推到远程密封器上
    84  	if ethash.workCh != nil {
    85  		ethash.workCh <- &sealTask{block: block, results: results}
    86  	}
    87  	var (
    88  		pend   sync.WaitGroup
    89  		locals = make(chan *types.Block)
    90  	)
    91  	for i := 0; i < threads; i++ {
    92  		pend.Add(1)
    93  		go func(id int, nonce uint64) {
    94  			defer pend.Done()
    95  			ethash.mine(block, id, nonce, abort, locals)
    96  		}(i, uint64(ethash.rand.Int63()))
    97  	}
    98  //等待密封终止或找到一个nonce
    99  	go func() {
   100  		var result *types.Block
   101  		select {
   102  		case <-stop:
   103  //外部中止,停止所有矿工线程
   104  			close(abort)
   105  		case result = <-locals:
   106  //其中一个线程找到一个块,中止所有其他线程
   107  			select {
   108  			case results <- result:
   109  			default:
   110  				log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
   111  			}
   112  			close(abort)
   113  		case <-ethash.update:
   114  //线程计数已根据用户请求更改,请重新启动
   115  			close(abort)
   116  			if err := ethash.Seal(chain, block, results, stop); err != nil {
   117  				log.Error("Failed to restart sealing after update", "err", err)
   118  			}
   119  		}
   120  //等待所有矿工终止并返回区块
   121  		pend.Wait()
   122  	}()
   123  	return nil
   124  }
   125  
   126  //Mine是工作矿工从
   127  //导致正确最终阻塞困难的种子。
   128  func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
   129  //从头中提取一些数据
   130  	var (
   131  		header  = block.Header()
   132  		hash    = ethash.SealHash(header).Bytes()
   133  		target  = new(big.Int).Div(two256, header.Difficulty)
   134  		number  = header.Number.Uint64()
   135  		dataset = ethash.dataset(number, false)
   136  	)
   137  //开始生成随机的nonce,直到我们中止或找到一个好的nonce
   138  	var (
   139  		attempts = int64(0)
   140  		nonce    = seed
   141  	)
   142  	logger := log.New("miner", id)
   143  	logger.Trace("Started ethash search for new nonces", "seed", seed)
   144  search:
   145  	for {
   146  		select {
   147  		case <-abort:
   148  //挖掘已终止,更新状态并中止
   149  			logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed)
   150  			ethash.hashrate.Mark(attempts)
   151  			break search
   152  
   153  		default:
   154  //我们不需要每次更新哈希率,所以在2^x次之后更新
   155  			attempts++
   156  			if (attempts % (1 << 15)) == 0 {
   157  				ethash.hashrate.Mark(attempts)
   158  				attempts = 0
   159  			}
   160  //计算这个nonce的pow值
   161  			digest, result := hashimotoFull(dataset.dataset, hash, nonce)
   162  			if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
   163  //一旦找到正确的标题,就用它创建一个新的标题
   164  				header = types.CopyHeader(header)
   165  				header.Nonce = types.EncodeNonce(nonce)
   166  				header.MixDigest = common.BytesToHash(digest)
   167  
   168  //密封并返回一个块(如果仍然需要)
   169  				select {
   170  				case found <- block.WithSeal(header):
   171  					logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce)
   172  				case <-abort:
   173  					logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
   174  				}
   175  				break search
   176  			}
   177  			nonce++
   178  		}
   179  	}
   180  //数据集在终结器中未映射。确保数据集保持活动状态
   181  //
   182  	runtime.KeepAlive(dataset)
   183  }
   184  
   185  //远程是一个独立的goroutine,用于处理与远程挖掘相关的内容。
   186  func (ethash *Ethash) remote(notify []string, noverify bool) {
   187  	var (
   188  		works = make(map[common.Hash]*types.Block)
   189  		rates = make(map[common.Hash]hashrate)
   190  
   191  		results      chan<- *types.Block
   192  		currentBlock *types.Block
   193  		currentWork  [4]string
   194  
   195  		notifyTransport = &http.Transport{}
   196  		notifyClient    = &http.Client{
   197  			Transport: notifyTransport,
   198  			Timeout:   time.Second,
   199  		}
   200  		notifyReqs = make([]*http.Request, len(notify))
   201  	)
   202  //notifywork通知所有指定的挖掘终结点
   203  //要处理的新工作。
   204  	notifyWork := func() {
   205  		work := currentWork
   206  		blob, _ := json.Marshal(work)
   207  
   208  		for i, url := range notify {
   209  //终止任何以前挂起的请求并创建新工作
   210  			if notifyReqs[i] != nil {
   211  				notifyTransport.CancelRequest(notifyReqs[i])
   212  			}
   213  			notifyReqs[i], _ = http.NewRequest("POST", url, bytes.NewReader(blob))
   214  			notifyReqs[i].Header.Set("Content-Type", "application/json")
   215  
   216  //同时将新工作推送到所有远程节点
   217  			go func(req *http.Request, url string) {
   218  				res, err := notifyClient.Do(req)
   219  				if err != nil {
   220  					log.Warn("Failed to notify remote miner", "err", err)
   221  				} else {
   222  					log.Trace("Notified remote miner", "miner", url, "hash", log.Lazy{Fn: func() common.Hash { return common.HexToHash(work[0]) }}, "target", work[2])
   223  					res.Body.Close()
   224  				}
   225  			}(notifyReqs[i], url)
   226  		}
   227  	}
   228  //makework为外部矿工创建工作包。
   229  //
   230  //工作包由3个字符串组成:
   231  //结果[0],32字节十六进制编码的当前块头POW哈希
   232  //结果[1],用于DAG的32字节十六进制编码种子哈希
   233  //结果[2],32字节十六进制编码边界条件(“目标”),2^256/难度
   234  //结果[3],十六进制编码的块号
   235  	makeWork := func(block *types.Block) {
   236  		hash := ethash.SealHash(block.Header())
   237  
   238  		currentWork[0] = hash.Hex()
   239  		currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
   240  		currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
   241  		currentWork[3] = hexutil.EncodeBig(block.Number())
   242  
   243  //追踪遥控封口机取出的封印工作。
   244  		currentBlock = block
   245  		works[hash] = block
   246  	}
   247  //SubNetwork验证提交的POW解决方案,返回
   248  //解决方案是否被接受(不可能是一个坏的POW以及
   249  //任何其他错误,例如没有挂起的工作或过时的挖掘结果)。
   250  	submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
   251  		if currentBlock == nil {
   252  			log.Error("Pending work without block", "sealhash", sealhash)
   253  			return false
   254  		}
   255  //确保提交的工作存在
   256  		block := works[sealhash]
   257  		if block == nil {
   258  			log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", currentBlock.NumberU64())
   259  			return false
   260  		}
   261  //验证提交结果的正确性。
   262  		header := block.Header()
   263  		header.Nonce = nonce
   264  		header.MixDigest = mixDigest
   265  
   266  		start := time.Now()
   267  		if !noverify {
   268  			if err := ethash.verifySeal(nil, header, true); err != nil {
   269  				log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", time.Since(start), "err", err)
   270  				return false
   271  			}
   272  		}
   273  //确保分配了结果通道。
   274  		if results == nil {
   275  			log.Warn("Ethash result channel is empty, submitted mining result is rejected")
   276  			return false
   277  		}
   278  		log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", time.Since(start))
   279  
   280  //解决方案似乎是有效的,返回矿工并通知接受。
   281  		solution := block.WithSeal(header)
   282  
   283  //提交的解决方案在验收范围内。
   284  		if solution.NumberU64()+staleThreshold > currentBlock.NumberU64() {
   285  			select {
   286  			case results <- solution:
   287  				log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
   288  				return true
   289  			default:
   290  				log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
   291  				return false
   292  			}
   293  		}
   294  //提交的块太旧,无法接受,请删除它。
   295  		log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
   296  		return false
   297  	}
   298  
   299  	ticker := time.NewTicker(5 * time.Second)
   300  	defer ticker.Stop()
   301  
   302  	for {
   303  		select {
   304  		case work := <-ethash.workCh:
   305  //使用新接收的块更新当前工作。
   306  //注意,相同的工作可能会过去两次,发生在更改CPU线程时。
   307  			results = work.results
   308  
   309  			makeWork(work.block)
   310  
   311  //通知并请求新工作可用性的URL
   312  			notifyWork()
   313  
   314  		case work := <-ethash.fetchWorkCh:
   315  //将当前采矿工作返回给远程矿工。
   316  			if currentBlock == nil {
   317  				work.errc <- errNoMiningWork
   318  			} else {
   319  				work.res <- currentWork
   320  			}
   321  
   322  		case result := <-ethash.submitWorkCh:
   323  //根据维护的采矿块验证提交的POW解决方案。
   324  			if submitWork(result.nonce, result.mixDigest, result.hash) {
   325  				result.errc <- nil
   326  			} else {
   327  				result.errc <- errInvalidSealResult
   328  			}
   329  
   330  		case result := <-ethash.submitRateCh:
   331  //按提交的值跟踪远程密封程序的哈希率。
   332  			rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
   333  			close(result.done)
   334  
   335  		case req := <-ethash.fetchRateCh:
   336  //收集远程密封程序提交的所有哈希率。
   337  			var total uint64
   338  			for _, rate := range rates {
   339  //这可能会溢出
   340  				total += rate.rate
   341  			}
   342  			req <- total
   343  
   344  		case <-ticker.C:
   345  //清除过时提交的哈希率。
   346  			for id, rate := range rates {
   347  				if time.Since(rate.ping) > 10*time.Second {
   348  					delete(rates, id)
   349  				}
   350  			}
   351  //清除过时的挂起块
   352  			if currentBlock != nil {
   353  				for hash, block := range works {
   354  					if block.NumberU64()+staleThreshold <= currentBlock.NumberU64() {
   355  						delete(works, hash)
   356  					}
   357  				}
   358  			}
   359  
   360  		case errc := <-ethash.exitCh:
   361  //如果ethash关闭,则退出远程回路并返回相关错误。
   362  			errc <- nil
   363  			log.Trace("Ethash remote sealer is exiting")
   364  			return
   365  		}
   366  	}
   367  }
   368