【go共识算法】-POW

pow介绍

工作量证明(PoW,Proof of Work)。
通过计算一个数值(nonce ),使得拼揍上交易数据后内容的 Hash 值满足规定的上限。在节点成功找到满足的Hash值之后,会马上对全网进行广播打包区块,网络的节点收到广播打包区块,会立刻对其进行验证。

如果验证通过,则表明已经有节点成功解迷,自己就不再竞争当前区块打包,而是选择接受这个区块,记录到自己的账本中,然后进行下一个区块的竞争猜谜。 网络中只有最快解谜的区块,才会添加的账本中,其他的节点进行复制,这样就保证了整个账本的唯一性。

假如节点有任何的作弊行为,都会导致网络的节点验证不通过,直接丢弃其打包的区块,这个区块就无法记录到总账本中,作弊的节点耗费的成本就白费了,因此在巨大的挖矿成本下,也使得矿工自觉自愿的遵守比特币系统的共识协议,也就确保了整个系统的安全。
参考黎跃春的翻译

pow优缺点

  • 优点:完全去中心化,节点自由进出;
  • 缺点:目前bitcoin已经吸引全球大部分的算力,其它再用Pow共识机制的区块链应用很难获得相同的算力来保障自身的安全;挖矿造成大量的资源浪费;共识达成的周期较长,不适合商业应用

go实现pow共识算法

代码出处

依赖软件

  • spew 在控制台中格式化输出相应的结果。
    $ go get github.com/davecgh/go-spew/spew
  • gorilla/mux 是编写web处理程序的流行软件包。
    $ go get github.com/gorilla/mux
  • godotenv 可以从我们项目的根目录的 .env 文件中读取数据。
    $ go get github.com/joho/godotenv

.env

ADDR=8080

main.go

package main

import (
        "crypto/sha256"
        "encoding/hex"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "os"
        "strconv"
        "strings"
        "sync"
        "time"

        "github.com/davecgh/go-spew/spew"
        "github.com/gorilla/mux"
        "github.com/joho/godotenv"
)

const difficulty = 1

type Block struct {
        Index      int
        Timestamp  string
        BPM        int
        Hash       string
        PrevHash   string
        Difficulty int
        Nonce      string
}

var Blockchain []Block

type Message struct {
        BPM int
}

var mutex = &sync.Mutex{}

func generateBlock(oldBlock Block, BPM int) Block {
    var newBlock Block

    t := time.Now()

    newBlock.Index = oldBlock.Index + 1
    newBlock.Timestamp = t.String()
    newBlock.BPM = BPM
    newBlock.PrevHash = oldBlock.Hash
    newBlock.Difficulty = difficulty

    for i := 0; ; i++ {
            hex := fmt.Sprintf("%x", i)
            newBlock.Nonce = hex
            if !isHashValid(calculateHash(newBlock), newBlock.Difficulty) {
                    fmt.Println(calculateHash(newBlock), " do more work!")
                    time.Sleep(time.Second)
                    continue
            } else {
                    fmt.Println(calculateHash(newBlock), " work done!")
                    newBlock.Hash = calculateHash(newBlock)
                    break
            }

    }
    return newBlock
}

func isHashValid(hash string, difficulty int) bool {
    //复制 difficulty 个0,并返回新字符串,当 difficulty 为2 ,则 prefix 为 00
    prefix := strings.Repeat("0", difficulty)
    // HasPrefix判断字符串 hash 是否包含前缀 prefix
    return strings.HasPrefix(hash, prefix)
}

func calculateHash(block Block) string {
    record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash + block.Nonce
    h := sha256.New()
    h.Write([]byte(record))
    hashed := h.Sum(nil)
    return hex.EncodeToString(hashed)
}

func isBlockValid(newBlock, oldBlock Block) bool {
    if oldBlock.Index+1 != newBlock.Index {
            return false
    }

    if oldBlock.Hash != newBlock.PrevHash {
            return false
    }

    if calculateHash(newBlock) != newBlock.Hash {
            return false
    }

    return true
}


func run() error {
    mux := makeMuxRouter()
    httpAddr := os.Getenv("ADDR")
    log.Println("Listening on ", os.Getenv("ADDR"))
    s := &http.Server{
            Addr:           ":" + httpAddr,
            Handler:        mux,
            ReadTimeout:    10 * time.Second,
            WriteTimeout:   10 * time.Second,
            MaxHeaderBytes: 1 << 20,
    }

    if err := s.ListenAndServe(); err != nil {
            return err
    }

    return nil
}

func makeMuxRouter() http.Handler {
    muxRouter := mux.NewRouter()
    muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
    muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
    return muxRouter
}

func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
    bytes, err := json.MarshalIndent(Blockchain, "", "  ")
    if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
    }
    io.WriteString(w, string(bytes))
}

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    var m Message

    decoder := json.NewDecoder(r.Body)
    if err := decoder.Decode(&m); err != nil {
            respondWithJSON(w, r, http.StatusBadRequest, r.Body)
            return
    }   
    defer r.Body.Close()

    //ensure atomicity when creating new block
    mutex.Lock()
    newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
    mutex.Unlock()

    if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
            Blockchain = append(Blockchain, newBlock)
            spew.Dump(Blockchain)
    }   

    respondWithJSON(w, r, http.StatusCreated, newBlock)

}

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
    w.Header().Set("Content-Type", "application/json")
    response, err := json.MarshalIndent(payload, "", "  ")
    if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("HTTP 500: Internal Server Error"))
            return
    }
    w.WriteHeader(code)
    w.Write(response)
}

func main() {
    err := godotenv.Load()
    if err != nil {
            log.Fatal(err)
    }   

    go func() {
            t := time.Now()
            genesisBlock := Block{}
            genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), "", difficulty, ""} 
            spew.Dump(genesisBlock)

            mutex.Lock()
            Blockchain = append(Blockchain, genesisBlock)
            mutex.Unlock()
    }() 
    log.Fatal(run())

}
    原文作者:jincheng828
    原文地址: https://segmentfault.com/a/1190000016514842
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