Merge branch 'dev' into pr3442

This commit is contained in:
Hsiao-Wei Wang 2023-07-14 22:25:39 +08:00
commit 71c25f8664
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
28 changed files with 888 additions and 18 deletions

View File

@ -36,26 +36,26 @@ commands:
steps: steps:
- restore_cached_venv: - restore_cached_venv:
venv_name: v24-pyspec venv_name: v24-pyspec
reqs_checksum: cache-{{ checksum "setup.py" }} reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}
save_pyspec_cached_venv: save_pyspec_cached_venv:
description: Save a venv into a cache with pyspec keys" description: Save a venv into a cache with pyspec keys"
steps: steps:
- save_cached_venv: - save_cached_venv:
venv_name: v24-pyspec venv_name: v24-pyspec
reqs_checksum: cache-{{ checksum "setup.py" }} reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}
venv_path: ./venv venv_path: ./venv
restore_deposit_contract_tester_cached_venv: restore_deposit_contract_tester_cached_venv:
description: "Restore the venv from cache for the deposit contract tester" description: "Restore the venv from cache for the deposit contract tester"
steps: steps:
- restore_cached_venv: - restore_cached_venv:
venv_name: v23-deposit-contract-tester venv_name: v23-deposit-contract-tester
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }} reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
save_deposit_contract_tester_cached_venv: save_deposit_contract_tester_cached_venv:
description: "Save the venv to cache for later use of the deposit contract tester" description: "Save the venv to cache for later use of the deposit contract tester"
steps: steps:
- save_cached_venv: - save_cached_venv:
venv_name: v23-deposit-contract-tester venv_name: v23-deposit-contract-tester
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }} reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
venv_path: ./solidity_deposit_contract/web3_tester/venv venv_path: ./solidity_deposit_contract/web3_tester/venv
jobs: jobs:
checkout_specs: checkout_specs:
@ -168,6 +168,19 @@ jobs:
command: make citest fork=eip6110 command: make citest fork=eip6110
- store_test_results: - store_test_results:
path: tests/core/pyspec/test-reports 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: test-whisk:
docker: docker:
- image: circleci/python:3.9 - image: circleci/python:3.9
@ -304,6 +317,9 @@ workflows:
- test-eip6110: - test-eip6110:
requires: requires:
- install_pyspec_test - install_pyspec_test
- test-eip7002:
requires:
- install_pyspec_test
- test-whisk: - test-whisk:
requires: requires:
- install_pyspec_test - install_pyspec_test

View File

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

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ tests/core/pyspec/eth2spec/bellatrix/
tests/core/pyspec/eth2spec/capella/ tests/core/pyspec/eth2spec/capella/
tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/eip6110/ tests/core/pyspec/eth2spec/eip6110/
tests/core/pyspec/eth2spec/eip7002/
tests/core/pyspec/eth2spec/whisk/ tests/core/pyspec/eth2spec/whisk/
# coverage reports # coverage reports

View File

@ -104,9 +104,15 @@ generate_tests: $(GENERATOR_TARGETS)
pyspec: pyspec:
python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev
# check the setup tool requirements
preinstallation:
python3 -m venv venv; . venv/bin/activate; \
python3 -m pip install -r requirements_preinstallation.txt
# installs the packages to run pyspec tests # installs the packages to run pyspec tests
install_test: install_test: preinstallation
python3 -m venv venv; . venv/bin/activate; python3 -m pip install --upgrade wheel pip setuptools; python3 -m pip install -e .[lint]; python3 -m pip install -e .[test] python3 -m venv venv; . venv/bin/activate; \
python3 -m pip install -e .[lint]; python3 -m pip install -e .[test]
# Testing against `minimal` or `mainnet` config by default # Testing against `minimal` or `mainnet` config by default
test: pyspec test: pyspec

View File

