У меня есть блочная структура данных, которая определяется следующим образом:
package blockchain
import(
"crypto/sha256"
"encoding/hex"
"strconv"
"fmt"
//"strings"
)
type Block struct {
PrevHash []byte
Generation uint64
Difficulty uint8
Data string
Proof uint64
Hash []byte
}
// Create new initial (generation 0) block.
func Initial(difficulty uint8) Block {
b := new(Block)
b.PrevHash = make([]byte, 32)
b.Generation = 0
b.Difficulty = difficulty
b.Data = ""
return *b
}
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Create new block to follow this block, with provided data.
func (prev_block Block) Next(data string) Block {
nextblock := new(Block)
nextblock.PrevHash = prev_block.Hash
nextblock.Generation = prev_block.Generation + 1;
nextblock.Difficulty = prev_block.Difficulty
nextblock.Data = data
return *nextblock
}
func (blk Block) CalcString() string {
prevHash := hex.EncodeToString(blk.PrevHash)
generation := strconv.Itoa(int(blk.Generation))
difficulty := strconv.Itoa(int(blk.Difficulty))
data := blk.Data
proof := strconv.Itoa(int(blk.Proof))
hashstring := prevHash + ":" + generation + ":" + difficulty + ":" + data + ":" + proof
return hashstring
}
// Calculate the block's hash.
func (blk Block) CalcHash() []byte {
prevHash := hex.EncodeToString(blk.PrevHash)
generation := strconv.Itoa(int(blk.Generation))
difficulty := strconv.Itoa(int(blk.Difficulty))
data := blk.Data
proof := strconv.Itoa(int(blk.Proof))
hashstring := prevHash + ":" + generation + ":" + difficulty + ":" + data + ":" + proof
aStringToHash := []byte(hashstring)
sha256Bytes := sha256.Sum256(aStringToHash)
return sha256Bytes[:]
}
// Is this block's hash valid?
func (blk Block) ValidHash() bool {
blk.Hash = blk.CalcHash()
hash := blk.Hash
hashstring := Reverse(hex.EncodeToString(hash))
goalcounter := 0
goal := blk.Difficulty/uint8(4)
fmt.Println(hashstring)
for i := range hashstring{
if hashstring[i] == '0'{
goalcounter = goalcounter + 1
}else{
break
}
}
if goal == uint8(goalcounter){
return true
}else if uint8(goalcounter) == 32 && blk.Generation == 0{
return true
}else{
return false
}
}
// Set the proof-of-work and calculate the block's "true" hash.
func (blk *Block) SetProof(proof uint64) {
blk.Proof = proof
blk.Hash = blk.CalcHash()
}
Данные хешируются криптографической хеш-функцией.По сути, каждый блок содержит хэш предыдущего блока (кроме первого блока), текущую генерацию (+1 от начала), сложность (сложность d означает, что блок действителен только в том случае, если его хэш заканчивается d нулевыми битами), значение доказательства и его текущий хеш.
По сути, мы создали структуру данных work_queue для того, чтобы как можно быстрее вычислить доказательство работы.Он определяется следующим образом:
package work_queue
type Worker interface {
Run() interface{}
}
type WorkQueue struct {
Jobs chan Worker
Results chan interface{}
Halt chan int
numberWorkers int
}
// Create a new work queue capable of doing nWorkers simultaneous tasks, expecting to queue maxJobs tasks.
func Create(nWorkers uint, maxJobs uint) *WorkQueue {
q := new(WorkQueue)
q.Jobs = make(chan Worker, maxJobs)
q.Results = make(chan interface{})
q.Halt = make(chan int, maxJobs)
for i:=uint(0); i<nWorkers; i++{
go q.worker()
}
return q
}
// A worker goroutine that processes tasks from .Jobs unless .StopRequests has a message saying to halt now.
func (queue WorkQueue) worker() {
isTrue := true
for isTrue{
task := <-queue.Jobs
result := task.Run()
queue.Results <-result
if len(queue.Halt) > 0{
isTrue = false
return
}
}
}
func (queue WorkQueue) Enqueue(work Worker) {
queue.Jobs <- work
}
func (queue WorkQueue) Shutdown() {
for i := 0; i< int(queue.numberWorkers); i++{
queue.Halt <- 1
}
}
Теперь я хочу создать метод MineRange, который добывает диапазон значений доказательства (чтобы определить, какой из них дает действительный хэш), разбивая его на куски и проверяя«рабочие» блокируются одновременно в рабочей очереди, которая должна возвращаться вскоре после того, как будет найден результат.
У меня есть следующий код:
package blockchain
import (
"work_queue"
//"fmt"
)
type miningWorker struct {
work_queue.Worker
initial uint64
final uint64
block Block
}
type MiningResult struct {
Proof uint64 // proof-of-work value, if found.
Found bool // true if valid proof-of-work was found.
}
func (miner miningWorker) Run() interface{}{
miningResult := new(MiningResult)
initial := miner.initial
final := miner.final
block := miner.block
for i := initial; i<=final; i++{
block.SetProof(i)
if block.ValidHash() == true{
miningResult.Proof = i
miningResult.Found = true
return *miningResult
}
}
miningResult.Proof = 0
miningResult.Found = false
return *miningResult
}
// Mine the range of proof values, by breaking up into chunks and checking
// "workers" chunks concurrently in a work queue. Should return shortly after a result
// is found.
func (blk Block) MineRange(start uint64, end uint64, workers uint64, chunks uint64) MiningResult {
miningresult := new(MiningResult)
chunkslen := end/chunks
workqueue := work_queue.Create(uint(workers), uint(chunks))
for i := start; i<=end; i = i+chunkslen{
go func(begin uint64){
miner := new(miningWorker)
if miner.initial + chunkslen > end{
miner.initial = end
}else{
miner.initial = miner.initial + chunkslen
}
miner.block = blk
workqueue.Enqueue(miner)
}(i)
}
isValid := true
for isValid{
results := <-workqueue.Results
*miningresult = results.(MiningResult)
if miningresult.Found{
workqueue.Shutdown()
isValid = false
return *miningresult
}
}
return *miningresult
}
// Call .MineRange with some reasonable values that will probably find a result.
// Good enough for testing at least. Updates the block's .Proof and .Hash if successful.
func (blk *Block) Mine(workers uint64) bool {
reasonableRangeEnd := uint64(4 * 1 << blk.Difficulty) // 4 * 2^(bits that must be zero)
mr := blk.MineRange(0, reasonableRangeEnd, workers, 4321)
if mr.Found {
blk.SetProof(mr.Proof)
}
return mr.Found
}
Но когда я пытаюсь проверить это следующим образом:
b := blockchain.Initial(7)
b.Mine(1)
fmt.Println(b.Proof, hex.EncodeToString(b.Hash))
Ничего не работает.Он должен вернуть следующее:
385 379bf2fb1a558872f09442a45e300e72f00f03f2c6f4dd29971f67ea4f3d5300
Кто-нибудь знает, почему это происходит?