概述

Tendermint 简单的可以理解为是一个模块化的区块链开发框架,支持开发者个性化定制自己的区块链,而不需要考虑共识算法以及P2P网络的实现。
因为在 Tendermint 中,共识引擎和P2P网络都被封装在Tendermint Core 中,并通过ABCI与应用层进行交互。所以使用Tendermint开发时,我们只需要实现ABCI的接口,就可以快速的开发一个区块链应用。

KVStore 案例

KVStore官方文档地址:https://docs.tendermint.com/master/tutorials/go-built-in.html

KVStore application

package mainimport ("bytes"abcitypes "github.com/tendermint/tendermint/abci/types""github.com/dgraph-io/badger"
)//实现abci接口
var _ abcitypes.Application = (*KVStoreApplication)(nil)//定义KVStore程序的结构体
type KVStoreApplication struct {db           *badger.DBcurrentBatch *badger.Txn
}// 创建一个 ABCI APP
func NewKVStoreApplication(db *badger.DB) *KVStoreApplication {return &KVStoreApplication{db: db,}
}// 检查交易是否符合自己的要求,返回0时代表有效交易
func (app *KVStoreApplication) isValid(tx []byte) (code uint32) {// 格式校验,如果不是k=v格式的返回码为1parts := bytes.Split(tx, []byte("="))if len(parts) != 2 {return 1}key, value := parts[0], parts[1]//检查是否存在相同的KVerr := 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) BeginBlock(req abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock {app.currentBatch = app.db.NewTransaction(true)return abcitypes.ResponseBeginBlock{}
}//当新的交易被添加到Tendermint Core时,它会要求应用程序进行检查(验证格式、签名等),当返回0时才通过
func (app KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx {code := app.isValid(req.Tx)return abcitypes.ResponseCheckTx{Code: code, GasUsed: 1}
}//这里我们创建了一个batch,它将存储block的交易。
func (app *KVStoreApplication) DeliverTx(req abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx {code := app.isValid(req.Tx)if code != 0 {return abcitypes.ResponseDeliverTx{Code: code}}parts := bytes.Split(req.Tx, []byte("="))key, value := parts[0], parts[1]err := app.currentBatch.Set(key, value)if err != nil {panic(err)}return abcitypes.ResponseDeliverTx{Code: 0}
}func (app *KVStoreApplication) Commit() abcitypes.ResponseCommit {// 往数据库中提交事务,当 Tendermint core 提交区块时,会调用这个函数app.currentBatch.Commit()return abcitypes.ResponseCommit{Data: []byte{}}
}func (app *KVStoreApplication) Query(reqQuery abcitypes.RequestQuery) (resQuery abcitypes.ResponseQuery) {resQuery.Key = reqQuery.Dataerr := 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 = valreturn nil})}return nil})if err != nil {panic(err)}return
}func (KVStoreApplication) Info(req abcitypes.RequestInfo) abcitypes.ResponseInfo {return abcitypes.ResponseInfo{}
}func (KVStoreApplication) InitChain(req abcitypes.RequestInitChain) abcitypes.ResponseInitChain {return abcitypes.ResponseInitChain{}
}func (KVStoreApplication) EndBlock(req abcitypes.RequestEndBlock) abcitypes.ResponseEndBlock {return abcitypes.ResponseEndBlock{}
}func (KVStoreApplication) ListSnapshots(abcitypes.RequestListSnapshots) abcitypes.ResponseListSnapshots {return abcitypes.ResponseListSnapshots{}
}func (KVStoreApplication) OfferSnapshot(abcitypes.RequestOfferSnapshot) abcitypes.ResponseOfferSnapshot {return abcitypes.ResponseOfferSnapshot{}
}func (KVStoreApplication) LoadSnapshotChunk(abcitypes.RequestLoadSnapshotChunk) abcitypes.ResponseLoadSnapshotChunk {return abcitypes.ResponseLoadSnapshotChunk{}
}func (KVStoreApplication) ApplySnapshotChunk(abcitypes.RequestApplySnapshotChunk) abcitypes.ResponseApplySnapshotChunk {return abcitypes.ResponseApplySnapshotChunk{}
}

Tendermint Core

