99 lines
1.9 KiB
Go
99 lines
1.9 KiB
Go
package p2p
|
|
|
|
import (
|
|
"blockchain/chain"
|
|
"blockchain/config"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type miner struct {
|
|
hashDelay *time.Duration
|
|
onMined func(*chain.Block, time.Duration)
|
|
work chan *chain.Block
|
|
}
|
|
|
|
func newMiner(hashDelay *time.Duration, onMined func(*chain.Block, time.Duration)) *miner {
|
|
return &miner{
|
|
hashDelay: hashDelay,
|
|
onMined: onMined,
|
|
work: make(chan *chain.Block, 1),
|
|
}
|
|
}
|
|
|
|
// SubmitWork sends a new block template to the miner.
|
|
// If the miner is busy, the previous job is cancelled automatically.
|
|
func (m *miner) SubmitWork(block *chain.Block) {
|
|
// drain old work if any
|
|
select {
|
|
case <-m.work:
|
|
default:
|
|
}
|
|
m.work <- block
|
|
}
|
|
|
|
func (m *miner) Run(stop <-chan struct{}) {
|
|
var (
|
|
cancel chan struct{}
|
|
done chan bool
|
|
t0 time.Time
|
|
block *chain.Block
|
|
)
|
|
|
|
for {
|
|
select {
|
|
case block = <-m.work:
|
|
// cancel previous mining if running
|
|
if cancel != nil {
|
|
close(cancel)
|
|
<-done
|
|
}
|
|
cancel = make(chan struct{})
|
|
done = make(chan bool, 1)
|
|
t0 = time.Now()
|
|
go func(b *chain.Block, c chan struct{}, d chan bool) {
|
|
d <- mine(b, c, *m.hashDelay)
|
|
}(block, cancel, done)
|
|
|
|
case ok := <-done:
|
|
cancel = nil
|
|
done = nil
|
|
if ok {
|
|
m.onMined(block, time.Since(t0))
|
|
}
|
|
|
|
case <-stop:
|
|
if cancel != nil {
|
|
close(cancel)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// mine перебирает nonce пока хеш не начнётся с нужного кол-ва нулей.
|
|
func mine(block *chain.Block, cancel chan struct{}, hashDelay time.Duration) bool {
|
|
prefix := strings.Repeat("0", config.Default.Chain.Difficulty)
|
|
base := block.HashBase()
|
|
for {
|
|
select {
|
|
case <-cancel:
|
|
return false
|
|
default:
|
|
}
|
|
data := base + strconv.Itoa(block.Nonce)
|
|
h := sha256.Sum256([]byte(data))
|
|
block.Hash = hex.EncodeToString(h[:])
|
|
if strings.HasPrefix(block.Hash, prefix) {
|
|
return true
|
|
}
|
|
block.Nonce++
|
|
if hashDelay > 0 {
|
|
time.Sleep(hashDelay)
|
|
}
|
|
}
|
|
}
|