Merge EIP7002 into Electra

This commit is contained in:
Hsiao-Wei Wang 2024-03-06 14:23:23 +08:00
parent 82271d1229
commit ec9bbfa2ea
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
22 changed files with 72 additions and 500 deletions

View File

@ -168,19 +168,6 @@ jobs:
command: make citest fork=electra
- store_test_results:
path: tests/core/pyspec/test-reports
test-eip7002:
docker:
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Run py-tests
command: make citest fork=eip7002
- store_test_results:
path: tests/core/pyspec/test-reports
test-whisk:
docker:
- image: circleci/python:3.9
@ -330,9 +317,6 @@ workflows:
- test-electra:
requires:
- install_pyspec_test
- test-eip7002:
requires:
- install_pyspec_test
- test-whisk:
requires:
- install_pyspec_test

View File

@ -71,7 +71,7 @@ jobs:
needs: [preclear,lint,codespell,table_of_contents]
strategy:
matrix:
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "eip7002", "whisk", "eip7594"]
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "whisk", "eip7594"]
steps:
- name: Checkout this repo
uses: actions/checkout@v3.2.0

1
.gitignore vendored
View File

@ -22,7 +22,6 @@ tests/core/pyspec/eth2spec/bellatrix/
tests/core/pyspec/eth2spec/capella/
tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/electra/
tests/core/pyspec/eth2spec/eip7002/
tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip7594/

View File

