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:完整代码在这里。