diff options
author | Gustav Simonsson <gustav.simonsson@gmail.com> | 2015-06-12 13:45:23 +0800 |
---|---|---|
committer | Gustav Simonsson <gustav.simonsson@gmail.com> | 2015-10-07 19:19:30 +0800 |
commit | ec6a548ee3555813d83f86f82bd25694bfd9c303 (patch) | |
tree | 9d0ec5022dc952f7b1053b85382df07347bc48f0 /Godeps/_workspace/src/github.com/ethereum | |
parent | 8b865fa9bf75e728d5d76f5a1460e0c37d8b5f9e (diff) | |
download | go-tangerine-ec6a548ee3555813d83f86f82bd25694bfd9c303.tar.gz go-tangerine-ec6a548ee3555813d83f86f82bd25694bfd9c303.tar.zst go-tangerine-ec6a548ee3555813d83f86f82bd25694bfd9c303.zip |
all: Add GPU mining, disabled by default
Diffstat (limited to 'Godeps/_workspace/src/github.com/ethereum')
4 files changed, 1246 insertions, 13 deletions
diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go index d0864da7f..ddb8ba583 100644 --- a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go +++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash.go @@ -30,8 +30,8 @@ import ( ) var ( - minDifficulty = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) - sharedLight = new(Light) + maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) + sharedLight = new(Light) ) const ( @@ -140,7 +140,7 @@ func (l *Light) Verify(block pow.Block) bool { // the finalizer before the call completes. _ = cache // The actual check. - target := new(big.Int).Div(minDifficulty, difficulty) + target := new(big.Int).Div(maxUint256, difficulty) return h256ToHash(ret.result).Big().Cmp(target) <= 0 } @@ -199,7 +199,7 @@ func (d *dag) generate() { if d.dir == "" { d.dir = DefaultDir } - glog.V(logger.Info).Infof("Generating DAG for epoch %d (%x)", d.epoch, seedHash) + glog.V(logger.Info).Infof("Generating DAG for epoch %d (size %d) (%x)", d.epoch, dagSize, 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]))) @@ -220,14 +220,18 @@ func (d *dag) generate() { }) } -func freeDAG(h *dag) { - C.ethash_full_delete(h.ptr) - h.ptr = nil +func freeDAG(d *dag) { + C.ethash_full_delete(d.ptr) + d.ptr = nil +} + +func (d *dag) Ptr() unsafe.Pointer { + return unsafe.Pointer(d.ptr.data) } //export ethashGoCallback func ethashGoCallback(percent C.unsigned) C.int { - glog.V(logger.Info).Infof("Still generating DAG: %d%%", percent) + glog.V(logger.Info).Infof("Generating DAG: %d%%", percent) return 0 } @@ -273,7 +277,7 @@ func (pow *Full) getDAG(blockNum uint64) (d *dag) { return d } -func (pow *Full) Search(block pow.Block, stop <-chan struct{}) (nonce uint64, mixDigest []byte) { +func (pow *Full) Search(block pow.Block, stop <-chan struct{}, index int) (nonce uint64, mixDigest []byte) { dag := pow.getDAG(block.NumberU64()) r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -286,7 +290,7 @@ func (pow *Full) Search(block pow.Block, stop <-chan struct{}) (nonce uint64, mi nonce = uint64(r.Int63()) hash := hashToH256(block.HashNoNonce()) - target := new(big.Int).Div(minDifficulty, diff) + target := new(big.Int).Div(maxUint256, diff) for { select { case <-stop: diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl.go new file mode 100644 index 000000000..332b7f524 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl.go @@ -0,0 +1,629 @@ +// Copyright 2014 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 opencl + +package ethash + +//#cgo LDFLAGS: -w +//#include <stdint.h> +//#include <string.h> +//#include "src/libethash/internal.h" +import "C" + +import ( + crand "crypto/rand" + "encoding/binary" + "fmt" + "math" + "math/big" + mrand "math/rand" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/Gustav-Simonsson/go-opencl/cl" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/pow" +) + +/* + + This code have two main entry points: + + 1. The initCL(...) function configures one or more OpenCL device + (for now only GPU) and loads the Ethash DAG onto device memory + + 2. The Search(...) function loads a Ethash nonce into device(s) memory and + executes the Ethash OpenCL kernel. + + Throughout the code, we refer to "host memory" and "device memory". + For most systems (e.g. regular PC GPU miner) the host memory is RAM and + device memory is the GPU global memory (e.g. GDDR5). + + References mentioned in code comments: + + 1. https://github.com/ethereum/wiki/wiki/Ethash + 2. https://github.com/ethereum/cpp-ethereum/blob/develop/libethash-cl/ethash_cl_miner.cpp + 3. https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/ + 4. http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_OpenCL_Programming_User_Guide.pdf + +*/ + +type OpenCLDevice struct { + deviceId int + device *cl.Device + openCL11 bool // OpenCL version 1.1 and 1.2 are handled a bit different + openCL12 bool + + dagBuf *cl.MemObject // Ethash full DAG in device mem + headerBuf *cl.MemObject // Hash of block-to-mine in device mem + searchBuffers []*cl.MemObject + + searchKernel *cl.Kernel + hashKernel *cl.Kernel + + queue *cl.CommandQueue + ctx *cl.Context + workGroupSize int + + nonceRand *mrand.Rand // seeded by crypto/rand, see comments where it's initialised + result common.Hash +} + +type OpenCLMiner struct { + mu sync.Mutex + + ethash *Ethash // Ethash full DAG & cache in host mem + + deviceIds []int + devices []*OpenCLDevice + + dagSize uint64 + + hashRate int32 // Go atomics & uint64 have some issues; int32 is supported on all platforms +} + +type pendingSearch struct { + bufIndex uint32 + startNonce uint64 +} + +const ( + SIZEOF_UINT32 = 4 + + // See [1] + ethashMixBytesLen = 128 + ethashAccesses = 64 + + // See [4] + workGroupSize = 32 // must be multiple of 8 + maxSearchResults = 63 + searchBufSize = 2 + globalWorkSize = 1024 * 256 +) + +func NewCL(deviceIds []int) *OpenCLMiner { + ids := make([]int, len(deviceIds)) + copy(ids, deviceIds) + return &OpenCLMiner{ + ethash: New(), + dagSize: 0, // to see if we need to update DAG. + deviceIds: ids, + } +} + +func PrintDevices() { + fmt.Println("=============================================") + fmt.Println("============ OpenCL Device Info =============") + fmt.Println("=============================================") + + var found []*cl.Device + + platforms, err := cl.GetPlatforms() + if err != nil { + fmt.Println("Plaform error (check your OpenCL installation): %v", err) + return + } + + for i, p := range platforms { + fmt.Println("Platform id ", i) + fmt.Println("Platform Name ", p.Name()) + fmt.Println("Platform Vendor ", p.Vendor()) + fmt.Println("Platform Version ", p.Version()) + fmt.Println("Platform Extensions ", p.Extensions()) + fmt.Println("Platform Profile ", p.Profile()) + fmt.Println("") + + devices, err := cl.GetDevices(p, cl.DeviceTypeGPU) + if err != nil { + fmt.Println("Device error (check your GPU drivers) :", err) + return + } + + for _, d := range devices { + fmt.Println("Device OpenCL id ", i) + fmt.Println("Device id for mining ", len(found)) + fmt.Println("Device Name ", d.Name()) + fmt.Println("Vendor ", d.Vendor()) + fmt.Println("Version ", d.Version()) + fmt.Println("Driver version ", d.DriverVersion()) + fmt.Println("Address bits ", d.AddressBits()) + fmt.Println("Max clock freq ", d.MaxClockFrequency()) + fmt.Println("Global mem size ", d.GlobalMemSize()) + fmt.Println("Max constant buffer size", d.MaxConstantBufferSize()) + fmt.Println("Max mem alloc size ", d.MaxMemAllocSize()) + fmt.Println("Max compute units ", d.MaxComputeUnits()) + fmt.Println("Max work group size ", d.MaxWorkGroupSize()) + fmt.Println("Max work item sizes ", d.MaxWorkItemSizes()) + fmt.Println("=============================================") + + found = append(found, d) + } + } + if len(found) == 0 { + fmt.Println("Found no GPU(s). Check that your OS can see the GPU(s)") + } else { + var idsFormat string + for i := 0; i < len(found); i++ { + idsFormat += strconv.Itoa(i) + if i != len(found)-1 { + idsFormat += "," + } + } + fmt.Printf("Found %v devices. Benchmark first GPU: geth gpubench 0\n", len(found)) + fmt.Printf("Mine using all GPUs: geth --minegpu %v\n", idsFormat) + } +} + +// See [2]. We basically do the same here, but the Go OpenCL bindings +// are at a slightly higher abtraction level. +func InitCL(blockNum uint64, c *OpenCLMiner) error { + platforms, err := cl.GetPlatforms() + if err != nil { + return fmt.Errorf("Plaform error: %v\nCheck your OpenCL installation and then run geth gpuinfo", err) + } + + var devices []*cl.Device + for _, p := range platforms { + ds, err := cl.GetDevices(p, cl.DeviceTypeGPU) + if err != nil { + return fmt.Errorf("Devices error: %v\nCheck your GPU drivers and then run geth gpuinfo", err) + } + for _, d := range ds { + devices = append(devices, d) + } + } + + pow := New() + _ = pow.getDAG(blockNum) // generates DAG if we don't have it + pow.Light.getCache(blockNum) // and cache + + c.ethash = pow + dagSize := uint64(C.ethash_get_datasize(C.uint64_t(blockNum))) + c.dagSize = dagSize + + for _, id := range c.deviceIds { + if id > len(devices)-1 { + return fmt.Errorf("Device id not found. See available device ids with: geth gpuinfo") + } else { + err := initCLDevice(id, devices[id], c) + if err != nil { + return err + } + } + } + if len(c.devices) == 0 { + return fmt.Errorf("No GPU devices found") + } + return nil +} + +func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { + devMaxAlloc := uint64(device.MaxMemAllocSize()) + devGlobalMem := uint64(device.GlobalMemSize()) + + // TODO: more fine grained version logic + if device.Version() == "OpenCL 1.0" { + fmt.Println("Device OpenCL version not supported: ", device.Version()) + return fmt.Errorf("opencl version not supported") + } + + var cl11, cl12 bool + if device.Version() == "OpenCL 1.1" { + cl11 = true + } + if device.Version() == "OpenCL 1.2" { + cl12 = true + } + + // log warnings but carry on; some device drivers report inaccurate values + if c.dagSize > devGlobalMem { + fmt.Printf("WARNING: device memory may be insufficient: %v. DAG size: %v.\n", devGlobalMem, c.dagSize) + } + + if c.dagSize > devMaxAlloc { + fmt.Printf("WARNING: DAG size (%v) larger than device max memory allocation size (%v).\n", c.dagSize, devMaxAlloc) + fmt.Printf("You probably have to export GPU_MAX_ALLOC_PERCENT=95\n") + } + + fmt.Printf("Initialising device %v: %v\n", deviceId, device.Name()) + + context, err := cl.CreateContext([]*cl.Device{device}) + if err != nil { + return fmt.Errorf("failed creating context:", err) + } + + // TODO: test running with CL_QUEUE_PROFILING_ENABLE for profiling? + queue, err := context.CreateCommandQueue(device, 0) + if err != nil { + return fmt.Errorf("command queue err:", err) + } + + // See [4] section 3.2 and [3] "clBuildProgram". + // The OpenCL kernel code is compiled at run-time. + kvs := make(map[string]string, 4) + kvs["GROUP_SIZE"] = strconv.FormatUint(workGroupSize, 10) + kvs["DAG_SIZE"] = strconv.FormatUint(c.dagSize/ethashMixBytesLen, 10) + kvs["ACCESSES"] = strconv.FormatUint(ethashAccesses, 10) + kvs["MAX_OUTPUTS"] = strconv.FormatUint(maxSearchResults, 10) + kernelCode := replaceWords(kernel, kvs) + + program, err := context.CreateProgramWithSource([]string{kernelCode}) + if err != nil { + return fmt.Errorf("program err:", err) + } + + /* if using AMD OpenCL impl, you can set this to debug on x86 CPU device. + see AMD OpenCL programming guide section 4.2 + + export in shell before running: + export AMD_OCL_BUILD_OPTIONS_APPEND="-g -O0" + export CPU_MAX_COMPUTE_UNITS=1 + + buildOpts := "-g -cl-opt-disable" + + */ + buildOpts := "" + err = program.BuildProgram([]*cl.Device{device}, buildOpts) + if err != nil { + return fmt.Errorf("program build err:", err) + } + + var searchKernelName, hashKernelName string + searchKernelName = "ethash_search" + hashKernelName = "ethash_hash" + + searchKernel, err := program.CreateKernel(searchKernelName) + hashKernel, err := program.CreateKernel(hashKernelName) + if err != nil { + return fmt.Errorf("kernel err:", err) + } + + // TODO: when this DAG size appears, patch the Go bindings + // (context.go) to work with uint64 as size_t + if c.dagSize > math.MaxInt32 { + fmt.Println("DAG too large for allocation.") + return fmt.Errorf("DAG too large for alloc") + } + + // TODO: patch up Go bindings to work with size_t, will overflow if > maxint32 + // TODO: fuck. shit's gonna overflow around 2017-06-09 12:17:02 + dagBuf := *(new(*cl.MemObject)) + dagBuf, err = context.CreateEmptyBuffer(cl.MemReadOnly, int(c.dagSize)) + if err != nil { + return fmt.Errorf("allocating dag buf failed: ", err) + } + + // write DAG to device mem + dagPtr := unsafe.Pointer(c.ethash.Full.current.ptr.data) + _, err = queue.EnqueueWriteBuffer(dagBuf, true, 0, int(c.dagSize), dagPtr, nil) + if err != nil { + return fmt.Errorf("writing to dag buf failed: ", err) + } + + searchBuffers := make([]*cl.MemObject, searchBufSize) + for i := 0; i < searchBufSize; i++ { + searchBuff, err := context.CreateEmptyBuffer(cl.MemWriteOnly, (1+maxSearchResults)*SIZEOF_UINT32) + if err != nil { + return fmt.Errorf("search buffer err:", err) + } + searchBuffers[i] = searchBuff + } + + headerBuf, err := context.CreateEmptyBuffer(cl.MemReadOnly, 32) + if err != nil { + return fmt.Errorf("header buffer err:", err) + } + + // Unique, random nonces are crucial for mining efficieny. + // While we do not need cryptographically secure PRNG for nonces, + // we want to have uniform distribution and minimal repetition of nonces. + // We could guarantee strict uniqueness of nonces by generating unique ranges, + // but a int64 seed from crypto/rand should be good enough. + // we then use math/rand for speed and to avoid draining OS entropy pool + seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + return err + } + nonceRand := mrand.New(mrand.NewSource(seed.Int64())) + + deviceStruct := &OpenCLDevice{ + deviceId: deviceId, + device: device, + openCL11: cl11, + openCL12: cl12, + + dagBuf: dagBuf, + headerBuf: headerBuf, + searchBuffers: searchBuffers, + + searchKernel: searchKernel, + hashKernel: hashKernel, + + queue: queue, + ctx: context, + + workGroupSize: workGroupSize, + + nonceRand: nonceRand, + } + c.devices = append(c.devices, deviceStruct) + + return nil +} + +func (c *OpenCLMiner) Search(block pow.Block, stop <-chan struct{}, index int) (uint64, []byte) { + c.mu.Lock() + newDagSize := uint64(C.ethash_get_datasize(C.uint64_t(block.NumberU64()))) + if newDagSize > c.dagSize { + // TODO: clean up buffers from previous DAG? + err := InitCL(block.NumberU64(), c) + if err != nil { + fmt.Println("OpenCL init error: ", err) + return 0, []byte{0} + } + } + defer c.mu.Unlock() + + // Avoid unneeded OpenCL initialisation if we received stop while running InitCL + select { + case <-stop: + return 0, []byte{0} + default: + } + + headerHash := block.HashNoNonce() + diff := block.Difficulty() + target256 := new(big.Int).Div(maxUint256, diff) + target64 := new(big.Int).Rsh(target256, 192).Uint64() + var zero uint32 = 0 + + d := c.devices[index] + + _, err := d.queue.EnqueueWriteBuffer(d.headerBuf, false, 0, 32, unsafe.Pointer(&headerHash[0]), nil) + if err != nil { + fmt.Println("Error in Search clEnqueueWriterBuffer : ", err) + return 0, []byte{0} + } + + for i := 0; i < searchBufSize; i++ { + _, err := d.queue.EnqueueWriteBuffer(d.searchBuffers[i], false, 0, 4, unsafe.Pointer(&zero), nil) + if err != nil { + fmt.Println("Error in Search clEnqueueWriterBuffer : ", err) + return 0, []byte{0} + } + } + + // wait for all search buffers to complete + err = d.queue.Finish() + if err != nil { + fmt.Println("Error in Search clFinish : ", err) + return 0, []byte{0} + } + + err = d.searchKernel.SetArg(1, d.headerBuf) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + + err = d.searchKernel.SetArg(2, d.dagBuf) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + + err = d.searchKernel.SetArg(4, target64) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + err = d.searchKernel.SetArg(5, uint32(math.MaxUint32)) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + + // wait on this before returning + var preReturnEvent *cl.Event + if d.openCL12 { + preReturnEvent, err = d.ctx.CreateUserEvent() + if err != nil { + fmt.Println("Error in Search create CL user event : ", err) + return 0, []byte{0} + } + } + + pending := make([]pendingSearch, 0, searchBufSize) + var p *pendingSearch + searchBufIndex := uint32(0) + var checkNonce uint64 + loops := int64(0) + prevHashRate := int32(0) + start := time.Now().UnixNano() + // we grab a single random nonce and sets this as argument to the kernel search function + // the device will then add each local threads gid to the nonce, creating a unique nonce + // for each device computing unit executing in parallel + initNonce := uint64(d.nonceRand.Int63()) + for nonce := initNonce; ; nonce += uint64(globalWorkSize) { + select { + case <-stop: + + /* + if d.openCL12 { + err = cl.WaitForEvents([]*cl.Event{preReturnEvent}) + if err != nil { + fmt.Println("Error in Search WaitForEvents: ", err) + } + } + */ + + atomic.AddInt32(&c.hashRate, -prevHashRate) + return 0, []byte{0} + default: + } + + if (loops % (1 << 7)) == 0 { + elapsed := time.Now().UnixNano() - start + // TODO: verify if this is correct hash rate calculation + hashes := (float64(1e9) / float64(elapsed)) * float64(loops*1024*256) + hashrateDiff := int32(hashes) - prevHashRate + prevHashRate = int32(hashes) + atomic.AddInt32(&c.hashRate, hashrateDiff) + } + loops++ + + err = d.searchKernel.SetArg(0, d.searchBuffers[searchBufIndex]) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + err = d.searchKernel.SetArg(3, nonce) + if err != nil { + fmt.Println("Error in Search clSetKernelArg : ", err) + return 0, []byte{0} + } + + // execute kernel + _, err := d.queue.EnqueueNDRangeKernel( + d.searchKernel, + []int{0}, + []int{globalWorkSize}, + []int{d.workGroupSize}, + nil) + if err != nil { + fmt.Println("Error in Search clEnqueueNDRangeKernel : ", err) + return 0, []byte{0} + } + + pending = append(pending, pendingSearch{bufIndex: searchBufIndex, startNonce: nonce}) + searchBufIndex = (searchBufIndex + 1) % searchBufSize + + if len(pending) == searchBufSize { + p = &(pending[searchBufIndex]) + cres, _, err := d.queue.EnqueueMapBuffer(d.searchBuffers[p.bufIndex], true, + cl.MapFlagRead, 0, (1+maxSearchResults)*SIZEOF_UINT32, + nil) + if err != nil { + fmt.Println("Error in Search clEnqueueMapBuffer: ", err) + return 0, []byte{0} + } + + results := cres.ByteSlice() + nfound := binary.LittleEndian.Uint32(results) + nfound = uint32(math.Min(float64(nfound), float64(maxSearchResults))) + // OpenCL returns the offsets from the start nonce + for i := uint32(0); i < nfound; i++ { + lo := (i + 1) * SIZEOF_UINT32 + hi := (i + 2) * SIZEOF_UINT32 + upperNonce := uint64(binary.LittleEndian.Uint32(results[lo:hi])) + checkNonce = p.startNonce + upperNonce + if checkNonce != 0 { + cn := C.uint64_t(checkNonce) + ds := C.uint64_t(c.dagSize) + // We verify that the nonce is indeed a solution by + // executing the Ethash verification function (on the CPU). + ret := C.ethash_light_compute_internal(c.ethash.Light.current.ptr, ds, hashToH256(headerHash), cn) + // TODO: return result first + if ret.success && h256ToHash(ret.result).Big().Cmp(target256) <= 0 { + _, err = d.queue.EnqueueUnmapMemObject(d.searchBuffers[p.bufIndex], cres, nil) + if err != nil { + fmt.Println("Error in Search clEnqueueUnmapMemObject: ", err) + } + if d.openCL12 { + err = cl.WaitForEvents([]*cl.Event{preReturnEvent}) + if err != nil { + fmt.Println("Error in Search WaitForEvents: ", err) + } + } + return checkNonce, C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32)) + } + + _, err := d.queue.EnqueueWriteBuffer(d.searchBuffers[p.bufIndex], false, 0, 4, unsafe.Pointer(&zero), nil) + if err != nil { + fmt.Println("Error in Search cl: EnqueueWriteBuffer", err) + return 0, []byte{0} + } + } + } + _, err = d.queue.EnqueueUnmapMemObject(d.searchBuffers[p.bufIndex], cres, nil) + if err != nil { + fmt.Println("Error in Search clEnqueueUnMapMemObject: ", err) + return 0, []byte{0} + } + pending = append(pending[:searchBufIndex], pending[searchBufIndex+1:]...) + } + } + if d.openCL12 { + err := cl.WaitForEvents([]*cl.Event{preReturnEvent}) + if err != nil { + fmt.Println("Error in Search clWaitForEvents: ", err) + return 0, []byte{0} + } + } + return 0, []byte{0} +} + +func (c *OpenCLMiner) Verify(block pow.Block) bool { + return c.ethash.Light.Verify(block) +} +func (c *OpenCLMiner) GetHashrate() int64 { + return int64(atomic.LoadInt32(&c.hashRate)) +} +func (c *OpenCLMiner) Turbo(on bool) { + // This is GPU mining. Always be turbo. +} + +func replaceWords(text string, kvs map[string]string) string { + for k, v := range kvs { + text = strings.Replace(text, k, v, -1) + } + return text +} + +func logErr(err error) { + if err != nil { + fmt.Println("Error in OpenCL call:", err) + } +} + +func argErr(err error) error { + return fmt.Errorf("arg err: %v", err) +} diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl_kernel_go_str.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl_kernel_go_str.go new file mode 100644 index 000000000..695ff1829 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_opencl_kernel_go_str.go @@ -0,0 +1,600 @@ +package ethash + +/* DO NOT EDIT!!! + + This code is version controlled at + https://github.com/ethereum/cpp-ethereum/blob/develop/libethash-cl/ethash_cl_miner_kernel.cl + + If needed change it there first, then copy over here. +*/ + +const kernel = ` +// author Tim Hughes <tim@twistedfury.com> +// Tested on Radeon HD 7850 +// Hashrate: 15940347 hashes/s +// Bandwidth: 124533 MB/s +// search kernel should fit in <= 84 VGPRS (3 wavefronts) + +#define THREADS_PER_HASH (128 / 16) +#define HASHES_PER_LOOP (GROUP_SIZE / THREADS_PER_HASH) + +#define FNV_PRIME 0x01000193 + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +void keccak_f1600_round(uint2* a, uint r, uint out_size) +{ + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif + + uint2 b[25]; + uint2 t; + + // Theta + b[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + b[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + b[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + b[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + b[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + t = b[4] ^ (uint2)(b[1].x << 1 | b[1].y >> 31, b[1].y << 1 | b[1].x >> 31); + a[0] ^= t; + a[5] ^= t; + a[10] ^= t; + a[15] ^= t; + a[20] ^= t; + t = b[0] ^ (uint2)(b[2].x << 1 | b[2].y >> 31, b[2].y << 1 | b[2].x >> 31); + a[1] ^= t; + a[6] ^= t; + a[11] ^= t; + a[16] ^= t; + a[21] ^= t; + t = b[1] ^ (uint2)(b[3].x << 1 | b[3].y >> 31, b[3].y << 1 | b[3].x >> 31); + a[2] ^= t; + a[7] ^= t; + a[12] ^= t; + a[17] ^= t; + a[22] ^= t; + t = b[2] ^ (uint2)(b[4].x << 1 | b[4].y >> 31, b[4].y << 1 | b[4].x >> 31); + a[3] ^= t; + a[8] ^= t; + a[13] ^= t; + a[18] ^= t; + a[23] ^= t; + t = b[3] ^ (uint2)(b[0].x << 1 | b[0].y >> 31, b[0].y << 1 | b[0].x >> 31); + a[4] ^= t; + a[9] ^= t; + a[14] ^= t; + a[19] ^= t; + a[24] ^= t; + + // Rho Pi + b[0] = a[0]; + b[10] = (uint2)(a[1].x << 1 | a[1].y >> 31, a[1].y << 1 | a[1].x >> 31); + b[7] = (uint2)(a[10].x << 3 | a[10].y >> 29, a[10].y << 3 | a[10].x >> 29); + b[11] = (uint2)(a[7].x << 6 | a[7].y >> 26, a[7].y << 6 | a[7].x >> 26); + b[17] = (uint2)(a[11].x << 10 | a[11].y >> 22, a[11].y << 10 | a[11].x >> 22); + b[18] = (uint2)(a[17].x << 15 | a[17].y >> 17, a[17].y << 15 | a[17].x >> 17); + b[3] = (uint2)(a[18].x << 21 | a[18].y >> 11, a[18].y << 21 | a[18].x >> 11); + b[5] = (uint2)(a[3].x << 28 | a[3].y >> 4, a[3].y << 28 | a[3].x >> 4); + b[16] = (uint2)(a[5].y << 4 | a[5].x >> 28, a[5].x << 4 | a[5].y >> 28); + b[8] = (uint2)(a[16].y << 13 | a[16].x >> 19, a[16].x << 13 | a[16].y >> 19); + b[21] = (uint2)(a[8].y << 23 | a[8].x >> 9, a[8].x << 23 | a[8].y >> 9); + b[24] = (uint2)(a[21].x << 2 | a[21].y >> 30, a[21].y << 2 | a[21].x >> 30); + b[4] = (uint2)(a[24].x << 14 | a[24].y >> 18, a[24].y << 14 | a[24].x >> 18); + b[15] = (uint2)(a[4].x << 27 | a[4].y >> 5, a[4].y << 27 | a[4].x >> 5); + b[23] = (uint2)(a[15].y << 9 | a[15].x >> 23, a[15].x << 9 | a[15].y >> 23); + b[19] = (uint2)(a[23].y << 24 | a[23].x >> 8, a[23].x << 24 | a[23].y >> 8); + b[13] = (uint2)(a[19].x << 8 | a[19].y >> 24, a[19].y << 8 | a[19].x >> 24); + b[12] = (uint2)(a[13].x << 25 | a[13].y >> 7, a[13].y << 25 | a[13].x >> 7); + b[2] = (uint2)(a[12].y << 11 | a[12].x >> 21, a[12].x << 11 | a[12].y >> 21); + b[20] = (uint2)(a[2].y << 30 | a[2].x >> 2, a[2].x << 30 | a[2].y >> 2); + b[14] = (uint2)(a[20].x << 18 | a[20].y >> 14, a[20].y << 18 | a[20].x >> 14); + b[22] = (uint2)(a[14].y << 7 | a[14].x >> 25, a[14].x << 7 | a[14].y >> 25); + b[9] = (uint2)(a[22].y << 29 | a[22].x >> 3, a[22].x << 29 | a[22].y >> 3); + b[6] = (uint2)(a[9].x << 20 | a[9].y >> 12, a[9].y << 20 | a[9].x >> 12); + b[1] = (uint2)(a[6].y << 12 | a[6].x >> 20, a[6].x << 12 | a[6].y >> 20); + + // Chi + a[0] = bitselect(b[0] ^ b[2], b[0], b[1]); + a[1] = bitselect(b[1] ^ b[3], b[1], b[2]); + a[2] = bitselect(b[2] ^ b[4], b[2], b[3]); + a[3] = bitselect(b[3] ^ b[0], b[3], b[4]); + if (out_size >= 4) + { + a[4] = bitselect(b[4] ^ b[1], b[4], b[0]); + a[5] = bitselect(b[5] ^ b[7], b[5], b[6]); + a[6] = bitselect(b[6] ^ b[8], b[6], b[7]); + a[7] = bitselect(b[7] ^ b[9], b[7], b[8]); + a[8] = bitselect(b[8] ^ b[5], b[8], b[9]); + if (out_size >= 8) + { + a[9] = bitselect(b[9] ^ b[6], b[9], b[5]); + a[10] = bitselect(b[10] ^ b[12], b[10], b[11]); + a[11] = bitselect(b[11] ^ b[13], b[11], b[12]); + a[12] = bitselect(b[12] ^ b[14], b[12], b[13]); + a[13] = bitselect(b[13] ^ b[10], b[13], b[14]); + a[14] = bitselect(b[14] ^ b[11], b[14], b[10]); + a[15] = bitselect(b[15] ^ b[17], b[15], b[16]); + a[16] = bitselect(b[16] ^ b[18], b[16], b[17]); + a[17] = bitselect(b[17] ^ b[19], b[17], b[18]); + a[18] = bitselect(b[18] ^ b[15], b[18], b[19]); + a[19] = bitselect(b[19] ^ b[16], b[19], b[15]); + a[20] = bitselect(b[20] ^ b[22], b[20], b[21]); + a[21] = bitselect(b[21] ^ b[23], b[21], b[22]); + a[22] = bitselect(b[22] ^ b[24], b[22], b[23]); + a[23] = bitselect(b[23] ^ b[20], b[23], b[24]); + a[24] = bitselect(b[24] ^ b[21], b[24], b[20]); + } + } + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif +} + +void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) +{ + for (uint i = in_size; i != 25; ++i) + { + a[i] = 0; + } +#if __ENDIAN_LITTLE__ + a[in_size] ^= 0x0000000000000001; + a[24-out_size*2] ^= 0x8000000000000000; +#else + a[in_size] ^= 0x0100000000000000; + a[24-out_size*2] ^= 0x0000000000000080; +#endif + + // Originally I unrolled the first and last rounds to interface + // better with surrounding code, however I haven't done this + // without causing the AMD compiler to blow up the VGPR usage. + uint r = 0; + do + { + // This dynamic branch stops the AMD compiler unrolling the loop + // and additionally saves about 33% of the VGPRs, enough to gain another + // wavefront. Ideally we'd get 4 in flight, but 3 is the best I can + // massage out of the compiler. It doesn't really seem to matter how + // much we try and help the compiler save VGPRs because it seems to throw + // that information away, hence the implementation of keccak here + // doesn't bother. + if (isolate) + { + keccak_f1600_round((uint2*)a, r++, 25); + } + } + while (r < 23); + + // final round optimised for digest size + keccak_f1600_round((uint2*)a, r++, out_size); +} + +#define copy(dst, src, count) for (uint i = 0; i != count; ++i) { (dst)[i] = (src)[i]; } + +#define countof(x) (sizeof(x) / sizeof(x[0])) + +uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +uint fnv_reduce(uint4 v) +{ + return fnv(fnv(fnv(v.x, v.y), v.z), v.w); +} + +typedef union +{ + ulong ulongs[32 / sizeof(ulong)]; + uint uints[32 / sizeof(uint)]; +} hash32_t; + +typedef union +{ + ulong ulongs[64 / sizeof(ulong)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint uints[128 / sizeof(uint)]; + uint4 uint4s[128 / sizeof(uint4)]; +} hash128_t; + +hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) +{ + hash64_t init; + uint const init_size = countof(init.ulongs); + uint const hash_size = countof(header->ulongs); + + // sha3_512(header .. nonce) + ulong state[25]; + copy(state, header->ulongs, hash_size); + state[hash_size] = nonce; + keccak_f1600_no_absorb(state, hash_size + 1, init_size, isolate); + + copy(init.ulongs, state, init_size); + return init; +} + +uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate) +{ + uint4 mix = init; + + // share init0 + if (thread_id == 0) + *share = mix.x; + barrier(CLK_LOCAL_MEM_FENCE); + uint init0 = *share; + + uint a = 0; + do + { + bool update_share = thread_id == (a/4) % THREADS_PER_HASH; + + #pragma unroll + for (uint i = 0; i != 4; ++i) + { + if (update_share) + { + uint m[4] = { mix.x, mix.y, mix.z, mix.w }; + *share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE; + } + barrier(CLK_LOCAL_MEM_FENCE); + + mix = fnv4(mix, *share>=3 * DAG_SIZE / 4 ? g_dag3[*share - 3 * DAG_SIZE / 4].uint4s[thread_id] : *share>=DAG_SIZE / 2 ? g_dag2[*share - DAG_SIZE / 2].uint4s[thread_id] : *share>=DAG_SIZE / 4 ? g_dag1[*share - DAG_SIZE / 4].uint4s[thread_id]:g_dag[*share].uint4s[thread_id]); + } + } while ((a += 4) != (ACCESSES & isolate)); + + return fnv_reduce(mix); +} + + + +uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) +{ + uint4 mix = init; + + // share init0 + if (thread_id == 0) + *share = mix.x; + barrier(CLK_LOCAL_MEM_FENCE); + uint init0 = *share; + + uint a = 0; + do + { + bool update_share = thread_id == (a/4) % THREADS_PER_HASH; + + #pragma unroll + for (uint i = 0; i != 4; ++i) + { + if (update_share) + { + uint m[4] = { mix.x, mix.y, mix.z, mix.w }; + *share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE; + } + barrier(CLK_LOCAL_MEM_FENCE); + + mix = fnv4(mix, g_dag[*share].uint4s[thread_id]); + } + } + while ((a += 4) != (ACCESSES & isolate)); + + return fnv_reduce(mix); +} + + +hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) +{ + ulong state[25]; + + hash32_t hash; + uint const hash_size = countof(hash.ulongs); + uint const init_size = countof(init->ulongs); + uint const mix_size = countof(mix->ulongs); + + // keccak_256(keccak_512(header..nonce) .. mix); + copy(state, init->ulongs, init_size); + copy(state + init_size, mix->ulongs, mix_size); + keccak_f1600_no_absorb(state, init_size+mix_size, hash_size, isolate); + + // copy out + copy(hash.ulongs, state, hash_size); + return hash; +} + +hash32_t compute_hash_simple( + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + hash64_t init = init_hash(g_header, nonce, isolate); + + hash128_t mix; + for (uint i = 0; i != countof(mix.uint4s); ++i) + { + mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)]; + } + + uint mix_val = mix.uints[0]; + uint init0 = mix.uints[0]; + uint a = 0; + do + { + uint pi = fnv(init0 ^ a, mix_val) % DAG_SIZE; + uint n = (a+1) % countof(mix.uints); + + #pragma unroll + for (uint i = 0; i != countof(mix.uints); ++i) + { + mix.uints[i] = fnv(mix.uints[i], g_dag[pi].uints[i]); + mix_val = i == n ? mix.uints[i] : mix_val; + } + } + while (++a != (ACCESSES & isolate)); + + // reduce to output + hash32_t fnv_mix; + for (uint i = 0; i != countof(fnv_mix.uints); ++i) + { + fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]); + } + + return final_hash(&init, &fnv_mix, isolate); +} + +typedef union +{ + struct + { + hash64_t init; + uint pad; // avoid lds bank conflicts + }; + hash32_t mix; +} compute_hash_share; + + +hash32_t compute_hash( + __local compute_hash_share* share, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + + // Compute one init hash per work item. + hash64_t init = init_hash(g_header, nonce, isolate); + + // Threads work together in this phase in groups of 8. + uint const thread_id = gid % THREADS_PER_HASH; + uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH; + + hash32_t mix; + uint i = 0; + do + { + // share init with other threads + if (i == thread_id) + share[hash_id].init = init; + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))]; + barrier(CLK_LOCAL_MEM_FENCE); + + uint thread_mix = inner_loop(thread_init, thread_id, share[hash_id].mix.uints, g_dag, isolate); + + share[hash_id].mix.uints[thread_id] = thread_mix; + barrier(CLK_LOCAL_MEM_FENCE); + + if (i == thread_id) + mix = share[hash_id].mix; + barrier(CLK_LOCAL_MEM_FENCE); + } + while (++i != (THREADS_PER_HASH & isolate)); + + return final_hash(&init, &mix, isolate); +} + + +hash32_t compute_hash_chunks( + __local compute_hash_share* share, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + __global hash128_t const* g_dag1, + __global hash128_t const* g_dag2, + __global hash128_t const* g_dag3, + ulong nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + + // Compute one init hash per work item. + hash64_t init = init_hash(g_header, nonce, isolate); + + // Threads work together in this phase in groups of 8. + uint const thread_id = gid % THREADS_PER_HASH; + uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH; + + hash32_t mix; + uint i = 0; + do + { + // share init with other threads + if (i == thread_id) + share[hash_id].init = init; + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))]; + barrier(CLK_LOCAL_MEM_FENCE); + + uint thread_mix = inner_loop_chunks(thread_init, thread_id, share[hash_id].mix.uints, g_dag, g_dag1, g_dag2, g_dag3, isolate); + + share[hash_id].mix.uints[thread_id] = thread_mix; + barrier(CLK_LOCAL_MEM_FENCE); + + if (i == thread_id) + mix = share[hash_id].mix; + barrier(CLK_LOCAL_MEM_FENCE); + } + while (++i != (THREADS_PER_HASH & isolate)); + + return final_hash(&init, &mix, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash_simple( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search_simple( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + uint const gid = get_global_id(0); + hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); + + if (hash.ulongs[countof(hash.ulongs)-1] < target) + { + uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1)); + g_output[slot] = gid; + } +} + + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + hash32_t hash = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); + + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min((uint)MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); + g_output[slot] = gid; + } +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash_chunks( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + __global hash128_t const* g_dag1, + __global hash128_t const* g_dag2, + __global hash128_t const* g_dag3, + ulong start_nonce, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash_chunks(share, g_header, g_dag, g_dag1, g_dag2, g_dag3,start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search_chunks( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + __global hash128_t const* g_dag1, + __global hash128_t const* g_dag2, + __global hash128_t const* g_dag3, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + hash32_t hash = compute_hash_chunks(share, g_header, g_dag, g_dag1, g_dag2, g_dag3, start_nonce + gid, isolate); + + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1)); + g_output[slot] = gid; + } +} +` diff --git a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_test.go b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_test.go index 1e1de989d..c19e45d1d 100644 --- a/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_test.go +++ b/Godeps/_workspace/src/github.com/ethereum/ethash/ethash_test.go @@ -92,7 +92,7 @@ func TestEthashConcurrentVerify(t *testing.T) { defer os.RemoveAll(eth.Full.Dir) block := &testBlock{difficulty: big.NewInt(10)} - nonce, md := eth.Search(block, nil) + nonce, md := eth.Search(block, nil, 0) block.nonce = nonce block.mixDigest = common.BytesToHash(md) @@ -135,7 +135,7 @@ func TestEthashConcurrentSearch(t *testing.T) { // launch n searches concurrently. for i := 0; i < nsearch; i++ { go func() { - nonce, md := eth.Search(block, stop) + nonce, md := eth.Search(block, stop, 0) select { case found <- searchRes{n: nonce, md: md}: case <-stop: @@ -167,7 +167,7 @@ func TestEthashSearchAcrossEpoch(t *testing.T) { for i := epochLength - 40; i < epochLength+40; i++ { block := &testBlock{number: i, difficulty: big.NewInt(90)} rand.Read(block.hashNoNonce[:]) - nonce, md := eth.Search(block, nil) + nonce, md := eth.Search(block, nil, 0) block.nonce = nonce block.mixDigest = common.BytesToHash(md) if !eth.Verify(block) { |