本文详细探讨了在动态验证者集下的安全性问题,特别是针对权益证明(PoS)协议中的链选择和最终性问题。文章分析了同步PoS和动态验证者集下的攻击模式,并提出了解决方案和优化策略。
传统共识算法,无论是在同步、部分异步或完全异步的网络模型中操作,还是围绕简单故障、拜占庭故障或可追责故障设计,通常在一个固定的参与者集合模型中运行,假设该固定集合中的至少部分参与者正确地遵循协议。
然而,在权益证明协议中,验证者可以随时进出,甚至验证者集合的绝对大小也可能随时间大幅缩减或增长。某一时刻的 80% 验证者集合可能会明显小于另一时刻的 20% 验证者集合,而在固定集合模型中显然构成的二义性,在动态集合模型中则可能根本不构成任何二义性。我们如何应对这一问题?
值得处理的第一个案例是 同步权益证明,即没有最终性的传统链式权益证明(可能带有“dunkle inclusion”机制以惩罚双重签名)。这包括 Peercoin 风格的算法、NXT 风格的算法、DPOS、Mauve Paper 中描述的第一个算法,基本上所有目前仍在运行的方案。在这里,问题较为简单,因为没有实现或保持“经济最终性”的可能性或目标;相反,目标仅仅是创建一种正反馈机制,以鼓励验证者在出现分叉时达成一致,选择一个主导区块,并随着时间的推移强化此区块在规范链中的地位。
在同步权益证明(带 dunkles 机制)中,每个验证者的激励是对他们认为最有可能成为规范链的链进行投票,从而提高这种情况的可能性;这创造了一个正反馈循环,使系统持续收敛于一个不断增长的规范链。
通过一个固定的验证者集合,选择分叉链的规则很简单:最长链获胜¹。然而,如果验证者集合发生变化呢?那么,我们需要避免的攻击如下:
左侧链最终变得“更长”,因为区块提议者在左侧出现的频率为 90%,在右侧为 40%,但显然这不是我们希望成为规范链的链——直观上我们希望拥有最多质押 ETH 的链。
假设某条链有 1000 万 ETH 被质押,其中 450,000(4.5%)由攻击者控制。现在,假设一个攻击者从某个区块开始维护自己的私有链。他们不允许新的验证者进入他们的链,但确实包含提现交易²。随着时间的推移,链中唯一剩余的质押只有攻击者的,可能带有少量例外。尽管这条链质押的 ETH 更少,该链看起来却“质量”更高,区块制造者 90% 的时间出现在这个链上,而在主链中区块制造者是普通用户,并且他们可能下线的频率更高。因此,一个天真的最长链模型可能会被这种攻击打败。
为了解决此问题,我们使用与工作证明中相同的技巧:不仅仅寻找最长链,我们还寻找最长的 困难加权 链。在此情况下,难度等于某个特定时间点所质押的 ETH 总量。
验证者集合现在可以随意进出,可能甚至在每个区块之间完全变化。无论验证者集合发生什么变化,经济学规律仍然适用,并创建激励机制,引导其趋向于可能的胜利结果,且由更多 ETH 支持的链将超过由更少 ETH 支持的链。
上述案例之所以比较简单,是因为惩罚错误的权益证明方案对每条消息独立进行评估,因此即使每毫秒验证者的身份被替换,经济学也不会改变,因为无论其历史如何,每个参与者都具有相同的激励。然而,我们更近期的 Casper 设计则惩罚 二义性——即验证者发送互相冲突的两个消息的行为。
在这种模型中,验证者集合的变化固有地难以处理,因为假设验证者集合相同时可以工作的安全证明在验证者集合变化时将失效。
哎呀,没更多交集了!
一种可能性是简单地禁止验证者集合在每个周期内变化超过,例如 1%,从而确保在每 33 个周期中至少有一个新块被最终确认,从而保持某种程度的安全性。这实际上是有效的。然而,这种方案效率低,针对这一问题我们可以采取更全面的策略以取得更好的效果。
另一个立刻产生的直觉是“为了保持连续性,我们应等待验证者集合 A 确认验证者集合 B,然后让验证者集合 B 从那里接手”。为了说明这一点,考虑以下玩具算法:
请注意,这里共识发生在区块之间,即存在“创建区块 1,确认区块 1,创建区块 2,确认区块 2 …”的模式,与我们其他算法不同的是,即使对旧区块的共识仍在完成中,新区块仍在出产。为了使此方案出现分叉,必须在某个初始区块高度上存在区块分歧,因此我们可以使用我们可追溯的安全性证明来证明在该区块高度处至少 1/3 的验证者可以被削减。
如果每个区块都被最终确认,并且每个区块都包含对下一个验证者集合的承诺,那么如果两个冲突的区块被最终确认,这就意味着在单个验证者集合内存在冲突,进而可以用来削减有问题的验证者。
然而,如果我们尝试将其应用于“交织共识”模型,其中提议新区块和最终确认现有区块并行发生,如我们最新版本的 Casper,那么将会面临挑战。因为在任何单个周期中达成共识的概率并不是 100%,我们不能简单地允许第 N 周期中的区块为第 N + 1 周期确定验证者集合;如果第 N 周期最终无效,且存在两个竞争候选人,那么最终确认两个具有不交集验证者集合的冲突链的风险就在于此。
那么我们如何通过使用 N 50 周期来确定 (N + 1) 50 的验证者集合来解决此问题,并希望每五十个周期至少确认一个周期。然而,这也会导致一个问题:
所以如果我们想要交织共识,我们该怎么做呢?首先,请注意状态转换函数确实对给定区块是否已被最终确认有一些部分知识:如果一个区块已被最终确认,则证据可以及时提交到下一个区块,而它可能未能提交,但是如果某个区块未最终确认,则证据绝对无法提交。我们可以将其视为最终确认的“是或可能”神谕。
让我们利用这一点。如果神谕说可能,我们不改变验证者集合;如果它说是,我们就改变。
不幸的是,事情并不是那么简单。
这里的问题是,我们不立即达成是或否是否共识,因此可以生成拥有两个不同验证者集合的父区块的子区块。
因此,我们得到了我们的解决方案。我们将保留上述相同的方案,只是在每个块必须受到之前的验证者集合和新验证者集合的最终确认这一条款。也就是说,在削减条件描述中,在任何我们之前需要 2/3 的活动验证者集合的某种类型消息的情况下,我们现在需要来自旧验证者集合的 2/3 和新验证者集合的 2/3 的联合消息。
现在,让我们看看当对某个给定区块的最终确认存在争议时的情况。
这个证明过程相对简单。给定任何父区块,该区块的子区块只能有两个可能的验证者集合:父区块的验证者集合本身,以及与在该区块中提交的验证者集合连接的父区块的验证者集合。如果有两个子区块,并且两个子区块都被最终确认,则父区块中的 1/3 验证者集合将被削减。如果其中一个子区块未被最终确认,那么其子区块中的验证者集合将保持不变,因此证明逻辑简单地延续到第一个被最终确认的子代。
注意,这里有两种方式来实现该方案。一是明确使用并集规则,计数父区块和子区块中的签名。另一种是将每个区块的验证者集合变化(增加和删除)的数量限制为某个 VAL_ROTATE_LIMIT
(例如, VAL_ROTATE_LIMIT = 5%
),并将阈值设置为 (2 + VAL_ROTATE_LIMIT) / 3
,而不是简单的三分之二,从而使得在旧集合上达到更高的阈值也能够在新集合中达到足够高的阈值(总体而言,你获得的容错水平为 (1 - VAL_ROTATE_LIMIT) / 3
)³。没有明显的理由表明哪种方法优于另一种;这取决于个人偏好。
我们如何将此与所有其他共识机制相结合,包括先前文章中描述的与提议信标的共识缓存方案在这里和分叉选择规则?上述方案在任何存在检查点链的模型中表现良好,无论检查点是否直接相互跟随,还是之间有区块。重要的是,给定一个检查点,如果这个检查点被最终确认,那么就可以创建一个反映确认的子检查点或一个不反映确认的子检查点。
左:2/3 的提交在第一检查点之前及时进入了区块链,因此左侧的下一个检查点包括了 A 和 B 作为验证者。右:提交未能进入,因此下一个检查点仅包括 A 作为验证者。如果上面两个检查点都确认,1/3 的 A 将被削减。
对于分叉选择规则,而不是对单一验证者集合计数提交,我们可以采取对旧验证者集合以及对新验证者集合的提交数量的最小值。这就是我们所需的一切。
(2 + 2 * VAL_ROTATE_LIMIT) / (3 + 2 * VAL_ROTATE_LIMIT)
。推导出的依据是,如果你的阈值为 t
,而新验证者集合删除 c
个验证者并添加 d
个验证者(其中 c + d <= VAL_ROTATE_LIMIT
),那么作为新验证者集合的百分比,你至少有 (t-c) / (1-c+d)
,这简化为 t - ((1-t)*c+t*d) / (1 - c+d)
,并且由于 t >> 1-t
,所以对于小的 VAL_ROTATE_LIMIT
,这个值至少为 t - t*d / (1+d)
,或 t/(1+d)
。因此安全容忍度为 t + t/(1+d) - 1
,活跃容忍度是 1-t
;两者最大相等时,故 t + t/(1+d) - 1 - 1 + t = 0
,或 (t*(1+d) + t + t*(1+d)) = 2*(1+d)
,所以 t = (2 + 2*d) / (3 + 2*d)
,容忍度为 1-t
。在 VAL_ROTATE_LIMIT = 0.05
时,这使我们得到 t ~= 0.6774
。对于更高的 VAL_ROTATE_LIMIT
值,移除验证者是更易于造成故障的方式,计算公式为 t = (2 - VAL_ROTATE_LIMIT) / (3 - 2 * VAL_ROTATE_LIMIT)
。
- 原文链接: medium.com/@VitalikButer...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!