Alert Source Discuss
⚠️ Draft Standards Track: Core

EIP-7688: 向前兼容的共识数据结构

将共识 SSZ 数据结构转换为 StableContainer

Authors Etan Kissling (@etan-status), Cayman (@wemeetagain)
Created 2024-04-15
Discussion Link https://ethereum-magicians.org/t/eip-7688-forward-compatible-consensus-data-structures/19673
Requires EIP-6110, EIP-7002, EIP-7251, EIP-7495, EIP-7549, EIP-7569

摘要

本 EIP 定义了在共识数据结构中采用来自 EIP-7495StableContainer 所需的更改。

动机

以太坊的共识数据结构大量使用 Simple Serialize (SSZ) Container,它定义了它们的序列化和 Merkle 化方式。Merkle 化方案允许应用程序实现验证单个字段(和部分字段)是否已被篡改。例如,这对于希望验证参与验证者是否已被削减的去中心化质押池的智能合约非常有用。

虽然 SSZ Container 定义了数据结构的 Merkle 化方式,但 Merkle 化容易在不同的分叉中发生变化。当这种情况发生时,例如,因为添加了新功能或删除了旧功能,现有验证器实现需要更新才能继续处理证明。

EIP-7495StableContainer 是一种向前兼容的替代方案,可保证向前兼容的 Merkle 化方案。通过转换共识数据结构以使用 StableContainer,包含验证器逻辑的智能合约不再需要与以太坊的分叉时间表保持同步维护,只要它们验证的基础功能没有改变。例如,只要削减的概念使用布尔值 slashed 字段表示,当添加或删除不相关的功能时,现有验证器就不会中断。这对于链下验证器也是如此,例如,在硬件钱包中或在与以太坊不同的软件更新节奏的移动设备操作系统中。

规范

本文档中的关键词“必须”,“不得”,“必需”,“应”,“不应”,“应该”,“不应该”,“推荐”,“不推荐”,“可以”和“可选”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。

转换过程

对于每个转换的数据结构,都会引入一个新的与分叉无关的 StableContainer 类型 B,作为每个数据结构的主要定义。

  • 每个 StableContainer 都被分配一个容量来表示其潜在的设计空间,该容量在未来的分叉中不得更改;如果以后确定它不足,则可以添加一个新字段以在子容器中包含其他字段。
  • StableContainer 最初是最新分叉的 Container 等效项的副本,除了所有字段类型 T 都被包装到 Optional[T] 中。
  • 为了保证向前和向后兼容性,来自未来分叉的新字段必须仅附加到 StableContainer 定义。
  • 现有字段的类型不得更改,包括 List/Bitlist 的容量。如果需要更改,则不得再使用旧字段,并且应考虑使用新名称的新字段。在决定 StableContainer 本身和各种列表的容量时,重要的是要预测潜在的未来扩展。
  • 对于 List/Bitlist,应该利用该机会来重新评估其设计空间容量。如果设计空间增加,则应用程序逻辑应检查特定于分叉的长度限制;SSZ 库仅定义 Merkle 化限制,而不定义序列化限制。
  • 对于每个字段类型重复转换过程。StableContainer 引用的所有字段类型必须是 StableContainer 本身,否则被认为是不可变的。

随后,对于每个 StableContainer 基本类型 B,都会引入一个特定于分叉的 Profile[B] 类型,该类型与最新分叉的 Container 等效项匹配。不再需要旧的 ContainerProfile 的 SSZ 序列化与 Container 兼容,但 Merkle 化和 hash_tree_root 的计算方式不同。此外,如果需要,Profile 可以使用 Optional 类型的字段。

后续分叉指定一个新的 Profile

  • 如果添加了类型为 T 的新字段,则将它们作为 Optional[T] 附加到 StableContainer,以在稳定的 Merkle 化方案中注册它们。在新分叉的 Profile 中,新字段可以是 T(必需)或 Optional[T](可选)。
  • 如果旧字段已弃用,则将它们保留在 StableContainer 中以保留稳定的 Merkle 化方案。在新分叉的 Profile 中,该字段从定义中省略。SSZ 库保证 hash_tree_root 和所有广义索引保持不变。
  • 其他字段可以在新分叉的 Profile 中在 T(必需)和 Optional[T](可选)之间更改。对于此类更改,不需要对 StableContainer 进行任何更改。

不可变类型

这些类型用作 StableContainer 定义的一部分,并且,由于它们本身不是 StableContainer,因此被认为具有不可变的 Merkle 化。如果未来的分叉需要以不兼容的方式更改这些类型,则应定义一个新类型并分配一个新字段名称。