@ -42,6 +42,7 @@ Features are researched and developed in parallel, and then consolidated into se
Additional specifications and standards outside of requisite client functionality can be found in the following repos: Additional specifications and standards outside of requisite client functionality can be found in the following repos:
* [Beacon APIs](https://github.com/ethereum/beacon-apis) * [Beacon APIs](https://github.com/ethereum/beacon-apis)
* [Engine APIs](https://github.com/ethereum/execution-apis/tree/main/src/engine)
* [Beacon Metrics](https://github.com/ethereum/beacon-metrics/) * [Beacon Metrics](https://github.com/ethereum/beacon-metrics/)
## Design goals ## Design goals

View File

@ -53,6 +53,9 @@ DENEB_FORK_EPOCH: 18446744073709551615
# EIP6110 # EIP6110
EIP6110_FORK_VERSION: 0x05000000 # temporary stub EIP6110_FORK_VERSION: 0x05000000 # temporary stub
EIP6110_FORK_EPOCH: 18446744073709551615 EIP6110_FORK_EPOCH: 18446744073709551615
# EIP7002
EIP7002_FORK_VERSION: 0x05000000 # temporary stub
EIP7002_FORK_EPOCH: 18446744073709551615
# WHISK # WHISK
WHISK_FORK_VERSION: 0x06000000 # temporary stub WHISK_FORK_VERSION: 0x06000000 # temporary stub
WHISK_FORK_EPOCH: 18446744073709551615 WHISK_FORK_EPOCH: 18446744073709551615

View File

@ -52,6 +52,9 @@ DENEB_FORK_EPOCH: 18446744073709551615
# EIP6110 # EIP6110
EIP6110_FORK_VERSION: 0x05000001 EIP6110_FORK_VERSION: 0x05000001
EIP6110_FORK_EPOCH: 18446744073709551615 EIP6110_FORK_EPOCH: 18446744073709551615
# EIP7002
EIP7002_FORK_VERSION: 0x05000001
EIP7002_FORK_EPOCH: 18446744073709551615
# WHISK # WHISK
WHISK_FORK_VERSION: 0x06000001 WHISK_FORK_VERSION: 0x06000001
WHISK_FORK_EPOCH: 18446744073709551615 WHISK_FORK_EPOCH: 18446744073709551615

View File

@ -5,9 +5,11 @@ BELLATRIX = 'bellatrix'
CAPELLA = 'capella' CAPELLA = 'capella'
DENEB = 'deneb' DENEB = 'deneb'
EIP6110 = 'eip6110' EIP6110 = 'eip6110'
EIP7002 = 'eip7002'
WHISK = 'whisk' WHISK = 'whisk'
# The helper functions that are used when defining constants # The helper functions that are used when defining constants
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = ''' CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = '''
def ceillog2(x: int) -> uint64: def ceillog2(x: int) -> uint64:

View File

@ -8,6 +8,7 @@ from .constants import (
DENEB, DENEB,
EIP6110, EIP6110,
WHISK, WHISK,
EIP7002,
) )
@ -19,6 +20,7 @@ PREVIOUS_FORK_OF = {
DENEB: CAPELLA, DENEB: CAPELLA,
EIP6110: DENEB, EIP6110: DENEB,
WHISK: CAPELLA, WHISK: CAPELLA,
EIP7002: CAPELLA,
} }
ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) ALL_FORKS = list(PREVIOUS_FORK_OF.keys())

View File

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

View File

@ -0,0 +1,12 @@
from .base import BaseSpecBuilder
from ..constants import EIP7002
class EIP7002SpecBuilder(BaseSpecBuilder):
fork: str = EIP7002
@classmethod
def imports(cls, preset_name: str):
return super().imports(preset_name) + f'''
from eth2spec.capella import {preset_name} as capella
'''

View File

@ -0,0 +1,3 @@
pip>=23.1.2
wheel>=0.40.0
setuptools>=68.0.0

View File

@ -0,0 +1,300 @@
# 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

@ -0,0 +1,140 @@
# 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

