跳转至

使用 Quorum 构建区块链

在上一章中,我们看到了什么是 DApp、DLT 和区块链。我们还看到了一些流行的区块链 DApps 的概述。目前,以太坊是仅次于比特币的最受欢迎的 DApp。在这一章中,我们将学习如何使用 Quorum 构建许可的基于区块链的 DApps。我们将通过探索 Quorum 支持的所有各种共识协议、它的许可和隐私特性,以及最终使我们能够快速部署 Quorum 网络的工具,来深入了解 Quorum。

在本章中,我们将讨论以下主题:

  • 以太坊中的用户帐户
  • 什么是 Merkle 树,它在区块链有什么用途?
  • 伊斯坦布尔拜占庭容错 ( IBFT )和 Raft 是如何工作的?
  • 法定人数支持的各种实现隐私的机制
  • 设置星座、Raft 和 IBFT 网络
  • 与 Quorum 相关的各种第三方工具或库

法定人数概述

Quorum 是一个许可的分散平台,允许我们在其上部署 DApps。DApps 是使用一个或多个智能合约创建的。智能合同是完全按照程序运行的程序,没有任何停机、审查、欺诈或第三方接口的可能性。在 Quorum 中,智能合同可以用 Solidity、LLL 或 Serpent 编写。稳健是首选。智能合约可以有多个实例。每个实例由一个唯一的地址标识,您可以在同一个法定网络上部署多个 DApps。

在以太坊,有一种内部货币叫做以太。要部署或执行智能合同,你需要向矿工支付以太,因为 Quorum 是以太坊的一个分支,这里也存在同样的事情。但是在 Quorum 中,以太是没有价值的,在创世区块会产生固定数量的以太,之后就再也不会产生以太了。用户帐户和智能合约都可以持有以太网。在 Quorum 中,您需要一些以太网来执行网络上的交易,但是以太网不会被扣除,并且将以太网发送到另一个帐户也不会扣除以太网,因此您可以说 Quorum 中的以太网提供了一种方法来跟踪帐户的所有者,如果任何可疑的事情都是通过帐户跟踪以太网转移,并且还提供了一种方法来进行交易,您需要从一个允许的成员那里获得许可;也就是说,从网络成员那里获得一些乙醚。

目前,Quorum 支持三种共识协议:QuorumChain、IBFT 和 Raft。在本书中,我们将只学习 Raft 和 IBFT,因为它们是最常用的。对于隐私,它支持两种机制:零知识安全层协议和私有契约。我们将学习私人合同,但不会涵盖 ZSL,因为它仍然没有生产准备就绪。

以太坊账户

要创建一个帐户,我们只需要一个非对称密钥对。生成非对称密钥对的算法有Rivest–sha mir–ad leman(RSA)和 E 椭圆曲线密码 ( ECC )。以太坊用的是 ECC。ECC 有各种曲线。这些弯道有不同的速度和安全性。以太坊使用 secp256k1 曲线。进入 ECC 及其曲线需要数学知识,使用以太坊构建 DApps 不需要深入理解。

以太坊使用 256 位加密。以太坊私钥和公钥是一个 256 位的数字。由于处理器不能表示如此大的数字,因此它总是被编码为长度为 64 的十六进制字符串。

每个账户都由一个地址代表。一旦我们有了生成地址所需的密钥,下面是生成地址的过程,下面是从公钥生成地址的过程:

  1. 首先,生成公钥的 Keccak-256 散列。它会给你一个 256 位的数字。
  2. 丢弃前 90 位和 12 个字节。现在你应该有 160 位二进制数据(20 字节)。
  3. 现在,将地址编码为十六进制字符串。所以,最后,你会有一个 40 个字符的字节串,这是你的账户地址。

现在,任何人都可以发送以太到这个地址,然后您可以从这个地址签名和发送交易。

什么是以太坊交易?

事务是一个数据包,用于将数据从一个帐户转移到另一个帐户或合同,调用合同的方法,或部署新的合同。一笔交易正在使用椭圆曲线数字签名算法 ( ECDSA ),这是一种基于 ECC 的数字签名算法。交易包含识别发送者并证明其意图的签名、要转移的乙醚量、交易执行允许采取的最大计算步骤数(称为气体限制)以及交易发送者愿意为每个计算步骤支付的成本(称为气体价格)。所用天然气和天然气价格的乘积称为交易费用

在许可网络中,以太是没有价值的。在法定网络中,以太是在起源块中提供的,而不是在运行时动态生成的。你需要在创世街区提供乙醚。你需要提供 gas 来防止攻击,比如无限循环。挖掘交易时,不会从帐户中扣除乙醚。

如果事务的目的是调用契约的方法,它也包含输入数据,或者如果它的目的是部署契约,那么它可以包含初始化代码。要发送以太网或执行合同方法,您需要向网络广播事务。发送者需要用其私钥对交易进行签名。

如果我们确信一项交易将永远出现在区块链的同一个地方,那么这项交易就被认为是得到了确认。在以太坊的工作证明中,建议在假设交易被确认之前,等待交易出现在最新块下面的 15 个块(即,等待 15 个确认),因为有分叉的机会,并且交易从区块链中消失。但是,在 Quorum's Raft 或 IBFT 中,一旦交易出现在其中一个区块中,我们就可以说它被确认了,因为没有分叉的可能性。

什么是 Merkle 树?

在我们进入区块链街区的 Merkle 根是什么之前,让我们了解一下区块链的结构。一个区块由两部分组成;第一部分是块头,第二部分是该块的事务集。块头包含诸如前一个块散列(它实际上是前一个块头的散列)、时间戳、Merkle 根以及与达成共识相关的信息。

同步时,在下载块时,节点下载块头和块的事务。现在,接收节点如何知道这些事务实际上是该块的一部分并且顺序正确呢?每个块由唯一的散列来标识,但是块散列不是块报头的一部分,并且在下载块之后由每个节点唯一地计算;因此,我们不能使用块散列的概念。相反,我们可以依赖类似事务散列的东西;存储在块头中的哈希,通过合并所有事务并对其进行哈希运算来计算。这个想法将完美地工作,我们可以检测是否有任何事务丢失或包含额外的事务,或者事务的顺序是否正确。

Merkle 根是事务散列方法的一种替代方法,但它提供了另一个主要优点:它允许网络有少量节点。当然,我们可以在没有 Merkle 根的情况下实现区块链,但是如果网络中需要轻型节点,则需要使用 Merkle 根。轻型节点只下载块头,但不下载事务,但它仍然应该能够向客户端提供所有 API。例如:智能手机不能拥有完整的区块链,因为它的尺寸可能非常大;因此,我们可以在智能手机中安装一个轻客户端。

