diff options
Diffstat (limited to 'consensus/ethash')
-rw-r--r-- | consensus/ethash/algorithm_test.go | 3 | ||||
-rw-r--r-- | consensus/ethash/consensus.go | 10 | ||||
-rw-r--r-- | consensus/ethash/ethash.go | 117 | ||||
-rw-r--r-- | consensus/ethash/sealer.go | 2 |
4 files changed, 80 insertions, 52 deletions
diff --git a/consensus/ethash/algorithm_test.go b/consensus/ethash/algorithm_test.go index 7e4307a74..7765ff9fe 100644 --- a/consensus/ethash/algorithm_test.go +++ b/consensus/ethash/algorithm_test.go @@ -703,8 +703,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) { go func(idx int) { defer pend.Done() - - ethash := New(cachedir, 0, 1, "", 0, 0) + ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal}) if err := ethash.VerifySeal(nil, block.Header()); err != nil { t.Errorf("proc %d: block verification failed: %v", idx, err) } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index e330b7ce5..775419e06 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -68,7 +68,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { // stock Ethereum ethash engine. func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { // If we're running a full engine faking, accept any input as valid - if ethash.fakeFull { + if ethash.config.PowMode == ModeFullFake { return nil } // Short circuit if the header is known, or it's parent not @@ -89,7 +89,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He // a results channel to retrieve the async verifications. func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { // If we're running a full engine faking, accept any input as valid - if ethash.fakeFull || len(headers) == 0 { + if ethash.config.PowMode == ModeFullFake || len(headers) == 0 { abort, results := make(chan struct{}), make(chan error, len(headers)) for i := 0; i < len(headers); i++ { results <- nil @@ -169,7 +169,7 @@ func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers [] // rules of the stock Ethereum ethash engine. func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { // If we're running a full engine faking, accept any input as valid - if ethash.fakeFull { + if ethash.config.PowMode == ModeFullFake { return nil } // Verify that there are at most 2 uncles included in this block @@ -455,7 +455,7 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { // the PoW difficulty requirements. func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error { // If we're running a fake PoW, accept any seal as valid - if ethash.fakeMode { + if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { time.Sleep(ethash.fakeDelay) if ethash.fakeFail == header.Number.Uint64() { return errInvalidPoW @@ -480,7 +480,7 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head cache := ethash.cache(number) size := datasetSize(number) - if ethash.tester { + if ethash.config.PowMode == ModeTest { size = 32 * 1024 } digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64()) diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index dd6147072..a78b3a895 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -45,7 +45,7 @@ var ( maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // sharedEthash is a full instance that can be shared between multiple users. - sharedEthash = New("", 3, 0, "", 1, 0) + sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal}) // algorithmRevision is the data structure version used for file naming. algorithmRevision = 23 @@ -320,15 +320,32 @@ func MakeDataset(block uint64, dir string) { d.release() } +// Mode defines the type and amount of PoW verification an ethash engine makes. +type Mode uint + +const ( + ModeNormal Mode = iota + ModeShared + ModeTest + ModeFake + ModeFullFake +) + +// Config are the configuration parameters of the ethash. +type Config struct { + CacheDir string + CachesInMem int + CachesOnDisk int + DatasetDir string + DatasetsInMem int + DatasetsOnDisk int + PowMode Mode +} + // Ethash is a consensus engine based on proot-of-work implementing the ethash // algorithm. type Ethash struct { - cachedir string // Data directory to store the verification caches - cachesinmem int // Number of caches to keep in memory - cachesondisk int // Number of caches to keep on disk - dagdir string // Data directory to store full mining datasets - dagsinmem int // Number of mining datasets to keep in memory - dagsondisk int // Number of mining datasets to keep on disk + config Config caches map[uint64]*cache // In memory caches to avoid regenerating too often fcache *cache // Pre-generated cache for the estimated future epoch @@ -342,10 +359,7 @@ type Ethash struct { hashrate metrics.Meter // Meter tracking the average hashrate // The fields below are hooks for testing - tester bool // Flag whether to use a smaller test dataset shared *Ethash // Shared PoW verifier to avoid cache regeneration - fakeMode bool // Flag whether to disable PoW checking - fakeFull bool // Flag whether to disable all consensus rules fakeFail uint64 // Block number which fails PoW check even in fake mode fakeDelay time.Duration // Time delay to sleep for before returning from verify @@ -353,28 +367,23 @@ type Ethash struct { } // New creates a full sized ethash PoW scheme. -func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) *Ethash { - if cachesinmem <= 0 { - log.Warn("One ethash cache must always be in memory", "requested", cachesinmem) - cachesinmem = 1 +func New(config Config) *Ethash { + if config.CachesInMem <= 0 { + log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem) + config.CachesInMem = 1 } - if cachedir != "" && cachesondisk > 0 { - log.Info("Disk storage enabled for ethash caches", "dir", cachedir, "count", cachesondisk) + if config.CacheDir != "" && config.CachesOnDisk > 0 { + log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk) } - if dagdir != "" && dagsondisk > 0 { - log.Info("Disk storage enabled for ethash DAGs", "dir", dagdir, "count", dagsondisk) + if config.DatasetDir != "" && config.DatasetsOnDisk > 0 { + log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk) } return &Ethash{ - cachedir: cachedir, - cachesinmem: cachesinmem, - cachesondisk: cachesondisk, - dagdir: dagdir, - dagsinmem: dagsinmem, - dagsondisk: dagsondisk, - caches: make(map[uint64]*cache), - datasets: make(map[uint64]*dataset), - update: make(chan struct{}), - hashrate: metrics.NewMeter(), + config: config, + caches: make(map[uint64]*cache), + datasets: make(map[uint64]*dataset), + update: make(chan struct{}), + hashrate: metrics.NewMeter(), } } @@ -382,12 +391,14 @@ func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinme // purposes. func NewTester() *Ethash { return &Ethash{ - cachesinmem: 1, - caches: make(map[uint64]*cache), - datasets: make(map[uint64]*dataset), - tester: true, - update: make(chan struct{}), - hashrate: metrics.NewMeter(), + config: Config{ + CachesInMem: 1, + PowMode: ModeTest, + }, + caches: make(map[uint64]*cache), + datasets: make(map[uint64]*dataset), + update: make(chan struct{}), + hashrate: metrics.NewMeter(), } } @@ -395,27 +406,45 @@ func NewTester() *Ethash { // all blocks' seal as valid, though they still have to conform to the Ethereum // consensus rules. func NewFaker() *Ethash { - return &Ethash{fakeMode: true} + return &Ethash{ + config: Config{ + PowMode: ModeFake, + }, + } } // NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that // accepts all blocks as valid apart from the single one specified, though they // still have to conform to the Ethereum consensus rules. func NewFakeFailer(fail uint64) *Ethash { - return &Ethash{fakeMode: true, fakeFail: fail} + return &Ethash{ + config: Config{ + PowMode: ModeFake, + }, + fakeFail: fail, + } } // NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that // accepts all blocks as valid, but delays verifications by some time, though // they still have to conform to the Ethereum consensus rules. func NewFakeDelayer(delay time.Duration) *Ethash { - return &Ethash{fakeMode: true, fakeDelay: delay} + return &Ethash{ + config: Config{ + PowMode: ModeFake, + }, + fakeDelay: delay, + } } // NewFullFaker creates an ethash consensus engine with a full fake scheme that // accepts all blocks as valid, without checking any consensus rules whatsoever. func NewFullFaker() *Ethash { - return &Ethash{fakeMode: true, fakeFull: true} + return &Ethash{ + config: Config{ + PowMode: ModeFullFake, + }, + } } // NewShared creates a full sized ethash PoW shared between all requesters running @@ -436,7 +465,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 { current, future := ethash.caches[epoch], (*cache)(nil) if current == nil { // No in-memory cache, evict the oldest if the cache limit was reached - for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.cachesinmem { + for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.config.CachesInMem { var evict *cache for _, cache := range ethash.caches { if evict == nil || evict.used.After(cache.used) { @@ -473,7 +502,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 { ethash.lock.Unlock() // Wait for generation finish, bump the timestamp and finalize the cache - current.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester) + current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) current.lock.Lock() current.used = time.Now() @@ -481,7 +510,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 { // If we exhausted the future cache, now's a good time to regenerate it if future != nil { - go future.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester) + go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) } return current.cache } @@ -498,7 +527,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 { current, future := ethash.datasets[epoch], (*dataset)(nil) if current == nil { // No in-memory dataset, evict the oldest if the dataset limit was reached - for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.dagsinmem { + for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.config.DatasetsInMem { var evict *dataset for _, dataset := range ethash.datasets { if evict == nil || evict.used.After(dataset.used) { @@ -536,7 +565,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 { ethash.lock.Unlock() // Wait for generation finish, bump the timestamp and finalize the cache - current.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester) + current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) current.lock.Lock() current.used = time.Now() @@ -544,7 +573,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 { // If we exhausted the future dataset, now's a good time to regenerate it if future != nil { - go future.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester) + go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) } return current.dataset } diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go index 784e8f649..c2447e473 100644 --- a/consensus/ethash/sealer.go +++ b/consensus/ethash/sealer.go @@ -34,7 +34,7 @@ import ( // the block's difficulty requirements. func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { // If we're running a fake PoW, simply return a 0 nonce immediately - if ethash.fakeMode { + if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { header := block.Header() header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{} return block.WithSeal(header), nil |