33 个主要重大版本更新
36.0.0 | 2024年7月18日 |
---|---|
35.0.0 | 2024年7月12日 |
34.0.0 | 2024年6月24日 |
33.0.0 | 2024年5月23日 |
0.0.0 | 2022年11月21日 |
#1221 in 魔法豆
6,212 每月下载量
在 64 个 crate 中使用(直接使用 8 个)
3MB
58K SLoC
多阶段、离链选举提供商托盘。
目前,此选举提供商有两个不同的阶段(见 Phase
),已签名 和 未签名。
阶段
货盘的时间线如下。在每个区块中,使用 frame_election_provider_support::ElectionDataProvider::next_election_prediction
来估计下一次调用 frame_election_provider_support::ElectionProvider::elect
的时间。基于此,选择一个阶段。时间线如下。
elect()
+ <--T::SignedPhase--> + <--T::UnsignedPhase--> +
+-------------------------------------------------------------------+
Phase::Off + Phase::Signed + Phase::Unsigned +
请注意,未签名阶段在 next_election_prediction
前开始 pallet::Config::UnsignedPhase
个区块,但只有在发生对 ElectionProvider::elect
的调用时才结束。如果没有发生 elect
,则签名阶段会延长。
因此,对于此组件的用户来说,确保在请求新的选举之前始终通过
elect
来结束选举是非常重要的。
可以通过基本上将它们的长度设置为零来禁用每个阶段。如果两个阶段的长度都是零,则组件基本上只运行标记为 Config::Fallback
的回退策略。
签名阶段
在签名阶段,提交并排队在链上的解决方案(类型为 RawSolution
)。基于解决方案的大小,预留了一定的存款,用于在链上保持该解决方案的区块数以及解决方案被检查时的潜在权重。最多存储 pallet::Config::SignedMaxSubmissions
个解决方案。队列始终根据分数(从差到好)排序。
新解决方案到达时
- 如果队列未满,则将其存储在适当的排序索引中。
- 如果队列已满,但提交的解决方案比队列中的任何一个都要好,则丢弃较差的解决方案,退还即将提交解决方案的保证金,并将新解决方案存储在正确的索引中。
- 如果队列已满,且解决方案与队列中的任何一个相比都没有改进,则立即拒绝该解决方案,不预留额外的保证金。
签名解决方案不能被撤销、撤回、更新或撤回。换句话说,如果解决方案已排队,原始来源不能以任何方式退出。
在签名阶段结束时,从最好到最差(即 pop()
直到排空)检查解决方案。每个解决方案都要经过昂贵的 Pallet::feasibility_check
检查,确保声明的分数是正确的,并且根据选举数据(即选票和目标)是有效的。在每一步中,如果当前最佳解决方案通过了可行性检查,则被认为是最佳解决方案。奖励原始发送者,其余排队的解决方案将退还其保证金并被丢弃,而无需检查。
以下示例涵盖了签名阶段结束时的所有情况。
Queue
+-------------------------------+
|Solution(score=20, valid=false)| +--> Slashed
+-------------------------------+
|Solution(score=15, valid=true )| +--> Rewarded, Saved
+-------------------------------+
|Solution(score=10, valid=true )| +--> Discarded
+-------------------------------+
|Solution(score=05, valid=false)| +--> Discarded
+-------------------------------+
| None |
+-------------------------------+
请注意,两个解决方案最终都被丢弃并退还了保证金,尽管其中一个无效。
未签名阶段
未签名阶段始终紧随签名阶段之后,持续特定时长。在此阶段,只有验证节点可以提交解决方案。启用了离链工作者的验证节点将开始在此阶段挖掘解决方案并将其作为未签名交易提交回链,因此得名“未签名”阶段。这种未签名交易在传播时永远不可能有效,并且其行为类似于固有。
验证节点只有在他们计算的解决方案严格优于队列中最好的一个时才会提交解决方案,并将解决方案的权重限制为MinerConfig::MaxWeight
。
根据上一个签名阶段的结果,未签名阶段可以被设置为被动,通过将Phase
的第一个内部值设置为false
。目前,签名阶段始终处于激活状态。
回退
如果我们达到两个阶段(即调用ElectionProvider::elect
)的末尾)且没有好的解决方案排队,则使用回退策略pallet::Config::Fallback
来确定需要做什么。链上选举速度慢,且不包含平衡或减少后处理。如果pallet::Config::Fallback
失败,则启用下一个阶段Phase::Emergency
,这是一种更安全的方案。
紧急阶段
如果有以下任何原因之一
- 未提交任何签名或未签名解决方案,且未提供成功的
Config::Fallback
- 其他未预见的内部错误
调用 T::ElectionProvider::elect
时,无法返回 Ok(_)
,此时包将进入 Phase::Emergency
状态。在此阶段,任何解决方案都可以通过 Config::ForceOrigin
,无需任何检查,通过 Pallet::set_emergency_election_result
交易提交。因此,[
Config::ForceOrigin]
应仅设置为一个可信的来源,如委员会或根。一旦提交,强制解决方案将保存在 QueuedSolution
中,直到下一次调用 T::ElectionProvider::elect
,此时它将返回,并且 Phase
将返回到 Off
。
这意味着,此包的用户(即质押包)应在出现错误时重新尝试调用 T::ElectionProvider::elect
,直到返回 OK(_)
。
要生成紧急解决方案,只需提供一个参数:Supports
。这实际上是选举中选出的获胜者及其支持者的集合。支持可以通过任何方式生成。在最简单的情况下,可能是手动生成。例如,在大量网络故障或恶意行为的情况下,Config::ForceOrigin
可能会决定只选择少数紧急获胜者(这将大大限制下一个验证者集,如果此包与 pallet-staking
一起使用)。如果故障是由于其他技术原因,那么使用 Polkadot 存储库中提供的 staking-miner 二进制文件生成支持是一个简单且安全的方法。此二进制文件有一个名为 emergency-solution
的子命令,能够连接到实时网络,并使用标准算法生成适当的 supports
,并以十六进制格式输出,以便提交。请注意,虽然此二进制文件位于 Polkadot 存储库中,但此特定子命令可以在任何基于 substrate 的链上工作。
有关更多信息,请参阅 staking-miner
文档。
可行解决方案(正确解决方案)
所有提交都必须经过可行性检查。在签名阶段结束时逐个检查签名解决方案,而未签名解决方案则现场检查。一个可行的解决方案如下
- 所有使用的索引都必须正确。
- 确保显示正确的获胜者数量。
- 任何分配都会与
RoundSnapshot::voters
匹配。 - 所声明的分数有效,基于定点算术精度。
精度
通过 SolutionAccuracyOf
配置选举的精度,这是提交的解决方案必须遵守的精度。
请注意,精度非常重要。链下解决方案应尽可能小,以减少解决方案的大小/重量。
错误类型
本模块提供了一种详细的错误系统,以简化未来的调试和调试。错误的整体层次结构如下
pallet::Error
:这些是模块的 dispatchables 可以返回的错误,可以是已签名或未签名的。由于这里不能进行嵌套枚举的分解,因此它们以它们所属的逻辑子系统为前缀。ElectionError
:这些是在模块在自动场景下执行某些操作时可能生成的错误,例如offchain_worker
或on_initialize
。这些错误对日志记录很有帮助,因此作为以下嵌套ElectionError::Miner
:包装一个unsigned::MinerError
。ElectionError::Feasibility
:包装一个FeasibilityError
。ElectionError::Fallback
:包装一个回退错误。ElectionError::DataProvider
:包装一个静态字符串。
请注意,这些子错误之间可能存在重叠。例如,SnapshotUnavailable
可能在矿工和可行性检查阶段同时发生。
未来计划
紧急阶段恢复脚本:此脚本应从波卡中的抵押矿工中取出,理想情况下位于 substrate/utils/frame/elections
。
挑战阶段。我们计划在模块中添加第三个阶段,称为挑战阶段。这是一个不再处理任何解决方案的阶段,任何人(已签名或未签名)都可以挑战当前的最佳解决方案。主要计划是强制解决方案为PJR。在链上检查PJR相当昂贵,而证明一个解决方案不是PJR则相对便宜。如果成功证明一个排队中的解决方案是错误的
- 我们肯定会削减提交该解决方案的人(可能对未签名的解决方案构成挑战)。
- 我们将回退到应急策略(可能延长当前时代)。
援助。从队列中提交解决方案的功能非常好。矿工可以在他们认为解决方案有很高的可行性时立即提交,然后在之后进行检查,并移除他们的解决方案(可能仅涉及交易费用或保证金的一部分)。
有条件地开放未签名阶段:目前,未签名阶段始终开放。这很有用,因为诚实验证器将运行substrate OCW代码,这应该足以战胜平庸或恶意的签名提交(假设在没有诚实签名机器人)。如果有签名提交,可以将其与绝对度量(例如PJR)进行比较,然后我们只能在极端条件下(即“未收到好的签名解决方案”)开放未签名阶段,以减轻活动验证器的负担。
允许较小的解决方案并逐步建立:目前,我们只允许与DesiredTargets
完全相同的解决方案,不多也不少。随着时间的推移,我们可以将其更改为[min, max],其中任何在此范围内的解决方案都是可接受的,较大的解决方案优先。
根据(字节)大小评分:如果有平局,我们应该始终优先考虑较小的解决方案。更严格的做法是强制执行reduce
算法的界限。
在基准测试中考虑编码/解码的权重。目前,我们只考虑在submit_unsigned
中的编码/解码权重,因为它的优先级。尽管如此,所有针对解决方案和快照的操作都值得考虑这一点。这里的所有测试都应该仅用于测试可行性检查,而不应涉及更多。审计和审查这些测试的最好方法是尝试找到一个无效的解决方案,但该解决方案仍然通过系统被视为有效。
依赖项
~17-32MB
~529K SLoC