深入 Solana 共识 - 从分叉到最终确定性

  • neodyme
  • 更新于 3天前
  • 阅读 294

Solana 共识算法基于权益证明(PoS),通过预定的领导者序列、 TowerBFT 以及历史证明(PoH)确保高效的区块创建、验证和最终确认。该算法允许每个时间段内随机选择的领导者负责生成新区块,且通过加权投票机制保障了区块的最终性和网络稳定性

介绍

共识是每个区块链构建的基本要素。它确保交易,无论是花费代币还是执行智能合约,都能在没有中央权威的情况下得到正确的验证和执行。设计和构建共识协议有很多方法。在这篇博客文章中,我们将详细介绍 Solana 共识协议。本文是为那些对权益证明(PoS)区块链算法有基础知识的人编写的。它提供了 Solana 共识过程的解释,包括 Solana Agave 验证器 的代码参考。

我们为什么要写关于 Solana 共识的博客文章? 在撰写时,Solana 共识算法的文档并不完善,只有不完整或过时的信息可用。目前,Solana 遵循“代码即法律”的方法,这意味着共识算法在 验证器代码库 中定义。其他文档(例如 Solana 白皮书)包含过时的信息。此外,关于因共识破坏行为而进行惩罚的讨论——如 SIMD-0204: 可惩罚事件验证 中的讨论——正在获得关注。

作为生态系统的积极参与者,我们最近 启动了自己的验证器,并意识到填补这一知识空白的必要性。因此,我们花时间再次深入代码库,并记录 Solana 共识算法。在本文中,我们将通过连续步骤介绍验证器过程,将与共识相关的所有内容线性化。

TL;DR

Solana 共识算法是一种权益证明(PoS)区块链,其中为每个时隙选择一个指定的领导者来创建新区块。这个领导者序列是在一个Epoch(固定的时隙周期)开始之前随机确定的。区块由历史证明链组成,这是一系列加密哈希,每个哈希都是基于前一个哈希和交易数据计算的。这作为经过时间的证明,直到计算出足够的哈希以完成区块,下一位领导者在其上构建。为了确认一个区块,它必须获得代表三分之二总权益的验证者的投票。最终确认(交易被视为不可逆转的时刻)发生在三分之二的权益发送了 32 次后续投票的情况下。在出现分叉(多个链同时存在的情况)时,TowerBFT 机制通过使用锁定来鼓励验证者投票支持最重的分叉(即具有最多权益加权投票的分叉),锁定是对切换链的惩罚。这确保了共识收敛于投票最多的链。

共识

区块链共识

区块链是一个去中心化的数字账本,能够不可变地记录多个计算机之间的交易,这意味着它们无法被更改或删除。这确保了数据的完整性,并防止在没有整个网络共识的情况下进行任何更改。区块链是加密货币和称为智能合约的自执行程序的基础。

在像区块链这样的去中心化系统中,没有中央权威负责确认新交易等决策。相反,交易在网络中的多个节点上记录和执行。挑战在于让所有这些节点在没有中央协调者的情况下就区块链的状态达成一致。在权益证明区块链中,节点被称为验证者。

共识机制是一种协议,允许区块链网络就区块链的当前状态达成一致。它确保网络中所有参与者就哪些交易是有效的达成一致,从而维护区块链的完整性和安全性。

具体来说,共识确保:

  • 防止双重支付:一旦代币在交易中使用,就不能在其他地方重复使用。这防止了数字货币被复制和多次消费。
  • 智能合约的正确执行:改变智能合约状态的交易按照编码在智能合约中的条件按预期执行。

区块链类型

虽然有很多不同的去中心化账本协议,但区块链是最流行的分布式账本类型。区块链的基本结构是仅追加的,从第一个区块(称为创世区块)开始。这意味着包含新交易的区块被追加到现有的区块链上。为了明确前一个区块是什么,追加的区块包含前一个区块的哈希。

image.png

如果创建并追加两个不同的区块到同一个区块,我们就会得到两个相互矛盾的链(称为分叉)。为了解决这个问题,我们需要规则来确定节点何时可以追加区块,并在出现分叉时需要一个有效分叉的决策标准。解决这个问题的两种最常见的区块链协议是工作量证明和权益证明。Solana 使用权益证明。以下是它们的比较:

工作量证明 (PoW) 权益证明 (PoS)
网络节点 节点被称为矿工。 在 PoS 中,网络节点被称为验证者。
描述 在 PoW 区块链中,节点竞争解决加密难题。第一个解决难题的节点可以将下一个区块添加到链中。这意味着拥有更多计算能力的矿工更有可能将区块追加到区块链。 在 PoS 中,验证者拥有的权益越多,越有可能将区块追加到链中,即根据其拥有的权益数量选择验证者。权益是指验证者锁定作为抵押以参与共识过程的加密货币数量。这种权益作为验证者诚实行为的经济激励,因为如果他们试图欺骗系统,可能会失去部分权益(这一过程称为惩罚)。
共识链规则(如果存在多个分叉,哪个是有效链?) 在出现分叉时,最长的链是共识链。 当存在多个分叉时,PoS 通常使用“最重分叉”规则来确定共识链。这意味着选择具有最多权益加权投票的分叉,或者(根据具体实现)选择大多数其他验证者追加的分叉。
优点 PoW 协议简单。 能源效率:PoS 不需要 PoW 所需的大量计算资源,使其运营成本更低,更环保。速度:PoS 可以比 PoW 更快地处理交易,因为它不依赖于解决复杂的难题。
缺点 需要大量计算能力,导致能源消耗高。 PoS 协议的实现往往相当复杂,具有较低的活跃性弹性,并且在低代币估值时难以启动。