让我们先了解一下,相对于区块链,什么是二叉 Merkle 树。散列树或 Merkle 树是这样一种树,其中每个叶节点是事务的散列,每个非叶节点是其子节点的散列的散列。散列树允许高效和安全地验证哪些事务是块的一部分。每个积木都有自己的 Merkle 树。当每个父母都有两个孩子时,Merkle 树被称为二叉树。二进制 Merkle 树是在区块链使用的。这是一个二进制 Merkle 树的例子:

在上图中,首先计算每个事务的单个哈希。然后,他们被分成两组。然后,为每一对计算两个散列的散列。这个过程将继续下去,直到我们得到一个名为 Merkle root 的散列。如果有奇数笔交易,则复制最后一笔交易,使交易总数为偶数。

现在,在下载完整的块、块头和块的事务时,节点可以通过形成二进制 Merkle 树并检查生成的 Merkle 根与包含在块头中的相同来验证事务集是否正确。当然,如前所述,这可以在没有 Merkle 树的情况下完成。

一个轻量级节点可以利用 Merkle 树为客户机提供请求服务。例如,轻型节点可以向完整节点发出请求,询问特定事务是否在块中提交,如果事务在块中提交,完整节点将回复块号和 Merkle 证明。如果全节点提供一个块号,光节点不能只相信全节点,因此全节点也提供 Merkle 证明。为了理解什么是 Merkle 证明,让我们看前面的图表和一个场景,其中 light 节点询问 full 节点 TxD 是否被提交。现在,full 节点连同一个子树一起返回块号,子树是HABCDT5、HABT9、 H CDH CH D 。这个子树是 Merkle 证明。现在,轻型客户端可以得到这个 Merkle 证明并验证它。验证将包括查看 Merkle 证明是否被正确构造,以及 Merkle 证明的 Merkle 根是否与完整节点声称事务所在的块的块头中存在的 Merkle 根相同。您一定想知道,如果一个完整的节点声称事务即使在提交后也没有提交,该怎么办?在这种情况下,解决这个问题的唯一方法是请求多个完整的节点,而不太可能所有的节点都是谎言。没有 Merkle 树就无法实现这一功能。**

以太坊区块链更加复杂。现在,假设一个以太坊光节点想要知道一个账户的余额,从一个智能合同中读取数据,找到一个交易的气体估计,等等,那么对于这个简单的交易,binary Merkle 将不能提供这个功能。因此,在以太坊中,每个块头不仅仅包含一棵 Merkle 树,而是包含三棵用于三种对象的树:

  • 处理
  • 交易收据(本质上是显示每笔交易效果的数据)
  • 状态

因为我们现在有三棵树,所以让我们举一个高级查询例子,一个轻型节点将对一个完整节点进行查询。查询是假装在这个合同上运行这个事务。交易收据和新状态是什么 这是由状态树处理的,但是它的计算方式更复杂。这里,我们需要构造一个可以成为 Merkle 态跃迁的证明。本质上,这是一个证明:如果您在根为 S 的状态上运行事务 T ,结果将是一个根为S’的状态,带有事务收据 R 。为了计算状态事务证明,完整节点在本地创建一个假块,将状态设置为 S ,并在应用事务时假装为轻节点。也就是说,如果应用事务的过程要求轻型节点确定账户的余额,则轻型节点进行余额查询。如果轻型节点需要检查特定契约的存储中的特定项目,轻型节点会进行查询,依此类推。完整节点正确地响应它自己的所有查询,但是跟踪它发回的所有数据。完整节点然后将来自所有这些请求的组合数据发送给轻型节点作为证明。然后,轻型客户端执行完全相同的过程,但是使用提供的证据作为其数据库,而不是向完整节点发出请求,并且如果其结果与完整节点声明的结果相同,则轻型客户端接受完整节点声明的输出。

对于状态,以太坊使用 Merkle Patricia 树而不是二叉树。对于状态树,情况更复杂。以太坊中的状态本质上由一个键-值映射组成,其中键是地址,值是帐户余额、随机数、代码和每个帐户的存储空间(存储空间本身是一棵树)。要了解 Merkle Patricia 树是如何工作的,请访问https://easythereentropy . WordPress . com/2014/06/04/understanding-the-ether eum-trie/。

在企业区块链中,没有使用轻客户端,因为节点代表一个企业,而企业拥有运行完整节点的基础架构。

区块链的分叉是什么?

当节点之间关于区块链的有效性存在冲突时,即网络中碰巧有一个以上的区块链时,就称发生了分叉。有三种叉子:普通的、软的和硬的。

当有两个或两个以上的积木在同一高度时,就发生了规则的分叉。这是暂时的冲突,会自动解决。这是通过节点选择最精确的区块链来解决的。例如,在工作证明中,如果两个矿工同时开采一个区块,那么就会产生一个常规分叉。并且,这通过选择具有最高难度的区块链作为最准确的一个来解决。

相比之下,软分叉是向后兼容的对区块链协议的任何改变。比方说,新规则可能只允许 1 MB 的数据块,而不是 2 MB 的数据块。未升级的节点仍会将新事务视为有效(在本例中,1 MB 小于 2 MB)。但是,如果未升级的节点继续创建块,它们创建的块将被升级的节点拒绝。因此,如果网络中的少数节点被升级,那么它们将形成的链将变得不太准确,并被未升级节点创建的区块链覆盖。当网络中的大多数节点升级它们的节点软件时,软分叉被解决。

硬件是一种软件升级,它向网络引入了旧软件没有的新规则。你可以把硬叉看作是规则的扩展。例如,允许块大小为 2 MB 而不是 1 MB 的新规则将需要一个硬分叉。继续运行旧版本软件的节点会将新事务视为无效。因此,只有当网络中的所有节点升级它们的节点软件时,分叉才能被解决。在那之前,网络中会有两个不同的区块链。

你一定听说过比特币和以太坊叉子。例如,比特币现金和以太坊经典是硬分叉的形成。网络中的许多矿工和节点不同意新协议而选择运行旧软件,并从网络中分裂出来,形成不同的网络。

Raft 共识

让我们看看 Raft 共识协议是如何工作的,它将使我们足够舒适地构建 DApps。我们不会深入研究筏子,因为没有必要。

