Add EIP-7251 to Electra fork

This commit is contained in:
Alex Stokes 2024-04-09 12:49:58 -06:00
parent 77ec547cc7
commit ce3931653d
No known key found for this signature in database
29 changed files with 1723 additions and 400 deletions

1
.gitignore vendored
View File

@ -23,7 +23,6 @@ tests/core/pyspec/eth2spec/capella/
tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/electra/
tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip7251/
tests/core/pyspec/eth2spec/eip7594/
# coverage reports

View File

@ -53,9 +53,6 @@ DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC
# Electra
ELECTRA_FORK_VERSION: 0x05000000
ELECTRA_FORK_EPOCH: 18446744073709551615
# EIP7251
EIP7251_FORK_VERSION: 0x06000000 # temporary stub
EIP7251_FORK_EPOCH: 18446744073709551615
# WHISK
WHISK_FORK_VERSION: 0x08000000 # temporary stub
WHISK_FORK_EPOCH: 18446744073709551615
@ -163,6 +160,6 @@ NUMBER_OF_COLUMNS: 128
DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32
MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384
# [New in EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_EIP7251: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)
# [New in Electra:EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 # 2**8 * 10**9 (= 256,000,000,000)

View File

@ -52,9 +52,6 @@ DENEB_FORK_EPOCH: 18446744073709551615
# Electra
ELECTRA_FORK_VERSION: 0x05000001
ELECTRA_FORK_EPOCH: 18446744073709551615
# EIP7251
EIP7251_FORK_VERSION: 0x06000001 # temporary stub
EIP7251_FORK_EPOCH: 18446744073709551615
# WHISK
WHISK_FORK_VERSION: 0x08000001
WHISK_FORK_EPOCH: 18446744073709551615
@ -161,6 +158,6 @@ NUMBER_OF_COLUMNS: 128
DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32
MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384
# [New in EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_EIP7251: 64000000000 # 2**6 * 10**9 (= 64,000,000,000)
# [New in Electra:EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000 # 2**6 * 10**9 (= 64,000,000,000)
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)

View File

@ -1,28 +0,0 @@
# Mainnet preset - EIP7251
# Gwei values
# ---------------------------------------------------------------
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MIN_ACTIVATION_BALANCE: 32000000000
# 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_EIP7251: 2048000000000
# State list lengths
# ---------------------------------------------------------------
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728
PENDING_CONSOLIDATIONS_LIMIT: 262144
# Reward and penalty quotients
# ---------------------------------------------------------------
MIN_SLASHING_PENALTY_QUOTIENT_EIP7251: 4096
WHISTLEBLOWER_REWARD_QUOTIENT_EIP7251: 4096
# Max operations per block
# ---------------------------------------------------------------
MAX_CONSOLIDATIONS: 1
# Execution
# ---------------------------------------------------------------
# 2**3 (= 8) partial withdrawals
MAX_PARTIAL_WITHDRAWALS_PER_PAYLOAD: 8

View File

@ -1,11 +1,30 @@
# Mainnet preset - Electra
# Gwei values
# ---------------------------------------------------------------
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MIN_ACTIVATION_BALANCE: 32000000000
# 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000
# State list lengths
# ---------------------------------------------------------------
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728
PENDING_CONSOLIDATIONS_LIMIT: 262144
# Reward and penalty quotients
# ---------------------------------------------------------------
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096
WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096
# # Max operations per block
# ---------------------------------------------------------------
# `uint64(2**0)` (= 1)
MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2 * 3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
MAX_CONSOLIDATIONS: 1
# Execution
# ---------------------------------------------------------------
@ -13,3 +32,5 @@ MAX_ATTESTATIONS_ELECTRA: 8
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192
# 2**4 (= 16) exits
MAX_EXECUTION_LAYER_EXITS: 16
# 2**3 (= 8) partial withdrawals
MAX_PARTIAL_WITHDRAWALS_PER_PAYLOAD: 8

View File

