From 66cc836d0cc103d641b70b6887af177a0aff9bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 22 Mar 2016 12:00:24 +0200 Subject: [PATCH 1/2] opencl: fix ethash light handling, fix errorf formats --- ethash_opencl.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ethash_opencl.go b/ethash_opencl.go index 332b7f52..6f6016d6 100644 --- a/ethash_opencl.go +++ b/ethash_opencl.go @@ -138,7 +138,7 @@ func PrintDevices() { platforms, err := cl.GetPlatforms() if err != nil { - fmt.Println("Plaform error (check your OpenCL installation): %v", err) + fmt.Println("Plaform error (check your OpenCL installation):", err) return } @@ -267,13 +267,13 @@ func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { context, err := cl.CreateContext([]*cl.Device{device}) if err != nil { - return fmt.Errorf("failed creating context:", err) + return fmt.Errorf("failed creating context: %v", 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) + return fmt.Errorf("command queue err: %v", err) } // See [4] section 3.2 and [3] "clBuildProgram". @@ -287,7 +287,7 @@ func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { program, err := context.CreateProgramWithSource([]string{kernelCode}) if err != nil { - return fmt.Errorf("program err:", err) + return fmt.Errorf("program err: %v", err) } /* if using AMD OpenCL impl, you can set this to debug on x86 CPU device. @@ -303,7 +303,7 @@ func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { buildOpts := "" err = program.BuildProgram([]*cl.Device{device}, buildOpts) if err != nil { - return fmt.Errorf("program build err:", err) + return fmt.Errorf("program build err: %v", err) } var searchKernelName, hashKernelName string @@ -313,7 +313,7 @@ func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { searchKernel, err := program.CreateKernel(searchKernelName) hashKernel, err := program.CreateKernel(hashKernelName) if err != nil { - return fmt.Errorf("kernel err:", err) + return fmt.Errorf("kernel err: %v", err) } // TODO: when this DAG size appears, patch the Go bindings @@ -328,28 +328,28 @@ func initCLDevice(deviceId int, device *cl.Device, c *OpenCLMiner) error { dagBuf := *(new(*cl.MemObject)) dagBuf, err = context.CreateEmptyBuffer(cl.MemReadOnly, int(c.dagSize)) if err != nil { - return fmt.Errorf("allocating dag buf failed: ", err) + return fmt.Errorf("allocating dag buf failed: %v", 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) + return fmt.Errorf("writing to dag buf failed: %v", 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) + return fmt.Errorf("search buffer err: %v", err) } searchBuffers[i] = searchBuff } headerBuf, err := context.CreateEmptyBuffer(cl.MemReadOnly, 32) if err != nil { - return fmt.Errorf("header buffer err:", err) + return fmt.Errorf("header buffer err: %v", err) } // Unique, random nonces are crucial for mining efficieny. @@ -560,7 +560,8 @@ func (c *OpenCLMiner) Search(block pow.Block, stop <-chan struct{}, index int) ( 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) + cache := c.ethash.Light.getCache(block.NumberU64()) // Separate variable to keep reference alive while inside C + ret := C.ethash_light_compute_internal(cache.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) @@ -575,7 +576,6 @@ func (c *OpenCLMiner) Search(block pow.Block, stop <-chan struct{}, index int) ( } 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) From 6b598e1e66d39a221535ddf1e29d55f384f3065c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 22 Mar 2016 23:44:30 +0100 Subject: [PATCH 2/2] Add cache.compute to ensure proper finalizers. --- ethash.go | 23 +++++++++++++---------- ethash_opencl.go | 11 +++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ethash.go b/ethash.go index 60121bb3..2a31aaf2 100644 --- a/ethash.go +++ b/ethash.go @@ -105,6 +105,15 @@ func freeCache(cache *cache) { cache.ptr = nil } +func (cache *cache) compute(dagSize uint64, hash common.Hash, nonce uint64) (ok bool, mixDigest, result common.Hash) { + ret := C.ethash_light_compute_internal(cache.ptr, C.uint64_t(dagSize), hashToH256(hash), C.uint64_t(nonce)) + // 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 + return bool(ret.success), h256ToHash(ret.mix_hash), h256ToHash(ret.result) +} + // Light implements the Verify half of the proof of work. It uses a few small // in-memory caches to verify the nonces found by Full. type Light struct { @@ -140,29 +149,23 @@ func (l *Light) Verify(block pow.Block) bool { cache := l.getCache(blockNum) dagSize := C.ethash_get_datasize(C.uint64_t(blockNum)) - if l.test { dagSize = dagSizeForTesting } // 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 { + ok, mixDigest, result := cache.compute(uint64(dagSize), block.HashNoNonce(), block.Nonce()) + if !ok { return false } // avoid mixdigest malleability as it's not included in a block's "hashNononce" - if block.MixDigest() != h256ToHash(ret.mix_hash) { + if block.MixDigest() != mixDigest { 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(maxUint256, difficulty) - return h256ToHash(ret.result).Big().Cmp(target) <= 0 + return result.Big().Cmp(target) <= 0 } func h256ToHash(in C.ethash_h256_t) common.Hash { diff --git a/ethash_opencl.go b/ethash_opencl.go index 6f6016d6..451049ea 100644 --- a/ethash_opencl.go +++ b/ethash_opencl.go @@ -556,14 +556,13 @@ func (c *OpenCLMiner) Search(block pow.Block, stop <-chan struct{}, index int) ( 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). - cache := c.ethash.Light.getCache(block.NumberU64()) // Separate variable to keep reference alive while inside C - ret := C.ethash_light_compute_internal(cache.ptr, ds, hashToH256(headerHash), cn) + cache := c.ethash.Light.getCache(block.NumberU64()) + ok, mixDigest, result := cache.compute(c.dagSize, headerHash, checkNonce) + // TODO: return result first - if ret.success && h256ToHash(ret.result).Big().Cmp(target256) <= 0 { + if ok && 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) @@ -574,7 +573,7 @@ func (c *OpenCLMiner) Search(block pow.Block, stop <-chan struct{}, index int) ( fmt.Println("Error in Search WaitForEvents: ", err) } } - return checkNonce, C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32)) + return checkNonce, mixDigest.Bytes() } _, err := d.queue.EnqueueWriteBuffer(d.searchBuffers[p.bufIndex], false, 0, 4, unsafe.Pointer(&zero), nil) if err != nil {