Raft 用于半可信网络中,需要更快的阻塞时间(毫秒级而不是秒级)和单一确认(没有常规分叉)。

网络中的每个节点都保存着网络中所有其他节点的列表,而不管它们是否启动和运行。Raft 集群中的服务器可以是领导者,也可以是跟随者,并且在领导者不可用的情况下,可以作为选举的候选人。一次只能有一个领导者。领导者负责创建块并将其发送给追随者。它通过发送心跳消息来定期通知追随者它的存在。每个跟随者都有一个被称为选举超时的超时时间(通常在 150 到 300 毫秒之间),在这个时间里,它期待来自领导者的心跳。每个节点使用 120-300 毫秒范围内的随机选举超时。选举超时在接收到心跳时重置。如果没有接收到心跳,跟随者将其状态更改为候选,并开始领导者选举,以选举网络中的新领导者。当候选人开始领导人选举时,它基本上以自己为新领导人的目的,如果超过 50%的节点投票支持它,它就成为领导人。如果在特定的超时时间内没有选出领导者,则开始新的领导者选举过程。没必要深入了解领导人选举过程。

Raft 的设计方式是,Raft 网络需要 50%以上的节点可用于新事务,以提交给区块链;如果群集有 2 * F + 1 个节点,它可以容忍 F 个故障,并且仍然正常工作。如果超过 F 个节点发生故障,那么应用程序将会失败,一旦集群再次有超过 F 个节点正常工作,应用程序将再次恢复正常工作。如果网络中有超过 50%的节点不可用,即使是领导者选举也会失败。

来自每个节点的每个事务被发送到网络中的每个其他节点。领导者负责创建和广播块。当领导者创建块时,它会首先将块发送给所有的追随者,一旦超过 50%的追随者接收到块,领导者会将块提交给其区块链,然后向追随者发送提交消息,以便追随者也将块提交给他们的区块链。在跟随者不可用的情况下,领导者无限期地重试请求,直到该块最终被所有跟随者提交。这一过程确保了一旦一个区块被提交给区块链,它就永远不会被撤销。甚至领导人选举过程也确保无论谁被选为领导人,他的区块链都是最新的。

在 Quorum 中,默认的阻塞时间是 50 毫秒,您可以根据需要更改它。因此,每隔 50 毫秒,就会创建一个块,但是请记住,如果没有事务,则不会创建块;Raft 中不创建空块。领导者可以在提交前一个块之前创建新的块并将其发送给追随者,并且块创建是异步的。当然,他们是连续作案的。当一个节点启动时,它只从领头节点检索丢失的块,而不从网络中的其他节点检索。

为了让 Raft 集群正常工作,服务器向集群中的每个服务器发送心跳请求并接收响应的平均时间必须小于选举超时,这一点非常重要。此外,领导者没有办法删除或修改提交的块;领导者只能将新区块附加到区块链。

伊斯坦布尔拜占庭容错

让我们看看 IBFT 共识协议是如何在一个能让我们足够舒适地构建 DApps 的水平上工作的。我们不会深入 IBFT,因为没有必要。

IBFT 是一种权威证明协议。在 IBFT 中,有两种类型的节点:验证者节点(当它们链接到物理实体时被称为权威)和常规节点。授权节点是创建块的节点。IBFT 用于需要 BFT 的网络中,几秒钟的阻塞时间就足够了,我们需要一次确认(没有常规分叉)。

在一个 N 验证器节点网络中,即 F = (N-1)/3 ,系统最多可以容忍 F 个拜占庭或崩溃节点。在 IBFT,默认的阻塞时间在 1 到 10 秒之间,Quorum 允许您自定义这个时间。

在 IBFT,一轮包括创建和提交一个新的区块到区块链。一旦在 (2F + 1) 验证器区块链中提交了新的块,就开始新的一轮。在每一轮块创建之前,验证者将选择其中一个作为提议者。提议者是负责创建块的验证者。对于要提交给区块链的块,它必须由至少 (2F + 1) 个验证者签名。因此,在每一轮都有一个过程,包括在目的器和其他验证器之间发送和接收各种消息,以同意新的块。

Quorum 支持两种选择目的的算法:循环法和粘滞目的法。默认情况下使用循环调度,在循环调度算法中,以循环调度方式选择目的。但是,在粘性目的算法中,单个验证器成为所有回合的目的器,并且如果目的器崩溃,则下一个验证器被选择为新的目的器,其再次保持所有回合的唯一目的器;目标保持不变,直到它失败。不管是循环法还是粘滞目的算法,如果目的器在 1-10 秒时间内未能提交一个块,则开始新一轮,下一个验证器成为新一轮的目的器。

如果网络设法有超过 F 个故障节点,那么这些故障节点可以通过拒绝签署块来防止新块的创建。当网络中的崩溃节点出现时,它可以从网络中的任何节点获得丢失的块。没有办法比 F 故障节点更能重写块。

验证器列表存储在 genesis 块的头中,头中的extraData字段包含验证器列表。对于第一轮,选择第一个验证器。报头还包含与 IBFT 相关的各种其他字段和细节,以帮助网络达成共识。

验证程序可以添加或删除验证程序。甚至在网络中添加或删除新的验证器都需要得到 2F + 1 验证器的同意。验证者同意或不同意添加或删除验证者的过程是手工完成的。这不可能是一个自动的过程,因为验证者可以开始添加他们自己的多个验证节点并危及网络。因此,手动过程确保其他验证器知道新的验证器是谁,并决定是否允许它。

你可以在 https://github.com/ethereum/EIPs/issues/650.深入了解 IBFT 是如何工作的

私人合同和星座

私有契约是 Quorum 为实现数据隐私而提供的一项现成功能。私有契约用于在两个或多个节点之间秘密共享信息,而其他节点无法看到这些信息。

我们来看看 Quorum 中的私人契约是什么。使用私有事务部署的契约被称为私有契约。私有事务基本上是这样一种事务,其有效负载(用于契约部署的契约代码或用于调用函数的函数参数、事务的数据部分)在区块链之外,在发送事务时提到的所选对等方列表之间点对点共享,并且通过用有效负载的散列替换实际有效负载,有效负载的散列被记录在区块链中。现在,网络中的节点检查它们是否具有散列到区块链中作为有效载荷存在的散列的内容,如果是,则它们执行原始有效载荷。法定人数构成了同一个区块链的两个不同的州:公立和私立州。私人交易形成私人国家,而公共交易形成公共国家。这些状态不能相互作用。但是,私人与私人之间的契约肯定可以相互影响。