类型 描述
Slot 信标链上的槽位号
Epoch 信标链上的纪元号,一组槽位
CommitteeIndex 一个槽位内委员会的索引
ValidatorIndex 信标链验证器的唯一索引
Gwei Gwei 金额 (1 ETH = 10^9 Gwei = 10^18 Wei)
Root 包含 SSZ Merkle 根的字节向量
Hash32 包含不透明 32 字节哈希的字节向量
Version 共识分叉版本号
BLSPubkey 表示 BLS12-381 公钥的密码类型
BLSSignature 表示 BLS12-381 签名的密码类型
KZGCommitment KZG 多项式承诺方案的 G1 曲线点
Fork 共识分叉信息
Checkpoint 引用截至一个纪元开始槽位的最新信标区块的元组
Validator 有关信标链验证器的信息
AttestationData 证明特定共识区块的可用性和有效性的投票
Eth1Data 用于从交易日志导入存款的目标跟踪器
DepositData 将存款存入信标链时,作为交易收据一部分发出的日志数据
BeaconBlockHeader 共识区块头
ProposerSlashing 两个等价的共识区块头的元组
Deposit 存款数据及其包含证明的元组
VoluntaryExit 从信标链退出验证器的共识发起请求
SignedVoluntaryExit 自愿退出请求及其签名的元组
SyncAggregate 表示聚合同步委员会签名的密码类型
ExecutionAddress 包含执行层上帐户地址的字节向量
Transaction 包含 RLP 编码交易的字节列表
WithdrawalIndex 从任何验证器的余额到执行层的提款的唯一索引
Withdrawal 从信标链验证器的余额到执行层的提款
DepositRequest 展平的存款数据及其顺序索引的元组
WithdrawalRequest 执行层发起的从验证器提取到执行层的请求
ConsolidationRequest 执行层发起的合并两个信标链验证器的请求
BLSToExecutionChange 注册信标链验证器的提款帐户地址的请求
SignedBLSToExecutionChange 提款帐户地址注册请求及其签名的元组
ParticipationFlags 一个纪元内信标链验证器的参与跟踪器
HistoricalSummary 结合历史区块根和历史状态根的元组
PendingDeposit 用于存入信标链验证器的挂起操作
PendingPartialWithdrawal 用于从信标链验证器提取的挂起操作
PendingConsolidation 用于合并两个信标链验证器的挂起操作

StableContainer 容量

名字 描述
MAX_ATTESTATION_FIELDS uint64(2**3) (= 8) StableAttestation 未来可能增长的最大字段数
MAX_INDEXED_ATTESTATION_FIELDS uint64(2**3) (= 8) StableIndexedAttestation 未来可能增长的最大字段数
MAX_EXECUTION_PAYLOAD_FIELDS uint64(2**6) (= 64) StableExecutionPayload 未来可能增长的最大字段数
MAX_EXECUTION_REQUESTS_FIELDS uint64(2**4) (= 16) StableExecutionRequests 未来可能增长的最大字段数
MAX_BEACON_BLOCK_BODY_FIELDS uint64(2**6) (= 64) StableBeaconBlockBody 未来可能增长的最大字段数
MAX_BEACON_STATE_FIELDS uint64(2**7) (= 128) StableBeaconState 未来可能增长的最大字段数

最大证明深度:

  • StableBeaconState > validators (1 + 7) > <item> (1 + 40) > pubkey (3) > <chunk> (1) = 53 位
  • StableBeaconBlockBody > execution_payload (1 + 6) > transactions (1 + 6) > <item> (1 + 20) > <chunk> (1 + 25) = 61 位

与分叉无关的 StableContainer 定义

这些类型定义与分叉无关,并在所有分叉之间共享。它们不会通过 libp2p 交换。

class StableAttestation(StableContainer[MAX_ATTESTATION_FIELDS]):
    aggregation_bits: Optional[Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]]
    data: Optional[AttestationData]
    signature: Optional[BLSSignature]
    committee_bits: Optional[Bitvector[MAX_COMMITTEES_PER_SLOT]]

class StableIndexedAttestation(StableContainer[MAX_INDEXED_ATTESTATION_FIELDS]):
    attesting_indices: Optional[List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]]
    data: Optional[AttestationData]
    signature: Optional[BLSSignature]

class StableAttesterSlashing(Container):
    attestation_1: StableIndexedAttestation
    attestation_2: StableIndexedAttestation