@ -1,30 +0,0 @@
# Minimal preset - EIP7251
# Gwei values
# ---------------------------------------------------------------
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MIN_ACTIVATION_BALANCE: 32000000000
# 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_EIP7251: 2048000000000
# State list lengths
# ---------------------------------------------------------------
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
# [customized] smaller queue
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64
# [customized] smaller queue
PENDING_CONSOLIDATIONS_LIMIT: 64
# Reward and penalty quotients
# ---------------------------------------------------------------
MIN_SLASHING_PENALTY_QUOTIENT_EIP7251: 4096
WHISTLEBLOWER_REWARD_QUOTIENT_EIP7251: 4096
# Max operations per block
# ---------------------------------------------------------------
MAX_CONSOLIDATIONS: 1
# Execution
# ---------------------------------------------------------------
# [customized] 2**1 (= 2)
MAX_PARTIAL_WITHDRAWALS_PER_PAYLOAD: 2

View File

@ -1,11 +1,32 @@
# Minimal preset - Electra
# Gwei values
# ---------------------------------------------------------------
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MIN_ACTIVATION_BALANCE: 32000000000
# 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000
# State list lengths
# ---------------------------------------------------------------
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728
# [customized] smaller queue
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64
# [customized] smaller queue
PENDING_CONSOLIDATIONS_LIMIT: 64
# Reward and penalty quotients
# ---------------------------------------------------------------
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096
WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096
# # Max operations per block
# ---------------------------------------------------------------
# `uint64(2**0)` (= 1)
MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2 * 3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
MAX_CONSOLIDATIONS: 1
# Execution
# ---------------------------------------------------------------
@ -13,3 +34,5 @@ MAX_ATTESTATIONS_ELECTRA: 8
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4
# 2**4 (= 16) exits
MAX_EXECUTION_LAYER_EXITS: 16
# [customized] 2**1 (= 2)
MAX_PARTIAL_WITHDRAWALS_PER_PAYLOAD: 2

View File

@ -6,7 +6,6 @@ CAPELLA = 'capella'
DENEB = 'deneb'
ELECTRA = 'electra'
EIP7594 = 'eip7594'
EIP7251 = 'eip7251'
WHISK = 'whisk'

View File

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

View File

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

View File

@ -1,24 +0,0 @@
from typing import Dict
from .base import BaseSpecBuilder
from ..constants import EIP7251
class EIP7251SpecBuilder(BaseSpecBuilder):
fork: str = EIP7251
@classmethod
def imports(cls, preset_name: str):
return super().imports(preset_name) + f'''
from eth2spec.deneb import {preset_name} as deneb
'''
## TODO: deal with changed gindices
@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'FINALIZED_ROOT_GINDEX': 'GeneralizedIndex(169)',
'CURRENT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(87)',
}

View File

@ -1,3 +1,4 @@
from typing import Dict
from .base import BaseSpecBuilder
from ..constants import ELECTRA
@ -10,3 +11,13 @@ class ElectraSpecBuilder(BaseSpecBuilder):
return f'''
from eth2spec.deneb import {preset_name} as deneb
'''
## TODO: deal with changed gindices
@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'FINALIZED_ROOT_GINDEX': 'GeneralizedIndex(169)',
'CURRENT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(87)',
}

View File