理论背景:CAP 定理

区块链共识通常伴随着脆弱性。区块链协议的设计者无法拥有一切,即存在“区块链协议能有多好”的自然限制。CAP 定理总结了这一限制。CAP 代表一致性-可用性-分区。CAP 定理是一个基本原则,展示了分布式系统(因此也包括共识协议)的局限性。它强调在网络分区条件下,没有共识协议能够实现完美。

CAP 定理指出,一个系统只能同时提供以下三个保证中的两个:

  1. 一致性: 每个参与者在任何时间看到相同的系统状态。一个没有一致性的区块链的例子是,它可能会导致双重消费代币或反转智能合约执行。

  2. 可用性: 该系统始终可操作且可访问。例如,没有可用性的区块链意味着它停止了,即不再接受交易,因为没有更多的区块被确认。

  3. 分区容忍: 即使发生网络分区(即,某些参与者无法与其他参与者通信),系统仍然可以继续运行。

CAP 定理从数学上证明了同时实现这三种性质的不可能性。这意味着每个共识协议必须进行权衡,尤其是在网络分区期间。Solana 的共识算法在网络分区期间优先考虑一致性而非可用性。有关 CAP 定理的视觉解释,请参见此插图

Solana 区块链上的区块生产

在介绍了区块链共识算法的性质后,让我们深入了解 Solana 的共识算法是如何工作的。大致上,验证者处理可以分为两个领域:(1)生成新块和(2)其他验证者对块进行投票。只有当足够多的验证者对该区块投票时,该区块才能被网络接受。这确保了如果生成多个相互冲突的区块,网络将选择共识块。让我们开始看看区块生产是如何工作的……

1. 确定领导者序列

Solana 的区块生产依赖于一个随机但固定的领导者验证者序列。领导者序列决定在特定时期内,哪个验证者负责生成带有新交易的新块。验证者的质押越多,被选择为领导者的可能性就越大。

在 Solana 的协议中,与区块生产相关的两个重要时间间隔是:

  • 时隙(slot): 这些是验证者轮流生成区块的时间单位。每个时隙可以生成一个区块,每个时隙持续 400 毫秒。

  • Epoch: 这些是较长的时间段,在此期间领导者计划保持固定。Epoch之间的过渡发生在一个新块跨越Epoch边界时,每个Epoch大约持续两天。下一个Epoch的领导者序列在当前Epoch开始时定义。委托的代币总量(质押)在一个Epoch内是固定的,仅在Epoch过渡期间进行更改。

例如,允许验证者在各自时隙中生成块的领导者序列可能如下所示:

image.png

在 Solana 中,每个领导者被分配固定数量的 NUM_CONSECUTIVE_LEADER_SLOTS = 4 个时隙来生成区块。

为什么领导者序列应该是随机的? 随机化领导者序列确保每个验证者都有机会生成区块,这保证了去中心化。该序列使用上一Epoch的区块数据作为伪随机源进行确定。

固定的领导者序列在 DoS 攻击威胁方面有优势和劣势:

  • 优势: 可预测性允许验证者拒绝来自非领导者的区块,抵御基于白名单的潜在 DDoS 攻击。

  • 劣势: 计划的领导者对每个人都是已知的,因此可以计划针对未来领导者的 DoS 攻击。

2. 接收和验证交易

在验证者分配的领导者时隙之前和期间,它会从 RPC(远程过程调用)服务器和其他验证者那里接收交易,以将其包含在下一个区块中。RPC 服务器的任务是实现客户端与验证者网络之间的通信。如果用户花费了一些 Solana 代币,他们将其发送到 RPC 服务器,后者将其转发给验证者。验证者通过点对点通信相互转发新交易,这被称为 gossip。之所以称为 gossip,是因为它的目标是尽快传播新信息。

接收到交易后,它们会被验证。这个过程包括(但不限于)验证交易签名和账户余额。请注意,这只是一个非常早期的验证,只足以验证交易在块中包含的可能性。这并不能保证交易执行的成功!如果交易未包含在即将到来的块中,它会过期 。实际上,这个过期窗口目前大约为 2 分钟。过期的交易无法再包含在块中。在这种情况下,发送者必须创建新的交易。

3. 使用历史证明生成区块

历史证明(PoH)哈希链是 Solana 特有的一个特性,而大多数其他区块链协议中不存在。它与 Solana 的权益证明(PoS)机制结合,以创建基于质押的随机领导者序列。

