NYC's Blog - 区块链 http://niyanchun.com/tag/block-chain/ 区块链学习(1) http://niyanchun.com/blockchain-learning-part1.html 2018-03-20T22:17:00+08:00 区块链那么火,作为一个有理想的工程师,不去学习下实在说不过去。什么是区块链(BlockChain)?我觉得这篇文章讲的通俗易懂,推荐一下:《1分钟了解区块链的本质》,这里摘抄一下总结:区块是一块存储空间,可以存储数据区块链不但像链表一样把区块串起来,还有约定了一系列的方法管理这些数据,所以它是存储系统区块链有很多节点,每个节点都保存了全部的数据,所以它是高可用的每一个中心节点都可以生成区块,并写入数据,所以每一个点都是中心节点,或者说区块链是去中心化的,要想控制整个系统,必须控制一半以上的节点,才能控制投票,于是这个系统没有管理员综上,区块链实际上是一个没有管理员的,去中心化的,每个节点都拥有全部数据的分布式存储系统。只要你愿意,你随时可以成为区块链中的一个节点,并参与区块的生成与写入,比特币就是基于这个分布式存储上的电子货币。由于节点很多,很多数据需要同步,这个系统的存储容量其实不大,目前全球存储比特币的区块链也就100多G。好,什么是区块链就到这里,网上有很多资料也可以查阅。下面我们通过不到200行的Go代码实现一个简单的区块链,来近距离的感受一下简易版的区块链。当然,这个不是我弄出来的,是国外一个大牛。推荐读原文:《Code your own blockchain in less than 200 lines of Go!》。这里就不全文翻译了,代码和原理都很简单,这里简单记录一下流程以及关键点。建立数据模型:定义区块链以及每个节点。// Block in Blockchain type Block struct { Index int // 节点在区块链中的位置 Timestamp string // 数据写入的时间戳 BPM int // 每分钟心跳数,beats per minute Hash string // 本节点数据哈希值 PrevHash string // 前一个节点哈希值 } // BlockChain is Block slice var BlockChain []Block这里区块链每个节点的记录的数据就是BPM,其它说明在注释中都有,不再赘述。数据模型如下图所示: 增加一些基本操作函数:(1)生成新节点(2)校验节点合法性(3)多个链时如何处理。// 计算节点哈希值 func calculateHash(block Block) string { // 使用节点除`Hash`字段之外的其他字段计算本节点哈希值 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.BPM = BPM newBlock.Timestamp = t.String() newBlock.PrevHash = oldBlock.Hash newBlock.Hash = calculateHash(newBlock) return newBlock, nil } // 判断节点合法性 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 } }这里的函数实现都非常简单,是个合格的程序员都看得懂。说一下最后一个函数replaceChain,对于分布式系统而言,数据一致性一直是个挑战,区块链作为一个分布式存储自然也不例外。这里采取的策略比较简单,遇到多个链的时候,以最长的链为准,如下图所示: 实现一个WebServer,这个其实和区块链没啥关系,只是为了方便使用和显示。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("/", handleWriteBlockChain).Methods("POST") return muxRouter } 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)) } func handleWriteBlockChain(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.StatusInternalServerError, 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) }这就是一个非常简单的Go版本的Webserver了,其实还可以不使用gorilla/mux包,就用Go原生的http包就行,当然这些都不是重点。最后来个main函数就大功告成了。func main() { go func() { t := time.Now() genesisBlock := Block{0, t.String(), 0, "", ""} spew.Dump(genesisBlock) BlockChain = append(BlockChain, genesisBlock) }() log.Fatal(run()) }main函数里面我们先构造了一个初始节点。因为原文已经讲的比较清楚了,而且代码实现也非常简单,所以这里就不再赘述啥了,一切都在代码中。其实,如果仅从这个例子看的话,区块链非常像链表。当然还是非常不一样的。原文是一个系列,我也还没看完,后面如果感觉有值得分析的部分会转载过来。还是那句话,强烈推荐看原文。PS:完整代码在这里。