Quorum 使用 constellation 来发送和接收私有事务的实际事务负载。Constellation 是 J.P. Morgan 打造的一款独立软件。Constellation 形成了一个节点网络,每个节点都公布了它们是接收方的公钥列表。每个节点公开一个 API,允许用户向一个或多个公钥发送有效载荷。该有效载荷在被传送到接收节点之前将被加密公钥。它通过 IPC 为应用程序公开 API,以连接到它们的星座节点并发送或接收数据。在高层次上,如果您连接到一个星座网络,那么您只需提及接收方的公钥,数据就会被加密并发送到与公钥对应的 IP 地址。当发送一个专用事务时,只有当有效载荷被成功发送到所有列出的星座节点时,公共密钥和事务的列表才被广播到区块链网络。如果任何列出的星座节点关闭,则事务失败,并且永远不会广播到区块链网络。

因此,基本上,在启动您的法定节点之前,您需要启动您的星座节点,然后在启动法定节点之前提供星座到法定节点的 IPC 路径。然后,您的法定节点使用星座来发送或接收私有事务。

私下交易不是实现法定人数隐私的最终解决方案。它们有各种各样的缺点。以下是其中的一些:

  • 一旦向节点列表发送了私有事务,就不能向该列表添加新节点。例如,如果您部署了一个用于银行间转账的私人合同。假设最初中央银行不是网络的一部分,后来如果他们决定加入,那么他们将无法监控交易,因为我们无法让他们看到私人合同,也无法让他们看到以前的银行转账。虽然他们可以看到新的私有事务,但是由于他们没有新的私有事务,所以他们无法执行事务,因此也就看不到输出。
  • 没有办法检查指向私有协定的私有事务是否具有在部署私有协定时使用的完全相同的公钥列表。这可能导致双重花费攻击;换句话说,您将能够两次转移相同的资产。例如,在部署合同时,您提到了三个节点 ABC 。现在,当 A 正在转移资产时,它可以将 C 从私有交易中排除,然后稍后通过创建新的私有交易将相同的资产转移到 CC 没有办法验证资产的新所有者是 B 。因此,私有交易不用于传输数字资产,但是私有交易可以用于所有其他形式的数据表示。
  • 您需要为星座节点建立自己的备份机制。所以,如果你的星座节点崩溃,那么它不会自动从星座网络取回有效载荷。

安装仲裁和星座

现在我们对定额组的共识协议、以太坊账户、交易和私人契约相当有信心。是时候建立一个法定网络了。在此之前,我们需要学习如何安装 Quorum 和 constellation。请记住,星座是可选的,只有在需要私人合同的情况下,才应该集成到法定网络中。

安装 Quorum 和 constellation 的最佳方式是构建源代码。在本书中,我们将只关注 Ubuntu 和 macOS 的步骤。你可以在 https://github.com/jpmorganchase/quorum 的找到 Quorum 的源代码,而你可以在 https://github.com/jpmorganchase/constellation.的找到星座的源代码

下面是从源代码构建仲裁的三个基本命令:

git clone https://github.com/jpmorganchase/quorum.git
cd quorum
make all

现在,进入build/bin/目录,您将找到geth可执行文件,这是运行法定节点的节点软件。此外,您将找到另一个名为bootnode的可执行文件,我们将用它来生成 enode ID。我们将在后面看到什么是 enode ID。

要安装 constellation,您需要一些先决条件。在 Ubuntu 中,运行以下命令来安装必备组件:

apt-get install libdb-dev libleveldb-dev libsodium-dev zlib1g-dev libtinfo-dev
curl -sSL https://get.haskellstack.org/ | sh
stack setup  

并且,在 macOS 中,运行以下命令来安装必备组件:


brew install berkeley-db leveldb libsodium
brew install haskell-stack
stack setup

现在,要安装 constellation,运行以下命令:

git clone https://github.com/jpmorganchase/constellation.git
cd constellation
stack install

现在,在您运行前面的命令并且它们被成功执行后,您将得到一条消息,说明constellation-node可执行文件的路径。将可执行文件从该路径移动到一个容易找到的位置。

建立你的第一个筏网

现在,我们已经成功地安装了 Quorum 和 constellation,是时候建立我们的第一个 Quorum 网络了。在设置网络之前,您需要决定是使用 Raft 还是 IBFT,并相应地进行规划和设置。我们将学习设置这两种网络。我们还将建立一个星座网络。

现在,让我们用星座建立一个筏网。我们还将了解一旦网络启动并运行,如何添加和删除新节点。我们将建立一个由四个节点组成的网络。

创建一个名为raft的目录。然后,将gethconstellation-node二进制文件放入其中。您可以使用gethconstellation-node--help选项找到可用的各种子命令和选项。

建立一个星座网络

现在,让我们首先创建四个星座节点。出于开发目的,我们将在同一台机器上运行所有四个节点。对于每个星座节点,我们必须生成一个单独的非对称密钥对。在raft目录中运行以下命令来创建密钥对:

./constellation-node --generatekeys=node1
./constellation-node --generatekeys=node2
./constellation-node --generatekeys=node3
./constellation-node --generatekeys=node4

这里,我们为每个星座节点生成一个公钥。但是,每个星座节点可以有多个公钥。在运行前面的命令时,它会要求您输入一个密码来加密密钥,但是您可以通过按下 Enter 键来跳过这个步骤。如果你想在运行星座节点的时候加密,你必须提供解密的密码。为了简单起见,我们不设置密码。

在启动一个 constellation 节点时,您需要传递各种必需的和可选的变量,比如通告给其他节点的 URL(它们可以到达)、监听的本地端口、存储有效负载的目录、公钥、私钥、TLS 设置等等。您可以将这些变量作为命令的选项或者以配置文件的形式传递给星座节点。让我们为每个星座节点创建一个配置文件,它将为星座节点启动提供这些设置。以下是星座节点的配置文件:

以下是constellation1.conf的代码:

url = "http://127.0.0.1:9001/"
port = 9001
storage = "dir:./cnode_data/cnode1/"
socket = "./cnode_data/cnode1/constellation_node1.ipc"
othernodes = ["http://127.0.0.1:9002/", "http://127.0.0.1:9003/", "http://127.0.0.1:9004/"]
publickeys = ["./cnode1.pub"]
privatekeys = ["./cnode1.key"]
tls = "off"

以下是constellation2.conf的代码:

url = "http://127.0.0.1:9002/"
port = 9002
storage = "dir:./cnode_data/cnode2/"
socket = "./cnode_data/cnode1/constellation_node2.ipc"
othernodes = ["http://127.0.0.1:9001/"]
publickeys = ["./cnode2.pub"]
privatekeys = ["./cnode2.key"]
tls = "off"

以下是constellation3.conf的代码:

url = "http://127.0.0.1:9003/"
port = 9003
storage = "dir:./cnode_data/cnode3/"
socket = "./cnode_data/cnode1/constellation_node3.ipc"
othernodes = ["http://127.0.0.1:9001/"]
publickeys = ["./cnode3.pub"]
privatekeys = ["./cnode3.key"]
tls = "off"

以下是constellation4.conf的代码:

url = "http://127.0.0.1:9004/"
port = 9004
storage = "dir:./cnode_data/cnode4/"
socket = "./cnode_data/cnode1/constellation_node4.ipc"
othernodes = ["http://127.0.0.1:9001/"]
publickeys = ["./cnode4.pub"]
privatekeys = ["./cnode4.key"]
tls = "off"

在这里,变量的名字揭示了变量的含义。这里需要注意的一件重要事情是,我们没有在最后三个节点中提供其他三个节点的 URL,因为 constellation 有一个内置的自动发现协议来查找网络中的节点。所以,这里第一个节点指向后三个节点,后三个节点与第一个节点有连接,但最终所有节点都能找到彼此。

现在,在不同的 shell 窗口中运行以下命令来启动星座节点:

./constellation-node constellation1.conf
./constellation-node constellation2.conf
./constellation-node constellation3.conf
./constellation-node constellation4.conf

生成 enodes

在 Raft 中,在建立网络之前,您必须决定网络中的节点总数,然后为每个节点生成一个 enode ID。然后,创建一个列出所有节点的 enode URL 的static-nodes.json文件,并将该文件提供给网络中的每个节点。一旦网络建立,向网络添加节点涉及不同的过程。

在更进一步之前,你需要知道以太坊的一个 enode 是什么。enode 是以 URI 的形式描述以太坊节点的一种方式。网络中的每个节点都有不同的 enode。enode 包含一个名为节点 ID 的 512 位公钥,用于验证来自网络上特定节点的通信。编码还包含 IP 地址和端口号以及节点 ID。与节点 ID 相关联的私钥被称为节点密钥

我们将建立一个包含三个节点的网络,然后动态添加第四个节点。使用以下三个命令生成所有四个节点的节点密钥:

./bootnode -genkey enode_id_1
./bootnode -genkey enode_id_2
./bootnode -genkey enode_id_3
./bootnode -genkey enode_id_4

前面的命令将创建私钥。现在,要找到节点 ID,您需要运行以下命令:

./bootnode -nodekey enode_id_1
./bootnode -nodekey enode_id_2
./bootnode -nodekey enode_id_3
./bootnode -nodekey enode_id_4

前面的命令不会创建任何新文件;取而代之的是,它们将简单地打印一个样本节点 URL,其中包含与相应的私钥相关联的实际节点 ID。比如:enode://[nodeID]@[IP]:[port]

现在,创建一个static-nodes.json文件并添加以下代码。确保用您生成的节点 id 替换节点 id:

[
 "enode://480cd6ab5c7910af0e413e17135d494d9a6b74c9d67692b0611e4eefea1cd082adbdaa4c22467c583fb881e30fda415f0f84cfea7ddd7df45e1e7499ad3c680c@127.0.0.1:23000?raftport=21000",
 "enode://60998b26d4a1ecbb29eff66c428c73f02e2b8a2936c4bbb46581ef59b2678b7023d300a31b899a7d82cae3cbb6f394de80d07820e0689b505c99920803d5029a@127.0.0.1:23001?raftport=21001",
 "enode://e03f30b25c1739d203dd85e2dcc0ad79d53fa776034074134ec2bf128e609a0521f35ed341edd12e43e436f08620ea68d39c05f63281772b4cce15b21d27941e@127.0.0.1:23002?raftport=21002"
]

这里,2300x端口用于以太坊协议通信,2100x端口用于 Raft 协议通信。

在以太坊中,static-nodes.json用来列出你一直想连接的一些节点的 enodes。使用这些节点,您的节点可以发现网络中的其他节点。但是,在 Quorum 的 Raft 中,该文件必须包含网络中所有节点的 enode,因为在 Raft 中,该文件用于达成共识,而在以太坊中,该文件用于节点发现。

创建帐户

现在,我们需要生成一个以太坊账户。我们现在这样做是因为在创建 genesis 块时,我们必须向网络提供以太网。因此,我们将向这个生成的帐户提供乙醚。以下是创建以太坊帐户的命令:

./geth --datadir ./accounts account new

在运行该命令时,它将要求输入密码来加密帐户。您可以按两次回车键跳过。这将使密码变成一个空字符串来解密帐户。在这里,--datadir选项用于指出存储密钥的位置。基本上,在accounts/keystore目录中,你会找到一个格式为UTC--DATE-TIME--ADDRESS的文件。将该文件重命名为key1。该文件存储帐户的私钥和地址。打开文件并复制地址,因为在创建创世区块时会用到它。

创造创世块

现在,最后一步是创建创世纪块。genesis 块总是硬编码在网络中。以下是创世纪板块内容。创建一个genesis.json文件,并将以下代码放入其中:

{
     "alloc": {
         "0x65d8c00633404140986e5e23aa9de8ea689c1d05": {
             "balance": "1000000000000000000000000000"
          }
     },
     "coinbase": "0x0000000000000000000000000000000000000000",
     "config": {
         "homesteadBlock": 0
     },
     "difficulty": "0x0",
     "extraData": "0x",
     "gasLimit": "0x7FFFFFFFFFFFFFFF",
     "mixhash": "0x00000000000000000000000000000000000000
       647572616c65787365646c6578",
     "nonce": "0x0",
     "parentHash": "0x00000000000000000000000000000000
       00000000000000000000000000000000",
     "timestamp": "0x00"
}

在这里,确保用您的帐户地址替换帐户地址0x65d8c00633404140986e5e23aa9de8ea689c1d05。这里,我们向0x65d8c00633404140986e5e23aa9de8ea689c1d05帐户提供了乙醚。

如果你想在一个法定网络中消除以太,你可以在启动geth时使用--gasPrice 0选项。因此,您不需要在 genesis 块中提供乙醚。但是,以太具有可追溯性的优势。

