Skip to content

Commit

Permalink
New mining system
Browse files Browse the repository at this point in the history
Print CPU caches size at startup

Code review
  • Loading branch information
christiankakesa committed Jan 25, 2021
1 parent cb71036 commit cfb4e31
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 74 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.15

require (
github.com/alitto/pond v1.4.0
github.com/dustin/go-humanize v1.0.0
github.com/google/uuid v1.1.2
github.com/gorilla/websocket v1.4.2
github.com/klauspost/cpuid/v2 v2.0.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
Expand Down
125 changes: 88 additions & 37 deletions miner/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
"fmt"
"minaxnt/types"
"net/url"
"strconv"
"strings"
"sync"
"time"

"github.com/alitto/pond"
"github.com/dustin/go-humanize"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/klauspost/cpuid/v2"
Expand All @@ -25,6 +25,7 @@ type Client struct {
CPUModel string
CPUFeatures string
CPUCores string
CPUCaches string
NodeURL string
conn *websocket.Conn
sendChan chan *types.MessageResponse
Expand All @@ -44,6 +45,7 @@ func NewClient(clientName string, nodeURL string, walletAddr string, numProcess
CPUModel: cpuModel(),
CPUFeatures: cpuFeatures(),
CPUCores: fmt.Sprintf("Physical => %d, Logical => %d, Threads/core => %d", cpuid.CPU.PhysicalCores, cpuid.CPU.LogicalCores, cpuid.CPU.ThreadsPerCore),
CPUCaches: fmt.Sprintf("L2 => %s, L3 => %s", humanize.Bytes(uint64(cpuid.CPU.Cache.L2)), humanize.Bytes(uint64(cpuid.CPU.Cache.L3))),
NodeURL: nodeURL,
conn: buildConn(nodeURL, false),
sendChan: make(chan *types.MessageResponse),
Expand All @@ -54,8 +56,8 @@ func NewClient(clientName string, nodeURL string, walletAddr string, numProcess
Stats: NewStats(),
pool: pond.New(numProcess, 0, pond.MinWorkers(numProcess)),
handshake: &types.MessageResponse{
Type: types.TYPE_MINER_HANDSHAKE,
Content: fmt.Sprintf("{\"version\":%d,\"address\":\"%s\",\"mid\":\"%s\"}", types.CORE_VERSION, walletAddr, minerID),
Type: types.TypeMinerHandshake,
Content: fmt.Sprintf("{\"version\":%d,\"address\":\"%s\",\"mid\":\"%s\"}", types.CoreVersion, walletAddr, minerID),
},
}
}
Expand Down Expand Up @@ -164,36 +166,44 @@ func (c *Client) stopMining() {
c.pool.StopAndWait()
}

func (c *Client) restartMining() {
c.stopMining()
c.startMining()
}

func (c *Client) foundNonce(resp types.PeerResponse, workerID int) {
for {
log.Debugf("[#%d] Start mining block index: %d", workerID, resp.Block.Index)
blockNonce, computedDifficulty, stop := Mining(resp.Block, resp.MiningDifficulty, c)
if stop {
log.Debugf("[#%d] Stop mining block index: %d", workerID, resp.Block.Index)
return
}
go func() {
log.Infof("[#%d] Found new nonce(diff. %d, required %d): %d", workerID, computedDifficulty, resp.MiningDifficulty, blockNonce)
log.Debugf("[#%d] => Nonce for block: %d", workerID, resp.Block.Index)
log.Debugf("[#%d] Start mining block index: %d", workerID, resp.Block.Index)

mnc := types.MinerNonceContent{
Nonce: types.NewMinerNonce(),
}
mnc.Nonce.Mid = c.MinerID
mnc.Nonce.Value = strconv.Itoa(int(blockNonce))
mnc.Nonce.Timestamp = time.Now().UTC().UnixNano() / int64(time.Millisecond)
mnc.Nonce.Address = c.Address
mncJSON, err := json.Marshal(mnc)
if err != nil {
log.Errorf("[#%d] Can't convert miner nonce to JSON: %s", workerID, err)
}
resultNonce := types.MessageResponse{
Type: types.TYPE_MINER_FOUND_NONCE,
Content: string(mncJSON),
}
c.sendChan <- &resultNonce
}()
mr, stop := Mining(resp.Block, resp.MiningDifficulty, c)
if stop {
log.Debugf("[#%d] Stop mining block index: %d", workerID, resp.Block.Index)
return
}

log.Infof("[#%d] Found new nonce(diff. %d, required %d): %s", workerID, mr.Difficulty, resp.MiningDifficulty, mr.Nonce)
log.Debugf("[#%d] => Nonce for block: %d", workerID, resp.Block.Index)

mnc := types.MinerNonceContent{
Nonce: types.NewMinerNonce(),
}
mnc.Nonce.Mid = c.MinerID
mnc.Nonce.Value = mr.Nonce
mnc.Nonce.Timestamp = mr.Timestamp
mnc.Nonce.Address = c.Address
mnc.Nonce.Difficulty = resp.MiningDifficulty

mncJSON, err := json.Marshal(mnc)
if err != nil {
log.Errorf("[#%d] Can't convert miner nonce to JSON: %s", workerID, err)
}

resultNonce := types.MessageResponse{
Type: types.TypeMinerFoundNonce,
Content: string(mncJSON),
}

c.sendChan <- &resultNonce
log.Debugf("Miner nonce content sent to node: %v", resultNonce)
}

func (c *Client) resetConnOrFail() {
Expand Down Expand Up @@ -264,27 +274,66 @@ func (c *Client) recv() {
log.Debugf("Received message from blockchain: %+v", result)

switch result.Type {
case types.TYPE_MINER_BLOCK_UPDATE:
case types.TypeMinerBlockInvalid:
log.Debug("[MINER_BLOCK_INVALID]")

c.restartMining()

resp := types.PeerResponseWithReason{}
err = json.Unmarshal([]byte(result.Content), &resp)
if err != nil {
log.Error("Can't parse mining block data: ", err)
return
}
log.Errorf("[MINING BLOCK INVALID]: %s", resp.Reason)
log.Warnf("[MINING BLOCK UPDATE (last was invalid)]: block index %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)
for i := 0; i < c.Process; i++ {
func(workerID int) {
c.pool.Submit(func() {
c.foundNonce(resp.ToPeerResponse(), workerID)
})
}(i)
}
case types.TypeMinerBlockDifficultyAdjust:
log.Debug("[MINER_BLOCK_DIFFICULTY_ADJUST]")

c.restartMining()

resp := types.PeerResponseWithReason{}
err = json.Unmarshal([]byte(result.Content), &resp)
if err != nil {
log.Error("Can't parse mining block data: ", err)
return
}
log.Infof("[MINING DIFFICULTY ADJUST]: %s", resp.Reason)
log.Infof("=> [BLOCK INFO]: block index %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)
for i := 0; i < c.Process; i++ {
func(workerID int) {
c.pool.Submit(func() {
c.foundNonce(resp.ToPeerResponse(), workerID)
})
}(i)
}
case types.TypeMinerBlockUpdate:
log.Debug("[MINER_BLOCK_UPDATE]")

c.stopMining()
c.startMining()
c.restartMining()

resp := types.PeerResponse{}
err = json.Unmarshal([]byte(result.Content), &resp)
if err != nil {
log.Error("Can't parse mining block data: ", err)
return
}
log.Infof("[BLOCK-UPDATE] PREPARING NEXT SLOW BLOCK: %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)
log.Infof("[NEW BLOCK]: block index %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)
for i := 0; i < c.Process; i++ {
func(workerID int) {
c.pool.Submit(func() {
c.foundNonce(resp, workerID)
})
}(i)
}
case types.TYPE_MINER_HANDSHAKE_ACCEPTED:
case types.TypeMinerHandshakeAccepted:
log.Debug("[MINER_HANDSHAKE_ACCEPTED]")

resp := types.PeerResponse{}
Expand All @@ -293,7 +342,7 @@ func (c *Client) recv() {
log.Error("Can't parse mining block data: ", err)
return
}
log.Infof("[START] PREPARING NEXT SLOW BLOCK: %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)
log.Infof("[START MINING]: block index %d at approximate difficulty: %d", resp.Block.Index, resp.Block.Difficulty)

for i := 0; i < c.Process; i++ {
func(workerID int) {
Expand All @@ -302,13 +351,15 @@ func (c *Client) recv() {
})
}(i)
}
case types.TYPE_MINER_HANDSHAKE_REJECTED:
case types.TypeMinerHandshakeRejected:
reason := types.PeerRejectedResponse{}
err = json.Unmarshal([]byte(result.Content), &reason)
if err != nil {
log.Error("Can't convert rejected message")
}
log.Fatal("Handshake rejected: ", reason.Reason)
default:
log.Fatalf("Unknonw response type %d: %v", result.Type, result)
}
}
}
48 changes: 31 additions & 17 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"minaxnt/types"
"strconv"
"strings"
"time"
"unicode/utf8"

"golang.org/x/crypto/argon2"
Expand All @@ -22,8 +23,8 @@ const (
argon2KeyLength = 512
)

func computeDifficulty(blockHash string, blockNonce uint32, difficulty int32) int32 {
nonce := strconv.FormatUint(uint64(blockNonce), 16)
func computeDifficulty(blockHash string, blockNonce uint64) int32 {
nonce := strconv.FormatUint(blockNonce, 16)
if utf8.RuneCountInString(nonce)%2 != 0 {
nonce = "0" + nonce
}
Expand All @@ -42,33 +43,46 @@ func computeDifficulty(blockHash string, blockNonce uint32, difficulty int32) in
return int32(len(splitedStr))
}

func Mining(block types.MinerBlock, miningDifficulty int32, c *Client) (nonce uint32, difficulty int32, stop bool) {
type MiningResult struct {
Nonce string
Difficulty int32
Timestamp int64
Sha256Sum string
}

func Mining(block types.MinerBlock, miningDifficulty int32, c *Client) (mr MiningResult, stop bool) {
var blockJSON []byte
var cDiff int32
var tempoHash [32]byte
var diff int32
var blockHash [32]byte
var blockHashString string

nonce = rand.Uint32()
block.Nonce = strconv.Itoa(int(nonce))
nonce := rand.Uint64()
block.Difficulty = miningDifficulty
for {
if c.StopClient.IsSet() {
return 0, 0, true
return MiningResult{}, true
}
block.Timestamp = time.Now().Unix()

if nonce == math.MaxUint32 {
if nonce == math.MaxUint64 {
nonce = 0
} else if c.Stats.Counter()%250 == 0 {
nonce = rand.Uint32()
} else if c.Stats.Counter()%100 == 0 {
nonce = rand.Uint64()
} else {
nonce++
}
block.Nonce = strconv.Itoa(int(nonce))
block.Nonce = strconv.FormatUint(nonce, 10)
blockJSON, _ = json.Marshal(block)
tempoHash = sha256.Sum256(blockJSON)
cDiff = computeDifficulty(hex.EncodeToString(tempoHash[:]), nonce, miningDifficulty)
blockHash = sha256.Sum256(blockJSON)
blockHashString = hex.EncodeToString(blockHash[:])
diff = computeDifficulty(blockHashString, nonce)
go c.Stats.Incr()

if cDiff >= miningDifficulty {
return nonce, cDiff, false
if diff >= miningDifficulty {
mr.Nonce = block.Nonce
mr.Difficulty = block.Difficulty
mr.Timestamp = block.Timestamp
mr.Sha256Sum = blockHashString
return mr, false
}
}
}
3 changes: 1 addition & 2 deletions miner/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ func (s *Stats) Counter() uint64 {
return atomic.LoadUint64(&s.counter)
}

func (s *Stats) humanizeRate(rate float64) string {
var hr string
func (s *Stats) humanizeRate(rate float64) (hr string) {
if rate/1000.0 <= 1.0 {
hr = fmt.Sprintf("%.1f Work/s", rate)
} else if rate/1000000.0 <= 1.0 {
Expand Down
Loading

0 comments on commit cfb4e31

Please sign in to comment.