变更记录
- 2019-10-15:初始草案
- 2020-05-25:移除相关性根惩罚
- 2020-07-01:更新为使用 S 曲线函数而非线性函数
背景
在基于权益证明的链中,共识权力集中在少数验证者手中,会因审查风险增加、活性失效、分叉攻击等问题对网络造成损害。然而,尽管这种中心化会给网络带来负外部性,这种影响并不会被那些继续委托给已足够大的验证者的委托人直接感知。我们希望有一种方式,能够将中心化带来的负外部性成本转移给这些大型验证者及其委托人。决策
设计
为了解决这个问题,我们将实现一种称为“按比例惩罚”的机制。目标是:验证者规模越大,其应承担的惩罚越重。最直接的初步方案,是让验证者的惩罚比例与其在共识投票权中的占比成正比。参数化
这要求对逻辑函数进行参数化。其参数化方式已经非常成熟,共包含四个参数:- 最小惩罚因子
- 最大惩罚因子
- S 曲线的拐点位置(本质上就是你希望 S 的中心落在哪里)
- S 曲线的增长速率(S 被拉长到什么程度)
非女巫验证者之间的相关性
可以注意到,这个模型并不会区分多个验证者究竟是由同一批运营者运行,还是由不同运营者运行。从某种意义上说,这实际上还是一个额外的优点。它会激励验证者尽量让自己的部署方案与其他验证者不同,以避免与其他验证者发生相关性故障,否则就要承担更高的惩罚。例如,运营者应避免使用相同的热门云托管平台,或使用相同的 Staking as a Service 提供商。这将使网络更加稳健,也更具去中心化特性。恶意连带
恶意连带指的是攻击者故意让自己被惩罚,从而加重他人的惩罚。在这里,这种情况可能会成为一种担忧。不过按照本文描述的协议,攻击者自身也会和受害者一样受到同等影响,因此这种行为对恶意实施者并没有太大收益。实现
在 slashing 模块中,我们将新增两个队列,用于追踪近期所有的惩罚事件。对于双签故障,我们将“近期惩罚”定义为过去unbonding period 内发生的惩罚;对于活性故障,我们将“近期惩罚”定义为过去 jail period 内发生的惩罚。
SlashEvent 结构体,其中记录出错验证者的投票权占比,并将 SlashedSoFar 初始化为 0。由于近期惩罚事件会在 unbonding period 和 unjail period 到期前被剪枝,因此同一个验证者理论上不可能在同一时间于同一个队列中存在多个 SlashEvent。
随后,我们会遍历队列中的所有 SlashEvent,累加它们的 ValidatorVotingPercent,以此计算队列中所有验证者新的统一惩罚比例,计算方式采用上文引入的“根之和的平方”公式。
得到 NewSlashPercent 之后,我们会再次遍历队列中的所有 SlashEvent。如果某个 SlashEvent 满足 NewSlashPercent > SlashedSoFar,则调用 staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)(这里传入的是验证者在任何惩罚发生之前的 power,以确保扣减的代币数量正确)。随后,将该 SlashEvent.SlashedSoFar 更新为 NewSlashPercent。
状态
提议中后果
正面影响
- 通过抑制向大型验证者委托,提升去中心化程度
- 激励验证者之间降低相关性
- 对攻击行为的惩罚比对意外故障更严厉
- 惩罚比例的参数化更灵活
负面影响
- 比当前实现具有更高的计算开销,并且需要在链上存储更多关于“近期惩罚事件”的数据。
Changelog
- 2019-10-15: Initial draft
- 2020-05-25: Removed correlation root slashing
- 2020-07-01: Updated to include S-curve function instead of linear
Context
In Proof of Stake-based chains, centralization of consensus power amongst a small set of validators can cause harm to the network due to increased risk of censorship, liveness failure, fork attacks, etc. However, while this centralization causes a negative externality to the network, it is not directly felt by the delegators contributing towards delegating towards already large validators. We would like a way to pass on the negative externality cost of centralization onto those large validators and their delegators.Decision
Design
To solve this problem, we will implement a procedure called Proportional Slashing. The desire is that the larger a validator is, the more they should be slashed. The first naive attempt is to make a validator’s slash percent proportional to their share of consensus voting power.Parameterization
This requires parameterizing a logistic function. It is very well understood how to parameterize this. It has four parameters:- A minimum slashing factor
- A maximum slashing factor
- The inflection point of the S-curve (essentially where do you want to center the S)
- The rate of growth of the S-curve (How elongated is the S)
Correlation across non-sybil validators
One will note, that this model doesn’t differentiate between multiple validators run by the same operators vs validators run by different operators. This can be seen as an additional benefit in fact. It incentivizes validators to differentiate their setups from other validators, to avoid having correlated faults with them or else they risk a higher slash. So for example, operators should avoid using the same popular cloud hosting platforms or using the same Staking as a Service providers. This will lead to a more resilient and decentralized network.Griefing
Griefing, the act of intentionally getting oneself slashed in order to make another’s slash worse, could be a concern here. However, using the protocol described here, the attacker also gets equally impacted by the grief as the victim, so it would not provide much benefit to the griefer.Implementation
In the slashing module, we will add two queues that will track all of the recent slash events. For double sign faults, we will define “recent slashes” as ones that have occurred within the lastunbonding period. For liveness faults, we will define “recent slashes” as ones that have occurred withing the last jail period.
SlashEvent struct is created with the faulting validator’s voting percent and a SlashedSoFar of 0. Because recent slash events are pruned before the unbonding period and unjail period expires, it should not be possible for the same validator to have multiple SlashEvents in the same Queue at the same time.
We then will iterate over all the SlashEvents in the queue, adding their ValidatorVotingPercent to calculate the new percent to slash all the validators in the queue at, using the “Square of Sum of Roots” formula introduced above.
Once we have the NewSlashPercent, we then iterate over all the SlashEvents in the queue once again, and if NewSlashPercent > SlashedSoFar for that SlashEvent, we call the staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent) (we pass in the power of the validator before any slashes occurred, so that we slash the right amount of tokens). We then set SlashEvent.SlashedSoFar amount to NewSlashPercent.
Status
ProposedConsequences
Positive
- Increases decentralization by disincentivizing delegating to large validators
- Incentivizes Decorrelation of Validators
- More severely punishes attacks than accidental faults
- More flexibility in slashing rates parameterization
Negative
- More computationally expensive than current implementation. Will require more data about “recent slashing events” to be stored on chain.