go 实现简单的区块链

2018-10-06  本文已影响0人  Venture_Mark

安装依赖软件

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

$ go get github.com/gorilla/mux

$ go get github.com/joho/godotenv

实现区块链

新建 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"
)

定义区块

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

区块数组

定义 Blockchain 存储各个 Block

var Blockchain []Block

生成区块

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
}

生成区块的 HashcalculateHash 函数连接 IndexTimestampBPMPrevHashBlock 我们提供作为参数,并返回SHA256哈希为一个字符串。

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

验证区块

我们通过检查 Index 来确保它们按预期递增。我们也检查以确保我们 PrevHash 的确与 Hash 前一个区块相同。最后,我们希望通过在当前块上 calculateHash 再次运行该函数来检查当前块的散列。让我们写一个 isBlockValid 函数来完成所有这些事情并返回一个 bool

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
}

web 服务器

使用 run() 创建我们的服务器

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
}

makeMuxRouter 主要定义路由处理,当收到 GET 请求,就会调用 handleGetBlockchain 方法。当收到 POST 请求,就会调用 handleWriteBlock方法。

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

handleGetBlockchain 获取所有区块的列表信息。

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

handleWriteBlock 主要是生成新的区块。

type Message struct {
    BPM int
}

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
    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()

    newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
    if err != nil {
        respondWithJSON(w, r, http.StatusInternalServerError, m)
        return
    }
    if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
        newBlockchain := append(Blockchain, newBlock)
        replaceChain(newBlockchain)
        spew.Dump(Blockchain)
    }

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

}

func respondWithJSON(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)
}

主函数

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

    go func() {
        t := time.Now()
        genesisBlock := Block{0, t.String(), 0, "", ""}
        genesisBlock.Hash = calculateHash(genesisBlock)
        spew.Dump(genesisBlock)
        Blockchain = append(Blockchain, genesisBlock)
    }()
    log.Fatal(run())

}

godotenv.Load() 允许我们从 根目录的文件 .env 读取相应的变量。

genesisBlock 创建初始区块。

run() 启动 web 服务

启动web服务器

$ go run main.go

可以通过 Postman软件模拟网络请求。

通过 GET 访问 http://localhost:8080 可以获取区块链信息。

image

通过 POST 访问 http://localhost:8080 可以添加新的区块信息。

image

通过 curl 测试

GET

curl http://localhost:8080

源码下载

go实现简单的区块链

上一篇下一篇

猜你喜欢

热点阅读