共识算法是任何区块链的核心。它提供了安全、有效性和去中心化的基本保证,使建立在上面的应用程序得以实现。2023年1月,Flow升级到Jolteon共识算法。这次升级使区块和交易的最终完成时间加快了约33%,同时也加快了网络升级。这将转化为用户互动得到更快确认,以及更高的整体系统稳定性和可用性。
背景介绍
自2020年Flow推出以来,它一直使用拜占庭容错(BFT)算法的HotStuff系列中的一种共识算法[1]。除了共识算法的核心要求之外,HotStuff还提供了许多有用的属性:
- 确定性的终结性--被算法标记为终结的区块被保证永不改变。
- 乐观的响应性--在快乐的路径中,算法将在网络允许的范围内快速运行,而不是需要为每个区块等待一个固定的时间段。
- 管道式区块生产--每一个新的提议都同时扩展了链,并有助于最终完成前一个区块。
HotStuff协议在一系列的回合中进行(通常称为视图),其中一个节点是领导者,可以通过扩展以前提出的区块来提出新的区块。随后的每一轮都会选择一个新的领导者。(在下面的图表中,领导者以外的节点被称为副本)。在快乐路径中,委员会的超级多数[2]将通过投票支持该区块而接受它。我们把这称为一个成功的回合。
如果没有提出任何区块,或者没有收集到足够的票数来达到超级多数,那么我们认为这一轮失败了。该协议将在下一轮中以新的提议再次尝试。
对最新升级特别感兴趣的是HotStuff中一个叫做 "起搏器 "的组件。每个节点都维护着一个本地的current_view变量,当观察到其当前(或更大)视图的成功建议时,或者当起搏器指示时,该变量会被递增。起搏器负责确保各个节点在当前视图上达成一致,这样他们就可以共同取得进展。
这在协议的不快乐路径中特别重要,可以从脱机的领导者或瞬时的网络故障中恢复。如果每一轮都有一个成功的提议,那么起搏器就不会被调用。起搏器的工作是在本轮没有成功提案的情况下,决定何时进行下一轮的工作。
挑战
到目前为止,Flow使用的是被动起搏器,它的运行不需要与其他节点交换任何信息。它根据以前的视图是否成功的知识和一个指数增加的超时函数来决定是否跳过当前视图。这使得它的实现相对简单,但也限制了它的有效性,特别是在边缘情况下。我们将在后面看到,它对性能也有间接的影响,而这些影响并不明显。
为了说明被动起搏器的局限性,假设有一群朋友正在组织晚餐。爱丽丝、鲍勃、查理和夏娃每周五去吃饭。他们轮流去,每周由其中一人决定一家餐厅。这个星期五,轮到了夏娃,但鲍勃没有收到她的任何消息。他不确定他是否是唯一一个错过夏娃电话的人。也可能是夏娃在露营,没有信号,在这种情况下,没有人会听到夏娃的消息。
在这种情况下,他们通常的程序是由下一个排队的人(爱丽丝)来挑选餐厅。直观地说,鲍勃会给爱丽丝和查理打电话,确认他们也没有夏娃的消息,然后同意跳过夏娃的回合,让爱丽丝挑选餐厅。然而,在被动起搏器的更严格的通信方案中,这种后备呼叫是不允许的。鲍勃所能做的就是等待足够长的时间,直到他非常确定夏娃不会打电话。
在讨论实际的升级之前,让我们考虑一下一个最佳的起搏器会是什么样子。为了取得进展,当一个视图的提议产生时,超级多数的节点必须在同一个视图中,所以一个最佳的起搏器将确保所有节点保持一致的视图。当一个视图因为领导者的失败而无法进行时,一个最佳的起搏器应该尽快跳过该视图,以避免等待一个永远不会到来的提案。
由于在一个分散的系统中协调时钟是不切实际的,更不用说有拜占庭节点的系统了,所以被动起搏器不能让所有节点保持在同一个视图上。当超时较少,且大多数视图都能成功提议时,被动起搏器的表现相当好。然而,当几个视图失败,更多的时间没有成功的提议时,最终确定率会受到严重影响,因为每一个连续失败的回合都会指数级地增加下一个回合的超时。
在极限情况下,这些具有挑战性的网络条件相当于系统中断。[3] 幸运的是,这种故障情况很少。然而,在更长的时间内,计划外的中断是不可避免的,必须对其进行规划。类似的情况也可能发生在有计划的网络升级期间(称为 "Sporks "的Flow[4]),因为不同的节点运营商在不同的时间将他们的节点上线。
当这些情况发生时,网络被分成几个组,每个组的节点数都少于超级多数,不同的组有不同的视图和超时值。由于我们要求超级多数的节点处于相同的视图中,所以在这些组中有足够多的节点收敛在相同的视图上之前,无法取得进展。由于缺乏主动通信,收敛变得更加耗时。被动起搏器经过调整,使处于较高视图的节点超时更慢,所以收敛最终会发生,但 "最终 "可能是一个很长的时间。当组与组之间相差10个视图时(一个组的current_view=n,另一个组的current_view=n+10),恢复的时间可以增长到几分钟甚至几小时。
当视图分歧发生时,可以通过调整不同队列节点上的视图和超时值来降低恢复时间,以手动增加收敛的速度。这种策略可以奏效,但它是一个敏感的过程,需要正确的处理。它需要不同节点操作员之间的协调,以及对被动起搏器机制的理解。错误的调整很容易使问题恶化,延长停电时间(我们已经亲身经历过!)。虽然这种缓解措施可以帮助,但系统恢复一般来说太不可靠,而且不切实际地漫长。
为了最大限度地减少在分叉过程中出现视图分歧的风险,运营商事先商定了一个启动时间,并在这个时间配置他们的节点进行协调启动。这样做效果很好,但会给拼接过程带来额外的停机时间。启动时间需要适应最坏情况下的时间,以确保所有操作员在启动前做好准备。如果启动时间因任何原因需要推后,这需要一轮人工协调,并再次适应最坏情况下的时间。
Jolteon共识协议
Jolteon最初于2021年6月由Meta的区块链研究团队、Novi和学术合作者 发布。Meta的团队(现在叫Diem) 实现了Jolteon,并将其命名为 DiemBFT v4,随后被重新命名为 AptosBFT,没有进一步修改。在概念上,Jolteon(DiemBFT,AptosBFT)属于HotStuff共识协议家族。Jolteon遵循HotStuff的流水线区块终结框架,但增加了两项重大改进。
主动起搏器
Jolteon算法的第一次升级是引入了一个主动起搏器。主动起搏器扩展了协议,为没有成功提议的视图引入了主动恢复机制。当一个节点超时时,他们不会立即过渡到下一个视图,而是首先广播一个超时消息,包括他们当前的视图和最后观察到的成功提议的信息。在观察到超多数节点的超时消息之前,单个节点不会离开一个失败的视图。
这个明确的超时步骤确保了单个节点只根据集体协议跳过失败的视图,而不是孤立的局部超时。其结果是,超级多数的节点必须在任何时候都在彼此的一个视图之内[5]。这有效地消除了视图分歧的可能性及其相关的负面影响。
回到朋友们组织晚餐的例子,这就类似于鲍勃在没有看到夏娃的信息时,能够回退到与爱丽丝和查理的直接沟通。他们可以迅速确定夏娃是否去露营了,或者鲍勃是否只是错过了她的电话,并决定如何继续。
在孢子期间,节点只要有能力就可以启动。起初,当超级多数还没有上线时,这些节点不会跳过任何视图。一旦有超级多数的节点上线,网络将开始最终确定区块,而无需人工协调或等待视图收敛。同样,在可能发生的任何中断期间,所有诚实的节点将保持在同一视图中,即使它们不能最终完成任何块。一旦根本原因得到解决,最终确定可以立即恢复。这意味着更短的时间,更少的操作开销,以及更快地从意外中断中恢复。
此外,由于失败的视图可以被明确地处理,同时保持视图同步,我们有更多的灵活性来调整超时配置。在任何系统中,尤其是必须考虑到拜占庭参与者的存在,失败是可以预期的。被动式起搏器的每个失败视图都会导致随后的失败视图需要两倍的超时时间,而主动式起搏器可以在不增加超时的情况下允许几个连续的失败视图通过,然后只有在网络条件似乎已经持久退化时才开始增加超时。
定稿速度
Flow使用HotStuff共识算法的一种风味,一旦有足够多的区块确认被纳入链中,该算法就会为区块提供确定性的最终结果。当一个区块被最终确定时,意味着其中的交易已经被网络不可逆转地接受[6],所以最终确定一个区块所需的时间与用户对其操作完成时间的体验直接相关。
由于HotStuff是一种流水线算法,对前一个区块的每一次确认都同时是一个新提议的区块,包含新的用户交易。这也意味着,每次确认一个区块所需的时间与提议和表决一个新区块的时间一样多。在Mainnet上这是一秒多一点。
到目前为止,Flow的共识需要3次成功的确认才能最终确定一个区块。随着Jolteon的升级,只需进行两次确认就可以实现最终确定。这一差异之所以能够实现,是因为引入了主动起搏器。协议中增加了关于失败观点的明确通信,改变了安全的逻辑,使个别节点能够更快地说服自己对一个区块达成共识。
让我们考虑一个例子来说明恢复路径中的积极沟通如何使快乐路径中的最终结果更快。在Asterix的漫画中,罗马军队正试图攻击高卢村。罗马人认为只有同时进攻才能成功,所以他们的军队必须就何时进攻达成一致。每支军队都隐藏在村庄的不同侧面,所以他们必须用信鸽进行沟通。有时信鸽会迷路或延误,尽管他们尽了最大努力。
凯撒的提议 (0-链)
日出时分,凯撒向所有军队发出了8点进攻的建议。我们的军队发出回应说,我们已经准备好在8点进攻。在这一点上,我们只知道我们的军队和凯撒在进攻时间上达成一致。我们不确定其他军队是否收到了凯撒的提议,甚至不知道凯撒是否收到了我们的回应,所以我们承诺进攻并不安全。
我们把我们的本地知识称为0链,因为我们有一个建议,但没有对它进行确认。
第1次确认(1-链)
中午,我们收到凯撒的第二条消息,说所有军队都确认他们可以在8点进攻。我们发出回复,确认了这一点。在这一点上,我们知道凯撒从所有其他军队那里收到了8点的确认。但其他军队知道什么呢?
如果凯撒的第二个信息未能到达其他军队,那么这些军队在只收到建议后将处于与我们相同的位置,并会认为致力于攻击是不安全的。在这一点上,我们知道所有军队都转达了他们可以在8点准备进攻。然而,我们不确定其他军队是否知道这一点。因此,对我们来说,承诺进攻仍然是不安全的。
我们现在把我们的本地知识称为1链,因为我们有一个提案和一轮对提案的确认。
第二次确认 (2-链)
最后,随着暮色降临,我们的士兵越来越焦虑,我们收到了凯撒的第三个信息,说所有军队都承认他的第二个信息。在这一点上,我们知道所有其他军队都知道罗马对8点的承诺。这第三个信息是罗马承诺进攻的证据。
看起来我们现在应该可以进攻了,但如果另一支军队没有收到凯撒的第三条消息呢?那么他们就会和我们在中午收到第二条信息后有同样的认识。事实上,在我们收到每条信息后,我们只能知道所有军队都收到了之前的信息。在最坏的情况下,最近的信息可能只到达我们这里。
如果所有的军队都收到了第三条信息,那么所有的军队都会有他们所需要的信息来承诺进行攻击。换句话说,说服所有军队投入的证据存在。问题是要确保所有军队都能看到这些证据。
那么,我们该怎么做?一个选择是,一旦凯撒收到所有军队的另一轮确认,他就再发一条信息。这条信息将告诉我们,所有军队都看到了罗马人的共识证据,他们需要承诺进行攻击。这将创造一个3链(对提案的连续3次确认),这足以最终确定提案,而不需要主动的后退通信。
但是,如果军队有能力在他们之间进行交流,只有在他们未能及时收到凯撒的通信时才会这样做?在罗马帝国,视频会议是一项新的、非常昂贵的技术,所以财政部只在最危急的情况下才会批准其使用。他们强烈倾向于鸽子。
有了这种退而求其次的团体通信能力,我们的军队只需要知道协议证据的存在(2-链),就可以投入进攻。希望凯撒的最后信息,包括这个证据,将到达所有军队。如果做不到这一点,我们将能够通过快速的视频会议将我们的证据副本传达给其他军队。
总结
这次升级代表着Flow的发展迈出了重要一步,将共识研究的最新进展带到了生产环境中。主动起搏器通过促进从网络升级中更快地恢复,并减少计划外中断的可能性和影响来提高可靠性。这反过来又实现了2链区块终结规则,使交易终结的速度提高了约33%。这两项改进最终为用户和应用开发者提供了一个更强大的平台。
感谢Alex Hentschel、Jerome Pimmel、Misha Rybalov、Tarak Ben Youssef和Yurii Oleksyshyn对本内容的贡献。
笔记
[1] 拜占庭节点可能无法接收或发送消息(就像受良性网络故障影响的节点),但也可能违反协议规则,如向不同的节点发送不同的提议,为多个冲突的提议投票,或试图冒充不同的节点。
[2] 超级多数是由严格大于2/3的委员会组成的。特别是,在选定的 "拜占庭阈值 "f下,一个委员会必须至少有3f+1个节点,超级多数由2f+1个节点组成。例如,一个有4个节点的委员会最多可以容忍1个拜占庭节点;一个有100个节点的委员会最多可以容忍33个拜占庭节点。
[3] 例如,假设我们开始时的超时值是2秒,并且面临一系列连续11次的离线领导。在第10个失败的视图之后,一个共识的节点超时将增加2s * 2^10 = 2048s。因此,该节点将仅在第11个视图中等待34分钟,然后放弃并过渡到第12个视图。
[4] 分叉发生在区块链被引入破坏性变化时--如果一些节点没有升级,那么他们可能对什么是有效的链的扩展有不同意见,并在升级后建立一个冲突的历史版本。当区块链的应用状态被用来初始化一个新的区块链,而不是从一个空的状态初始化时,就会发生勺子。我们使用 "勺子 "一词是因为Flow中的网络升级结合了这两种特性,使协议(节点如何相互通信)和执行环境(交易如何执行和应用数据如何存储)都能快速升级。
[5] 这个超级多数是指贡献了一票或用于结束前一轮的超时的节点集合。其成员可能会在每一轮中发生变化。这对不同拜占庭门槛假设的安全性有影响。最高的拜占庭门槛是f个拜占庭节点,至少有2f+1个诚实的节点,但是一个给定的系统可能会也可能不会允许这f个节点不时地变化。
[6] 从技术上讲,这意味着交易的最终顺序已经确定,所以交易的结果是固定的、可知的,但还不能提供给客户。要想完成,它们也必须被执行和密封,但最终确定是交易处理路径中的一个关键步骤。