什么是历史证明?

历史证明哈希链是一种加密技术,旨在在区块链内建立可验证的时间流逝,即加密时钟。PoH 不是独立存在的,而是与 Solana 的权益证明(PoS)一起工作,以组成共识协议。PoH 通过创建一系列加密哈希来运作,其中每个哈希验证自前一个哈希以来已经经过的特定时间量。这个过程使用 SHA256 算法,因为它在多种硬件平台上得到了广泛的优化和可用性。每次哈希计算都需要使用前一个哈希值。这确保了无法进行并行化。由于每次哈希操作都需要最小时间,因此我们可以对哈希生成的时间做出一些保证。通过在计算当前哈希时将数据点(如交易)和前一个哈希结合,PoH 可以证明在哈希计算时该数据点存在。此方法确保了交易序列与时间流逝之间的可验证链接。

区块生产和 PoH

在接收到交易后,验证者拥有开始区块生产所需的所有数据。在 Solana 中,一个区块由一个时隙的 PoH 序列组成,包括所包含的交易数据。当前时隙的领导者负责生成这些区块或 PoH 链。如果前一个时隙的领导者(或多个前一个时隙的领导者)未发布区块,则当前领导者必须为跳过的时隙创建 PoH 序列。这就是为什么 PoH 记录器始终在后台运行:如果当前领导者的时隙开始,但尚未接收到前一个区块,它仍然可以按时生成区块,因为它预计算了 PoH 序列。在这个预计算中,假设区块为空,因此只有在之前的领导者确实离线的情况下,这才是有效的预计算。

区块结构

以下图像显示了四个连续区块的结构,每个区块包含两个条目。第三个时隙的领导者离线。因此,时隙 4 的领导者必须计算时隙 3 的 PoH 哈希,这充当时隙 3 中缺失区块的占位符。

image.png

每个区块必须包含 12,500 个哈希,这些哈希被分组为多个条目。一个条目是一系列哈希和交易的默克尔根 。仅包含默克尔根是一种节省存储空间的方式,同时允许证明某个交易包含在区块中。每个交易由一条消息、一个最近的区块哈希和发起者的签名组成。区块哈希是区块中最后一个条目的最后一个哈希。

PoH 解决了哪些问题?

  • 消除对时间服务器的依赖:通过连接的哈希值生成事件的时间顺序记录,PoH 独立地为交易添加时间戳,消除节点之间为了达成时间共识而进行通信的需要。

  • 激励区块的包含:计算哈希的必要性使跳过时隙变得困难,从而促使每个领导者的区块包含,防止过早建立新的分叉。区块生产不应被延迟,借助 PoH,验证者可以证明他们给之前的验证者足够的时间来生成和发送区块。PoH 确保当前领导者可以跳过之前的领导者,但同时没有不诚实这么做的动机,因为自己计算之前领导者的 PoH 序列不会在更早完成其区块方面带来优势。

    PoH 确保了时间记录的防篡改性,PoS 补充了它,解决了不良验证者行为,共同构成了 Solana 区块链的强大安全共识协议。

附带说明:PoH 的构建基于这样的假设:没有验证者可以显著快于其他验证者生成 PoH 序列,这受到芯片处理速度的限制。除了当前内置于验证者中的 SHA256 实现外,一些实现的速度是其数倍,这可能会危及安全性。

分叉创建

如果一个验证者决定跳过之前的区块(例如,因为来自前一个领导者的区块未能到达当前领导者),则可能会导致分叉,这意味着现在存在多个可能的区块链版本。

分叉是导致时隙高度和区块高度不相同的原因:

  • 时隙高度是当前时隙的编号,从创世区块开始计算。
  • 区块高度是从创世区块开始在当前分叉中包含的区块数量。

一般来说,具有不同分叉构建选择的验证者会选择最重的分叉,即来自其他验证者的持权加权投票最多的分叉。

4. 分发区块

区块在 broadcast_stage.rs 中使用一种称为 Turbine 的分发协议进行分发,其目标是每个区块快速且容错地传输到其他验证者。5

在区块生产之后,区块被分发并发送到其他验证者。为了便于传输,区块被分割成所谓的碎片。具体是如何工作的又是一个很好的博客主题,但与共识无关。

验证和投票区块

我们为什么需要投票? 产生一个区块并不自动使其成为共识区块,因为可能存在多个区块链的分叉。我们需要一个机制来确定共识分叉。这是通过网络中验证者的投票来实现的。验证者持有的委托权益(币)越多,投票的权重就越大。这被称为持权加权投票。在像 Solana 这样的委托 PoS 区块链中,币主可以在不转移币所有权的情况下,委派其参与验证过程的能力(= 委托)。通过投票,验证者表示对特定分叉的独占支持,排除竞争分叉。此外,验证者以其投票保证其将账本视为有效,直到当前区块。在 Solana 中,投票是普通交易,也包含在区块中。投票可以通过以下两种方式发送和接收:

  1. 作为单个松散的投票,通过验证者之间的 gossip 进行传输。
  2. 作为重放投票:这意味着投票已经落地,即被包含在一个区块中。

投票表示验证者对一个区块的承诺。描述单个验证者对一个区块承诺如何的不同状态:

验证者承诺级别 描述
冻结 冻结意味着该区块已被验证者成功重放(即检查和验证)。
投票 验证者决定对该区块进行投票,承诺该区块并排除竞争区块。
根区 验证者已对同一分叉发出 32 次后续投票。这意味着该区块已达到了 32 的最大锁定期(有关锁定期的工作原理,请参阅“TowerBFT 协议中的锁定期”部分)。一旦区块被确立,验证者将无法再切换离开它。

集群是一个描述构建在同一创世区块上的验证者网络的术语。从个别验证者的角度放大到整个集群,有不同阶段描述在集群中交易的“接受”程度,称为承诺状态:

集群承诺级别 描述
已处理 交易包含在一个区块中(并且通过 RPC 查询的验证者必须已对该区块投票)。
乐观确认 三分之二的持权加权投票都是针对包含该交易的区块。
最终确认 最终确认意味着该区块已获得三分之二的集群确认,这可以在 32 个区块后发生,但不早于此。

最重要的承诺级别是“最终确认”。在一个被最终确认的区块中的交易不可能在不违反共识协议的情况下撤销。然而,一些 RPC 客户端使用“已确认”的承诺状态来提前获取交易状态的良好指示。

为什么确认不够? 单靠确认不足以维持共识。Solana 网络中的验证者可以切换分叉并通过锁定期“撤回”投票,这意味着即使有三分之二的投票,某个区块可能仍未进入共识链,如果一些验证者恶意地进行了双重投票。为了确保某个区块保留在共识链中,它需要达到最终确认状态。在 32 个区块后,撤回投票变得不可行,从而巩固了区块在链中的位置。

为什么需要三分之二的多数? 如果一个区块获得至少三分之二的持权加权投票,则该区块被确认。现在假设这些投票中有 50%(占总持权的 33%)来自恶意验证者,这些恶意验证者也为不同分叉的区块 B 投票。这些恶意验证者破坏规则,但我们仍然希望维持共识。如果区块确认需要三分之二的持权加权投票,并且我们假设没有超过 50%的验证者是恶意的,那么区块 A 保持投票的多数,即使剩下的三分之一的验证者的持权(未对区块 A 投票)投票支持区块 B,确保共识得以维持。

需要三分之二的多数票来确认区块,这意味着如果超过三分之一的验证者离线或未投票,区块链可能会停止,因为再也无法确认区块。这就是为什么三分之一的阈值被称为超级少数。

请注意,这是一种选择。选择略微不同的乐观确认或共识规则是完全合理的,Solana 刚好选择了这些。

需要在某一个Slot上投票的股份百分比的阈值在 consensus.rs 中定义为 VOTE_THRESHOLD_SIZE = 2/3。SUPERMINORITY_THRESHOLD = 1/3 在 replay_stage.rs 中定义。7

乐观确认

Solana 使用乐观确认来评估Slot的承诺状态。核心思想是,一旦一个区块获得了代表超过三分之二总股份的验证者的投票,它就会变得乐观被确认,除非有验证者因不当行为而被削减,否则不太可能被撤回。为了使区块 D 被乐观确认,它需要获得三分之二的投票。对于乐观确认,投票的接收方式并不重要。这意味着,通过gossip协议接收到的投票,即使尚未包含在区块中,也会用于确定承诺状态。如果我们能从祖先区块 B 重放区块到区块 D,则祖先也将被乐观确认。乐观确认的区块在其投票根植之前被确认,基于的假设是gossip投票交易最终会被包含在区块中。

现在我们了解了区块可以处于的不同投票状态。让我们检查验证者执行的所有步骤,以决定支持哪些区块…

1. 接收区块和投票

区块验证从验证者接收来自其他验证者的新区块和投票开始。投票是一项调用投票程序的交易,包括要投票的账本的哈希,该哈希是通过区块哈希和账本的当前状态计算得出的。账本维护所有账户的当前状态,包括余额和智能合约状态。

验证者在 shred_fetch_stage.rs 中接收新的区块,在 fetch_stage.rs 中接收gossip交易(包括投票)。新区块存储在 blockstore.rs 中。在其当前实现中,区块存储只能在每个Slot存储一个区块,如果存在重复区块,则只能留出一个位置。9

2. 验证区块

区块的验证过程包括验证区块元数据和重新计算 PoH 哈希。顺序计算哈希(如在 PoH 创建期间)会耗费很多时间。这就是验证者将 PoH 哈希链拆分成几部分的原因。重新计算每一部分可以实现并行化,从而使验证过程比原始的 PoH 创建过程更快。

在重新计算 PoH 哈希链后,它会验证并重放来自区块的所有交易,并更新账本。验证和重放投票与领导模式下的交易验证非常相似。