package mainimport ("flag""fmt""os""os/signal""path/filepath""syscall""github.com/dgraph-io/badger""github.com/spf13/viper"abci "github.com/tendermint/tendermint/abci/types"cfg "github.com/tendermint/tendermint/config"tmflags "github.com/tendermint/tendermint/libs/cli/flags""github.com/tendermint/tendermint/libs/log"nm "github.com/tendermint/tendermint/node""github.com/tendermint/tendermint/p2p""github.com/tendermint/tendermint/privval""github.com/tendermint/tendermint/proxy"
)var configFile string// 设置配置文件路径
func init() {flag.StringVar(&configFile, "config", "$HOME/.tendermint/config/config.toml", "Path to config.toml")
}func main() {//初始化Badger数据库并创建一个应用实例db, err := badger.Open(badger.DefaultOptions("/tmp/badger"))if err != nil {fmt.Fprintf(os.Stderr, "failed to open badger db: %v", err)os.Exit(1)}defer db.Close()// 创建 ABCI APPapp := NewKVStoreApplication(db)flag.Parse()// 创建一个Tendermint Core Node实例node, err := newTendermint(app, configFile)if err != nil {fmt.Fprintf(os.Stderr, "%v", err)os.Exit(2)}// 开启节点node.Start()// 程序退出时关闭节点defer func() {node.Stop()node.Wait()}()// 退出程序c := make(chan os.Signal, 1)signal.Notify(c, os.Interrupt, syscall.SIGTERM)<-cos.Exit(0)
}//创建一个本地节点
func newTendermint(app abci.Application, configFile string) (*nm.Node, error) {// 读取默认的Validator配置config := cfg.DefaultValidatorConfig()// 设置配置文件的路径config.RootDir = filepath.Dir(filepath.Dir(configFile))// 这里使用的是viper 工具,// 官方文档:https://github.com/spf13/viperviper.SetConfigFile(configFile)  if err := viper.ReadInConfig(); err != nil {return nil, fmt.Errorf("viper failed to read config file: %w", err)}if err := viper.Unmarshal(config); err != nil {return nil, fmt.Errorf("viper failed to unmarshal config: %w", err)}if err := config.ValidateBasic(); err != nil {return nil, fmt.Errorf("config is invalid: %w", err)}// 创建日志logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))var err errorlogger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel)if err != nil {return nil, fmt.Errorf("failed to parse log level: %w", err)}// 读取配置文件pv, _ := privval.LoadFilePV(config.PrivValidatorKeyFile(),config.PrivValidatorStateFile(),)nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())if err != nil {return nil, fmt.Errorf("failed to load node's key: %w", err)}// 根据配置文件 创建节点node, err := nm.NewNode(config,pv,nodeKey,proxy.NewLocalClientCreator(app),nm.DefaultGenesisDocProviderFunc(config),nm.DefaultDBProvider,nm.DefaultMetricsProvider(config.Instrumentation),logger)if err != nil {return nil, fmt.Errorf("failed to create new Tendermint node: %w", err)}return node, nil
}

程序工作流程

1. 通过RPC调用broadcast_tx_commit,提交交易,也就是图中的User Input。交易首先被存放在交易池缓存(mempool cache)中。
2. 交易池调用CheckTx验证交易的有效性。验证通过就放入交易池。
3. 从Validator中选出的Proposer在交易池中打包交易、形成区块。
4. 第一轮投票。Proposer通过Gossip协议进行广播区块。全网的Validator对区块进行校验。校验通过,同意进行Pre-vote;不通过或者超时,产生一个空块。然后每个Validator都进行广播
5. 第二轮投票。所有的Validator收集其他Validator的投票信息,如果有大于2/3的节点同意pre-vote,那么自己就投票Pre-commit。如果小于2/3的节点或者超时,也继续产生一个空块。
6. 所有Validator广播自己的投票结果,并收集其他Validator的投票结果。如果收到的投票信息没有超过2/3的节点投票pre-commit或者超时。那么就不提交区块。如果有超过2/3的pre-commit那么就提交区块。最后,区块高度加1。

最后

这里只是通过一个简单的案例对Tendermint工作流程进行疏通。如果有任何问题可以来群里进行交流讨论。群里还有很多视频书籍资料可以自行下载。

最后推荐一位大佬的公众号,欢迎关注哦:区块链技术栈

