Merge branch 'dev' into pr3442
This commit is contained in:
commit
71c25f8664
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
'''
|
|
@ -0,0 +1,3 @@
|
||||||
|
pip>=23.1.2
|
||||||
|
wheel>=0.40.0
|
||||||
|
setuptools>=68.0.0
|
|
@ -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
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue