Tendermint 简介
什么是Tendermint
Tendermint是可以安全地和在许多机器上一致复制一个应用程序的一个软件。安全性上,即使多达三分之一的机器以任意方式失败,Tendermint也能工作。一致性方面,每一台没有故障的机器都能看到相同的事务日志并计算相同的状态。安全性和一致性复制是分布式系统中的一个基本问题;它在广泛的应用程序(从货币、选举、基础设施编制等)的容错性方面起着关键的作用。
容忍机器以任意方式失败的能力,包括变得恶意,被称为拜占庭式容错(BFT)。BFT的理论已经有几十年的历史了,但是软件的实现最近才变得流行起来,这主要是由于“区块链技术”的成功,比如比特币和Ethereum。区块链技术只是在更现代的环境中对BFT的重新形式化,重点是对等网络和密码认证。这个名称来自于事务在块中被批处理的方式,在块中每个块包含前一个块的加密散列,形成一个链。实际上,区块链数据结构实际上优化了BFT设计。
Tendermint由两个主要的技术组成部分组成:一个区块链共识引擎和一个通用的应用程序接口。一致的引擎,称为Tendermint Core,确保相同的事务在每台机器上以相同的顺序记录下来。应用程序接口,称为应用程序区块链接口(ABCI),允许在任何编程语言中处理事务。不同于其他区块链和协商一致的解决方案,这些解决方案都是预先打包在状态机中(比如一个花哨的键值存储库,或者一种古怪的脚本语言),开发人员可以使用Tendermint来进行BFT状态机复制,这些应用程序编写在任何编程语言和开发环境中都是正确的。
Tendermint被设计成易于使用、易于理解、高性能,并适用于广泛的分布式应用程序。
ABCI
区块链应用接口,允许任意语言实现拜占庭容错复制
到目前为止,所有区块链“栈”(如比特币)有一个整体的设计。即每个区块链栈是一个单一的程序却处理分散分类帐的所有关注事项;这包括P2P连接,”mempool”交易广播,最近的区块共识,账户余额,图灵完备的合同,用户级权限,等等。
使用单体架构通常是计算机科学中的坏习惯。这使得它很难重用代码组件,对代码库分叉的话,就非常复杂的维护程序。尤其是当代码没有模块化设计的时候,就像”意大利面代码”一样糟糕。
单体架构设计的另一个问题是,它限制了你开发区块链栈的语言(或反之亦然)。比如在Ethereum,它支持一个图灵完备的字节码虚拟机的情况下,却限制了你编译到字节码的语言;目前,它仅支持Serpent和Solidity两种语言。
相比之下,我们的做法是将一致的引擎和P2P层从目前流行的区块链应用的具体应用细节状态解耦。我们通过将应用程序的细节抽象到一个接口来实现,该接口是作为socket协议实现的。
这样我们有个接口,区块链应用接口(ABCI),它主要的实现有Tendermint Socket 协议(TSP或Teaspoon)
打个比方,就拿一个比较著名的加密货币比特币来说。比特币是一个区块链加密货币,每个节点维护一个全面审计Unspent Transaction Output (utxo)数据库。如果你在ABCI上想创建一个类似比特币系统,Tendermint Core将会有一下职责
- 在不同节点间扩散区块以及交易
- 建立权威/不可修改的交易订单(在区块链中)
应用程序需要负责
- 维护UTXO数据库
- 验证交易的加密签名
- 阻止不存在的交易进行交易
- 允许客户端查询UTXO数据库
从核心交付到应用程序,ABCI包含三种主要的消息类型。
-
DeliverTx 消息是应用程序的核心。区块链中的每笔交易都是通过这个消息传递。应用程序需要验证收到的每笔交易DeliverTx消息包含的当前状态,应用协议和密码凭据。一个已经验证过的交易需要去更新应用程序的状态,通过绑定一个value到key-values存储中,或者更新UTXO数据库实例。
-
CheckTx 消息类似于DeliverTx,但是它仅用于验证事务。Tendermint核心的内存池首先检查使用CheckTx的事务的有效性,并且只将有效的事务转发给它的对等方。例如,应用程序可以检查事务中的递增序列号,如果序列号是旧的,则在CheckTx上返回一个错误。或者,它们可能使用一个基于功能的系统,该系统需要在每次事务中更新功能。
-
Commit消息用于计算当前应用程序状态的加密承诺,并将其放入下一个块标题。 这有一些方便的属性。 更新状态时的不一致现在将显示为区块链分叉,它会捕捉到一整类编程错误。 这也简化了安全轻量级客户端的开发,因为Merkle-Hash证明可以通过检查块散列来验证,并且块散列由法定数量签名。
sample 解析
注意下面代码可知
ABCI 应用只要需要实现这个Application
接口
type Application interface {
// Info/Query Connection
Info(RequestInfo) ResponseInfo // Return application info
SetOption(RequestSetOption) ResponseSetOption // Set application option
Query(RequestQuery) ResponseQuery // Query for state
// Mempool Connection
CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool
// Consensus Connection
InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore
BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing
EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set
Commit() ResponseCommit // Commit the state and return the application Merkle root hash
}
复制代码
比如ABCI应用Counter
type CounterApplication struct {
// 继承基本区块链
types.BaseApplication
hashCount int
txCount int
serial bool
}
func NewCounterApplication(serial bool) *CounterApplication {
return &CounterApplication{serial: serial}
}
// 实现返回链信息接口
func (app *CounterApplication) Info(req types.RequestInfo) types.ResponseInfo {
return types.ResponseInfo{Data: cmn.Fmt("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)}
}
// 实现设置接口
func (app *CounterApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption {
key, value := req.Key, req.Value
if key == "serial" && value == "on" {
app.serial = true
} else {
/*
TODO Panic and have the ABCI server pass an exception.
The client can call SetOptionSync() and get an `error`.
return types.ResponseSetOption{
Error: cmn.Fmt("Unknown key (%s) or value (%s)", key, value),
}
*/
return types.ResponseSetOption{}
}
return types.ResponseSetOption{}
}
// 实现传递接口
func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
if app.serial {
if len(tx) > 8 {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
}
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue != uint64(app.txCount) {
return types.ResponseDeliverTx{
Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
}
}
app.txCount++
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
// 实现校验接口
func (app *CounterApplication) CheckTx(tx []byte) types.ResponseCheckTx {
if app.serial {
if len(tx) > 8 {
return types.ResponseCheckTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
}
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue < uint64(app.txCount) {
return types.ResponseCheckTx{
Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected >= %v, got %v", app.txCount, txValue)}
}
}
return types.ResponseCheckTx{Code: code.CodeTypeOK}
}
// 实现提交接口
func (app *CounterApplication) Commit() (resp types.ResponseCommit) {
app.hashCount++
if app.txCount == 0 {
return types.ResponseCommit{}
}
hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
return types.ResponseCommit{Data: hash}
}
// 实现查询接口
func (app *CounterApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
switch reqQuery.Path {
case "hash":
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.hashCount))}
case "tx":
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.txCount))}
default:
return types.ResponseQuery{Log: cmn.Fmt("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
}
}
复制代码
具体的区块链应用数据流如图所示
定制自己的区块链程序
下面我们举例实现一个Contract,即ABCI Application
// 空实现
import (
"github.com/tendermint/abci/example/code"
"github.com/tendermint/tendermint/types"
)
type Example struct {
app types.BaseApplication
ValUpdates []types.Validator // 校验器
}
func (app *Example) Info(req types.RequestInfo) types.ResponseInfo {
return app.app.Info(req)
}
func (app *Example) SetOption(req types.RequestSetOption) types.ResponseSetOption {
return app.app.SetOption(req)
}
func (app *Example) Query(req types.RequestQuery) types.ResponseQuery {
return req
}
func (app *Example) CheckTx(tx []byte) types.ResponseCheckTx {
// 自身业务逻辑处理层
return types.ResponseCheckTx{Code: code.CodeTypeOK}
}
func (app *Example) InitChain(req types.RequestInitChain) types.ResponseInitChain {
for _, v := range req.Validators {
r := app.updateValidator(v)
if r.IsErr() {
// Add Log
}
}
return types.ResponseInitChain{}
}
func (app *Example) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
app.ValUpdates = make([]types.Validator, 0)
return types.ResponseBeginBlock{}
}
func (app *Example) DeliverTx(tx []byte) types.ResponseDeliverTx {
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (app *Example) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
}
func (app *Example) Commit() types.ResponseCommit {
return app.app.Commit()
}
// 更新验证器
func (app *Example) updateValidator(v types.Validator) types.ResponseDeliverTx {
key := []byte("val:" + string(v.PubKey))
if v.Power == 0 { // 如果验证器权益为0,则移除,否则添加 (持久化部分)
// remove validator from db
} else {
// add or update validator to db
}
// we only update the changes array if we successfully updated the tree
app.ValUpdates = append(app.ValUpdates, v)
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
复制代码
over...