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.go296
1 files changed, 296 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
new file mode 100644
index 000000000..32d3f0264
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go
@@ -0,0 +1,296 @@
+package ethash
+
+/*
+#cgo CFLAGS: -std=gnu99 -Wall
+#include "libethash/ethash.h"
+#include "libethash/util.c"
+#include "libethash/internal.c"
+#include "libethash/sha3.c"
+*/
+import "C"
+
+import (
+ "bytes"
+ "encoding/binary"
+ "log"
+ "math/big"
+ "math/rand"
+ "sync"
+ "time"
+ "unsafe"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/pow"
+)
+
+var powlogger = logger.NewLogger("POW")
+
+type DAG struct {
+ SeedBlockNum uint64
+ dag unsafe.Pointer // full GB of memory for dag
+}
+
+type ParamsAndCache struct {
+ params *C.ethash_params
+ cache *C.ethash_cache
+ SeedBlockNum uint64
+}
+
+type Ethash struct {
+ turbo bool
+ HashRate int64
+ chainManager pow.ChainManager
+ dag *DAG
+ paramsAndCache *ParamsAndCache
+ nextdag unsafe.Pointer
+ ret *C.ethash_return_value
+ dagMutex *sync.Mutex
+ cacheMutex *sync.Mutex
+}
+
+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
+
+func getSeedBlockNum(blockNum uint64) uint64 {
+ var seedBlockNum uint64 = 0
+ if blockNum >= 2*epochLength {
+ seedBlockNum = ((blockNum / epochLength) - 1) * epochLength
+ }
+ return seedBlockNum
+}
+
+func makeParamsAndCache(chainManager pow.ChainManager, blockNum uint64) *ParamsAndCache {
+ seedBlockNum := getSeedBlockNum(blockNum)
+ paramsAndCache := &ParamsAndCache{
+ params: new(C.ethash_params),
+ cache: new(C.ethash_cache),
+ SeedBlockNum: seedBlockNum,
+ }
+ C.ethash_params_init(paramsAndCache.params, C.uint32_t(seedBlockNum))
+ paramsAndCache.cache.mem = C.malloc(paramsAndCache.params.cache_size)
+ seedHash := chainManager.GetBlockByNumber(seedBlockNum).Header().Hash()
+ log.Println("Params", paramsAndCache.params)
+
+ log.Println("Making Cache")
+ start := time.Now()
+ C.ethash_mkcache(paramsAndCache.cache, paramsAndCache.params, (*C.uint8_t)((unsafe.Pointer)(&seedHash[0])))
+ log.Println("Took:", time.Since(start))
+ return paramsAndCache
+}
+
+func (pow *Ethash) updateCache() {
+ pow.cacheMutex.Lock()
+ seedNum := getSeedBlockNum(pow.chainManager.CurrentBlock().NumberU64())
+ if pow.paramsAndCache.SeedBlockNum != seedNum {
+ pow.paramsAndCache = makeParamsAndCache(pow.chainManager, pow.chainManager.CurrentBlock().NumberU64())
+ }
+ pow.cacheMutex.Unlock()
+}
+
+func makeDAG(p *ParamsAndCache) *DAG {
+ d := &DAG{
+ dag: C.malloc(p.params.full_size),
+ SeedBlockNum: p.SeedBlockNum,
+ }
+ C.ethash_compute_full_data(d.dag, p.params, p.cache)
+ return d
+}
+
+func (pow *Ethash) updateDAG() {
+ pow.cacheMutex.Lock()
+ pow.dagMutex.Lock()
+
+ seedNum := getSeedBlockNum(pow.chainManager.CurrentBlock().NumberU64())
+ if pow.dag == nil || pow.dag.SeedBlockNum != seedNum {
+ pow.dag = nil
+ log.Println("Making Dag")
+ start := time.Now()
+ pow.dag = makeDAG(pow.paramsAndCache)
+ log.Println("Took:", time.Since(start))
+ }
+
+ pow.dagMutex.Unlock()
+ pow.cacheMutex.Unlock()
+}
+
+func New(chainManager pow.ChainManager) *Ethash {
+ return &Ethash{
+ turbo: false,
+ paramsAndCache: makeParamsAndCache(chainManager, chainManager.CurrentBlock().NumberU64()),
+ chainManager: chainManager,
+ dag: nil,
+ ret: new(C.ethash_return_value),
+ cacheMutex: new(sync.Mutex),
+ dagMutex: new(sync.Mutex),
+ }
+}
+
+func (pow *Ethash) DAGSize() uint64 {
+ return uint64(pow.paramsAndCache.params.full_size)
+}
+
+func (pow *Ethash) CacheSize() uint64 {
+ return uint64(pow.paramsAndCache.params.cache_size)
+}
+
+func (pow *Ethash) GetSeedHash(blockNum uint64) []byte {
+ return pow.chainManager.GetBlockByNumber(getSeedBlockNum(blockNum)).Header().Hash()
+}
+
+func (pow *Ethash) Stop() {
+ pow.cacheMutex.Lock()
+ pow.dagMutex.Lock()
+ if pow.paramsAndCache.cache != nil {
+ C.free(pow.paramsAndCache.cache.mem)
+ }
+ if pow.dag != nil {
+ C.free(pow.dag.dag)
+ }
+ pow.dagMutex.Unlock()
+ pow.cacheMutex.Unlock()
+}
+
+func (pow *Ethash) Search(block pow.Block, stop <-chan struct{}) ([]byte, []byte, []byte) {
+ pow.updateDAG()
+
+ // Not very elegant, multiple mining instances are not supported
+ pow.dagMutex.Lock()
+ pow.cacheMutex.Lock()
+ defer pow.cacheMutex.Unlock()
+ defer pow.dagMutex.Unlock()
+
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ miningHash := block.HashNoNonce()
+ diff := block.Difficulty()
+ log.Println("difficulty", diff)
+ i := int64(0)
+ start := time.Now().UnixNano()
+ t := time.Now()
+
+ nonce := uint64(r.Int63())
+
+ for {
+ select {
+ case <-stop:
+ powlogger.Infoln("Breaking from mining")
+ pow.HashRate = 0
+ pow.dagMutex.Unlock()
+ return nil, nil, nil
+ default:
+ i++
+
+ if time.Since(t) > (1 * time.Second) {
+ elapsed := time.Now().UnixNano() - start
+ hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
+ pow.HashRate = int64(hashes)
+ powlogger.Infoln("Hashing @", pow.HashRate, "khash")
+
+ t = time.Now()
+ }
+
+ cMiningHash := (*C.uint8_t)(unsafe.Pointer(&miningHash))
+ cnonce := C.uint64_t(nonce)
+ log.Printf("seed hash, nonce: %x %x\n", miningHash, nonce)
+ // pow.hash is the output/return of ethash_full
+ C.ethash_full(pow.ret, pow.dag.dag, pow.paramsAndCache.params, cMiningHash, cnonce)
+ res := C.ethash_check_difficulty((*C.uint8_t)(&pow.ret.result[0]), (*C.uint8_t)(unsafe.Pointer(&diff.Bytes()[0])))
+ if res == 1 {
+ mixDigest := C.GoBytes(unsafe.Pointer(&pow.ret.mix_hash[0]), 32)
+ // We don't really nead 32 bytes here
+ buf := make([]byte, 32)
+ binary.PutUvarint(buf, nonce)
+ return buf, mixDigest, pow.GetSeedHash(block.NumberU64())
+ }
+ nonce += 1
+ }
+
+ if !pow.turbo {
+ time.Sleep(20 * time.Microsecond)
+ }
+ }
+}
+
+func (pow *Ethash) Verify(block pow.Block) bool {
+ // Make sure the SeedHash is set correctly
+ if bytes.Compare(block.SeedHash(), pow.GetSeedHash(block.NumberU64())) != 0 {
+ log.Println("Block had wrong SeedHash")
+ log.Println("Expected: ", pow.GetSeedHash(block.NumberU64()))
+ log.Println("Actual: ", block.SeedHash())
+ return false
+ }
+
+ nonceInt, err := parseNonce(block.Nonce())
+ if err != nil {
+ log.Println("nonce to int err:", err)
+ return false
+ }
+ return pow.verify(block.HashNoNonce(), block.MixDigest(), block.Difficulty(), block.NumberU64(), nonceInt)
+}
+
+func (pow *Ethash) verify(hash []byte, mixDigest []byte, difficulty *big.Int, blockNum uint64, nonce uint64) bool {
+ // First check: make sure header, mixDigest, nonce are correct without hitting the DAG
+ // This is to prevent DOS attacks
+ chash := (*C.uint8_t)(unsafe.Pointer(&hash))
+ cnonce := C.uint64_t(nonce)
+ cmixDigest := (*C.uint8_t)(unsafe.Pointer(&mixDigest))
+ cdifficulty := (*C.uint8_t)(unsafe.Pointer(&difficulty.Bytes()[0]))
+ if C.ethash_quick_check_difficulty(chash, cnonce, cmixDigest, cdifficulty) != 1 {
+ log.Println("Failed to pass quick check. Are you sure that the mix digest is correct?")
+ return false
+ }
+
+ 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.
+ // If current, updateCache will do nothing.
+ if getSeedBlockNum(blockNum) < pow.paramsAndCache.SeedBlockNum {
+ pAc = makeParamsAndCache(pow.chainManager, blockNum)
+ } else {
+ pow.updateCache()
+ pow.cacheMutex.Lock()
+ defer pow.cacheMutex.Unlock()
+ pAc = pow.paramsAndCache
+ }
+
+ C.ethash_light(pow.ret, pAc.cache, pAc.params, chash, cnonce)
+ res := C.ethash_check_difficulty((*C.uint8_t)(unsafe.Pointer(&pow.ret.result[0])), cdifficulty)
+ return res == 1
+}
+
+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 {
+ pow.updateDAG()
+ pow.dagMutex.Lock()
+ defer pow.dagMutex.Unlock()
+ cMiningHash := (*C.uint8_t)(unsafe.Pointer(&miningHash))
+ cnonce := C.uint64_t(nonce)
+ log.Println("seed hash, nonce:", miningHash, nonce)
+ // pow.hash is the output/return of ethash_full
+ C.ethash_full(pow.ret, pow.dag.dag, pow.paramsAndCache.params, cMiningHash, cnonce)
+ ghash_full := C.GoBytes(unsafe.Pointer(&pow.ret.result[0]), 32)
+ return ghash_full
+}
+
+func (pow *Ethash) LightHash(nonce uint64, miningHash []byte) []byte {
+ cMiningHash := (*C.uint8_t)(unsafe.Pointer(&miningHash))
+ cnonce := C.uint64_t(nonce)
+ C.ethash_light(pow.ret, pow.paramsAndCache.cache, pow.paramsAndCache.params, cMiningHash, cnonce)
+ ghash_light := C.GoBytes(unsafe.Pointer(&pow.ret.result[0]), 32)
+ return ghash_light
+}