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