该文档提出了部分集合安全(PSS)的概念,旨在解决复制安全中现有的一些问题,例如强制验证器验证其不感兴趣的消费者链、小验证器验证额外链的成本高昂以及新消费者链难以加入复制安全。PSS允许每个消费者链仅由提供者验证器集的一个子集来保护,通过引入Top N和Opt In机制,使得验证器可以选择加入或退出特定消费者链的验证,从而提高灵活性和效率。
已接受
目前,在 复制安全性 中,提供链的整个验证器集合被用于保护消费链。这种方法至少存在三个问题。 首先,大量验证器可能被迫验证他们不感兴趣保护的消费链。 其次,对于小型验证器来说,保护额外的链成本很高。这个问题仅通过 软退出 部分解决,软退出 允许小型验证器选择不验证消费链。 第三,由于上述原因,新的消费链加入复制安全性具有挑战性。
作为一种解决方案,我们提出 部分集合安全性 (PSS)。顾名思义,PSS 允许每个消费链仅由提供链验证器集合的一个子集来保护。 接下来,我们将提出实施 PSS 需要采取的确切步骤。这是 PSS 的第一个迭代版本,因此我们提出了使 PSS 成为可能的最精简的解决方案。
在复制安全性中,所有提供链验证器都必须保护每个消费链(除了那些允许通过 软退出 功能选择退出的验证器)。
在 PSS 中,我们允许验证器选择加入和退出验证任何给定的消费链。
这有一个例外:我们为每个消费链引入一个参数 N
,并要求提供链投票权排名前 N%
的验证器必须保护该消费链。
不在前 N%
的验证器如果想在消费链上进行验证,可以动态选择加入。
例如,如果一个消费链的 N = 95%
,那么它最终会获得与当前复制安全性相同的安全性(默认 SoftOptOutThreshold 为 5%)。
另一方面,如果一个消费链的 N = 0%
,那么没有验证器被迫验证该链,但验证器可以选择加入来代替。
在本 ADR 的剩余部分中,如果消费链以 N > 0
的 Top N 链加入,我们称之为 Top N,否则称之为 Opt In 链。Opt In 消费链仅由选择加入以保护该链的验证器提供保护。
我们计划使用来自 v4.0.0 interchain security 的特性分支来实现 PSS。
作为一种简化,并为了避免 chain id squatting,消费链只能通过治理提案加入 PSS,而不能以无需许可的方式加入。
但是,此提案类型将被修改,使其需要比普通提案更低的法定人数百分比,并且每个对提案投“YES”票的验证器都将形成消费链的初始验证器集合。
消费链加入 PSS 的方式与链现在加入复制安全性相同,即通过 ConsumerAdditionProposal
提案。
我们使用一个可选字段扩展 ConsumerAdditionProposal
:
uint32 top_N
:对应于在 Top N 情况下加入的验证器的百分比。
例如,53
对应于 Top 53% 链,这意味着前 53%
的提供链验证器必须验证提议的消费链。
top_N
可以是 0
或包括 [50, 100]
中的任何值。链可以作为 Opt In 加入,其中 top_N == 0
,或者作为 Top N 链加入,其中 top_N ∈ [50, 100]
。
如果是 Top N 链,我们将 top_N
的可能值从 (0, 100]
限制为 [50, 100]
。
通过使 top_N >= 50
,我们可以保证我们不会有成功的攻击,假设最多 1/3
的提供链验证器可能是恶意验证器。
这是因为,N >= 50%
的 Top N 链将至少有 1/3
诚实验证器,这足以阻止攻击。
此外,通过具有 N >= 50%
(因此 N > (VetoThreshold = 33.4%)
),我们使 top N 验证器能够 Veto 他们不想验证的消费链的任何 ConsumerAdditionProposal
。
如果提案的 top_N
参数设置错误,则应在 [ValidateBasic] 中被拒绝。
在代码中,我们通过检查 top_N
是否为零来区分链是 Top N 还是 Opt In。
在 PSS 的未来版本中,我们计划引入一个 ConsumerModificationProposal
,以便我们可以修改消费链的参数,例如,Opt In 链变为 Top N,等等。
我们增加了提供者模块的状态,以跟踪每个消费链的 top_N
值。存储此信息的键将是:
topNBytePrefix | len(chainID) | chainID
要创建上述密钥,我们可以使用 ChainIdWithLenKey
。
然后在 keeper 中,我们引入如下方法:
func (k Keeper) SetTopN(ctx sdk.Context, chainID string, topN uint32)
func (k Keeper) IsTopN(ctx sdk.Context, chainID string) bool
func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool
// 如果是 Top N 链,则返回 N,否则返回错误
func (k Keeper) GetTopN(ctx sdk.Context, chainID string) (uint32, error)
我们还扩展了 interchain-security-pd query provider list-consumer-chains
查询,以返回有关消费链是 Opt In 链还是 Top N 链以及 N 是多少的信息。
这样,区块浏览器可以为消费链呈现信息性消息,例如“此链受提供链的 N% 的保护”。
验证器可以通过发送我们在 tx.proto 中引入的一种新消息类型来选择加入。
message MsgOptIn {
// 要选择加入的消费链的链 ID
string chainID = 1;
// 验证器的提供者地址
string providerAddr = 2;
// (可选)要在消费者上使用的共识公钥
optional string consumerKey = 3;
}
请注意,在 Top N 消费链中,前 N%
的提供链验证器必须验证消费链。
尽管如此,底部 (100 - N)%
的验证器也可以选择加入进行验证。
属于或进入前 N%
验证器的提供链验证器会 自动 选择加入以验证 Top N 消费链。
这意味着,如果验证器 V
属于前 N%
验证器,但后来(例如,由于取消委托)降至底部 (100 - N)%
,则 V
仍被视为已选择加入,并且必须进行验证,除非 V
发送 MsgOptOut
消息(见下文)。
通过在验证器进入前 N%
验证器时自动选择加入验证器,并通过强制前 N%
验证器在降至 (100 - N)%
底部验证器时明确选择退出,我们简化了 PSS 的设计。
请注意,即使消费链尚未运行,验证器也可以发送 MsgOptIn
消息。为此,我们重用 IsConsumerProposedOrRegistered
。如果 chainID
不存在,则 MsgOptIn
应该失败,如果提供者地址不存在,也应该失败。
可选地,选择加入的验证器可以提供 consumerKey
,以便它将不同的消费者密钥(来自提供者)分配给消费链。
当然,验证器始终可以通过在稍后的时间点发送 MsgAssignConsumerKey
消息来更改消费链上的使用者密钥,就像在复制安全性中所做的那样。
对于每个验证器,我们存储一个对 (blockHeight, isOptedIn)
,其中包含验证器选择加入的区块高度以及验证器当前是否选择加入,密钥为:
optedInBytePrefix | len(chainID) | chainID | addr
通过在 optedInBytePrefix | len(chainID) | chainID
上使用前缀迭代器,我们检索所有选择加入的验证器。
我们引入了以下 Keeper
方法。
// 返回已选择加入链 `chainID` 的所有验证器
func (k Keeper) GetOptedInValidators(ctx sdk.Context, chainID string) []Validators
func (k Keeper) IsValidatorOptedIn(ctx sdk.Context, chainID string, val Validator) bool
我们引入了以下两个查询:
interchain-security-pd query provider optedInValidators $chainID
interchain-security-pd query provider hasToValidate $providerAddr
一个查询用于检索已选择加入的验证器,因此也是需要验证消费链的验证器,一个查询用于给定验证器的地址,返回此验证器必须验证的所有链。
如前所述,验证器可以通过发送 MsgOptIn
消息手动选择加入。
此外,在 Top N 链中,当验证器从底部 (100 - N)%
移动到顶部 N%
验证器时,会自动选择加入。
最后,如果验证器在引入消费链的 ConsumerAdditionProposal
期间投 Yes
票,它们也可以选择加入。
这简化了验证器的操作,因为它们不必发送额外的消息来选择加入。
由于 Tally
方法在读取后 删除投票,因此我们无法在投票统计后检查验证器的投票。
为了规避这个问题,我们为 AfterProposalVote
引入了一个Hook,并跟踪验证器投出的所有投票。
如果验证器投了多个投票,我们只考虑最新的投票。
最后,如果验证器在 加权投票 的情况下投了 100% 的 Yes
票,我们才认为验证器已选择加入。
已选择加入某链的验证器可以通过发送以下消息选择退出:
message MsgOptOut {
// 要选择退出的消费链的链 ID
string chainID = 1;
// 验证器的提供者地址
string providerAddr = 2;
}
只有在消费链启动后,验证器才能选择退出,因此如果 chainID
的链未运行,则上述消息会返回错误。
此外,属于前 N%
验证器的验证器无法从 Top N 链中选择退出,因此在这种情况下,MsgOptOut
会出错。
当验证器选择退出时,我们还会通过删除选择退出的验证器来更新已选择加入的验证器的状态。
请注意,只有已选择加入的验证器才会因消费链上的停机时间而受到惩罚。
为此,我们使用所有已选择加入的验证器的历史信息;我们可以检查密钥 optedInBytePrefix | len(chainID) | chainID | addr
下存储的 blockHeight
,以查看验证器是否在某个时间点选择加入。
这样,我们可以因停机时间而监禁验证器,因为我们知道验证器过去确实已选择加入。
否则,我们可以考虑这样一种情况:验证器 V
停机一段时间,但在 V
因停机时间受到惩罚之前,验证器 V
选择退出,然后我们不知道是否应该惩罚 V
。
如果 ConsumerAdditionProposal
已通过,则 Top N 消费链始终在指定日期 (spawn_time
) 启动。
只有在至少一个验证器已选择加入时,Opt In 消费链才会启动。我们在 BeginBlockInit 中检查这一点:
func (k Keeper) BeginBlockInit(ctx sdk.Context) {
propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx)
for _, prop := range propsToExecute {
chainID := prop.ChainId
if !k.IsTopN(ctx, chainID) && len(k.GetOptedInValidators(ctx, chainID)) == 0 {
// 删除提案
ctx.Logger().Info("could not start chain because no validator has opted in")
continue
}
...
消费链应该只由已选择加入的验证器进行验证。
当 queue VSCPacket
时,我们引入了执行此操作的逻辑。
这背后的逻辑并不像看起来那么简单,因为 CometBFT 不会收到必须验证链的验证器集合,而是收到 验证器更新 的增量。
例如,要从消费链中删除已选择退出的验证器,我们必须发送一个 power
为 0
的验证器更新,类似于在 分配使用者密钥 中所做的那样。
我们计划在稍后阶段更新此 ADR,说明我们计划如何准确地实施此逻辑。
目前,奖励分配如下:消费者 定期将奖励发送 到提供者的 ConsumerRewardsPool
地址。
然后,提供者 将这些奖励转移到费用收集器地址,并且这些转移的奖励将分配给验证器和委托人。
在 PSS 中,我们仅将奖励分配给实际验证消费链的验证器。 为此,我们有一个与每个消费链关联的池,并且消费者 IBC 将奖励转移到此池。 然后,我们从每个消费池中提取奖励,并将它们分配给已选择加入的验证器。
请注意,我们只将奖励分配给已选择加入一段时间(例如,10000 个区块)的验证器,以避免验证器仅为了获得奖励而选择加入,然后立即选择退出的情况。
在 Opt In 链中,一组验证器可能会尝试执行攻击。为了阻止此类潜在攻击,PSS 允许使用欺诈投票。 欺诈投票 是一项治理提案,旨在削减执行攻击的验证器。 由于其固有的复杂性,我们计划在不同的 ADR 中以及 PSS 的未来迭代中引入欺诈投票。
我们不会更改双重签名和轻客户端攻击的削减功能的方式。 如果验证器在消费者上发生不当行为,那么我们会削减提供者上的该验证器。
我们不会更改停机时间监禁功能的方式。 如果验证器在消费链上停机足够长的时间,我们会在提供者上监禁该验证器,但前提是该验证器在最近已选择加入该消费链。
新的消费链更容易消耗提供者链的经济安全性,因为如果不是每个人都被迫验证,提案更有可能通过。
如果小型验证器不想再验证链,则不会被迫验证链。
我们可以弃用软退出实现。
SoftOptOutThreshold
的值为 5%
),除非它是 N >= 95%
的 Top N 链。
- 原文链接: github.com/cosmos/interc...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!