一、Tendermint交易组成
package main
import (
abcitypes "github.com/tendermint/tendermint/abci/types"
)
type KVStoreApplication struct {}
var _ abcitypes.Application = (*KVStoreApplication)(nil)
func NewKVStoreApplication() *KVStoreApplication {
return &KVStoreApplication{}
}
func (KVStoreApplication) Info(req abcitypes.RequestInfo) abcitypes.ResponseInfo {
return abcitypes.ResponseInfo{}
}
func (KVStoreApplication) SetOption(req abcitypes.RequestSetOption) abcitypes.ResponseSetOption {
return abcitypes.ResponseSetOption{}
}
func (KVStoreApplication) DeliverTx(req abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx {
return abcitypes.ResponseDeliverTx{Code: 0}
}
func (KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx {
return abcitypes.ResponseCheckTx{Code: 0}
}
func (KVStoreApplication) Commit() abcitypes.ResponseCommit {
return abcitypes.ResponseCommit{}
}
func (KVStoreApplication) Query(req abcitypes.RequestQuery) abcitypes.ResponseQuery {
return abcitypes.ResponseQuery{Code: 0}
}
func (KVStoreApplication) InitChain(req abcitypes.RequestInitChain) abcitypes.ResponseInitChain {
return abcitypes.ResponseInitChain{}
}
func (KVStoreApplication) BeginBlock(req abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock {
return abcitypes.ResponseBeginBlock{}
}
func (KVStoreApplication) EndBlock(req abcitypes.RequestEndBlock) abcitypes.ResponseEndBlock {
return abcitypes.ResponseEndBlock{}
}
二、逐个交易分析
2.1 CheckTx
当有新交易提交给Tendermint Code时,将会调用check函数去检查,校验格式、签名等,校验通过则继续下一步。
When a new transaction is added to the Tendermint Core, it will ask the application to check it (validate the format, signatures, etc.).
示例:
import "bytes"
func (app *KVStoreApplication) isValid(tx []byte) (code uint32) {
// check format
parts := bytes.Split(tx, []byte("="))
if len(parts) != 2 {
return 1
}
key, value := parts[0], parts[1]
// check if the same key=value already exists
err := app.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
if err != nil && err != badger.ErrKeyNotFound {
return err
}
if err == nil {
return item.Value(func(val []byte) error {
if bytes.Equal(val, value) {
code = 2
}
return nil
})
}
return nil
})
if err != nil {
panic(err)
}
return code
}
func (app *KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx {
code := app.isValid(req.Tx)
return abcitypes.ResponseCheckTx{Code: code, GasWanted: 1}
}
注:
1、通过校验返回0。
2、未通过交易检查返会1,判断出重复 交易返回2,其它非法返回非0。
请求参数分析
type RequestCheckTx struct {
Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"`
Type CheckTxType `protobuf:"varint,2,opt,name=type,proto3,enum=tendermint.abci.types.CheckTxType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
CheckTxType
type CheckTxType int32
const (
CheckTxType_New CheckTxType = 0
CheckTxType_Recheck CheckTxType = 1
)
var CheckTxType_name = map[int32]string{
0: "New",
1: "Recheck",
}
返回参数分析
type ResponseCheckTx struct {
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"`
GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"`
Events []Event `protobuf:"bytes,7,rep,name=events,proto3" json:"events,omitempty"`
Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
2.2 BeginBlock -> DeliverTx -> EndBlock -> Commit (区块产生)
注 DeliverTx 是异步的
2.3 BeginBlock/EndBlock
BeginBlock/EndBlock:当tendermint就新区块交易达成共识后,将通过BeginBlock请求开始启动ABCI应用 的交易执行流程,并以EndBlock请求作为交易执行流程的结束。
DeliverTx:在BeginBlock和EndBlock请求之间,tendermint会为区块中的每一个交易向ABCI应用 发出一个DeliverTx请求消息,这是应用状态机更新的时机。
注:DeliverTx是异步 的
Commit:作为执行交易的最后一步,tendermint会发送Commit请求,并在获取响应后持久化区块状态。
2.3 Query
Query:当Rpc客户端发出abci_query调用时,tendermint将通过Query请求消息转发给ABCI应用,并 将响应结果转发回Rpc客户端。
RequestQuery 字段分析
type RequestQuery struct {
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"`
Prove bool `protobuf:"varint,4,opt,name=prove,proto3" json:"prove,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
ResponseQuery 字段分析
type ResponseQuery struct {
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
// bytes data = 2; // use "value" instead.
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
Index int64 `protobuf:"varint,5,opt,name=index,proto3" json:"index,omitempty"`
Key []byte `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"`
Value []byte `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"`
Proof *merkle.Proof `protobuf:"bytes,8,opt,name=proof,proto3" json:"proof,omitempty"`
Height int64 `protobuf:"varint,9,opt,name=height,proto3" json:"height,omitempty"`
Codespace string `protobuf:"bytes,10,opt,name=codespace,proto3" json:"codespace,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
示例
func (app *KVStoreApplication) Query(reqQuery abcitypes.RequestQuery) (resQuery abcitypes.ResponseQuery) {
resQuery.Key = reqQuery.Data
err := app.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(reqQuery.Data)
if err != nil && err != badger.ErrKeyNotFound {
return err
}
if err == badger.ErrKeyNotFound {
resQuery.Log = "does not exist"
} else {
return item.Value(func(val []byte) error {
resQuery.Log = "exists"
resQuery.Value = val
return nil
})
}
return nil
})
if err != nil {
panic(err)
}
return
}
2.4 Info
Info:当tendermint与ABCI应用建立初始连接时,将发送Info请求消息尝试获取应用状态对应的区块 高度、状态哈希等信息,以确定是否需要重放(replay)区块交易。
2.5 InitChain
InitChain:当创建创世块时,tendermint会发送InitChain请求消息给ABCI应用,可以在此刻进行 应用状态机的状态初始化。
三、交易概述
2.1 链启动初始化时
info -> initchain -> beginblock -> endblock -> commit
2.2 提交交易时
check -> beginblock -> delivertx -> endblock -> commit
2.3 查询交易时
query