github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/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 12:09:33</date> 10 //</624342612056215552> 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) 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 // 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 // 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 //mineresult包装指定块的POW解决方案参数。 405 type mineResult struct { 406 nonce types.BlockNonce 407 mixDigest common.Hash 408 hash common.Hash 409 410 errc chan error 411 } 412 413 //哈希率包装远程密封程序提交的哈希率。 414 type hashrate struct { 415 id common.Hash 416 ping time.Time 417 rate uint64 418 419 done chan struct{} 420 } 421 422 //密封件包裹远程密封件的密封件工作包。 423 type sealWork struct { 424 errc chan error 425 res chan [3]string 426 } 427 428 //ethash是基于实施ethash的工作证明的共识引擎。 429 //算法。 430 type Ethash struct { 431 config Config 432 433 caches *lru //内存缓存以避免重新生成太频繁 434 datasets *lru // 435 436 //采矿相关领域 437 rand *rand.Rand //当前正确播种的随机源 438 threads int //中频挖掘要挖掘的线程数 439 update chan struct{} //更新挖掘参数的通知通道 440 hashrate metrics.Meter //米跟踪平均哈希率 441 442 //远程密封相关字段 443 workCh chan *types.Block //通知通道将新工作推送到远程封口机 444 resultCh chan *types.Block //挖掘线程用于返回结果的通道 445 fetchWorkCh chan *sealWork //用于远程封口机获取采矿作业的通道 446 submitWorkCh chan *mineResult //用于远程封口机提交其采矿结果的通道 447 fetchRateCh chan chan uint64 //用于收集本地或远程密封程序提交的哈希率的通道。 448 submitRateCh chan *hashrate //用于远程密封程序提交其挖掘哈希的通道 449 450 //下面的字段是用于测试的挂钩 451 shared *Ethash //共享POW验证程序以避免缓存重新生成 452 fakeFail uint64 // 453 fakeDelay time.Duration //从验证返回前的睡眠时间延迟 454 455 lock sync.Mutex // 456 closeOnce sync.Once // 457 exitCh chan chan error //退出后端线程的通知通道 458 } 459 460 //new创建一个完整的ethash pow方案,并为 461 //远程挖掘,也可以选择将新工作通知一批远程服务 462 //包装。 463 func New(config Config, notify []string) *Ethash { 464 if config.CachesInMem <= 0 { 465 log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem) 466 config.CachesInMem = 1 467 } 468 if config.CacheDir != "" && config.CachesOnDisk > 0 { 469 log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk) 470 } 471 if config.DatasetDir != "" && config.DatasetsOnDisk > 0 { 472 log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk) 473 } 474 ethash := &Ethash{ 475 config: config, 476 caches: newlru("cache", config.CachesInMem, newCache), 477 datasets: newlru("dataset", config.DatasetsInMem, newDataset), 478 update: make(chan struct{}), 479 hashrate: metrics.NewMeter(), 480 workCh: make(chan *types.Block), 481 resultCh: make(chan *types.Block), 482 fetchWorkCh: make(chan *sealWork), 483 submitWorkCh: make(chan *mineResult), 484 fetchRateCh: make(chan chan uint64), 485 submitRateCh: make(chan *hashrate), 486 exitCh: make(chan chan error), 487 } 488 go ethash.remote(notify) 489 return ethash 490 } 491 492 //NewTester创建了一个小型ethash pow方案,该方案仅用于测试 493 //目的。 494 func NewTester(notify []string) *Ethash { 495 ethash := &Ethash{ 496 config: Config{PowMode: ModeTest}, 497 caches: newlru("cache", 1, newCache), 498 datasets: newlru("dataset", 1, newDataset), 499 update: make(chan struct{}), 500 hashrate: metrics.NewMeter(), 501 workCh: make(chan *types.Block), 502 resultCh: make(chan *types.Block), 503 fetchWorkCh: make(chan *sealWork), 504 submitWorkCh: make(chan *mineResult), 505 fetchRateCh: make(chan chan uint64), 506 submitRateCh: make(chan *hashrate), 507 exitCh: make(chan chan error), 508 } 509 go ethash.remote(notify) 510 return ethash 511 } 512 513 //Newfaker创建了一个具有假POW方案的ethash共识引擎,该方案接受 514 //所有区块的封条都是有效的,尽管它们仍然必须符合以太坊。 515 //共识规则。 516 func NewFaker() *Ethash { 517 return &Ethash{ 518 config: Config{ 519 PowMode: ModeFake, 520 }, 521 } 522 } 523 524 //newfakefailer创建了一个具有假POW方案的ethash共识引擎, 525 //接受除指定的单个块之外的所有块,尽管它们 526 //仍然必须遵守以太坊共识规则。 527 func NewFakeFailer(fail uint64) *Ethash { 528 return &Ethash{ 529 config: Config{ 530 PowMode: ModeFake, 531 }, 532 fakeFail: fail, 533 } 534 } 535 536 //Newfakedelayer创建了一个具有假POW方案的ethash共识引擎, 537 //接受所有块为有效,但将验证延迟一段时间 538 //他们仍然必须遵守以太坊共识规则。 539 func NewFakeDelayer(delay time.Duration) *Ethash { 540 return &Ethash{ 541 config: Config{ 542 PowMode: ModeFake, 543 }, 544 fakeDelay: delay, 545 } 546 } 547 548 //newfullfaker创建了一个具有完全伪造方案的ethash共识引擎, 549 //接受所有块为有效块,而不检查任何共识规则。 550 func NewFullFaker() *Ethash { 551 return &Ethash{ 552 config: Config{ 553 PowMode: ModeFullFake, 554 }, 555 } 556 } 557 558 //newshared创建一个在所有运行的请求者之间共享的完整大小的ethash pow 559 //在同样的过程中。 560 func NewShared() *Ethash { 561 return &Ethash{shared: sharedEthash} 562 } 563 564 //关闭关闭退出通道以通知所有后端线程退出。 565 func (ethash *Ethash) Close() error { 566 var err error 567 ethash.closeOnce.Do(func() { 568 //如果没有分配出口通道,则短路。 569 if ethash.exitCh == nil { 570 return 571 } 572 errc := make(chan error) 573 ethash.exitCh <- errc 574 err = <-errc 575 close(ethash.exitCh) 576 }) 577 return err 578 } 579 580 //缓存尝试检索指定块号的验证缓存 581 //首先检查内存中缓存的列表,然后检查缓存 582 //存储在磁盘上,如果找不到,则最终生成一个。 583 func (ethash *Ethash) cache(block uint64) *cache { 584 epoch := block / epochLength 585 currentI, futureI := ethash.caches.get(epoch) 586 current := currentI.(*cache) 587 588 //等待生成完成。 589 current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) 590 591 //如果我们需要一个新的未来缓存,现在是重新生成它的好时机。 592 if futureI != nil { 593 future := futureI.(*cache) 594 go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) 595 } 596 return current 597 } 598 599 // 600 //首先检查内存中的数据集列表,然后检查DAG 601 //存储在磁盘上,如果找不到,则最终生成一个。 602 // 603 //如果指定了异步,那么不仅是将来,而且当前的DAG也是 604 //在后台线程上生成。 605 func (ethash *Ethash) dataset(block uint64, async bool) *dataset { 606 //检索请求的ethash数据集 607 epoch := block / epochLength 608 currentI, futureI := ethash.datasets.get(epoch) 609 current := currentI.(*dataset) 610 611 //如果指定了异步,则在后台线程中生成所有内容 612 if async && !current.generated() { 613 go func() { 614 current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 615 616 if futureI != nil { 617 future := futureI.(*dataset) 618 future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 619 } 620 }() 621 } else { 622 //请求了阻止生成,或已完成生成 623 current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 624 625 if futureI != nil { 626 future := futureI.(*dataset) 627 go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) 628 } 629 } 630 return current 631 } 632 633 //线程返回当前启用的挖掘线程数。这不 634 //一定意味着采矿正在进行! 635 func (ethash *Ethash) Threads() int { 636 ethash.lock.Lock() 637 defer ethash.lock.Unlock() 638 639 return ethash.threads 640 } 641 642 //setthreads更新当前启用的挖掘线程数。打电话 643 //此方法不启动挖掘,只设置线程计数。如果为零 644 //指定,矿工将使用机器的所有核心。设置线程 645 //允许计数低于零,将导致矿工闲置,没有任何 646 //正在完成的工作。 647 func (ethash *Ethash) SetThreads(threads int) { 648 ethash.lock.Lock() 649 defer ethash.lock.Unlock() 650 651 //如果我们运行的是一个共享的POW,则改为设置线程计数 652 if ethash.shared != nil { 653 ethash.shared.SetThreads(threads) 654 return 655 } 656 // 657 ethash.threads = threads 658 select { 659 case ethash.update <- struct{}{}: 660 default: 661 } 662 } 663 664 //hashRate实现pow,返回搜索调用的测量速率 665 //最后一分钟的每秒。 666 //注意,返回的哈希率包括本地哈希率,但也包括 667 //所有远程矿工的哈希率。 668 func (ethash *Ethash) Hashrate() float64 { 669 //如果在正常/测试模式下运行ethash,则短路。 670 if ethash.config.PowMode != ModeNormal && ethash.config.PowMode != ModeTest { 671 return ethash.hashrate.Rate1() 672 } 673 var res = make(chan uint64, 1) 674 675 select { 676 case ethash.fetchRateCh <- res: 677 case <-ethash.exitCh: 678 //仅当ethash停止时返回本地哈希率。 679 return ethash.hashrate.Rate1() 680 } 681 682 // 683 return ethash.hashrate.Rate1() + float64(<-res) 684 } 685 686 //API实现共识引擎,返回面向用户的RPC API。 687 func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API { 688 //为了确保向后兼容性,我们公开了ethash RPC API 689 //Eth和Ethash名称空间。 690 return []rpc.API{ 691 { 692 Namespace: "eth", 693 Version: "1.0", 694 Service: &API{ethash}, 695 Public: true, 696 }, 697 { 698 Namespace: "ethash", 699 Version: "1.0", 700 Service: &API{ethash}, 701 Public: true, 702 }, 703 } 704 } 705 706 //seedhash是用于生成验证缓存和挖掘的种子 707 //数据集。 708 func SeedHash(block uint64) []byte { 709 return seedHash(block) 710 } 711