Browse Source

DERO AstroBWT CPU miner optimized

webwallet
Captain 1 year ago
parent
commit
325806ec2e
No known key found for this signature in database GPG Key ID: 18CDB3ED5E85D2D4
11 changed files with 587 additions and 112 deletions
  1. +42
    -43
      astrobwt/astrobwt.go
  2. +228
    -0
      astrobwt/astrobwt_optimized.go
  3. +105
    -0
      astrobwt/astrobwt_optimized_test.go
  4. +4
    -3
      astrobwt/astrobwt_test.go
  5. +8
    -6
      build_package.sh
  6. +84
    -0
      cmd/dero-miner/difficulty.go
  7. +36
    -49
      cmd/dero-miner/miner.go
  8. +3
    -1
      cmd/dero-miner/thread.go
  9. +7
    -9
      cmd/dero-miner/thread_linux.go
  10. +69
    -0
      cmd/dero-miner/thread_windows.go
  11. +1
    -1
      config/version.go

+ 42
- 43
astrobwt/astrobwt.go View File

@@ -1,4 +1,4 @@
package astrobwt
package astrobwt

//import "fmt"
import "strings"
@@ -24,45 +24,44 @@ func Transform(s []byte, es byte) ([]byte, error) {
// InverseTransform reverses the bwt to original byte slice. Not optimized yet.
func InverseTransform(t []byte, es byte) []byte {

le := len(t)
table := make([]string, le)
for range table {
for i := 0; i < le; i++ {
table[i] = string(t[i:i+1]) + table[i]
}
sort.Strings(table)
}
for _, row := range table {
if strings.HasSuffix(row, "$") {
return []byte(row[ : le-1])
}
}
return []byte("")


/*
n := len(t)
lines := make([][]byte, n)
for i := 0; i < n; i++ {
lines[i] = make([]byte, n)
le := len(t)
table := make([]string, le)
for range table {
for i := 0; i < le; i++ {
table[i] = string(t[i:i+1]) + table[i]
}
sort.Strings(table)
}

for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
lines[j][n-1-i] = t[j]
for _, row := range table {
if strings.HasSuffix(row, "$") {
return []byte(row[:le-1])
}
sort.Sort(byteutil.SliceOfByteSlice(lines))
}
return []byte("")

s := make([]byte, n-1)
for _, line := range lines {
if line[n-1] == es {
s = line[0 : n-1]
break
/*
n := len(t)
lines := make([][]byte, n)
for i := 0; i < n; i++ {
lines[i] = make([]byte, n)
}
}
return s
*/

for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
lines[j][n-1-i] = t[j]
}
sort.Sort(byteutil.SliceOfByteSlice(lines))
}

s := make([]byte, n-1)
for _, line := range lines {
if line[n-1] == es {
s = line[0 : n-1]
break
}
}
return s
*/
}

// SuffixArray returns the suffix array of s.
@@ -117,7 +116,7 @@ func BWT(input []byte) ([]byte, int) {
}

const stage1_length int = 147253 // it is a prime
const max_length int = 1024*1024 + stage1_length + 1024
const MAX_LENGTH int = 1024*1024 + stage1_length + 1024