@ -10,12 +10,13 @@ from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_ca
from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal
from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal
from eth2spec.whisk import mainnet as spec_whisk_mainnet, minimal as spec_whisk_minimal from eth2spec.whisk import mainnet as spec_whisk_mainnet, minimal as spec_whisk_minimal
from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal
from eth2spec.utils import bls from eth2spec.utils import bls
from .exceptions import SkippedTest from .exceptions import SkippedTest
from .helpers.constants import ( from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
EIP6110, EIP6110, EIP7002,
WHISK, WHISK,
MINIMAL, MAINNET, MINIMAL, MAINNET,
ALL_PHASES, ALL_PHASES,
@ -85,6 +86,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
CAPELLA: spec_capella_minimal, CAPELLA: spec_capella_minimal,
DENEB: spec_deneb_minimal, DENEB: spec_deneb_minimal,
EIP6110: spec_eip6110_minimal, EIP6110: spec_eip6110_minimal,
EIP7002: spec_eip7002_minimal,
WHISK: spec_whisk_minimal, WHISK: spec_whisk_minimal,
}, },
MAINNET: { MAINNET: {
@ -94,6 +96,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
CAPELLA: spec_capella_mainnet, CAPELLA: spec_capella_mainnet,
DENEB: spec_deneb_mainnet, DENEB: spec_deneb_mainnet,
EIP6110: spec_eip6110_mainnet, EIP6110: spec_eip6110_mainnet,
EIP7002: spec_eip7002_mainnet,
WHISK: spec_whisk_mainnet, WHISK: spec_whisk_mainnet,
}, },
} }
@ -545,6 +548,7 @@ with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
with_capella_and_later = with_all_phases_from(CAPELLA) with_capella_and_later = with_all_phases_from(CAPELLA)
with_deneb_and_later = with_all_phases_from(DENEB) with_deneb_and_later = with_all_phases_from(DENEB)
with_eip6110_and_later = with_all_phases_from(EIP6110) with_eip6110_and_later = with_all_phases_from(EIP6110)
with_eip7002_and_later = with_all_phases_from(EIP7002)
with_whisk_and_later = with_all_phases_from(WHISK) with_whisk_and_later = with_all_phases_from(WHISK)

View File

@ -0,0 +1,107 @@
from eth2spec.test.context import spec_state_test, with_eip7002_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
@spec_state_test
def test_basic_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit)
@with_eip7002_and_later
@spec_state_test
def test_incorrect_source_address(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
incorrect_address = b'\x33' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
execution_layer_exit = spec.ExecutionLayerExit(
source_address=incorrect_address,
validator_pubkey=validator_pubkey,
)
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@spec_state_test
def test_incorrect_withdrawal_credential_prefix(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
# Set incorrect prefix
state.validators[validator_index].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX
+ state.validators[validator_index].withdrawal_credentials[1:]
)
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@spec_state_test
def test_on_exit_initiated_validator(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
# Initiate exit earlier
spec.initiate_validator_exit(state, validator_index)
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)
@with_eip7002_and_later
@spec_state_test
def test_activation_epoch_less_than_shard_committee_period(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
assert spec.get_current_epoch(state) < (
state.validators[validator_index].activation_epoch + spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False)

View File

@ -0,0 +1,174 @@
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot
)
from eth2spec.test.context import (
spec_state_test,
with_eip7002_and_later,
)
from eth2spec.test.helpers.bls_to_execution_changes import (
get_signed_address_change,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_block_hash,
)
from eth2spec.test.helpers.voluntary_exits import (
prepare_signed_exits,
)
from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
)
@with_eip7002_and_later
@spec_state_test
def test_basic_el_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
yield 'pre', state
validator_index = 0
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.exits = [execution_layer_exit]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [signed_block]
yield 'post', state
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@with_eip7002_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
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
yield 'pre', state
validator_index = 0
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
block = build_empty_block_for_next_slot(spec, state)
address = b'\x22' * 20
signed_address_change = get_signed_address_change(
spec,
state,
validator_index=validator_index,
to_execution_address=address,
)
block.body.bls_to_execution_changes = [signed_address_change]
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
block.body.execution_payload.exits = [execution_layer_exit]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [signed_block]
yield 'post', state
# BTEC is executed after EL-Exit, so it doesn't take effect. `initiate_validator_exit` is not called.
validator = state.validators[validator_index]
assert validator.exit_epoch == spec.FAR_FUTURE_EPOCH
# Check if BTEC is effect
is_execution_address = validator.withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
is_correct_source_address = validator.withdrawal_credentials[12:] == address
assert is_execution_address and is_correct_source_address
@with_eip7002_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
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
yield 'pre', state
validator_index = 0
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
# block_1 contains a BTEC operation of the given validator
address = b'\x22' * 20
signed_address_change = get_signed_address_change(
spec,
state,
validator_index=validator_index,
to_execution_address=address,
)
block_1 = build_empty_block_for_next_slot(spec, state)
block_1.body.bls_to_execution_changes = [signed_address_change]
signed_block_1 = state_transition_and_sign_block(spec, state, block_1)
validator = state.validators[validator_index]
assert validator.exit_epoch == spec.FAR_FUTURE_EPOCH
# Check if BTEC is effect
is_execution_address = validator.withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
is_correct_source_address = validator.withdrawal_credentials[12:] == address
assert is_execution_address and is_correct_source_address
# block_2 contains an EL-Exit operation of the given validator
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
block_2 = build_empty_block_for_next_slot(spec, state)
block_2.body.execution_payload.exits = [execution_layer_exit]
block_2.body.execution_payload.block_hash = compute_el_block_hash(spec, block_2.body.execution_payload)
signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
yield 'blocks', [signed_block_1, signed_block_2]
yield 'post', state
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@with_eip7002_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
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
yield 'pre', state
validator_index = 0
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
# CL-Exit
signed_voluntary_exits = prepare_signed_exits(spec, state, indices=[validator_index])
# EL-Exit
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_exit = spec.ExecutionLayerExit(
source_address=address,
validator_pubkey=validator_pubkey,
)
block = build_empty_block_for_next_slot(spec, state)
block.body.voluntary_exits = signed_voluntary_exits
block.body.execution_payload.exits = [execution_layer_exit]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [signed_block]
yield 'post', state
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH

View File

@ -17,6 +17,7 @@ SHARDING = SpecForkName('sharding')
CUSTODY_GAME = SpecForkName('custody_game') CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das') DAS = SpecForkName('das')
EIP6110 = SpecForkName('eip6110') EIP6110 = SpecForkName('eip6110')
EIP7002 = SpecForkName('eip7002')
WHISK = SpecForkName('whisk') WHISK = SpecForkName('whisk')
# #
@ -35,6 +36,7 @@ ALL_PHASES = (
DENEB, DENEB,
# Experimental patches # Experimental patches
EIP6110, EIP6110,
EIP7002,
) )
# The forks that have light client specs # The forks that have light client specs
LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB) LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB)

View File

@ -0,0 +1,39 @@
from eth2spec.test.context import expect_assertion_error
from eth2spec.test.helpers.state import get_validator_index_by_pubkey
#
# Run processing
#
def run_execution_layer_exit_processing(spec, state, execution_layer_exit, valid=True, success=True):
"""
Run ``process_execution_layer_exit``, yielding:
- pre-state ('pre')
- execution_layer_exit ('execution_layer_exit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
If ``success == False``, it doesn't initiate exit successfully
"""
validator_index = get_validator_index_by_pubkey(state, execution_layer_exit.validator_pubkey)
yield 'pre', state
yield 'execution_layer_exit', execution_layer_exit
if not valid:
expect_assertion_error(lambda: spec.process_execution_layer_exit(state, execution_layer_exit))
yield 'post', None
return
pre_exit_epoch = state.validators[validator_index].exit_epoch
spec.process_execution_layer_exit(state, execution_layer_exit)
yield 'post', state
if success:
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[validator_index].exit_epoch == pre_exit_epoch

View File

