前言
本文件旨在描述 Polkadot 的平行链的功能主机的目的、功能和实现--即为组成平行链提供安全和推进的软件。它不是为一个特定的平行链的实现者,而是为平行链主机的实现者。实际上,这是为一般的 Polkadot 的实现者准备的。
还有一些其他文件更详细地描述了这项研究。所有参考文件都将在此链接,并应与本文件一起阅读,以获得对全貌的最佳理解。然而,这是唯一一份旨在描述 Polkadot 特定实例的关键方面的文件,其中包括许多研究的低层次技术细节和软件架构。
平行链从何而来
平行链是问题的解决方案。就像任何解决方案一样,如果不首先了解问题,就无法理解它。因此,让我们先来看看区块链技术所面临的问题,这些问题导致我们开始探索类似于平行链的设计空间。
问题 1: 扩展性
几年前,简单的工作证明(PoW)区块链,如比特币、以太坊和无数其他区块链的交易吞吐量显然太低。
TODO: 如果有更多的区块链,等等。
权益证明(PoS)系统可以完成比 PoW 区块链更高的吞吐量。PoS 系统是由绑定的资本而不是花费的算力来保证的--流动性机会成本与燃烧的电力。它们的工作方式是选择一组具有已知经济身份的验证者,他们锁定代币以换取 "验证" 或参与共识过程的权利。如果他们被发现错误地执行该过程,他们将被惩罚,这意味着部分或全部被锁定的代币将被烧毁。这提供了一个强大的抑制不当行为的方向。
由于共识协议不围绕着浪费算力,区块时间和协议可以更快发生。在编写区块之前,不必找到 PoW 挑战的解决方案,因此编写区块的开销被减少到只有创建和分发区块的成本。
然而,PoS 链上的共识需要 2/3 以上的验证者对发生在第一层的一切达成完全一致:所有的逻辑都是作为区块链的状态机的一部分进行的。这意味着每个人仍然需要检查一切。此外,验证者可能会根据他们在异步网络上收到的信息对系统有不同的看法,这使得对最新状态的共识更加困难。
平行链是一个分片协议的例子。分片是一个从传统数据库架构中借用的概念。我们不要求每个参与者检查每笔交易,而是要求每个参与者检查一些交易的子集,其中有足够的冗余,使那些杂乱无章的(任意恶意的)参与者不能偷偷地进入无效的交易--至少不会被发现并被惩罚,这些交易会被恢复。
分片和权益证明相互协调,允许一个平行链主机在许多平行链上提供完全的安全性,即使没有所有参与者检查所有的状态转换。
TODO: 注意网络影响和桥接
问题 2: 灵活性/专业化
"dumb" 虚拟机并没有给你带来灵活性。任何工程师都知道,能够对一个问题进行专业化的处理会给他们和他们的用户带来更多的 杠杆。
TODO: 扩展杠杆
认识到这些问题后,我们开始寻找解决这些问题的方法,这可以让开发者创建和部署专用的区块链,统一在一个共同的安全源下,并且能够在它们之间进行消息传递;一个 异质分片解决方案,我们称之为平行链。
协议概览
本节旨在高水平地描述在 Polkadot 中运行平行链所涉及的角色和协议。具体来说,我们描述了不同的行为者如何相互沟通,他们单独和集体保存什么样的数据结构,以及他们为什么要做这些事情的高级目的。
我们的最高目标是将一个平行链区块从编写到安全包含,并定义一个可以重复和平行进行的过程,用于许多不同的平行链,以便随着时间的推移扩展它们。理解这里所采取的高层次的方法对于进一步提供拟议的架构的背景是很重要的。与此相关的 Polkadot 的关键部分是主要的 Polkadot 区块链,被称为中继链,以及为该区块链提供安全和输入的行为者。
首先,有必要介绍一下我们参与这个协议的主要行为者。
- 验证人。这些节点负责验证拟议的平行链区块。他们通过检查区块的有效性证明(PoV)来做到这一点,并确保 PoV 仍然可用。他们把金融资本作为 "游戏中的皮肤",如果他们被证明是错误的验证,可以被砍掉(销毁)。
- 收集人。这些节点负责创建验证人知道如何检查的有效性证明。创建 PoV 通常需要熟悉交易格式和平行链的区块编写规则,以及能够访问平行链的全部状态。
- 渔民。这些是由用户操作的、无权限的节点,其目标是抓住行为不端的验证人以换取赏金。收集人和验证人也可以作为 "渔夫" 行事。渔民对于安全来说并不是必须的,而且本文也没有深入涉及。
这意味着一个简单的管道,收集人向验证人发送平行链区块和其必要的 PoV 来检查。然后,验证人使用 PoV 验证区块,签署描述积极或消极结果的声明,如果有足够的积极声明,该区块可以在中继链上被注意到。负面的声明不是否决,但会导致争议,那些错误的一方会被惩罚。如果另一个验证人后来发现一个验证人或一组验证人错误地签署了一份声明,声称一个区块是有效的,那么这些验证人将被惩罚,而验证人将获得赏金。
然而,这种表述有一个问题。为了让另一个验证人在事后检查前一组验证人的工作,PoV 必须保持 可用,以便其他验证人可以获取它以检查工作。预计 PoVs 太大,无法直接包含在区块链中,所以我们需要一个替代的 数据可用性 方案,要求验证人证明他们工作的输入将保持可用,所以他们的工作可以被检查。经验性测试告诉我们,许多 PoV 在重载期间可能在 1 到 10MB 之间。
下面是对包含管道的描述:一个平行链区块(或简称 parablock)从创建到包含的路径。
- 验证人是由验证人分配程序选择并分配给平行链的。
- 收集人产生平行链区块,它被称为平行链候选人或候选人,同时还有候选人的 PoV。
- 收集人通过收集人协议将候选人和 PoV 转发给分配给同一平行链的验证人。
- 在某一特定时间点分配到某一平行链的验证人参与到候选人支持子系统中,以验证被提出来验证的候选人。从验证人那里收集到足够多的签名有效性声明的候选人被认为是 "可支持的"。他们的支持是已签署的有效性声明的集合。
- 由 BABE 选择的中继链区块的作者,可以为每个平行链记下最多一个可支持的候选者,并将其与支持一起纳入中继链区块。一个可支持的候选者一旦被纳入中继链,就被认为在中继链的那个分叉中得到了支持。
- 一旦在中继链中得到支持,该平行链候选者就被认为是 "待定可用性"。它不被认为是作为平行链的一部分,直到它被证明是可用的。
- 在接下来的中继链区块中,验证人将参与可用性分布子系统以确保候选者的可用性。有关候选者可用性的信息将在随后的中继链块中被指出。
- 一旦中继链状态机有足够的信息认为候选人的 PoV 是可用的,该候选人就被认为是平行链的一部分,并逐渐成为一个完整的平行链区块,或简称为 parablock。
请注意,候选人可以在以下任何一种情况下不被列入:
- 收集人无法将该候选者传播给分配给该平行链的任何验证人。
- 候选人没有被参与候选人支持子系统的验证人支持。
- 候选人没有被中继链区块作者选择纳入中继链。
- 候选人的 PoV 在超时内不被认为是可用的,并被从中继链中丢弃。
这个过程还可以进一步划分。步骤 2 和 3 与收集人的工作有关,即整理并通过整理分发子系统将候选人分发至验证人。步骤 3 和 4 涉及到候选人支持子系统中的验证人和区块作者(本身就是一个验证人)的工作,将区块纳入中继链。步骤 6、7 和 8 对应于中继链状态机的逻辑(也称为运行时),用于将区块完全纳入链中。第 7 步需要验证人的进一步工作,以参与可用性分布子系统,并将该信息纳入中继链,以便第 8 步完全实现。
这给我们带来了该过程的第二部分。一旦一个 parablock 被认为是可用的和平行链的一部分,它仍然是 "等待批准"。在流水线的这一阶段,该 parablock 已经得到了分配给该 parablock 的小组中大多数验证人的支持,其数据已经被整个验证人集合保证为可用。一旦它被认为是可用的,主机甚至会开始接受该块的子块。在这一点上,我们可以认为该区块已经被暂时包含在平行链中,尽管还需要更多的确认。然而,平行链组中的验证人(被称为该平行链的 "平行链验证人")是从一个验证人集合中抽出的,这个验证人集合中含有一定比例的拜占庭,或者任意的恶意成员。这就意味着,某些区块的平行链验证人可能是多数人不诚实的,这意味着在区块被认为是被批准之前,必须对其进行(二次)批准检查。这一点是必要的,因为一个给定的 parachain 的验证人是从整个验证人集合中抽取的,而这个验证人集合被假定为最多<1/3 不诚实--这意味着有机会随机抽取 parachain 的验证人,这些验证人多数或完全不诚实,可能错误地支持一个候选人。审批程序允许我们在事后发现这种错误行为,而不需要分配更多的 Parachain 验证人,并减少系统的吞吐量。一个 parablock 未能通过审批过程,将使该区块以及其所有的子代无效。然而,只有支持有问题的区块的验证人会被惩罚,而不是支持后代的验证人。
审批程序,一目了然,看起来像这样:
- 被纳入流水线的 Parablocks 正在等待批准,其时间窗口被称为二次检查窗口。
- 在二次检查窗口期间,验证人随机地自我选择对 parablock 进行二次检查。
- 这些验证人在这里被称为二次检查者,他们获得了 parablock 和其 PoV,并重新运行验证功能。
- 二级检查员对他们的检查结果进行 gossip。相互矛盾的结果会导致升级,所有验证人都需要检查该块。争端中失败一方的验证人被惩罚。
- 在审批过程结束时,该区块要么被批准,要么被拒绝。稍后会有更多关于拒绝过程的内容。
关于审批程序的更多信息,可在 "审批" 专门章节中找到。关于争议的更多信息,可在 "争议" 的专门章节中找到。
这两条流水线总结了扩展和获得一个 Parablock 的全部安全所需的事件顺序。请注意,在接受一个新的区块之前,必须结束对一个特定的 parachain 的包含流水线。纳入后,审批程序开始启动,可以同时为许多平行链区块运行。
重申候选人的生命周期:
- 候选:由一个收集人向一个验证人提出。
- 附议:由一个验证人向其他验证人提出。
- 可支持:由大多数指定的验证人证明的有效性。
- 被支持:可支持的并在中继链的分叉中指出。
- 待定的可用性:有支持,但还没有被认为是可用的。
- 包含:已支持并被认为是可用的。
- 接受:已支持,可使用,且无争议。
上图显示了一个区块从(1)候选状态到(7)批准状态的乐观路径。
还需要注意的是,中继链是由 BABE 扩展的,它是一种分叉算法。这意味着不同的区块作者可以同时被选择,而且可能不是建立在同一个区块父本上。此外,验证人的集合并不固定,平行链的集合也不固定。而且,即使有相同的验证人和平行链集,验证人对平行链集的分配也是灵活的。这意味着,在接下来的章节中提出的架构必须处理网络状态的可变性和多重性。
在这个例子中,第 1 组已经收到了 C 块,而其他组由于网络不同步而没有收到。现在,第 2 组的验证人可以在 B 的基础上建立另一个块,称为 C'。假设之后,一些验证人同时知道了 C 和 C',而其他验证人仍然只知道一个。
那些意识到许多竞争头的验证人必须意识到在每个头上发生的工作。他们可能对两个头都有一些或全部的贡献。由于网络的不同步性,两个分叉有可能在一段时间内平行增长,尽管在没有对抗性网络的情况下,在有验证人知道两个链头的情况下这是不可能的。
审批程序
审批程序是中继链确保只有有效的区块被最终确定的机制,而支持验证者要对设法将坏区块纳入中继链的行为负责。
只要区块没有被中继链的最终性程序 GRANDPA 最终确定,那么让一个平行链区块包含在中继链的分叉中并不是灾难性的。如果该区块没有被最终确定,这意味着中继链的分叉可以通过动态分叉选择规则来恢复,从而使诚实的验证者忽略任何包含该区块的分叉。
处理一个坏的分叉要经过以下几个阶段:
- 检测
- 升级
- 结果
首先,坏块必须被诚实的一方发现。其次,诚实的一方必须将坏块升级到由所有验证者检查。最后,一个坏区块的正确结果必须发生。第一个结果,如上所述,是恢复链,使所有节点认为是最好的,不再包含坏块。第二个结果是砍掉所有恶意验证者。请注意,如果包含坏块的链被还原,那么争议的结果需要被移植,或者至少可以移植到该链的所有其他分叉上,这样恶意验证者在所有可能的历史上都会被砍掉。换句话说,需要没有任何可能的中继链可以让恶意验证者无代价地逃脱。
接受一个 parablock 是通过检测阶段而没有争议的最终结果,或者是通过升级/争议阶段而获得积极的结果。为了使其发挥作用,我们需要检测程序具有这样的特性:总是选择足够多的诚实验证者来检查联锁,并且他们不能被对手干扰。这需要与一般的平行链的规模问题相平衡:获得第一个属性的最简单方法是让每个人都检查所有的东西,但这显然太沉重了。因此,我们对另一个属性也有一个期望的约束,即让尽可能少的验证者检查任何特定的 parablock。我们的赋值函数是我们选择验证人对 parablocks 进行批准检查的方法。
相对于考虑 parablocks 是否被批准而言,把中继链块看作是已经被批准或没有被批准,往往更有意义。含有一个坏块的中继链块需要被撤销,而只含有已批准的块的中继链块可以被称为已批准,只要其父中继链块也被批准。重要的是,任何特定的中继链块的有效性都取决于其祖先的有效性,因此我们不会最终确定一个在其祖先中有坏块的块。
审批大致有两个部分:
-
分配 决定了哪些验证者对哪些候选人进行批准检查。它确保每个候选人收到足够的随机检查器,同时减少对手获得足够检查器的几率,并限制对手的预知能力。它跟踪批准投票,以识别 "不显示" 批准检查所需的可疑时间,也许表明该节点正在受到攻击,并在这种情况下分配更多的检查。它跟踪中继链的等价交换,以确定对手何时可能获得关于分配的预知,并在这种情况下增加额外的检查。
-
批准检查 监听分配子系统发出的分配通知,我们应检查特定的候选人。然后,它通过首先调用重建子系统来获得候选人,其次在候选人身上调用候选人有效性子系统来执行这些检查,最后发出批准投票,或者可能启动一个争议。
这两个协议首先作为链外共识协议运行,使用所有验证者之间的 gossip 的 信息,其次作为这个链外协议的事后进度的链上记录。我们需要链上协议来为链下协议提供奖励。
审批需要两种 gossip 消息类型,即由其分配子系统创建的分配通知,以及由我们的审批检查子系统在得到候选人有效性效用子系统授权后发送的审批投票。
审批密钥
我们需要为审批子系统提供两个单独的密钥:
-
审批分配密钥 是仅用于分配标准 VRF 的 sr25519/schnorrkel 密钥。我们通过在 VRF 的额外消息中包括其中继链上下文和额外数据,隐含地用批准分配密钥签署分配通知,但将这些从其 VRF 输入中排除。
-
审批投票密钥 将只签署候选人的 parablock 有效性,没有自然的密钥类型限制。这没有必要实际体现一个新的会话密钥类型。我们只是想在分配和批准之间做出区分,尽管遥远的未来的节点配置可能有利于单独的角色。我们重新使用与实践中用于 parachain 支持的相同的密钥。
审批投票密钥可以比较容易地由一些加固的签名者工具来处理,也许甚至是 HSM,假设我们为审批投票钥匙选择 ed25519。审批分配键可能支持也可能不支持加固的签名人工具,但这样做听起来要复杂得多。事实上,分配密钥只决定了决定审批检查器分配的 VRF 输出,对于这些输出,他们只能采取行动或不采取行动,所以他们不能含糊其辞、撒谎等,对验证器操作者来说,如果有惩罚的风险,也是很少的。
在未来,我们将确定在几种加固技术中,哪一种对整个网络最有利。我们可以为验证者提供一个多进程多机器的架构,也许甚至让人想起 GNUNet,或者更像智能 HSM 工具。我们可能反而会设计一个更像完整系统的系统,比如像 Cosmos 的哨兵节点。在任何一种情况下,审批分配可能由一个稍微加固的机器处理,但不一定像审批投票那样加固,但批准投票机器必须同样运行外国 WASM 代码,这增加了他们的风险,所以分配听起来是有帮助的。
分配
审批分配决定了每个验证者对哪些候选中继链块进行审批检查。一个审批环节只考虑一个中继链块,并且只分配中继链块宣布可用的那些候选人。
分配要兼顾几个方面:
- 限制对手对任务的预知性。
- 确保有足够的检查者,并且
- 相对公平地分配任务。
受托人决定自己的任务,使用两到三个任务标准检查特定的候选人。在相关之前,受托人从不透露他们的分配,流言蜚语延迟了提前发送的分配,这限制了其他人的预知性。受托人只通过中继链块了解他们的分配。
所有标准都要求验证者使用他们的 VRF 秘钥评估一个可验证的随机函数(VRF)。所有的标准都输入关于会话的中继链块的特定数据,称为 "故事",并输出要检查的候选者和称为 DelayTranche
的优先次序。
当然,当他们的候选人变得可用时,我们会解放可用的核心,但有一个批准分配标准继续将每个候选人与它在可用时占据的核心号码联系起来。
分配在由这个DelayTranche
决定的松散的时间回合中进行,假设半秒的闲话时间,其进行速度大约是六秒块生产的 12 倍。如果在我们到达第t
轮时,候选人C
需要更多的批准检查员,那么任何在延迟批次t
中对C
有任务的验证者都会说闲话,为C
发送任务通知。我们继续下去,直到所有候选人都有足够的批准检查员被分配。如果我们还没有足够的核对器,我们就把整个批次的核对器放在一起,所以我们期望严格地超过足够的核对器。如果有些核对人返回他们的批准票的速度太慢,我们也会采取较晚的批次(见下文没有显示)。
分配确保验证者检查那些他们有延迟档零又有最高优先权的中继链块,所以对手总是面对与延迟档零的预期分配数量相等的诚实检查者。
在这些标准中,BABE VRF 输出为两个标准提供了故事,它减少了对手可以定位自己的检查器的频率。我们有一个标准,其故事由候选人的区块哈希值和外部知识组成,即中继链等价物存在一个冲突的候选人。当对手通过在中继链区块生产中的等价交换获得对另外两个人的预知时,它提供了不可预见的分配。
公告/通知
我们在节点之间 gossip 分配通知,以便所有验证者知道哪些验证者应该检查每个候选人,以及任何候选人是否需要更多的验证者。
分配通知包括一个由区块哈希值给出的中继链上下文、一个分配标准(由标准标识符和可选的标准特定字段组成)、一个受托人标识符和受托人的 VRF 签名,其本身由一个 VRF 预输出和一个 DLEQ 证明组成。它的 VRF 输入由标准组成,通常包括一个特定的标准字段,以及一个关于其中继链上下文块的 "故事"。
我们从不在包含分配通知的 gossip 消息中包含故事,而是要求每个验证者重建它们。我们在争端过程中从不关心分配,所以这不会使远程争端复杂化。
在 Schnorr VRF 中,有一个与此输入不同的额外的签名信息,我们将其设置为中继链块散列。因此,分配通知是自签名的,并且可以在没有额外签名的情况下 "礼貌地" gossip,也就是说,在能够从中继链上下文计算出故事的节点之间。换句话说,如果我们不能计算出一个分配通知的 VRF 部分所要求的故事,那么我们的自签名属性就会失效,我们就不能验证它的来源。我们可以通过另一个签名层(64 字节)或包括从故事中计算出来的 VRF 输入点(32 字节)来解决这个问题,但这样做似乎没有什么用处。
任何验证者都可以过早地发送他们的分配通知和/或批准投票。我们提前 gossip 批准票,因为它们代表了验证者的一个重要承诺。然而,我们推迟发送分配通知,直到它们与我们的本地时钟一致。我们还规定了一个礼貌性条件,即接收者知道分配通知所使用的中继链上下文。
故事
我们根据关于中继链区块R
的两个可能的 "故事" 来制定分配标准,其中包括候选人又称宣布候选人可用。所有的故事都有一个输出,试图最小化对抗性影响,然后作为分配标准的 VRF 输入。
我们首先有一个RelayVRFStory
,它从中继链块生产者在创建R
时产生的另一个 VRF 输出中输出随机性。在诚实的节点中,只有这一个创建R
的中继链块生产者事先知道这个故事,甚至他们在两个纪元前也一无所知。
在 BABE 中,我们创建这个值时,调用schnorrkel::vrf::VRFInOut::make_bytes
,上下文为 "A&V RC-VRF",VRFInOut
来自授权生产一级区块的 VRF,否则来自二级区块类型的 VRF。
在 Sassafras 中,我们将始终使用非匿名的回收 VRF 输出,而不是授权区块生产的匿名环形 VRF。我们目前不知道 Sassafras 是否会有一个单独的 schnorrkel 密钥,但如果它重复使用其环形 VRF 密钥,有一个等价的ring_vrf::VRFInOut::make_bytes
。
我们喜欢RelayVRFStory
允许相对较少的选择,但在中继链区块生产中含糊其辞的对手可能会过早地了解依赖于RelayVRFStory
的分配,因为同一个中继链 VRF 出现在多个区块中。
因此,我们提供了一个次要的RelayEquivocationStory
,它输出候选人的区块哈希值,但只针对候选人的等价交换。当存在另一个中继链区块R1
时,我们说R
中的候选人C
是一个等价物,即R
和R1
具有相同的RelayVRFStory
,但R
包含C
,R1
不包含C
。
我们也想要检查位于我们首选的中继链之外的候选等价物,这代表了分配模块的一个稍微不同的用法,并且可能需要 gossip 消息中的更多信息。
分配标准
分配标准使用故事和验证人的密钥批准分配密钥来计算实际分配。分配标准输出一个Position
,包括要检查的ParaId
,以及分配生效时的优先级DelayTranche
。
赋值标准有三种类型:RelayVRFModulo
、RelayVRFDelay
和RelayEquivocation
。其中,RelayVRFModulo
和RelayVRFDelay
都运行一个 VRF,其输入是RelayVRFStory
的输出,而RelayEquivocation
运行一个 VRF,其输入是RelayEquivocationStory
的输出。
其中,我们有两个不同的 VRF 输出计算。
RelayVRFModulo
运行几个不同的样本,其 VRF 输入是RelayVRFStory
和样本号。它用schnorrkel::vrf::VRFInOut::make_bytes
计算 VRF 输出,使用 "A&V 核心 "的上下文,将这个数字与可用核心的数量相乘,并输出刚刚被宣布为可用的候选人,并由 aka 离开该可用核心包括。我们放弃任何没有返回候选人的样本,因为在这个中继链块中没有候选人离开采样的可用性核心。我们最初选择三个样本,但我们可以通过增加到四个或五个样本,并相应地减少支持检查来使 polkadot 更加安全和高效。所有成功的`RelayVRFModulo'样本都被分配到延迟档位 0。
RelayVRFDelay
和RelayEquivocation
没有采样过程。相反,我们在特定的候选人上运行它们,它们从其 VRF 输出中计算延迟。 RelayVRFDelay'对包括在中继链块下的所有候选者运行,也就是宣布可用,并通过
RelayVRFStory'输入相关 VRF 输出。 RelayEquivocation
只对候选区块等值运行,并通过RelayEquivocation
故事输入其区块散列值。
RelayVRFDelay
和RelayEquivocation
都使用schnorrkel::vrf::VRFInOut::make_bytes
计算它们的输出,使用上下文 "A&V Tranche",并将结果按num_delay_tranches + zeroth_delay_tranche_width
的方式减少,并将结果 0 到zeroth_delay_tranche_width
合并为 0。通过这种方式,他们确保了 zeroth 延迟段的zeroth_delay_tranche_width+1
倍于任何其他段的分配数。
作为未来的工作(或 TODO?),我们应该使用vrf_merge
合并具有相同延迟和故事的分配通知。我们不能合并那些具有相同延迟和不同故事的通知,因为RelayEquivocationStory
可能会改变,但RelayVRFStory
永远不会改变。
播音员和观察者/跟踪者
我们跟踪所有验证人对每个中继链区块相关的候选人宣布的批准分配,这告诉我们哪些验证人被分配给哪些候选人。
我们允许每个验证人在每个故事中最多为每个候选人分配一次任务,因此一个验证人可以在 "RelayVRFDelay"和 "RelayEquivocation"标准下被分配,但不能在 "RelayVRFModulo"和 "RelayVRFDelay"标准下分配,因为它们都使用同一个故事。我们只允许每个审定者对每个候选人投一次批准票,这对任何适用的标准都是有意义的。
我们宣布,并开始检查我们自己的任务,当其批次的延迟达到时,但只有当跟踪器说受让人候选人需要更多的批准检查者。我们从不宣布我们认为不必要的任务,因为早期宣布会给对手带来信息。所有延迟批零的分配总是被宣布,这包括所有RelayVRFModulo
分配。
换句话说,如果某个候选者C
在我们到达第t
轮时需要更多的批准检查者,那么任何在延迟档t
中对C
有任务的验证者都会散布他们对C
的任务通知,并开始重建和验证C
。然而,如果C
达到了足够的分配,那么后来有分配的验证者就跳过宣布他们的分配。
我们继续下去,直到所有候选人都有足够的批准检查员被分配。我们从不在批次内对任务进行优先排序,并将某一批次的所有任务或未分配的任务放在一起计算,所以我们经常会超额完成分配的批准检查员的目标数量。
不显示
我们有一个 "不显示" 的超时时间,比一个中继链槽长,所以至少 6 秒,在此期间,我们希望批准检查应该成功地重建候选区块,重做其擦除编码以检查候选接收,最后重新检查候选区块本身。
如果验证者在我们收到他们的任务通知后,在这个 "不显示" 的时间内没有批准或提出异议,我们就认为他们是 "不显示"。我们从收到他们的任务通知开始计时,而不是从我们想象中的实际时间开始计时,因为否则的话,收到迟到的任务通知就会立即产生 "不显示 "和不必要的工作。
我们担心 "不显示" 代表验证器受到了拒绝服务攻击,可能是为了防止它重建候选人,但也可能是为了延迟它对争端的 gossip。因此,我们总是通过增加一整批额外的延迟验证器来取代 "不显示",所以这种攻击总是导致额外的验证器。
举个例子,假设我们需要 20 个验证人,但第 0 档只产生 14 个,第 1 档只产生 4 个,那么我们从第 2 档拿走所有 5 个,因此该候选人需要 23 个验证人。如果第一组或第二组的检查员查理在 8 秒内没有反应,那么我们就从第三组中加入所有 7 名检查员。如果第三组中的一个检查员 Cindy 在 8 秒内没有反应,那么我们就从第四组中抽取全部 3 个检查员。我们现在有 33 个检查员在对候选人进行检查,所以这个过程升级得很快。
我们之所以升级得这么快,是因为我们担心查理和辛迪可能是分配给该候选人的唯一诚实的检查员。因此,如果查理或辛迪最终返回批准,那么我们就可以得出批准的结论,并放弃第四批的检查员。
因此,我们要求 "不显示" 的超时时间要比中继链的时间长,这样我们就可以在链上见证 "不显示"。我们将在下面讨论这如何有助于奖励取代 "不显示" 的验证者。
我们避免对 "不显示" 本身进行削减,尽管 "不显示" 可能会进入一些惩罚重复不良表现的计算,可能会取代`ImOnline',我们可以减少他们的奖励并进一步奖励那些填写的人。
作为未来的工作,我们可以预见扩大 "不露面 "的方案,使额外的检查者匿名,比如通过使用分配注意到一个新的标准,采用环形 VRF,然后所有验证者通过请求几个擦除编码的碎片来提供掩护,但这样的匿名方案听起来非常复杂,远远超出我们最初的功能。
分配推迟
我们希望验证者在随机获得过多的任务时,偶尔会超载。所有这些波动都会在多个区块中得到很好的摊销,但这就会减慢最终结果。
因此,我们允许验证者故意推迟发送他们的任务。如果没有人知道他们的任务,那么他们就会避免产生 "不显示" 的情况,而且工作量会正常进行。
我们强烈希望延期来自于比零更重要的批次,因为零批次的检查在一定程度上提供了更多的安全性。
TODO:什么时候?这对网络来说是最好的吗?
链上验证
我们应该在链上验证批准,以奖励批准检查者。因此,我们要求 "不显示" 的超时时间比中继链槽长,这样我们就可以在链上见证 "不显示",这有助于实现这一目标。链上记录链下过程的主要挑战是对抗性区块生产者,他们可能会审查投票或向链上发布投票,导致其他投票被忽略和不被奖励(奖励偷窃)。
原则上,所有验证者都有一些 "档次",在这些档次上,他们被分配给准链上的候选人,这确保我们最终达到足够的验证者。如上所述,当缓慢的验证者最终出现时,我们经常收回 "不显示",所以见证他们最初是 "不显示" 有助于管理奖励。
我们希望链上验证应该分两个阶段进行。我们首先在中继链区块中记录分配通知和批准投票,在区块验证中再次进行 VRF 或常规签名验证,并在中继链状态中插入链上验证的无签名说明,其中包含每个分配通知的验证人、批次、准绳和中继区块高度。然后,我们后来有另一个中继链块,运行一些 "批准的"内在的,它从状态中提取所有这些笔记,并将它们输入我们的批准代码。
我们现在在推迟和链上验证之间的互动中遇到了一个利基关注。任何拥有零档(或其他低档)分配的验证者可以延迟发送分配通知,比如因为他们推迟了他们的分配档(这是允许的)。如果他们后来在决赛时间前后发送了这个分配通知,那么他们就与这个批准的比赛。本质上:如果他们的公告得到链上(也允许),那么是的,它推迟了决赛。如果它没有上链,那么是的,我们有一个公告,链外的共识系统说是有效的,但链上的人因为太慢而忽略了。
在这种情况下,我们需要链上的人获胜,但这样做需要对最终结果施加一个令人厌恶的漫长的总体延迟。我们也可以探索对推迟的限制,但这听起来更难。
参数
我们倾向于在 RelayVRFModulo
下进行审批检查员的分配,而不是 RelayVRFDelay
,因为 RelayVRFModulo
可以避免给单个检查员太多的分配,而且零档分配最有利于安全。我们建议在RelayVRFModulo
下至少分配 16 个检查员,尽管分配水平从未被正确分析过。
我们的延迟标准RelayVRFDelay
和RelayEquivocation
都有两个主要参数,即每档预期检查器和零延迟档宽度。
我们要求每批的预期检查者少于 3 个,因为否则一个拥有 1/3 利益的对手可以强迫所有节点检查所有的块。我们强烈建议每批的预期检查者少于两个,这有助于避免意外的和故意的爆炸。我们还建议每档的预期检查器大于 1,这有助于防止攻击者预测比推进一档只增加他们自己的验证器。
我们通过零档分配来提高安全性,所以RelayEquivocation
应该将其前几个档合并到零档。我们将其描述为零延迟段的宽度,最初我们将RelayEquivocation
设置为 12,RelayVRFDelay
设置为 1。
为什么用 VRFs?
我们用 VRF 做分配,是为了给 "足够多的" 检查者一些意义,而不仅仅是 "预期" 检查者。
我们可以指定一个只使用系统随机性的协议,这很有效,因为我们最强的防御是预期的分配自己的诚实检查员的数量。在这种情况下,对手可以轻而易举地用他们自己的检查员淹没他们自己的区块,所以这个强大的防御成为我们唯一的防御,延迟分批变得毫无用处,所以一些区块实际上有零个批准检查员,可能总体上只有一个检查员。
虽然 VRFs 要求对手在这种攻击之间等待的时间要长得多,这也有助于对付那些没有什么利害关系的对手,因为他们破坏了验证器。VRFs 提高了用户的信心,即没有发生这种 "驱赶"攻击,因为延迟付款系统至少确保了一些最低数量的批准检查者。在这种情况下,VRFs 允许减少支持检查和增加批准检查,这使 polkadot 更有效率。
Gossip
任何验证人都可能过早地发送他们的任务通知和/或批准票。我们对批准票进行gossip,因为它们代表了验证人的一个重要承诺。我们保留但推迟gossip分配通知,直到它们与我们的本地时钟一致。
过早地 gossip 分配通知可能会产生一个拒绝服务的载体。如果是这样的话,我们可能会利用使我们的时钟同步的相对时间方案,可以想象它允许放弃过早的分配。
最终性 GRANDPA 投票规则
中继链要求验证者参与 GRANDPA。在 GRANDPA 中,验证者就他们认为是链上最好的区块提交链外投票,GRANDPA 决定由超级多数的子链所包含的共同区块。根据前几轮投票的结果,对可以提交的内容也有额外的限制。
为了避免最终确定任何没有收到足够的批准票或有争议的东西,我们将把批准协议与对 GRANDPA 投票策略的改变结合起来,使诚实的节点只对其中的每个准链候选人都被批准的链进行投票。此外,该投票规则防止对存在任何现场争议或任何争议已解决的候选人无效的链进行投票。
因此,最终确定的中继链应该只包含大多数人认为其中的每一个区块都得到了充分的批准的中继链区块。
未来工作
我们可以考虑额外的 gossip 消息,节点声称 "缓慢的可用性"和/或 "缓慢的候选",以微调任务的 "不显示" 系统,但足够长的 "不显示" 延迟可能就足够了。
一旦可用性系统使用直接的 UDP 连接工作,我们将在 UDP 方面发展更多的实践经验。在这一点上,我们应该发现重建是否在完整的图中表现得足够好或 从拓扑结构的限制中获益。在这一点上,一个分配通知可以隐含地从一个随机的 1/3 中请求碎片,也许是拓扑结构的限制,这可以节省一个闲话轮。如果这种初步的快速重建失败了,那么节点就直接请求替代的棋子。这与 "慢速可用性"的要求重叠,有一个有趣的设计空间。