2015-03-14 15:42:05 +00:00
|
|
|
/*
|
|
|
|
###################################################################################
|
|
|
|
###################################################################################
|
|
|
|
#################### ####################
|
|
|
|
#################### EDIT AND YOU SHALL FEEL MY WRATH - jeff ####################
|
|
|
|
#################### ####################
|
|
|
|
###################################################################################
|
|
|
|
###################################################################################
|
|
|
|
*/
|
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
package ethash
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo CFLAGS: -std=gnu99 -Wall
|
2015-03-06 12:27:11 +00:00
|
|
|
#include "src/libethash/util.c"
|
|
|
|
#include "src/libethash/internal.c"
|
|
|
|
#include "src/libethash/sha3.c"
|
2015-02-28 19:58:37 +00:00
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2015-03-14 15:42:05 +00:00
|
|
|
"fmt"
|
2015-03-06 12:27:11 +00:00
|
|
|
"io/ioutil"
|
2015-02-28 19:58:37 +00:00
|
|
|
"math/big"
|
|
|
|
"math/rand"
|
2015-03-06 12:27:11 +00:00
|
|
|
"os"
|
|
|
|
"path"
|
2015-02-28 19:58:37 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
"unsafe"
|
|
|
|
|
2015-03-16 10:27:38 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2015-03-20 10:19:12 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2015-02-28 19:58:37 +00:00
|
|
|
"github.com/ethereum/go-ethereum/logger"
|
2015-04-07 11:17:27 +00:00
|
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
2015-02-28 19:58:37 +00:00
|
|
|
"github.com/ethereum/go-ethereum/pow"
|
|
|
|
)
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
var minDifficulty = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
type ParamsAndCache struct {
|
|
|
|
params *C.ethash_params
|
|
|
|
cache *C.ethash_cache
|
|
|
|
Epoch uint64
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
type DAG struct {
|
|
|
|
dag unsafe.Pointer // full GB of memory for dag
|
|
|
|
file bool
|
|
|
|
paramsAndCache *ParamsAndCache
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Ethash struct {
|
|
|
|
turbo bool
|
|
|
|
HashRate int64
|
|
|
|
chainManager pow.ChainManager
|
|
|
|
dag *DAG
|
|
|
|
paramsAndCache *ParamsAndCache
|
|
|
|
ret *C.ethash_return_value
|
2015-03-14 15:42:05 +00:00
|
|
|
dagMutex *sync.RWMutex
|
|
|
|
cacheMutex *sync.RWMutex
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseNonce(nonce []byte) (uint64, error) {
|
|
|
|
nonceBuf := bytes.NewBuffer(nonce)
|
|
|
|
nonceInt, err := binary.ReadUvarint(nonceBuf)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return nonceInt, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const epochLength uint64 = 30000
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
func makeParamsAndCache(chainManager pow.ChainManager, blockNum uint64) (*ParamsAndCache, error) {
|
|
|
|
if blockNum >= epochLength*2048 {
|
|
|
|
return nil, fmt.Errorf("block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048)
|
2015-03-06 12:27:11 +00:00
|
|
|
}
|
2015-02-28 19:58:37 +00:00
|
|
|
paramsAndCache := &ParamsAndCache{
|
2015-03-14 15:42:05 +00:00
|
|
|
params: new(C.ethash_params),
|
|
|
|
cache: new(C.ethash_cache),
|
|
|
|
Epoch: blockNum / epochLength,
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
2015-03-14 15:42:05 +00:00
|
|
|
C.ethash_params_init(paramsAndCache.params, C.uint32_t(uint32(blockNum)))
|
2015-04-05 13:14:55 +00:00
|
|
|
paramsAndCache.cache.mem = C.malloc(C.size_t(paramsAndCache.params.cache_size))
|
2015-02-28 19:58:37 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
seedHash, err := GetSeedHash(blockNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infoln("Making cache")
|
2015-02-28 19:58:37 +00:00
|
|
|
start := time.Now()
|
2015-04-07 11:17:27 +00:00
|
|
|
C.ethash_mkcache(paramsAndCache.cache, paramsAndCache.params, (*C.ethash_blockhash_t)(unsafe.Pointer(&seedHash[0])))
|
|
|
|
|
|
|
|
if glog.V(logger.Info) {
|
|
|
|
glog.Infoln("Took:", time.Since(start))
|
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
return paramsAndCache, nil
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
2015-04-05 13:14:55 +00:00
|
|
|
func (pow *Ethash) UpdateCache(blockNum uint64, force bool) error {
|
2015-02-28 19:58:37 +00:00
|
|
|
pow.cacheMutex.Lock()
|
2015-03-15 20:47:59 +00:00
|
|
|
defer pow.cacheMutex.Unlock()
|
|
|
|
|
2015-04-05 13:14:55 +00:00
|
|
|
thisEpoch := blockNum / epochLength
|
2015-03-14 15:42:05 +00:00
|
|
|
if force || pow.paramsAndCache.Epoch != thisEpoch {
|
|
|
|
var err error
|
2015-04-05 13:14:55 +00:00
|
|
|
pow.paramsAndCache, err = makeParamsAndCache(pow.chainManager, blockNum)
|
2015-03-14 15:42:05 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
2015-03-15 20:47:59 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
return nil
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func makeDAG(p *ParamsAndCache) *DAG {
|
|
|
|
d := &DAG{
|
2015-04-05 13:14:55 +00:00
|
|
|
dag: C.malloc(C.size_t(p.params.full_size)),
|
2015-03-14 15:42:05 +00:00
|
|
|
file: false,
|
|
|
|
paramsAndCache: p,
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
2015-03-14 15:42:05 +00:00
|
|
|
|
|
|
|
donech := make(chan string)
|
|
|
|
go func() {
|
|
|
|
t := time.NewTicker(5 * time.Second)
|
|
|
|
tstart := time.Now()
|
|
|
|
done:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t.C:
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("... still generating DAG (%v) ...\n", time.Since(tstart).Seconds())
|
2015-03-14 15:42:05 +00:00
|
|
|
case str := <-donech:
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("... %s ...\n", str)
|
2015-03-14 15:42:05 +00:00
|
|
|
break done
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2015-02-28 19:58:37 +00:00
|
|
|
C.ethash_compute_full_data(d.dag, p.params, p.cache)
|
2015-03-14 15:42:05 +00:00
|
|
|
donech <- "DAG generation completed"
|
2015-02-28 19:58:37 +00:00
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
func (pow *Ethash) writeDagToDisk(dag *DAG, epoch uint64) *os.File {
|
|
|
|
if epoch > 2048 {
|
|
|
|
panic(fmt.Errorf("Epoch must be less than 2048 (is %v)", epoch))
|
|
|
|
}
|
|
|
|
data := C.GoBytes(unsafe.Pointer(dag.dag), C.int(dag.paramsAndCache.params.full_size))
|
2015-03-06 12:27:11 +00:00
|
|
|
file, err := os.Create("/tmp/dag")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
dataEpoch := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(dataEpoch, epoch)
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
file.Write(dataEpoch)
|
2015-03-06 12:27:11 +00:00
|
|
|
file.Write(data)
|
|
|
|
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) UpdateDAG() {
|
2015-03-14 15:42:05 +00:00
|
|
|
blockNum := pow.chainManager.CurrentBlock().NumberU64()
|
|
|
|
if blockNum >= epochLength*2048 {
|
|
|
|
// This will crash in the 2030s or 2040s
|
|
|
|
panic(fmt.Errorf("Current block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048))
|
|
|
|
}
|
2015-02-28 19:58:37 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dagMutex.Lock()
|
|
|
|
defer pow.dagMutex.Unlock()
|
|
|
|
thisEpoch := blockNum / epochLength
|
|
|
|
if pow.dag == nil || pow.dag.paramsAndCache.Epoch != thisEpoch {
|
2015-03-06 12:27:11 +00:00
|
|
|
if pow.dag != nil && pow.dag.dag != nil {
|
|
|
|
C.free(pow.dag.dag)
|
|
|
|
pow.dag.dag = nil
|
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
if pow.dag != nil && pow.dag.paramsAndCache.cache.mem != nil {
|
|
|
|
C.free(pow.dag.paramsAndCache.cache.mem)
|
|
|
|
pow.dag.paramsAndCache.cache.mem = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the params and cache for the DAG
|
|
|
|
paramsAndCache, err := makeParamsAndCache(pow.chainManager, blockNum)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: On non-SSD disks, loading the DAG from disk takes longer than generating it in memory
|
|
|
|
pow.paramsAndCache = paramsAndCache
|
2015-03-06 12:27:11 +00:00
|
|
|
path := path.Join("/", "tmp", "dag")
|
2015-02-28 19:58:37 +00:00
|
|
|
pow.dag = nil
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infoln("Retrieving DAG")
|
2015-02-28 19:58:37 +00:00
|
|
|
start := time.Now()
|
2015-03-06 12:27:11 +00:00
|
|
|
|
|
|
|
file, err := os.Open(path)
|
|
|
|
if err != nil {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("No DAG found. Generating new DAG in '%s' (this takes a while)...\n", path)
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dag = makeDAG(paramsAndCache)
|
|
|
|
file = pow.writeDagToDisk(pow.dag, thisEpoch)
|
|
|
|
pow.dag.file = true
|
2015-03-06 12:27:11 +00:00
|
|
|
} else {
|
|
|
|
data, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("DAG load err: %v\n", err)
|
2015-03-06 12:27:11 +00:00
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
if len(data) < 8 {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("DAG in '%s' is less than 8 bytes, it must be corrupted. Generating new DAG (this takes a while)...\n", path)
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dag = makeDAG(paramsAndCache)
|
|
|
|
file = pow.writeDagToDisk(pow.dag, thisEpoch)
|
|
|
|
pow.dag.file = true
|
2015-03-06 12:27:11 +00:00
|
|
|
} else {
|
2015-03-14 15:42:05 +00:00
|
|
|
dataEpoch := binary.BigEndian.Uint64(data[0:8])
|
|
|
|
if dataEpoch < thisEpoch {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("DAG in '%s' is stale. Generating new DAG (this takes a while)...\n", path)
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dag = makeDAG(paramsAndCache)
|
|
|
|
file = pow.writeDagToDisk(pow.dag, thisEpoch)
|
|
|
|
pow.dag.file = true
|
|
|
|
} else if dataEpoch > thisEpoch {
|
|
|
|
// FIXME
|
|
|
|
panic(fmt.Errorf("Saved DAG in '%s' reports to be from future epoch %v (current epoch is %v)\n", path, dataEpoch, thisEpoch))
|
|
|
|
} else if len(data) != (int(paramsAndCache.params.full_size) + 8) {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infof("DAG in '%s' is corrupted. Generating new DAG (this takes a while)...\n", path)
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dag = makeDAG(paramsAndCache)
|
|
|
|
file = pow.writeDagToDisk(pow.dag, thisEpoch)
|
|
|
|
pow.dag.file = true
|
|
|
|
} else {
|
|
|
|
data = data[8:]
|
|
|
|
pow.dag = &DAG{
|
|
|
|
dag: unsafe.Pointer(&data[0]),
|
|
|
|
file: true,
|
|
|
|
paramsAndCache: paramsAndCache,
|
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infoln("Took:", time.Since(start))
|
2015-03-06 12:27:11 +00:00
|
|
|
|
|
|
|
file.Close()
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(chainManager pow.ChainManager) *Ethash {
|
2015-03-14 15:42:05 +00:00
|
|
|
paramsAndCache, err := makeParamsAndCache(chainManager, chainManager.CurrentBlock().NumberU64())
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
return &Ethash{
|
2015-03-06 12:27:11 +00:00
|
|
|
turbo: true,
|
2015-03-14 15:42:05 +00:00
|
|
|
paramsAndCache: paramsAndCache,
|
2015-02-28 19:58:37 +00:00
|
|
|
chainManager: chainManager,
|
|
|
|
dag: nil,
|
2015-03-14 15:42:05 +00:00
|
|
|
cacheMutex: new(sync.RWMutex),
|
|
|
|
dagMutex: new(sync.RWMutex),
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) DAGSize() uint64 {
|
2015-03-14 15:42:05 +00:00
|
|
|
return uint64(pow.dag.paramsAndCache.params.full_size)
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) CacheSize() uint64 {
|
|
|
|
return uint64(pow.paramsAndCache.params.cache_size)
|
|
|
|
}
|
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
func GetSeedHash(blockNum uint64) ([]byte, error) {
|
|
|
|
if blockNum >= epochLength*2048 {
|
|
|
|
return nil, fmt.Errorf("block number is out of bounds (value %v, limit is %v)", blockNum, epochLength*2048)
|
|
|
|
}
|
|
|
|
|
|
|
|
epoch := blockNum / epochLength
|
|
|
|
seedHash := make([]byte, 32)
|
|
|
|
var i uint64
|
|
|
|
for i = 0; i < 32; i++ {
|
|
|
|
seedHash[i] = 0
|
|
|
|
}
|
|
|
|
for i = 0; i < epoch; i++ {
|
|
|
|
seedHash = crypto.Sha3(seedHash)
|
|
|
|
}
|
|
|
|
return seedHash, nil
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) Stop() {
|
|
|
|
pow.cacheMutex.Lock()
|
|
|
|
pow.dagMutex.Lock()
|
2015-03-06 12:27:11 +00:00
|
|
|
defer pow.dagMutex.Unlock()
|
|
|
|
defer pow.cacheMutex.Unlock()
|
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
if pow.paramsAndCache.cache != nil {
|
|
|
|
C.free(pow.paramsAndCache.cache.mem)
|
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
if pow.dag.dag != nil && !pow.dag.file {
|
2015-02-28 19:58:37 +00:00
|
|
|
C.free(pow.dag.dag)
|
|
|
|
}
|
2015-03-14 15:42:05 +00:00
|
|
|
if pow.dag != nil && pow.dag.paramsAndCache != nil && pow.dag.paramsAndCache.cache.mem != nil {
|
|
|
|
C.free(pow.dag.paramsAndCache.cache.mem)
|
|
|
|
pow.dag.paramsAndCache.cache.mem = nil
|
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
pow.dag.dag = nil
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
2015-03-06 12:27:11 +00:00
|
|
|
func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte, []byte) {
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.UpdateDAG()
|
2015-02-28 19:58:37 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.dagMutex.RLock()
|
|
|
|
defer pow.dagMutex.RUnlock()
|
2015-02-28 19:58:37 +00:00
|
|
|
|
|
|
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
miningHash := block.HashNoNonce()
|
|
|
|
diff := block.Difficulty()
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
i := int64(0)
|
2015-03-06 12:27:11 +00:00
|
|
|
starti := i
|
2015-02-28 19:58:37 +00:00
|
|
|
start := time.Now().UnixNano()
|
|
|
|
|
|
|
|
nonce := uint64(r.Int63())
|
2015-04-07 11:17:27 +00:00
|
|
|
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
|
2015-03-14 15:42:05 +00:00
|
|
|
target := new(big.Int).Div(minDifficulty, diff)
|
2015-02-28 19:58:37 +00:00
|
|
|
|
2015-03-06 12:27:11 +00:00
|
|
|
var ret C.ethash_return_value
|
2015-02-28 19:58:37 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
pow.HashRate = 0
|
2015-03-06 12:27:11 +00:00
|
|
|
return 0, nil, nil
|
2015-02-28 19:58:37 +00:00
|
|
|
default:
|
|
|
|
i++
|
|
|
|
|
2015-03-06 12:27:11 +00:00
|
|
|
elapsed := time.Now().UnixNano() - start
|
|
|
|
hashes := ((float64(1e9) / float64(elapsed)) * float64(i-starti)) / 1000
|
|
|
|
pow.HashRate = int64(hashes)
|
2015-02-28 19:58:37 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
C.ethash_full(&ret, pow.dag.dag, pow.dag.paramsAndCache.params, cMiningHash, C.uint64_t(nonce))
|
2015-04-07 11:17:27 +00:00
|
|
|
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-03-14 15:42:05 +00:00
|
|
|
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
|
2015-03-06 12:27:11 +00:00
|
|
|
if result.Cmp(target) <= 0 {
|
2015-04-07 11:17:27 +00:00
|
|
|
mixDigest := C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32))
|
2015-03-14 15:42:05 +00:00
|
|
|
seedHash, err := GetSeedHash(block.NumberU64()) // This seedhash is useless
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return nonce, mixDigest, seedHash
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
nonce += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if !pow.turbo {
|
|
|
|
time.Sleep(20 * time.Microsecond)
|
|
|
|
}
|
|
|
|
}
|
2015-03-06 12:27:11 +00:00
|
|
|
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) Verify(block pow.Block) bool {
|
2015-03-25 13:57:49 +00:00
|
|
|
|
|
|
|
return pow.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), block.Nonce())
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
2015-03-25 13:57:49 +00:00
|
|
|
func (pow *Ethash) verify(hash common.Hash, mixDigest common.Hash, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
|
2015-03-14 15:42:05 +00:00
|
|
|
// Make sure the block num is valid
|
|
|
|
if blockNum >= epochLength*2048 {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infoln(fmt.Sprintf("Block number exceeds limit, invalid (value is %v, limit is %v)",
|
2015-03-14 15:42:05 +00:00
|
|
|
blockNum, epochLength*2048))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// First check: make sure header, mixDigest, nonce are correct without hitting the cache
|
2015-02-28 19:58:37 +00:00
|
|
|
// This is to prevent DOS attacks
|
2015-04-07 11:17:27 +00:00
|
|
|
chash := (*C.ethash_blockhash_t)(unsafe.Pointer(&hash[0]))
|
2015-02-28 19:58:37 +00:00
|
|
|
cnonce := C.uint64_t(nonce)
|
2015-03-14 15:42:05 +00:00
|
|
|
target := new(big.Int).Div(minDifficulty, difficulty)
|
2015-02-28 19:58:37 +00:00
|
|
|
|
|
|
|
var pAc *ParamsAndCache
|
|
|
|
// If its an old block (doesn't use the current cache)
|
|
|
|
// get the cache for it but don't update (so we don't need the mutex)
|
2015-03-14 15:42:05 +00:00
|
|
|
// Otherwise, it's the current block or a future block.
|
2015-02-28 19:58:37 +00:00
|
|
|
// If current, updateCache will do nothing.
|
2015-03-14 15:42:05 +00:00
|
|
|
if blockNum/epochLength < pow.paramsAndCache.Epoch {
|
|
|
|
var err error
|
|
|
|
// If we can't make the params for some reason, this block is invalid
|
2015-04-05 13:14:55 +00:00
|
|
|
pAc, err = makeParamsAndCache(pow.chainManager, blockNum+1)
|
2015-03-14 15:42:05 +00:00
|
|
|
if err != nil {
|
2015-04-07 11:17:27 +00:00
|
|
|
glog.V(logger.Info).Infoln("big fucking eror", err)
|
2015-03-14 15:42:05 +00:00
|
|
|
return false
|
|
|
|
}
|
2015-02-28 19:58:37 +00:00
|
|
|
} else {
|
2015-04-05 13:14:55 +00:00
|
|
|
pow.UpdateCache(blockNum, false)
|
2015-03-14 15:42:05 +00:00
|
|
|
pow.cacheMutex.RLock()
|
|
|
|
defer pow.cacheMutex.RUnlock()
|
2015-02-28 19:58:37 +00:00
|
|
|
pAc = pow.paramsAndCache
|
|
|
|
}
|
|
|
|
|
2015-03-06 12:27:11 +00:00
|
|
|
ret := new(C.ethash_return_value)
|
|
|
|
|
|
|
|
C.ethash_light(ret, pAc.cache, pAc.params, chash, cnonce)
|
|
|
|
|
2015-04-07 11:17:27 +00:00
|
|
|
result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
|
2015-03-06 12:27:11 +00:00
|
|
|
return result.Cmp(target) <= 0
|
2015-02-28 19:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) GetHashrate() int64 {
|
|
|
|
return pow.HashRate
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) Turbo(on bool) {
|
|
|
|
pow.turbo = on
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) FullHash(nonce uint64, miningHash []byte) []byte {
|
2015-03-06 12:27:11 +00:00
|
|
|
pow.UpdateDAG()
|
2015-02-28 19:58:37 +00:00
|
|
|
pow.dagMutex.Lock()
|
|
|
|
defer pow.dagMutex.Unlock()
|
2015-04-07 11:17:27 +00:00
|
|
|
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
|
2015-02-28 19:58:37 +00:00
|
|
|
cnonce := C.uint64_t(nonce)
|
2015-03-06 12:27:11 +00:00
|
|
|
ret := new(C.ethash_return_value)
|
2015-02-28 19:58:37 +00:00
|
|
|
// pow.hash is the output/return of ethash_full
|
2015-03-06 12:27:11 +00:00
|
|
|
C.ethash_full(ret, pow.dag.dag, pow.paramsAndCache.params, cMiningHash, cnonce)
|
|
|
|
ghash_full := C.GoBytes(unsafe.Pointer(&ret.result), 32)
|
2015-02-28 19:58:37 +00:00
|
|
|
return ghash_full
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pow *Ethash) LightHash(nonce uint64, miningHash []byte) []byte {
|
2015-04-07 11:17:27 +00:00
|
|
|
cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
|
2015-02-28 19:58:37 +00:00
|
|
|
cnonce := C.uint64_t(nonce)
|
2015-03-06 12:27:11 +00:00
|
|
|
ret := new(C.ethash_return_value)
|
|
|
|
C.ethash_light(ret, pow.paramsAndCache.cache, pow.paramsAndCache.params, cMiningHash, cnonce)
|
|
|
|
ghash_light := C.GoBytes(unsafe.Pointer(&ret.result), 32)
|
2015-02-28 19:58:37 +00:00
|
|
|
return ghash_light
|
|
|
|
}
|