开始节点

现在,在我们启动节点之前,我们需要初始化它们并为每个节点创建数据目录;将static-nodes.json文件复制到每个节点的数据目录中,将账户密钥复制到数据目录中,用 genesis 块引导区块链。

以太坊节点的数据目录结构包括gethkeystore目录,以及一个static-nodes.json文件。keystore目录包含账户文件,geth目录包含所有其他与以太坊相关的数据,如区块链交易、状态和 enode 密钥。

以下是对所有节点执行所有初始化操作的命令:

#Configuring Node 1
#'keystore' dir stores acccounts and 'geth' dir stores all other data
mkdir -p qdata/node1/{keystore,geth} 
cp static-nodes.json qdata/node1
cp accounts/keystore/key1 qdata/node1/keystore
cp enode_id_1 qdata/node1/geth/nodekey
./geth --datadir qdata/node1 init genesis.json #bootstrap the blockchain

#Configuring Node 2
mkdir -p qdata/node2/geth
cp static-nodes.json qdata/node2
cp enode_id_2 qdata/node2/geth/nodekey
./geth --datadir qdata/node2 init genesis.json

#Configuring Node 3
mkdir -p qdata/node3/geth
cp static-nodes.json qdata/node3
cp enode_id_3 qdata/node3/geth/nodekey
./geth --datadir qdata/node3 init genesis.json

前面的命令不言自明。现在,运行以下命令来启动法定节点。在新的 shell 窗口中运行每个命令:

#Starting node 1
PRIVATE_CONFIG=constellation1.conf ./geth --datadir qdata/node1 --port 23000 --raftport 21000 --raft --ipcpath "./geth.ipc"

#Starting node 2
PRIVATE_CONFIG=constellation2.conf ./geth --datadir qdata/node2 --port 23001 --raftport 21001 --raft --ipcpath "./geth.ipc"

#Starting node 3
PRIVATE_CONFIG=constellation3.conf ./geth --datadir qdata/node3 --port 23002 --raftport 21002 --raft --ipcpath "./geth.ipc"

下面是我们提供的不同选项的含义:

  • PRIVATE_CONFIG:该变量用于使geth知道它需要向哪个星座节点发送私有净荷。它指向星座节点的配置文件。
  • --datadir:存储状态、交易、账户等的数据目录。
  • 这用于指定我们想要运行 Raft 共识。
  • --port:以太坊传输绑定的端口。
  • --raft-port:木排运输要绑定的港口。
  • --ipcpath:IPC 插座和管道的文件名。默认情况下,IPC 处于启用状态。

geth提供 JSON-RPC API 供客户端与之通信。geth使用 HTTP、WS 和 IPC 提供 JSON-RPC API。JSON-RPC 提供的 API 分为不同的类别。geth还提供了一个交互式 JavaScript 控制台,使用 JavaScript APIs 与它进行编程交互。交互控制台使用 JSON-RPC over IPC 与geth通信。稍后我们将了解更多这方面的内容。

现在,要打开node1的交互控制台,使用以下命令:

./geth attach ipc:./qdata/node1/geth.ipc

现在,我们已经完成了创建我们的第一个 Raft 网络。

动态添加或删除节点

现在,让我们动态添加第四个节点。任何节点都可以向网络中添加第四个节点。从node1开始补充吧。第一步是对node4进行初始化操作。为此,运行以下命令:

#Configuring Node 4
mkdir -p qdata/node4/geth
cp enode_id_4 qdata/node4/geth/nodekey
./geth --datadir qdata/node4 init genesis.json

注意,这里我们没有复制static-nodes.json文件,因为我们是动态添加的。现在,从第四个节点的交互控制台,运行以下代码行,将第四个对等节点添加到网络中:

raft.addPeer("enode://27d3105b2c1173792786ab40e466fda80edf9582cd7fa1a867123dab9e2f170be0b7e16d4065cbe81637759555603cc0619fcdf0fc7296d506b9c26c26f3ae0c@127.0.0.1:23003?raftport=21003") 

这里,用您生成的 ID 替换节点 ID。当您运行以下命令时,您将获得一个数字作为返回值。这个数字很重要,被称为节点的 Raft ID。Raft consensus 为每个节点分配一个唯一的 ID。文件中的第一个节点被赋予 Raft if 1,下一个节点被赋予 Raft ID 2,依此类推。第四个节点将具有 Raft ID 4。在启动第四个节点时,您将需要这个号码。现在,使用以下命令启动第四个节点:

PRIVATE_CONFIG=constellation4.conf ./geth --datadir qdata/node4 --port 23003 --raftport 21003 --raft --ipcpath "./geth.ipc" --raftjoinexisting 4

在前面的命令中,除了一个新选项--raftjoinexisting之外,所有内容看起来都很相似。在启动动态添加的节点时,我们需要指定这个选项,并将其分配给节点的 Raft ID。当使用raft.addPeer添加一个节点时,这个 Raft ID 会出现。

现在,让我们从网络中删除一个节点。让我们从static-nodes.json文件中删除第三个节点。该节点将拥有3raft ID。在节点 1 的交互式控制台中,运行以下代码从网络中删除第三个节点:

raft.removePeer(3)

现在,第三个对等体将从网络中移除。您现在可以使用admin.peers API 来检查连接到该节点的所有节点的列表。它应该是列表中的两个节点,网络中总共有三个节点。

如果在向网络添加或删除新节点时某个节点停机,那么一旦该节点启动并运行,该停机节点将会知道网络的变化。

构建您的第一个 IBFT 网络

我们将建立一个由六个节点组成的网络。前四个是验证器,另外两个是非验证器。我们不会在这个网络中添加星座。如果您想添加一个,说明与前面相同。

在 IBFT,每个验证器都使用一个从它的节点密钥导出的以太坊帐户进行唯一标识。与 Raft 类似,在 IBFT,在建立网络之前,您必须决定网络中的验证器总数,然后为每个验证器生成一个 enode。然后,我们创建一个static-nodes.json文件,列出所有验证节点的 enodes,并将这个文件提供给网络中的每个验证器。之后,从节点 id 中导出以太坊地址。最后,我们构造了extraData字段并创建了genesis文件。

在 IBFT 的情况下,没有必要创建static-nodes.json文件。您也可以使用admin.addPeer(url) API 连接节点。

安装 IBFT 工具

