首页 体育世界 正文

东革阿里,只用200行go代码写自己的块链!,霞浦

“用不到200行 Go 代码就能完成一个自己的区块链!” 听起来有意思吗?有什么能比开发一个自己的区块链更好的学习实践办法呢?那咱们就一起来实践下!

咱们选用人类安静时的心跳数据(BPM心率)作为这篇文章中的示例数据。让咱们先来核算一下你一分钟内的心跳数,然后记下来,这个数字或许会在接下来的内容中用到。

经过本文,你将能够做到:

创立自己的区块链

了解 hash 函数是怎么坚持区块链的完好性

怎么发明并增加新的块

多个节点怎么竞赛生成块

经过浏览器来检查整个链

一切其他关于区块链的基础常识

可是,关于比方作业量证明算法(PoW)以及权益证明算法(PoS)这类的共同算法文章中将不会触及。一起为了让你更清楚得检查区块链以及块的增加,咱们将网络交东革阿里,只用200行go代码写自己的块链!,霞浦互的进程简化了,关于 P2P 网络比方“全网播送”这个进程等内容将在下一篇文章中补上。

让咱们开端吧!

设置

咱们假定你现已具有一点 Go 言语的开发经历。在装置和装备 Go 开发环境后之后,咱们还要获取以下一些依靠:

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

spew 能够协助咱们在 console 中直接检查 struct 和 slice批毛 这两种数据结构。

go get github.com/gorilla/mux

Gorilla 的 mux 包十分盛行, 咱们用它来写 web handler。

go get github.com/joho/godotenv

godotenv 能够协助咱们读取项目根目录中的 .env 装备文件,这样咱们就不必将金山夜话 http port 之类的装备硬编码进代码中了。比方像这样:

ADDR=8080

接下来,咱们创立一个 main.go 文件。之后咱们的东革阿里,只用200行go代码写自己的块链!,霞浦大部分作业都环绕这个文件,让我开端编码吧!

导入依靠

咱们将一切的依靠包以声明的方法导入进去:

package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/广州飞威网络科技有限公司json"
"io"
"log"
"net/http"
"os"
"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 清楚明了便是块生成时的时刻戳

Hash 是这个块经过 SHA256 算法生成的散列值

PrevHash 代表前一个块的 SHA256 散列值

BPM 每分钟心跳数,也便是心率。还记得文章最初说到的吗?

接着,咱们再界说一个结构表明整个链,最简略的表明方法便是一个 Block 的 slice:

var Blockchain []Bl我的ps伙伴ock

咱们运用散列算法(SHA256)来确认和保护链中块和块正确的次序,保证每一个块的 PrevHash 值等于前一个块中的 Hash 值shinee,这样就以正确的块次序构建出链:

散列和生成块

咱们为什么需求散列?主要是两个原因:

1) 在节约空间的条件下去仅有标识数据。散列是用整个块的数据核算得出,在咱们的比方中,将整个块的数据经过 SHA256 核算成一个定长不行假造的字符串。

2) 保持链的完好性。经过存储前一个块的散列值,咱们就能够保证每个块在链中的正确次序。任何对数据的篡改都将改动散列值,一起也就破坏了链。以咱们从事的医疗健康范畴为例,比方有一个歹意的第三方为了调整“人寿险”的价格,而修改了一个或若干个块中的代表不健康的 BPM 值,那么整个链都变得不行信了。

咱们接着写一个函数,用来核算给定的数据的 SHA256 散列值:

func calculateHash(block Block) string {
record := string(block.Index) + bloc东革阿里,只用200行go代码写自己的块链!,霞浦k.Timestamp + string(block.BPM) + block.PrevHash
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}

这个 calculateHash 函数承受一个块,经过块谷素全中的 Index,Timestamp,BPM,以及 PrevHash 值来核算出 SHA256 散列值。接下来咱们就能便携一个生成块的函数:

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 = calculateHas羊杂汤的做法h(newBlock)
return newBlock, nil
}

其间,Index 是从给定的前一块的 Index 递加得出,时刻戳是直接经过 time.Now() 函数来取得的,Hash 值经过前面的 calculateHash 函数核算得出,PrevHash 则是给定的前一个块的 Hash 值。

校验块

