# 使用 Tendermint

这是使用命令行中的 tendermint 程序的指南。 它只假设您已经安装了 tendermint 二进制文件,并且对 Tendermint 和 ABCI 有一些基本的概念。

您可以看到帮助菜单上的 tendermint --help,以及版本号上的 tendermint version

# 根目录

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

# 初始化

运行以下命令初始化根目录:

tendermint init
1

这将在 $TMHOME/config 中创建一个新的私钥 (priv_validator.json)和一个包含关联公钥的创世文件(genesis.json)。这就是运行带有一个验证者的本地测试网络所需要的全部内容。

有关更详细的初始化,请参见 tesnet 命令:

tendermint testnet --help
1

# 创世

$TMHOME/config/ 中的 genesis.json 文件定义了区块链生成时的初始 TendermintCore 状态(参见定义)。

# 字段

  • 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 的初始分发)。

# 示例 genesis.json

{
  "genesis_time": "2018-11-13T18:11:50.277637Z",
  "chain_id": "test-chain-s4ui7D",
  "consensus_params": {
    "block_size": {
      "max_bytes": "22020096",
      "max_gas": "-1"
    },
    "evidence": {
      "max_age": "100000"
    },
    "validator": {
      "pub_key_types": [
        "ed25519"
      ]
    }
  },
  "validators": [
    {
      "address": "39C04A480B54AB258A45355A5E48ADDED9956C65",
      "pub_key": {
        "type": "tendermint/PubKeyEd25519",
        "value": "DMEMMj1+thrkUCGocbvvKzXeaAtRslvX9MWtB+smuIA="
      },
      "power": "10",
      "name": ""
    }
  ],
  "app_hash": ""
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 运行

要运行 Tendermint 节点,请使用

tendermint node
1

默认情况下,Tendermint 将尝试连接到 127.0.0.1:26658 上的 ABCI 应用程序。如果安装了 kvstore ABCI 应用程序,请在另一个窗口中运行它。如果你不这样做,杀死 Tendermint,运行一个进程中版本的 kvstore 应用程序:

tendermint node --proxy_app=kvstore
1

几秒钟后,您应该会看到块开始流入。注意,即使没有交易,也会定期生成块。要修改此设置,请参见下面的 没有空块

Tendermint支持进程中版本的 counterkvstorenoop 应用程序,这些应用程序附带 abci-cli 作为示例。如果是用 Go 编写的,你可以很容易地用 Tendermint 编译自己的应用程序。如果您的应用程序不是用 Go 编写的,那么只需在另一个进程中运行它,并使用 --proxy_app 标志指定它正在监听的套接字的地址,例如:

tendermint node --proxy_app=/var/run/abci.sock
1

# 交易

要发送交易,使用 curl 向 Tendermint RPC 服务器发出请求,例如:

curl http://localhost:26657/broadcast_tx_commit?tx=\"abcd\"
1

我们可以看到链在 /status 端点处的状态:

curl http://localhost:26657/status | json_pp
1

特别是 latest_app_hash

curl http://localhost:26657/status | json_pp | grep latest_app_hash
1

在浏览器中访问 http://localhost:26657,查看其他端点的列表。有些不接受参数(如 /status),而另一些指定参数名并使用 _ 作为占位符。

::: 提示 找到 RPC 文档这里 :::

# 格式化

发送/格式化交易时应考虑以下细微差别:

对于 GET

要发送 UTF8 字符串字节数组,请引用 tx 参数的值:

curl 'http://localhost:26657/broadcast_tx_commit?tx="hello"'
1

发送一个 5 字节的交易: "h e l l o" [68 65 6c 6c 6f].

注意 URL 必须用单引号括起来,否则 bash 将忽略双引号。要避免单引号,请转义双引号:

curl http://localhost:26657/broadcast_tx_commit?tx=\"hello\"
1

使用特殊字符:

curl 'http://localhost:26657/broadcast_tx_commit?tx="€5"'
1

发送一个 4 字节的交易: "€5" (UTF8) [e2 82 ac 35].

要发送原始十六进制,省略引号,并在十六进制字符串前面加上 0x

curl http://localhost:26657/broadcast_tx_commit?tx=0x01020304
1

发送一个 4 字节的交易: [01 02 03 04].

With POST (using json), the raw hex must be base64 encoded:

curl --data-binary '{"jsonrpc":"2.0","id":"anything","method":"broadcast_tx_commit","params": {"tx": "AQIDBA=="}}' -H 'content-type:text/plain;' http://localhost:26657
1

发送相同的 4 字节交易:[01 02 03 04].

注意,原始十六进制不能在 POST 交易中使用。

# 复位

警告:不安全只有在开发中这样做,并且只有在您能够承受丢失所有区块链数据的代价时才这样做!

要重置区块链,请停止节点并运行:

tendermint unsafe_reset_all
1

该命令将删除数据目录并重置私有验证者和地址簿文件。

# 配置

Tendermint使用了 config.toml 配置。有关详细信息,请参见配置规范

值得注意的选项包括应用程序的套接字地址(proxy_app)、Tendermint对等点的监听地址(p2p.laddr)和RPC服务器的监听地址(rpc.laddr)。

配置文件中的一些字段可以用标记覆盖。

# 没有空块

虽然 tendermint 的默认行为仍然是大约每秒创建一次块,但是可以禁用空块或设置块创建间隔。在前一种情况下,当有新的交易或应用哈希更改时,将创建块。

要将 Tendermint 配置为不产生空块,除非有交易或应用程序哈希更改,运行 Tendermint 时附加这个标志:

tendermint node --consensus.create_empty_blocks=false
1

或者通过设置配置 config.toml 文件:

[consensus]
create_empty_blocks = false
1
2

记住:因为默认值是 创建空块,所以避免空块需要将配置选项设置为 false

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

[consensus]
create_empty_blocks_interval = 5
1
2

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

# 广播 API

前面,我们使用 broadcast_tx_commit 端点发送交易。当交易被发送到 Tendermint 节点时,它将通过应用程序的 CheckTx 运行。如果它通过 CheckTx,它将被包括在内存池中,广播给其他节点,并最终包含在一个块中。

由于处理交易有多个阶段,我们提供多个端点来广播交易:

/broadcast_tx_async
/broadcast_tx_sync
/broadcast_tx_commit
1
2
3

它们分别对应于不处理、通过内存池的处理和通过块的处理。也就是说,broadcast_tx_async 将立即返回,而无需等待是否该交易有效,而 broadcast_tx_sync 将返回通过 CheckTx 运行该交易的结果。使用 broadcast_tx_commit 将等待交易在一个块中提交或达到某个超时,但如果交易没有通过 CheckTx,则会立即返回。broadcast_tx_commit 的返回值包括两个字段 check_txdeliver_tx,用于修饰通过这些 ABCI 消息运行交易的结果。

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

注意内存池不提供强保证 - 仅仅因为交易通过了 CheckTx (即。,是否被接受进入内存池) 并不意味着它将被提交,因为在它们的内存池中包含交易的节点可能在它们提交之前崩溃。 有关更多信息,请参见内存池预写日志

# Tendermint 网络

运行 tendermint init 时,在 ~/.tendermint/config 中创建两个文件 genesis.jsonpriv_validator.json。这个 genesis.json 可能看起来像:

{
  "validators" : [
    {
      "pub_key" : {
        "value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
        "type" : "tendermint/PubKeyEd25519"
      },
      "power" : 10,
      "name" : ""
    }
  ],
  "app_hash" : "",
  "chain_id" : "test-chain-rDlYSN",
  "genesis_time" : "0001-01-01T00:00:00Z"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

和这个 priv_validator.json

{
  "last_step" : 0,
  "last_round" : "0",
  "address" : "B788DEDE4F50AD8BC9462DE76741CCAFF87D51E2",
  "pub_key" : {
    "value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
    "type" : "tendermint/PubKeyEd25519"
  },
  "last_height" : "0",
  "priv_key" : {
    "value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
    "type" : "tendermint/PrivKeyEd25519"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

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

还要注意 priv_validator.json 中的 pub_key (公钥) 也出现在 genesis.json 中。

创世文件包含可能参与共识的公钥列表及其相应的投票权。超过 2/3 的投票权必须是活跃的(即相应的私钥必须产生签名),共识才能取得进展。在我们的例子中,创世文件包含了 priv_validator.json 的公钥。因此,一个使用默认根目录启动的 Tendermint 节点将能够取得进展。投票权使用 int64,但必须是正数,因此范围是:0 到9223372036854775807。 由于目前提议人选择演算法的工作原理,我们不建议投票权大于 10^12 (即。1万亿)。

如果我们想要添加更多的节点网络,我们有两个选择:我们可以添加一个新的验证者节点,将参与共识,提出区块并对其进行表决,或者我们可以添加一个新的非验证者节点,没有直接参与,但将验证和跟上共识协议。

# 节点

# 种子

种子节点是一个节点,它传递它们知道的其他节点的地址。这些节点不断地在网络中爬行,试图获得更多的节点。种子节点中继到本地地址簿中的地址。一旦这些在地址簿中,您将直接连接到这些地址。 基本上种子节点的工作就是转发每个人的地址。一旦收到足够的地址,就不会连接到种子节点,因此通常只在第一次启动时需要它们。种子节点将在发送给您一些地址后立即断开与您的连接。

# 持续节点

持续节点是你想要经常联系的人。如果断开连接,您将尝试直接连接回它们,而不是使用地址簿中的另一个地址。在重新启动时,无论地址簿的大小,您总是试图连接到这些节点。

默认情况下,所有节点都中继它们知道的节点。这称为对等交换协议(PeX)。使用 PeX,节点将广播已知的节点并形成一个网络,将节点地址存储在地址簿中。因此,如果您有一个活动的持久节点,则不必使用种子节点。

# 连接到节点

要在启动时连接到节点,请在 $TMHOME/config/config.toml 中指定它们。或者在命令行上。使用 seeds 指定种子节点,使用 persistent_peers 指定你的节点将与之保持持久连接的节点。

例如,

tendermint node --p2p.seeds "f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656,0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"
1

或者,您可以使用 RPC 的 /dial_seeds 端点为要连接到的正在运行的节点指定种子:

curl 'localhost:26657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"\]'
1

注意,启用 PeX 后,在第一次启动之后不需要种子。

如果您希望 Tendermint 连接到特定的地址集,并与每个地址保持持久连接,可以使用--p2p.persistent_peers 标志或者相应设置 config.toml 或者 /dial_peers RPC 端点在不停止 Tendermint 核心实例的情况下执行此操作。

tendermint node --p2p.persistent_peers "429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656,96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"

curl 'localhost:26657/dial_peers?persistent=true&peers=\["429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656","96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"\]'
1
2
3

# 添加一个非验证者

添加非验证者很简单。复制原始的 genesis.json~/.tendermint/config。启动节点,根据需要指定种子节点或持久节点。如果没有指定种子节点或持久对等节点,则节点不会生成任何块,因为它不是验证者,也不会听到任何块的消息,因为它没有连接到其他节点。

# 添加一个验证者

添加新验证者的最简单方法是在启动网络之前在 genesis.json 中进行。例如,我们可以创建一个新的 priv_validator.json。,然后将它的 pub_key 复制到上面的创世文件中。

通过这个命令我们可以生成一个新的 priv_validator.json

tendermint gen_validator
1

现在我们可以更新创世文件了。例如,如果新的priv_validator.json 的如下:

{
  "address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
  "pub_key" : {
    "value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
    "type" : "tendermint/PubKeyEd25519"
  },
  "priv_key" : {
    "value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
    "type" : "tendermint/PrivKeyEd25519"
  },
  "last_step" : 0,
  "last_round" : "0",
  "last_height" : "0"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

然后新的 genesis.json 将是:

{
  "validators" : [
    {
      "pub_key" : {
        "value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
        "type" : "tendermint/PubKeyEd25519"
      },
      "power" : 10,
      "name" : ""
    },
    {
      "pub_key" : {
        "value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
        "type" : "tendermint/PubKeyEd25519"
      },
      "power" : 10,
      "name" : ""
    }
  ],
  "app_hash" : "",
  "chain_id" : "test-chain-rDlYSN",
  "genesis_time" : "0001-01-01T00:00:00Z"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

更新~/.tendermint/config 中的 genesis.json。复制创世文件和新的 priv_validator.json到新机器上 ~/.tendermint/config

现在在两台机器上运行 tendermint node,并使用其中任何一台 --p2p.persistent_peers/dial_peers 让他们连接该节点。 他们应该开始制作区块,并且只会在他们都在线的情况下继续这样做。

要使 Tendermint 网络能够容忍一个验证者失败,至少需要四个验证者节点(例如,2/3)。

支持在活动网络中更新验证者,但必须由应用程序开发人员显式编程。有关详细信息,请参阅应用程序开发人员指南

# 本地网络

要在本地运行网络,比如在一台机器上,您必须更改 config.toml (或使用标志)中的 _laddr 字段,以便不同套接字的侦听地址不会冲突。此外,必须在 config.toml 中设置 addr_book_strict=false。否则,Tendermint 的 p2p 库将拒绝连接到具有相同 IP 地址的节点。

# 升级

看到 UPGRADING.md 指南。您可能需要在主要中断版本之间重置链。 尽管如此,我们预计 Tendermint 在未来会有更少的中断版本(尤其是 1.0 版本之后)。