Alert Source Discuss
Standards Track: Core

EIP-7251: 增加 MAX_EFFECTIVE_BALANCE

允许验证者拥有更大的有效余额,同时保持 32 ETH 的下限。

Authors mike (@michaelneuder), Francesco (@fradamt), dapplion (@dapplion), Mikhail (@mkalinin), Aditya (@adiasg), Justin (@justindrake), lightclient (@lightclient), Felix Lange (@fjl)
Created 2023-06-28
Requires EIP-7002, EIP-7685

摘要

增加常量 MAX_EFFECTIVE_BALANCE,同时保持最小质押余额 32 ETH。这允许大型节点运营商整合到更少的验证器中,同时也允许单人质押者获得复利奖励并以更灵活的增量进行质押。

动机

截至 2023 年 10 月 3 日,目前有超过 830,000 个验证器参与共识层。这个集合的大小持续增长,部分原因是 MAX_EFFECTIVE_BALANCE,它将单个验证器的 stake 限制为 32 ETH。这导致大量的“冗余验证器”,它们由单个实体控制,可能在同一个信标节点上运行,但具有不同的 BLS 签名密钥。对 MAX_EFFECTIVE_BALANCE 的限制是原始分片设计的技术债务,其中子委员会(不是证明委员会,而是 is_aggregator 中计算的委员会)需要是多数诚实的。因此,保持子委员会成员的权重大致相等,降低了单个大型验证器包含过多影响力的风险。在当前设计下,这些子委员会仅用于证明聚合,因此只有 1/N 的诚实假设。

由于协议的安全模型不再依赖于 MAX_EFFECTIVE_BALANCE 的低值,我们建议在保持 32 ETH 的最小验证器阈值的同时提高此值。增加旨在减少验证器集的大小,从而减少网络上的 P2P 消息数量、每个 epoch 需要聚合的 BLS 签名数量以及 BeaconState 的内存占用。此更改为小型和大型验证器都增加了价值。大型验证器可以整合以运行更少的验证器,从而减少信标节点的数量。小型验证器现在受益于复利奖励以及以更灵活的增量进行 stake 的能力(例如,能够 stake 40 ETH,而不是像今天这样需要积累 64 ETH 才能运行两个验证器)。

规范

常量

执行层

Name Value Comment
CONSOLIDATION_REQUEST_TYPE 0x02 EIP-7685 的 consolidation request 类型前缀
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS 0x0000BBdDc7CE488642fb579F8B00f3a590007251 在哪里调用并存储有关 consolidation request 机制的相关详细信息
SYSTEM_ADDRESS 0xfffffffffffffffffffffffffffffffffffffffe 用于在合约上调用系统操作的地址
EXCESS_CONSOLIDATION_REQUESTS_STORAGE_SLOT 0  
CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT 1  
CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT 2 指向 consolidation request 消息队列头部的指针
CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT 3 指向 consolidation request 消息队列尾部的指针
CONSOLIDATION_REQUEST_QUEUE_STORAGE_OFFSET 4 状态内 consolidation request 消息队列的起始内存槽
MAX_CONSOLIDATION_REQUESTS_PER_BLOCK 2 可以出队到区块中的 consolidation request 的最大数量
TARGET_CONSOLIDATION_REQUESTS_PER_BLOCK 1  
MIN_CONSOLIDATION_REQUEST_FEE 1  
CONSOLIDATION_REQUEST_FEE_UPDATE_FRACTION 17  
EXCESS_INHIBITOR 2**256-1 在第一次系统调用之前用于计算费用的 Excess 值

共识层

Name Value
COMPOUNDING_WITHDRAWAL_PREFIX Bytes1('0x02')
MIN_ACTIVATION_BALANCE Gwei(2**5 * 10**9) (32 ETH)
MAX_EFFECTIVE_BALANCE_ELECTRA Gwei(2**11 * 10**9) (2048 ETH)

执行层

Consolidation request

新的 consolidation request 是一个类型为 0x02EIP-7685 request,由以下字段组成:

  1. source_address: Bytes20
  2. source_pubkey: Bytes48
  3. target_pubkey: Bytes48

