Skip to content

Commit

Permalink
add CPU PoW lib
Browse files Browse the repository at this point in the history
  • Loading branch information
auxten committed May 4, 2018
1 parent 56f3808 commit bdc7ca6
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 28 deletions.
19 changes: 19 additions & 0 deletions crypto/hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ package hash
import (
"encoding/hex"
"fmt"
"math/bits"
)

// HashSize of array used to store hashes. See Hash.
Expand Down Expand Up @@ -93,6 +94,24 @@ func (hash *Hash) IsEqual(target *Hash) bool {
return *hash == *target
}

// Difficulty returns the leading Zero **bit** count of Hash in binary.
// return -1 indicate the Hash pointer is nil
func (hash *Hash) Difficulty() (difficulty int) {
if hash == nil {
return -1
}

for i, _ := range *hash {
v := (*hash)[HashSize-i-1]
if v != byte(0) {
difficulty = 8 * i
difficulty += bits.LeadingZeros8(v)
return
}
}
return HashSize * 8
}

// NewHash returns a new Hash from a byte slice. An error is returned if
// the number of bytes passed in is not HashSize.
func NewHash(newHash []byte) (*Hash, error) {
Expand Down
53 changes: 53 additions & 0 deletions crypto/hash/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,56 @@ func TestNewHashFromStr(t *testing.T) {
}
}
}

