从架构层面,区块链可以简单的分为三个概念层:
大致框架如下:
区块链是一个具备确定性的状态机,可以在不信任的节点之间进行状态复制,Tendermint 基于上面的架构,将应用程序与底层进行了分离,将P2P网络和共识引擎封装组成Tendermint Core, 同时提供ABCI(Application BlockChain Interface)接口与应用层进行交互,
应用的逻辑可以用任何语言编写。应用层做的事情实际上就是状态机控制. 这样开发者可以很方便的实现自己的区块链.
[外链图片转存失败(img-YnzxsQgL-1566198289277)(https://github.com/xueqianLu/icloudImages/raw/master/cosmos-images/10002.png)]
总结,Tendermint可以理解为是一个模块化的区块链软件框架,使开发者可以不考虑共识以及网络传输的实现,个性化定制自己的区块链.
ABCI是Tendermint中定义的一套Application与Tendermint Core之间交互的协议。详细如下:
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(RequestCheckTx) 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(RequestDeliverTx) 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接口可以分为三类:信息查询、交易校验以及共识相关处理。而Tendermint Core作为ABCI Client 在启动时,会与ABCI Server建立三个连接,分别用于这三类接口消息的处理。
下面简单描述几种消息类型的作用:
Tendermint共识引擎,包含区块链需要的大部分功能,主要包括:
Cosmos 是在Tendermint 基础上丰富插件实现的App.
[外链图片转存失败(img-JYGtPRTp-1566198289279)(https://github.com/xueqianLu/icloudImages/raw/master/cosmos-images/10003.png)]
kvstore 是一个简单的ABCI app, 也可以说是一个链,这个app的功能仅仅是存储key-value, 不涉及账户/余额/奖励/转账等内容.
type KVStoreApplication struct {
types.BaseApplication
state State
}
func NewKVStoreApplication() *KVStoreApplication {
state := loadState(dbm.NewMemDB())
return &KVStoreApplication{state: state}
}
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{
Data: fmt.Sprintf("{\"size\":%v}", app.state.Size),
Version: version.ABCIVersion,
AppVersion: ProtocolVersion.Uint64(),
}
}
// tx is either "key=value" or just arbitrary bytes
func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
var key, value []byte
parts := bytes.Split(req.Tx, []byte("="))
if len(parts) == 2 {
key, value = parts[0], parts[1]
} else {
key, value = req.Tx, req.Tx
}
app.state.db.Set(prefixKey(key), value)
app.state.Size += 1
events := []types.Event{
{
Type: "app",
Attributes: []cmn.KVPair{
{Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")},
{Key: []byte("key"), Value: key},
},
},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events}
}
func (app *KVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
func (app *KVStoreApplication) Commit() types.ResponseCommit {
// Using a memdb - just return the big endian size of the db
appHash := make([]byte, 8)
binary.PutVarint(appHash, app.state.Size)
app.state.AppHash = appHash
app.state.Height += 1
saveState(app.state)
return types.ResponseCommit{Data: appHash}
}
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
resQuery.Key = reqQuery.Data
value := app.state.db.Get(prefixKey(reqQuery.Data))
resQuery.Value = value
if value != nil {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
}
app是abci 的server端,而tendermint是abci的client端, 因此是需要先启动app然后再启动tendermint去连接server.
而事务是通过调用tendermint的rpc接口发起的,由tendermint通知到app层进行处理.
luxq@testpc:~$curl http://localhost:26657/broadcast_tx_commit?tx=\"abcd=121321\"
{
"jsonrpc": "2.0",
"id": "",
"result": {
"check_tx": {
"gasWanted": "1"
},
"deliver_tx": {
"tags": [
{
"key": "YXBwLmNyZWF0b3I=", //app.creator
"value": "Q29zbW9zaGkgTmV0b3dva28=" //Cosmoshi Netowoko
},
{
"key": "YXBwLmtleQ==", //app.key
"value": "YWJjZA==" //abcd
}
]
},
"hash": "2A71EC7647ACC0F582DC245F1080F0FC4C0D8DCDED53FBDFD6B1D899AC2D1373",
"height": "51464"
}
}
luxq@testpc:~$
从上面的例子中看到Tx并不是之前见到的账户转账,而是一段数据,具体的数据解析和使用是在App层做处理的.
Tx 的数据结构也只是一段纯数据.
//file: tendermint/types/tx.go
type Tx []byte
Tendermint Core 和ABCI 的基本知识已经了解,下一步了解Cosmos-SDK