consolidation request 的 EIP-7685 编码如下。请注意,我们只是返回由合约返回的编码 request 值。

request_type = CONSOLIDATION_REQUEST_TYPE
request_data = dequeue_consolidation_requests()

Consolidation request contract

该合约具有三个不同的代码路径,可以概括为以下高级别:

  1. 添加 consolidation request - 需要一个 96 字节的输入,连接源验证器和目标验证器的公钥。
  2. Fee getter - 如果输入长度为零,则返回添加 consolidation request 所需的当前费用。
  3. 系统处理 - 如果由系统地址调用,则从队列中弹出当前区块的 consolidation request。
添加 Consolidation Request

如果对合约的调用数据输入正好是 96 字节,则执行以下操作:

  1. 确保发送了足够的 ETH 来支付当前的 consolidation request 费用 (msg.value >= get_fee())
  2. 将当前区块的 consolidation request 计数增加 1 (increment_count())
  3. 将 consolidation request 插入到源地址和源和目标公钥的队列中 (insert_consolidation_request_into_queue())

具体来说,该功能在伪代码中定义为函数 add_consolidation_request()

def add_consolidation_request(Bytes48: source_pubkey, Bytes48: target_pubkey):
    """
    Add consolidation request adds new request to the consolidation request queue, so long as a sufficient fee is provided.
    只要提供足够的费用,添加 consolidation request 就会将新的 request 添加到 consolidation request 队列。
    """

    # Verify sufficient fee was provided.
    # 验证是否提供了足够的费用。
    fee = get_fee()
    require(msg.value >= fee, 'Insufficient value for fee')

    # Increment consolidation request count.
    # 增加 consolidation request 计数。
    count = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT)
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT, count + 1)

    # Insert into queue.
    # 插入队列。
    queue_tail_index = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT)
    queue_storage_slot = CONSOLIDATION_REQUEST_QUEUE_STORAGE_OFFSET + queue_tail_index * 4
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot, msg.sender)
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 1, source_pubkey[0:32])
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 2, source_pubkey[32:48] ++ target_pubkey[0:16])
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 3, target_pubkey[16:48])
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT, queue_tail_index + 1)
Fee calculation

以下伪代码可以计算单个 consolidation request 的成本,给定一定数量的 excess consolidation request。

def get_fee() -> int:
    excess = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, EXCESS_CONSOLIDATION_REQUESTS_STORAGE_SLOT)
    require(excess != EXCESS_INHIBITOR, 'Inhibitor still active')
    return fake_exponential(
        MIN_CONSOLIDATION_REQUEST_FEE,
        excess,
        CONSOLIDATION_REQUEST_FEE_UPDATE_FRACTION
    )

def fake_exponential(factor: int, numerator: int, denominator: int) -> int:
    i = 1
    output = 0
    numerator_accum = factor * denominator
    while numerator_accum > 0:
        output += numerator_accum
        numerator_accum = (numerator_accum * numerator) // (denominator * i)
        i += 1
    return output // denominator
Fee Getter

当合约的输入长度为零时,将其解释为对当前费用的 get request,即合约返回 get_fee() 的结果。

系统调用

在处理此 EIP 处于活动状态的任何执行区块的末尾(即,在处理所有交易并执行区块主体 consolidation request 验证之后),将合约作为 SYSTEM_ADDRESS 以无 calldata 调用 CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS。调用触发以下操作:

  • 合约的队列根据 consolidation request 出队进行更新,如果队列已清除,则 consolidation request 队列的头部/尾部将重置 (dequeue_consolidation_requests())
  • 合约的 excess consolidation request 根据当前区块中的使用情况进行更新 (update_excess_consolidation_requests())
  • 合约的 consolidation request 计数重置为 0 (reset_consolidation_requests_count())

每个 consolidation request 必须以 dequeue_consolidation_requests() 返回的完全相同的顺序出现在 EIP-7685 request 列表中。