@ -1,137 +0,0 @@
# EIP7251 -- 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 EIP7251](#fork-to-eip7251)
- [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 the EIP7251 upgrade.
## Configuration
Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `EIP7251_FORK_VERSION` | `Version('0x06000000')` |
| `EIP7251_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 >= EIP7251_FORK_EPOCH:
return EIP7251_FORK_VERSION
if epoch >= DENEB_FORK_EPOCH:
return DENEB_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 EIP7251
### 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 `EIP7251_FORK_EPOCH`.
Note that for the pure EIP7251 networks, we don't apply `upgrade_to_eip7251` since it starts with EIP7251 version logic.
### Upgrading the state
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7251_FORK_EPOCH`,
an irregular state change is made to upgrade to EIP7251.
```python
def upgrade_to_eip7251(pre: deneb.BeaconState) -> BeaconState:
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=EIP7251_FORK_VERSION,
epoch=deneb.get_current_epoch(pre),
),
# 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=pre.latest_execution_payload_header,
# 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,
# [New in EIP7251]
deposit_balance_to_consume=0,
exit_balance_to_consume=get_activation_exit_churn_limit(pre),
earliest_exit_epoch=max([v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]) + 1,
consolidation_balance_to_consume=get_consolidation_churn_limit(pre),
earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)),
pending_balance_deposits=[],
pending_partial_withdrawals=[],
pending_consolidations=[],
)
# Ensure early adopters of compounding credentials go through the activation churn
for index, validator in enumerate(post.validators):
if has_compounding_withdrawal_credential(validator):
queue_excess_active_balance(post, ValidatorIndex(index))
return post
```

View File

@ -1,73 +0,0 @@
# EIP-7251 -- Honest Validator
## 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)
- [Prerequisites](#prerequisites)
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
- [Block and sidecar proposal](#block-and-sidecar-proposal)
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
- [ExecutionPayload](#executionpayload)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This document represents the changes to be made in the code of an "honest validator".
## Prerequisites
This document is an extension of the [Deneb -- Honest Validator](../deneb/validator.md) guide.
All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden.
All terminology, constants, functions, and protocol mechanics defined in the updated [Beacon Chain doc of EIP-7251](./beacon-chain.md) are requisite for this document and used throughout.
Please see related Beacon Chain doc before continuing and use them as a reference throughout.
## Beacon chain responsibilities
All validator responsibilities remain unchanged other than those noted below.
### Block and sidecar proposal
#### Constructing the `BeaconBlockBody`
##### ExecutionPayload
`prepare_execution_payload` is updated from the Deneb specs.
*Note*: In this section, `state` is the state of the slot for the block proposal _without_ the block yet applied.
That is, `state` is the `previous_state` processed through any empty slots up to the assigned slot using `process_slots(previous_state, slot)`.
*Note*: The only change to `prepare_execution_payload` is the new definition of `get_expected_withdrawals`.
```python
def prepare_execution_payload(state: BeaconState,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
suggested_fee_recipient: ExecutionAddress,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
# Verify consistency of the parent hash with respect to the previous execution payload header
parent_hash = state.latest_execution_payload_header.block_hash
# Set the forkchoice head and initiate the payload build process
withdrawals, _ = get_expected_withdrawals(state) # [Modified in EIP-7251]
payload_attributes = PayloadAttributes(
timestamp=compute_timestamp_at_slot(state, state.slot),
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
withdrawals=withdrawals,
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
)
return execution_engine.notify_forkchoice_updated(
head_block_hash=parent_hash,
safe_block_hash=safe_block_hash,
finalized_block_hash=finalized_block_hash,
payload_attributes=payload_attributes,
)
```

File diff suppressed because it is too large Load Diff

View File

@ -139,9 +139,24 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
# EIP6110
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in Electra:EIP6110]
# [New in Electra:EIP6110]
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX,
# [New in Electra:EIP7251]
deposit_balance_to_consume=0,
exit_balance_to_consume=get_activation_exit_churn_limit(pre),
earliest_exit_epoch=max([v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]) + 1,
consolidation_balance_to_consume=get_consolidation_churn_limit(pre),
earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)),
pending_balance_deposits=[],
pending_partial_withdrawals=[],
pending_consolidations=[],
)
# [New in Electra:EIP7251]
# Ensure early adopters of compounding credentials go through the activation churn
for index, validator in enumerate(post.validators):
if has_compounding_withdrawal_credential(validator):
queue_excess_active_balance(post, ValidatorIndex(index))
return post
```

View File

@ -13,6 +13,7 @@
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Execution payload](#execution-payload)
- [Attesting](#attesting)
- [Construct attestation](#construct-attestation)
- [Attestation aggregation](#attestation-aggregation)
@ -81,6 +82,42 @@ def get_eth1_pending_deposit_count(state: BeaconState) -> uint64:
return uint64(0)
```
#### Execution payload
`prepare_execution_payload` is updated from the Deneb specs.
*Note*: In this section, `state` is the state of the slot for the block proposal _without_ the block yet applied.
That is, `state` is the `previous_state` processed through any empty slots up to the assigned slot using `process_slots(previous_state, slot)`.
*Note*: The only change to `prepare_execution_payload` is the new definition of `get_expected_withdrawals`.
```python
def prepare_execution_payload(state: BeaconState,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
suggested_fee_recipient: ExecutionAddress,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
# Verify consistency of the parent hash with respect to the previous execution payload header
parent_hash = state.latest_execution_payload_header.block_hash
# Set the forkchoice head and initiate the payload build process
withdrawals, _ = get_expected_withdrawals(state) # [Modified in EIP-7251]
payload_attributes = PayloadAttributes(
timestamp=compute_timestamp_at_slot(state, state.slot),
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
withdrawals=withdrawals,
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
)
return execution_engine.notify_forkchoice_updated(
head_block_hash=parent_hash,
safe_block_hash=safe_block_hash,
finalized_block_hash=finalized_block_hash,
payload_attributes=payload_attributes,
)
```
## Attesting
### Construct attestation