搞定了块的生成,接下来咱们需求有函数帮咱们康清明判别一个块是否有被篡改。检查 Index 来看这个块是否正确得递加,检查 PrevHash 与前一个块的 Ha东革阿里,只用200行go代码写自己的块链!,霞浦sh 是否共同,再来经过 calculateHash 检查当时块的 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 页面或许是最合适的方法!

Web 效劳

我猜你必定对传统的 web 效劳及开发十分了解,所以这部分你必定一看就会。

凭借 Gorilla/mux 包,咱们先写一个函数来初始化咱们的 纸杯蛋糕的做法web 效劳:

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
}

其间的端口号是经过前面说到的 .env 来取得,再增加一些根本的装备参数,这个 web 效劳就现已能够 listen and serve 了!

接下来咱们再来界说不同 endp东革阿里,只用200行go代码写自己的块链!,霞浦oint 以及对应的 handler。例如,对“/”的 GET 恳求咱们能够检查整个链,“/”的 POST 恳求能够创立块。

func makeMuxRouter() http.Handler {
muxRouter := mux.NewRouter()
绅士的品质muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
muxRouter.HandleFunc("/", handleWriteBloc夏河骂吴京k).Methods("POST")
return muxRouter
}

GET 恳求的 handler:

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

为了简化,咱们直接以 JSON 格局回来整个链,你能够在浏览器中拜访 localhost:8080 或许 127.0.0.1:8080 来检查(这儿的8080便是你在 .env 中界说的端口号 ADDR)。

POST 恳求的 handler 略微有些杂乱,咱们先来界说一下 POST 恳求的 payload:

type Message struct {
BPM int
}

再看看 handler 苏妙龄的完成:

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.StatusBadReque东革阿里,只用200行go代码写自己的块链!,霞浦st, r.Body)
return
}
defer r.Body.Close()
newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
i苹组词f 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)
}

咱们的 POST 恳求体中能够运用上面界说的 pay吉杰load,比方:

{"BPM":75}

还记得前面咱们写的 generateBlock 这个函数吗?它承受一个“前一个块”参数,和一个 BPM 值。POST handler 承受恳求后就能取得恳求体中的 BPM 值,接着凭借生成块的函数以及校验块的函数就能生成一个新的块了!

除此之外,你也能够:

运用spew.Dump 这个函数能够以十分漂亮和便利阅览的方法将 struct、slice 等数据打印在控制台里,便利咱们调试。

测验 POST 恳求时,能够运用 POSTMAN 这个 chrome 插件,比较 curl它更直观和便利。

POST 恳求处理完之后,不管创立块成功与否,咱们需求回来客户端一个呼应:

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

快要功德圆满了

接下来,咱们把这些关于区块链的函数,web 效劳的函数“拼装”起来:

func main() {
err := godotenv.Load()
if err != nil {
log.Fatal(err)
}
go func() {
t := time.Now()
genesisBlock := Block{0, t.String(), 0, "", ""}
spew.Dump(ge1寸相片尺度nesisBlock)
Bloc东革阿里,只用200行go代码写自己的块链!,霞浦kchain = append(Blockchain, genesisBlock)
}()
log.Fatal(run())
}

这儿的 genesisBlock (创世块)是 main 函数中最重要的部分,经过它来初始化区块链,究竟第一个块的 PrevHash 是空的。

哦耶!完成了

你们能够从这儿取得完好的代码:[Github repo](https://github.com/mycoralhealth/blockchain-tutorial/blob/master/main.go)

让咱们来发动它:

go run main.go

在终端中,咱们能够看到 web 效劳器谌天舒发动的日志信息,而且打印出了创世块的信息:

接着咱们翻开浏览器,拜访 localhost:8080 这个地址,咱们能够看到页面中展现了当时整个区块链的信息(当然,现在只要一个创世块):

接着,咱们再经过 POSTMAN 来发送一些 POST 恳求:

改写方才的页面,现在的链中多了一些块,正是咱们方才生成的,一起你们能够看到,块的次序和散列值都正确。

下一步

刚刚咱们完成了一个自己的区块链,尽管很简略(陋),但它具有块生成、散列核算、块校验等根本才能。接下来你就能够持续深化的学习区块链的其他重要常识,比方作业量证明、权益证明这样的共同算法,或许是智能合约、Dapp、侧链等等。

现在这个完成中不包含任何 P2P 网络的内容,咱们会在下一篇文章中弥补这部分内容,当然,咱们鼓舞你在这个基础上自己实践一遍!