5 分钟 GET Blockchain - 开发区块链 开发 BlockChain
本文攻略:解决区块链开发难题,学习Tendermint,自己搭建区块链
推荐玩家等级:技术新手、学生党、初级码农
先阅读本文解锁技能:
一点命令行基础一点GO基础:3天包学一点区块链知识:5分钟学区块链-关于区块链的一切All About Blockchain
练习本文的推荐设备:
操作系统:macOS Sierra(Windows没问题)IDE:VSCode,并自动安装一批官方推荐的GO插件Restful API工具:Paw(非必需,浏览器也可以)
第 1 部分:什么是 Tendermint
上一篇我们引用了哥打麻将联合记账的Rebs、Jack Ma、Pone Ma和强东栗子,这次PO提倡他们的手写记账太原始了,所以他想写一个区块链记账APP这四个人,让他们可以告别手写书籍。
既然没有重新发明轮子这回事,我想介绍一个叫Tendermint(以下简称TM)的区块链轮子。如果有玩家听说过以太坊的话,这个What Workshop有一个分支,Ethermint,它是基于TM开发的。无论如何,PO所有者只知道如何编码而不是投机硬币。他真的不明白这是什么研讨会。
好,我们先来了解一下TM的原理,因为真的没有什么UI可以可视化让玩家一眼看懂。 TM主要由两部分组成:
Tendermint Core:区块链共识引擎。它负责两件事:节点之间同步有序的数据传输,以及拜占庭共识机制的实现。 ABCI:区块链应用程序接口。它被设计成一组带有接口规范的协议,使得区块链应用逻辑可以用多种语言实现。
那他们两个在做什么,我看了半天还是不明白。可以先记住它们。 PO高手第一次看原稿的感觉和玩家一样。
第 2 部分:Tendermint 安装和运行
1.官方安装指南
让我们忽略更多的理论,卷起袖子做一个程序员我最喜欢做的事情,先下载轮子,进入命令行:
go get github.com/Masterminds/glide
go get github.com/tendermint/tendermint/cmd/tendermint
下载过程比较长,这个轮子比较大(可能需要科学上网),下载安装完成后进入命令行:
cd $GOPATH/src/github.com/tendermint/tendermint
glide install
go install ./cmd/tendermint
安装过程也比较长。安装完成后,验证是否安装成功。输入命令行:
tendermint version
abci-cli version
截至本文发表,tendermint版本0.15.0,abci-cli版本0.9.0
2.跑官方栗子
官方文档提供了两个栗子,之前的安装已经集成:
dummy:一个简单的键值存储区块链应用程序,使用有点像 Redis 或 ElasticSearch。 counter:一个简单的计数器区块链应用,写入区块的数字必须递增,否则不会被区块接受。
这里简单介绍一下栗子dummy,首先启动区块链应用,进入命令行:
abci-cli dummy
然后启动 TM ,命令行输入:
tendermint init
tendermint node
如果顺利的话,可以看到abci和tendermint这两个程序是连通的(Connect),并且tendermint会像心跳一样每秒提交一个空块。接下来,我们准备编写一个包含大量内容的新块。输入命令行:
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
在我们的区块链中有一个记录“abcd”的区块,另一个记录“name=satoshi”的区块,如果需要查询区块链的内容,输入命令行:
curl -s 'localhost:46657/abci_query?data="name"'
curl是Linux的常用工具,玩家可以通过网页浏览器直接输入localhost:46657/abci_query?data="name"
3. 探索官方工具
最后,让我们关闭刚刚运行的dummy application和tendermintTECO区块链能做吗,重新进入命令行运行dummy:
abci-cli dummy
再次输入命令行:
abci-cli console
那么这两者是连通的(Connect),这时候很多玩家都会被这两个叫abci的家伙搞糊涂了,怎么会互相连通,abci是什么?他做了什么?让我们通过 PART 3 重新组织我们刚刚在区块链中所做的事情。
第 3 部分:重新理解 Tendermint
PO 首先列出了官方文档中最常用和最容易混淆的术语:
ABCI ServerABCI ClientTendermint CoreABCI ApplicationABCI App
然后总结三个术语帮助理解,以下约定统一使用这三个术语:
ABCI:区块链内部,ABCI Server,启动后加载ABCI Application/App,为Tendermint提供Socket服务,服务地址:tcp://localhost:46658Tendermint:区块链内部,ABCI Client/Tendermint Core,启动后,使用 ABCI 提供的 Socket 服务创建 3 个连接(Connect),同时会向区块链外的客户端提供 HTTP 服务,服务地址::46657Client:区块链外,真正的客户端,读写权限通过 Tendermint 提供的服务进行区块链
或者把栗子一个动作,假设强哥用的是我写的区块链记账APP(iOS开发):
Client是区块链记账APP,强哥记录了一个新的Data 'Jack Pony $10',然后save Client会发起请求:46657/broadcast_tx_commit?tx="Jack Pony $10" Tendermint收到tx="Jack Pony $10 "并通过Socket向ABCI发送指令CheckTx:"Jack Pony $10"ABCI 在我写的ABCI App(用GO开发)中运行CheckTx方法,作用是验证“Jack Pony $10”是否符合数据规范如果不符合,它将通知 Temdermint 拒绝此 tx。假设通过 ABCI CheckTx 并通知 TendermintTendermint 将“Jack Pony $10”临时存储在 mempool 中,并通过 P2P 网络将这个 tx 复制到其他 Tendermint 节点(由 Jack Ma、Pony Ma 和 Rebs 运行的 Tendermint 节点)。 Jack Pony $10" 拜占庭共识投票为本次交易,所有 4 十个 dermint 节点都参与其中。投票过程分为三轮,第一轮预投票(PreVote),第二轮预提交(PreCommit ) 超过 2/3 批准后,最后一轮正式提交(Commit)超过 2/3 批准(Commit)Tendermint 提交(Commit)),依次向 ABCI 发送指令:BeginBlock -> DeliverTx * n 次-> EndBlock -> Commit,大致意思是:快速接受新区域 -> 接受区块内容 * n 块 -> 区块内容已被接受 -> 提交 上链TECO区块链能做吗,提交成功后 ABCI 会通知 Tendermint 有区块链中多了一个区块,记录了一笔交易“Jack Pony $10”。当然我们也可以记录n个项目,然后保存,这个区块会记录n个交易。
最后我总结几点:
回到刚才的abci-cli,当他运行'abci-cli dummy'的时候,其实是在运行ABCI。他运行'abci-cli console'的时候,其实是在运行Tendermint,所以一起运行之后,会建立一个Socket连接(Connect)Tendermint和ABCI之间有3个Socket连接:1个用于验证数据(CheckTx命令) , 1个为查询数据(Query command),1个为共识数据(BeginBlock, DeliverTx, EndBlock, Commit command) Tendermit提供给Client的HTTP接口很多,浏览器访问:46657都知道,玩家也可以看官方亲自提交文件
第 4 部分:开发区块链
终于到了编码部分,我们先梳理一下项目结构:
ClientApp:区块链记账APP,可实现任何语言、任何平台(iOS/Android/H5/other)。在这个版本中,我们使用 GO 做最简单的控制台命令行实现,甚至没有 UI 界面。 . TendermintApp:区块链服务程序,包括 Tendermint 和 ABCI。 ABCI:因为是接口协议,所以可以设计成任何语言实现。我们使用 GO 语言。开发主要是实现 CheckTx、DeliverTx、Commit 等 ABCI 指令的具体逻辑。 Tendermint:几乎不需要开发。如前所述,它为我们做了两件事,P2P网络同步有序传输数据和拜占庭共识引擎。我们可以在这里运行它。
接下来正式开始代码,主要讲解一些核心逻辑:
1. 客户端应用程序
模拟5个block,每个block Block记录10条记录,将这些记录转成json提交给Tendermint
blocksNumber := 5 // how many blocks
transactionsPerBlock := 10 // how many transactions in each block
players := []string{"Lei", "Jack", "Pony", "Richard"} // 4 players
random := rand.New(rand.NewSource(time.Now().UnixNano()))
json := jsoniter.ConfigCompatibleWithStandardLibrary
for i := 0; i < blocksNumber; i++ {
time.Sleep(time.Second * 1)
transactions := []controllers.Transaction{}
for j := 0; j < transactionsPerBlock; j++ {
from := players[random.Intn(len(players))]
to := players[random.Intn(len(players))]
for from == to {
to = players[random.Intn(len(players))]
}
btc := float32(random.Intn(10) + 1)
tran := controllers.Transaction{
From: from,
To: to,
Bitcoin: btc,
}
_, _ = tran.Create()
transactions = append(transactions, tran)
}
bytes, _ := json.Marshal(&transactions)
data := strings.Replace(string(bytes), "\"", "'", -1)
tx := data
// tmAsync(tx)
tmCommit(tx)
}
func tmCommit(tx string) {
url := "http://localhost:46657/broadcast_tx_async?tx=\"" + tx + "\""
txHandle(url)
}
2.@ > Tendermint
从命令行运行已定义的 shell 脚本并将结果打印到控制台和日志文件
f, err := os.Create("logs/tendermint.log")
if err != nil {
fmt.Println("Tendermint log init error:", err)
}
multiWriter := io.MultiWriter(f, os.Stdout)
go func() {
cmd := exec.Command("bash", "-c", "sh run-tm.sh")
cmd.Stdout = multiWriter
cmd.Start()
}()
run-tm.sh 脚本内容:
echo tendermint start
tendermint init
tendermint unsafe_reset_all
tendermint node --consensus.create_empty_blocks=false
echo tendermint end
unsafe_reset_all 每次都会重置本地区块链数据,仅供开发使用共识。 create_empty_blocks的作用是关闭Tendermint自带的每秒生成新空块的功能
3.ABCI
实现ABCI命令接口,这里我们直接使用官方的栗子dummy应用,保存提交的json记录,并在各个实现接口打印日志(限于篇幅,长代码省略)
func (app *DummyApplication) CheckTx(tx []byte) types.ResponseCheckTx {
lib.Log.Debug("CheckTx")
lib.Log.Notice(string(tx))
return types.ResponseCheckTx{Code: code.CodeTypeOK}
}
func (app *DummyApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
lib.Log.Debug("DeliverTx")
lib.Log.Notice(string(tx))
// ...
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
}
func (app *DummyApplication) Commit() types.ResponseCommit {
lib.Log.Debug("Commit")
// ...
lib.Log.Debug("Commit Hash", hash)
return types.ResponseCommit{Code: code.CodeTypeOK, Data: hash}
}
func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
lib.Log.Debug("Query")
// ...
}
4. 编译
我们已经编写好了编译脚本,进入命令行:
sh make.sh
编译脚本的本质是从一个正在运行的程序中编译出来的:
go build ./TendermintApp/ABCIServer/
go build ./TendermintApp/ABCIClient/
go build -o Client ./ClientApp/
5.测试
在激动人心的时刻,我们已经离自制的区块链很近了。
首先运行ABCI,命令行输入:
./ABCIServer
然后运行Tendermint,命令行输入:
./ABCIClient
可以看到Tendermint和ABCI已经有3次连接(Connect)Socket握手,说明整个区块链服务已经准备好了,最后我们只需要使用Client进入区块链就可以了,写入数据,进入命令行:
./Client
命令理解为使用记账APP记录5页,每页10条记录,然后点击保存后发送到区块链。
附上运行结果图,大致可以看出三个程序都运行成功了
最后,我们创建了一个区块链,共有 7 个区块。区块 2 到 6 各有 10 条分类账记录。区块 1 和 7 是系统创建的空区块。
空块呢?原 PO 所有者也认为是 bug,到官方开发 issue 中了解到,每次 Tendermint 收到新的 tx 或区块链哈希值发生变化(即区块链状态发生变化)时,都会生成一个新的区块接受内存池中未提交的新交易,该交易由 Tendermint 生成,用于正常的自检工作。
让我们看看这个区块链长什么样,这里是区块 2、3、4 的账本记录
我觉得这些小街区里有很多$$。最后,我将提供本文的代码。我希望所有玩家都创建自己的区块链。