pow: only support prime calculations on Go 1.8 and above

This commit is contained in:
Péter Szilágyi 2017-03-06 15:00:20 +02:00 committed by Felix Lange
parent 023670f6ba
commit df72e20cc5
6 changed files with 157 additions and 63 deletions

View File

@ -35,6 +35,7 @@ import (
) )
var ( var (
ErrNonceOutOfRange = errors.New("nonce out of range")
ErrInvalidDifficulty = errors.New("non-positive difficulty") ErrInvalidDifficulty = errors.New("non-positive difficulty")
ErrInvalidMixDigest = errors.New("invalid mix digest") ErrInvalidMixDigest = errors.New("invalid mix digest")
ErrInvalidPoW = errors.New("pow difficulty invalid") ErrInvalidPoW = errors.New("pow difficulty invalid")
@ -174,13 +175,18 @@ func NewSharedEthash() PoW {
// Verify implements PoW, checking whether the given block satisfies the PoW // Verify implements PoW, checking whether the given block satisfies the PoW
// difficulty requirements. // difficulty requirements.
func (ethash *Ethash) Verify(block Block) error { func (ethash *Ethash) Verify(block Block) error {
// Sanity check that the block number is below the lookup table size (60M blocks)
number := block.NumberU64()
if number/epochLength >= uint64(len(cacheSizes)) {
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
return ErrNonceOutOfRange
}
// Ensure twe have a valid difficulty for the block // Ensure twe have a valid difficulty for the block
difficulty := block.Difficulty() difficulty := block.Difficulty()
if difficulty.Sign() <= 0 { if difficulty.Sign() <= 0 {
return ErrInvalidDifficulty return ErrInvalidDifficulty
} }
// Recompute the digest and PoW value and verify against the block // Recompute the digest and PoW value and verify against the block
number := block.NumberU64()
cache := ethash.cache(number) cache := ethash.cache(number)
size := datasetSize(number) size := datasetSize(number)

View File

@ -19,7 +19,6 @@ package pow
import ( import (
"encoding/binary" "encoding/binary"
"io" "io"
"math/big"
"runtime" "runtime"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -45,42 +44,6 @@ const (
loopAccesses = 64 // Number of accesses in hashimoto loop loopAccesses = 64 // Number of accesses in hashimoto loop
) )
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(cacheSizes) {
return cacheSizes[epoch]
}
// No known cache size, calculate manually (sanity branch only)
size := uint64(cacheInitBytes + cacheGrowthBytes*epoch - hashBytes)
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize calculates and returns the size of the ethash mining dataset that
// belongs to a certain block number. The dataset size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(datasetSizes) {
return datasetSizes[epoch]
}
// No known dataset size, calculate manually (sanity branch only)
size := uint64(datasetInitBytes + datasetGrowthBytes*epoch - mixBytes)
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}
// seedHash is the seed to use for generating a verification cache and the mining // seedHash is the seed to use for generating a verification cache and the mining
// dataset. // dataset.
func seedHash(block uint64) []byte { func seedHash(block uint64) []byte {

47
pow/ethash_algo_go1.7.go Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !go1.8
package pow
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(cacheSizes) {
return cacheSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}
// datasetSize calculates and returns the size of the ethash mining dataset that
// belongs to a certain block number. The dataset size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(datasetSizes) {
return datasetSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}

57
pow/ethash_algo_go1.8.go Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build go1.8
package pow
import "math/big"
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(cacheSizes) {
return cacheSizes[epoch]
}
// No known cache size, calculate manually (sanity branch only)
size := uint64(cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes)
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize calculates and returns the size of the ethash mining dataset that
// belongs to a certain block number. The dataset size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < len(datasetSizes) {
return datasetSizes[epoch]
}
// No known dataset size, calculate manually (sanity branch only)
size := uint64(datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes)
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}

View File

@ -0,0 +1,46 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build go1.8
package pow
import "testing"
// Tests whether the dataset size calculator work correctly by cross checking the
// hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) {
var tests []uint64
// Verify all the cache sizes from the lookup table
defer func(sizes []uint64) { cacheSizes = sizes }(cacheSizes)
tests, cacheSizes = cacheSizes, []uint64{}
for i, test := range tests {
if size := cacheSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", i, size, test)
}
}
// Verify all the dataset sizes from the lookup table
defer func(sizes []uint64) { datasetSizes = sizes }(datasetSizes)
tests, datasetSizes = datasetSizes, []uint64{}
for i, test := range tests {
if size := datasetSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", i, size, test)
}
}
}

View File

@ -23,31 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
) )
// Tests whether the dataset size calculator work correctly by cross checking the
// hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) {
var tests []uint64
// Verify all the cache sizes from the lookup table
defer func(sizes []uint64) { cacheSizes = sizes }(cacheSizes)
tests, cacheSizes = cacheSizes, []uint64{}
for i, test := range tests {
if size := cacheSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", i, size, test)
}
}
// Verify all the dataset sizes from the lookup table
defer func(sizes []uint64) { datasetSizes = sizes }(datasetSizes)
tests, datasetSizes = datasetSizes, []uint64{}
for i, test := range tests {
if size := datasetSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", i, size, test)
}
}
}
// Tests that verification caches can be correctly generated. // Tests that verification caches can be correctly generated.
func TestCacheGeneration(t *testing.T) { func TestCacheGeneration(t *testing.T) {
tests := []struct { tests := []struct {