IBFT 软件包含用于配置 IBFT 网络、生成 e node、生成从节点密钥导出的地址、创建 genesis 块等的工具。为 IBFT 创建一个 genesis 块不像为 Raft 创建那么简单,因为有一个编码的extraData字段需要包含在列出验证器列表的 genesis 块中。

以下是安装 IBFT 工具的步骤:

cd ~
mkdir -p go/src/github.com/getamis
cd go/src/github.com/getamis
git clone https://github.com/getamis/istanbul-tools.git
export GOPATH=~/go
go get github.com/getamis/istanbul-tools/cmd/istanbul
cd ~/go/bin

现在,在~/go/bin目录中,您会发现一个名为istanbul的可执行文件。这是创造创世块的工具。创建一个名为ibft的目录,并将可执行文件移动到那里。

创造一个创世块

IBFT 工具可以自动创建一个创世纪块。同时,它还生成节点键、从节点键导出的地址和static-nodes.json文件。

运行以下命令来生成所有这些:

./istanbul setup --num 4 --nodes --verbose

现在,您将得到类似的输出:

validators
{
 "Address": "0x05a6245732c2350ba2ed64e840394c2239f8ad1f",
 "Nodekey": "eae5093e524bf14ba6e95c13591d6a785be9ea486b9e8e9c1281314f75a3d4f9",
 "NodeInfo":     "enode://bd1049d796f1b71bef17d428ce8db5f22e478ecbeb9513c57e90d93ca1e9ec107f4f4b43585556ca8bb3ab630f1f6543d0d4147f5d890e1fde301b2af1fd7a08@0.0.0.0:30303?discport=0"
}
{
 "Address": "0x97a80dc7a7e27f41ae006fa1253f1f105f77335c",
 "Nodekey": "decc1787fda1f4079511bcff92e83f868755c8e06636303c42cfb3cce554919e",
 "NodeInfo":     "enode://6344e12a9b3f4fd5c154ee13ebe5351a5460a44302fd493a5e742adf8a294b6dc112fab1fa8ff19dde0027373c96c51ab6254153877c9fadabfc057624e522f0@0.0.0.0:30303?discport=0"
}
{
 "Address": "0xf69faf33e8690e82b0043e9131e09bbbc394cbed",
 "Nodekey": "7e1a7660f4ec525096ebea34a7a3b78803138fbaaa3f61b7dc13439ce3e08c95",
 "NodeInfo": "enode://0955966accd8f36256e876790c9b66098675f7ac6bfc10b805d7356d66844cf696902b8dadb62c44cdb783db69197ebacc709ab1908229fe7e13be3f1eae35fe@0.0.0.0:30303?discport=0"
}
{
 "Address": "0x68795d3e326b553dc8b2c5739b87a9cb827037c8",
 "Nodekey": "9f0e0b268671c29c43a0976faa7e08fd20aae24219ad1db6dfc7e645413600c1",
 "NodeInfo": "enode://a76bf5be8ddd1b1b9bd8d46e5947ccef9c1ce492d4e8fe800e234e61be67a0dbd586e33afb4e17998dc53fa2ea5c72a8a0544c7baae45fc4c16c401c1de90a22@0.0.0.0:30303?discport=0"
}

static-nodes.json
[
 "enode://bd1049d796f1b71bef17d428ce8db5f22e478ecbeb9513c57e90d93ca1e9ec107f4f4b43585556ca8bb3ab630f1f6543d0d4147f5d890e1fde301b2af1fd7a08@0.0.0.0:30303?discport=0",
 "enode://6344e12a9b3f4fd5c154ee13ebe5351a5460a44302fd493a5e742adf8a294b6dc112fab1fa8ff19dde0027373c96c51ab6254153877c9fadabfc057624e522f0@0.0.0.0:30303?discport=0",
 "enode://0955966accd8f36256e876790c9b66098675f7ac6bfc10b805d7356d66844cf696902b8dadb62c44cdb783db69197ebacc709ab1908229fe7e13be3f1eae35fe@0.0.0.0:30303?discport=0",
 "enode://a76bf5be8ddd1b1b9bd8d46e5947ccef9c1ce492d4e8fe800e234e61be67a0dbd586e33afb4e17998dc53fa2ea5c72a8a0544c7baae45fc4c16c401c1de90a22@0.0.0.0:30303?discport=0"
]

