r语言error in match.fun(fun) :_Go语言200行写区块链源代码分析
Github上有一个Repo,是一个使用Go语言(golang),不到200行代码写的区块链源代码,准确的说是174行。原作者起了个名字是 Code your own blockchain in less than 200 lines of Go! 而且作者也为此写了一篇文章。https://medium.com/@mycoralhealth/code-your-own-blockchain-in-less-than-200-lines-of-go-e296282bcffc
这篇文章是一个大概的思路和代码的实现,当然还有很多代码的逻辑没有涉及,所以我就针对这不到200行的代码进行一个分析,包含原文章里没有涉及到的知识点,对Go语言,区块链都会有一个更深的认识。
所有的源代码都在这里:https://github.com/nosequeldeebee/blockchain-tutorial/blob/master/main.go
import ( "crypto/sha256" "encoding/hex" "encoding/json" "io" "log" "net/http" "os" "strconv" "sync" "time"
"github.com/davecgh/go-spew/spew" "github.com/gorilla/mux" "github.com/joho/godotenv")
在源代码的开头,是作者引入的一些包,有标准的,也有第三方的。像sha256,hex这些标准包是为了sha-256编码用的,其他还有启动http服务,打印日志的log,并发控制的sync,时间戳的time。
第三方包有三个,其中两个我都详细介绍过,相信大家不会陌生。
go-spew
是一个变量结构体的调试利器,可以打印出变量结构体对应的数据和结构,调试非常方便
gorilla/mux
是一个web路由服务,可以很简单的帮我们构建web服务。
不过目前用gin的比较多,也推荐使用gin https://github.com/gin-gonic/gin。
godotenv
是一个读取配置文章的库,可以让我们读取.env
格式的配置文件,比如从配置文件里读取IP、PORT等。不过目前配置文件还是推荐YAML和TOML,对应的第三方库是:
gopkg.in/yaml.v21https://github.com/BurntSushi/toml
既然要写一个区块链,那么肯定的有一个区块的实体,我们通过golang的struct
来实现。
// Block represents each 'item' in the blockchaintype Block struct { Index int Timestamp string BPM int Hash string PrevHash string}
Block
里包含几个字段:
Index 就是Block的顺序索引
Timestamp是生成Block的时间戳
BPM,作者说代表心率,每分钟心跳数
Hash是通过sha256生成的散列值,对整个Block数据的Hash
PrevHash 上一个Block的Hash,这样区块才能连在一起构成区块链
有了区块Block了,那么区块链就非常好办了。
// Blockchain is a series of validated Blocksvar Blockchain []Block
就是这么简单,一个Block数组就是一个区块链。区块链的构成关键在于Hash和PrevHash,通过他们一个个串联起来,就是一串Block,也就是区块链。因为相互之间通过Hash和PrevHash进行关联,所以整个链很难被篡改,链越长被篡改的成本越大,因为要把整个链全部改掉才能完成篡改的目的,这样的话,其他节点验证这次篡改肯定是不能通过的。
既然关键点在于Hash,所以我们要先算出来一个Block的数据的Hash,也就是对Block里的字段进行Hash,计算出一个唯一的Hash值。
// SHA256 hasingfunc calculateHash(block Block) string { record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash h := sha256.New() h.Write([]byte(record)) hashed := h.Sum(nil) return hex.EncodeToString(hashed)}
sha256
是golang内置的sha256的散列标准库,可以让我们很容易的生成对应数据的散列值。从源代码看,是把Block
的所有字段进行字符串拼接,然后通过sha256
进行散列,散列的数据再通过hex.EncodeToString
转换为16进制的字符串,这样就得到了我们常见的sha256
散列值,类似这样的字符串8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
。
Block的散列值被我们计算出来了,Block的Hash和PrevHash这两个字段搞定了,那么我们现在就可以生成一个区块了,因为其他几个字段都是可以自动生成的。
// create a new block using previous block's hashfunc generateBlock(oldBlock Block, BPM int) Block {
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}
因为区块链是顺序相连的,所以我们在生成一个新的区块的时候,必须知道上一个区块,也就是源代码里的oldBlock
。另外一个参数BPM
就是我们需要在区块里存储的数据信息了,这里作者演示的例子是心率,我们可以换成其他业务中想要的数据。
Index
是上一个区块的Index+1
,保持顺序;Timestamp
通过time.Now()
可以得到;Hash通过calculateHash
方法计算出来。这样我们就生成了一个新的区块。
在这里作者并没有使用POW(工作量证明)这类算法来生成区块,而是没有任何条件的,这里主要是为了模拟区块的生成,演示方便。
区块可以生成了,但是生成的区块是否可信,我们还得对他进行校验,不能随便生成一个区块。在比特币(BitCoin)中校验比较复杂,这里作者采用了简单模拟的方式。
// make sure block is valid by checking index, and comparing the hash of the previous blockfunc 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}
简单的对比Index
,Hash
是否是正确的,并且重新计算了一遍Hash
,防止被篡改。
到了这里,关于区块链的代码已经全部完成了,剩下的就是把区块链的生成、查看等包装成一个Web服务,可以通过API、浏览器访问查看。因为作者这里没有实现P2P网络,所以采用的是WEB服务的方式。
// create handlersfunc makeMuxRouter() http.Handler { muxRouter := mux.NewRouter() muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET") muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST") return muxRouter}
通过mux
定义了两个Handler,URL都是/
,但是对应的Method
是不一样的。
GET
方法通过handleGetBlockchain
函数实现,用于获取区块链的信息。
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))}
Blockchain
是一个[]Block
,handleGetBlockchain
函数的作用是把Blockchain
格式化为JSON字符串,然后显示出来。io.WriteString
是一个很好用的函数,可以往Writer
里写入字符串。更多参考 Go语言实战笔记(十九)| Go Writer 和 Reader
'POST'方法通过handleWriteBlock
函数实现,用于模拟区块的生成。
func handleWriteBlock(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json")
//使用了一个Mesage结构体,更方便的存储BPM var msg Message
//接收请求的数据信息,类似{"BPM":60}这样的格式 decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&msg); err != nil { respondWithJSON(w, r, http.StatusBadRequest, r.Body) return } defer r.Body.Close()
//控制并发,生成区块链,并且校验 mutex.Lock() prevBlock := Blockchain[len(Blockchain)-1] newBlock := generateBlock(prevBlock, msg.BPM)
//校验区块链 if isBlockValid(newBlock, prevBlock) { Blockchain = append(Blockchain, newBlock) spew.Dump(Blockchain) } mutex.Unlock()
//返回新的区块信息 respondWithJSON(w, r, http.StatusCreated, newBlock)
}
以上代码我进行了注释,便于理解。主要是通过POST发送一个{"BPM":60}
格式的BODY来添加区块,如果格式正确,那么就生成区块进行校验,合格了就加入到区块里;如果格式不对,那么返回错误信息。
用于控制并发的锁可以参考Go语言实战笔记(十七)| Go 读写锁
这个方法里有个Message
结构体,主要是为了便于操作方便。
// Message takes incoming JSON payload for writing heart ratetype Message struct { BPM int}
返回的JSON信息,也被抽取成了一个函数respondWithJSON
,便于公用。
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的Handler已经好了,现在我们要启动我们的Web服务了。
// web serverfunc run() error { mux := makeMuxRouter() //从配置文件里读取监听的端口 httpPort := os.Getenv("PORT") log.Println("HTTP Server Listening on port :", httpPort) s := &http.Server{ Addr: ":" + httpPort, Handler: mux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 <20, }
if err := s.ListenAndServe(); err != nil { return err }
return nil}
和原生的http.Server
基本一样,应该比较好理解。mux其实也是一个Handler,这就是整个Handler处理链。现在我们就差一个main
主函数来启动我们整个程序了。
//控制并发的锁var mutex = &sync.Mutex{}
func main() { //加载env配置文件 err := godotenv.Load() if err != nil { log.Fatal(err) }
//开启一个goroutine生成一个创世区块 go func() { t := time.Now() genesisBlock := Block{} genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), ""} spew.Dump(genesisBlock)
mutex.Lock() Blockchain = append(Blockchain, genesisBlock) mutex.Unlock() }() log.Fatal(run())
}
整个main
函数并不太复杂,主要就是加载env配置文件,开启一个go协程生成一个创世区块并且添加到区块链的第一个位置,然后就是通过run
函数启动Web服务。
一个区块链都有一个创世区块,也就是第一个区块。有了第一个区块我们才能添加第二个,第三个,第N个区块。创世区块因为是第一个区块,所以它是没有PrevHash
的。
终于可以运行了,假设我们设置的PORT是8080,现在我们通过go run main.go
启动这个简易的区块链程序,就可以看到控制台输出的创世区块信息。然后我们通过浏览器打开http://localhost:8080
也可以看到这个区块链的信息,里面只有一个创世区块。
如果我们要新增一个区块,通过curl或者postman,向http://localhost:8080
发送body格式为{"BPM":60}
的POST的信息即可。然后在通过浏览器访问http://localhost:8080
查看区块链信息,验证是否已经添加成功。
到这里,整个源代码的分析已经完了,我们看下这个简易的区块链涉及到多少知识:
sha256散列
字节到16进制转换
并发同步锁
Web服务
配置文件
后向式链表
结构体
JSON
……
等等,上面的很多知识,我已经在文章中讲解或者通过以前些的文章说明,大家可以看一下,详细了解。
推荐阅读
go语言的开源区块链代码都有哪些?欢迎留言补充
学习交流 Go 语言,扫码回复「进群」即可
站长 polarisxu
自己的原创文章
不限于 Go 技术
职场和创业经验
Go语言中文网
每天为你
分享 Go 知识
Go爱好者值得关注
r语言error in match.fun(fun) :_Go语言200行写区块链源代码分析相关推荐
- go语言的iota是什么意思_go语言基础之iota枚举
1.iota (在常量的时候,当成枚举使用) 示例1 package main import "fmt" func main() { //1.iota常量自动生成器,每个一行,自动 ...
- go net.conn读取请求头信息_go语言网络编程socket sever的实现
首先说一下socket,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket(百度百科). 图片来源于网络 其本质就是就是编程接口(API),对TCP.UDP的封装 ...
- .net httpwebrequest 并发慢_go语言并发之MPG模型
请记住go并发的真理:Do not communicate by sharing memory; instead, share memory by communicating. 不要以共享内存的方式来 ...
- gophp解释器_go语言环境搭建、基本使用
一.go语言介绍 1.诞生于 2009年,天然支持并发,很新,生态不完善 python:1989年,java:1990年.2005年,之前的语言,对并发支持不是特别好 2.google出的. pyth ...
- Go语言基础(一篇上手go语言基本语法)
Go简介 Go语言的创始人有三位,分别是图灵奖获得者.C语法联合发明人.Unix之父肯·汤普森(Ken Thompson).Plan 9操作系统领导者.UTF-8编码的最初设计者罗伯·派克(Rob P ...
- 区块链技术要用到什么开发语言?
回顾2009年到如今,区块链技术的火热程度是逐年增加.各大行业巨头也在疯狂布局,势必要上演一场"群雄逐鹿"的商业大战.那这场好戏什么时候上演?有人说就在最近这两三年,还有人甚至说就 ...
- 区块链开发如何选择底层平台和开发语言?
2019独角兽企业重金招聘Python工程师标准>>> 大多数人并不需要自己重新创建一套区块链,而是基于现有的区块链底层平台去开发自己的应用,对于类似加密算法. P2P技术.共识算法 ...
- 什么语言最适合开发区块链
区块链技术并不限制语言,换言之,几乎所有的语言都可以做区块链开发. 但是因为语言的特点不同,还是有些语言更适合做区块链开发. 最适合开发区块链的语言是C/C++语言和Go语言.因为区块链应用中有大量的 ...
- 区块链用哪种语言 Java_区块链开发用什么语言呢?区块链与编程语言的关系详解...
原标题:区块链开发用什么语言呢?区块链与编程语言的关系详解 区块链的概念就随着比特币的大热开始逐渐进入公众视野,比特币的拥有者为了使其创造更高的财富,开始疯狂炒作,因而引发购买比特币大浪潮.然而等到比 ...
最新文章
- 怎么确定迭代器后面还有至少两个值_如何理解Python中的可迭代对象、迭代器和生成器
- 欢迎来到“现实”世界,bilibili!
- SAP Cloud for Customer upselling的前台实现
- C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理
- CodeForces 1361E James and the Chase(dfs + 结论)
- 华为鸿蒙宣传悟空视频_华为自研鸿蒙系统定档9月?《悟空》微电影透露玄机...
- iCHM Reader for Mac(chm格式文件阅读器)
- 命令行工具恢复文件 foremost 和 extundelete 简介
- 创业与投资 - 谈谈融资这个事儿(上)
- 软件项目管理实验一补充
- CAJ是什么格式的文档,怎么打开和使用?
- 涨姿势 之 Sourcetree 显示头像
- 回射程序改进3——消息的群发
- 2021-3-21-第三周
- 数据仓库知识与实战——电信运营商数仓建模
- 绿盟科技网络安全威胁周报2017.15 建议关注方程式组织泄漏大量针对Windows攻击工具...
- Word代做,线上兼职,月入过万
- 2022跨年演讲分享
- Jetson TX2实现EfficientDet推理加速(一)
- 网络安全:使用各类编码混淆攻击
热门文章
- 360发布穿戴设备“儿童卫士”手环
- C#如何使用httpwebrequest通过代理访问网页
- 使用vue-CLI构建vue工程项目
- Android vector矢量图应用实例
- Android 集成微信分享2,实现微信分享
- mongoose 查询 find 指定字段
- 计算机专业英语教程计算机硬件翻译,计算机专业英语教程第5版翻译
- mysql5.1win7_免安装版mysql5.1.57在win7下成功配置
- c语言插件实现原理,C语言实现插件机制
- eclipse中git解决冲突