func POW(inputdata []byte) (outputhash [32]byte) {

@@ -126,7 +125,7 @@ func POW(inputdata []byte) (outputhash [32]byte) {
key := sha3.Sum256(inputdata)

var stage1 [stage1_length]byte // stages are taken from it
var stage2 [1024*1024 + stage1_length + 1024]byte // it will always be less than 1 MB 0x7fff * 31 + stage1_length
var stage2 [1024*1024 + stage1_length + 1024]byte

salsa.XORKeyStream(stage1[:stage1_length], stage1[:stage1_length], &counter, &key)

@@ -149,7 +148,7 @@ func POW(inputdata []byte) (outputhash [32]byte) {

//fmt.Printf("result %x\n", key)

copy(outputhash[:],key[:])
copy(outputhash[:], key[:])

_ = eos
return
@@ -192,13 +191,13 @@ func POW_0alloc(inputdata []byte) (outputhash [32]byte) {

var counter [16]byte

var sa [max_length]int32
var sa [MAX_LENGTH]int32
// var bwt [max_length]int32

var stage1 [stage1_length]byte // stages are taken from it
var stage1_result [stage1_length + 1]byte
var stage2 [1024*1024 + stage1_length+1]byte // it will always be less than 1 MB 0x7fff * 31 + stage1_length
var stage2_result [1024*1024 + stage1_length+1]byte
var stage2 [1024*1024 + stage1_length + 1]byte
var stage2_result [1024*1024 + stage1_length + 1]byte

key := sha3.Sum256(inputdata)

@@ -221,10 +220,10 @@ func POW_0alloc(inputdata []byte) (outputhash [32]byte) {
}

eos = BWT_0alloc(stage2[:stage2_length], sa[:stage2_length+1], stage2_result[:stage2_length+1])
_ = eos
_ = eos

key = sha3.Sum256(stage2_result[:stage2_length+1])

copy(outputhash[:],key[:])
copy(outputhash[:], key[:])
return
}

+ 228
- 0
astrobwt/astrobwt_optimized.go View File

@@ -0,0 +1,228 @@
package astrobwt

//import "os"
//import "fmt"

import "sync"
import "encoding/binary"
import "golang.org/x/crypto/sha3"

import "golang.org/x/crypto/salsa20/salsa"

// see here to improve the algorithms more https://github.com/y-256/libdivsufsort/blob/wiki/SACA_Benchmarks.md

// Original implementation was in xmrig miner, however it had a flaw which has been fixed
// this optimized algorithm is used only in the miner and not in the blockchain

//const stage1_length int = 147253 // it is a prime
//const max_length int = 1024*1024 + stage1_length + 1024

type Data struct {
stage1 [stage1_length + 64]byte // stages are taken from it
stage1_result [stage1_length + 1]byte
stage2 [1024*1024 + stage1_length + 1 + 64]byte
stage2_result [1024*1024 + stage1_length + 1]byte
indices [ALLOCATION_SIZE]uint64
tmp_indices [ALLOCATION_SIZE]uint64
}

var pool = sync.Pool{New: func() interface{} { return &Data{} }}

func POW_optimized_v1(inputdata []byte, max_limit int) (outputhash [32]byte, success bool) {

var counter [16]byte

data := pool.Get().(*Data)
for i := range data.stage1 {
data.stage1[i] = 0
}
/* for i := range data.stage1_result{
data.stage1_result[i] =0
}*/
for i := range data.stage2 {
data.stage2[i] = 0
}
/*for i := range data.stage2_result{
data.stage2_result[i] =0
}*/

key := sha3.Sum256(inputdata)

salsa.XORKeyStream(data.stage1[1:stage1_length+1], data.stage1[1:stage1_length+1], &counter, &key)

sort_indices(stage1_length+1, data.stage1[:], data.stage1_result[:], data)

key = sha3.Sum256(data.stage1_result[:])

stage2_length := stage1_length + int(binary.LittleEndian.Uint32(key[:])&0xfffff)

if stage2_length > max_limit {
for i := range outputhash { // will be optimized by compiler
outputhash[i] = 0xff
}
success = false
return
}

for i := range counter { // will be optimized by compiler
counter[i] = 0
}

salsa.XORKeyStream(data.stage2[1:stage2_length+1], data.stage2[1:stage2_length+1], &counter, &key)

sort_indices(stage2_length+1, data.stage2[:], data.stage2_result[:], data)

key = sha3.Sum256(data.stage2_result[:stage2_length+1])

copy(outputhash[:], key[:])
pool.Put(data)
success = true
return
}

const COUNTING_SORT_BITS uint64 = 10
const COUNTING_SORT_SIZE uint64 = 1 << COUNTING_SORT_BITS

const ALLOCATION_SIZE = MAX_LENGTH

func BigEndian_Uint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

func smaller(input []uint8, a, b uint64) bool {
value_a := a >> 21
value_b := b >> 21

if value_a < value_b {
return true
}

if value_a > value_b {
return false
}

data_a := BigEndian_Uint64(input[(a%(1<<21))+5:])
data_b := BigEndian_Uint64(input[(b%(1<<21))+5:])
return data_a < data_b
}

// basically
func sort_indices(N int, input_extra []byte, output []byte, d *Data) {

var counters [2][COUNTING_SORT_SIZE]uint32
indices := d.indices[:]
tmp_indices := d.tmp_indices[:]

input := input_extra[1:]

loop3 := N / 3 * 3
for i := 0; i < loop3; i += 3 {
k0 := BigEndian_Uint64(input[i:])
counters[0][(k0>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]++
counters[1][k0>>(64-COUNTING_SORT_BITS)]++
k1 := k0 << 8
counters[0][(k1>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]++
counters[1][k1>>(64-COUNTING_SORT_BITS)]++
k2 := k0 << 16
counters[0][(k2>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]++
counters[1][k2>>(64-COUNTING_SORT_BITS)]++
}

if N%3 != 0 {
for i := loop3; i < N; i++ {
k := BigEndian_Uint64(input[i:])
counters[0][(k>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]++
counters[1][k>>(64-COUNTING_SORT_BITS)]++
}
}

/*
for i := 0; i < N ; i++{
k := BigEndian_Uint64(input[i:])
counters[0][(k >> (64 - COUNTING_SORT_BITS * 2)) & (COUNTING_SORT_SIZE - 1)]++
counters[1][k >> (64 - COUNTING_SORT_BITS)]++
}
*/

prev := [2]uint32{counters[0][0], counters[1][0]}
counters[0][0] = prev[0] - 1
counters[1][0] = prev[1] - 1
var cur [2]uint32
for i := uint64(1); i < COUNTING_SORT_SIZE; i++ {
cur[0], cur[1] = counters[0][i]+prev[0], counters[1][i]+prev[1]
counters[0][i] = cur[0] - 1
counters[1][i] = cur[1] - 1
prev[0] = cur[0]
prev[1] = cur[1]
}

for i := N - 1; i >= 0; i-- {
k := BigEndian_Uint64(input[i:])
// FFFFFFFFFFE00000 = (0xFFFFFFFFFFFFFFF<< 21) // to clear bottom 21 bits
tmp := counters[0][(k>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]
counters[0][(k>>(64-COUNTING_SORT_BITS*2))&(COUNTING_SORT_SIZE-1)]--

tmp_indices[tmp] = (k & 0xFFFFFFFFFFE00000) | uint64(i)
}

for i := N - 1; i >= 0; i-- {
data := tmp_indices[i]
tmp := counters[1][data>>(64-COUNTING_SORT_BITS)]
counters[1][data>>(64-COUNTING_SORT_BITS)]--
indices[tmp] = data
}

prev_t := indices[0]
for i := 1; i < N; i++ {
t := indices[i]
if smaller(input, t, prev_t) {
t2 := prev_t
j := i - 1
for {
indices[j+1] = prev_t
j--
if j < 0 {
break
}
prev_t = indices[j]
if !smaller(input, t, prev_t) {
break
}
}
indices[j+1] = t
t = t2
}
prev_t = t
}

// optimized unrolled code below this comment
/*for i := 0; i < N;i++{
output[i] = input_extra[indices[i] & ((1 << 21) - 1) ]
}*/

loop4 := ((N + 1) / 4) * 4
for i := 0; i < loop4; i += 4 {
output[i+0] = input_extra[indices[i+0]&((1<<21)-1)]
output[i+1] = input_extra[indices[i+1]&((1<<21)-1)]
output[i+2] = input_extra[indices[i+2]&((1<<21)-1)]
output[i+3] = input_extra[indices[i+3]&((1<<21)-1)]
}
for i := loop4; i < N; i++ {
output[i] = input_extra[indices[i]&((1<<21)-1)]
}

// there is an issue above, if the last byte of input is 0x00, initialbytes are wrong
if N > 3 && input[N-2] == 0 {
backup_byte := output[0]
output[0] = 0
for i := 1; i < N; i++ {
if output[i] != 0 {
output[i-1] = backup_byte
break
}
}
}

}

+ 105
- 0
astrobwt/astrobwt_optimized_test.go View File

@@ -0,0 +1,105 @@
package astrobwt

import "crypto/rand"
import "strings"
import "testing"
import "encoding/hex"

// see https://www.geeksforgeeks.org/burrows-wheeler-data-transform-algorithm/
func TestBWTTransform(t *testing.T) {

tests := []struct {
input string
bwt string
}{
{"BANANA", "ANNB$AA"}, // from https://www.geeksforgeeks.org/burrows-wheeler-data-transform-algorithm/
{"abracadabra", "ard$rcaaaabb"},
{"appellee", "e$elplepa"},
{"GATGCGAGAGATG", "GGGGGGTCAA$TAA"},
}
for _, test := range tests {

input := "\x00" + test.input + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

var output = make([]byte, 64, 64)
sort_indices(len(test.input)+1, []byte(input), output, &Data{})

output = output[:len(test.input)+1]
output_s := strings.Replace(string(output), "\x00", "$", -1)

if output_s != test.bwt {
t.Errorf("Test failed: Transform %s %s %x", output_s, test.bwt, output)
}
}

}

func TestPOW_optimized_v1(t *testing.T) {
p := POW([]byte{0, 0, 0, 0})
p0 := POW_0alloc([]byte{0, 0, 0, 0})
p_optimized,_ := POW_optimized_v1([]byte{0, 0, 0, 0}, MAX_LENGTH)
if string(p[:]) != string(p0[:]) {
t.Error("Test failed: POW and POW_0alloc returns different ")
}
if string(p[:]) != string(p_optimized[:]) {
t.Error("Test failed: POW and POW_rewrite returns different ")
}

for i := 20; i < 200; i++ {
buf := make([]byte, 20, 20)
rand.Read(buf)

p := POW(buf)
p0 := POW_0alloc(buf)
p_optimized,_ := POW_optimized_v1(buf, MAX_LENGTH)
if string(p[:]) != string(p0[:]) {
t.Errorf("Test failed: POW and POW_0alloc returns different for i=%d buf %x", i, buf)
}
if string(p[:]) != string(p_optimized[:]) {
t.Errorf("Test failed: POW and POW_rewrite returns different for i=%d buf %x", i, buf)
}

}
}

func TestPOW_optimized_v1Tests(t *testing.T) {
tests := []struct {
input string
}{
{"57b84420d2028aef8a05b42ea893a8c4f2219f73"},
{"67d49bd1c53645ec96c50230083e55b120b5005ffbaf4b2f"},
{"67d49bd1c53645ec96c50230183e55b120b5005ffbaf4b2f"},
{"3fe7baa520edb5d0b43b7a6999c146262b2c7f26e030fd7c256611262db727833a40a79f9988"},
{"ed32ea6f1c0ee2514eb73f6a0f9f00d1e2c2392a8896963eefddfd6600c105d52db6e93ba98f8454433894293eaa9f31973658c49f67f3361af70fac27bac6f8f5c69f52be9d7c86a5fea3e5b5d99d8f73888b4d4a7dbb28169035583632ef26604e472eb26a5da9da4e95f80460dfc7b788e8ee75194a7d2f1190a788f92e98cb83fd4c63d9976ec06c2df005d321baed360599af58ff45aa63b00261ea60b5adf623f256bfbc75da961c5960db68e8"},
{"7cf76f0d4072574bae246c4f7184000af5ce818943605151a73a49d7b704c127891e6e7008c331fa41776540b0db3b2ea2c187e119191adde6b0f5438fb48cc242c02420f44d070ef4c87a00952560f2ffcc5ac5932c5a0f40df9029ddc10d29b23ff4150fbe0dda5b14a73eadd90a3b6eaf049075b89c1c16da33f049c3235f158c"},
{"fbaf4f7ebec36c97f8994e67e74b281960846e6b5ce30e4fd95ce68d8875e19ab3ebf716e5887adb6eefbc3c5ca6096f936643f4bf22a9f61a1e35b019cfaabfe331ad2897a3b70bd6846c5003a999719d26246796a1d60b18bf89bdf4f5fea3b976ad7739e00089f7f11a5833351515e330d8580f918ea694a438f384946cdae0d9d3ccda33bc6de1a64d6c25c0b3f7d905172956"},
{"ff7f99a16b3e2c0f9daa2a44c9a364b212d836ba57f8d9b0e050490d1e74"},
{"f299d507d916e67f93345a42042e859170eb755262355826fcb7ed0d2e9c999bb21662275d1b99a53b397bf77f4e2af38a41358c41e9ecd750f3cc2859a3fef8a9ef9c189b7489fb0048903cfe78f5171f2476f86aae2346e5390740b09bb185268af16146ccab9876d8931f670f9ba93805f0277a3cba0fc9671cc78ac53ce60f538c7aa616660e3ca1e1eabf8938c095baeacb4ce11889c52ce63b9511d2f176d563a75a34418fddcb4e712a5936e4f72a2269b423954dadcf"},
}

for i, test := range tests {

buf, err := hex.DecodeString(test.input)
if err != nil {
t.Error(err)
}

p := POW(buf)
p0 := POW_0alloc(buf)
p_optimized,_ := POW_optimized_v1(buf, MAX_LENGTH)
if string(p[:]) != string(p0[:]) {
t.Errorf("Test failed: POW and POW_0alloc returns different for i=%d buf %x", i, buf)
}
if string(p[:]) != string(p_optimized[:]) {
t.Errorf("Test failed: POW and POW_optimized returns different for i=%d buf %x", i, buf)
}
}

}

func BenchmarkPOW_optimized_v1(t *testing.B) {
for i := 0; i < t.N; i++ {
rand.Read(cases[0][:])
_,_ = POW_optimized_v1(cases[0][:], MAX_LENGTH)
}
}

+ 4
- 3
astrobwt/astrobwt_test.go View File

@@ -1,4 +1,4 @@
package astrobwt
package astrobwt

import "math/rand"
import "testing"
@@ -14,6 +14,7 @@ func TestBWTAndInverseTransform(t *testing.T) {
{"abracadabra", "ard$rcaaaabb"},
{"appellee", "e$elplepa"},
{"GATGCGAGAGATG", "GGGGGGTCAA$TAA"},
{"abcdefg", "g$abcdef"},
}

for _, test := range tests {
@@ -21,10 +22,10 @@ func TestBWTAndInverseTransform(t *testing.T) {
trans2[eos] = '$'

if string(trans2) != test.bwt {
t.Error("Test failed: Transform")
t.Errorf("Test failed: Transform %s", test.input)
}
if string(InverseTransform([]byte(trans2), '$')) != test.input {
t.Errorf("Test failed: InverseTransform expected '%s' actual '%s`", test.input,string(InverseTransform([]byte(trans2), '$')) )
t.Errorf("Test failed: InverseTransform expected '%s' actual '%s`", test.input, string(InverseTransform([]byte(trans2), '$')))
}

p := POW([]byte(test.input))


+ 8
- 6
build_package.sh View File

@@ -18,7 +18,7 @@ PLATFORMS="$PLATFORMS linux/amd64 linux/386"
#PLATFORMS="$PLATFORMS linux/mips64le" # experimental in go1.6 is it common enough ??
PLATFORMS="$PLATFORMS freebsd/amd64 freebsd/386"
PLATFORMS="$PLATFORMS netbsd/amd64" # amd64 only as of go1.6
PLATFORMS="$PLATFORMS openbsd/amd64" # amd64 only as of go1.6
#PLATFORMS="$PLATFORMS openbsd/amd64" # amd64 only as of go1.6
PLATFORMS="$PLATFORMS dragonfly/amd64" # amd64 only as of go1.5
#PLATFORMS="$PLATFORMS plan9/amd64 plan9/386" # as of go1.4, is it common enough ??
# solaris disabled due to badger error below
@@ -36,20 +36,22 @@ FAILURES=""
CURRENT_DIRECTORY=${PWD##*/}
OUTPUT="$package_name" # if no src file given, use current dir name

GCFLAGS=""
if [[ "${OUTPUT}" == "dero-miner" ]]; then GCFLAGS="github.com/deroproject/derosuite/astrobwt=-B"; fi

for PLATFORM in $PLATFORMS; do
GOOS=${PLATFORM%/*}
GOARCH=${PLATFORM#*/}
OUTPUT_DIR="${ABSDIR}/build/dero_${GOOS}_${GOARCH}"
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}"
echo mkdir -p $OUTPUT_DIR
echo mkdir -p $OUTPUT_DIR
if [[ "${GOOS}" == "windows" ]]; then BIN_FILENAME="${BIN_FILENAME}.exe"; fi
CMD="GOOS=${GOOS} GOARCH=${GOARCH} go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
CMD="GOOS=${GOOS} GOARCH=${GOARCH} go build -gcflags=${GCFLAGS} -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"

# build docker image for linux amd64 competely static
if [[ "${GOOS}" == "linux" && "${GOARCH}" == "amd64" && "${OUTPUT}" != "explorer" ]]; then
if [[ "${GOOS}" == "linux" && "${GOARCH}" == "amd64" && "${OUTPUT}" != "explorer" && "${OUTPUT}" != "dero-miner" ]] ; then
BIN_FILENAME="docker-${OUTPUT}-${GOOS}-${GOARCH}"
CMD="GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
@@ -64,7 +66,7 @@ if [[ $PLATFORMS_ARM == *"linux"* ]]; then
GOOS="linux"
GOARCH="arm64"
OUTPUT_DIR="${ABSDIR}/build/dero_${GOOS}_${GOARCH}"
CMD="GOOS=linux GOARCH=arm64 go build -o $OUTPUT_DIR/${OUTPUT}-linux-arm64 $package"
CMD="GOOS=linux GOARCH=arm64 go build -gcflags=${GCFLAGS} -o $OUTPUT_DIR/${OUTPUT}-linux-arm64 $package"
echo "${CMD}"
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
fi
@@ -77,7 +79,7 @@ for GOOS in $PLATFORMS_ARM; do
for GOARM in 7 6 5; do
OUTPUT_DIR="${ABSDIR}/build/dero_${GOOS}_${GOARCH}${GOARM}"
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}${GOARM}"
CMD="GOARM=${GOARM} GOOS=${GOOS} GOARCH=${GOARCH} go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
CMD="GOARM=${GOARM} GOOS=${GOOS} GOARCH=${GOARCH} go build -gcflags=${GCFLAGS} -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
eval "${CMD}" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}${GOARM}"
done


+ 84
- 0
cmd/dero-miner/difficulty.go View File

@@ -0,0 +1,84 @@
// Copyright 2017-2018 DERO Project. All rights reserved.
// Use of this source code in any form is governed by RESEARCH license.
// license can be found in the LICENSE file.
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

// ripoff from blockchain folder
import "math/big"
import "github.com/deroproject/derosuite/crypto"

var (
// bigZero is 0 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigZero = big.NewInt(0)

// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)

// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)

// enabling this will simulation mode with hard coded difficulty set to 1
// the variable is knowingly not exported, so no one can tinker with it
//simulation = false // simulation mode is disabled
)

// HashToBig converts a PoW has into a big.Int that can be used to
// perform math comparisons.
func HashToBig(buf crypto.Hash) *big.Int {
// A Hash is in little-endian, but the big package wants the bytes in
// big-endian, so reverse them.
blen := len(buf) // its hardcoded 32 bytes, so why do len but lets do it
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}

return new(big.Int).SetBytes(buf[:])
}

// this function calculates the difficulty in big num form
func ConvertDifficultyToBig(difficultyi uint64) *big.Int {
if difficultyi == 0 {
panic("difficulty can never be zero")
}
// (1 << 256) / (difficultyNum )
difficulty := new(big.Int).SetUint64(difficultyi)
denominator := new(big.Int).Add(difficulty, bigZero) // above 2 lines can be merged
return new(big.Int).Div(oneLsh256, denominator)
}

func ConvertIntegerDifficultyToBig(difficultyi *big.Int) *big.Int {

if difficultyi.Cmp(bigZero) == 0 { // if work_pow is less than difficulty
panic("difficulty can never be zero")
}

return new(big.Int).Div(oneLsh256, difficultyi)
}

// this function check whether the pow hash meets difficulty criteria
// however, it take diff in bigint format
func CheckPowHashBig(pow_hash crypto.Hash, big_difficulty_integer *big.Int) bool {
big_pow_hash := HashToBig(pow_hash)

big_difficulty := ConvertIntegerDifficultyToBig(big_difficulty_integer)
if big_pow_hash.Cmp(big_difficulty) <= 0 { // if work_pow is less than difficulty
return true
}
return false
}

+ 36
- 49
cmd/dero-miner/miner.go View File

@@ -24,7 +24,6 @@ import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/astrobwt"
import "github.com/deroproject/derosuite/structures"
import "github.com/deroproject/derosuite/blockchain"
import "github.com/deroproject/derosuite/cryptonight"

import log "github.com/sirupsen/logrus"
@@ -40,7 +39,8 @@ var mutex sync.RWMutex
var job structures.GetBlockTemplate_Result
var maxdelay int = 10000
var threads int
var iterations int = 20
var iterations int = 100
var max_pow_size int = astrobwt.MAX_LENGTH
var wallet_address string
var daemon_rpc_address string

@@ -57,8 +57,8 @@ ONE CPU, ONE VOTE.
http://wiki.dero.io

Usage:
dero-miner --wallet-address=<wallet_address> [--daemon-rpc-address=<http://127.0.0.1:20206>] [--mining-threads=<threads>] [--testnet] [--debug]
dero-miner --bench
dero-miner --wallet-address=<wallet_address> [--daemon-rpc-address=<http://127.0.0.1:20206>] [--mining-threads=<threads>] [--max-pow-size=1120] [--testnet] [--debug]
dero-miner --bench [--max-pow-size=1120]
dero-miner -h | --help
dero-miner --version

@@ -69,6 +69,7 @@ Options:
--daemon-rpc-address=<http://127.0.0.1:20206> Miner will connect to daemon RPC on this port.
--wallet-address=<wallet_address> This address is rewarded when a block is mined sucessfully.
--mining-threads=<threads> Number of CPU threads for mining [default: ` + fmt.Sprintf("%d", runtime.GOMAXPROCS(0)) + `]
--max-pow-size=1120 Max amount of PoW size in KiB to mine, some older/newer cpus can increase their work

Example Mainnet: ./dero-miner-linux-amd64 --wallet-address dERoXHjNHFBabzBCQbBDSqbkLURQyzmPRCLfeFtzRQA3NgVfU4HDbRpZQUKBzq59QU2QLcoAviYQ59FG4bu8T9pZ1woERqciSL --daemon-rpc-address=http://explorer.dero.io:20206
Example Testnet: ./dero-miner-linux-amd64 --wallet-address dEToYsDQtFoabzBCQbBDSqbkLURQyzmPRCLfeFtzRQA3NgVfU4HDbRpZQUKBzq59QU2QLcoAviYQ59FG4bu8T9pZ1woEQQstVq --daemon-rpc-address=http://127.0.0.1:30306
@@ -111,6 +112,9 @@ func main() {
// all screen output must go through the readline
globals.Logger.Out = l.Stdout()

os.Setenv("RLOG_LOG_LEVEL", "INFO")
rlog.UpdateEnv()

rlog.Infof("Arguments %+v", globals.Arguments)

globals.Logger.Infof("DERO Atlantis AstroBWT miner : It is an alpha version, use it for testing/evaluations purpose only.")
@@ -149,14 +153,23 @@ func main() {
globals.Logger.Fatalf("Mining threads (%d) is more than available CPUs (%d). This is NOT optimal", threads, runtime.GOMAXPROCS(0))
}
}
if globals.Arguments["--max-pow-size"] != nil {
if s, err := strconv.Atoi(globals.Arguments["--max-pow-size"].(string)); err == nil && s > 200 && s < 100000 {
max_pow_size = s*1024
} else {
globals.Logger.Fatalf("max-pow-size argument cannot be parsed: err %s", err)
}
}
globals.Logger.Infof("max-pow-size limited to %d bytes. Good Luck!!", max_pow_size)

if globals.Arguments["--bench"].(bool) {

var wg sync.WaitGroup

fmt.Printf("%20s %20s %20s %20s %20s \n", "Threads", "Total Time", "Total Iterations", "Time/PoW ", "Hash Rate/Sec")
iterations = 100
iterations = 500
for bench := 1; bench <= threads; bench++ {
processor = 0
now := time.Now()
for i := 0; i < bench; i++ {
wg.Add(1)
@@ -218,7 +231,6 @@ func main() {

best_height, best_topo_height := int64(0), int64(0)
peer_count := uint64(0)
topo_height := int64(0)

mempool_tx_count := 0

@@ -276,7 +288,7 @@ func main() {
testnet_string = "\033[31m TESTNET"
}

l.SetPrompt(fmt.Sprintf("\033[1m\033[32mDERO Miner: \033[0m"+color+"%d/%d "+pcolor+" FOUND_BLOCKS %d \033[32mNW %s %s>%s>>\033[0m ", our_height, topo_height, block_counter, hash_rate_string, mining_string, testnet_string))
l.SetPrompt(fmt.Sprintf("\033[1m\033[32mDERO Miner: \033[0m"+color+"Height %d "+pcolor+" FOUND_BLOCKS %d \033[32mNW %s %s>%s>>\033[0m ", our_height, block_counter, hash_rate_string, mining_string, testnet_string))
l.Refresh()
last_our_height = our_height
last_best_height = best_height
@@ -363,41 +375,6 @@ func main() {
<-Exit_In_Progress

return
/*

if *bench_ptr {

fmt.Printf("%20s %20s %20s %20s %20s \n", "Threads", "Total Time", "Total Iterations", "Time/PoW ", "Hash Rate/Sec")
for bench := 1; bench <= threads; bench++ {
now := time.Now()
for i := 0; i < bench; i++ {
wg.Add(1)
go random_execution(&wg, iterations)
}
wg.Wait()
duration := time.Now().Sub(now)

fmt.Printf("%20s %20s %20s %20s %20s \n", fmt.Sprintf("%d", bench), fmt.Sprintf("%s", duration), fmt.Sprintf("%d", bench*iterations),
fmt.Sprintf("%s", duration/time.Duration(bench*iterations)), fmt.Sprintf("%.1f", float32(time.Second)/(float32(duration/time.Duration(bench*iterations)))))

}

} else {
fmt.Printf("Starting %d threads\n", threads)
now := time.Now()
for i := 0; i < threads; i++ {
wg.Add(1)
go random_execution(&wg, iterations)
}
wg.Wait()
duration := time.Now().Sub(now)
fmt.Printf("Total iterations %d ( per thread %d)\n", threads*iterations, iterations)
fmt.Printf("Total time %s\n", duration)
fmt.Printf("time per PoW (avg) %s\n", duration/time.Duration(threads*iterations))

// now we should get the job and
}
*/

}

@@ -405,13 +382,19 @@ func random_execution(wg *sync.WaitGroup, iterations int) {
var workbuf [255]byte

runtime.LockOSThread()
//threadaffinity()

for i := 0; i < iterations; i++ {
rand.Read(workbuf[:])
//astrobwt.POW(workbuf[:])
astrobwt.POW_0alloc(workbuf[:])
//astrobwt.POW_0alloc(workbuf[:])
_,success := astrobwt.POW_optimized_v1(workbuf[:], max_pow_size)
if !success{
i--
}
}
wg.Done()
runtime.UnlockOSThread()
}

func increase_delay() {
@@ -514,7 +497,7 @@ func mineblock() {
pow := cryptonight.SlowHash(work[:])
copy(powhash[:], pow[:])

if blockchain.CheckPowHashBig(powhash, &diff) == true {
if CheckPowHashBig(powhash, &diff) == true {
globals.Logger.Infof("Successfully found DERO block at difficulty:%d", myjob.Difficulty)
maxdelay = 200
block_counter++
@@ -531,14 +514,18 @@ func mineblock() {
}
}
} else {
for i := uint32(0); i < 20; i++ {
atomic.AddUint64(&counter, 1)
for i := uint32(0); i < 32; i++ {
binary.BigEndian.PutUint32(nonce_buf, i)
pow := astrobwt.POW_0alloc(work[:])
//pow := astrobwt.POW_0alloc(work[:])
pow, success := astrobwt.POW_optimized_v1(work[:],max_pow_size)
if !success {
continue
}
atomic.AddUint64(&counter, 1)
copy(powhash[:], pow[:])

if blockchain.CheckPowHashBig(powhash, &diff) == true {
globals.Logger.Infof("Successfully found DERO block astroblock at difficulty:%d powhash %s", myjob.Difficulty, powhash)
if CheckPowHashBig(powhash, &diff) == true {
globals.Logger.Infof("Successfully found DERO block astroblock at difficulty:%d at height %d", myjob.Difficulty, myjob.Height)
maxdelay = 200

block_counter++


+ 3
- 1
cmd/dero-miner/thread.go View File

@@ -1,7 +1,9 @@
//+build !linux
//+build !linux,!windows

package main

var processor int32

// TODO
func threadaffinity() {



+ 7
- 9
cmd/dero-miner/thread_linux.go View File

@@ -1,5 +1,3 @@
//+build !windows

package main

import "runtime"
@@ -22,11 +20,11 @@ func threadaffinity() {
unix.SchedSetaffinity(0, &cpuset)
}

func avoidHT( i int ) int {
count := runtime.GOMAXPROCS(0)
if i < count/2 {
return i*2
}else{
return (i-count/2)*2+1
}
func avoidHT(i int) int {
count := runtime.GOMAXPROCS(0)
if i < count/2 {
return i * 2
} else {
return (i-count/2)*2 + 1
}
}

+ 69
- 0
cmd/dero-miner/thread_windows.go View File

@@ -0,0 +1,69 @@
package main

import "runtime"
import "sync/atomic"
import "syscall"
import "unsafe"
import "math/bits"

var libkernel32 uintptr
var setThreadAffinityMask uintptr

func doLoadLibrary(name string) uintptr {
lib, _ := syscall.LoadLibrary(name)
return uintptr(lib)
}

func doGetProcAddress(lib uintptr, name string) uintptr {
addr, _ := syscall.GetProcAddress(syscall.Handle(lib), name)
return uintptr(addr)
}

func syscall3(trap, nargs, a1, a2, a3 uintptr) uintptr {
ret, _, _ := syscall.Syscall(trap, nargs, a1, a2, a3)
return ret
}

func init() {
libkernel32 = doLoadLibrary("kernel32.dll")
setThreadAffinityMask = doGetProcAddress(libkernel32, "SetThreadAffinityMask")
}

var processor int32

// currently we suppport upto 64 cores
func SetThreadAffinityMask(hThread syscall.Handle, dwThreadAffinityMask uint) *uint32 {
ret1 := syscall3(setThreadAffinityMask, 2,
uintptr(hThread),
uintptr(dwThreadAffinityMask),
0)
return (*uint32)(unsafe.Pointer(ret1))
}

// CurrentThread returns the handle for the current thread.
// It is a pseudo handle that does not need to be closed.
func CurrentThread() syscall.Handle { return syscall.Handle(^uintptr(2 - 1)) }

// sets thread affinity to avoid cache collision and thread migration
func threadaffinity() {
lock_on_cpu := atomic.AddInt32(&processor, 1)
if lock_on_cpu >= int32(runtime.GOMAXPROCS(0)) { // threads are more than cpu, we do not know what to do
return
}

if lock_on_cpu >= bits.UintSize {
return
}
var cpuset uint
cpuset = 1 << uint(avoidHT(int(lock_on_cpu)))
SetThreadAffinityMask(CurrentThread(), cpuset)
}

func avoidHT(i int) int {
count := runtime.GOMAXPROCS(0)
if i < count/2 {
return i * 2
} else {
return (i-count/2)*2 + 1
}
}

+ 1
- 1
config/version.go View File

@@ -20,4 +20,4 @@ import "github.com/blang/semver"

// right now it has to be manually changed
// do we need to include git commitsha??
var Version = semver.MustParse("2.2.0-0.Atlantis.Astrobwt+22022020")
var Version = semver.MustParse("2.2.0-1.Atlantis.Astrobwt+05032020")

Loading…
Cancel
Save