genesis.json
{
 "config": {
 "chainId": 2017,
 "homesteadBlock": 1,
 "eip150Block": 2,
 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
 "eip155Block": 3,
 "eip158Block": 3,
 "istanbul": {
 "epoch": 30000,
 "policy": 0
 }
 },
 "nonce": "0x0",
 "timestamp": "0x5a213583",
 "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f89af8549405a6245732c2350ba2ed64e840394c2239f8ad1f9497a80dc7a7e27f41ae006fa1253f1f105f77335c94f69faf33e8690e82b0043e9131e09bbbc394cbed9468795d3e326b553dc8b2c5739b87a9cb827037c8b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0",
 "gasLimit": "0x47b760",
 "difficulty": "0x1",
 "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
 "coinbase": "0x0000000000000000000000000000000000000000",
 "alloc": {
 "05a6245732c2350ba2ed64e840394c2239f8ad1f": {
 "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
 },
 "68795d3e326b553dc8b2c5739b87a9cb827037c8": {
 "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
 },
 "97a80dc7a7e27f41ae006fa1253f1f105f77335c": {
 "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
 },
 "f69faf33e8690e82b0043e9131e09bbbc394cbed": {
 "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
 }
 },
 "number": "0x0",
 "gasUsed": "0x0",
 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

您将看到不同的地址、enodes 等等。现在,创建static-nodes.jsongenesis.jsonenode密钥文件,并将前面的内容放入其中。将节点关键文件名设置为enode_id_1enode_id_2enode_id_3enode_id_1。将 enode URLs 中的端口更改为23000230012300223003

现在,让我们生成一个以太坊账户,并在创世纪块中给它分配一些以太。乙醚不是动态生成的,因此我们需要预先供应。使用以下命令生成一个以太坊帐户:

./geth --datadir ./accounts account new

现在,将accounts/keystore中的文件名改为key1。然后复制地址,放入genesis文件,并分配一些余额。例如,如果我新生成的账户地址是0x65d8c00633404140986e5e23aa9de8ea689c1d05,那么我的genesis文件内容将如下:

{
     "config": {
         "chainId": 2017,
         "homesteadBlock": 1,
         "eip150Block": 2,
         "eip150Hash": 
           "0x000000000000000000000000000000000000000000
            0000000000000000000000",
         "eip155Block": 3,
         "eip158Block": 3,
         "istanbul": {
             "epoch": 30000,
             "policy": 0
         }
     },
     "nonce": "0x0",
     "timestamp": "0x5a213583",
     "extraData": "0x00000000000000000000000000000000000000000000
       00000000000000000000f89af8549405a6245732c2350ba2ed64e840
       394c2239f8ad1f9497a80dc7a7e27f41ae006fa1253f1f105f77
       335c94f69faf33e8690e82b0043e9131e09bbbc394cbed9468795
       d3e326b553dc8b2c5739b87a9cb827037c8b841000000000000000
       0000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000
       00000000000000000000000c0",
     "gasLimit": "0x47b760",
     "difficulty": "0x1",
     "mixHash": "0x63746963616c2062797a616e74696e65206661756c7
       420746f6c6572616e6365",
     "coinbase": "0x0000000000000000000000000000000000000000",
     "alloc": {
         "05a6245732c2350ba2ed64e840394c2239f8ad1f": {
             "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
         },
         "68795d3e326b553dc8b2c5739b87a9cb827037c8": {
             "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
         },
         "97a80dc7a7e27f41ae006fa1253f1f105f77335c": {
             "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
         },
         "f69faf33e8690e82b0043e9131e09bbbc394cbed": {
             "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
         },
         "65d8c00633404140986e5e23aa9de8ea689c1d05": {
             "balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
         }
     },
     "number": "0x0",
     "gasUsed": "0x0",
     "parentHash": "0x00000000000000000000000000000
       00000000000000000000000000000000000"
}

开始节点

现在,在启动节点之前,我们需要初始化它们:为每个节点创建数据目录,将帐户密钥复制到数据目录,复制验证器的 enode 密钥,并用 genesis 块引导区块链。

以下是针对所有六个节点实现这些目标的命令:

#Configuring Node 1
mkdir -p qdata/node1/{keystore,geth}
cp accounts/keystore/key1 qdata/node1/keystore
cp static-nodes.json qdata/node1
cp enode_id_1 qdata/node1/geth/nodekey
./geth --datadir qdata/node1 init genesis.json 

#Configuring Node 2
mkdir -p qdata/node2/geth
cp static-nodes.json qdata/node2
cp enode_id_2 qdata/node2/geth/nodekey
./geth --datadir qdata/node2 init genesis.json 

#Configuring Node 3
mkdir -p qdata/node3/geth
cp static-nodes.json qdata/node3
cp enode_id_3 qdata/node3/geth/nodekey
./geth --datadir qdata/node3 init genesis.json 

#Configuring Node 4
mkdir -p qdata/node4/geth
cp static-nodes.json qdata/node4
cp enode_id_4 qdata/node4/geth/nodekey
./geth --datadir qdata/node4 init genesis.json 

#Configuring Node 5
mkdir -p qdata/node5/geth
cp static-nodes.json qdata/node5
./geth --datadir qdata/node5 init genesis.json 

#Configuring Node 6
mkdir -p qdata/node6/geth
cp static-nodes.json qdata/node6
./geth --datadir qdata/node6 init genesis.json 

前面的命令不言自明。对于最后两个节点,我们没有生成任何 enode 键,因为如果不存在的话,geth会自动生成。现在,运行以下命令来启动法定节点。在新的 shell 窗口中运行每个命令:

./geth --datadir qdata/node1 --mine --port 23000 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console
./geth --datadir qdata/node2 --mine --port 23001 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console
./geth --datadir qdata/node3 --mine --port 23002 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console
./geth --datadir qdata/node4 --mine --port 23003 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console
./geth --datadir qdata/node5 --port 23004 --ipcpath "./geth.ipc" console
./geth --datadir qdata/node6 --port 23005 --ipcpath "./geth.ipc" console

下面是我们刚刚传递的不同选项的含义:

  • 运行验证器时需要--mine
  • --istanbul.requesttimeout为最大阻塞时间(default: 10000ms)。
  • --istanbul.blockperiod是最小闭塞时间(default: 1s)。
  • --istanbul.blockpausetime是前一个块中零交易时的暂停时间。值应该大于istanbul.blockperiod ( default: 2s)。

获取网络中所有验证器的列表。可以使用istanbul.getValidators() API。

动态添加或删除验证程序

让我们首先看看如何动态地添加一个新的验证器。要添加一个验证器,我们首先需要生成新验证器的节点键和地址。运行以下命令来生成它:

./istanbul setup --num 1 --nodes --verbose

这与我们之前使用的命令相同。现在,我们不需要genesis文件或static-nodes.json文件。我们只需要节点密钥和地址。创建一个名为node_id_5的文件,并将节点键放入其中。运行以下命令来初始化新的验证器:

#Configuring Node 7
mkdir -p qdata/node7/geth
cp static-nodes.json qdata/node7
cp enode_id_5 qdata/node7/geth/nodekey
./geth --datadir qdata/node7 init genesis.json

现在,在前面的命令成功运行之后,是时候让( 2F+ 1 )其他验证器同意插入新的验证器了。为此,在所有其他验证器中运行以下命令:

istanbul.propose("0x349ec6eefe8453a875c4905f5581ea792806a3e5", true)

用获得的新验证器的地址替换第一个参数。现在,使用以下命令启动新的验证器节点:

./geth --datadir qdata/node7 --mine --port 23006 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console

现在,您可以运行istanbul.getValidators()来检查网络中所有验证器的列表。现在应该是五点了。让我们从网络中移除一个验证器。让我们假设我们想要删除第一个验证器。在第一个验证器的控制台中运行eth.coinbase,找到它的唯一地址。然后,在( 2F + 1 )验证器中运行以下命令,从网络中删除第一个验证器:

istanbul.propose("0x05a6245732c2350ba2ed64e840394c2239f8ad1f", false)

这里,用您生成的第一个验证器的地址替换第一个参数。

在删除或添加验证器时,如果某个验证节点关闭,那么一旦它启动并运行,它将自动知道这些变化。

摘要

在这一章中,我们从以太坊区块链的基础知识开始,然后我们跳到定额组的特性和共识协议。然后,我们通过建立一个星座、Raft 和 IBFT 网络,第一次接触到了 Quorum。现在,您应该已经熟悉了建立网络的过程。下一步是学习编写智能合同,并部署我们的第一个智能合同。我们将在下一章实现这一点。


我们一直在努力

apachecn/AiLearning

【布客】中文翻译组