aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go')
-rw-r--r--Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go576
1 files changed, 245 insertions, 331 deletions
diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
index 74285a33c..1ee980765 100644
--- a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
+++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
@@ -1,32 +1,22 @@
-/*
-###################################################################################
-###################################################################################
-#################### ####################
-#################### EDIT AND YOU SHALL FEEL MY WRATH - jeff ####################
-#################### ####################
-###################################################################################
-###################################################################################
-*/
-
package ethash
/*
-#cgo CFLAGS: -std=gnu99 -Wall
-#include "src/libethash/util.c"
-#include "src/libethash/internal.c"
-#include "src/libethash/sha3.c"
+#include "src/libethash/internal.h"
+
+int ethashGoCallback_cgo(unsigned);
*/
import "C"
import (
- "bytes"
- "encoding/binary"
+ "errors"
"fmt"
"io/ioutil"
"math/big"
"math/rand"
"os"
- "path"
+ "os/user"
+ "path/filepath"
+ "runtime"
"sync"
"time"
"unsafe"
@@ -38,318 +28,267 @@ import (
"github.com/ethereum/go-ethereum/pow"
)
-var minDifficulty = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
-
-type ParamsAndCache struct {
- params *C.ethash_params
- cache *C.ethash_cache
- Epoch uint64
-}
-
-type DAG struct {
- dag unsafe.Pointer // full GB of memory for dag
- file bool
- paramsAndCache *ParamsAndCache
-}
-
-type Ethash struct {
- turbo bool
- HashRate int64
- chainManager pow.ChainManager
- dag *DAG
- paramsAndCache *ParamsAndCache
- ret *C.ethash_return_value
- dagMutex *sync.RWMutex
- cacheMutex *sync.RWMutex
-}
+var (
+ minDifficulty = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
+ sharedLight = new(Light)
+)
-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
+ cacheSizeForTesting C.uint64_t = 1024
+ dagSizeForTesting C.uint64_t = 1024 * 32
+)
-const epochLength uint64 = 30000
+var DefaultDir = defaultDir()
-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)
+func defaultDir() string {
+ home := os.Getenv("HOME")
+ if user, err := user.Current(); err == nil {
+ home = user.HomeDir
}
- paramsAndCache := &ParamsAndCache{
- params: new(C.ethash_params),
- cache: new(C.ethash_cache),
- Epoch: blockNum / epochLength,
+ if runtime.GOOS == "windows" {
+ return filepath.Join(home, "AppData", "Ethash")
}
- C.ethash_params_init(paramsAndCache.params, C.uint32_t(uint32(blockNum)))
- paramsAndCache.cache.mem = C.malloc(C.size_t(paramsAndCache.params.cache_size))
-
- seedHash, err := GetSeedHash(blockNum)
- if err != nil {
- return nil, err
- }
-
- glog.V(logger.Info).Infof("Making cache for epoch: %d (%v) (%x)\n", paramsAndCache.Epoch, blockNum, seedHash)
- start := time.Now()
- C.ethash_mkcache(paramsAndCache.cache, paramsAndCache.params, (*C.ethash_blockhash_t)(unsafe.Pointer(&seedHash[0])))
+ return filepath.Join(home, ".ethash")
+}
- if glog.V(logger.Info) {
- glog.Infoln("Took:", time.Since(start))
- }
+// cache wraps an ethash_light_t with some metadata
+// and automatic memory management.
+type cache struct {
+ epoch uint64
+ test bool
- return paramsAndCache, nil
+ gen sync.Once // ensures cache is only generated once.
+ ptr *C.struct_ethash_light
}
-func (pow *Ethash) UpdateCache(blockNum uint64, force bool) error {
- pow.cacheMutex.Lock()
- defer pow.cacheMutex.Unlock()
-
- thisEpoch := blockNum / epochLength
- if force || pow.paramsAndCache.Epoch != thisEpoch {
- var err error
- pow.paramsAndCache, err = makeParamsAndCache(pow.chainManager, blockNum)
- if err != nil {
- panic(err)
+// generate creates the actual cache. it can be called from multiple
+// goroutines. the first call will generate the cache, subsequent
+// calls wait until it is generated.
+func (cache *cache) generate() {
+ cache.gen.Do(func() {
+ started := time.Now()
+ seedHash := makeSeedHash(cache.epoch)
+ glog.V(logger.Debug).Infof("Generating cache for epoch %d (%x)", cache.epoch, seedHash)
+ size := C.ethash_get_cachesize(C.uint64_t(cache.epoch * epochLength))
+ if cache.test {
+ size = cacheSizeForTesting
}
- }
-
- return nil
+ cache.ptr = C.ethash_light_new_internal(size, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0])))
+ runtime.SetFinalizer(cache, freeCache)
+ glog.V(logger.Debug).Infof("Done generating cache for epoch %d, it took %v", cache.epoch, time.Since(started))
+ })
}
-func makeDAG(p *ParamsAndCache) *DAG {
- d := &DAG{
- dag: C.malloc(C.size_t(p.params.full_size)),
- file: false,
- paramsAndCache: p,
- }
+func freeCache(cache *cache) {
+ C.ethash_light_delete(cache.ptr)
+ cache.ptr = nil
+}
- donech := make(chan string)
- go func() {
- t := time.NewTicker(5 * time.Second)
- tstart := time.Now()
- done:
- for {
- select {
- case <-t.C:
- glog.V(logger.Info).Infof("... still generating DAG (%v) ...\n", time.Since(tstart).Seconds())
- case str := <-donech:
- glog.V(logger.Info).Infof("... %s ...\n", str)
- break done
- }
- }
- }()
- C.ethash_compute_full_data(d.dag, p.params, p.cache)
- donech <- "DAG generation completed"
- return d
+// Light implements the Verify half of the proof of work.
+// It uses a small in-memory cache to verify the nonces
+// found by Full.
+type Light struct {
+ test bool // if set use a smaller cache size
+ mu sync.Mutex // protects current
+ current *cache // last cache which was generated.
+ // TODO: keep multiple caches.
}
-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))
+// Verify checks whether the block's nonce is valid.
+func (l *Light) Verify(block pow.Block) bool {
+ // TODO: do ethash_quick_verify before getCache in order
+ // to prevent DOS attacks.
+ var (
+ blockNum = block.NumberU64()
+ difficulty = block.Difficulty()
+ cache = l.getCache(blockNum)
+ dagSize = C.ethash_get_datasize(C.uint64_t(blockNum))
+ )
+ if l.test {
+ dagSize = dagSizeForTesting
}
- data := C.GoBytes(unsafe.Pointer(dag.dag), C.int(dag.paramsAndCache.params.full_size))
- file, err := os.Create("/tmp/dag")
- if err != nil {
- panic(err)
+ if blockNum >= epochLength*2048 {
+ glog.V(logger.Debug).Infof("block number %d too high, limit is %d", epochLength*2048)
+ return false
}
+ // Recompute the hash using the cache.
+ hash := hashToH256(block.HashNoNonce())
+ ret := C.ethash_light_compute_internal(cache.ptr, dagSize, hash, C.uint64_t(block.Nonce()))
+ if !ret.success {
+ return false
+ }
+ // Make sure cache is live until after the C call.
+ // This is important because a GC might happen and execute
+ // the finalizer before the call completes.
+ _ = cache
+ // The actual check.
+ target := new(big.Int).Div(minDifficulty, difficulty)
+ return h256ToHash(ret.result).Big().Cmp(target) <= 0
+}
- dataEpoch := make([]byte, 8)
- binary.BigEndian.PutUint64(dataEpoch, epoch)
-
- file.Write(dataEpoch)
- file.Write(data)
+func h256ToHash(in C.struct_ethash_h256) common.Hash {
+ return *(*common.Hash)(unsafe.Pointer(&in.b))
+}
- return file
+func hashToH256(in common.Hash) C.struct_ethash_h256 {
+ return C.struct_ethash_h256{b: *(*[32]C.uint8_t)(unsafe.Pointer(&in[0]))}
}
-func (pow *Ethash) UpdateDAG() {
- 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))
+func (l *Light) getCache(blockNum uint64) *cache {
+ var c *cache
+ epoch := blockNum / epochLength
+ // Update or reuse the last cache.
+ l.mu.Lock()
+ if l.current != nil && l.current.epoch == epoch {
+ c = l.current
+ } else {
+ c = &cache{epoch: epoch, test: l.test}
+ l.current = c
}
+ l.mu.Unlock()
+ // Wait for the cache to finish generating.
+ c.generate()
+ return c
+}
- pow.dagMutex.Lock()
- defer pow.dagMutex.Unlock()
- thisEpoch := blockNum / epochLength
- if pow.dag == nil || pow.dag.paramsAndCache.Epoch != thisEpoch {
- if pow.dag != nil && pow.dag.dag != nil {
- C.free(pow.dag.dag)
- pow.dag.dag = nil
- }
+// dag wraps an ethash_full_t with some metadata
+// and automatic memory management.
+type dag struct {
+ epoch uint64
+ test bool
+ dir string
- if pow.dag != nil && pow.dag.paramsAndCache.cache.mem != nil {
- C.free(pow.dag.paramsAndCache.cache.mem)
- pow.dag.paramsAndCache.cache.mem = nil
- }
+ gen sync.Once // ensures DAG is only generated once.
+ ptr *C.struct_ethash_full
+}
- // Make the params and cache for the DAG
- paramsAndCache, err := makeParamsAndCache(pow.chainManager, blockNum)
- if err != nil {
- panic(err)
+// generate creates the actual DAG. it can be called from multiple
+// goroutines. the first call will generate the DAG, subsequent
+// calls wait until it is generated.
+func (d *dag) generate() {
+ d.gen.Do(func() {
+ var (
+ started = time.Now()
+ seedHash = makeSeedHash(d.epoch)
+ blockNum = C.uint64_t(d.epoch * epochLength)
+ cacheSize = C.ethash_get_cachesize(blockNum)
+ dagSize = C.ethash_get_datasize(blockNum)
+ )
+ if d.test {
+ cacheSize = cacheSizeForTesting
+ dagSize = dagSizeForTesting
}
-
- // TODO: On non-SSD disks, loading the DAG from disk takes longer than generating it in memory
- pow.paramsAndCache = paramsAndCache
- path := path.Join("/", "tmp", "dag")
- pow.dag = nil
- glog.V(logger.Info).Infoln("Retrieving DAG")
- start := time.Now()
-
- file, err := os.Open(path)
- if err != nil {
- glog.V(logger.Info).Infof("No DAG found. Generating new DAG in '%s' (this takes a while)...\n", path)
- pow.dag = makeDAG(paramsAndCache)
- file = pow.writeDagToDisk(pow.dag, thisEpoch)
- pow.dag.file = true
- } else {
- data, err := ioutil.ReadAll(file)
- if err != nil {
- glog.V(logger.Info).Infof("DAG load err: %v\n", err)
- }
-
- if len(data) < 8 {
- 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)
- pow.dag = makeDAG(paramsAndCache)
- file = pow.writeDagToDisk(pow.dag, thisEpoch)
- pow.dag.file = true
- } else {
- dataEpoch := binary.BigEndian.Uint64(data[0:8])
- if dataEpoch < thisEpoch {
- glog.V(logger.Info).Infof("DAG in '%s' is stale. Generating new DAG (this takes a while)...\n", path)
- 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) {
- glog.V(logger.Info).Infof("DAG in '%s' is corrupted. Generating new DAG (this takes a while)...\n", path)
- 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,
- }
- }
- }
+ if d.dir == "" {
+ d.dir = DefaultDir
}
- glog.V(logger.Info).Infoln("Took:", time.Since(start))
-
- file.Close()
- }
-}
-
-func New(chainManager pow.ChainManager) *Ethash {
- paramsAndCache, err := makeParamsAndCache(chainManager, chainManager.CurrentBlock().NumberU64())
- if err != nil {
- panic(err)
- }
-
- return &Ethash{
- turbo: true,
- paramsAndCache: paramsAndCache,
- chainManager: chainManager,
- dag: nil,
- cacheMutex: new(sync.RWMutex),
- dagMutex: new(sync.RWMutex),
- }
+ glog.V(logger.Info).Infof("Generating DAG for epoch %d (%x)", d.epoch, seedHash)
+ // Generate a temporary cache.
+ // TODO: this could share the cache with Light
+ cache := C.ethash_light_new_internal(cacheSize, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0])))
+ defer C.ethash_light_delete(cache)
+ // Generate the actual DAG.
+ d.ptr = C.ethash_full_new_internal(
+ C.CString(d.dir),
+ hashToH256(seedHash),
+ dagSize,
+ cache,
+ (C.ethash_callback_t)(unsafe.Pointer(C.ethashGoCallback_cgo)),
+ )
+ if d.ptr == nil {
+ panic("ethash_full_new IO or memory error")
+ }
+ runtime.SetFinalizer(d, freeDAG)
+ glog.V(logger.Info).Infof("Done generating DAG for epoch %d, it took %v", d.epoch, time.Since(started))
+ })
}
-func (pow *Ethash) DAGSize() uint64 {
- return uint64(pow.dag.paramsAndCache.params.full_size)
+func freeDAG(h *dag) {
+ C.ethash_full_delete(h.ptr)
+ h.ptr = nil
}
-func (pow *Ethash) CacheSize() uint64 {
- return uint64(pow.paramsAndCache.params.cache_size)
+//export ethashGoCallback
+func ethashGoCallback(percent C.unsigned) C.int {
+ glog.V(logger.Info).Infof("Still generating DAG: %d%%", percent)
+ return 0
}
-func GetSeedHash(blockNum uint64) ([]byte, error) {
+// MakeDAG pre-generates a DAG file for the given block number in the
+// given directory. If dir is the empty string, the default directory
+// is used.
+func MakeDAG(blockNum uint64, dir string) error {
+ d := &dag{epoch: blockNum / epochLength, dir: dir}
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
+ return fmt.Errorf("block number too high, limit is %d", epochLength*2048)
}
- for i = 0; i < epoch; i++ {
- seedHash = crypto.Sha3(seedHash)
+ d.generate()
+ if d.ptr == nil {
+ return errors.New("failed")
}
- return seedHash, nil
+ return nil
}
-func (pow *Ethash) Stop() {
- pow.cacheMutex.Lock()
- pow.dagMutex.Lock()
- defer pow.dagMutex.Unlock()
- defer pow.cacheMutex.Unlock()
+// Full implements the Search half of the proof of work.
+type Full struct {
+ Dir string // use this to specify a non-default DAG directory
- if pow.paramsAndCache.cache != nil {
- C.free(pow.paramsAndCache.cache.mem)
- }
- if pow.dag.dag != nil && !pow.dag.file {
- C.free(pow.dag.dag)
- }
- 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
- }
- pow.dag.dag = nil
+ test bool // if set use a smaller DAG size
+ turbo bool
+ hashRate int64
+
+ mu sync.Mutex // protects dag
+ current *dag // current full DAG
}
-func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte, []byte) {
- pow.UpdateDAG()
+func (pow *Full) getDAG(blockNum uint64) (d *dag) {
+ epoch := blockNum / epochLength
+ pow.mu.Lock()
+ if pow.current != nil && pow.current.epoch == epoch {
+ d = pow.current
+ } else {
+ d = &dag{epoch: epoch, test: pow.test, dir: pow.Dir}
+ pow.current = d
+ }
+ pow.mu.Unlock()
+ // wait for it to finish generating.
+ d.generate()
+ return d
+}
- pow.dagMutex.RLock()
- defer pow.dagMutex.RUnlock()
+func (pow *Full) Search(block pow.Block, stop <-chan struct{}) (nonce uint64, mixDigest []byte) {
+ dag := pow.getDAG(block.NumberU64())
r := rand.New(rand.NewSource(time.Now().UnixNano()))
- miningHash := block.HashNoNonce()
diff := block.Difficulty()
i := int64(0)
starti := i
start := time.Now().UnixNano()
- nonce := uint64(r.Int63())
- cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
+ nonce = uint64(r.Int63())
+ hash := hashToH256(block.HashNoNonce())
target := new(big.Int).Div(minDifficulty, diff)
-
- var ret C.ethash_return_value
for {
select {
case <-stop:
- pow.HashRate = 0
- return 0, nil, nil
+ pow.hashRate = 0
+ return 0, nil
default:
i++
elapsed := time.Now().UnixNano() - start
hashes := ((float64(1e9) / float64(elapsed)) * float64(i-starti)) / 1000
- pow.HashRate = int64(hashes)
+ pow.hashRate = int64(hashes)
- C.ethash_full(&ret, pow.dag.dag, pow.dag.paramsAndCache.params, cMiningHash, C.uint64_t(nonce))
- result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
+ ret := C.ethash_full_compute(dag.ptr, hash, C.uint64_t(nonce))
+ result := h256ToHash(ret.result).Big()
// TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining
- if result.Cmp(target) <= 0 {
- mixDigest := C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32))
- seedHash, err := GetSeedHash(block.NumberU64()) // This seedhash is useless
- if err != nil {
- panic(err)
- }
- return nonce, mixDigest, seedHash
+ if ret.success && result.Cmp(target) <= 0 {
+ mixDigest = C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32))
+ return nonce, mixDigest
}
-
nonce += 1
}
@@ -357,82 +296,57 @@ func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte
time.Sleep(20 * time.Microsecond)
}
}
-
}
-func (pow *Ethash) Verify(block pow.Block) bool {
-
- return pow.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), block.Nonce())
+func (pow *Full) GetHashrate() int64 {
+ // TODO: this needs to use an atomic operation.
+ return pow.hashRate
}
-func (pow *Ethash) verify(hash common.Hash, mixDigest common.Hash, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
- // Make sure the block num is valid
- if blockNum >= epochLength*2048 {
- glog.V(logger.Info).Infoln(fmt.Sprintf("Block number exceeds limit, invalid (value is %v, limit is %v)",
- blockNum, epochLength*2048))
- return false
- }
-
- // First check: make sure header, mixDigest, nonce are correct without hitting the cache
- // This is to prevent DOS attacks
- chash := (*C.ethash_blockhash_t)(unsafe.Pointer(&hash[0]))
- cnonce := C.uint64_t(nonce)
- target := new(big.Int).Div(minDifficulty, difficulty)
-
- 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)
- // Otherwise, it's the current block or a future block.
- // If current, updateCache will do nothing.
- if blockNum/epochLength < pow.paramsAndCache.Epoch {
- var err error
- // If we can't make the params for some reason, this block is invalid
- pAc, err = makeParamsAndCache(pow.chainManager, blockNum)
- if err != nil {
- glog.V(logger.Info).Infoln("big fucking eror", err)
- return false
- }
- } else {
- pow.UpdateCache(blockNum, false)
- pow.cacheMutex.RLock()
- defer pow.cacheMutex.RUnlock()
- pAc = pow.paramsAndCache
- }
-
- ret := new(C.ethash_return_value)
-
- C.ethash_light(ret, pAc.cache, pAc.params, chash, cnonce)
+func (pow *Full) Turbo(on bool) {
+ // TODO: this needs to use an atomic operation.
+ pow.turbo = on
+}
- result := common.Bytes2Big(C.GoBytes(unsafe.Pointer(&ret.result), C.int(32)))
- return result.Cmp(target) <= 0
+// Ethash combines block verification with Light and
+// nonce searching with Full into a single proof of work.
+type Ethash struct {
+ *Light
+ *Full
}
-func (pow *Ethash) GetHashrate() int64 {
- return pow.HashRate
+// New creates an instance of the proof of work.
+// A single instance of Light is shared across all instances
+// created with New.
+func New() *Ethash {
+ return &Ethash{sharedLight, &Full{turbo: true}}
}
-func (pow *Ethash) Turbo(on bool) {
- pow.turbo = on
+// NewForTesting creates a proof of work for use in unit tests.
+// It uses a smaller DAG and cache size to keep test times low.
+// DAG files are stored in a temporary directory.
+//
+// Nonces found by a testing instance are not verifiable with a
+// regular-size cache.
+func NewForTesting() (*Ethash, error) {
+ dir, err := ioutil.TempDir("", "ethash-test")
+ if err != nil {
+ return nil, err
+ }
+ return &Ethash{&Light{test: true}, &Full{Dir: dir, test: true}}, nil
}
-func (pow *Ethash) FullHash(nonce uint64, miningHash []byte) []byte {
- pow.UpdateDAG()
- pow.dagMutex.Lock()
- defer pow.dagMutex.Unlock()
- cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
- cnonce := C.uint64_t(nonce)
- ret := new(C.ethash_return_value)
- // pow.hash is the output/return of ethash_full
- C.ethash_full(ret, pow.dag.dag, pow.paramsAndCache.params, cMiningHash, cnonce)
- ghash_full := C.GoBytes(unsafe.Pointer(&ret.result), 32)
- return ghash_full
+func GetSeedHash(blockNum uint64) ([]byte, error) {
+ if blockNum >= epochLength*2048 {
+ return nil, fmt.Errorf("block number too high, limit is %d", epochLength*2048)
+ }
+ sh := makeSeedHash(blockNum / epochLength)
+ return sh[:], nil
}
-func (pow *Ethash) LightHash(nonce uint64, miningHash []byte) []byte {
- cMiningHash := (*C.ethash_blockhash_t)(unsafe.Pointer(&miningHash[0]))
- cnonce := C.uint64_t(nonce)
- 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)
- return ghash_light
+func makeSeedHash(epoch uint64) (sh common.Hash) {
+ for ; epoch > 0; epoch-- {
+ sh = crypto.Sha3Hash(sh[:])
+ }
+ return sh
}