class StableExecutionPayload(StableContainer[MAX_EXECUTION_PAYLOAD_FIELDS]):
    parent_hash: Optional[Hash32]
    fee_recipient: Optional[ExecutionAddress]  # 'beneficiary' in the yellow paper
    state_root: Optional[Bytes32]
    receipts_root: Optional[Bytes32]
    logs_bloom: Optional[ByteVector[BYTES_PER_LOGS_BLOOM]]
    prev_randao: Optional[Bytes32]  # 'difficulty' in the yellow paper
    block_number: Optional[uint64]  # 'number' in the yellow paper
    gas_limit: Optional[uint64]
    gas_used: Optional[uint64]
    timestamp: Optional[uint64]
    extra_data: Optional[ByteList[MAX_EXTRA_DATA_BYTES]]
    base_fee_per_gas: Optional[uint256]
    block_hash: Optional[Hash32]  # Hash of execution block
    transactions: Optional[List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]]
    withdrawals: Optional[List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]]  # [New in Capella]
    blob_gas_used: Optional[uint64]  # [New in Deneb:EIP4844]
    excess_blob_gas: Optional[uint64]  # [New in Deneb:EIP4844]

class StableExecutionPayloadHeader(StableContainer[MAX_EXECUTION_PAYLOAD_FIELDS]):
    parent_hash: Optional[Hash32]
    fee_recipient: Optional[ExecutionAddress]
    state_root: Optional[Bytes32]
    receipts_root: Optional[Bytes32]
    logs_bloom: Optional[ByteVector[BYTES_PER_LOGS_BLOOM]]
    prev_randao: Optional[Bytes32]
    block_number: Optional[uint64]
    gas_limit: Optional[uint64]
    gas_used: Optional[uint64]
    timestamp: Optional[uint64]
    extra_data: Optional[ByteList[MAX_EXTRA_DATA_BYTES]]
    base_fee_per_gas: Optional[uint256]
    block_hash: Optional[Hash32]  # Hash of execution block
    transactions_root: Optional[Root]
    withdrawals_root: Optional[Root]  # [New in Capella]
    blob_gas_used: Optional[uint64]  # [New in Deneb:EIP4844]
    excess_blob_gas: Optional[uint64]  # [New in Deneb:EIP4844]

class StableExecutionRequests(StableContainer[MAX_EXECUTION_REQUESTS_FIELDS]):
    deposits: Optional[List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD]]  # [New in Electra:EIP6110]
    withdrawals: Optional[List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]]  # [New in Electra:EIP7002:EIP7251]
    consolidations: Optional[List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD]]  # [New in Electra:EIP7251]

class StableBeaconBlockBody(StableContainer[MAX_BEACON_BLOCK_BODY_FIELDS]):
    randao_reveal: Optional[BLSSignature]
    eth1_data: Optional[Eth1Data]  # Eth1 data vote
    graffiti: Optional[Bytes32]  # Arbitrary data
    proposer_slashings: Optional[List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]]
    attester_slashings: Optional[List[StableAttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA]]  # [Modified in Electra:EIP7549]
    attestations: Optional[List[StableAttestation, MAX_ATTESTATIONS_ELECTRA]]  # [Modified in Electra:EIP7549]
    deposits: Optional[List[Deposit, MAX_DEPOSITS]]
    voluntary_exits: Optional[List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]]
    sync_aggregate: Optional[SyncAggregate]  # [New in Altair]
    execution_payload: Optional[StableExecutionPayload]  # [New in Bellatrix]
    bls_to_execution_changes: Optional[List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]]  # [New in Capella]
    blob_kzg_commitments: Optional[List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]]  # [New in Deneb:EIP4844]
    execution_requests: Optional[StableExecutionRequests]  # [New in Electra]

