EIP-7917: 确定性的提议者预读
在每个 epoch 开始时,预先计算并将确定性的 `proposer_lookahead` 存储在信标状态中
Authors | Lin Oshitani (@linoscope) <lin@nethermind.io>, Justin Drake (@JustinDrake) <justin@ethereum.org> |
---|---|
Created | 2025-03-24 |
Discussion Link | https://ethereum-magicians.org/t/eip-7917-deterministic-proposer-lookahead/23259 |
摘要
在每个 epoch 开始时,预先计算并将确定性的 proposer_lookahead
存储在 beacon_state
中,用于接下来的 MIN_SEED_LOOKAHEAD + 1
个 epoch。
动机
与至少具有 MIN_SEED_LOOKAHEAD == 1
个 epoch 的确定性预读的 RANDAO 种子不同,epoch N + 1
的信标提议者计划无法从 epoch N
期间的信标状态完全预测。原因是,在某些极端情况下,活跃验证者的有效余额 (EB)(本身用作 epoch N + 1
中提议者选举的输入)可以在 epoch N
内发生变化。
基于预确认的协议依赖于确定性的提议者计划来实现平稳运行。自从信标链创世以来,积累到至少 1 ETH 的 slashing 和惩罚可能会改变活跃验证者的 EB,并导致不可预测的提议者计划,尽管 RANDAO 种子是提前知道的。EIP-7251 增加 MaxEB 会增加有效余额的不可预测性,因为积累到至少 1 ETH 的奖励、验证者合并和存款可能会使活跃验证者的 EB 超过 32 ETH。
除了修复下一个 epoch 提议者计划的非确定性之外,此 EIP 还使得下一个 epoch 的提议者计划可以通过信标根和一个简单的 Merkle 证明供应用层访问。这大大简化了基于预确认协议的链上组件的实现。
引入完全确定性的预读修复了一个长期存在的信标链设计缺陷,正如 Danny Ryan 在下面的评论中强调的那样:
通过此更改,验证者将不再可能通过调整有效余额来操纵下一个 epoch 的提议者计划,并且对有效余额调整的分析将从当前对 EB 边缘情况的微妙分析简化为简单的安全分析。最后,提议者预读为 CL 客户端提供了对下一个提议者的提前可见性,这可能会简化实现。
规范
BeaconState
容器扩展了一个 proposer_lookahead
字段,这是一个验证者索引向量,涵盖了完整的可见预读周期,从当前 epoch 的开始到接下来的 MIN_SEED_LOOKAHEAD
个 epoch。
class BeaconState:
...
proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH]
例如,proposer_lookahead[0]
是当前 epoch 中第一个提议者的验证者索引,proposer_lookahead[SLOTS_PER_EPOCH + 4]
是下一个 epoch 中第五个提议者的验证者索引,依此类推。
修改函数 get_beacon_proposer_index
以使用预先计算的 proposer_lookahead
,而不是按需计算提议者索引。
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
"""
返回当前插槽的信标提议者索引。
"""
return state.proposer_lookahead[state.slot % SLOTS_PER_EPOCH]
在 epoch 边界,通过移出当前 epoch 的预读并附加新的预读来更新 proposer_lookahead
。
def process_epoch(state: BeaconState) -> None:
...
process_proposer_lookahead(state)
def process_proposer_lookahead(state: BeaconState) -> None:
last_epoch_start = len(state.proposer_lookahead) - SLOTS_PER_EPOCH
# 移出第一个 epoch 中的提议者
state.proposer_lookahead[:last_epoch_start] = state.proposer_lookahead[SLOTS_PER_EPOCH:]
# 用新的提议者索引填充最后一个 epoch
last_epoch_proposers = compute_proposer_indices(state, Epoch(get_current_epoch(state) + MIN_SEED_LOOKAHEAD))
state.proposer_lookahead[last_epoch_start:] = last_epoch_proposers
此外,fork 后的第一个区块将计算高达 MIN_SEED_LOOKAHEAD
前方 epoch 的所有预读,并填充信标状态中的 proposer_lookahead
字段。
有关更多详细信息,请参阅 consensus-specs 中更新后的信标链规范。
原理
考虑的替代方案
另一种方法是在 epoch N
开始时缓存有效余额,以便它可以用于计算 epoch N+MIN_SEED_LOOKAHEAD
开始时的提议者预读。但是,这种方法需要在信标状态中占用额外的空间,并且无法通过信标根向 EVM 提供提议者预读。
单一秘密领导者选举兼容性
将来,我们可能会引入单一秘密领导者选举 (SSLE) 机制,其中只有选定的验证者知道他们的角色,直到他们提出一个区块。但是,当前的 SSLE 设计仍然依赖于预读,尽管是加密的预读。在这种设计中,我们可以通过将其类型更改为类似 List[EncryptedValidatorIndex]
的内容来重用 proposer_lookahead
字段。如果某个构造完全消除了预读,我们可以简单地将 proposer_lookahead
设置为空列表,这意味着这不会成为障碍。
也就是说,任何此类更改都会给预确认协议带来额外的复杂性,但无论此 EIP 如何,都会出现这种复杂性。此外,APS(Attester Proposer Separation,证明者提议者分离)设想了更复杂的(即,更具 DDoS 抵抗力)执行提议者,从而减少了对 SSLE 的需求。
在 Epoch 边界增加的计算量
在此 EIP 之前,共识客户端只需要在每个 slot 计算当前提议者的索引。通过此 EIP 引入的更改,他们必须在每个 epoch 开始时计算整个 epoch 的提议者计划。但是,计算提议者索引很简单,涉及对验证者进行采样,直到我们找到具有足够的有效余额以被选为提议者的验证者。尽管如此,仍需要进行测试以确认这些额外的计算在实践中不会产生性能瓶颈。
向后兼容性
未发现向后兼容性问题。
测试用例
请参阅 consensus-specs 中的规范测试。
安全考虑
恶意预读篡改
任何提议者选举机制的一个关键考虑因素是防止验证者操纵预读以获得不公平的优势。该提案不会增加此类攻击的可能性,因为我们不会更改预读中使用的“RANDAO 延迟”——epoch N
的预读仍然由 epoch N - MIN_SEED_LOOKAHEAD - 1
的 RANDAO 确定 (这在 epoch N - MIN_SEED_LOOKAHEAD
开始时可用)。唯一的区别是它改变了“有效余额延迟”:现在不是使用 epoch N
开始时的有效余额 (EB),而是使用 epoch N - MIN_SEED_LOOKAHEAD
开始时的 EB。
此外,通过以这种方式对齐 RANDAO 和有效余额,该提案消除了验证者在看到 RANDAO 结果后调整其 EB 的任何机会,这是一个需要考虑的攻击向量。到目前为止还没有发现此类攻击,但此更改消除了这种可能性,从而简化了安全分析。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Lin Oshitani (@linoscope) <lin@nethermind.io>, Justin Drake (@JustinDrake) <justin@ethereum.org>, "EIP-7917: 确定性的提议者预读 [DRAFT]," Ethereum Improvement Proposals, no. 7917, March 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7917.