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