@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \
$(wildcard $(SPEC_DIR)/_features/*/*/*.md) \
$(wildcard $(SSZ_DIR)/*.md)
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra eip7002 whisk
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk
# The parameters for commands. Use `foreach` to avoid listing specs again.
COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S)

View File

@ -53,9 +53,6 @@ DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC
# Electra
ELECTRA_FORK_VERSION: 0x05000000 # temporary stub
ELECTRA_FORK_EPOCH: 18446744073709551615
# EIP7002
EIP7002_FORK_VERSION: 0x05000000 # temporary stub
EIP7002_FORK_EPOCH: 18446744073709551615
# WHISK
WHISK_FORK_VERSION: 0x06000000 # temporary stub
WHISK_FORK_EPOCH: 18446744073709551615

View File

@ -52,9 +52,6 @@ DENEB_FORK_EPOCH: 18446744073709551615
# ELECTRA
ELECTRA_FORK_VERSION: 0x05000001
ELECTRA_FORK_EPOCH: 18446744073709551615
# EIP7002
EIP7002_FORK_VERSION: 0x05000001
EIP7002_FORK_EPOCH: 18446744073709551615
# WHISK
WHISK_FORK_VERSION: 0x06000001
WHISK_FORK_EPOCH: 18446744073709551615

View File

@ -4,3 +4,5 @@
# ---------------------------------------------------------------
# 2**13 (= 8192) receipts
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192
# 2**4 (= 16) exits
MAX_EXECUTION_LAYER_EXITS: 16

View File

@ -4,3 +4,5 @@
# ---------------------------------------------------------------
# [customized]
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4
# 2**4 (= 16) exits
MAX_EXECUTION_LAYER_EXITS: 16

View File

@ -5,7 +5,6 @@ BELLATRIX = 'bellatrix'
CAPELLA = 'capella'
DENEB = 'deneb'
ELECTRA = 'electra'
EIP7002 = 'eip7002'
WHISK = 'whisk'
EIP7594 = 'eip7594'

View File

@ -8,7 +8,6 @@ from .constants import (
DENEB,
ELECTRA,
WHISK,
EIP7002,
EIP7594,
)
@ -21,7 +20,6 @@ PREVIOUS_FORK_OF = {
DENEB: CAPELLA,
ELECTRA: DENEB,
WHISK: CAPELLA,
EIP7002: CAPELLA,
EIP7594: DENEB,
}

View File

@ -4,7 +4,6 @@ from .bellatrix import BellatrixSpecBuilder
from .capella import CapellaSpecBuilder
from .deneb import DenebSpecBuilder
from .electra import ElectraSpecBuilder
from .eip7002 import EIP7002SpecBuilder
from .whisk import WhiskSpecBuilder
from .eip7594 import EIP7594SpecBuilder
@ -13,6 +12,6 @@ spec_builders = {
builder.fork: builder
for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
ElectraSpecBuilder, EIP7002SpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder,
ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder,
)
}

View File

@ -1,300 +0,0 @@
# EIP-7002 -- The Beacon Chain
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Preset](#preset)
- [Max operations per block](#max-operations-per-block)
- [Containers](#containers)
- [New containers](#new-containers)
- [`ExecutionLayerExit`](#executionlayerexit)
- [Extended Containers](#extended-containers)
- [`ExecutionPayload`](#executionpayload)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [`BeaconState`](#beaconstate)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Block processing](#block-processing)
- [Execution payload](#execution-payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Operations](#operations)
- [Modified `process_operations`](#modified-process_operations)
- [New `process_execution_layer_exit`](#new-process_execution_layer_exit)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This is the beacon chain specification of the execution layer triggerable exits feature.
This mechanism relies on the changes proposed by [EIP-7002](http://eips.ethereum.org/EIPS/eip-7002).
*Note:* This specification is built upon [Capella](../../capella/beacon-chain.md) and is under active development.
## Preset
### Max operations per block
| Name | Value |
| - | - |
| `MAX_EXECUTION_LAYER_EXITS` | `2**4` (= 16) |
## Containers
### New containers
#### `ExecutionLayerExit`
```python
class ExecutionLayerExit(Container):
source_address: ExecutionAddress
validator_pubkey: BLSPubkey
```
### Extended Containers
#### `ExecutionPayload`
```python
class ExecutionPayload(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
exits: List[ExecutionLayerExit, MAX_EXECUTION_LAYER_EXITS] # [New in EIP7002]
```
#### `ExecutionPayloadHeader`
```python
class ExecutionPayloadHeader(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32
transactions_root: Root
withdrawals_root: Root
exits_root: Root # [New in EIP7002]
```
#### `BeaconState`
```python
class BeaconState(Container):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
# History
latest_block_header: BeaconBlockHeader
block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT]
# Eth1
eth1_data: Eth1Data
eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]
eth1_deposit_index: uint64
# Registry
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
# Slashings
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
# Finality
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
previous_justified_checkpoint: Checkpoint
current_justified_checkpoint: Checkpoint
finalized_checkpoint: Checkpoint
# Inactivity
inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT]
# Sync
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in EIP7002]
# Withdrawals
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
```
## Beacon chain state transition function
### Block processing
#### Execution payload
##### Modified `process_execution_payload`
```python
def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None:
payload = body.execution_payload
# Verify consistency of the parent hash with respect to the previous execution payload header
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
# Verify prev_randao
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
# Verify timestamp
assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
# Verify the execution payload is valid
assert execution_engine.verify_and_notify_new_payload(NewPayloadRequest(execution_payload=payload))
# Cache execution payload header
state.latest_execution_payload_header = ExecutionPayloadHeader(
parent_hash=payload.parent_hash,
fee_recipient=payload.fee_recipient,
state_root=payload.state_root,
receipts_root=payload.receipts_root,
logs_bloom=payload.logs_bloom,
prev_randao=payload.prev_randao,
block_number=payload.block_number,
gas_limit=payload.gas_limit,
gas_used=payload.gas_used,
timestamp=payload.timestamp,
extra_data=payload.extra_data,
base_fee_per_gas=payload.base_fee_per_gas,
block_hash=payload.block_hash,
transactions_root=hash_tree_root(payload.transactions),
withdrawals_root=hash_tree_root(payload.withdrawals),
exits_root=hash_tree_root(payload.exits), # [New in EIP7002]
)
```
#### Operations
##### Modified `process_operations`
*Note*: The function `process_operations` is modified to process `ExecutionLayerExit` operations included in the block.
```python
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# Verify that outstanding deposits are processed up to the maximum number of deposits
assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
for operation in operations:
fn(state, operation)
for_ops(body.proposer_slashings, process_proposer_slashing)
for_ops(body.attester_slashings, process_attester_slashing)
for_ops(body.attestations, process_attestation)
for_ops(body.deposits, process_deposit)
for_ops(body.voluntary_exits, process_voluntary_exit)
for_ops(body.execution_payload.exits, process_execution_layer_exit) # [New in EIP7002]
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
```
##### New `process_execution_layer_exit`
```python
def process_execution_layer_exit(state: BeaconState, execution_layer_exit: ExecutionLayerExit) -> None:
validator_pubkeys = [v.pubkey for v in state.validators]
validator_index = ValidatorIndex(validator_pubkeys.index(execution_layer_exit.validator_pubkey))
validator = state.validators[validator_index]
# Verify withdrawal credentials
is_execution_address = validator.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX
is_correct_source_address = validator.withdrawal_credentials[12:] == execution_layer_exit.source_address
if not (is_execution_address and is_correct_source_address):
return
# Verify the validator is active
if not is_active_validator(validator, get_current_epoch(state)):
return
# Verify exit has not been initiated
if validator.exit_epoch != FAR_FUTURE_EPOCH:
return
# Verify the validator has been active long enough
if get_current_epoch(state) < validator.activation_epoch + SHARD_COMMITTEE_PERIOD:
return
# Initiate exit
initiate_validator_exit(state, validator_index)
```
## Testing
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-7002 testing only.
Modifications include:
1. Use `EIP7002_FORK_VERSION` as the previous and current fork version.
2. Utilize the EIP-7002 `BeaconBlockBody` when constructing the initial `latest_block_header`.
```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
eth1_timestamp: uint64,
deposits: Sequence[Deposit],
execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader()
) -> BeaconState:
fork = Fork(
previous_version=EIP7002_FORK_VERSION, # [Modified in EIP7002] for testing only
current_version=EIP7002_FORK_VERSION, # [Modified in EIP7002]
epoch=GENESIS_EPOCH,
)
state = BeaconState(
genesis_time=eth1_timestamp + GENESIS_DELAY,
fork=fork,
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
)
# Process deposits
leaves = list(map(lambda deposit: deposit.data, deposits))
for index, deposit in enumerate(deposits):
deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
process_deposit(state, deposit)
# Process activations
for index, validator in enumerate(state.validators):
balance = state.balances[index]
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH
# Set genesis validators root for domain separation and chain versioning
state.genesis_validators_root = hash_tree_root(state.validators)
# Fill in sync committees
# Note: A duplicate committee is assigned for the current and next committee at genesis
state.current_sync_committee = get_next_sync_committee(state)
state.next_sync_committee = get_next_sync_committee(state)
# Initialize the execution payload header
state.latest_execution_payload_header = execution_payload_header
return state
```

View File

@ -1,140 +0,0 @@
# EIP-7002 -- Fork Logic
**Notice**: This document is a work-in-progress for researchers and implementers.
## Table of contents
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Configuration](#configuration)
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [Modified `compute_fork_version`](#modified-compute_fork_version)
- [Fork to EIP-7002](#fork-to-eip-7002)
- [Fork trigger](#fork-trigger)
- [Upgrading the state](#upgrading-the-state)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Introduction
This document describes the process of EIP-7002 upgrade.
## Configuration
Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `EIP7002_FORK_VERSION` | `Version('0x05000000')` |
| `EIP7002_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
## Helper functions
### Misc
#### Modified `compute_fork_version`
```python
def compute_fork_version(epoch: Epoch) -> Version:
"""
Return the fork version at the given ``epoch``.
"""
if epoch >= EIP7002_FORK_EPOCH:
return EIP7002_FORK_VERSION
if epoch >= CAPELLA_FORK_EPOCH:
return CAPELLA_FORK_VERSION
if epoch >= BELLATRIX_FORK_EPOCH:
return BELLATRIX_FORK_VERSION
if epoch >= ALTAIR_FORK_EPOCH:
return ALTAIR_FORK_VERSION
return GENESIS_FORK_VERSION
```
## Fork to EIP-7002
### Fork trigger
TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade.
For now, we assume the condition will be triggered at epoch `EIP7002_FORK_EPOCH`.
Note that for the pure EIP-7002 networks, we don't apply `upgrade_to_eip7002` since it starts with EIP-7002 version logic.
### Upgrading the state
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7002_FORK_EPOCH`,
an irregular state change is made to upgrade to EIP-7002.
```python
def upgrade_to_eip7002(pre: capella.BeaconState) -> BeaconState:
epoch = capella.get_current_epoch(pre)
latest_execution_payload_header = ExecutionPayloadHeader(
parent_hash=pre.latest_execution_payload_header.parent_hash,
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
state_root=pre.latest_execution_payload_header.state_root,
receipts_root=pre.latest_execution_payload_header.receipts_root,
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
prev_randao=pre.latest_execution_payload_header.prev_randao,
block_number=pre.latest_execution_payload_header.block_number,
gas_limit=pre.latest_execution_payload_header.gas_limit,
gas_used=pre.latest_execution_payload_header.gas_used,
timestamp=pre.latest_execution_payload_header.timestamp,
extra_data=pre.latest_execution_payload_header.extra_data,
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
block_hash=pre.latest_execution_payload_header.block_hash,
transactions_root=pre.latest_execution_payload_header.transactions_root,
withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
exits_root=Root(), # [New in EIP-7002]
)
post = BeaconState(
# Versioning
genesis_time=pre.genesis_time,
genesis_validators_root=pre.genesis_validators_root,
slot=pre.slot,
fork=Fork(
previous_version=pre.fork.current_version,
current_version=EIP7002_FORK_VERSION, # [Modified in EIP-7002]
epoch=epoch,
),
# History
latest_block_header=pre.latest_block_header,
block_roots=pre.block_roots,
state_roots=pre.state_roots,
historical_roots=pre.historical_roots,
# Eth1
eth1_data=pre.eth1_data,
eth1_data_votes=pre.eth1_data_votes,
eth1_deposit_index=pre.eth1_deposit_index,
# Registry
validators=pre.validators,
balances=pre.balances,
# Randomness
randao_mixes=pre.randao_mixes,
# Slashings
slashings=pre.slashings,
# Participation
previous_epoch_participation=pre.previous_epoch_participation,
current_epoch_participation=pre.current_epoch_participation,
# Finality
justification_bits=pre.justification_bits,
previous_justified_checkpoint=pre.previous_justified_checkpoint,
current_justified_checkpoint=pre.current_justified_checkpoint,
finalized_checkpoint=pre.finalized_checkpoint,
# Inactivity
inactivity_scores=pre.inactivity_scores,
# Sync
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
# Execution-layer
latest_execution_payload_header=latest_execution_payload_header, # [Modified in EIP-7002]
# Withdrawals
next_withdrawal_index=pre.next_withdrawal_index,
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
)
return post
```

View File

@ -16,6 +16,7 @@
- [Containers](#containers)
- [New containers](#new-containers)
- [`DepositReceipt`](#depositreceipt)
- [`ExecutionLayerExit`](#executionlayerexit)
- [Extended Containers](#extended-containers)
- [`ExecutionPayload`](#executionpayload)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
@ -24,6 +25,7 @@
- [Block processing](#block-processing)
- [Modified `process_operations`](#modified-process_operations)
- [New `process_deposit_receipt`](#new-process_deposit_receipt)
- [New `process_execution_layer_exit`](#new-process_execution_layer_exit)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Testing](#testing)
@ -34,6 +36,7 @@
Electra is a consensus-layer upgrade containing a number of features. Including:
* [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110): Supply validator deposits on chain
* [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002): Execution layer triggerable exits
## Constants
@ -52,6 +55,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Description |
| - | - | - |
| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload |
| `MAX_EXECUTION_LAYER_EXITS` | `2**4` (= 16) | *[New in Electra:EIP7002]* |
## Containers
@ -70,6 +74,16 @@ class DepositReceipt(Container):
index: uint64
```
#### `ExecutionLayerExit`
*Note*: The container is new in EIP7002.
```python
class ExecutionLayerExit(Container):
source_address: ExecutionAddress
validator_pubkey: BLSPubkey
```
### Extended Containers
#### `ExecutionPayload`
@ -96,6 +110,7 @@ class ExecutionPayload(Container):
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in Electra:EIP6110]
exits: List[ExecutionLayerExit, MAX_EXECUTION_LAYER_EXITS] # [New in Electra:EIP7002]
```
#### `ExecutionPayloadHeader`
@ -122,6 +137,7 @@ class ExecutionPayloadHeader(Container):
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_receipts_root: Root # [New in Electra:EIP6110]
exits_root: Root # [New in Electra:EIP7002]
```
#### `BeaconState`
@ -163,7 +179,7 @@ class BeaconState(Container):
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in Electra:EIP6110]
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in Electra:EIP6110:EIP7002]
# Withdrawals
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
@ -190,7 +206,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
#### Modified `process_operations`
*Note*: The function `process_operations` is modified to process `DepositReceipt` operations included in the payload.
*Note*: The function `process_operations` is modified to process `DepositReceipt` and `ExecutionLayerExit` operations included in the payload.
```python
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
@ -211,6 +227,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.attestations, process_attestation)
for_ops(body.deposits, process_deposit)
for_ops(body.voluntary_exits, process_voluntary_exit)
for_ops(body.execution_payload.exits, process_execution_layer_exit) # [New in Electra:EIP7002]
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
# [New in EIP6110]
@ -236,6 +253,35 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt)
)
```
#### New `process_execution_layer_exit`
*Note*: This function is new in Electra:EIP7002.
```python
def process_execution_layer_exit(state: BeaconState, execution_layer_exit: ExecutionLayerExit) -> None:
validator_pubkeys = [v.pubkey for v in state.validators]
validator_index = ValidatorIndex(validator_pubkeys.index(execution_layer_exit.validator_pubkey))
validator = state.validators[validator_index]
# Verify withdrawal credentials
is_execution_address = validator.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX
is_correct_source_address = validator.withdrawal_credentials[12:] == execution_layer_exit.source_address
if not (is_execution_address and is_correct_source_address):
return
# Verify the validator is active
if not is_active_validator(validator, get_current_epoch(state)):
return
# Verify exit has not been initiated
if validator.exit_epoch != FAR_FUTURE_EPOCH:
return
# Verify the validator has been active long enough
if get_current_epoch(state) < validator.activation_epoch + SHARD_COMMITTEE_PERIOD:
return
# Initiate exit
initiate_validator_exit(state, validator_index)
```
#### Modified `process_execution_payload`
*Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type.
@ -281,6 +327,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
blob_gas_used=payload.blob_gas_used,
excess_blob_gas=payload.excess_blob_gas,
deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in Electra:EIP6110]
exits_root=hash_tree_root(payload.exits), # [New in Electra:EIP7002]
)
```

View File

@ -91,6 +91,7 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
blob_gas_used=uint64(0),
excess_blob_gas=uint64(0),
deposit_receipts_root=Root(), # [New in Electra:EIP6110]
exits_root=Root(), # [New in Electra:EIP-7002],
)
post = BeaconState(
# Versioning
@ -132,7 +133,7 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
# Execution-layer
latest_execution_payload_header=latest_execution_payload_header, # [Modified in Electra:EIP6110]
latest_execution_payload_header=latest_execution_payload_header, # [Modified in Electra:EIP6110:EIP7002]
# Withdrawals
next_withdrawal_index=pre.next_withdrawal_index,
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,

View File

@ -8,7 +8,7 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA,
EIP7002, EIP7594,
EIP7594,
WHISK,
MINIMAL,
ALL_PHASES,
@ -508,7 +508,6 @@ with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
with_capella_and_later = with_all_phases_from(CAPELLA)
with_deneb_and_later = with_all_phases_from(DENEB)
with_electra_and_later = with_all_phases_from(ELECTRA)
with_eip7002_and_later = with_all_phases_from(EIP7002)
with_whisk_and_later = with_all_phases_from(WHISK, all_phases=ALLOWED_TEST_RUNNER_FORKS)
with_eip7594_and_later = with_all_phases_from(EIP7594, all_phases=ALLOWED_TEST_RUNNER_FORKS)

View File

@ -1,9 +1,9 @@
from eth2spec.test.context import spec_state_test, with_eip7002_and_later
from eth2spec.test.context import spec_state_test, with_electra_and_later
from eth2spec.test.helpers.execution_layer_exits import run_execution_layer_exit_processing
from eth2spec.test.helpers.withdrawals import set_eth1_withdrawal_credential_with_balance
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_basic_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -22,7 +22,7 @@ def test_basic_exit(spec, state):
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit)
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_incorrect_source_address(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -42,7 +42,7 @@ def test_incorrect_source_address(spec, state):
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_incorrect_withdrawal_credential_prefix(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -66,7 +66,7 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_on_exit_initiated_validator(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -87,7 +87,7 @@ def test_on_exit_initiated_validator(spec, state):
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_activation_epoch_less_than_shard_committee_period(spec, state):
current_epoch = spec.get_current_epoch(state)

View File

@ -3,7 +3,7 @@ from eth2spec.test.helpers.block import (
)
from eth2spec.test.context import (
spec_state_test,
with_eip7002_and_later,
with_electra_and_later,
)
from eth2spec.test.helpers.bls_to_execution_changes import (
get_signed_address_change,
@ -22,7 +22,7 @@ from eth2spec.test.helpers.withdrawals import (
)
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_basic_el_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -51,7 +51,7 @@ def test_basic_el_exit(spec, state):
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_basic_btec_and_el_exit_in_same_block(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -94,7 +94,7 @@ def test_basic_btec_and_el_exit_in_same_block(spec, state):
assert is_execution_address and is_correct_source_address
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_basic_btec_before_el_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -141,7 +141,7 @@ def test_basic_btec_before_el_exit(spec, state):
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@with_eip7002_and_later
@with_electra_and_later
@spec_state_test
def test_cl_exit_and_el_exit_in_same_block(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit

View File

@ -17,7 +17,6 @@ SHARDING = SpecForkName('sharding')
CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
ELECTRA = SpecForkName('electra')
EIP7002 = SpecForkName('eip7002')
WHISK = SpecForkName('whisk')
EIP7594 = SpecForkName('eip7594')
@ -37,7 +36,6 @@ ALL_PHASES = (
DENEB,
ELECTRA,
# Experimental patches
EIP7002,
EIP7594,
)
# The forks that have light client specs
@ -58,7 +56,6 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB,
# Experimental patches
WHISK: CAPELLA,
EIP7002: CAPELLA,
EIP7594: DENEB,
}

View File

@ -8,7 +8,6 @@ from eth2spec.test.helpers.forks import (
is_post_capella,
is_post_deneb,
is_post_electra,
is_post_eip7002,
)
@ -36,7 +35,6 @@ def get_execution_payload_header(spec, execution_payload):
payload_header.excess_blob_gas = execution_payload.excess_blob_gas
if is_post_electra(spec):
payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts)
if is_post_eip7002(spec):
payload_header.exits_root = spec.hash_tree_root(execution_payload.exits)
return payload_header
@ -109,7 +107,6 @@ def compute_el_header_block_hash(spec,
# deposit_receipts_root
assert deposit_receipts_trie_root is not None
execution_payload_header_rlp.append((Binary(32, 32), deposit_receipts_trie_root))
if is_post_eip7002(spec):
# exits_trie_root
execution_payload_header_rlp.append((Binary(32, 32), exits_trie_root))
@ -184,7 +181,6 @@ def compute_el_block_hash(spec, payload):
if is_post_electra(spec):
deposit_receipts_encoded = [get_deposit_receipt_rlp(spec, receipt) for receipt in payload.deposit_receipts]
deposit_receipts_trie_root = compute_trie_root_from_indexed_data(deposit_receipts_encoded)
if is_post_eip7002(spec):
exits_encoded = [get_exit_rlp(exit) for exit in payload.exits]
exits_trie_root = compute_trie_root_from_indexed_data(exits_encoded)

View File

@ -1,6 +1,6 @@
from .constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
ELECTRA, EIP7002, WHISK,
ELECTRA, WHISK,
PREVIOUS_FORK_OF,
)
@ -41,10 +41,6 @@ def is_post_electra(spec):
return is_post_fork(spec.fork, ELECTRA)
def is_post_eip7002(spec):
return is_post_fork(spec.fork, EIP7002)
def is_post_whisk(spec):
return is_post_fork(spec.fork, WHISK)

View File

@ -6,7 +6,7 @@ from eth2spec.test.helpers.execution_payload import (
compute_el_header_block_hash,
)
from eth2spec.test.helpers.forks import (
is_post_altair, is_post_bellatrix, is_post_capella, is_post_electra, is_post_eip7002, is_post_whisk,
is_post_altair, is_post_bellatrix, is_post_capella, is_post_electra, is_post_whisk,
)
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.whisk import compute_whisk_initial_tracker_cached, compute_whisk_initial_k_commitment_cached
@ -57,7 +57,6 @@ def get_sample_genesis_execution_payload_header(spec,
withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
if is_post_electra(spec):
deposit_receipts_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
if is_post_eip7002(spec):
exits_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
payload_header.block_hash = compute_el_header_block_hash(