@ -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 cachesinm em <= 0 {
log . Warn ( "One ethash cache must always be in memory" , "requested" , cachesinm em )
cachesinm em = 1
func New ( config Config ) * Ethash {
if config . CachesInM em <= 0 {
log . Warn ( "One ethash cache must always be in memory" , "requested" , config . CachesInM em )
config . CachesInM em = 1
}
if cached ir != "" && cachesond isk > 0 {
log . Info ( "Disk storage enabled for ethash caches" , "dir" , cached ir , "count" , cachesond isk )
if config . CacheD ir != "" && config . CachesOnD isk > 0 {
log . Info ( "Disk storage enabled for ethash caches" , "dir" , config . CacheD ir , "count" , config . CachesOnD isk )
}
if dagd ir != "" && dagsond isk > 0 {
log . Info ( "Disk storage enabled for ethash DAGs" , "dir" , dagd ir, "count" , dagsond isk)
if config . DatasetD ir != "" && config . DatasetsOnD isk > 0 {
log . Info ( "Disk storage enabled for ethash DAGs" , "dir" , config . DatasetD ir, "count" , config . DatasetsOnD isk)
}
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 . cachesinm em {
for len ( ethash . caches ) > 0 && len ( ethash . caches ) >= ethash . config . CachesInM em {
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 . cached ir , ethash . cachesond isk , ethash . tester )
current . generate ( ethash . config . CacheD ir , ethash . config . CachesOnD isk , 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 . cached ir , ethash . cachesond isk , ethash . tester )
go future . generate ( ethash . config . CacheD ir , ethash . config . CachesOnD isk , 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 . dagsinm em {
for len ( ethash . datasets ) > 0 && len ( ethash . datasets ) >= ethash . config . DatasetsInM em {
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 . dagd ir, ethash . dagsond isk, ethash . tester )
current . generate ( ethash . config . DatasetD ir, ethash . config . DatasetsOnD isk, 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 . dagd ir, ethash . dagsond isk, ethash . tester )
go future . generate ( ethash . config . DatasetD ir, ethash . config . DatasetsOnD isk, ethash . config . PowMode == ModeTest )
}
return current . dataset
}