交易验证和重放协调发生在 replay_stage.rs。顾名思义,它重放来自区块的所有交易,即检查交易与账户余额的一致性,并重新执行智能合约程序以验证正确执行。其他步骤包括在 accounts_hash_verifier.rs 中计算账户哈希,在 sigverify_shreds.rs 中验证领导者的签名,以及在 retransmit_stage.rs 中帮助网络将未完全接收的区块片重新广播给其他验证者。10

3. 选择分叉

分区检测与解决

Solana 旨在防止分叉,理想情况下保持每个Slot一个区块的序列。Solana 中的每个区块都包含一个指向其父区块的指针,确保区块只能附加到一条分叉上。如果验证者工作完美且没有网络问题,则应该没有分区。但是,验证者离线、网络问题或恶意行为等问题可能导致验证者跳过一个区块并将其区块附加到一个非直接祖先,从而形成一个包含多个版本链的分区。

最重分叉规则

为了在出现分区的情况下确定有效链,Solana 使用最重分叉规则。这个决策规则选择最重区块的分叉。要计算区块的权重,需要考虑从该区块开始的所有分叉,使用最重子树分叉选择规则。该规则评估具有最高累计投票权重的子树的区块。验证者为每个子树计算总的按股份加权投票,并选择投票最多的那个。这确保了来自验证者支持最多的链继续增长。如果两个区块之间存在平局,验证者优先选择较早的区块。

heaviest_subtree_fork_choice.rs 实现了关于最重分叉和重复Slot的分叉选择。在 replay_stage.rs 中通过函数 is_partition_detected() 检测分区,该函数检查最后投票的Slot是否不是最重Slot的祖先。11

处理重复区块

重复Slot的超级多数阈值在 replay_stage.rs 中定义为 DUPLICATE_THRESHOLD,默认设置为 52%。12

分区也可能由于重复Slot而产生,其中故障或恶意的领导者为同一Slot生成多个区块。这些重复Slot被标记为“无效分叉选择”。这意味着验证者将忽略该分叉并尝试为不同的分叉投票。然而,如果 52% 的股权验证者对重复Slot进行了投票,它将被标记为“重复确认”,并且可以再次视为投票的分叉。尽管这个术语有点模糊,但这与承诺状态被“确认”无关。重复区块仍然需要三分之二的投票才能获得“(乐观)确认”的承诺状态。

对于重复确认,投票的处理方式与乐观确认有所不同:重放投票也被视为对其所有祖先区块的投票,以确定重复确认状态。另一方面,传播投票仅计入其所针对的确切区块。

以下是插槽 2 和 3 中重复区块的示例。所有红色区块被标记为无效分叉选择,因为它们是重复区块或重复区块的后代。为什么要接受重复区块?单个验证者可能不知道某个区块是重复的。网络分区可能导致某些验证者看到区块 B1,而另一些验证者看到区块 B2。在重新连接后,他们需要弄清楚该怎么做。允许在两个版本之一上继续构建,使得在发生重复区块时网络更加可用。

image.png

4. 投票

现在我们知道验证者如何选择要投票的分叉。然而,投票有几个条件。如果这些条件未满足,验证者无法投票并必须等待。以下四个条件可能会暂时阻止验证者投票……

函数 make_check_switch_threshold_decision()consensus.rs 中决定如何处理给定的分叉。投票是在函数 generate_vote_tx() 中生成的,位于 reply_stage.rs(当验证者当前是领导者时)和 record_vote()consensus.rs 中(当验证者不是领导者时)。在 replay_stage.rs 中,尝试在最重的分叉上投票时可能发生的不同失败被定义为 HeaviestForkFailuresLockedOutFailedSwitchThresholdFailedThresholdNoPropagatedConfirmation13

4.1 使用 TowerBFT 协议的锁定

验证者使用 Solana 的 Tower 拜占庭容错(TowerBFT)协议进行投票。TowerBFT 的目标是为验证者提供激励,以便共识围绕一个分叉收敛。TowerBFT 使用 实用拜占庭容错 算法,并将其与 PoH 结合。拜占庭容错(BFT)是用于在某些组件可能失败或恶意行为的分布式系统中进行投票的一类协议的名称。该术语源于 拜占庭将军问题,该问题说明了在具有不可靠节点的网络中实现共识的困难。

但什么是 TowerBFT?

TowerBFT 为投票引入了锁定机制。如果你决定为一个分叉投票,并随后决定切换到另一个分叉,你将在一定时间内被锁定,无法再次投票。Tower 是一个数据结构,用于跟踪验证者的投票及其相应的锁定。它本质上是一个包含插槽编号及其相应锁定期的元组列表。如果一个区块在 Tower 中,这意味着相应的验证者对该区块进行了投票。Tower 的目的是让网络中的每个验证者承诺某个分叉,同时也保留改变该决定并切换到另一个分叉的可能性。如果验证者在错误的分叉上投票,它将被锁定并失去一段时间的投票能力。这防止了验证者在分叉之间快速切换投票,从而增强了网络的安全性,并使双重支付攻击变得更加困难。