func TestHash_Difficulty(t *testing.T) {
tests := []struct {
hash Hash
difficulty int
}{
{
Hash([HashSize]byte{ // Make go vet happy.
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
7,
},
{
Hash([HashSize]byte{ // Make go vet happy.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
256,
},
{
Hash([HashSize]byte{ // Make go vet happy.
0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
8,
},
}
for i, v := range tests {
if v.hash.Difficulty() != v.difficulty {
t.Errorf(
"Difficulty test #%d, hash:%s actual difficulty == %d, expect %d ",
i, v.hash.String(), v.hash.Difficulty(), v.difficulty,
)
}
}

nilDifficulty := (*Hash)(nil).Difficulty()
if nilDifficulty != -1 {
t.Errorf("Difficulty test nil expect -1 got %d", nilDifficulty)
}

newDifficulty := new(Hash).Difficulty()
if newDifficulty != 256 {
t.Errorf("Difficulty test new(Hash) expect 256 got %d", newDifficulty)
}
}
26 changes: 0 additions & 26 deletions pow/cpu/miner.go

This file was deleted.

106 changes: 106 additions & 0 deletions pow/cpuminer/miner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* MIT License
*
* Copyright (c) 2016-2018. ThunderDB
*
* 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.
*/

// Package cpuminer implements CPU based PoW functions
package cpuminer

import (
"errors"
"math/big"

log "github.com/sirupsen/logrus"
"github.com/thunderdb/ThunderDB/crypto/hash"
)

// Nonce contains nonce and the difficulty to the block
type Nonce struct {
Nonce big.Int
Difficulty int
}

// MiningBlock contains Data tobe mined
type MiningBlock struct {
Data []byte
// NonceChan is used to notify the got nonce
NonceChan chan Nonce
// Stop chan is used to stop mining and return the max difficult nonce
Stop chan struct{}
}

// CPUMiner provides concurrency-safe PoW worker group to solve hash puzzle
// Inspired by:
// "S/Kademlia: A Practicable Approach Towards Secure Key-Based Routing"
// - Section 4.1. Secure nodeId assignment.
// - Figure 3. Static (left) and dynamic (right) crypto puzzles for nodeId
// generation
type CPUMiner struct {
quit chan struct{}
}

// NewCPUMiner init a new CPU miner
func NewCPUMiner(quit chan struct{}) *CPUMiner {
return &CPUMiner{quit: quit}
}

// HashBlock calculate the hash of MiningBlock
func (miner *CPUMiner) HashBlock(data []byte, nonce big.Int) hash.Hash {
return hash.DoubleHashH(append(data, nonce.Bytes()...))
}

// CalclateBlockNonce find nonce make HashBlock() match the MiningBlock Difficulty from the startNonce
// if interrupted or stopped highest difficulty nonce will be sent to the NonceCh
func (miner *CPUMiner) CalculateBlockNonce(
block MiningBlock,
startNonce big.Int,
difficulty int,
) (err error) {
var (
bestNonce Nonce
)
for i := startNonce; ; i.Add(&i, big.NewInt(1)) {
select {
case <-block.Stop:
log.Info("Stop mining job")
block.NonceChan <- bestNonce
return errors.New("mining job stopped")
case <-miner.quit:
log.Info("Stop mining worker")
block.NonceChan <- bestNonce
return errors.New("miner interrupted")
default:
currentHash := miner.HashBlock(block.Data, i)
currentDifficulty := currentHash.Difficulty()
if currentDifficulty >= difficulty {
bestNonce.Difficulty = currentDifficulty
bestNonce.Nonce.Set(&i)
block.NonceChan <- bestNonce
return
}
if currentDifficulty > bestNonce.Difficulty {
bestNonce.Difficulty = currentDifficulty
bestNonce.Nonce.Set(&i)
}
}
}
}
138 changes: 138 additions & 0 deletions pow/cpuminer/miner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* MIT License
*
* Copyright (c) 2016-2018. ThunderDB
*
* 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.
*/

package cpuminer

import (
"math/big"
"testing"

"time"

"github.com/thunderdb/ThunderDB/crypto/hash"
)

func TestCPUMiner_HashBlock(t *testing.T) {
miner := NewCPUMiner(make(chan struct{}))
nonceCh := make(chan Nonce)
stop := make(chan struct{})
diffWanted := 20
data := []byte{
0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1,
0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8,
0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f,
0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
block := MiningBlock{
Data: data,
NonceChan: nonceCh,
Stop: stop,
}
var (
err error
)
go func() {
err = miner.CalculateBlockNonce(block, *big.NewInt(0), diffWanted)
}()
nonceFromCh := <-nonceCh

hash := hash.DoubleHashH(append(data, nonceFromCh.Nonce.Bytes()...))
if err != nil || nonceFromCh.Difficulty < diffWanted || hash.Difficulty() < diffWanted {
t.Errorf("CalculateBlockNonce got %v, difficulty %d, nonce %s",
err, nonceFromCh.Difficulty, nonceFromCh.Nonce.String())
}
t.Logf("Difficulty: %d, Hash: %s", nonceFromCh.Difficulty, hash.String())
}

func TestCPUMiner_HashBlock_stop(t *testing.T) {
minerQuit := make(chan struct{})
miner := NewCPUMiner(minerQuit)
nonceCh := make(chan Nonce)
stop := make(chan struct{})
diffWanted := 256
data := []byte{
0x79, 0xa6,
}
block := MiningBlock{
Data: data,
NonceChan: nonceCh,
Stop: stop,
}
var (
err error
)
go func() {
err = miner.CalculateBlockNonce(block, *big.NewInt(0), diffWanted)
}()
// stop miner
time.Sleep(2 * time.Second)
block.Stop <- struct{}{}
//miner.quit <- struct{}{}

nonceFromCh := <-block.NonceChan

//hasha := miner.HashBlock(data, nonceFromCh.Nonce)
hasha := hash.DoubleHashH(append(data, nonceFromCh.Nonce.Bytes()...))
if nonceFromCh.Difficulty < 1 || hasha.Difficulty() != nonceFromCh.Difficulty {
t.Errorf("CalculateBlockNonce got %v, difficulty %d, nonce %s, hash %s",
err, nonceFromCh.Difficulty, nonceFromCh.Nonce.String(), hasha.String())
}
t.Logf("Difficulty: %d, Hash: %s", nonceFromCh.Difficulty, hasha.String())
}

func TestCPUMiner_HashBlock_quit(t *testing.T) {
minerQuit := make(chan struct{})
miner := NewCPUMiner(minerQuit)
nonceCh := make(chan Nonce)
stop := make(chan struct{})
diffWanted := 256
data := []byte{
0x79, 0xa6,
}
block := MiningBlock{
Data: data,
NonceChan: nonceCh,
Stop: stop,
}
var (
err error
)
go func() {
err = miner.CalculateBlockNonce(block, *big.NewInt(0), diffWanted)
}()
// stop miner
time.Sleep(2 * time.Second)
//block.Stop <- struct{}{}
miner.quit <- struct{}{}

nonceFromCh := <-block.NonceChan

//hasha := miner.HashBlock(data, nonceFromCh.Nonce)
hasha := hash.DoubleHashH(append(data, nonceFromCh.Nonce.Bytes()...))
if nonceFromCh.Difficulty < 1 || hasha.Difficulty() != nonceFromCh.Difficulty {
t.Errorf("CalculateBlockNonce got %v, difficulty %d, nonce %s, hash %s",
err, nonceFromCh.Difficulty, nonceFromCh.Nonce.String(), hasha.String())
}
t.Logf("Difficulty: %d, Hash: %s", nonceFromCh.Difficulty, hasha.String())
}
4 changes: 2 additions & 2 deletions route/dht.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
package route

import (
"net"

log "github.com/sirupsen/logrus"
"github.com/thunderdb/ThunderDB/rpc"
"net"
)

// InitDHTserver install DHTService payload to RPC server, also set listener
Expand All @@ -39,6 +40,5 @@ func InitDHTserver(l net.Listener) (server *rpc.Server, err error) {
return
}
server.SetListener(l)

return
}

0 comments on commit bdc7ca6

Please sign in to comment.