此外,系统调用和该区块的处理必须符合以下条件:

  • 该调用具有 30_000_000 的专用 gas 限制。
  • 此调用消耗的 Gas 不计入区块的总体 gas 使用量。
  • 分配给该调用的 gas 限制和消耗的 gas 都将从对区块 gas 限制的任何检查中排除。
  • 该调用不遵循 EIP-1559 gas 燃烧语义 - 不应作为此调用的一部分传输任何 value。
  • 如果 CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS 上没有代码,则相应的区块必须标记为无效。
  • 如果对合约的调用失败或返回错误,则该区块必须无效。

系统调用触发的功能在伪代码中定义为函数 process_consolidation_requests()

###################
# Public function #
###################

def process_consolidation_requests():
    reqs = dequeue_consolidation_requests()
    update_excess_consolidation_requests()
    reset_consolidation_requests_count()
    return ssz.serialize(reqs)

###########
# Helpers #
###########

class ConsolidationRequest(object):
    source_address: Bytes20
    source_pubkey: Bytes48
    target_pubkey: Bytes48

def dequeue_consolidation_requests():
    queue_head_index = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT)
    queue_tail_index = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT)
    num_in_queue = queue_tail_index - queue_head_index
    num_dequeued = min(num_in_queue, MAX_CONSOLIDATION_REQUESTS_PER_BLOCK)

    reqs = []
    for i in range(num_dequeued):
        queue_storage_slot = CONSOLIDATION_REQUEST_QUEUE_STORAGE_OFFSET + (queue_head_index + i) * 4
        source_address = address(sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot)[0:20])
        source_pubkey = (
            sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 1)[0:32] + sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 2)[0:16]
        )
        target_pubkey = (
            sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 2)[16:32] + sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, queue_storage_slot + 3)[0:32]
        )
        req = ConsolidationRequest(
            source_address=Bytes20(source_address),
            source_pubkey=Bytes48(source_pubkey),
            target_pubkey=Bytes48(target_pubkey)
        )
        reqs.append(req)

    new_queue_head_index = queue_head_index + num_dequeued
    if new_queue_head_index == queue_tail_index:
        # Queue is empty, reset queue pointers
        # 队列为空,重置队列指针
        sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0)
        sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT, 0)
    else:
        sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT, new_queue_head_index)

    return reqs

def update_excess_consolidation_requests():
    previous_excess = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, EXCESS_CONSOLIDATION_REQUESTS_STORAGE_SLOT)
    # Check if excess needs to be reset to 0 for first iteration after activation
    # 检查在激活后的第一次迭代中是否需要将 excess 重置为 0
    if previous_excess == EXCESS_INHIBITOR:
        previous_excess = 0

    count = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT)

    new_excess = 0
    if previous_excess + count > TARGET_CONSOLIDATION_REQUESTS_PER_BLOCK:
        new_excess = previous_excess + count - TARGET_CONSOLIDATION_REQUESTS_PER_BLOCK

    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, EXCESS_CONSOLIDATION_REQUESTS_STORAGE_SLOT, new_excess)

def reset_consolidation_requests_count():
    sstore(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT, 0)
Bytecode
caller
push20 0xfffffffffffffffffffffffffffffffffffffffe
eq
push1 0xd3
jumpi

push1 0x11
push0
sload
dup1
push32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
eq
push2 0x019a
jumpi

push1 0x01
dup3
mul
push1 0x01
swap1
push0

jumpdest
push0
dup3
gt
iszero
push1 0x68
jumpi

dup2
add
swap1
dup4
mul
dup5
dup4
mul
swap1
div
swap2
push1 0x01
add
swap2
swap1
push1 0x4d
jump

jumpdest
swap1
swap4
swap1
div
swap3
pop
pop
pop
calldatasize
push1 0x60
eq
push1 0x88
jumpi

calldatasize
push2 0x019a
jumpi

callvalue
push2 0x019a
jumpi

push0
mstore
push1 0x20
push0
return

jumpdest
callvalue
lt
push2 0x019a
jumpi