这一系列后续区块的投票称为投票塔。Tower 在 consensus.rs 中定义。14

以下是一个示例,说明当验证者决定切换分叉时,Tower 可能如何影响投票的可能性:

锁定机制的主要思想是对同一分叉的每次投票将锁定期加倍,因此在错误分叉上投票会导致锁定期呈指数增长。对一个分叉的投票在 Tower 中堆叠。只有当 Tower 达到新高度时,锁定期才会加倍。投票在 32 次投票后达到最大锁定,并被出队,收集积分(在Epoch结束时可以触发奖励)。如果验证者想要在不同的分叉上投票,它需要回滚(从 Tower 中弹出最近的投票)并等待足够的区块被生成以满足锁定期。如果验证者被锁定,它必须等待在最重的分叉中生成更多区块,才能再次投票。这时 PoH 就派上用场,因为它限制了新区块附加到链上的速度。

4.2 失败的切换证明

如果最后一次投票是在与当前最重分叉不同的分叉上,验证者将尝试切换并为更重的分叉投票。然而,最重的分叉需要有切换证明,表明当前分叉永远不会达到最终性。如果切换阈值未满足,验证者必须继续在当前分叉上投票。

这个切换证明意味着某些插槽需要达到切换阈值,即 38% 的权重投票。有某些插槽(所谓的候选插槽)用于计算切换阈值。候选插槽的规则是:获取最后一次投票插槽(验证者最近投票的插槽)和候选插槽的最大公共祖先。切换插槽(验证者想要投票的最重分叉的插槽)是这个最大公共祖先的后代。请参见以下图像以进行说明:

切换分叉阈值为 38% = 1/3 加上 4.66% 的随机值,这是乐观确认的恶意缓冲。这意味着如果一个乐观确认的区块被回滚,必须有 4.66% 或更多的验证者犯下可削减的违规行为。

is_valid_switching_proof_vote() 方法在 consensus.rs 中决定某个插槽是否计入切换阈值。15

4.3 失败的阈值

TowerBFT 锁定和切换证明在验证者切换分叉时是相关的。还有其他限制,即阈值和传播确认,这些在任何情况下都需要满足才能投票。

失败的阈值意味着最重的分叉在塔中的最后八个区块中缺乏足够的权重(三分之二的权重)。如果没有这个检查,在分区的情况下(即与最后一次投票的分叉不同的分叉是最重的分叉),验证者可能会被锁定。这个检查确保验证者的投票不会“跑得太远”,而没有其他验证者赶上,这将导致较长的锁定期。这就是为什么检查最后八个区块,而不仅仅是当前区块。如果验证者的最新投票受到失败阈值的影响,则无法投票。一旦一个区块未通过阈值检查,它将永远无法再次达到阈值。因此,验证者必须等待一个子区块获得更多投票(或直到锁定期到期以便在不同的分叉上投票)。八个区块的投票深度是默认值,可以由验证者操作员更改。

image.png

consensus.rs 中定义了阈值为 VOTE_THRESHOLD_SIZE = 2/3,而八个槽的深度在 VOTE_THRESHOLD_DEPTH 常量中定义。16

4.4 没有传播确认

progress_map.rs 中的 is_propagated 字段跟踪区块是否被传播。17

验证者在弃用分叉之前,必须确保超级少数(三分之一)在领导模式下已经接收到其最后产生的区块。换句话说,当验证者承认该槽的股份超过 SUPERMINORITY_THRESHOLD 时,领导槽就被传播。另一位验证者可以通过(1)发送对该区块的投票或(2)通过通过gossip传播的被冻结指示符确认它收到过该区块。这个冻结指示符意味着验证者重播了一个区块,但可能没有为该区块投票(例如,如果验证者对一个冲突的区块进行了投票)。

这确保了大部分网络收到了验证者的区块,防止在网络分区期间投票。对于非领导槽,其传播状态依赖于其最近的祖先领导槽。如果那个祖先领导槽被传播,那么非领导槽也被视为已传播。

5. 发送投票

在决定投票的分叉/区块后,如果所有投票条件都满足且没有阻止验证者投票的因素,投票将通过gossip发送给其他验证者(尤其是即将到来的领导者)。

验证者经济学

到目前为止,我们讨论了验证者应该如何产生新区块并为区块投票以使其最终确认。但是什么给予验证者遵循共识协议的激励?验证者运营商并非仅凭善意工作,而是因诚实行为而获得奖励。虽然验证者经济学并不是共识过程的直接组成部分,但它设置了验证者正确执行共识协议的激励。这使得经济学成为构建安全且顺畅运行的共识算法的重要部分。

区块奖励结构在 bank.rs 中的 update_fees() 定义。通货膨胀委员会在 bank.rs 中的 calculate_previous_epoch_inflation_rewards() 定义。投票费用在 transaction_cost.rs 中的 SIMPLE_VOTE_USAGE_COST 下定义。19

验证者收入