Tendermint KVStore案例解析相关推荐

  1. 《用于物联网的Arduino项目开发:实用案例解析》—— 3.4 小结

    本节书摘来自华章出版社<用于物联网的Arduino项目开发:实用案例解析>一 书中的第3章,第3.4节,作者[美]安德尔·杰韦德(Adeel Javed),更多章节内容可以访问云栖社区&q ...

  2. 【许晓笛】 EOS智能合约案例解析(1)

    详解 EOS 智能合约的 hpp 文件 为了帮助大家熟悉 EOS 智能合约,EOS 官方提供了一个代币(资产)智能合约 Demo -- eosio.token.eosio.token 智能合约目前还不 ...

  3. 福利继续:赠书《Spring Cloud微服务-全栈技术与案例解析》

    <Spring Cloud微服务-全栈技术与案例解析> 在互联网时代,互联网产品的最大特点就是需要快速发布新功能,支持高并发和大数据.传统的架构已经慢慢不能支撑互联网业务的发展,这时候微服 ...

  4. 转盘抽奖php,使用PHP实现转盘抽奖算法案例解析

    这次给大家带来使用PHP实现转盘抽奖算法案例解析,使用PHP实现转盘抽奖算法的注意事项有哪些,下面就是实战案例,一起来看一下. 流程: 1.拼装奖项数组 2.计算概率 3.返回中奖情况 代码如下: 中 ...

  5. Database之SQL:SQL之over partition by开窗函数的简介、使用方法(求各班级内各自排名/求各班级内第一名/求各班级内分数递增和等案例解析)之详细攻略

    Database之SQL:SQL之over partition by开窗函数的简介.使用方法(求各班级内各自排名/求各班级内第一名/求各班级内分数递增和等案例解析)之详细攻略 目录 over part ...

  6. auto-sklearn案例解析二

    度量函数-metrics auto-sklearn的度量函数是对sklearn度量函数的封装,我们即可以使用autosklearn已经封装好的metrics函数,也可以使用autosklearn的me ...

  7. python查找字符串关键词_Python字符串查找基本操作案例解析

    本篇文章小编给大家分享一下Python字符串查找基本操作案例解析,文章介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 字符串查找基本操作主要分为三个关键词:fi ...

  8. 刘子佼 mysql 下载_MySQL数据管理之备份恢复案例解析 23讲 Mysql备份恢复实战 视频教程...

    课程名称:MySQL数据管理之备份恢复案例解析 23讲 Mysql备份恢复实战课程简介: 课程独家解析MySQL 5.6最新特性,课程讲师刘子佼讲课风格幽默,善于与人沟通,善于组建和协调团队攻克技术难 ...

  9. OOAD实践之路——真实案例解析OO理论与实践(二、第一项任务:特性列表)

    查看本系列全部文章: <OOA&D实践之路--真实案例解析OO理论与实践>索引贴 第一份说明       当这个项目开始时,我们得到的关于我们要做的系统的唯一说明是一页Word文档 ...

最新文章

  1. 使用此代码可以解决python包导入路径问题?
  2. 5-1 Django的路由层(urlconf)
  3. win7怎么合并计算机窗口,win7 已设置任务栏窗口从不合并,游戏多开,如何固定窗口顺序?...
  4. Oracle区分中文和英文,oracle中中英文段落划分实现
  5. Java EE 6测试第二部分– Arquillian和ShrinkWrap简介
  6. Can you find it(HDU-5478)
  7. 误删除了mysql库的user表解决办法
  8. 关于 ls 命令的一个小小的缺陷
  9. 机器学习代码实战——网格搜索和交叉验证(GridSearchCV)
  10. 基于VisualStudio11开发Windows8的Metro sample讲解(1)MessageBox
  11. 逆向:Windows消息钩取
  12. ABAQUS后处理常用功能
  13. WIFI计量插座之计量芯片选型
  14. m1芯片 服务器,M1芯片相当于什么水平 苹果M1芯片相当于英特尔什么芯片
  15. [视频发布] 掘金 Podcast 报名中,摩拜单车、美团点评团队分享 Vue 最佳实践
  16. jsLint 检查分析
  17. 你能把我带走吗?python:走吧
  18. 区块链技术发展现状与展望 论文阅读摘要(袁勇、王飞跃)
  19. 计算机网络有线通信媒体,计算机网络基础知识之传输媒体
  20. word制作多个单位联合发文的文件头

热门文章

  1. html图片菱形,CSS3 菱形拼图实现只旋转div 背景图片不旋转功能
  2. 单位办公电脑声音和摄像头被关闭,如何打开笔记本麦克风和摄像头。
  3. caffe检测图片是否包含人脸_人脸识别(基于Caffe)
  4. Scrapy抓取接口中文数据显示问号问题
  5. 成都java培训要多少钱
  6. 徒步新纪录--从植物园到北理
  7. 万能的APT!编译时注解的妙用
  8. 国科大学习资料--最优化计算方法(王晓)-期末考试真题2
  9. PY:工资项目的配置、属性及分类?
  10. NKOJ-3776 工资管理