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

9/29/2018 Go区块链后端

使用Go语言实现自己的区块链创建自己的区块链理解区块链中hash的生成添加新的区块通过web查询区块链写入一个新的区块设置需要安装的三方库是go get github.com/davecgh/go-spew/spewspew是格式化structs和slices终端的输出go get github.c

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

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

# 设置

需要安装的三方库是

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

spew是格式化structsslices终端的输出

go get github.com/gorilla/mux
1

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"
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 数据模型

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

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

区块链定义成一个 slice

var Blockchain []Block
1

# 创建数据的 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)
}
1
2
3
4
5
6
7

# 创建数据的区块

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
}
1
2
3
4
5
6
7
8
9
10
11
12

# 验证区块的合法性

验证一个区块是否是一个合法的区块,根据 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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func replaceChain(newBlocks []Block) {
	if len(newBlocks) > len(Blockchain) {
		Blockchain = newBlocks
	}
}
1
2
3
4
5

# 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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 创建路由

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

# 创建路由 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))
}
1
2
3
4
5
6
7
8

# 创建路由 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 封装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)
}
1
2
3
4
5
6
7
8
9
10

# main

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

	log.Fatal(run())
}
1
2
3
4
5
6
7
8
9
10

效果图

更多精彩内容 (opens new window)
译自 (opens new window)