验证者节点有以下收入来源(更多详细列表,请查看 这篇博客文章):

  • 区块奖励 = 区块生产的激励:被指定为给定区块领导者的验证者在其区块最终进入链时会收到奖励,称为区块奖励。这些奖励由 50%的基础费用和 50%的优先费用组成,另一半被销毁。区块奖励的分配激励验证者成为领导者并生产区块,确保在网络中的积极参与。
  • 通货膨胀委员会 = 投票的激励:通货膨胀约为每年 1.5%,根据验证者的股份及其投票规律分配。验证者在Epoch结束时收到奖励,且每个验证者每投票一次将根植一个新区块。这激励验证者投票支持最重的分叉,并持续为共识做出贡献,因为他们的奖励依赖于投票行为。
  • MEV 奖励:Solana 上有第三方 MEV 提供商,最著名的是 Jito。如果验证者在生成的区块中包含交易包,他们将获得额外的小费。这不是 Solana 核心协议的一部分,本文不再深入探讨。实际上,截止 2024 年末,小费与区块奖励大致相同,但这会因网络条件和使用而变化。

在领导模式下,验证者被激励尽可能多地包含交易并在区块生成中包含任何先前的区块。先前的领导者可能会获得更多的投票,因为它更早发布区块,因此更有可能最终进入链。

验证者有激励在其他验证者之后发送其投票,从而使其票据更有可能最终进入链。为了对抗这种情况, 及时投票信用奖励及早投票的验证者,以对抗那些迟到投票的验证者。

惩罚

惩罚是对验证者在网络中进行非法操作(例如,产生同一槽的两个不同区块版本或对不允许的不同分叉投票)的处罚过程。目前虽然还未实施,但有一个提案计划在 Solana 中引入惩罚。如果实施,发布重复的区块或冲突的投票将受到惩罚。在当前状态下,Solana 可以被视为一种手动惩罚区块链,因为验证者可以集体决定排除恶意或故障的验证者。

然而,Solana 中惩罚的未来依然不确定。这个概念在质押其代币的用户中并不受欢迎,因为他们可能会失去质押的金额。此外,网络在没有惩罚的情况下运作良好,减少了立即实施的必要性。但最近围绕一个新提案的活动有所增加。第一部分,证明可惩罚事件已在链上发生,最为完整,目前在作为 Solana 改进文档活跃讨论:SIMD-0204: 可惩罚事件验证

激励兼容性

简单来说,激励兼容性意味着使验证者的利益与网络目标对齐。在理论计算机科学和博弈论中,激励兼容性指的是设计出一种系统,使得每个参与者的最佳策略都是遵循协议。对于区块链而言,这意味着确保验证者因诚实而获得奖励,并因不诚实行为而受到惩罚。

负责对区块进行投票的 TowerBFT 机制是激励兼容的。完全证明激励兼容性是困难的(而且 Solana 协议中可能存在不完全激励兼容的部分)。虽然缺乏完全的激励兼容性会拓展理论攻击面,但在 Solana 区块链的实际影响似乎微不足道。Solana 更优先考虑实际解决方案,例如错误修复和优化,而不是正式验证其共识算法。虽然一些区块链旨在实现 100%的激励兼容性,但 Solana 认为达到实际兼容性以确保网络平稳运行就足够了。

恭喜你!现在你应该对 Solana 共识算法的工作原理有一个概述。在以前的部分中,我们讨论了 (1)验证者如何生成新块, (2)验证者如何通过投票来最终确定块,以及 (3)验证者为何应遵循所有这些步骤(通过验证者经济激励)。

总结

这是很多信息!这就是为什么我们为你准备了一个快速回顾,涉及所有与块生成和块投票有关的验证者规则:

领导模式下的块生产

  • 在最重的分叉上构建。
  • 如果验证者在不同的分叉上投票,但最重的分叉没有切换证明,验证者将基于先前的(而不是最重的)分叉进行构建。

验证模式下的投票

  1. 设定根:识别在相关验证者上达到最大锁定的最新区块。这是所有活动分叉的起点。
  2. 选择最重的分叉
    • 同一分叉:如果最重的分叉是验证者最近投票的分叉,则继续在该分叉上投票(SwitchForkDecision::SameFork)。
    • 不同分叉:如果验证者之前在不同的分叉上投票,尝试切换到较重的分叉投票:
      • 成功切换:需要切换证明,这意味着至少需要支持切换的投票必须达到切换分叉阈值(38%)的比例(SwitchForkDecision::SwitchProof)。
      • 切换失败阈值:如果切换失败,则继续在验证者先前投票的分叉上投票(SwitchForkDecision::FailedSwitchThreshold)。
  3. 处理重复时隙
    • 时隙为重复确认:如果一个块获得了大多数(52%)的投票,则将该分叉重新考虑为最重选择的一部分(基本上将重复确认的块视为非重复),并照常选择最重的分叉。
    • 时隙未重复确认:如果块的投票少于 52%,则将其标记为无效分叉选择,并且不要在投票时考虑它。继续在所有有效分叉中进行选择。
      • 因重复时隙回滚:如果没有其他可投票的分叉,验证者将重置到最后的有效祖先,并且在产生干净的分叉之前不进行投票(SwitchForkDecision::FailedSwitchDuplicateRollback)。