push1 0x01
sload
push1 0x01
add
push1 0x01
sstore
push1 0x03
sload
dup1
push1 0x04
mul
push1 0x04
add
caller
dup2
sstore
push1 0x01
add
push0
calldataload
dup2
sstore
push1 0x01
add
push1 0x20
calldataload
dup2
sstore
push1 0x01
add
push1 0x40
calldataload
swap1
sstore
caller
push1 0x60
shl
push0
mstore
push1 0x60
push0
push1 0x14
calldatacopy
push1 0x74
push0
log0
push1 0x01
add
push1 0x03
sstore
stop

jumpdest
push1 0x03
sload
push1 0x02
sload
dup1
dup3
sub
dup1
push1 0x02
gt
push1 0xe7
jumpi

pop
push1 0x02

jumpdest
push0

jumpdest
dup2
dup2
eq
push2 0x0129
jumpi

dup3
dup2
add
push1 0x04
mul
push1 0x04
add
dup2
push1 0x74
mul
dup2
sload
push1 0x60
shl
dup2
mstore
push1 0x14
add
dup2
push1 0x01
add
sload
dup2
mstore
push1 0x20
add
dup2
push1 0x02
add
sload
dup2
mstore
push1 0x20
add
swap1
push1 0x03
add
sload
swap1
mstore
push1 0x01
add
push1 0xe9
jump

jumpdest
swap2
add
dup1
swap3
eq
push2 0x013b
jumpi

swap1
push1 0x02
sstore
push2 0x0146
jump

jumpdest
swap1
pop
push0
push1 0x02
sstore
push0
push1 0x03
sstore

jumpdest
push0
sload
dup1
push32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
eq
iszero
push2 0x0173
jumpi

pop
push0

jumpdest
push1 0x01
sload
push1 0x01
dup3
dup3
add
gt
push2 0x0188
jumpi

pop
pop
push0
push2 0x018e
jump

jumpdest
add
push1 0x01
swap1
sub

jumpdest
push0
sstore
push0
push1 0x01
sstore
push1 0x74
mul
push0
return

jumpdest
push0
push0
revert
部署

consolidation request 合约像任何其他智能合约一样部署。通过从所需的部署交易向后工作来生成一个特殊的合成地址:

{
  "type": "0x0",
  "nonce": "0x0",
  "to": null,
  "gas": "0x3d090",
  "gasPrice": "0xe8d4a51000",
  "maxPriorityFeePerGas": null,
  "maxFeePerGas": null,
  "value": "0x0",
  "input": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f5561019e80602d5f395ff33373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd,
  "v": "0x1b",
  "r": "0x539",
  "s": "0xc0730f92dc275b663d377a7cbb141b6600052",
  "hash": "0x379269d571beff3ed1d8eba3abb24076a7267b0eaf0cc66d728fb0544f5a690d"
}
Sender: 0x13d1913d623E6a9D8811736359E50fD31Fe54fCA
Address: 0x0000BBdDc7CE488642fb579F8B00f3a590007251

共识层

此 EIP 的定义特征是:

  1. 增加 MAX_EFFECTIVE_BALANCE,同时创建一个 MIN_ACTIVATION_BALANCE 允许可变大小验证器的核心功能。
  2. 允许通过协议合并多个验证器索引。 一种大型节点运营商无需循环通过退出和激活队列即可合并验证器的机制。
  3. 添加执行层部分提款(EIP-7002 的一部分)。 除了完全退出之外,还允许执行层消息触发部分提款(例如,100 ETH 验证器可以移除最多 68 ETH,而无需退出验证器)。
  4. 使初始 slashing 惩罚可以忽略不计。 这降低了大型验证器合并的风险。

