Skip to content

Commit

Permalink
add CUDA mining support
Browse files Browse the repository at this point in the history
  • Loading branch information
asdvxgxasjab committed Jul 20, 2019
1 parent f77d01d commit 83119db
Show file tree
Hide file tree
Showing 12 changed files with 675 additions and 17 deletions.
4 changes: 2 additions & 2 deletions block.go
Expand Up @@ -179,11 +179,11 @@ func (header BlockHeader) ID() (BlockID, error) {
}

// IDFast computes an ID for a given block header when mining.
func (header *BlockHeader) IDFast() *big.Int {
func (header *BlockHeader) IDFast(minerNum int) (*big.Int, int64) {
if header.hasher == nil {
header.hasher = NewBlockHeaderHasher()
}
return header.hasher.Update(header)
return header.hasher.Update(minerNum, header)
}

// Compare returns true if the header indicates it is a better chain than "theirHeader" up to both points.
Expand Down
67 changes: 55 additions & 12 deletions block_header_hasher.go
Expand Up @@ -6,6 +6,7 @@ package cruzbit
import (
"encoding/hex"
"hash"
"log"
"math/big"
"strconv"

Expand All @@ -32,12 +33,13 @@ type BlockHeaderHasher struct {
txCountLen int

// used for hashing
initialized bool
bufLen int
buffer []byte
hasher HashWithRead
resultBuf [32]byte
result *big.Int
initialized bool
bufLen int
buffer []byte
hasher HashWithRead
resultBuf [32]byte
result *big.Int
hashesPerAttempt int64
}

// HashWithRead extends hash.Hash to provide a Read interface.
Expand Down Expand Up @@ -69,9 +71,10 @@ func NewBlockHeaderHasher() *BlockHeaderHasher {

// initialize the hasher
return &BlockHeaderHasher{
buffer: make([]byte, bufLen),
hasher: sha3.New256().(HashWithRead),
result: new(big.Int),
buffer: make([]byte, bufLen),
hasher: sha3.New256().(HashWithRead),
result: new(big.Int),
hashesPerAttempt: 1,
}
}

Expand Down Expand Up @@ -146,12 +149,20 @@ func (h *BlockHeaderHasher) initBuffer(header *BlockHeader) {
}

// Update is called everytime the header is updated and the caller wants its new hash value/ID.
func (h *BlockHeaderHasher) Update(header *BlockHeader) *big.Int {
func (h *BlockHeaderHasher) Update(minerNum int, header *BlockHeader) (*big.Int, int64) {
if !h.initialized {
h.initBuffer(header)
if CUDA_ENABLED {
lastOffset := h.nonceOffset + h.nonceLen
h.hashesPerAttempt = CudaMinerUpdate(minerNum, h.buffer, h.bufLen,
h.nonceOffset, lastOffset, header.Target)
}
} else {
var bufferChanged bool

// hash_list_root
if h.previousHashListRoot != header.HashListRoot {
bufferChanged = true
// write out the new value
h.previousHashListRoot = header.HashListRoot
hex.Encode(h.buffer[h.hashListRootOffset:], header.HashListRoot[:])
Expand All @@ -161,6 +172,7 @@ func (h *BlockHeaderHasher) Update(header *BlockHeader) *big.Int {

// time
if h.previousTime != header.Time {
bufferChanged = true
h.previousTime = header.Time

// write out the new value
Expand Down Expand Up @@ -194,7 +206,8 @@ func (h *BlockHeaderHasher) Update(header *BlockHeader) *big.Int {
}

// nonce
if offset != 0 || h.previousNonce != header.Nonce {
if offset != 0 || (!CUDA_ENABLED && h.previousNonce != header.Nonce) {
bufferChanged = true
h.previousNonce = header.Nonce

// write out the new value (or old value at a new location)
Expand All @@ -221,6 +234,7 @@ func (h *BlockHeaderHasher) Update(header *BlockHeader) *big.Int {

// transaction_count
if offset != 0 || h.previousTransactionCount != header.TransactionCount {
bufferChanged = true
h.previousTransactionCount = header.TransactionCount

// write out the new value (or old value at a new location)
Expand All @@ -241,12 +255,41 @@ func (h *BlockHeaderHasher) Update(header *BlockHeader) *big.Int {

// it's possible (likely) we did a bunch of encoding with no net impact to the buffer length
h.bufLen += offset

if CUDA_ENABLED && bufferChanged {
// something besides the nonce changed since last time. update the buffers in CUDA.
lastOffset := h.nonceOffset + h.nonceLen
h.hashesPerAttempt = CudaMinerUpdate(minerNum, h.buffer, h.bufLen,
h.nonceOffset, lastOffset, header.Target)
}
}

if CUDA_ENABLED {
var nonce int64 = CudaMinerMine(minerNum, header.Nonce)
if nonce == 0x7FFFFFFFFFFFFFFF {
h.result.SetBytes(
// indirectly let miner.go know we failed
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
)
// -1 here for the +1 done in miner.go
header.Nonce += h.hashesPerAttempt - 1
return h.result, h.hashesPerAttempt
} else {
log.Printf("CUDA miner %d found a possible solution: %d, double-checking it...\n", minerNum, nonce)
// rebuild the buffer with the new nonce since we don't update it
// per attempt when using CUDA.
header.Nonce = nonce
h.initBuffer(header)
}
}

// hash it
h.hasher.Reset()
h.hasher.Write(h.buffer[:h.bufLen])
h.hasher.Read(h.resultBuf[:])
h.result.SetBytes(h.resultBuf[:])
return h.result
return h.result, h.hashesPerAttempt
}
9 changes: 9 additions & 0 deletions client/main.go
Expand Up @@ -68,6 +68,15 @@ func main() {
}
}

// initialize CUDA if enabled
if CUDA_ENABLED && *numMinersPtr > 0 {
deviceCount := CudaInit()
if deviceCount != *numMinersPtr {
log.Fatalf("CUDA enabled but -numminers is %d and supported devices is %d\n",
*numMinersPtr, deviceCount)
}
}

// load genesis block
genesisBlock := new(Block)
if err := json.Unmarshal([]byte(GenesisBlockJson), genesisBlock); err != nil {
Expand Down
41 changes: 41 additions & 0 deletions cuda.go
@@ -0,0 +1,41 @@
// +build cuda
// Copyright 2019 cruzbit developers
// Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.

package cruzbit

//#cgo LDFLAGS: -L./cuda/build -lcuda
//
// #include <stdint.h>
//
// extern int cuda_init();
// extern int miner_update(int miner_num, const void *first, size_t first_len, const void *last,
// size_t last_len, const void *target);
// extern int64_t miner_mine(int miner_num, int64_t start_nonce);
//
import "C"

import (
"unsafe"
)

const CUDA_ENABLED = true

// CudaInit is called on startup.
func CudaInit() int {
return int(C.cuda_init())
}

// CudaMinerUpdate is called by a miner goroutine when the underlying header changes.
func CudaMinerUpdate(minerNum int, headerBytes []byte, headerBytesLen, startNonceOffset, endNonceOffset int, target BlockID) int64 {
return int64(C.miner_update(C.int(minerNum), unsafe.Pointer(&headerBytes[0]), C.size_t(startNonceOffset),
unsafe.Pointer(&headerBytes[endNonceOffset]), C.size_t(headerBytesLen-endNonceOffset),
unsafe.Pointer(&target[0])))
}

// CudaMine is called on every solution attempt by a miner goroutine.
// It will perform N hashing attempts where N is the maximum number of threads your device is capable of executing.
// Returns a solving nonce; otherwise 0x7FFFFFFFFFFFFFFF.
func CudaMinerMine(minerNum int, startNonce int64) int64 {
return int64(C.miner_mine(C.int(minerNum), C.int64_t(startNonce)))
}
9 changes: 9 additions & 0 deletions cuda/CMakeLists.txt
@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.12)
project(cuda)
find_package(CUDA REQUIRED)
set(CUDA_SEPARABLE_COMPILATION ON)
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS}; -Xcompiler -fPIC)
CUDA_ADD_LIBRARY(cuda SHARED
mine.cu
sha3.cu
)
22 changes: 22 additions & 0 deletions cuda/LICENSE
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2015 Markku-Juhani O. Saarinen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

63 changes: 63 additions & 0 deletions cuda/README.md
@@ -0,0 +1,63 @@
# This is a CUDA port of tiny_sha3 which also includes mining specific calls for cruzbit. Original project's README follows. -asdvxgxasjab 19-Jul-19

# tiny_sha3
Very small, readable implementation of the FIPS 202 and SHA3 hash function.
Public domain.

### Updated 27-Dec-15:

Added SHAKE128 and SHAKE256 code and test vectors. The code can actually do
a XOF of arbitrary size (like "SHAKE512").


### Updated 03-Sep-15:

Made the implementation portable. The API is now pretty much the
same that OpenSSL uses.


### Updated 07-Aug-15:

Now that SHA3 spec is out, I've updated the package to match with the
new padding rules. There is literally one line difference between
Keccak 3.0 and SHA-3 implementations:

```
temp[inlen++] = 0x06; // XXX Padding Changed from Keccak 3.0
```

The 0x06 constant there used to be 0x01. But this of course totally
breaks compatibility and test vectors had to be revised.

SHA-3 Spec: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf

Cheers,
- markku


### Original README.TXT from 19-Nov-11:

Hi.

The SHA-3 competition is nearing it's end and I would personally like
to support Keccak as the winner. I have a PhD in hash function cryptanalysis
so don't take my word for it, go ahead and look into the code !

Since I couldn't find a *compact* and/or *readable* implementation of Keccak
anywhere, here's one I cooked up as a service to the curious.

This implementation is intended for study of the algorithm, not for
production use.

The code works correctly on 64-bit little-endian platforms with gcc.
Like your Linux box. The main.c module contains self-tests for all
officially supported hash sizes.

If you're looking for production code, the official multi-megabyte package
covers everyting you could possibly need and too much much more:
http://keccak.noekeon.org/

Cheers,
- Markku 19-Nov-11

Dr. Markku-Juhani O. Saarinen <mjos@iki.fi>

0 comments on commit 83119db

Please sign in to comment.