github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/consensus/ethash/ethash.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 //</624450075069779968> 11 12 13 //包ethash实现ethash工作证明共识引擎。 14 package ethash 15 16 import ( 17 "errors" 18 "fmt" 19 "math" 20 "math/big" 21 "math/rand" 22 "os" 23 "path/filepath" 24 "reflect" 25 "runtime" 26 "strconv" 27 "sync" 28 "sync/atomic" 29 "time" 30 "unsafe" 31 32 mmap "github.com/edsrzf/mmap-go" 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/consensus" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/log" 37 "github.com/ethereum/go-ethereum/metrics" 38 "github.com/ethereum/go-ethereum/rpc" 39 "github.com/hashicorp/golang-lru/simplelru" 40 ) 41 42 var ErrInvalidDumpMagic = errors.New("invalid dump magic") 43 44 var ( 45 //two256是表示2^256的大整数 46 two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) 47 48 //sharedethash是可以在多个用户之间共享的完整实例。 49 sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal}, nil, false) 50 51 //AlgorithmRevision是用于文件命名的数据结构版本。 52 algorithmRevision = 23 53 54 //dumpmagic是一个数据集转储头,用于检查数据转储是否正常。 55 dumpMagic = []uint32{0xbaddcafe, 0xfee1dead} 56 ) 57 58 //Islittleendian返回本地系统是以小规模还是大规模运行 59 //结束字节顺序。 60 func isLittleEndian() bool { 61 n := uint32(0x01020304) 62 return *(*byte)(unsafe.Pointer(&n)) == 0x04 63 } 64 65 //memory map尝试为只读访问存储uint32s的映射文件。 66 func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) { 67 file, err := os.OpenFile(path, os.O_RDONLY, 0644) 68 if err != nil { 69 return nil, nil, nil, err 70 } 71 mem, buffer, err := memoryMapFile(file, false) 72 if err != nil { 73 file.Close() 74 return nil, nil, nil, err 75 } 76 for i, magic := range dumpMagic { 77 if buffer[i] != magic { 78 mem.Unmap() 79 file.Close() 80 return nil, nil, nil, ErrInvalidDumpMagic 81 } 82 } 83 return file, mem, buffer[len(dumpMagic):], err 84 } 85 86 //memoryMapFile尝试对已打开的文件描述符进行内存映射。 87 func memoryMapFile(file *os.File, write bool) (mmap.MMap, []uint32, error) { 88 //尝试内存映射文件 89 flag := mmap.RDONLY 90 if write { 91 flag = mmap.RDWR 92 } 93 mem, err := mmap.Map(file, flag, 0) 94 if err != nil { 95 return nil, nil, err 96 } 97 //是的,我们设法记忆地图文件,这里是龙。 98 header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem)) 99 header.Len /= 4 100 header.Cap /= 4 101 102 return mem, *(*[]uint32)(unsafe.Pointer(&header)), nil 103 } 104 105 //memoryMandGenerate尝试对uint32s的临时文件进行内存映射以进行写入 106 //访问,用生成器中的数据填充它,然后将其移动到最终版本 107 //请求路径。 108 func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) { 109 //确保数据文件夹存在 110 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 111 return nil, nil, nil, err 112 } 113 //创建一个巨大的临时空文件来填充数据 114 temp := path + "." + strconv.Itoa(rand.Int()) 115 116 dump, err := os.Create(temp) 117 if err != nil { 118 return nil, nil, nil, err 119 } 120 if err = dump.Truncate(int64(len(dumpMagic))*4 + int64(size)); err != nil { 121 return nil, nil, nil, err 122 } 123 //内存映射要写入的文件并用生成器填充它 124 mem, buffer, err := memoryMapFile(dump, true) 125 if err != nil { 126 dump.Close() 127 return nil, nil, nil, err 128 } 129 copy(buffer, dumpMagic) 130 131 data := buffer[len(dumpMagic):] 132 generator(data) 133 134 if err := mem.Unmap(); err != nil { 135 return nil, nil, nil, err 136 } 137 if err := dump.Close(); err != nil { 138 return nil, nil, nil, err 139 } 140 if err := os.Rename(temp, path); err != nil { 141 return nil, nil, nil, err 142 } 143 return memoryMap(path) 144 } 145 146 //lru按缓存或数据集的最后使用时间跟踪它们,最多保留n个缓存或数据集。 147 type lru struct { 148 what string 149 new func(epoch uint64) interface{} 150 mu sync.Mutex 151 //项目保存在LRU缓存中,但有一种特殊情况: 152 //我们总是保留一个项目为(最高看到的时代)+1作为“未来项目”。 153 cache *simplelru.LRU 154 future uint64 155 futureItem interface{} 156 } 157 158 //newlru为验证缓存创建新的最近使用最少的缓存 159 //或挖掘数据集。 160 func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru { 161 if maxItems <= 0 { 162 maxItems = 1 163 } 164 cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) { 165 log.Trace("Evicted ethash "+what, "epoch", key) 166 }) 167 return &lru{what: what, new: new, cache: cache} 168 } 169 170 //get为给定的epoch检索或创建项。第一个返回值总是 171 //非零。如果LRU认为某个项目在 172 //不久的将来。 173 func (lru *lru) get(epoch uint64) (item, future interface{}) { 174 lru.mu.Lock() 175 defer lru.mu.Unlock() 176 177 //获取或创建请求的epoch的项。 178 item, ok := lru.cache.Get(epoch) 179 if !ok { 180 if lru.future > 0 && lru.future == epoch { 181 item = lru.futureItem 182 } else { 183 log.Trace("Requiring new ethash "+lru.what, "epoch", epoch) 184 item = lru.new(epoch) 185 } 186 lru.cache.Add(epoch, item) 187 } 188 //如果epoch大于以前看到的值,则更新“future item”。 189 if epoch < maxEpoch-1 && lru.future < epoch+1 { 190 log.Trace("Requiring new future ethash "+lru.what, "epoch", epoch+1) 191 future = lru.new(epoch + 1) 192 lru.future = epoch + 1 193 lru.futureItem = future 194 } 195 return item, future 196 } 197 198 //cache用一些元数据包装ethash缓存,以便于并发使用。 199 type cache struct { 200 epoch uint64 //与此缓存相关的epoch 201 dump *os.File //内存映射缓存的文件描述符 202 mmap mmap.MMap //释放前内存映射到取消映射 203 cache []uint32 //实际缓存数据内容(可能是内存映射) 204 once sync.Once //确保只生成一次缓存 205 } 206 207 //new cache创建一个新的ethash验证缓存,并将其作为普通缓存返回 208 //可在LRU缓存中使用的接口。 209 func newCache(epoch uint64) interface{} { 210 return &cache{epoch: epoch} 211 } 212 213 //generate确保在使用前生成缓存内容。 214 func (c *cache) generate(dir string, limit int, test bool) { 215 c.once.Do(func() { 216 size := cacheSize(c.epoch*epochLength + 1) 217 seed := seedHash(c.epoch*epochLength + 1) 218 if test { 219 size = 1024 220 } 221 //如果我们不在磁盘上存储任何内容,则生成并返回。 222 if dir == "" { 223 c.cache = make([]uint32, size/4) 224 generateCache(c.cache, c.epoch, seed) 225 return 226 } 227 //磁盘存储是必需的,这会变得花哨。 228 var endian string 229 if !isLittleEndian() { 230 endian = ".be" 231 } 232 path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian)) 233 logger := log.New("epoch", c.epoch) 234 235 //我们将对该文件进行mmap,确保在 236 //缓存将变为未使用。 237 runtime.SetFinalizer(c, (*cache).finalizer) 238 239 //尝试从磁盘和内存中加载文件 240 var err error 241 c.dump, c.mmap, c.cache, err = memoryMap(path) 242 if err == nil { 243 logger.Debug("Loaded old ethash cache from disk") 244 return 245 } 246 logger.Debug("Failed to load old ethash cache", "err", err) 247 248 //以前没有可用的缓存,请创建新的缓存文件以填充 249 c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) }) 250 if err != nil { 251 logger.Error("Failed to generate mapped ethash cache", "err", err) 252 253 c.cache = make([]uint32, size/4) 254 generateCache(c.cache, c.epoch, seed) 255 } 256 //迭代所有以前的实例并删除旧实例 257 for ep := int(c.epoch) - limit; ep >= 0; ep-- { 258 seed := seedHash(uint64(ep)*epochLength + 1) 259 path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian)) 260 os.Remove(path) 261 } 262 }) 263 } 264 265 //终结器取消映射内存并关闭文件。 266 func (c *cache) finalizer() { 267 if c.mmap != nil { 268 c.mmap.Unmap() 269 c.dump.Close() 270 c.mmap, c.dump = nil, nil 271 } 272 } 273 274 //数据集使用一些元数据包装ethash数据集,以便于并发使用。 275 type dataset struct { 276 epoch uint64 //与此缓存相关的epoch 277 dump *os.File //内存映射缓存的文件描述符 278 mmap mmap.MMap //释放前内存映射到取消映射 279 dataset []uint32 //实际缓存数据内容 280 once sync.Once //确保只生成一次缓存 281 done uint32 //用于确定生成状态的原子标记 282 } 283 284 //NewDataSet创建一个新的ethash挖掘数据集,并将其作为简单的go返回 285 //可在LRU缓存中使用的接口。 286 func newDataset(epoch uint64) interface{} { 287 return &dataset{epoch: epoch} 288 } 289 290 //生成确保在使用前生成数据集内容。 291 func (d *dataset) generate(dir string, limit int, test bool) { 292 d.once.Do(func() { 293 //标记完成后生成的数据集。这是遥控器需要的 294 defer atomic.StoreUint32(&d.done, 1) 295 296 csize := cacheSize(d.epoch*epochLength + 1) 297 dsize := datasetSize(d.epoch*epochLength + 1) 298 seed := seedHash(d.epoch*epochLength + 1) 299 if test { 300 csize = 1024 301 dsize = 32 * 1024 302 } 303 //如果我们不在磁盘上存储任何内容,则生成并返回 304 if dir == "" { 305 cache := make([]uint32, csize/4) 306 generateCache(cache, d.epoch, seed) 307 308 d.dataset = make([]uint32, dsize/4) 309 generateDataset(d.dataset, d.epoch, cache) 310 311 return 312 } 313 //磁盘存储是必需的,这会变得花哨。 314 var endian string 315 if !isLittleEndian() { 316 endian = ".be" 317 } 318 path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian)) 319 logger := log.New("epoch", d.epoch) 320 321 //我们将对该文件进行mmap,确保在 322 //缓存将变为未使用。 323 runtime.SetFinalizer(d, (*dataset).finalizer) 324 325 //尝试从磁盘和内存中加载文件 326 var err error 327 d.dump, d.mmap, d.dataset, err = memoryMap(path) 328 if err == nil { 329 logger.Debug("Loaded old ethash dataset from disk") 330 return 331 } 332 logger.Debug("Failed to load old ethash dataset", "err", err) 333 334 //没有以前的数据集可用,请创建新的数据集文件来填充 335 cache := make([]uint32, csize/4) 336 generateCache(cache, d.epoch, seed) 337 338 d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) }) 339 if err != nil { 340 logger.Error("Failed to generate mapped ethash dataset", "err", err) 341 342 d.dataset = make([]uint32, dsize/2) 343 generateDataset(d.dataset, d.epoch, cache) 344 } 345 //迭代所有以前的实例并删除旧实例 346 for ep := int(d.epoch) - limit; ep >= 0; ep-- { 347 seed := seedHash(uint64(ep)*epochLength + 1) 348 path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian)) 349 os.Remove(path) 350 } 351 }) 352 } 353 354 //generated返回此特定数据集是否已完成生成 355 //或者没有(可能根本没有启动)。这对远程矿工很有用 356 //默认为验证缓存,而不是在DAG代上阻塞。 357 func (d *dataset) generated() bool { 358 return atomic.LoadUint32(&d.done) == 1 359 } 360 361 //终结器关闭所有打开的文件处理程序和内存映射。 362 func (d *dataset) finalizer() { 363 if d.mmap != nil { 364 d.mmap.Unmap() 365 d.dump.Close() 366 d.mmap, d.dump = nil, nil 367 } 368 } 369 370 //makecache生成一个新的ethash缓存,并可以选择将其存储到磁盘。 371 func MakeCache(block uint64, dir string) { 372 c := cache{epoch: block / epochLength} 373 c.generate(dir, math.MaxInt32, false) 374 } 375 376 //makedataset生成一个新的ethash数据集,并可以选择将其存储到磁盘。 377 func MakeDataset(block uint64, dir string) { 378 d := dataset{epoch: block / epochLength} 379 d.generate(dir, math.MaxInt32, false) 380 } 381 382 //模式定义了ethash引擎所做的POW验证的类型和数量。 383 type Mode uint 384 385 const ( 386 ModeNormal Mode = iota 387 ModeShared 388 ModeTest 389 ModeFake 390 ModeFullFake 391 ) 392 393 //config是ethash的配置参数。 394 type Config struct { 395 CacheDir string 396 CachesInMem int 397 CachesOnDisk int 398 DatasetDir string 399 DatasetsInMem int 400 DatasetsOnDisk int 401 PowMode Mode 402 } 403 404 //sealttask用远程密封器螺纹的相对结果通道包装密封块。 405 type sealTask struct { 406 block *types.Block 407 results chan<- *types.Block 408 } 409 410 //mineresult包装指定块的POW解决方案参数。 411 type mineResult struct { 412 nonce types.BlockNonce 413 mixDigest common.Hash 414 hash common.Hash 415 416 errc chan error 417 } 418 419 //哈希率包装远程密封程序提交的哈希率。 420 type hashrate struct { 421 id common.Hash 422 ping time.Time 423 rate uint64 424 425 done chan struct{} 426 } 427 428 //密封件包裹远程密封件的密封件工作包。 429 type sealWork struct { 430 errc chan error 431 res chan [4]string 432 } 433 434 //ethash是基于实施ethash的工作证明的共识引擎。 435 //算法。 436 type Ethash struct { 437 config Config 438 439 caches *lru //内存缓存以避免重新生成太频繁 440 datasets *lru //内存中的数据集,以避免过于频繁地重新生成 441 442 //采矿相关领域 443 rand *rand.Rand //当前正确播种的随机源 444 threads int //中频挖掘要挖掘的线程数 445 update chan struct{} //更新挖掘参数的通知通道 446 hashrate metrics.Meter //米跟踪平均哈希率 447 448 //远程密封相关字段 449 workCh chan *sealTask //通知通道将新工作和相关结果通道推送到远程封口机 450 fetchWorkCh chan *sealWork //用于远程封口机获取采矿作业的通道 451 submitWorkCh chan *mineResult //用于远程封口机提交其采矿结果的通道 452 fetchRateCh chan chan uint64 //用于收集本地或远程密封程序提交的哈希率的通道。 453 submitRateCh chan *hashrate //用于远程密封程序提交其挖掘哈希的通道 454 455 //下面的字段是用于测试的挂钩 456 shared *Ethash //共享POW验证程序以避免缓存重新生成 457 fakeFail uint64 //即使在假模式下也无法进行电源检查的块号 458 fakeDelay time.Duration //从验证返回前的睡眠时间延迟 459 460 lock sync.Mutex // 461 closeOnce sync.Once //确保出口通道不会关闭两次。 462 exitCh chan chan error //退出后端线程的通知通道 463 } 464 465 //new创建一个完整的ethash pow方案,并为 466 //远程挖掘,也可以选择将新工作通知一批远程服务 467 //包装。 468 func New(config Config, notify []string, noverify bool) *Ethash { 469 if config.CachesInMem <= 0 { 470 log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem) 471 config.CachesInMem = 1 472 } 473 if config.CacheDir != "" && config.CachesOnDisk > 0 { 474 log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk) 475 } 476 if config.DatasetDir != "" && config.DatasetsOnDisk > 0 { 477 log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk) 478 } 479 ethash := &Ethash{ 480 config: config, 481 caches: newlru("cache", config.CachesInMem, newCache), 482 datasets: newlru("dataset", config.DatasetsInMem, newDataset), 483 update: make(chan struct{}), 484 hashrate: metrics.NewMeterForced(), 485 workCh: make(chan *sealTask), 486 fetchWorkCh: make(chan *sealWork), 487 submitWorkCh: make(chan *mineResult), 488 fetchRateCh: make(chan chan uint64), 489 submitRateCh: make(chan *hashrate), 490 exitCh: make(chan chan error), 491 } 492 go ethash.remote(notify, noverify) 493 return ethash 494 } 495 496 //NewTester创建了一个小型ethash pow方案,该方案仅用于测试 497 //目的。 498 func NewTester(notify []string, noverify bool) *Ethash { 499 ethash := &Ethash{ 500 config: Config{PowMode: ModeTest}, 501 caches: newlru("cache", 1, newCache), 502 datasets: newlru("dataset", 1, newDataset), 503 update: make(chan struct{}), 504 hashrate: metrics.NewMeterForced(), 505 workCh: make(chan *sealTask), 506 fetchWorkCh: make(chan *sealWork), 507 submitWorkCh: make(chan *mineResult), 508 fetchRateCh: make(chan chan uint64), 509 submitRateCh: make(chan *hashrate), 510 exitCh: make(chan chan error), 511 } 512 go ethash.remote(notify, noverify) 513 return ethash 514 } 515 516 //Newfaker创建了一个具有假POW方案的ethash共识引擎,该方案接受 517 //所有区块的封条都是有效的,尽管它们仍然必须符合以太坊。 518 //共识规则。 519 func NewFaker() *Ethash { 520 return &Ethash{ 521 config: Config{ 522 PowMode: ModeFake, 523 }, 524 } 525 } 526 527 //newfakefailer创建了一个具有假POW方案的ethash共识引擎, 528 //接受除指定的单个块之外的所有块,尽管它们 529 //仍然必须遵守以太坊共识规则。 530 func NewFakeFailer(fail uint64) *Ethash { 531 return &Ethash{ 532 config: Config{ 533 PowMode: ModeFake, 534 }, 535 fakeFail: fail, 536 } 537 } 538 539 //Newfakedelayer创建了一个具有假POW方案的ethash共识引擎, 540 //接受所有块为有效,但将验证延迟一段时间 541 //他们仍然必须遵守以太坊共识规则。 542 func NewFakeDelayer(delay time.Duration) *Ethash { 543 return &Ethash{ 544 config: Config{ 545 PowMode: ModeFake, 546 }, 547 fakeDelay: delay, 548 } 549 } 550 551 //newfullfaker创建了一个具有完全伪造方案的ethash共识引擎, 552 //接受所有块为有效块,而不检查任何共识规则。 553 func NewFullFaker() *Ethash { 554 return &Ethash{ 555 config: Config{ 556 PowMode: ModeFullFake, 557 }, 558 } 559 } 560 561 //newshared创建一个在所有运行的请求者之间共享的完整大小的ethash pow 562 //在同样的过程中。 563 func NewShared() *Ethash { 564 return &Ethash{shared: sharedEthash} 565 } 566 567 //关闭关闭退出通道以通知所有后端线程退出。 568 func (ethash *Ethash) Close() error { 569 var err error 570 ethash.closeOnce.Do(func() { 571 //如果没有分配出口通道,则短路。 572 if ethash.exitCh == nil { 573 return 574 } 575 errc := make(chan error) 576 ethash.exitCh <- errc 577 err = <-errc 578 close(ethash.exitCh) 579 }) 580 return err 581 } 582 583 //缓存尝试检索指定块号的验证缓存 584 //首先检查内存中缓存的列表,然后检查缓存 585 //存储在磁盘上,如果找不到,则最终生成一个。 586 func (ethash *Ethash) cache(block uint64) *cache { 587 epoch := block / epochLength 588 currentI, futureI := ethash.caches.get(epoch) 589 current := currentI.(*cache) 590 591 //等待生成完成。 592 current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) 593 594 //如果我们需要一个新的未来缓存,现在是重新生成它的好时机。 595 if futureI != nil { 596 future := futureI.(*cache) 597 go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) 598 } 599 return current 600 } 601 602 //数据集尝试检索指定块号的挖掘数据集 603 //首先检查内存中的数据集列表,然后检查DAG 604 //存储在磁盘上,如果找不到,则最终生成一个。 605 // 606 //如果指定了异步,那么不仅是将来,而且当前的DAG也是 607 //在后台线程上生成。 608 func (ethash *Ethash) dataset(block uint64, async bool) *dataset { 609 //检索请求的ethash数据集 610 epoch := block / epochLength 611 currentI, futureI := ethash.datasets.get(epoch) 612 current := currentI.(*dataset) 613 614 //如果指定了异步,则在后台线程中生成所有内容 615 if async && !current.generated() { 616 go func() { 617 current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 618 619 if futureI != nil { 620 future := futureI.(*dataset) 621 future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 622 } 623 }() 624 } else { 625 //请求了阻止生成,或已完成生成 626 current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 627 628 if futureI != nil { 629 future := futureI.(*dataset) 630 go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 631 } 632 } 633 return current 634 } 635 636 //线程返回当前启用的挖掘线程数。这不 637 //一定意味着采矿正在进行! 638 func (ethash *Ethash) Threads() int { 639 ethash.lock.Lock() 640 defer ethash.lock.Unlock() 641 642 return ethash.threads 643 } 644 645 //setthreads更新当前启用的挖掘线程数。打电话 646 //此方法不启动挖掘,只设置线程计数。如果为零 647 //指定,矿工将使用机器的所有核心。设置线程 648 //允许计数低于零,将导致矿工闲置,没有任何 649 //正在完成的工作。 650 func (ethash *Ethash) SetThreads(threads int) { 651 ethash.lock.Lock() 652 defer ethash.lock.Unlock() 653 654 //如果我们运行的是一个共享的POW,则改为设置线程计数 655 if ethash.shared != nil { 656 ethash.shared.SetThreads(threads) 657 return 658 } 659 //更新螺纹并对任何运行密封进行ping操作,以拉入任何更改。 660 ethash.threads = threads 661 select { 662 case ethash.update <- struct{}{}: 663 default: 664 } 665 } 666 667 //hashRate实现pow,返回搜索调用的测量速率 668 //最后一分钟的每秒。 669 //注意,返回的哈希率包括本地哈希率,但也包括 670 //所有远程矿工的哈希率。 671 func (ethash *Ethash) Hashrate() float64 { 672 //如果在正常/测试模式下运行ethash,则短路。 673 if ethash.config.PowMode != ModeNormal && ethash.config.PowMode != ModeTest { 674 return ethash.hashrate.Rate1() 675 } 676 var res = make(chan uint64, 1) 677 678 select { 679 case ethash.fetchRateCh <- res: 680 case <-ethash.exitCh: 681 //仅当ethash停止时返回本地哈希率。 682 return ethash.hashrate.Rate1() 683 } 684 685 //收集远程密封程序提交的总哈希率。 686 return ethash.hashrate.Rate1() + float64(<-res) 687 } 688 689 //API实现共识引擎,返回面向用户的RPC API。 690 func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API { 691 //为了确保向后兼容性,我们公开了ethash RPC API 692 //Eth和Ethash名称空间。 693 return []rpc.API{ 694 { 695 Namespace: "eth", 696 Version: "1.0", 697 Service: &API{ethash}, 698 Public: true, 699 }, 700 { 701 Namespace: "ethash", 702 Version: "1.0", 703 Service: &API{ethash}, 704 Public: true, 705 }, 706 } 707 } 708 709 //seedhash是用于生成验证缓存和挖掘的种子 710 //数据集。 711 func SeedHash(block uint64) []byte { 712 return seedHash(block) 713 } 714