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