tendermint简介

Tendermint包括两个主要技术组件:区块链共识引擎通用应用程序接口

共识引擎,被称作Tendermint Core,保证所有的机器按照相同的顺序记录相同的交易。

应用程序接口,被称为应用程序区块链接口ABCI,实现任意编程语言处理交易的功能。

其他区块链和共识解决方案通常预置内部状态机,比如使用键值存储不常见的脚本语言来完成功能, 而Tendermint支持开发者用任意编程语言实现拜占庭容错的状态机复制功能,并且开发环境也非常友好。

ABCI的消息流

Tendermint通过在应用程序进程和共识进程之间提供非常简单的API(即ABCI)来分解区块链设计。

ABCI由3种主要消息类型组成,它们从Tendermint核心传递到应用程序,应用程序用相应的响应消息进行回复。消息规范可查看:ABCI消息类型

  • DeliverTx消息是应用程序的核心部分。 区块链中的每笔交易都会随此消息一起发送。用于应用程序验证和执行交易的消息。Tendermint节点将交易发送给应用程序执行,并期望应用程序返回交易执行结果。
  • Info消息:用于在应用程序启动时向Tendermint节点发送应用程序的基本信息,例如版本、特性和支持的选项。
  • InitChain消息:在区块链网络启动时,用于初始化应用程序的区块链状态。Tendermint节点会发送InitChain消息并包含初始的验证人列表、初始的验证参数和初始的应用程序状态。
  • BeginBlock消息:每当一个新区块开始时,Tendermint节点会发送BeginBlock消息给应用程序,其中包含区块的头信息和交易列表。
  • EndBlock消息:每当一个区块结束时,Tendermint节点会发送EndBlock消息给应用程序,其中包含区块的高度和区块上下文相关的信息。
  • Commit消息:当Tendermint节点达到共识并将一个区块提交到区块链时,会发送Commit消息给应用程序,该消息包含将被应用程序持久化的应用状态的哈希值。用于计算当前应用状态的加密承诺,并将其放入下一个区块头中。 这有一些方便的特性。 更新状态过程中如果出现不一致问题时会被认为区块链产生了分叉,这样就能捕获一整类的编程错误。
  • Query消息:用于应用程序处理Tendermint节点和其他应用程序的查询请求。当Tendermint节点、其他应用程序或用户发起查询时,会向应用程序发送Query消息,并期望应用程序返回查询结果。
  • CheckTx消息类似于DeliverTx,但它仅用于验证交易。 Tendermint Core的内存池首先使用CheckTx检查交易的有效性,然后只将有效交易转发给其他节点。 比如,应用程序可以检查交易中递增的序号,如果序号不是最新的,CheckTx就会返回错误。 或者,他们可以使用基于容量的系统,该系统要求对每笔交易都更新容量。

一个应用程序可以有多个ABCI socket连接。 Tendermint Core创建三个到应用程序的ABCI连接: 一个用于内存池广播时验证交易, 一个用于共识引擎运行区块提案, 最后一个用于查询应用状态。

共识概述

Tendermint是一个易于理解的、主要是异步操作的BFT共识协议。协议遵循一个简单的状态机,如下所示:

协议的参与者称为validators(验证者);他们轮流提出区块并对其进行投票。在链中提交的每一个区块都有一个height(高度)。 当一个区块提交失败时,协议将进入到下一round(轮),新的验证者将为该高度提出一个新的区块。在链中提交的每一个区块都有一个height(高度)。 当一个区块提交失败时,协议将进入到下一round(轮),新的验证者将为该高度提出一个新的区块。当超过2/3的验证者在同一轮中预提交同一个区块时,区块就会被提交到区块链中。

也就是说,可以将其流程分为四个阶段:

  1. 提议阶段:一个验证者被选为该轮的提议者,该提议者负责生成一个新的区块提案,并将其发送给其他验证者节点。
  2. 投票阶段:在收到区块提案后,其他验证者节点对该提案进行投票。节点会对提案进行验证和检查,然后根据一定的规则进行投票。
  3. 确认阶段:当超过两三分之两的验证者节点(即超过2/3的节点)接受了同一个区块提案时,该区块被确认为有效的。
  4. 提交阶段:一旦区块被确认为有效,验证者节点将在链上将该区块提交为新的区块。此后,系统进入下一个共识轮。