@ -8,6 +8,7 @@ from eth2spec.test.helpers.forks import (
is_post_capella, is_post_capella,
is_post_deneb, is_post_deneb,
is_post_eip6110, is_post_eip6110,
is_post_eip7002,
) )
@ -35,6 +36,8 @@ def get_execution_payload_header(spec, execution_payload):
payload_header.excess_data_gas = execution_payload.excess_data_gas payload_header.excess_data_gas = execution_payload.excess_data_gas
if is_post_eip6110(spec): if is_post_eip6110(spec):
payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts) 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 return payload_header
@ -56,7 +59,8 @@ def compute_el_header_block_hash(spec,
payload_header, payload_header,
transactions_trie_root, transactions_trie_root,
withdrawals_trie_root=None, withdrawals_trie_root=None,
deposit_receipts_trie_root=None): deposit_receipts_trie_root=None,
exits_trie_root=None):
""" """
Computes the RLP execution block hash described by an `ExecutionPayloadHeader`. Computes the RLP execution block hash described by an `ExecutionPayloadHeader`.
""" """
@ -105,6 +109,9 @@ def compute_el_header_block_hash(spec,
# deposit_receipts_root # deposit_receipts_root
assert deposit_receipts_trie_root is not None assert deposit_receipts_trie_root is not None
execution_payload_header_rlp.append((Binary(32, 32), deposit_receipts_trie_root)) 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))
sedes = List([schema for schema, _ in execution_payload_header_rlp]) sedes = List([schema for schema, _ in execution_payload_header_rlp])
values = [value for _, value in execution_payload_header_rlp] values = [value for _, value in execution_payload_header_rlp]
@ -114,7 +121,7 @@ def compute_el_header_block_hash(spec,
# https://eips.ethereum.org/EIPS/eip-4895 # https://eips.ethereum.org/EIPS/eip-4895
def get_withdrawal_rlp(spec, withdrawal): def get_withdrawal_rlp(withdrawal):
withdrawal_rlp = [ withdrawal_rlp = [
# index # index
(big_endian_int, withdrawal.index), (big_endian_int, withdrawal.index),
@ -131,6 +138,20 @@ def get_withdrawal_rlp(spec, withdrawal):
return encode(values, sedes) return encode(values, sedes)
# https://eips.ethereum.org/EIPS/eip-7002
def get_exit_rlp(exit):
exit_rlp = [
# source_address
(Binary(20, 20), exit.source_address),
# validator_pubkey
(Binary(48, 48), exit.validator_pubkey),
]
sedes = List([schema for schema, _ in exit_rlp])
values = [value for _, value in exit_rlp]
return encode(values, sedes)
def get_deposit_receipt_rlp(spec, deposit_receipt): def get_deposit_receipt_rlp(spec, deposit_receipt):
deposit_receipt_rlp = [ deposit_receipt_rlp = [
# pubkey # pubkey
@ -155,13 +176,17 @@ def compute_el_block_hash(spec, payload):
withdrawals_trie_root = None withdrawals_trie_root = None
deposit_receipts_trie_root = None deposit_receipts_trie_root = None
exits_trie_root = None
if is_post_capella(spec): if is_post_capella(spec):
withdrawals_encoded = [get_withdrawal_rlp(spec, withdrawal) for withdrawal in payload.withdrawals] withdrawals_encoded = [get_withdrawal_rlp(withdrawal) for withdrawal in payload.withdrawals]
withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded) withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded)
if is_post_eip6110(spec): if is_post_eip6110(spec):
deposit_receipts_encoded = [get_deposit_receipt_rlp(spec, receipt) for receipt in payload.deposit_receipts] 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) 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)
payload_header = get_execution_payload_header(spec, payload) payload_header = get_execution_payload_header(spec, payload)
@ -171,6 +196,7 @@ def compute_el_block_hash(spec, payload):
transactions_trie_root, transactions_trie_root,
withdrawals_trie_root, withdrawals_trie_root,
deposit_receipts_trie_root, deposit_receipts_trie_root,
exits_trie_root,
) )

View File

@ -16,6 +16,7 @@ from eth2spec.test.helpers.constants import (
CAPELLA, CAPELLA,
DENEB, DENEB,
EIP6110, EIP6110,
EIP7002,
) )
from eth2spec.test.helpers.deposits import ( from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit, prepare_state_and_deposit,
@ -161,6 +162,8 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=
state = post_spec.upgrade_to_deneb(state) state = post_spec.upgrade_to_deneb(state)
elif post_spec.fork == EIP6110: elif post_spec.fork == EIP6110:
state = post_spec.upgrade_to_eip6110(state) state = post_spec.upgrade_to_eip6110(state)
elif post_spec.fork == EIP7002:
state = post_spec.upgrade_to_eip7002(state)
assert state.fork.epoch == fork_epoch assert state.fork.epoch == fork_epoch
@ -179,6 +182,9 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=
elif post_spec.fork == EIP6110: elif post_spec.fork == EIP6110:
assert state.fork.previous_version == post_spec.config.DENEB_FORK_VERSION assert state.fork.previous_version == post_spec.config.DENEB_FORK_VERSION
assert state.fork.current_version == post_spec.config.EIP6110_FORK_VERSION assert state.fork.current_version == post_spec.config.EIP6110_FORK_VERSION
elif post_spec.fork == EIP7002:
assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION
assert state.fork.current_version == post_spec.config.EIP7002_FORK_VERSION
if with_block: if with_block:
return state, _state_transition_and_sign_block_at_slot( return state, _state_transition_and_sign_block_at_slot(

View File

@ -1,13 +1,14 @@
from .constants import ( from .constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
EIP6110, EIP6110, EIP7002, WHISK,
WHISK,
) )
def is_post_fork(a, b): def is_post_fork(a, b):
if a == WHISK: if a == WHISK:
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, WHISK] return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, WHISK]
if a == EIP7002:
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7002]
if a == EIP6110: if a == EIP6110:
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110] return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110]
if a == DENEB: if a == DENEB:
@ -43,5 +44,9 @@ def is_post_eip6110(spec):
return is_post_fork(spec.fork, EIP6110) return is_post_fork(spec.fork, EIP6110)
def is_post_eip7002(spec):
return is_post_fork(spec.fork, EIP7002)
def is_post_whisk(spec): def is_post_whisk(spec):
return is_post_fork(spec.fork, WHISK) return is_post_fork(spec.fork, WHISK)