Rationale 部分包含对每个提议的核心功能的解释。下面包括对共识层产生的更改的草图。

  1. 添加 COMPOUNDING_WITHDRAWAL_PREFIXMIN_ACTIVATION_BALANCE 常量,同时引入 MAX_EFFECTIVE_BALANCE 的更新 value (MAX_EFFECTIVE_BALANCE_ELECTRA)。
  2. 创建 PendingDeposit 容器,用于跟踪基于权重的速率限制机制中的传入存款。
  3. 使用存款和部分提款队列、存款所需的字段以及基于退出队列权重的速率限制来更新 BeaconState
  4. 修改 is_eligible_for_activation_queue 以检查 MIN_ACTIVATION_BALANCE 而不是 MAX_EFFECTIVE_BALANCE
  5. 修改 get_validator_churn_limit 以依赖于验证器权重而不是验证器数量。
  6. 创建一个辅助函数 compute_exit_epoch_and_update_churn,以基于当前未决提款计算退出 epoch。
  7. 修改 initiate_validator_exit 以按余额而不是验证器数量来限制退出队列的速率。
  8. 修改 initialize_beacon_state_from_eth1 以使用 MIN_ACTIVATION_BALANCE
  9. 修改 process_registry_updates 以激活所有符合条件的验证器。
  10. 添加一个按 epoch 辅助函数 process_pending_deposits,以消耗一些未决存款。
  11. 修改 get_validator_from_deposit 以将有效余额初始化为零(它由未决存款流程更新)。
  12. 修改 process_deposit 以将传入的存款存储在 state.pending_deposits 中。
  13. 修改 compute_weak_subjectivity_period 以使用新的流失限制函数。
  14. 添加 has_compounding_withdrawal_credential 以检查 0x02 凭据。
  15. 修改 is_fully_withdrawable_validator 以检查复利凭据。
  16. 添加 get_validator_excess_balance 以计算验证器的 excess balance。
  17. 修改 is_partially_withdrawable_validator 以检查 excess balance。
  18. 修改 get_expected_withdrawals 以使用 excess balance 并处理未决部分提款。
  19. 添加 process_consolidation 以启动协议内部验证器 consolidation 并将验证器切换到复利提款凭据。
  20. 将退出流失的使用限制为 256 ETH(相当于 8 个验证器,有效余额为 32 ETH),并将流失的剩余部分推迟到处理 consolidations。

完整的共识层规范可以在 ./electra/beacon-chain.md 中找到。

理由

此 EIP 旨在减少验证器的总数,而不改变协议的任何经济安全性。它提供了一种机制,通过该机制,控制大量 stake 的大型节点运营商可以整合到更少的验证器中。我们分析了每个核心功能背后的原因。

增加 MAX_EFFECTIVE_BALANCE,同时创建一个 MIN_ACTIVATION_BALANCE

在增加 MAX_EFFECTIVE_BALANCE 以允许更高 stake 的验证器的同时,重要的是通过引入一个新常量 – MIN_ACTIVATION_BALANCE) 来保持 32 ETH 的下限,以鼓励单人 stake。

允许通过协议合并多个验证器索引

对于已经控制数千个验证器的大型 staking pool,退出和重新进入将非常缓慢且成本高昂。通过允许协议内部 consolidation,EIP 的采用率将会更高。

添加执行层部分提款(EIP-7002 的一部分)

对于选择提高其有效余额上限的验证器,允许从执行层触发的自定义部分提款增加了 staking 配置的灵活性。验证器可以选择何时以及提取多少,但必须为 EL 交易支付 gas。

使初始 slashing 惩罚可以忽略不计

为了鼓励 consolidation,我们可以修改 slashing 惩罚。最大的打击来自验证器有效余额的 1/32 的初始惩罚。由于这在有效余额上呈线性缩放,因此更高 stake 的验证器会直接承担更高的风险。通过更改缩放属性,我们可以使 consolidation 更具吸引力。

Consolidation 合约参数 value

consolidation 智能合约使用一种费用机制来限制每个区块的 request 数量。有关费用机制的详细信息,请参见 EIP-7002

选择 TARGET_CONSOLIDATION_REQUESTS_PER_BLOCK1,以尽可能地限制 consolidation request 的速率。 选择 MAX_CONSOLIDATION_REQUESTS_PER_BLOCK2,以允许将验证器切换到复利凭据,并在同一区块中 request consolidation。

每个区块的 consolidation 数量仍然高于 consolidation 流失的大小,这可能导致 consolidation 队列的无限增长。 因此,consolidation 队列的大小有一个硬性限制,等于 262,144 个 request,即信标状态中的 4 MB 数据。

向后兼容性