tendermint简单使用

  • 安装tendermint

    方法1:

    1. git clone https://github.com/tendermint/tendermint.git
    2. make install
    3. make build

    二进制文件在build目录

    方法2:

    1. git clone https://github.com/tendermint/tendermint.git
    2. cd tendermint/cmd/tendermint
    3. go build

    输入 tendermint version,查看版本,成功显示即成功

    然后安装ABCI

    1. cd tendermint/abci/cmd/abci-cli
    2. go build

    然后输入abci-cli version,查看版本

  • 启动tendermimnt

    1. ./tendermint init
    2. ./tendermint node --proxy_app=kvstore

    这只是其中一种方法,tendermint node --proxy_app=kvstore中的--proxy_app=kvstore实际上是代理应用程序地址,或用于本地测试的,命令原文:proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'e2e' or 'noop' for local testing. (default "tcp://127.0.0.1:26658"),

    第二种启动方法:

    将abci和tendermint core 分别启动,还记得上面我们编译的文件,查看help可以看到kvstore,这是一个内置的简单例子。

    abci-cli kvstore
    tendermint node

    执行上述命令与tendermint node --proxy_app=kvstore是一样的,此时访问url:localhost:26657/status,可以看到区块链状态。

    现在可以通过访问localhost:26657/broadcast_tx_commit?tx="abcdee",将交易abcd发送到 kvstore,注意 url 周围的单引号('),这确保 bash 不会转义双引号(")。这个命令发送了一个带有字节 abcdee 的交易,因此 abcdee 将作为键和值存储在默克尔树中。此时的响应应该是这样的

    {
        "jsonrpc": "2.0",
        "id": -1,
        "result": {
            "check_tx": {
                "code": 0,
                "data": null,
                "log": "",
                "info": "",
                "gas_wanted": "1",
                "gas_used": "0",
                "events": [],
                "codespace": "",
                "sender": "",
                "priority": "0",
                "mempoolError": ""
            },
            "deliver_tx": {
                "code": 0,
                "data": null,
                "log": "",
                "info": "",
                "gas_wanted": "0",
                "gas_used": "0",
                "events": [
                    {
                        "type": "app",
                        "attributes": [
                            {
                                "key": "Y3JlYXRvcg==",
                                "value": "Q29zbW9zaGkgTmV0b3dva28=",
                                "index": true
                            },
                            {
                                "key": "a2V5",
                                "value": "YWJjZGVl",
                                "index": true
                            },
                            {
                                "key": "aW5kZXhfa2V5",
                                "value": "aW5kZXggaXMgd29ya2luZw==",
                                "index": true
                            },
                            {
                                "key": "bm9pbmRleF9rZXk=",
                                "value": "aW5kZXggaXMgd29ya2luZw==",
                                "index": false
                            }
                        ]
                    }
                ],
                "codespace": ""
            },
            "hash": "96DD75D85ABD5C6973A2567FAA35D2DD2A9508194891381F974A7AF020D25B80",
            "height": "725"
        }
    }

    成功发送交易后,可以通过访问localhost:26657/abci_query?data="abcdee"来查询交易是否正常工作,以及是否储存了值,result:

    {
        "jsonrpc": "2.0",
        "id": -1,
        "result": {
            "response": {
                "code": 0,
                "log": "exists",
                "info": "",
                "index": "0",
                "key": "YWJjZGVl",
                "value": "YWJjZGVl",
                "proofOps": null,
                "height": "829",
                "codespace": ""
            }
        }
    }

    注意结果中的 value (YWJjZGVl);这是 abcd ASCII 的 base64 编码。您可以在 python2 shell 中运行 "YWJjZGVl".decode('base64') 来验证这一点,或者在 python3 shell 中运行 import codecs; codecs.decode("YWJjZGVl".encode('utf-8'), 'base64').decode('ascii')

ABCL-CLI

ABCI 应用程序必须提供以下两点:

  • 一个套接字服务器
  • ABCI 消息的处理程序

当我们运行 abci-cli 工具时,我们打开到应用程序套接字服务器的新连接,发送给定的 ABCI 消息,并等待响应。

处理程序是特定于应用程序的,并且可以是任意的,只要它是确定的并且符合 ABCI 接口规范。下图是一个例子

这里的最终用户应用程序是 Cosmos Voyager,在左下角。 Voyager 与本地轻客户端守护进程公开的 REST API 通信。轻客户端守护进程是一个特定于应用程序的程序,它与 Tendermint 节点通信,并通过 Tendermint Core RPC 验证 Tendermint 轻客户端证明。Tendermint Core 处理与一个本地 ABCI 应用程序通信,用户查询或交易实际上是在这个应用程序中处理的。

ABCI 应用程序必须是 Tendermint 共识的确定性结果 - 任何外部对应用程序状态的影响,如果没有通过 Tendermint,都可能导致共识失败。因此,除了通过 ABCI 与 Tendermint 通信外, 应该与应用程序通信。

tendermint core

区块链数据的默认目录是 ~/.tendermint。通过设置 TMHOME 环境变量来覆盖它。

运行tendermint init命令初始化根目录

这将在 $TMHOME/config 中创建一个新的私钥 (priv_validator.json)和一个包含关联公钥的创世文件(genesis.json)。这就是运行带有一个验证者的本地测试网络所需要的全部内容。有关更详细的初始化,请参见tendermint testnet --help命令。

  • genesis_time: 区块链正式开始时间。
  • chain_id: 区块链的 ID。对于每个区块链,这必须是惟一的。如果您的测试网络区块链没有惟一的链 ID,那么您的日子就不好过了。链 ID 必须小于50个符号。
  • validators: 初始验证者列表。注意,这可能被应用程序完全覆盖,并且可能被保留为空,以明确应用程序将使用 ResponseInitChain 初始化验证者集。
    • pub_key: 第一个元素指定 pub_key 类型。1 == Ed25519。第二个元素是公钥字节。
    • power: 验证者的投票权。
    • name: 验证者的名称(可选)。
  • app_hash: 在创世中期望的应用程序哈希(由 ResponseInfo ABCI消息返回)。如果应用程序的哈希不匹配,Tendermint 会感到恐慌。
  • app_state: 应用程序状态(例如 tokens 的初始分发)。

要运行 Tendermint 节点,使用tendermint node,默认情况下,Tendermint 将尝试连接到 127.0.0.1:26658上的 ABCI 应用程序。几秒钟后,您应该会看到块开始流入。注意,即使没有交易,也会定期生成块。

要修改此设置,虽然 tendermint 的默认行为仍然是大约每秒创建一次块,但是可以禁用空块或设置块创建间隔。在前一种情况下,当有新的交易或应用哈希更改时,将创建块。要将 Tendermint 配置为不产生空块,除非有交易或应用程序哈希更改,运行 Tendermint 时附加这个标志:tendermint node --consensus.create_empty_blocks=false,或者通过设置配置 config.toml 文件:

[consensus]
create_empty_blocks = false

块间隔设置允许在创建每个新的空块之间有一个延迟(以秒为单位)。它是通过 config.toml 设置的:

[consensus]
create_empty_blocks_interval = 5

使用此设置,如果没有生成其他块,则每 5 秒生成一个空块,而不考虑 create_empty_blocks 的值。

broadcast(广播API)

我们使用 broadcast_tx_commit 端点发送交易。当交易被发送到 Tendermint 节点时,它将通过应用程序的 CheckTx 运行。如果它通过 CheckTx,它将被包括在内存池中,广播给其他节点,并最终包含在一个块中。由于处理交易有多个阶段,提供多个端点来广播交易:

/broadcast_tx_async  // 立即返回,而无需等待是否该交易有效
/broadcast_tx_sync  // 返回通过 CheckTx 运行该交易的结果
/broadcast_tx_commit  // 等待交易在一个块中提交或达到某个超时,但如果交易没有通过CheckTx,则会立即返回

broadcast_tx_commit 的返回值包括两个字段 check_txdeliver_tx,用于修饰通过这些 ABCI 消息运行交易的结果。

使用 broadcast_tx_commit 的好处是,请求在交易提交后返回(即包含在一个块中),但这可能需要一秒钟的时间。要得到一个快速的结果,可以使用broadcast_tx_sync,但是交易要到稍后才会提交,到那时它对状态的影响可能会发生变化。

tendermint网络

运行 tendermint init 时,在 ~/.tendermint/config 中创建两个文件 genesis.jsonpriv_validator.json

priv_validator.json 实际上包含一个私钥,因此应该绝对保密;现在我们使用纯文本。 注意 last_ 字段,它用于防止我们签署冲突的消息。还要注意 priv_validator.json 中的 pub_key (公钥) 也出现在 genesis.json 中。

genesis.json(创世) 文件包含可能参与共识的公钥列表及其相应的投票权。超过 2/3 的投票权必须是活跃的(即相应的私钥必须产生签名),共识才能取得进展。

关于节点和网络原文档:这里

小结

作为一个通用的区块链引擎,Tendermint 与想要运行的应用程序无关。因此,要运行一个完整的区块链来做一些有用的事情,您必须启动两个程序:一个是 Tenderint Core,另一个是您的应用程序,它可以用任何编程语言编写。回想一下ABCI, Tendermint Core 处理所有的 p2p 和协商一致的东西,当它们需要验证时,或者当它们准备提交到一个块时,就会将交易转发给应用程序。

Tendermint 中文文档

如果您有兴趣,可以参考这里原文