今年早些时候,Flow的共识团队对我们的共识算法进行了一次重大升级。这次升级将交易的最终完成时间减少了30%,并且能够更快地从网络分区、攻击和升级中自动恢复。
作为这次升级的一部分,研究团队为共识算法的追随者模式的一系列改进奠定了技术基础,提高了所有非共识节点的攻击弹性和处理速度,包括观察者节点,任何人都可以在没有桩的情况下操作。我们很高兴地分享,这些改进将在2023年6月的网络升级中推出。在这篇文章中,我们更详细地解释了我们的改进,以及它们对Flow网络的影响。
流动的共识的参与者和追随者
Flow以流水线的方式处理交易,其中有五种专门的节点类型--访问、收集、共识、执行和验证节点(都有桩)--一起工作。此外,还有非桩的观察者节点。简而言之,共识节点通过提出新区块并对其进行投票来安排交易的执行。其他节点跟踪链的进展,并按照共识节点的安排执行工作。
Flow的共识协议支持两种模式:参与者和追随者。参与者是一个节点,它通过提出和表决新的区块来积极促进共识的发展。参与者的核心职责是强制执行区块的有效性,只对有效的区块进行投票,对无效的区块提出抨击。共识节点在Flow的主要共识中以参与者模式运行[1]。
共识跟随者是允许任何非共识节点不受信任地跟踪共识进展的模块。跟随者独立地应用共识规则,并在本地宣称区块的最终性。从本质上讲,它屏蔽了节点内的高层处理逻辑,避免了任何恶意节点发送无效区块。与参与者不同,跟随者不需要完全验证区块的有效载荷--相反,他们可以只验证区块头,利用参与者已经完成的验证工作。
改进措施
新的Follower实现利用了我们最近的共识升级的架构改进,通过加固节点以抵御各种攻击,使Follower能够检测到违反共识的情况,并为未来极其高效的轻客户端奠定了基础。
大部分的改进是通过重新设计Flow节点验证Quorum证书(QC)的方式和时间来实现的。一个QC是一个超级多数的共识参与者的投票的集合。虽然提议或投票支持无效区块是一种可被砍掉的协议违规行为,但该协议仍然必须处理这种情况,因为个别共识参与者可能是有目的的恶意的(用技术术语说就是 "拜占庭")。
Flow采用Jolteon作为其BFT共识算法,它是HotStuff的一种优化版本。在HotStuff中,B区块的QC被包含在B的子区块中。一个有效的QC证明了超级多数的共识参与者认为该区块是链的有效延伸。我们说,当且仅当B区块的有效QC被知道时,区块B就被认证了。
Follower的升级重点是性能和对拜占庭攻击的弹性。为了减轻通过无效区块的攻击,我们重新设计了Follower对传入区块的内部处理:
- 在缓存前验证区块头信息
- 将经过认证的区块与未经认证的区块分开缓存
- 不要存储无效的块
验证或丢弃
在上一版本的Follower中,在我们验证区块的头之前,有必要知道一个区块的整个祖先。出于性能方面的考虑,临时缓存区块是很重要的,即使一些祖先仍然丢失。
对断开的块进行缓存的必要性可能会被利用,成为对以前的跟随者实现的内存耗尽攻击的载体:
在所描述的例子中,Evil Edward--一个拜占庭式的共识参与者--正在编造一个长长的区块分叉。由于这些区块是假的,它们还没有经过共识委员会的投票,也没有有效的QCs存在。因此,爱德华在他的分叉中包括无效的QCs。先前的追随者无法评估爱德华分叉中的QCs是否有效,因为追随者不知道分叉的全部祖先。
如果有足够的区块和时间,这种攻击可能会导致Follower崩溃,出现内存不足的异常。即使没有崩溃,这种内存耗尽的攻击也会通过用假的区块充斥其缓存来伤害Follower,从而延迟有效区块的处理。在测试中,我们已经看到,对一个节点的跟随者进行内存耗尽攻击会导致该节点落后,不能及时履行其在网络中的功能。
升级后的追随者现在可以验证当前纪元(一周)内的区块,然后才知道其祖先。因此,新的共识追随者会发现爱德华的假区块是无效的,丢弃它们,并对爱德华提出砍价挑战。当前纪元之外的区块会被立即丢弃。
缓存/存储的改进
追随者使用一个缓存来存储尚未被纳入区块链的区块。这个缓存现在被分离成不同的部分,用于认证和未认证的区块。
在上面的例子中,恶意的主程序创建了1001个冲突的区块。共识规则保证其中只有一个会得到认证(在我们的例子中是区块B)。然而,事先并不清楚哪一个最终会被认证给接收区块的追随者。因此,老追随者会将所有的区块存储在磁盘上,这是一个存储耗尽攻击的载体。
相比之下,新的Follower只在磁盘上存储认证块。由于许多冲突的块中只有一个可以被认证,认证的块几乎不构成存储耗尽攻击的风险。
未经认证的区块有一个有效的提议者签名,但在恶意领导的情况下可能会构成威胁。因此,这些区块被单独存储在一个有限大小的缓存中,以减轻内存耗尽的攻击。
平行验证
Flow的核心共识算法在一个单线程的事件循环中运行,这意味着一次处理一个事件。这对于这个关键任务组件的简单性是可取的,但却限制了性能。
最近Flow共识的架构变化的一个重点是尽可能地将工作从这个事件循环中转移出来。团队在2022年初实施了并行的投票处理,在最近的Jolteon升级中实施了并行的超时处理,我们正在将其扩展到Follower引擎中的并行QC验证,作为Follower V2的一部分。
当一个已经落后的节点正在追赶时,这种变化特别有利,因为它可以并行处理更多的块。
链式验证
当一个区块被认证时,超级多数的共识节点已经同意该区块是一个有效的扩展状态,并投票支持它。作为一个每个区块都建立在其父代之上的区块链,一个区块只有在扩展一个有效区块时才是有效的。换句话说,如果一个区块被认证,那么它的所有祖先也被认证。
新的Follower可以利用这一事实来避免不必要的工作。当处理一个连续的区块范围时,只需确保该范围末端的区块是经过认证的(并检查区块的哈希值是否匹配)。因此,我们只对整个批次进行一次昂贵的签名验证操作,而不是对批次中的每个区块进行一次验证。
当一个节点正在追赶时,它默认以顺序范围接收块,并可以利用这种验证改进来提高同步速度。
为智能手机大小的轻型客户端打下基础
当设想在嵌入式硬件(包括智能手机)上运行的轻型客户端时,有三个主要挑战需要解决:
- 轻型客户端能够合理消耗的CPU周期、内存和存储量是非常有限的。你可能更喜欢用你的智能手机存储空间来存放照片,而不是几千兆字节的链式数据。
- 为了使轻型客户端对普通终端用户有用,它必须处理长时间离线的问题。即使轻客户端被允许通过操作系统和用户行为不断地验证链的进度,这样做也会使电池消耗得很快,令人无法接受。
- 最后,轻型客户端必须能够快速有效地赶上多天的链式进展。下载所有区块是不可能的。想象一下,从为期三天的野营旅行回来,急切地想检查一些NBA顶级镜头时刻的交易是否成功--但你的手机需要20分钟来下载和验证所有它错过的区块头......并且耗尽了电池。
正如本文前面所讨论的,新的Follower允许在不知道其整个祖先的情况下验证区块的QC,只要这些区块是在一个已知的历时内。对于Flow来说,一个纪元通常是一个星期。在这一周内,授权节点的集合保持不变。如果一个节点被砍,网络可能会撤销他们提出区块的权利,但在纪元中不会有新的节点加入。
在纪元转换时,授权运行纪元N的节点将区块链的控制权移交给纪元N+1的节点。稍微简化一下,其工作原理如下:
- 在第N纪元开始时,新的节点可以参与第N+1纪元的定标拍卖。
- 在定标拍卖阶段结束时,一个EpochSetup事件被包含在一个区块中,它列出了Epoch N+1的所有授权节点。此后,在纪元设置阶段,纪元N+1的节点合作执行分布式密钥生成(DKG)协议。这一重要步骤使Flow成为极少数在协议内提供安全随机性的区块链之一。当DKG结束时,EpochCommit事件被包含在一个区块中。
- EpochSetup和EpochCommit事件共同构成了Epoch N+1的 "身份表"。简而言之,第X纪元的身份表包含所有必要的信息(如节点身份、角色、公钥等),用于验证第X纪元期间生成的QC。
请注意,任何拥有第N纪元身份表的追随者只需要几个区块来构建第N+1纪元的身份表。下图说明了这个过程:
- 跟随者只需联系一个完整的节点,要求 "包含EpochSetup和EpochCommit事件的块,为EpochN+1加上足够多的子块,证明EpochSetup和EpochCommit事件被封存"。
- 知道Epoch N身份表的追随者可以通过检查QC和区块散列来评估响应的完整性,因为它收到的所有区块必须属于Epoch N。然而,如果检查通过,追随者就有了加密证据,证明Epoch N的共识委员会提交了EpochSetup和EpochCommit事件。
- 在这一点上,追随者知道了N+1纪元的正确身份表,并可以重复同样的过程来检索N+2纪元的身份表,随后是N+3,等等。
总之,共识追随者只需要检索两个链段,每个链段有10-20个区块,就可以在本地构建下一纪元的身份表。块的有效性是通过在每个批次结束时验证尾随的QC和重新洗牌收到的块来断定的。
虽然在一个纪元中大约有46万个区块被最终确定,但一个共识追随者只需要检索这些区块的0.005%。因此,追随者可以快速浏览几个月甚至几年的区块,消耗的计算和网络资源可以忽略不计。
此外,考虑一个时代(过去或现在),追随者知道其身份表。跟随者可以在任何高度检索属于该纪元的短链段,并通过检查QCs来断定其有效性和最终性。因此,追随者可以不受信任地获得几乎任何区块的状态根哈希值,因为状态根哈希值也包含在区块中[2]。当知道某个区块的状态根哈希值时,就有可能利用Merkle证明,以无信任的方式选择性地检索部分状态。
笔记
- [1] 采集节点是一种特殊情况,因为它们既是参与者又是跟随者。它们遵循Flow的主共识,同时参与其集群的子共识协议,该协议摄取了一部分进入Flow的交易。这种责任的分离是支持Flow可扩展架构的创新之一。
- [2] 我们在这里稍微简化了一下讨论。在Flow中,块并不直接包含执行所含事务后获得的状态根哈希。相反,在Flow中,块的执行是异步的,一旦执行结果可用,就会嵌入到降级块中。如果你想了解更多,我们推荐 这个优秀的博客系列。
有兴趣在Flow上进行构建吗? 请访问我们的开发者门户网站来开始,并在Twitter或Discord上与我们保持联系。