此 EIP 向区块验证规则集引入了向后不兼容的更改,并且必须伴随硬分叉。这些更改不会破坏与当前用户活动和体验相关的任何内容。

安全注意事项

此更改修改了委员会和流失,但不会显着影响安全属性。

证明委员会的安全性

给定完全 consolidation 作为最坏情况,对抗方接管委员会的可能性仍然很低。即使在高 consolidation 场景中,所需的诚实验证器份额也远低于最终确定所需的 2/3 超级多数。

聚合器选择

在原始分片路线图中,需要以极高的概率来保证子委员会的安全性。现在,仅承担证明聚合的责任,我们只需要每个委员会都至少有一个诚实的聚合器。目前,聚合器是通过 VRF 彩票选择的,目标是几个可以被非 consolidated 攻击者偏置的验证器单元。此提议更改了 VRF 彩票以考虑权重,因此拥有至少一个诚实聚合器的概率不会更糟。

提议者选择概率

提议者选择已经通过其有效余额与 MAX_EFFECTIVE_BALANCE 的比率进行加权。由于概率较低,此更改将稍微增加计算下一个提议者索引所需的时间。

同步委员会选择概率

同步委员会选择也已经通过有效余额进行加权,因此该提议不需要修改同步协议。轻客户端仍然可以检查是否超多数的参与者已签署更新,而不管他们的权重如何,因为我们保持基于权重的选择概率。

流失不变量

此提议保持激活和退出流失不变量,限制活动权重而不是验证器数量。余额充值现在被显式处理,与完全存款一样受到相同的激活队列的限制。

Fee Overpayment

对系统合约的调用需要根据当前合约状态定义的费用支付。超额支付的费用不会退还给调用者。通常无法提前计算出所需的准确费用金额。从合约添加 consolidation request 时,合约可以执行读取操作以检查当前费用,然后支付完全需要的金额。以下是用 Solidity 编写的示例:

function addConsolidation(bytes memory srcPubkey, bytes memory targetPubkey, uint64 requestFeeLimit) private {
    assert(srcPubkey.length == 48);
    assert(targetPubkey.length == 48);

    // Read current fee from the contract.
    // 从合约读取当前费用。
    (bool readOK, bytes memory feeData) = ConsolidationsContract.staticcall('');
    if (!readOK) {
        revert('reading fee failed');
    }
    uint256 fee = uint256(bytes32(feeData));

    // Check the fee is not too high.
    // 检查费用是否过高。
    if (fee > requestFeeLimit) {
        revert('fee is too high');
    }

    // Add the request.
    // 添加 request。
    bytes memory callData = bytes.concat(srcPubkey, targetPubkey);
    (bool writeOK,) = ConsolidationsContract.call{value: fee}(callData);
    if (!writeOK) {
        revert('adding request failed');
    }
}

注意:系统合约使用 EVM 的 CALLER 操作(Solidity:msg.sender)来获取 consolidation request 中使用的地址,即调用系统合约的地址必须与信标状态中记录的 0x01 提款凭据匹配。

注意:如果费用过高,上述代码会恢复,费用可能会在 consolidation request 交易的创建与其包含到区块之间发生显着变化,因此,此检查对于避免 overpayment 非常重要。

使用 EOA request consolidations 总是会导致 overpayment 的费用。EOA 无法使用包装合约来 request consolidation。即使存在一种方法,退还 overage 的 gas 成本也可能高于 overage 本身。如果希望从 EOA 对系统合约进行 request consolidations,我们建议用户执行交易模拟以估计要发送的合理费用金额。

Consolidation 队列硬性限制

超过 consolidation 队列硬性限制(262,144 个 request)的 consolidations 将被共识层丢弃,需要重新提交,请注意,在这种情况下

Citation

Please cite this document as:

mike (@michaelneuder), Francesco (@fradamt), dapplion (@dapplion), Mikhail (@mkalinin), Aditya (@adiasg), Justin (@justindrake), lightclient (@lightclient), Felix Lange (@fjl), "EIP-7251: 增加 MAX_EFFECTIVE_BALANCE," Ethereum Improvement Proposals, no. 7251, June 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7251.