class StableBeaconState(StableContainer[MAX_BEACON_STATE_FIELDS]):
    # Versioning
    genesis_time: Optional[uint64]
    genesis_validators_root: Optional[Root]
    slot: Optional[Slot]
    fork: Optional[Fork]
    # History
    latest_block_header: Optional[BeaconBlockHeader]
    block_roots: Optional[Vector[Root, SLOTS_PER_HISTORICAL_ROOT]]
    state_roots: Optional[Vector[Root, SLOTS_PER_HISTORICAL_ROOT]]
    historical_roots: Optional[List[Root, HISTORICAL_ROOTS_LIMIT]]  # Frozen in Capella, replaced by historical_summaries
    # Eth1
    eth1_data: Optional[Eth1Data]
    eth1_data_votes: Optional[List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]]
    eth1_deposit_index: Optional[uint64]
    # Registry
    validators: Optional[List[Validator, VALIDATOR_REGISTRY_LIMIT]]
    balances: Optional[List[Gwei, VALIDATOR_REGISTRY_LIMIT]]
    # Randomness
    randao_mixes: Optional[Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]]
    # Slashings
    slashings: Optional[Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]]  # Per-epoch sums of slashed effective balances
    # Participation
    previous_epoch_participation: Optional[List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]]  # [Modified in Altair]
    current_epoch_participation: Optional[List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]]  # [Modified in Altair]
    # Finality
    justification_bits: Optional[Bitvector[JUSTIFICATION_BITS_LENGTH]]  # Bit set for every recent justified epoch
    previous_justified_checkpoint: Optional[Checkpoint]
    current_justified_checkpoint: Optional[Checkpoint]
    finalized_checkpoint: Optional[Checkpoint]
    # Inactivity
    inactivity_scores: Optional[List[uint64, VALIDATOR_REGISTRY_LIMIT]]  # [New in Altair]
    # Sync
    current_sync_committee: Optional[SyncCommittee]  # [New in Altair]
    next_sync_committee: Optional[SyncCommittee]  # [New in Altair]
    # Execution
    latest_execution_payload_header: Optional[StableExecutionPayloadHeader]  # [New in Bellatrix]
    # Withdrawals
    next_withdrawal_index: Optional[WithdrawalIndex]  # [New in Capella]
    next_withdrawal_validator_index: Optional[ValidatorIndex]  # [New in Capella]
    # Deep history valid from Capella onwards
    historical_summaries: Optional[List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]]  # [New in Capella]
    deposit_requests_start_index: Optional[uint64]  # [New in Electra:EIP6110]
    deposit_balance_to_consume: Optional[Gwei]  # [New in Electra:EIP7251]
    exit_balance_to_consume: Optional[Gwei]  # [New in Electra:EIP7251]
    earliest_exit_epoch: Optional[Epoch]  # [New in Electra:EIP7251]
    consolidation_balance_to_consume: Optional[Gwei]  # [New in Electra:EIP7251]
    earliest_consolidation_epoch: Optional[Epoch]  # [New in Electra:EIP7251]
    pending_deposits: Optional[List[PendingDeposit, PENDING_DEPOSITS_LIMIT]]  # [New in Electra:EIP7251]
    # [New in Electra:EIP7251]
    pending_partial_withdrawals: Optional[List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]]
    pending_consolidations: Optional[List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]]  # [New in Electra:EIP7251]

特定于分叉的 Profile 定义

引入此 EIP 的分叉特定的共识类型定义已更新为继承 StableContainer 定义的 Merkle 化。字段保持不变。

class Attestation(Profile[StableAttestation]):
    ...

class IndexedAttestation(Profile[StableIndexedAttestation]):
    ...

class ExecutionPayload(Profile[StableExecutionPayload]):
    ...

class ExecutionPayloadHeader(Profile[StableExecutionPayloadHeader]):
    ...

class ExecutionRequests(Profile[StableExecutionRequests]):
    ...

class BeaconBlockBody(Profile[StableBeaconBlockBody]):
    ...

class BeaconState(Profile[StableBeaconState]):
    ...

理由

最佳时机?

应用此 EIP 会中断 hash_tree_root 和 Merkle 树验证器一次,同时承诺从该分叉开始向前兼容。最好在 Merkle 化被不同的更改破坏之前应用它。当 Container 在其字段数中达到 2 的新幂时,Merkle 化会被破坏。

这可以追溯应用吗?

虽然 Profile 的序列化方式与旧版 Container 相同,但受影响的数据结构的 Merkle 化和 hash_tree_root 会发生变化。因此,希望处理旧版变体的 Merkle 证明的验证器仍然需要支持相应的旧版方案。

不可变性

一旦 StableContainer 中的字段被发布,它的名称就不能再用于表示未来不同的类型。这包括容量高于最初预期的列表类型。这与某些案例的历史管理相符:

  • Phase0: BeaconState 包含 previous_epoch_attestations / current_epoch_attestations
  • Altair: BeaconState 将这些字段替换为 previous_epoch_participation / current_epoch_participation

此外,新字段必须附加在 StableContainer 的末尾。这与其他案例的历史管理相符:

  • Capella 将 historical_summaries 附加到 BeaconState,而不是将新字段挤压在 historical_roots 旁边

使用 StableContainer,稳定的 Merkle 化要求这些规则变得严格。

向后兼容性

现有的 Merkle 证明验证器需要更新以支持新的 Merkle 树形状。这包括不同区块链上的智能合约中的验证器和硬件钱包中的验证器(如果适用)。

请注意,当转换后的 Container 数据结构之一在其字段数中达到 2 的新幂时,向后兼容性也会被破坏。

安全考虑

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Etan Kissling (@etan-status), Cayman (@wemeetagain), "EIP-7688: 向前兼容的共识数据结构 [DRAFT]," Ethereum Improvement Proposals, no. 7688, April 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7688.