blockchain sandbox: UTXO chain, p2p mining, attacker node
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user