Tendermint is a modular blockchain application framework that can achieve Byzantine Fault Tolerance (BFT). It mainly includes two parts:
Tendermint Core:
Implemented a p2p network; shared blocks and transactions between nodes;
The Byzantine fault-tolerant consensus algorithm is implemented, and the transaction order that does not change is determined;
ABCI Interface, the specific logic processing layer, can be implemented based on different languages (Golang, JS); at this layer, operations such as transaction verification processing and query are implemented.
These two parts will correspond to two different processes respectively. Core and ABCI establish three connections:
A connection used to verify the transaction, after the transaction verification is passed, it will be broadcast to the mempoll;
a proposal for the block;
The last connection is used to query the status of the application;
The following figure is the Workflow of the two:
Core and ABCI communication
Tendermint-based Key-Value Storage Example
Tendermint has a built-in KV storage application example, we can run this example:
Install
You need to install tendermint and abci-cli first:
go get -u github.com/tendermint/tendermint/cmd/tendermint
go get -u github.com/tendermint/abci
cd $GOPATH/src/github.com/tendermint/abci
make install
Verify that the installation was successful:
➜ blog git:(hexo) ✗ which tendermint
/Users/hbliu/go/bin/tendermint
➜ blog git:(hexo) ✗ which abci-cli
/Users/hbliu/go/bin/abci-cli
start up
Initialize node configuration:
tendermint init
Start the KV storage application:
abci-cli kvstore
Start the Tendermint node:
tendermint node --consensus.create_empty_blocks=false
The latter parameter is to prohibit the Tendermint node from periodically generating empty blocks.
Create transaction
Create a storage with key name and value hbliu in Tendermint:
➜ blog git:(hexo) ✗ curl -s 'localhost:46657/broadcast_tx_commit?tx="name=hbliu"'
{
"jsonrpc": "2.0",
"id": "",
"result": {
"check_tx": {
"fee": {}
},
"deliver_tx": {
"tags": [
{
"key": "YXBwLmNyZWF0b3I=",
"value": "amFl"
},
{
"key": "YXBwLmtleQ==",
"value": "bmFtZQ=="
}
],
"fee": {}
},
"hash": "BA0C60A3F391B35DEAE8A7E6E0491E9B2E0BA497",
"height": 2
}
}
返回的 Response 中的 key 和 value 使用了 base64 进行了编码, 我们可以通过命令 base64 对其进行解码:
➜ blog git:(hexo) ✗ echo "YXBwLmtleQ==" | base64 -D
app.key
➜ blog git:(hexo) ✗ echo "bmFtZQ==" | base64 -D
name
查询下我们之前的信息有没有成功写入:
➜ blog git:(hexo) ✗ curl -s 'localhost:46657/abci_query?data="name"'
{
"jsonrpc": "2.0",
"id": "",
"result": {
"response": {
"log": "exists",
"index": "-1",
"key": "bmFtZQ==",
"value": "aGJsaXU="
}
}
}
➜ blog git:(hexo) ✗ echo "bmFtZQ==" | base64 -D
name
➜ blog git:(hexo) ✗ echo "aGJsaXU=" | base64 -D
hbliu
在浏览器中打开 http://localhost:46657 可以显示当前所有支持的 API。
示例代码介绍
上述示例的代码存储在 Github(https://github.com/tendermint/abci/blob/master/example/kvstore/kvstore.go) 上。下面我们对这部分代码做一个简单的介绍。
在我们调用 broadcast_tx_commit 的时候,会先调用 CheckTx,验证通过后会把 TX 加入到 mempool 里。在 kvstore 示例中没有对 transaction 做检查,直接通过:
func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
return types.ResponseCheckTx{Code: code.CodeTypeOK}
}
放到 mempool 里的 TX 会被定期广播到所有节点。当 Tendermint 选出了 Proposal 节点后,它便会从 mempool 里选出一系列的 TXs,将它们组成一个 Block,广播给所有的节点。节点在收到 Block 后,会对 Block 里的所有 TX 执行 DeliverTX 操作,同时对 Block 执行 Commit 操作。
我们调用 broadcast_tx_commit 返回的结果其实就是 DeliverTX 返回的结果:
func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
var key, value []byte
parts := bytes.Split(tx, []byte("="))
if len(parts) == 2 {
key, value = parts[0], parts[1]
} else {
key, value = tx, tx
}
app.state.db.Set(prefixKey(key), value)
app.state.Size += 1
tags := []cmn.KVPair{
{[]byte("app.creator"), []byte("jae")},
{[]byte("app.key"), key},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
}
可以看出它会从输入参数中解析出 key 和 value,最后保存在应用的 State 中。
当所有的 TX 被处理完之后需要调用 Commit 来更新整个区块的状态,包括高度加 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}
}
References
Tendermint Introduction(http://tendermint.readthedocs.io/projects/tools/en/develop/introduction.html)
出处:https://hiberabyss.github.io/2018/04/02/tendermint-introduction-1/
Copyright statement: The content comes from the Internet, and the copyright belongs to the original creator. Unless we cannot confirm, we will indicate the author and source. If there is any infringement, please let us know, we will delete it immediately and apologize. thanks.
Architecture Digest
ID:ArchDigest
Internet Application Architecture丨Architecture Technology丨Large Websites丨Big Data丨Machine Learning
For more exciting articles, please click below: Read the original text