Используйте рабочую очередь для одновременной проверки проверочных значений, которые возвращают действительный хэш - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть блочная структура данных, которая определяется следующим образом:

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

Кто-нибудь знает, почему это происходит?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...