aboutsummaryrefslogtreecommitdiff
path: root/consensus/ethash/ethash.go
diff options
context:
space:
mode:
Diffstat (limited to 'consensus/ethash/ethash.go')
-rw-r--r--consensus/ethash/ethash.go175
1 files changed, 70 insertions, 105 deletions
diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go
index 53420d0..4a3912d 100644
--- a/consensus/ethash/ethash.go
+++ b/consensus/ethash/ethash.go
@@ -34,12 +34,10 @@ import (
"unsafe"
"github.com/ava-labs/coreth/consensus"
- "github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/metrics"
mmap "github.com/edsrzf/mmap-go"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
"github.com/hashicorp/golang-lru/simplelru"
)
@@ -50,7 +48,7 @@ var (
two256 = 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(Config{"", 3, 0, "", 1, 0, ModeNormal}, nil, false)
+ sharedEthash = New(Config{"", 3, 0, false, "", 1, 0, false, ModeNormal, nil}, nil, false)
// algorithmRevision is the data structure version used for file naming.
algorithmRevision = 23
@@ -67,7 +65,7 @@ func isLittleEndian() bool {
}
// memoryMap tries to memory map a file of uint32s for read only access.
-func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) {
+func memoryMap(path string, lock bool) (*os.File, mmap.MMap, []uint32, error) {
file, err := os.OpenFile(path, os.O_RDONLY, 0644)
if err != nil {
return nil, nil, nil, err
@@ -84,6 +82,13 @@ func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) {
return nil, nil, nil, ErrInvalidDumpMagic
}
}
+ if lock {
+ if err := mem.Lock(); err != nil {
+ mem.Unmap()
+ file.Close()
+ return nil, nil, nil, err
+ }
+ }
return file, mem, buffer[len(dumpMagic):], err
}
@@ -109,7 +114,7 @@ func memoryMapFile(file *os.File, write bool) (mmap.MMap, []uint32, error) {
// memoryMapAndGenerate tries to memory map a temporary file of uint32s for write
// access, fill it with the data from a generator and then move it into the final
// path requested.
-func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) {
+func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) {
// Ensure the data folder exists
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return nil, nil, nil, err
@@ -144,7 +149,7 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
if err := os.Rename(temp, path); err != nil {
return nil, nil, nil, err
}
- return memoryMap(path)
+ return memoryMap(path, lock)
}
// lru tracks caches or datasets by their last use time, keeping at most N of them.
@@ -215,7 +220,7 @@ func newCache(epoch uint64) interface{} {
}
// generate ensures that the cache content is generated before use.
-func (c *cache) generate(dir string, limit int, test bool) {
+func (c *cache) generate(dir string, limit int, lock bool, test bool) {
c.once.Do(func() {
size := cacheSize(c.epoch*epochLength + 1)
seed := seedHash(c.epoch*epochLength + 1)
@@ -242,7 +247,7 @@ func (c *cache) generate(dir string, limit int, test bool) {
// Try to load the file from disk and memory map it
var err error
- c.dump, c.mmap, c.cache, err = memoryMap(path)
+ c.dump, c.mmap, c.cache, err = memoryMap(path, lock)
if err == nil {
logger.Debug("Loaded old ethash cache from disk")
return
@@ -250,7 +255,7 @@ func (c *cache) generate(dir string, limit int, test bool) {
logger.Debug("Failed to load old ethash cache", "err", err)
// No previous cache available, create a new cache file to fill
- c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) })
+ c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, lock, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) })
if err != nil {
logger.Error("Failed to generate mapped ethash cache", "err", err)
@@ -292,7 +297,7 @@ func newDataset(epoch uint64) interface{} {
}
// generate ensures that the dataset content is generated before use.
-func (d *dataset) generate(dir string, limit int, test bool) {
+func (d *dataset) generate(dir string, limit int, lock bool, test bool) {
d.once.Do(func() {
// Mark the dataset generated after we're done. This is needed for remote
defer atomic.StoreUint32(&d.done, 1)
@@ -328,7 +333,7 @@ func (d *dataset) generate(dir string, limit int, test bool) {
// Try to load the file from disk and memory map it
var err error
- d.dump, d.mmap, d.dataset, err = memoryMap(path)
+ d.dump, d.mmap, d.dataset, err = memoryMap(path, lock)
if err == nil {
logger.Debug("Loaded old ethash dataset from disk")
return
@@ -339,7 +344,7 @@ func (d *dataset) generate(dir string, limit int, test bool) {
cache := make([]uint32, csize/4)
generateCache(cache, d.epoch, seed)
- d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
+ d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, lock, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
if err != nil {
logger.Error("Failed to generate mapped ethash dataset", "err", err)
@@ -374,13 +379,13 @@ func (d *dataset) finalizer() {
// MakeCache generates a new ethash cache and optionally stores it to disk.
func MakeCache(block uint64, dir string) {
c := cache{epoch: block / epochLength}
- c.generate(dir, math.MaxInt32, false)
+ c.generate(dir, math.MaxInt32, false, false)
}
// MakeDataset generates a new ethash dataset and optionally stores it to disk.
func MakeDataset(block uint64, dir string) {
d := dataset{epoch: block / epochLength}
- d.generate(dir, math.MaxInt32, false)
+ d.generate(dir, math.MaxInt32, false, false)
}
// Mode defines the type and amount of PoW verification an ethash engine makes.
@@ -396,43 +401,17 @@ const (
// 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
-}
+ CacheDir string
+ CachesInMem int
+ CachesOnDisk int
+ CachesLockMmap bool
+ DatasetDir string
+ DatasetsInMem int
+ DatasetsOnDisk int
+ DatasetsLockMmap bool
+ PowMode Mode
-// sealTask wraps a seal block with relative result channel for remote sealer thread.
-type sealTask struct {
- block *types.Block
- results chan<- *types.Block
-}
-
-// mineResult wraps the pow solution parameters for the specified block.
-type mineResult struct {
- nonce types.BlockNonce
- mixDigest common.Hash
- hash common.Hash
-
- errc chan error
-}
-
-// hashrate wraps the hash rate submitted by the remote sealer.
-type hashrate struct {
- id common.Hash
- ping time.Time
- rate uint64
-
- done chan struct{}
-}
-
-// sealWork wraps a seal work package for remote sealer.
-type sealWork struct {
- errc chan error
- res chan [4]string
+ Log log.Logger `toml:"-"`
}
// Ethash is a consensus engine based on proof-of-work implementing the ethash
@@ -448,52 +427,42 @@ type Ethash struct {
threads int // Number of threads to mine on if mining
update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate
-
- // Remote sealer related fields
- workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
- fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
- submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
- fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
- submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
+ remote *remoteSealer
// The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
fakeFail uint64 // Block number which fails PoW check even in fake mode
fakeDelay time.Duration // Time delay to sleep for before returning from verify
- lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
- closeOnce sync.Once // Ensures exit channel will not be closed twice.
- exitCh chan chan error // Notification channel to exiting backend threads
+ lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
+ closeOnce sync.Once // Ensures exit channel will not be closed twice.
}
// New creates a full sized ethash PoW scheme and starts a background thread for
// remote mining, also optionally notifying a batch of remote services of new work
// packages.
func New(config Config, notify []string, noverify bool) *Ethash {
+ if config.Log == nil {
+ config.Log = log.Root()
+ }
if config.CachesInMem <= 0 {
- log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
+ config.Log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
config.CachesInMem = 1
}
if config.CacheDir != "" && config.CachesOnDisk > 0 {
- log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
+ config.Log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
}
if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
- log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
+ config.Log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
}
ethash := &Ethash{
- config: config,
- caches: newlru("cache", config.CachesInMem, newCache),
- datasets: newlru("dataset", config.DatasetsInMem, newDataset),
- update: make(chan struct{}),
- hashrate: metrics.NewMeterForced(),
- workCh: make(chan *sealTask),
- fetchWorkCh: make(chan *sealWork),
- submitWorkCh: make(chan *mineResult),
- fetchRateCh: make(chan chan uint64),
- submitRateCh: make(chan *hashrate),
- exitCh: make(chan chan error),
- }
- go ethash.remote(notify, noverify)
+ config: config,
+ caches: newlru("cache", config.CachesInMem, newCache),
+ datasets: newlru("dataset", config.DatasetsInMem, newDataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeterForced(),
+ }
+ ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -501,19 +470,13 @@ func New(config Config, notify []string, noverify bool) *Ethash {
// purposes.
func NewTester(notify []string, noverify bool) *Ethash {
ethash := &Ethash{
- config: Config{PowMode: ModeTest},
- caches: newlru("cache", 1, newCache),
- datasets: newlru("dataset", 1, newDataset),
- update: make(chan struct{}),
- hashrate: metrics.NewMeterForced(),
- workCh: make(chan *sealTask),
- fetchWorkCh: make(chan *sealWork),
- submitWorkCh: make(chan *mineResult),
- fetchRateCh: make(chan chan uint64),
- submitRateCh: make(chan *hashrate),
- exitCh: make(chan chan error),
- }
- go ethash.remote(notify, noverify)
+ config: Config{PowMode: ModeTest, Log: log.Root()},
+ caches: newlru("cache", 1, newCache),
+ datasets: newlru("dataset", 1, newDataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeterForced(),
+ }
+ ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -524,6 +487,7 @@ func NewFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
}
}
@@ -535,6 +499,7 @@ func NewFakeFailer(fail uint64) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
fakeFail: fail,
}
@@ -547,6 +512,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
fakeDelay: delay,
}
@@ -558,6 +524,7 @@ func NewFullFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFullFake,
+ Log: log.Root(),
},
}
}
@@ -573,13 +540,11 @@ func (ethash *Ethash) Close() error {
var err error
ethash.closeOnce.Do(func() {
// Short circuit if the exit channel is not allocated.
- if ethash.exitCh == nil {
+ if ethash.remote == nil {
return
}
- errc := make(chan error)
- ethash.exitCh <- errc
- err = <-errc
- close(ethash.exitCh)
+ close(ethash.remote.requestExit)
+ <-ethash.remote.exitCh
})
return err
}
@@ -593,12 +558,12 @@ func (ethash *Ethash) cache(block uint64) *cache {
current := currentI.(*cache)
// Wait for generation finish.
- current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest)
// If we need a new future cache, now's a good time to regenerate it.
if futureI != nil {
future := futureI.(*cache)
- go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
+ go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest)
}
return current
}
@@ -618,20 +583,20 @@ func (ethash *Ethash) dataset(block uint64, async bool) *dataset {
// If async is specified, generate everything in a background thread
if async && !current.generated() {
go func() {
- current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
if futureI != nil {
future := futureI.(*dataset)
- future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
}
}()
} else {
// Either blocking generation was requested, or already done
- current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
if futureI != nil {
future := futureI.(*dataset)
- go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
}
}
return current
@@ -680,8 +645,8 @@ func (ethash *Ethash) Hashrate() float64 {
var res = make(chan uint64, 1)
select {
- case ethash.fetchRateCh <- res:
- case <-ethash.exitCh:
+ case ethash.remote.fetchRateCh <- res:
+ case <-ethash.remote.exitCh:
// Return local hashrate only if ethash is stopped.
return ethash.hashrate.Rate1()
}
@@ -691,7 +656,7 @@ func (ethash *Ethash) Hashrate() float64 {
}
// APIs implements consensus.Engine, returning the user facing RPC APIs.
-func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
+func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API {
// In order to ensure backward compatibility, we exposes ethash RPC APIs
// to both eth and ethash namespaces.
return []rpc.API{