结论

在这篇博客文章中,我们探讨了基于权益证明(PoS)的 Solana 共识算法,如何利用预定的领导者序列、TowerBFT 和历史证明(PoH)来确保高效的块创建、验证和最终确定。委托权益的使用调整了领导者的频率和投票权重,而 TowerBFT 则推动共识朝最重的分叉发展,促进了稳定性。这个结合形成了一个强大的系统,支持快速、安全的交易处理和可扩展性,使 Solana 成为去中心化应用程序的强有力候选者。

感谢 Anza 的 Ashwin Sekar 校对本帖!

如果你一直跟着阅读,并希望查看更多相关内容,和我们一起质押吧!我们最近推出了自己的验证者 NdMV1C3XMCRqSBwBtNmoUNnKctYh95Ug4xb6FSTcAWr ,为任何希望支持独立 Solana 安全研究的人提供了一条简单的途径。

进一步链接

代码参考

  1. 块生产过程在 Transaction Processing Unit (TPU)replay_stage.rs 中控制。

  2. leader_schedule.rs 定义了Epoch的领导者序列。

  3. 交易和投票在 fetch_stage.rs 中接收。验证过程在 sigverify.rsbanking_stage.rs 中定义。

  4. 新块的创建在 poh_recorder.rs 中定义。一个条目的结构在 entry.rs 中定义。

  5. 块在 broadcast_stage.rs 中通过名为 Turbine 的分发协议进行分发,目的是确保每个块都能够快速并且容错地传输给其他验证者。

  6. Transaction Validation Unit (TVU)replay_stage.rs 控制块的验证和投票过程。

  7. 需要对时隙投票的权益百分比阈值在 consensus.rs 中定义为 VOTE_THRESHOLD_SIZE = 2/3。SUPERMINORITY_THRESHOLD = 1/3 在 replay_stage.rs 中定义。

  8. 乐观确认optimistic_confirmation_verifier.rs 中实现。 cluster_info_vote_listener.rs 验证并处理来自其他验证者的投票。它输出乐观确认的插槽和关于投票过程的统计信息。 window_service.rs 收集、验证并存储数据块,包括处理冲突/重复插槽。

  9. 验证者在 shred_fetch_stage.rs 中接收新块,并在 fetch_stage.rs 中讨论交易(包括投票)。新块存储在 blockstore.rs 中。在目前的实现中,数据块存储只能在每个插槽存储一个数据块,如果有重复块,则仅保留一个。

  10. 交易验证和重放协调在 replay_stage.rs 中进行。如其名称所示,它从一个块重放所有交易,即检查交易是否符合账户余额,并重新执行智能合约程序以验证正确执行。其他步骤包括 accounts_hash_verifier.rs 用于计算账户哈希,sigverify_shreds.rs 用于验证领导者的签名,以及 retransmit_stage.rs 用于帮助网络将数据块重新传输给未完全接收的其他验证者。

  11. heaviest_subtree_fork_choice.rs 实现了关于最重分叉和重复插槽的分叉选择。在 replay_stage.rs 中通过函数 is_partition_detected() 检测分区,该函数检查最后投票的插槽是否不是最重插槽的祖先。

  12. 重复插槽的超级多数阈值在 replay_stage.rs 中定义为 DUPLICATE_THRESHOLD,默认设置为 52%。

  13. consensus.rs 中的函数 make_check_switch_threshold_decision() 决定如何处理给定的分叉。投票是在 reply_stage.rs 中的函数 generate_vote_tx() (当验证者当前是领导者)和 consensus.rs 中的 record_vote() (当验证者不是领导者)生成的。在 replay_stage.rs 中,尝试在最重分叉上投票时可能发生的不同失败定义为 HeaviestForkFailures: LockedOut, FailedSwitchThreshold, FailedThreshold, NoPropagatedConfirmation

  14. 随后块的投票堆栈称为投票塔。塔在 consensus.rs 中定义。

  15. consensus.rs 中的 is_valid_switching_proof_vote() 方法决定一个插槽是否计入切换阈值。

  16. consensus.rs 中在 VOTE_THRESHOLD_SIZE = 2/3 定义了阈值,八个插槽的深度在 VOTE_THRESHOLD_DEPTH 常量中定义。

  17. progress_map.rsPropagatedStatsis_propagated 字段跟踪一个块是否已传播。

  18. 投票通过 gossip 协议发送给即将到来的领导者,位于 send_transaction_service.rs 中。

  19. 区块奖励结构在 bank.rsupdate_fees() 下定义。通货膨胀佣金在 bank.rscalculate_previous_epoch_inflation_rewards() 下定义。投票费用在 transaction_cost.rsSIMPLE_VOTE_USAGE_COST 下定义。

  20. SwitchForkDecisionconsensus.rs 中定义。

我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
neodyme
neodyme
We secure software with deep-dive audits, cutting-edge research, and in-depth trainings. https://neodyme.io/