使用Go语言实现自己的区块链

使用Go语言实现自己的区块链

  • 创建自己的区块链
  • 理解区块链中hash的生成
  • 添加新的区块
  • 通过web查询区块链
  • 写入一个新的区块

设置

需要安装的三方库是

go get github.com/davecgh/go-spew/spew

spew是格式化structsslices终端的输出

go get github.com/gorilla/mux

mux创建web server,查看区块链

引入

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "log"
    "net/http"
    "time"

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

数据模型

定义区块链每个区块的数据结构

type Block struct {
    Index     int
    Timestamp string
    BPM       int
    Hash      string
    PrevHash  string
}
  • Index: 区块链中每个记录的索引
  • Timestamp: 添加数据时的时间
  • BPM: 每分钟的心跳
  • Hash: 数据的SHA256的hash值
  • PrevHash: 前一个数据的hash值

区块链定义成一个 slice

var Blockchain []Block

创建数据的 hash

根据数据的索引Index,数据的时间Timestamp,数据的心跳BPM,和前一个数据的hash值 PrevHash来计算当前数据的hash值

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

创建数据的区块

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

    t := time.Now()

    newBlock.Index = oldBlock.Index + 1
    newBlock.Timestamp = t.String()
    newBlock.BPM = BPM
    newBlock.PrevHash = oldBlock.Hash
    newBlock.Hash = calculateHash(newBlock)
    return newBlock, nil
}

验证区块的合法性

验证一个区块是否是一个合法的区块,根据 Index Hash是否相等

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 replaceChain(newBlocks []Block) {
    if len(newBlocks) > len(Blockchain) {
        Blockchain = newBlocks
    }
}

Web Server

启动一个web server

func run() error {
    mux := makeMuxRouter()
    s := &http.Server{
        Addr:           ":8080",
        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
}

创建路由 GET-方法

获取所有的区块链的所有信息

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))
}

创建路由 POST-方法

在区块链中写入一个新数据

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
    var m Message

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

    newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
    if err != nil {
        responseWithJson(w, r, http.StatusInternalServerError, m)
        return
    }

    if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
        newBlockchain := append(Blockchain, newBlock)
        replaceChain(newBlockchain)
        spew.Dump(Blockchain)
    }
    responseWithJson(w, r, http.StatusCreated, newBlock)
}

封装Server Response

func responseWithJson(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
    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)
}

main

func main() {
    go func() {
        t := time.Now()
        baseBlock := Block{0, t.String(), 0, "", ""}
        spew.Dump(baseBlock)
        Blockchain = append(Blockchain, baseBlock)
    }()

    log.Fatal(run())
}

效果图

更多精彩内容
译自