View File

@ -1,11 +1,11 @@
from eth2spec.test.helpers.constants import ( from eth2spec.test.helpers.constants import (
ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, WHISK, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, WHISK,
) )
from eth2spec.test.helpers.execution_payload import ( from eth2spec.test.helpers.execution_payload import (
compute_el_header_block_hash, compute_el_header_block_hash,
) )
from eth2spec.test.helpers.forks import ( from eth2spec.test.helpers.forks import (
is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, is_post_whisk is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, is_post_eip7002, is_post_whisk,
) )
from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.whisk import compute_whisk_initial_tracker_cached, compute_whisk_initial_k_commitment_cached from eth2spec.test.helpers.whisk import compute_whisk_initial_tracker_cached, compute_whisk_initial_k_commitment_cached
@ -50,11 +50,14 @@ def get_sample_genesis_execution_payload_header(spec,
transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
withdrawals_trie_root = None withdrawals_trie_root = None
deposit_receipts_trie_root = None deposit_receipts_trie_root = None
exits_trie_root = None
if is_post_capella(spec): if is_post_capella(spec):
withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
if is_post_eip6110(spec): if is_post_eip6110(spec):
deposit_receipts_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 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( payload_header.block_hash = compute_el_header_block_hash(
spec, spec,
@ -62,6 +65,7 @@ def get_sample_genesis_execution_payload_header(spec,
transactions_trie_root, transactions_trie_root,
withdrawals_trie_root, withdrawals_trie_root,
deposit_receipts_trie_root, deposit_receipts_trie_root,
exits_trie_root,
) )
return payload_header return payload_header
@ -87,6 +91,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
elif spec.fork == EIP6110: elif spec.fork == EIP6110:
previous_version = spec.config.DENEB_FORK_VERSION previous_version = spec.config.DENEB_FORK_VERSION
current_version = spec.config.EIP6110_FORK_VERSION current_version = spec.config.EIP6110_FORK_VERSION
elif spec.fork == EIP7002:
previous_version = spec.config.CAPELLA_FORK_VERSION
current_version = spec.config.EIP7002_FORK_VERSION
elif spec.fork == WHISK: elif spec.fork == WHISK:
previous_version = spec.config.CAPELLA_FORK_VERSION previous_version = spec.config.CAPELLA_FORK_VERSION
current_version = spec.config.WHISK_FORK_VERSION current_version = spec.config.WHISK_FORK_VERSION

View File

@ -166,3 +166,8 @@ def has_active_balance_differential(spec, state):
active_balance = spec.get_total_active_balance(state) active_balance = spec.get_total_active_balance(state)
total_balance = spec.get_total_balance(state, set(range(len(state.validators)))) total_balance = spec.get_total_balance(state, set(range(len(state.validators))))
return active_balance // spec.EFFECTIVE_BALANCE_INCREMENT != total_balance // spec.EFFECTIVE_BALANCE_INCREMENT return active_balance // spec.EFFECTIVE_BALANCE_INCREMENT != total_balance // spec.EFFECTIVE_BALANCE_INCREMENT
def get_validator_index_by_pubkey(state, pubkey):
index = next((i for i, validator in enumerate(state.validators) if validator.pubkey == pubkey), None)
return index

View File

@ -20,9 +20,14 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None
assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch) assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch)
def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None, address=None):
if balance is None:
balance = spec.MAX_EFFECTIVE_BALANCE
if address is None:
address = b'\x11' * 20
validator = state.validators[index] validator = state.validators[index]
validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + address
validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE)
state.balances[index] = balance state.balances[index] = balance