pow: fix Search with ethash test mode

The cache/dataset methods crashed with a nil pointer error if
cachesinmem/dagsinmem were zero. Fix it by skipping the eviction logic
if there are no caches/datasets.

Search always used the regular dataset size regardless of test mode. Fix
it by removing the redundant size parameter of hashimotoFull.

Fixes #3784
This commit is contained in:
Felix Lange 2017-03-18 00:55:37 +01:00
parent 61ede86737
commit 24dd0355a3
3 changed files with 25 additions and 20 deletions

View File

@ -428,7 +428,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) >= ethash.cachesinmem {
for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.cachesinmem {
var evict *cache
for _, cache := range ethash.caches {
if evict == nil || evict.used.After(cache.used) {
@ -480,22 +480,16 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
// Search implements PoW, attempting to find a nonce that satisfies the block's
// difficulty requirements.
func (ethash *Ethash) Search(block Block, stop <-chan struct{}) (uint64, []byte) {
// Extract some data from the block
var (
hash = block.HashNoNonce().Bytes()
diff = block.Difficulty()
target = new(big.Int).Div(maxUint256, diff)
)
// Retrieve the mining dataset
dataset, size := ethash.dataset(block.NumberU64()), datasetSize(block.NumberU64())
// Start generating random nonces until we abort or find a good one
var (
hash = block.HashNoNonce().Bytes()
diff = block.Difficulty()
target = new(big.Int).Div(maxUint256, diff)
dataset = ethash.dataset(block.NumberU64())
rand = rand.New(rand.NewSource(time.Now().UnixNano()))
nonce = uint64(rand.Int63())
attempts int64
rand = rand.New(rand.NewSource(time.Now().UnixNano()))
nonce = uint64(rand.Int63())
)
// Start generating random nonces until we abort or find a good one
for {
select {
case <-stop:
@ -511,7 +505,7 @@ func (ethash *Ethash) Search(block Block, stop <-chan struct{}) (uint64, []byte)
attempts = 0
}
// Compute the PoW value of this nonce
digest, result := hashimotoFull(size, dataset, hash, nonce)
digest, result := hashimotoFull(dataset, hash, nonce)
if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
return nonce, digest
}
@ -532,7 +526,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) >= ethash.dagsinmem {
for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.dagsinmem {
var evict *dataset
for _, dataset := range ethash.datasets {
if evict == nil || evict.used.After(dataset.used) {

View File

@ -349,12 +349,12 @@ func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]b
// hashimotoFull aggregates data from the full dataset (using the full in-memory
// dataset) in order to produce our final value for a particular header hash and
// nonce.
func hashimotoFull(size uint64, dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) {
func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) {
lookup := func(index uint32) []uint32 {
offset := index * hashWords
return dataset[offset : offset+hashWords]
}
return hashimoto(hash, nonce, size, lookup)
return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup)
}
// datasetSizes is a lookup table for the ethash dataset size for the first 2048

View File

@ -660,7 +660,7 @@ func TestHashimoto(t *testing.T) {
if !bytes.Equal(result, wantResult) {
t.Errorf("light hashimoto result mismatch: have %x, want %x", result, wantResult)
}
digest, result = hashimotoFull(32*1024, dataset, hash, nonce)
digest, result = hashimotoFull(dataset, hash, nonce)
if !bytes.Equal(digest, wantDigest) {
t.Errorf("full hashimoto digest mismatch: have %x, want %x", digest, wantDigest)
}
@ -713,6 +713,17 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
pend.Wait()
}
func TestTestMode(t *testing.T) {
head := &types.Header{Difficulty: big.NewInt(100)}
ethash := NewTestEthash()
nonce, mix := ethash.Search(types.NewBlockWithHeader(head), nil)
head.Nonce = types.EncodeNonce(nonce)
copy(head.MixDigest[:], mix)
if err := ethash.Verify(types.NewBlockWithHeader(head)); err != nil {
t.Error("unexpected Verify error:", err)
}
}
// Benchmarks the cache generation performance.
func BenchmarkCacheGeneration(b *testing.B) {
for i := 0; i < b.N; i++ {
@ -758,6 +769,6 @@ func BenchmarkHashimotoFullSmall(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
hashimotoFull(32*65536, dataset, hash, 0)
hashimotoFull(dataset, hash, 0)
}
}