美文网首页
go 实现简单的区块链

go 实现简单的区块链

作者: Venture_Mark | 来源:发表于2018-10-06 08:22 被阅读0次

    安装依赖软件

    $ go get github.com/davecgh/go-spew/spew
    
    $ go get github.com/gorilla/mux
    
    $ go get github.com/joho/godotenv
    
    
    • spew 在控制台中格式化输出相应的结果。

    • gorilla/mux 是编写web处理程序的流行软件包。

    • godotenv 可以从我们项目的根目录的 .env 文件中读取数据。

    实现区块链

    新建 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
    }
    
    
    • Index 是区块链中数据记录的位置

    • Timestamp 是自动确定的,并且是写入数据的时间

    • BPM 或每分钟跳动,是你的脉率

    • Hash 是代表这个数据记录的SHA256标识符

    • PrevHash 是链中上一条记录的SHA256标识符

    区块数组

    定义 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实现简单的区块链

    相关文章

      网友评论

          本文标题:go 实现简单的区块链

          本文链接:https://www.haomeiwen.com/subject/fqkqaftx.html