View File

@ -8,7 +8,6 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA,
EIP7251,
EIP7594,
WHISK,
MINIMAL,
@ -523,7 +522,6 @@ with_deneb_and_later = with_all_phases_from(DENEB)
with_electra_and_later = with_all_phases_from(ELECTRA)
with_whisk_and_later = with_all_phases_from(WHISK, all_phases=ALLOWED_TEST_RUNNER_FORKS)
with_eip7594_and_later = with_all_phases_from(EIP7594, all_phases=ALLOWED_TEST_RUNNER_FORKS)
with_eip7251_and_later = with_all_phases_from(EIP7251, all_phases=ALLOWED_TEST_RUNNER_FORKS)
class quoted_str(str):

View File

@ -1,7 +1,7 @@
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
with_electra_and_later,
with_presets,
always_bls,
spec_test,
@ -25,7 +25,7 @@ from eth2spec.test.helpers.withdrawals import (
# ***********************
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -73,7 +73,7 @@ def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -262,7 +262,7 @@ def test_basic_consolidation_with_compounding_credential(spec, state):
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -310,7 +310,7 @@ def test_consolidation_churn_limit_balance(spec, state):
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -357,7 +357,7 @@ def test_consolidation_balance_larger_than_churn_limit(spec, state):
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -404,7 +404,7 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state):
assert state.consolidation_balance_to_consume == expected_balance
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -461,7 +461,7 @@ def test_multiple_consolidations_below_churn(spec, state):
assert state.validators[2 * i].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -515,7 +515,7 @@ def test_multiple_consolidations_equal_churn(spec, state):
assert state.validators[2 * i].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -593,7 +593,7 @@ def test_multiple_consolidations_above_churn(spec, state):
assert state.validators[2 * i].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_electra_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
@ -651,8 +651,7 @@ def test_multiple_consolidations_equal_twice_churn(spec, state):
# Failing tests
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_source_equals_target(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -678,7 +677,7 @@ def test_invalid_source_equals_target(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_exceed_pending_consolidations_limit(spec, state):
state.pending_consolidations = [
@ -727,7 +726,7 @@ def test_invalid_not_enough_consolidation_churn_available(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_exited_source(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -749,7 +748,7 @@ def test_invalid_exited_source(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_exited_target(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -772,7 +771,7 @@ def test_invalid_exited_target(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_inactive_source(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -794,7 +793,7 @@ def test_invalid_inactive_source(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_inactive_target(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -817,7 +816,7 @@ def test_invalid_inactive_target(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_no_execution_withdrawal_credential(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -835,7 +834,7 @@ def test_invalid_no_execution_withdrawal_credential(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_different_credentials(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -856,7 +855,7 @@ def test_invalid_different_credentials(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
@always_bls
def test_invalid_source_signature(spec, state):
@ -880,7 +879,7 @@ def test_invalid_source_signature(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
@always_bls
def test_invalid_target_signature(spec, state):
@ -904,7 +903,7 @@ def test_invalid_target_signature(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_invalid_before_specified_epoch(spec, state):
current_epoch = spec.get_current_epoch(state)

View File

@ -0,0 +1,284 @@
from eth2spec.test.helpers.deposits import (
build_deposit,
prepare_state_and_deposit,
run_deposit_processing_electra,
run_deposit_processing_electra_with_specific_fork_version,
sign_deposit_data,
)
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.context import (
spec_state_test,
with_electra_and_later,
always_bls,
)
@with_electra_and_later
@spec_state_test
def test_new_deposit_under_min_activation_balance(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement.
amount = spec.MIN_ACTIVATION_BALANCE - 1
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_new_deposit_min(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MIN_DEPOSIT_AMOUNT
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_new_deposit_between_min_and_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE_electra // 2
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_new_deposit_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be exactly the same as balance.
amount = spec.MAX_EFFECTIVE_BALANCE_electra
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_new_deposit_over_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE_electra + 1
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
# @with_electra_and_later
# @spec_state_test
# def test_top_up__max_effective_balance(spec, state):
# validator_index = 0
# amount = spec.MAX_EFFECTIVE_BALANCE_electra // 4
# deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
# state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_electra
# state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE_electra
# yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
# assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE_electra + amount
# assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE_electra
@with_electra_and_later
@spec_state_test
@always_bls
def test_correct_sig_but_forked_state(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# deposits will always be valid, regardless of the current fork
state.fork.current_version = spec.Version('0x1234abcd')
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_new_deposit(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MIN_ACTIVATION_BALANCE
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index, effective=False)
@with_electra_and_later
@spec_state_test
def test_top_up__max_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE
state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
@with_electra_and_later
@spec_state_test
def test_top_up__less_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000
initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_electra_and_later
@spec_state_test
def test_top_up__zero_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
initial_balance = 0
initial_effective_balance = 0
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_electra_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_incorrect_withdrawal_credentials_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:]
deposit = prepare_state_and_deposit(
spec,
state,
validator_index,
amount,
withdrawal_credentials=withdrawal_credentials
)
# inconsistent withdrawal credentials, in top-ups, are allowed!
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_invalid_wrong_deposit_for_deposit_count(spec, state):
deposit_data_leaves = [spec.DepositData() for _ in range(len(state.validators))]
# build root for deposit_1
index_1 = len(deposit_data_leaves)
pubkey_1 = pubkeys[index_1]
privkey_1 = privkeys[index_1]
_, _, deposit_data_leaves = build_deposit(
spec,
deposit_data_leaves,
pubkey_1,
privkey_1,
spec.MAX_EFFECTIVE_BALANCE,
withdrawal_credentials=b'\x00' * 32,
signed=True,
)
deposit_count_1 = len(deposit_data_leaves)
# build root for deposit_2
index_2 = len(deposit_data_leaves)
pubkey_2 = pubkeys[index_2]
privkey_2 = privkeys[index_2]
deposit_2, root_2, deposit_data_leaves = build_deposit(
spec,
deposit_data_leaves,
pubkey_2,
privkey_2,
spec.MAX_EFFECTIVE_BALANCE,
withdrawal_credentials=b'\x00' * 32,
signed=True,
)
# state has root for deposit_2 but is at deposit_count for deposit_1
state.eth1_data.deposit_root = root_2
state.eth1_data.deposit_count = deposit_count_1
yield from run_deposit_processing_electra(spec, state, deposit_2, index_2, valid=False)
@with_electra_and_later
@spec_state_test
def test_invalid_bad_merkle_proof(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
# mess up merkle branch
deposit.proof[5] = spec.Bytes32()
sign_deposit_data(spec, deposit.data, privkeys[validator_index])
yield from run_deposit_processing_electra(spec, state, deposit, validator_index, valid=False)
@with_electra_and_later
@spec_state_test
def test_key_validate_invalid_subgroup(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey = b'\x00' * 48
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
def test_key_validate_invalid_decompression(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case.
# This pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
pubkey = bytes.fromhex(pubkey_hex)
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_processing_electra(spec, state, deposit, validator_index)
@with_electra_and_later
@spec_state_test
@always_bls
def test_ineffective_deposit_with_bad_fork_version(spec, state):
yield from run_deposit_processing_electra_with_specific_fork_version(
spec,
state,
fork_version=spec.Version('0xAaBbCcDd'),
effective=False,
)

View File

@ -1,7 +1,7 @@
from eth2spec.test.context import (
spec_state_test,
expect_assertion_error,
with_eip7251_and_later,
with_electra_and_later,
with_presets,
)
from eth2spec.test.helpers.constants import MINIMAL
@ -20,8 +20,7 @@ from eth2spec.test.helpers.withdrawals import (
# Modified tests from 7002. Just testing EL-triggered exits, not partial withdrawals
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_basic_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -104,7 +103,7 @@ def test_basic_exit_with_full_partial_withdrawal_queue(spec, state):
# Invalid tests
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_incorrect_source_address(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -129,7 +128,7 @@ def test_incorrect_source_address(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_incorrect_withdrawal_credential_prefix(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -158,7 +157,7 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_on_exit_initiated_validator(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
@ -184,7 +183,7 @@ def test_on_exit_initiated_validator(spec, state):
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_activation_epoch_less_than_shard_committee_period(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -212,8 +211,7 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
# Partial withdrawals tests
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request(spec, state):

View File

@ -0,0 +1,441 @@
from eth2spec.test.helpers.constants import (MINIMAL, MAINNET)
from eth2spec.test.context import (
spec_state_test,
with_electra_and_later,
with_presets,
always_bls,
spec_test, single_phase,
with_custom_state,
scaled_churn_balances_min_churn_limit,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import (
run_voluntary_exit_processing,
sign_voluntary_exit,
)
# ********************
# * EXIT QUEUE TESTS *
# ********************
@with_electra_and_later
@spec_state_test
def test_min_balance_exit(spec, state):
# This state has 64 validators each with 32 ETH
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
spec.initiate_validator_exit(state, 0)
yield "post", state
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - spec.MIN_ACTIVATION_BALANCE
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_electra_and_later
@spec_state_test
def test_min_balance_exits_up_to_churn(spec, state):
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
for i in range(churn_limit // spec.MIN_ACTIVATION_BALANCE):
validator_index = i
spec.initiate_validator_exit(state, validator_index)
yield f"post{i}", state
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance * (i + 1)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
yield "post", state
@with_electra_and_later
@spec_state_test
def test_min_balance_exits_above_churn(spec, state):
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
for i in range(churn_limit // spec.MIN_ACTIVATION_BALANCE):
validator_index = i
spec.initiate_validator_exit(state, validator_index)
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance * (i + 1)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Exit balance has been fully consumed
assert state.exit_balance_to_consume == 0
# Exit an additional validator, doesn't fit in the churn limit, so exit
# epoch is incremented
validator_index = churn_limit // spec.MIN_ACTIVATION_BALANCE
spec.initiate_validator_exit(state, validator_index)
yield "post", state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch + 1
# Check exit balance to consume is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance
# @with_electra_and_later
# @spec_state_test
# def test_exit_balance_to_consume_large_validator(spec, state):
# # Set 0th validator effective balance to 2048 ETH
# state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE_electra
# churn_limit = spec.get_validator_churn_limit(state)
# expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
# expected_exit_epoch += spec.MAX_EFFECTIVE_BALANCE_electra // churn_limit
# validator_index = 0
# spec.initiate_validator_exit(state, validator_index)
# # Check exit epoch
# assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# # Check exit_balance_to_consume
# assert state.exit_balance_to_consume == churn_limit - (spec.MAX_EFFECTIVE_BALANCE_electra % churn_limit)
# # Check earliest_exit_epoch
# assert state.earliest_exit_epoch == expected_exit_epoch
@with_electra_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_max_balance_exit(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
assert churn_limit == spec.MIN_ACTIVATION_BALANCE * spec.config.MIN_PER_EPOCH_CHURN_LIMIT
# Set 0th validator effective balance to 2048 ETH
state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE_electra
yield 'pre', state
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
# Validator consumes exit churn for 16 epochs, exits at the 17th one
expected_exit_epoch += (spec.MAX_EFFECTIVE_BALANCE_electra // churn_limit)
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == churn_limit
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_electra_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_exit_with_balance_equal_to_churn_limit(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set 0th validator effective balance to churn_limit
state.validators[0].effective_balance = churn_limit
yield 'pre', state
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Validator consumes churn limit fully in the current epoch
assert (state.validators[validator_index].exit_epoch ==
spec.compute_activation_exit_epoch(spec.get_current_epoch(state)))
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == 0
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == state.validators[validator_index].exit_epoch
@with_electra_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_exit_churn_limit_balance_existing_churn_(spec, state):
cl = spec.get_activation_exit_churn_limit(state)
# set exit epoch to the first available one and set exit balance to consume to full churn limit
state.earliest_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
state.exit_balance_to_consume = cl
# consume some churn in exit epoch
state.exit_balance_to_consume -= 1000000000
# Set 0th validator effective balance to the churn limit
state.validators[0].effective_balance = cl
yield 'pre', state
# The existing 1 ETH churn will push an extra epoch
expected_exit_epoch = state.earliest_exit_epoch + 1
yield 'post', state
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check balance consumed in exit epoch is the remainder 1 ETH
assert state.exit_balance_to_consume == cl - 1000000000
# check earliest exit epoch
assert expected_exit_epoch == state.earliest_exit_epoch
@with_electra_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_multi_epoch_exit_existing_churn(spec, state):
cl = spec.get_activation_exit_churn_limit(state)
# set exit epoch to the first available one and set exit balance to consume to full churn limit
state.earliest_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
state.exit_balance_to_consume = cl
# consume some churn in exit epoch
state.exit_balance_to_consume -= 1000000000
# Set 0th validator effective balance to 2x the churn limit
state.validators[0].effective_balance = 2 * cl
yield 'pre', state
# Two extra epochs will be necessary
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state)) + 2
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check balance consumed in exit epoch is the remainder 1 ETH
assert state.exit_balance_to_consume == cl - 1000000000
# check earliest exit epoch
assert expected_exit_epoch == state.earliest_exit_epoch
# Repurposed from phase0 voluntary exit tests, should disable the phase0 ones
def run_test_success_exit_queue(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)
churn_limit = spec.get_activation_exit_churn_limit(state)
# exit `MAX_EXITS_PER_EPOCH`
max_exits = churn_limit // spec.MIN_ACTIVATION_BALANCE
initial_indices = spec.get_active_validator_indices(state, current_epoch)[:max_exits]
# Prepare a bunch of exits, based on the current state
exit_queue = []
for index in initial_indices:
privkey = pubkey_to_privkey[state.validators[index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=index), privkey)
exit_queue.append(signed_voluntary_exit)
# Now run all the exits
for voluntary_exit in exit_queue:
# the function yields data, but we are just interested in running it here, ignore yields.
for _ in run_voluntary_exit_processing(spec, state, voluntary_exit):
continue
# exit an additional validator
validator_index = spec.get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
# This is the interesting part of the test: on a pre-state with a full exit queue,
# when processing an additional exit, it results in an exit in a later epoch
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
for index in initial_indices:
assert (
state.validators[validator_index].exit_epoch ==
state.validators[index].exit_epoch + 1
)
assert state.earliest_exit_epoch == state.validators[validator_index].exit_epoch
consumed_churn = spec.MIN_ACTIVATION_BALANCE * (max_exits + 1)
assert state.exit_balance_to_consume == churn_limit - (consumed_churn % churn_limit)
@with_electra_and_later
@spec_state_test
def test_success_exit_queue__min_churn(spec, state):
yield from run_test_success_exit_queue(spec, state)
@with_electra_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_success_exit_queue__scaled_churn(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
assert churn_limit > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
yield from run_test_success_exit_queue(spec, state)
# After here no modifications were made, can just leave them in phase0 as is
@with_electra_and_later
@spec_state_test
def test_basic(spec, state):
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]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch)
@with_electra_and_later
@spec_state_test
@always_bls
def test_invalid_incorrect_signature(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]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, 12345)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_electra_and_later
@spec_state_test
def test_default_exit_epoch_subsequent_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]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
# Exit one validator prior to this new one
exited_index = spec.get_active_validator_indices(state, current_epoch)[-1]
state.validators[exited_index].exit_epoch = current_epoch - 1
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch)
@with_electra_and_later
@spec_state_test
def test_invalid_validator_exit_in_future(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]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch + 1,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_electra_and_later
@spec_state_test
def test_invalid_validator_incorrect_validator_index(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]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=len(state.validators),
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_electra_and_later
@spec_state_test
def test_invalid_validator_not_active(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_electra_and_later
@spec_state_test
def test_invalid_validator_already_exited(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow validator able to 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]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
# but validator already has exited
state.validators[validator_index].exit_epoch = current_epoch + 2
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_electra_and_later
@spec_state_test
def test_invalid_validator_not_active_long_enough(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
assert (
current_epoch - state.validators[validator_index].activation_epoch <
spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)

View File

@ -1,11 +1,11 @@
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
with_electra_and_later,
)
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_pending_deposit_min_activation_balance(spec, state):
index = 0
@ -23,7 +23,7 @@ def test_pending_deposit_min_activation_balance(spec, state):
assert state.pending_balance_deposits == []
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_pending_deposit_balance_equal_churn(spec, state):
index = 0
@ -40,7 +40,7 @@ def test_pending_deposit_balance_equal_churn(spec, state):
assert state.pending_balance_deposits == []
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_pending_deposit_balance_above_churn(spec, state):
index = 0
@ -64,7 +64,7 @@ def test_pending_deposit_balance_above_churn(spec, state):
]
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_pending_deposit_preexisting_churn(spec, state):
index = 0
@ -85,7 +85,7 @@ def test_pending_deposit_preexisting_churn(spec, state):
assert state.pending_balance_deposits == []
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_multiple_pending_deposits_below_churn(spec, state):
amount = 10**9
@ -106,7 +106,7 @@ def test_multiple_pending_deposits_below_churn(spec, state):
assert state.pending_balance_deposits == []
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_multiple_pending_deposits_above_churn(spec, state):
# set third deposit to be over the churn

View File

@ -1,7 +1,7 @@
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
with_electra_and_later,
)
# ***********************
@ -9,7 +9,7 @@ from eth2spec.test.context import (
# ***********************
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_basic_pending_consolidation(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -78,7 +78,7 @@ def test_consolidation_not_yet_withdrawable_validator(spec, state):
assert state.pending_consolidations == pre_pending_consolidations
@with_eip7251_and_later
@with_electra_and_later
@spec_state_test
def test_skip_consolidation_when_source_slashed(spec, state):
current_epoch = spec.get_current_epoch(state)

View File

@ -1,11 +1,11 @@
from eth2spec.test.context import (
single_phase,
spec_test,
with_eip7251_and_later,
with_electra_and_later,
)
@with_eip7251_and_later
@with_electra_and_later
@spec_test
@single_phase
def test_withdrawals(spec):

View File

@ -19,7 +19,6 @@ CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
ELECTRA = SpecForkName('electra')
WHISK = SpecForkName('whisk')
EIP7251 = SpecForkName('eip7251')
EIP7594 = SpecForkName('eip7594')
#
@ -38,7 +37,6 @@ ALL_PHASES = (
DENEB,
ELECTRA,
# Experimental patches
EIP7251,
EIP7594,
)
# The forks that have light client specs
@ -59,7 +57,6 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB,
# Experimental patches
WHISK: CAPELLA,
EIP7251: DENEB,
EIP7594: DENEB,
}

View File

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

View File

@ -6,7 +6,7 @@ from eth2spec.test.helpers.execution_payload import (
compute_el_header_block_hash,
)
from eth2spec.test.helpers.forks import (
is_post_altair, is_post_bellatrix, is_post_capella, is_post_electra, is_post_whisk, is_post_eip7251,
is_post_altair, is_post_bellatrix, is_post_capella, is_post_electra, is_post_whisk,
)
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.whisk import compute_whisk_initial_tracker_cached, compute_whisk_initial_k_commitment_cached
@ -148,7 +148,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
for i in range(spec.WHISK_PROPOSER_TRACKERS_COUNT):
state.whisk_proposer_trackers[i] = compute_whisk_initial_tracker_cached(i % vc)
if is_post_eip7251(spec):
if is_post_electra(spec):
state.deposit_balance_to_consume = 0
state.exit_balance_to_consume = 0
state.earliest_exit_epoch = spec.GENESIS_EPOCH

View File

@ -4,7 +4,7 @@ from eth2spec.test.context import (
)
from eth2spec.test.helpers.constants import UINT64_MAX
from eth2spec.test.helpers.forks import (
is_post_altair, is_post_bellatrix, is_post_eip7251,
is_post_altair, is_post_bellatrix, is_post_electra,
)
@ -58,8 +58,8 @@ def test_incentives(spec, state):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
elif is_post_altair(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
elif is_post_eip7251(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_EIP7251 <= spec.WHISTLEBLOWER_REWARD_QUOTIENT_EIP7251
elif is_post_electra(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA <= spec.WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA
else:
assert spec.MIN_SLASHING_PENALTY_QUOTIENT <= spec.WHISTLEBLOWER_REWARD_QUOTIENT