Merge branch 'dev' into pr3778

This commit is contained in:
Hsiao-Wei Wang 2024-06-10 22:40:17 +08:00
commit 12f0e7cc44
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
33 changed files with 1802 additions and 1265 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/electra/
tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip7594/
tests/core/pyspec/eth2spec/eip6800/
# coverage reports
.htmlcov

View File

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

View File

@ -0,0 +1,12 @@
# Mainnet preset - EIP6800
# Misc
# ---------------------------------------------------------------
# `uint64(2**16)` (= 65,536)
MAX_STEMS: 65536
# `uint64(33)`
MAX_COMMITMENTS_PER_STEM: 33
# `uint64(2**8)` (= 256)
VERKLE_WIDTH: 256
# `uint64(2**3)` (= 8)
IPA_PROOF_DEPTH: 8

View File

@ -30,12 +30,12 @@ MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2**3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
# `uint64(2**0)` (= 1)
MAX_CONSOLIDATIONS: 1
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1
# Execution
# ---------------------------------------------------------------
# 2**13 (= 8192) receipts
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192
# 2**13 (= 8192) deposit requests
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 8192
# 2**4 (= 16) withdrawal requests
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16

View File

@ -0,0 +1,12 @@
# Minimal preset - EIP6800
# Execution
# ---------------------------------------------------------------
# `uint64(2**16)` (= 65,536)
MAX_STEMS: 65536
# `uint64(33)`
MAX_COMMITMENTS_PER_STEM: 33
# `uint64(2**8)` (= 256)
VERKLE_WIDTH: 256
# `uint64(2**3)` (= 8)
IPA_PROOF_DEPTH: 8

View File

@ -30,12 +30,12 @@ MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2**3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
# `uint64(2**0)` (= 1)
MAX_CONSOLIDATIONS: 1
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1
# Execution
# ---------------------------------------------------------------
# [customized]
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4
MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 4
# [customized] 2**1 (= 2) withdrawal requests
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2

View File

@ -6,10 +6,10 @@ CAPELLA = 'capella'
DENEB = 'deneb'
ELECTRA = 'electra'
EIP7594 = 'eip7594'
EIP6800 = 'eip6800'
WHISK = 'whisk'
# The helper functions that are used when defining constants
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = '''
def ceillog2(x: int) -> uint64:

View File

@ -178,7 +178,7 @@ def combine_dicts(old_dict: Dict[str, T], new_dict: Dict[str, T]) -> Dict[str, T
ignored_dependencies = [
'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature',
'Bytes1', 'Bytes4', 'Bytes8', 'Bytes20', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
'Bytes1', 'Bytes4', 'Bytes8', 'Bytes20', 'Bytes31', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
'bytes', 'byte', 'ByteList', 'ByteVector',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',

View File

@ -9,6 +9,7 @@ from .constants import (
ELECTRA,
WHISK,
EIP7594,
EIP6800,
)
@ -21,6 +22,7 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB,
WHISK: CAPELLA,
EIP7594: DENEB,
EIP6800: DENEB,
}
ALL_FORKS = list(PREVIOUS_FORK_OF.keys())

View File

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

View File

@ -0,0 +1,21 @@
from typing import Dict
from .base import BaseSpecBuilder
from ..constants import EIP6800
class EIP6800SpecBuilder(BaseSpecBuilder):
fork: str = EIP6800
@classmethod
def imports(cls, preset_name: str):
return f'''
from eth2spec.deneb import {preset_name} as deneb
from eth2spec.utils.ssz.ssz_typing import Bytes31
'''
@classmethod
def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:
return {
'MAX_STEMS': spec_object.preset_vars['MAX_STEMS'].value,
}

View File

@ -219,7 +219,13 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
elif source.startswith("class"):
class_name, parent_class = _get_class_info_from_source(source)
# check consistency with spec
assert class_name == current_name
try:
assert class_name == current_name
except Exception:
print('class_name', class_name)
print('current_name', current_name)
raise
if parent_class:
assert parent_class == "Container"
# NOTE: trim whitespace from spec

View File

@ -0,0 +1,221 @@
# EIP6800 -- 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)
- [Custom types](#custom-types)
- [Preset](#preset)
- [Execution](#execution)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [`ExecutionPayload`](#executionpayload)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [New containers](#new-containers)
- [`SuffixStateDiff`](#suffixstatediff)
- [`StemStateDiff`](#stemstatediff)
- [`IPAProof`](#ipaproof)
- [`VerkleProof`](#verkleproof)
- [`ExecutionWitness`](#executionwitness)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Block processing](#block-processing)
- [Execution payload](#execution-payload)
- [`process_execution_payload`](#process_execution_payload)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This upgrade adds transaction execution to the beacon chain as part of the eip6800 upgrade.
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `BanderwagonGroupElement` | `Bytes32` | |
| `BanderwagonFieldElement` | `Bytes32` | |
| `Stem` | `Bytes31` | |
## Preset
### Execution
| Name | Value |
| - | - |
| `MAX_STEMS` | `uint64(2**16)` (= 65,536) |
| `MAX_COMMITMENTS_PER_STEM` | `uint64(33)` |
| `VERKLE_WIDTH` | `uint64(2**8)` (= 256) |
| `IPA_PROOF_DEPTH` | `uint64(2**3)` (= 8) |
## Containers
### Extended containers
#### `ExecutionPayload`
```python
class ExecutionPayload(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32 # 'difficulty' in the yellow paper
block_number: uint64 # 'number' in the yellow paper
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 # Hash of execution block
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
blob_gas_used: uint64
excess_blob_gas: uint64
execution_witness: ExecutionWitness # [New in EIP6800]
```
#### `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 # Hash of execution block
transactions_root: Root
withdrawals_root: Root
blob_gas_used: uint64
excess_data_gas: uint64
execution_witness_root: Root # [New in EIP6800]
```
### New containers
#### `SuffixStateDiff`
```python
class SuffixStateDiff(Container):
suffix: Bytes1
# Null means not currently present
current_value: Optional[Bytes32]
# Null means value not updated
new_value: Optional[Bytes32]
```
*Note*: on the Kaustinen testnet, `new_value` is omitted from the container.
#### `StemStateDiff`
```python
class StemStateDiff(Container):
stem: Stem
# Valid only if list is sorted by suffixes
suffix_diffs: List[SuffixStateDiff, VERKLE_WIDTH]
```
#### `IPAProof`
```python
class IPAProof(Container):
cl: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH]
cr: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH]
final_evaluation = BanderwagonFieldElement
```
#### `VerkleProof`
```python
class VerkleProof(Container):
other_stems: List[Bytes31, MAX_STEMS]
depth_extension_present: ByteList[MAX_STEMS]
commitments_by_path: List[BanderwagonGroupElement, MAX_STEMS * MAX_COMMITMENTS_PER_STEM]
d: BanderwagonGroupElement
ipa_proof: IPAProof
```
#### `ExecutionWitness`
```python
class ExecutionWitness(Container):
state_diff: List[StemStateDiff, MAX_STEMS]
verkle_proof: VerkleProof
```
## Beacon chain state transition function
### Block processing
#### Execution payload
##### `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 commitments are under limit
assert len(body.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
# Verify the execution payload is valid
# Pass `versioned_hashes` to Execution Engine
# Pass `parent_beacon_block_root` to Execution Engine
versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments]
assert execution_engine.verify_and_notify_new_payload(
NewPayloadRequest(
execution_payload=payload,
versioned_hashes=versioned_hashes,
parent_beacon_block_root=state.latest_block_header.parent_root,
)
)
# 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),
excess_data_gas=payload.excess_data_gas,
execution_witness_root=hash_tree_root(payload.execution_witness), # [New in EIP6800]
)
```
## Testing
TBD

View File

@ -0,0 +1,145 @@
# EIP-6800 -- Fork Logic
## 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 eip6800](#fork-to-eip6800)
- [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 eip6800 upgrade.
## Configuration
Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `EIP6800_FORK_VERSION` | `Version('0x05000000')` |
| `EIP6800_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 >= EIP6800_FORK_EPOCH:
return EIP6800_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 eip6800
### Fork trigger
The fork is triggered at epoch `EIP6800_FORK_EPOCH`.
Note that for the pure eip6800 networks, we don't apply `upgrade_to_eip6800` since it starts with the eip6800 version logic.
### Upgrading the state
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP6800_FORK_EPOCH`,
an irregular state change is made to upgrade to eip6800.
The upgrade occurs after the completion of the inner loop of `process_slots` that sets `state.slot` equal to `EIP6800_FORK_EPOCH * SLOTS_PER_EPOCH`.
Care must be taken when transitioning through the fork boundary as implementations will need a modified [state transition function](../phase0/beacon-chain.md#beacon-chain-state-transition-function) that deviates from the Phase 0 document.
In particular, the outer `state_transition` function defined in the Phase 0 document will not expose the precise fork slot to execute the upgrade in the presence of skipped slots at the fork boundary. Instead, the logic must be within `process_slots`.
```python
def upgrade_to_eip6800(pre: deneb.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,
excess_data_gas=uint256(0),
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,
execution_witness_root=hash_tree_root(ExecutionWitness([], [])) # New in eip6800
)
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=EIP6800_FORK_VERSION, # [Modified in eip6800]
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,
# Withdrawals
next_withdrawal_index=pre.next_withdrawal_index,
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
)
return post
```

View File

@ -263,14 +263,14 @@ def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> Polynomial
def shift_polynomialcoeff(polynomial_coeff: PolynomialCoeff, factor: BLSFieldElement) -> PolynomialCoeff:
"""
Shift the evaluation of a polynomial in coefficient form by factor.
This results in a new polynomial g(x) = f(factor * x)
This returns a new polynomial g in coefficient form such that g(x) = f(factor * x).
In other words, each coefficient of f is scaled by a power of factor.
"""
factor_power = 1
inv_factor = pow(int(factor), BLS_MODULUS - 2, BLS_MODULUS)
o = []
for p in polynomial_coeff:
o.append(int(p) * factor_power % BLS_MODULUS)
factor_power = factor_power * inv_factor % BLS_MODULUS
factor_power = factor_power * int(factor) % BLS_MODULUS
return o
```

View File

@ -24,12 +24,11 @@
- [Validator cycle](#validator-cycle)
- [Containers](#containers)
- [New containers](#new-containers)
- [`DepositReceipt`](#depositreceipt)
- [`DepositRequest`](#depositrequest)
- [`PendingBalanceDeposit`](#pendingbalancedeposit)
- [`PendingPartialWithdrawal`](#pendingpartialwithdrawal)
- [`ExecutionLayerWithdrawalRequest`](#executionlayerwithdrawalrequest)
- [`Consolidation`](#consolidation)
- [`SignedConsolidation`](#signedconsolidation)
- [`WithdrawalRequest`](#withdrawalrequest)
- [`ConsolidationRequest`](#consolidationrequest)
- [`PendingConsolidation`](#pendingconsolidation)
- [Modified Containers](#modified-containers)
- [`AttesterSlashing`](#attesterslashing)
@ -42,6 +41,7 @@
- [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions)
- [Predicates](#predicates)
- [Updated `compute_proposer_index`](#updated-compute_proposer_index)
- [Updated `is_eligible_for_activation_queue`](#updated-is_eligible_for_activation_queue)
- [New `is_compounding_withdrawal_credential`](#new-is_compounding_withdrawal_credential)
- [New `has_compounding_withdrawal_credential`](#new-has_compounding_withdrawal_credential)
@ -91,11 +91,11 @@
- [Voluntary exits](#voluntary-exits)
- [Updated `process_voluntary_exit`](#updated-process_voluntary_exit)
- [Execution layer withdrawal requests](#execution-layer-withdrawal-requests)
- [New `process_execution_layer_withdrawal_request`](#new-process_execution_layer_withdrawal_request)
- [Deposit receipts](#deposit-receipts)
- [New `process_deposit_receipt`](#new-process_deposit_receipt)
- [Consolidations](#consolidations)
- [New `process_consolidation`](#new-process_consolidation)
- [New `process_withdrawal_request`](#new-process_withdrawal_request)
- [Deposit requests](#deposit-requests)
- [New `process_deposit_request`](#new-process_deposit_request)
- [Execution layer consolidation requests](#execution-layer-consolidation-requests)
- [New `process_consolidation_request`](#new-process_consolidation_request)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -119,7 +119,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Description |
| - | - | - |
| `UNSET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` | *[New in Electra:EIP6110]* |
| `UNSET_DEPOSIT_REQUESTS_START_INDEX` | `uint64(2**64 - 1)` | *[New in Electra:EIP6110]* |
| `FULL_EXIT_REQUEST_AMOUNT` | `uint64(0)` | *[New in Electra:EIP7002]* |
### Withdrawal prefixes
@ -164,16 +164,16 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value |
| - | - |
| `MAX_CONSOLIDATIONS` | `uint64(1)` |
| `MAX_ATTESTER_SLASHINGS_ELECTRA` | `2**0` (= 1) | *[New in Electra:EIP7549]* |
| `MAX_ATTESTATIONS_ELECTRA` | `2**3` (= 8) | *[New in Electra:EIP7549]* |
### Execution
| Name | Value | Description |
| - | - | - |
| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload |
| `MAX_ATTESTER_SLASHINGS_ELECTRA` | `2**0` (= 1) | *[New in Electra:EIP7549]* |
| `MAX_ATTESTATIONS_ELECTRA` | `2**3` (= 8) | *[New in Electra:EIP7549]* |
| `MAX_DEPOSIT_REQUESTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload |
| `MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD` | `uint64(2**4)` (= 16)| *[New in Electra:EIP7002]* Maximum number of execution layer withdrawal requests in each payload |
| `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` | `uint64(1)` (= 1) | *[New in Electra:EIP7002]* Maximum number of execution layer consolidation requests in each payload |
### Withdrawals processing
@ -194,12 +194,12 @@ The following values are (non-configurable) constants used throughout the specif
### New containers
#### `DepositReceipt`
#### `DepositRequest`
*Note*: The container is new in EIP6110.
```python
class DepositReceipt(Container):
class DepositRequest(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
@ -227,36 +227,26 @@ class PendingPartialWithdrawal(Container):
amount: Gwei
withdrawable_epoch: Epoch
```
#### `ExecutionLayerWithdrawalRequest`
#### `WithdrawalRequest`
*Note*: The container is new in EIP7251:EIP7002.
```python
class ExecutionLayerWithdrawalRequest(Container):
class WithdrawalRequest(Container):
source_address: ExecutionAddress
validator_pubkey: BLSPubkey
amount: Gwei
```
#### `Consolidation`
#### `ConsolidationRequest`
*Note*: The container is new in EIP7251.
```python
class Consolidation(Container):
source_index: ValidatorIndex
target_index: ValidatorIndex
epoch: Epoch
```
#### `SignedConsolidation`
*Note*: The container is new in EIP7251.
```python
class SignedConsolidation(Container):
message: Consolidation
signature: BLSSignature
class ConsolidationRequest(Container):
source_address: ExecutionAddress
source_pubkey: BLSPubkey
target_pubkey: BLSPubkey
```
#### `PendingConsolidation`
@ -319,7 +309,6 @@ class BeaconBlockBody(Container):
execution_payload: ExecutionPayload # [Modified in Electra:EIP6110:EIP7002]
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
consolidations: List[SignedConsolidation, MAX_CONSOLIDATIONS] # [New in Electra:EIP7251]
```
#### `ExecutionPayload`
@ -345,9 +334,11 @@ class ExecutionPayload(Container):
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in Electra:EIP6110]
deposit_requests: List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD] # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
withdrawal_requests: List[ExecutionLayerWithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]
withdrawal_requests: List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]
# [New in Electra:EIP7251]
consolidation_requests: List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD]
```
#### `ExecutionPayloadHeader`
@ -373,8 +364,9 @@ class ExecutionPayloadHeader(Container):
withdrawals_root: Root
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_receipts_root: Root # [New in Electra:EIP6110]
deposit_requests_root: Root # [New in Electra:EIP6110]
withdrawal_requests_root: Root # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root: Root # [New in Electra:EIP7251]
```
#### `BeaconState`
@ -422,7 +414,7 @@ class BeaconState(Container):
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
deposit_receipts_start_index: uint64 # [New in Electra:EIP6110]
deposit_requests_start_index: uint64 # [New in Electra:EIP6110]
deposit_balance_to_consume: Gwei # [New in Electra:EIP7251]
exit_balance_to_consume: Gwei # [New in Electra:EIP7251]
earliest_exit_epoch: Epoch # [New in Electra:EIP7251]
@ -438,6 +430,27 @@ class BeaconState(Container):
### Predicates
#### Updated `compute_proposer_index`
```python
def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
"""
Return from ``indices`` a random index sampled by effective balance.
"""
assert len(indices) > 0
MAX_RANDOM_BYTE = 2**8 - 1
i = uint64(0)
total = uint64(len(indices))
while True:
candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
effective_balance = state.validators[candidate_index].effective_balance
# [Modified in Electra:EIP7251]
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_byte:
return candidate_index
i += 1
```
#### Updated `is_eligible_for_activation_queue`
```python
@ -798,12 +811,27 @@ def process_pending_balance_deposits(state: BeaconState) -> None:
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state)
processed_amount = 0
next_deposit_index = 0
deposits_to_postpone = []
for deposit in state.pending_balance_deposits:
if processed_amount + deposit.amount > available_for_processing:
break
increase_balance(state, deposit.index, deposit.amount)
processed_amount += deposit.amount
validator = state.validators[deposit.index]
# Validator is exiting, postpone the deposit until after withdrawable epoch
if validator.exit_epoch < FAR_FUTURE_EPOCH:
if get_current_epoch(state) <= validator.withdrawable_epoch:
deposits_to_postpone.append(deposit)
# Deposited balance will never become active. Increase balance but do not consume churn
else:
increase_balance(state, deposit.index, deposit.amount)
# Validator is not exiting, attempt to process deposit
else:
# Deposit does not fit in the churn, no more deposit processing in this epoch.
if processed_amount + deposit.amount > available_for_processing:
break
# Deposit fits in the churn, process it. Increase balance and consume churn.
else:
increase_balance(state, deposit.index, deposit.amount)
processed_amount += deposit.amount
# Regardless of how the deposit was handled, we move on in the queue.
next_deposit_index += 1
state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:]
@ -812,6 +840,8 @@ def process_pending_balance_deposits(state: BeaconState) -> None:
state.deposit_balance_to_consume = Gwei(0)
else:
state.deposit_balance_to_consume = available_for_processing - processed_amount
state.pending_balance_deposits += deposits_to_postpone
```
#### New `process_pending_consolidations`
@ -1011,8 +1041,9 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
withdrawals_root=hash_tree_root(payload.withdrawals),
blob_gas_used=payload.blob_gas_used,
excess_blob_gas=payload.excess_blob_gas,
deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in Electra:EIP6110]
deposit_requests_root=hash_tree_root(payload.deposit_requests), # [New in Electra:EIP6110]
withdrawal_requests_root=hash_tree_root(payload.withdrawal_requests), # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root=hash_tree_root(payload.consolidation_requests), # [New in Electra:EIP7251]
)
```
@ -1026,7 +1057,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# [Modified in Electra:EIP6110]
# Disable former deposit mechanism once all prior deposits are processed
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index)
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index)
if state.eth1_deposit_index < eth1_deposit_index_limit:
assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
else:
@ -1042,10 +1073,11 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.deposits, process_deposit) # [Modified in Electra:EIP7251]
for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in Electra:EIP7251]
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
for_ops(body.execution_payload.deposit_requests, process_deposit_request) # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
for_ops(body.execution_payload.withdrawal_requests, process_execution_layer_withdrawal_request)
for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) # [New in Electra:EIP6110]
for_ops(body.consolidations, process_consolidation) # [New in Electra:EIP7251]
for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request)
# [New in Electra:EIP7251]
for_ops(body.execution_payload.consolidation_requests, process_consolidation_request)
```
##### Attestations
@ -1204,16 +1236,16 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu
##### Execution layer withdrawal requests
###### New `process_execution_layer_withdrawal_request`
###### New `process_withdrawal_request`
*Note*: This function is new in Electra following EIP-7002 and EIP-7251.
```python
def process_execution_layer_withdrawal_request(
def process_withdrawal_request(
state: BeaconState,
execution_layer_withdrawal_request: ExecutionLayerWithdrawalRequest
withdrawal_request: WithdrawalRequest
) -> None:
amount = execution_layer_withdrawal_request.amount
amount = withdrawal_request.amount
is_full_exit_request = amount == FULL_EXIT_REQUEST_AMOUNT
# If partial withdrawal queue is full, only full exits are processed
@ -1222,7 +1254,7 @@ def process_execution_layer_withdrawal_request(
validator_pubkeys = [v.pubkey for v in state.validators]
# Verify pubkey exists
request_pubkey = execution_layer_withdrawal_request.validator_pubkey
request_pubkey = withdrawal_request.validator_pubkey
if request_pubkey not in validator_pubkeys:
return
index = ValidatorIndex(validator_pubkeys.index(request_pubkey))
@ -1231,7 +1263,7 @@ def process_execution_layer_withdrawal_request(
# Verify withdrawal credentials
has_correct_credential = has_execution_withdrawal_credential(validator)
is_correct_source_address = (
validator.withdrawal_credentials[12:] == execution_layer_withdrawal_request.source_address
validator.withdrawal_credentials[12:] == withdrawal_request.source_address
)
if not (has_correct_credential and is_correct_source_address):
return
@ -1271,64 +1303,83 @@ def process_execution_layer_withdrawal_request(
))
```
##### Deposit receipts
##### Deposit requests
###### New `process_deposit_receipt`
###### New `process_deposit_request`
*Note*: This function is new in Electra:EIP6110.
```python
def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None:
# Set deposit receipt start index
if state.deposit_receipts_start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX:
state.deposit_receipts_start_index = deposit_receipt.index
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None:
# Set deposit request start index
if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX:
state.deposit_requests_start_index = deposit_request.index
apply_deposit(
state=state,
pubkey=deposit_receipt.pubkey,
withdrawal_credentials=deposit_receipt.withdrawal_credentials,
amount=deposit_receipt.amount,
signature=deposit_receipt.signature,
pubkey=deposit_request.pubkey,
withdrawal_credentials=deposit_request.withdrawal_credentials,
amount=deposit_request.amount,
signature=deposit_request.signature,
)
```
##### Consolidations
##### Execution layer consolidation requests
###### New `process_consolidation`
###### New `process_consolidation_request`
```python
def process_consolidation(state: BeaconState, signed_consolidation: SignedConsolidation) -> None:
# If the pending consolidations queue is full, no consolidations are allowed in the block
assert len(state.pending_consolidations) < PENDING_CONSOLIDATIONS_LIMIT
# If there is too little available consolidation churn limit, no consolidations are allowed in the block
assert get_consolidation_churn_limit(state) > MIN_ACTIVATION_BALANCE
consolidation = signed_consolidation.message
# Verify that source != target, so a consolidation cannot be used as an exit.
assert consolidation.source_index != consolidation.target_index
def process_consolidation_request(
state: BeaconState,
consolidation_request: ConsolidationRequest
) -> None:
# If the pending consolidations queue is full, consolidation requests are ignored
if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT:
return
# If there is too little available consolidation churn limit, consolidation requests are ignored
if get_consolidation_churn_limit(state) <= MIN_ACTIVATION_BALANCE:
return
validator_pubkeys = [v.pubkey for v in state.validators]
# Verify pubkeys exists
request_source_pubkey = consolidation_request.source_pubkey
request_target_pubkey = consolidation_request.target_pubkey
if request_source_pubkey not in validator_pubkeys:
return
if request_target_pubkey not in validator_pubkeys:
return
source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey))
target_index = ValidatorIndex(validator_pubkeys.index(request_target_pubkey))
source_validator = state.validators[source_index]
target_validator = state.validators[target_index]
# Verify that source != target, so a consolidation cannot be used as an exit.
if source_index == target_index:
return
# Verify source withdrawal credentials
has_correct_credential = has_execution_withdrawal_credential(source_validator)
is_correct_source_address = (
source_validator.withdrawal_credentials[12:] == consolidation_request.source_address
)
if not (has_correct_credential and is_correct_source_address):
return
# Verify that target has execution withdrawal credentials
if not has_execution_withdrawal_credential(target_validator):
return
source_validator = state.validators[consolidation.source_index]
target_validator = state.validators[consolidation.target_index]
# Verify the source and the target are active
current_epoch = get_current_epoch(state)
assert is_active_validator(source_validator, current_epoch)
assert is_active_validator(target_validator, current_epoch)
if not is_active_validator(source_validator, current_epoch):
return
if not is_active_validator(target_validator, current_epoch):
return
# Verify exits for source and target have not been initiated
assert source_validator.exit_epoch == FAR_FUTURE_EPOCH
assert target_validator.exit_epoch == FAR_FUTURE_EPOCH
# Consolidations must specify an epoch when they become valid; they are not valid before then
assert current_epoch >= consolidation.epoch
# Verify the source and the target have Execution layer withdrawal credentials
assert has_execution_withdrawal_credential(source_validator)
assert has_execution_withdrawal_credential(target_validator)
# Verify the same withdrawal address
assert source_validator.withdrawal_credentials[12:] == target_validator.withdrawal_credentials[12:]
# Verify consolidation is signed by the source and the target
domain = compute_domain(DOMAIN_CONSOLIDATION, genesis_validators_root=state.genesis_validators_root)
signing_root = compute_signing_root(consolidation, domain)
pubkeys = [source_validator.pubkey, target_validator.pubkey]
assert bls.FastAggregateVerify(pubkeys, signing_root, signed_consolidation.signature)
if source_validator.exit_epoch != FAR_FUTURE_EPOCH:
return
if target_validator.exit_epoch != FAR_FUTURE_EPOCH:
return
# Initiate source validator exit and append pending consolidation
source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
@ -1338,8 +1389,8 @@ def process_consolidation(state: BeaconState, signed_consolidation: SignedConsol
source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
state.pending_consolidations.append(PendingConsolidation(
source_index=consolidation.source_index,
target_index=consolidation.target_index
source_index=source_index,
target_index=target_index
))
```
@ -1349,7 +1400,7 @@ def process_consolidation(state: BeaconState, signed_consolidation: SignedConsol
Modifications include:
1. Use `ELECTRA_FORK_VERSION` as the previous and current fork version.
2. Utilize the Electra `BeaconBlockBody` when constructing the initial `latest_block_header`.
3. *[New in Electra:EIP6110]* Add `deposit_receipts_start_index` variable to the genesis state initialization.
3. *[New in Electra:EIP6110]* Add `deposit_requests_start_index` variable to the genesis state initialization.
4. *[New in Electra:EIP7251]* Initialize new fields to support increasing the maximum effective balance.
```python
@ -1369,7 +1420,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
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
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in Electra:EIP6110]
deposit_requests_start_index=UNSET_DEPOSIT_REQUESTS_START_INDEX, # [New in Electra:EIP6110]
)
# Process deposits

View File

@ -90,8 +90,9 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
blob_gas_used=pre.latest_execution_payload_header.blob_gas_used,
excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas,
deposit_receipts_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002],
deposit_requests_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002]
consolidation_requests_root=Root(), # [New in Electra:EIP7251]
)
exit_epochs = [v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
@ -146,7 +147,7 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
# [New in Electra:EIP6110]
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX,
deposit_requests_start_index=UNSET_DEPOSIT_REQUESTS_START_INDEX,
# [New in Electra:EIP7251]
deposit_balance_to_consume=0,
exit_balance_to_consume=0,

View File

@ -105,7 +105,7 @@ def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Att
```python
def get_eth1_pending_deposit_count(state: BeaconState) -> uint64:
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index)
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index)
if state.eth1_deposit_index < eth1_deposit_index_limit:
return min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
else:

View File

@ -1,811 +0,0 @@
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_electra_and_later,
with_presets,
always_bls,
spec_test,
single_phase,
with_custom_state,
scaled_churn_balances_exceed_activation_exit_churn_limit,
default_activation_threshold,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.consolidations import (
run_consolidation_processing,
sign_consolidation,
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_compounding_withdrawal_credential,
)
# ***********************
# * CONSOLIDATION TESTS *
# ***********************
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
yield from run_consolidation_processing(spec, state, signed_consolidation)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_in_new_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
# Set consolidation balance to consume to some arbitrary nonzero value below the churn limit
state.consolidation_balance_to_consume = spec.EFFECTIVE_BALANCE_INCREMENT
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
# consolidation_balance_to_consume is replenished to the churn limit since we move to a new consolidation epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epochs
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
# Set some nonzero preexisting churn lower than churn limit and sufficient to process the consolidation
preexisting_churn = 2 * spec.MIN_ACTIVATION_BALANCE
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, signed_consolidation)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== preexisting_churn - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_insufficient_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the first available epoch
state.earliest_consolidation_epoch = spec.compute_activation_exit_epoch(
current_epoch
)
# Set preexisting churn lower than required to process the consolidation
preexisting_churn = spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, signed_consolidation)
# It takes one more epoch to process the consolidation due to insufficient churn
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = spec.MIN_ACTIVATION_BALANCE % preexisting_churn
assert (
state.consolidation_balance_to_consume == consolidation_churn_limit - remainder
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_compounding_credential(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_churn_limit_balance(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
source_validator = state.validators[source_index]
source_validator.effective_balance = consolidation_churn_limit
# Churn limit increases due to higher total balance
updated_consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
# validator's effective balance fits into the churn, exit as soon as possible
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== updated_consolidation_churn_limit - consolidation_churn_limit
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_larger_than_churn_limit(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set source balance higher than consolidation churn limit
state.validators[source_index].effective_balance = 2 * consolidation_churn_limit
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
# Consolidation churn limit increases due to higher total balance
new_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % new_churn_limit
expected_balance = new_churn_limit - remainder
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == expected_balance
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_through_two_churn_epochs(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
# Set source balance higher than consolidation churn limit
state.validators[source_index].effective_balance = 3 * consolidation_churn_limit
new_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % new_churn_limit
expected_balance = new_churn_limit - remainder
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
# when exiting a multiple of the churn limit greater than 1, an extra exit epoch is added
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 2
assert state.validators[0].exit_epoch == expected_exit_epoch
# since the earliest exit epoch moves to a new one, consolidation balance is back to full
assert state.consolidation_balance_to_consume == expected_balance
# Failing tests
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_source_equals_target(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
# Set withdrawal credentials to eth1
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=validator_index,
target_index=validator_index,
),
validator_privkey,
validator_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_exceed_pending_consolidations_limit(spec, state):
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
] * spec.PENDING_CONSOLIDATIONS_LIMIT
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_not_enough_consolidation_churn_available(spec, state):
state.validators = state.validators[0:2]
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
]
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_exited_source(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# exit source
spec.initiate_validator_exit(state, 0)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_exited_target(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# exit target
spec.initiate_validator_exit(state, 1)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_inactive_source(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# set source validator as not yet activated
state.validators[0].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_inactive_target(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# set target validator as not yet activated
state.validators[1].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_no_execution_withdrawal_credential(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_different_credentials(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Set source and target withdrawal credentials to different eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1, address=b"\x10" * 20)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@always_bls
def test_invalid_source_signature(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Change the pubkey of the source validator, invalidating its signature
state.validators[0].pubkey = state.validators[1].pubkey
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@always_bls
def test_invalid_target_signature(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Change the pubkey of the target validator, invalidating its signature
state.validators[1].pubkey = state.validators[2].pubkey
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_invalid_before_specified_epoch(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
# set epoch=current_epoch + 1, so it's too early to process it
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch + 1, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)

View File

@ -0,0 +1,809 @@
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_electra_and_later,
with_presets,
spec_test,
single_phase,
with_custom_state,
scaled_churn_balances_exceed_activation_exit_churn_limit,
default_activation_threshold,
spec_state_test,
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_compounding_withdrawal_credential,
)
# ***********************
# * CONSOLIDATION TESTS *
# ***********************
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
yield from run_consolidation_processing(spec, state, consolidation)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_in_new_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
# Set consolidation balance to consume to some arbitrary nonzero value below the churn limit
state.consolidation_balance_to_consume = spec.EFFECTIVE_BALANCE_INCREMENT
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(spec, state, consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
# consolidation_balance_to_consume is replenished to the churn limit since we move to a new consolidation epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epochs
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
# Set some nonzero preexisting churn lower than churn limit and sufficient to process the consolidation
preexisting_churn = 2 * spec.MIN_ACTIVATION_BALANCE
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, consolidation)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== preexisting_churn - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_insufficient_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set earliest consolidation epoch to the first available epoch
state.earliest_consolidation_epoch = spec.compute_activation_exit_epoch(
current_epoch
)
# Set preexisting churn lower than required to process the consolidation
preexisting_churn = spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, consolidation)
# It takes one more epoch to process the consolidation due to insufficient churn
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = spec.MIN_ACTIVATION_BALANCE % preexisting_churn
assert (
state.consolidation_balance_to_consume == consolidation_churn_limit - remainder
)
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_compounding_credentials(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_compounding_withdrawal_credential(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_compounding_withdrawal_credential(spec, state, target_index)
# Set the consolidation balance to consume equal to churn limit
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
state.consolidation_balance_to_consume = consolidation_churn_limit
yield from run_consolidation_processing(spec, state, consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_churn_limit_balance(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set source effective balance to consolidation churn limit
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
state.validators[source_index].effective_balance = consolidation_churn_limit
# Churn limit increases due to higher total balance
updated_consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
yield from run_consolidation_processing(spec, state, consolidation)
# validator's effective balance fits into the churn, exit as soon as possible
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== updated_consolidation_churn_limit - consolidation_churn_limit
)
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_larger_than_churn_limit(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set source effective balance to 2 * consolidation churn limit
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
state.validators[source_index].effective_balance = 2 * consolidation_churn_limit
# Consolidation churn limit increases due to higher total balance
updated_consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % updated_consolidation_churn_limit
expected_balance = updated_consolidation_churn_limit - remainder
yield from run_consolidation_processing(spec, state, consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == expected_balance
# Check exit epoch
assert state.validators[source_index].exit_epoch == expected_exit_epoch
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_through_two_churn_epochs(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with source address
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
# Set target to eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set source balance higher to 3 * consolidation churn limit
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
state.validators[source_index].effective_balance = 3 * consolidation_churn_limit
new_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % new_churn_limit
expected_balance = new_churn_limit - remainder
yield from run_consolidation_processing(spec, state, consolidation)
# when exiting a multiple of the churn limit greater than 1, an extra exit epoch is added
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 2
assert state.validators[0].exit_epoch == expected_exit_epoch
# since the earliest exit epoch moves to a new one, consolidation balance is back to full
assert state.consolidation_balance_to_consume == expected_balance
# Failing tests
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_source_equals_target(spec, state):
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set source to eth1 credentials
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation from source to source
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[source_index].pubkey,
)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_exceed_pending_consolidations_limit(spec, state):
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
] * spec.PENDING_CONSOLIDATIONS_LIMIT
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@with_electra_and_later
@spec_state_test
@single_phase
def test_incorrect_not_enough_consolidation_churn_available(spec, state):
state.validators = state.validators[0:2]
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
]
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_exited_source(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# exit source
spec.initiate_validator_exit(state, source_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_exited_target(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# exit target
spec.initiate_validator_exit(state, 1)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_inactive_source(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# set source validator as not yet activated
state.validators[source_index].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_inactive_target(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# set target validator as not yet activated
state.validators[1].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_no_source_execution_withdrawal_credential(spec, state):
# Set up a correct consolidation, but source does not have
# an execution withdrawal credential
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_no_target_execution_withdrawal_credential(spec, state):
# Set up a correct consolidation, but target does not have
# an execution withdrawal credential
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_incorrect_source_address(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with different source address
consolidation = spec.ConsolidationRequest(
source_address=b"\x33" * 20,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_unknown_source_pubkey(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with different source pubkey
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=b"\x00" * 48,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
@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,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_incorrect_unknown_target_pubkey(spec, state):
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address
)
# Make consolidation with different target pubkey
consolidation = spec.ConsolidationRequest(
source_address=b"\x33" * 20,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=b"\x00" * 48,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
yield from run_consolidation_processing(
spec, state, consolidation, success=False
)
def run_consolidation_processing(spec, state, consolidation, success=True):
"""
Run ``process_consolidation``, yielding:
- pre-state ('pre')
- consolidation_request ('consolidation_request')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
if success:
validator_pubkeys = [v.pubkey for v in state.validators]
source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey))
target_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.target_pubkey))
source_validator = state.validators[source_index]
target_validator = state.validators[target_index]
pre_exit_epoch_source = source_validator.exit_epoch
pre_exit_epoch_target = target_validator.exit_epoch
pre_pending_consolidations = state.pending_consolidations.copy()
else:
pre_state = state.copy()
yield 'pre', state
yield 'consolidation_request', consolidation
spec.process_consolidation_request(state, consolidation)
yield 'post', state
if success:
# Check source and target have execution credentials
assert spec.has_execution_withdrawal_credential(source_validator)
assert spec.has_execution_withdrawal_credential(target_validator)
# Check source address in the consolidation fits the withdrawal credentials
assert source_validator.withdrawal_credentials[12:] == consolidation.source_address
# Check source and target are not the same
assert source_index != target_index
# Check source and target were not exiting
assert pre_exit_epoch_source == spec.FAR_FUTURE_EPOCH
assert pre_exit_epoch_target == spec.FAR_FUTURE_EPOCH
# Check source is now exiting
assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH
# Check that the exit epoch matches earliest_consolidation_epoch
assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch
# Check that the correct consolidation has been appended
expected_new_pending_consolidation = spec.PendingConsolidation(
source_index=source_index,
target_index=target_index,
)
assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation]
else:
assert pre_state == state

View File

@ -1,8 +1,8 @@
from eth2spec.test.context import spec_state_test, always_bls, with_electra_and_later
from eth2spec.test.helpers.deposits import (
prepare_deposit_receipt,
run_deposit_receipt_processing,
run_deposit_receipt_processing_with_specific_fork_version
prepare_deposit_request,
run_deposit_request_processing,
run_deposit_request_processing_with_specific_fork_version
)
from eth2spec.test.helpers.state import next_epoch_via_block
from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable
@ -15,9 +15,9 @@ def test_new_deposit_under_max(spec, state):
validator_index = len(state.validators)
# effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement.
amount = spec.MAX_EFFECTIVE_BALANCE - 1
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -27,9 +27,9 @@ def test_new_deposit_max(spec, state):
validator_index = len(state.validators)
# effective balance will be exactly the same as balance.
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -39,9 +39,9 @@ def test_new_deposit_over_max(spec, state):
validator_index = len(state.validators)
# just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing
amount = spec.MAX_EFFECTIVE_BALANCE + 1
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -55,7 +55,7 @@ def test_new_deposit_eth1_withdrawal_credentials(spec, state):
+ b'\x59' * 20 # a 20-byte eth1 address
)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(
deposit_request = prepare_deposit_request(
spec,
validator_index,
amount,
@ -63,7 +63,7 @@ def test_new_deposit_eth1_withdrawal_credentials(spec, state):
signed=True,
)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -76,7 +76,7 @@ def test_new_deposit_non_versioned_withdrawal_credentials(spec, state):
+ b'\x02' * 31 # Garabage bytes
)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(
deposit_request = prepare_deposit_request(
spec,
validator_index,
amount,
@ -84,7 +84,7 @@ def test_new_deposit_non_versioned_withdrawal_credentials(spec, state):
signed=True,
)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -95,8 +95,8 @@ def test_correct_sig_but_forked_state(spec, state):
amount = spec.MAX_EFFECTIVE_BALANCE
# deposits will always be valid, regardless of the current fork
state.fork.current_version = spec.Version('0x1234abcd')
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
deposit_request = prepare_deposit_request(spec, validator_index, amount, signed=True)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -106,8 +106,8 @@ def test_incorrect_sig_new_deposit(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, effective=False)
deposit_request = prepare_deposit_request(spec, validator_index, amount)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index, effective=False)
@with_electra_and_later
@ -115,12 +115,12 @@ def test_incorrect_sig_new_deposit(spec, state):
def test_top_up__max_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, 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_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
@ -132,14 +132,14 @@ def test_top_up__max_effective_balance(spec, state):
def test_top_up__less_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, 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_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
@ -152,14 +152,14 @@ def test_top_up__less_effective_balance(spec, state):
def test_top_up__zero_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
deposit_request = prepare_deposit_request(spec, 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_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount
@ -173,10 +173,10 @@ def test_top_up__zero_balance(spec, state):
def test_incorrect_sig_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount)
deposit_request = prepare_deposit_request(spec, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -185,7 +185,7 @@ 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_receipt = prepare_deposit_receipt(
deposit_request = prepare_deposit_request(
spec,
validator_index,
amount,
@ -193,7 +193,7 @@ def test_incorrect_withdrawal_credentials_top_up(spec, state):
)
# inconsistent withdrawal credentials, in top-ups, are allowed!
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -205,9 +205,9 @@ def test_key_validate_invalid_subgroup(spec, state):
# All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey = b'\x00' * 48
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -221,9 +221,9 @@ def test_key_validate_invalid_decompression(spec, state):
pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
pubkey = bytes.fromhex(pubkey_hex)
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
@with_electra_and_later
@ -235,7 +235,7 @@ def test_ineffective_deposit_with_previous_fork_version(spec, state):
# NOTE: it was effective in Altair.
assert state.fork.previous_version != state.fork.current_version
yield from run_deposit_receipt_processing_with_specific_fork_version(
yield from run_deposit_request_processing_with_specific_fork_version(
spec,
state,
fork_version=state.fork.previous_version,
@ -249,7 +249,7 @@ def test_ineffective_deposit_with_previous_fork_version(spec, state):
def test_effective_deposit_with_genesis_fork_version(spec, state):
assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version)
yield from run_deposit_receipt_processing_with_specific_fork_version(
yield from run_deposit_request_processing_with_specific_fork_version(
spec,
state,
fork_version=spec.config.GENESIS_FORK_VERSION,
@ -272,9 +272,9 @@ def test_success_top_up_to_withdrawn_validator(spec, state):
# Make a top-up balance to validator
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, len(state.validators), signed=True)
deposit_request = prepare_deposit_request(spec, validator_index, amount, len(state.validators), signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
yield from run_deposit_request_processing(spec, state, deposit_request, validator_index)
deposits_len = len(state.pending_balance_deposits)
assert state.pending_balance_deposits[deposits_len - 1].amount == amount

View File

@ -29,14 +29,14 @@ def test_basic_withdrawal_request(spec, state):
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request
)
@ -51,14 +51,14 @@ def test_basic_withdrawal_request_with_compounding_credentials(spec, state):
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request
)
@ -74,7 +74,7 @@ def test_basic_withdrawal_request_with_full_partial_withdrawal_queue(spec, state
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
@ -89,10 +89,10 @@ def test_basic_withdrawal_request_with_full_partial_withdrawal_queue(spec, state
] * spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
# Exit should still be processed
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
@ -113,14 +113,14 @@ def test_incorrect_source_address(spec, state):
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=incorrect_address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -142,14 +142,14 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
spec.BLS_WITHDRAWAL_PREFIX
+ state.validators[validator_index].withdrawal_credentials[1:]
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -168,14 +168,14 @@ def test_on_withdrawal_request_initiated_validator(spec, state):
)
# Initiate exit earlier
spec.initiate_validator_exit(state, validator_index)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -189,7 +189,7 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
@ -200,8 +200,8 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
+ spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -221,16 +221,16 @@ def test_basic_partial_withdrawal_request(spec, state):
state.balances[validator_index] += amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -253,16 +253,16 @@ def test_basic_partial_withdrawal_request_higher_excess_balance(spec, state):
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -286,16 +286,16 @@ def test_basic_partial_withdrawal_request_lower_than_excess_balance(spec, state)
state.balances[validator_index] += excess_balance
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -316,7 +316,7 @@ def test_partial_withdrawal_request_with_pending_withdrawals(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -331,10 +331,10 @@ def test_partial_withdrawal_request_with_pending_withdrawals(spec, state):
# Set balance so that the validator still has excess balance even with the pending withdrawals
state.balances[validator_index] += 3 * amount
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -357,7 +357,7 @@ def test_partial_withdrawal_request_with_pending_withdrawals_and_high_amount(
amount = spec.UINT64_MAX
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -376,10 +376,10 @@ def test_partial_withdrawal_request_with_pending_withdrawals_and_high_amount(
# Set balance so that the validator still has excess balance even with the pending withdrawals
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
@ -399,7 +399,7 @@ def test_partial_withdrawal_request_with_high_balance(spec, state):
)
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -407,10 +407,10 @@ def test_partial_withdrawal_request_with_high_balance(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -435,16 +435,16 @@ def test_partial_withdrawal_request_with_high_amount(spec, state):
state.balances[validator_index] += 1
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -467,16 +467,16 @@ def test_partial_withdrawal_request_with_low_amount(spec, state):
state.balances[validator_index] += amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
)
# Check that the assigned exit epoch is correct
@ -501,7 +501,7 @@ def test_partial_withdrawal_queue_full(spec, state):
# Ensure that the validator has sufficient excess balance
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -514,8 +514,8 @@ def test_partial_withdrawal_queue_full(spec, state):
state.pending_partial_withdrawals = [
partial_withdrawal
] * spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -534,16 +534,16 @@ def test_no_compounding_credentials(spec, state):
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
success=False,
)
@ -559,14 +559,14 @@ def test_no_excess_balance(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -583,7 +583,7 @@ def test_pending_withdrawals_consume_all_excess_balance(spec, state):
state.balances[validator_index] += 10 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -595,8 +595,8 @@ def test_pending_withdrawals_consume_all_excess_balance(spec, state):
)
state.pending_partial_withdrawals = [partial_withdrawal] * 10
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -615,16 +615,16 @@ def test_insufficient_effective_balance(spec, state):
].effective_balance -= spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
yield from run_withdrawal_request_processing(
spec,
state,
execution_layer_withdrawal_request,
withdrawal_request,
success=False,
)
@ -644,14 +644,14 @@ def test_partial_withdrawal_incorrect_source_address(spec, state):
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=incorrect_address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -673,14 +673,14 @@ def test_partial_withdrawal_incorrect_withdrawal_credential_prefix(spec, state):
spec.BLS_WITHDRAWAL_PREFIX
+ state.validators[validator_index].withdrawal_credentials[1:]
)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -699,14 +699,14 @@ def test_partial_withdrawal_on_exit_initiated_validator(spec, state):
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
# Initiate exit earlier
spec.initiate_validator_exit(state, validator_index)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -722,7 +722,7 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
amount = spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
@ -733,8 +733,8 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
+ spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, success=False
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request, success=False
)
@ -743,28 +743,28 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
#
def run_execution_layer_withdrawal_request_processing(
spec, state, execution_layer_withdrawal_request, valid=True, success=True
def run_withdrawal_request_processing(
spec, state, withdrawal_request, valid=True, success=True
):
"""
Run ``process_execution_layer_withdrawal_request``, yielding:
Run ``process_withdrawal_request``, yielding:
- pre-state ('pre')
- execution_layer_withdrawal_request ('execution_layer_withdrawal_request')
- withdrawal_request ('withdrawal_request')
- 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_withdrawal_request.validator_pubkey
state, withdrawal_request.validator_pubkey
)
yield "pre", state
yield "execution_layer_withdrawal_request", execution_layer_withdrawal_request
yield "withdrawal_request", withdrawal_request
if not valid:
expect_assertion_error(
lambda: spec.process_execution_layer_withdrawal_request(
state, execution_layer_withdrawal_request
lambda: spec.process_withdrawal_request(
state, withdrawal_request
)
)
yield "post", None
@ -776,11 +776,11 @@ def run_execution_layer_withdrawal_request_processing(
pre_effective_balance = state.validators[validator_index].effective_balance
pre_state = state.copy()
expected_amount_to_withdraw = compute_amount_to_withdraw(
spec, state, validator_index, execution_layer_withdrawal_request.amount
spec, state, validator_index, withdrawal_request.amount
)
spec.process_execution_layer_withdrawal_request(
state, execution_layer_withdrawal_request
spec.process_withdrawal_request(
state, withdrawal_request
)
yield "post", state
@ -794,7 +794,7 @@ def run_execution_layer_withdrawal_request_processing(
state.validators[validator_index].effective_balance == pre_effective_balance
)
# Full exit request
if execution_layer_withdrawal_request.amount == spec.FULL_EXIT_REQUEST_AMOUNT:
if withdrawal_request.amount == spec.FULL_EXIT_REQUEST_AMOUNT:
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
assert spec.get_pending_balance_to_withdraw(state, validator_index) == 0

View File

@ -5,6 +5,10 @@ from eth2spec.test.context import (
)
def run_process_pending_balance_deposits(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
@with_electra_and_later
@spec_state_test
def test_pending_deposit_min_activation_balance(spec, state):
@ -14,9 +18,9 @@ def test_pending_deposit_min_activation_balance(spec, state):
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
@ -32,9 +36,9 @@ def test_pending_deposit_balance_equal_churn(spec, state):
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
assert state.balances[index] == pre_balance + amount
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
@ -49,9 +53,9 @@ def test_pending_deposit_balance_above_churn(spec, state):
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
# deposit was above churn, balance hasn't changed
assert state.balances[index] == pre_balance
# deposit balance to consume is the full churn limit
@ -74,9 +78,9 @@ def test_pending_deposit_preexisting_churn(spec, state):
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
# balance was deposited correctly
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
@ -96,9 +100,9 @@ def test_multiple_pending_deposits_below_churn(spec, state):
spec.PendingBalanceDeposit(index=1, amount=amount)
)
pre_balances = state.balances.copy()
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
for i in [0, 1]:
assert state.balances[i] == pre_balances[i] + amount
# No leftover deposit balance to consume when there are no deposits left to process
@ -116,9 +120,9 @@ def test_multiple_pending_deposits_above_churn(spec, state):
spec.PendingBalanceDeposit(index=i, amount=amount)
)
pre_balances = state.balances.copy()
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
yield from run_process_pending_balance_deposits(spec, state)
# First two deposits are processed, third is not because above churn
for i in [0, 1]:
assert state.balances[i] == pre_balances[i] + amount
@ -132,3 +136,143 @@ def test_multiple_pending_deposits_above_churn(spec, state):
assert state.pending_balance_deposits == [
spec.PendingBalanceDeposit(index=2, amount=amount)
]
@with_electra_and_later
@spec_state_test
def test_skipped_deposit_exiting_validator(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
pre_balance = state.balances[index]
# Initiate the validator's exit
spec.initiate_validator_exit(state, index)
yield from run_process_pending_balance_deposits(spec, state)
# Deposit is skipped because validator is exiting
assert state.balances[index] == pre_balance
# All deposits either processed or postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# The deposit is still in the queue
assert state.pending_balance_deposits == pre_pending_balance_deposits
@with_electra_and_later
@spec_state_test
def test_multiple_skipped_deposits_exiting_validators(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
for i in [0, 1, 2]:
# Append pending deposit for validator i
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
# Initiate the exit of validator i
spec.initiate_validator_exit(state, i)
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
pre_balances = state.balances.copy()
yield from run_process_pending_balance_deposits(spec, state)
# All deposits are postponed, no balance changes
assert state.balances == pre_balances
# All deposits are postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# All deposits still in the queue, in the same order
assert state.pending_balance_deposits == pre_pending_balance_deposits
@with_electra_and_later
@spec_state_test
def test_multiple_pending_one_skipped(spec, state):
amount = spec.EFFECTIVE_BALANCE_INCREMENT
for i in [0, 1, 2]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
pre_balances = state.balances.copy()
# Initiate the second validator's exit
spec.initiate_validator_exit(state, 1)
yield from run_process_pending_balance_deposits(spec, state)
# First and last deposit are processed, second is not because of exiting
for i in [0, 2]:
assert state.balances[i] == pre_balances[i] + amount
assert state.balances[1] == pre_balances[1]
# All deposits either processed or postponed, no leftover deposit balance to consume
assert state.deposit_balance_to_consume == 0
# second deposit is still in the queue
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]
@with_electra_and_later
@spec_state_test
def test_mixture_of_skipped_and_above_churn(spec, state):
amount01 = spec.EFFECTIVE_BALANCE_INCREMENT
amount2 = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
# First two validators have small deposit, third validators a large one
for i in [0, 1]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount01))
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=2, amount=amount2))
pre_balances = state.balances.copy()
# Initiate the second validator's exit
spec.initiate_validator_exit(state, 1)
yield from run_process_pending_balance_deposits(spec, state)
# First deposit is processed
assert state.balances[0] == pre_balances[0] + amount01
# Second deposit is postponed, third is above churn
for i in [1, 2]:
assert state.balances[i] == pre_balances[i]
# First deposit consumes some deposit balance
# Deposit balance to consume is not reset because third deposit is not processed
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) - amount01
# second and third deposit still in the queue, but second is appended at the end
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=2, amount=amount2),
spec.PendingBalanceDeposit(index=1, amount=amount01)]
@with_electra_and_later
@spec_state_test
def test_processing_deposit_of_withdrawable_validator(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
pre_balance = state.balances[index]
# Initiate the validator's exit
spec.initiate_validator_exit(state, index)
# Set epoch to withdrawable epoch + 1 to allow processing of the deposit
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[index].withdrawable_epoch + 1)
yield from run_process_pending_balance_deposits(spec, state)
# Deposit is correctly processed
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
@with_electra_and_later
@spec_state_test
def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, state):
amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
for i in [0, 1]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
pre_balances = state.balances.copy()
# Initiate the first validator's exit
spec.initiate_validator_exit(state, 0)
# Set epoch to withdrawable epoch + 1 to allow processing of the deposit
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[0].withdrawable_epoch + 1)
# Don't use run_epoch_processing_with to avoid penalties being applied
yield 'pre', state
spec.process_pending_balance_deposits(state)
yield 'post', state
# First deposit is processed though above churn limit, because validator is withdrawable
assert state.balances[0] == pre_balances[0] + amount
# Second deposit is not processed because above churn
assert state.balances[1] == pre_balances[1]
# Second deposit is not processed, so there's leftover deposit balance to consume.
# First deposit does not consume any.
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state)
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]

View File

@ -36,12 +36,12 @@ def test_basic_el_withdrawal_request(spec, state):
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
)
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.withdrawal_requests = [execution_layer_withdrawal_request]
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
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)
@ -73,11 +73,11 @@ def test_basic_btec_and_el_withdrawal_request_in_same_block(spec, state):
block.body.bls_to_execution_changes = [signed_address_change]
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
)
block.body.execution_payload.withdrawal_requests = [execution_layer_withdrawal_request]
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
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)
@ -125,12 +125,12 @@ def test_basic_btec_before_el_withdrawal_request(spec, state):
# block_2 contains an EL-Exit operation of the given validator
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
)
block_2 = build_empty_block_for_next_slot(spec, state)
block_2.body.execution_payload.withdrawal_requests = [execution_layer_withdrawal_request]
block_2.body.execution_payload.withdrawal_requests = [withdrawal_request]
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)
@ -157,13 +157,13 @@ def test_cl_exit_and_el_withdrawal_request_in_same_block(spec, state):
signed_voluntary_exits = prepare_signed_exits(spec, state, indices=[validator_index])
# EL-Exit
validator_pubkey = state.validators[validator_index].pubkey
execution_layer_withdrawal_request = spec.ExecutionLayerWithdrawalRequest(
withdrawal_request = spec.WithdrawalRequest(
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.withdrawal_requests = [execution_layer_withdrawal_request]
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
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)

View File

@ -9,7 +9,7 @@ from eth2spec.test.context import (
from eth2spec.test.helpers.deposits import (
build_deposit_data,
deposit_from_context,
prepare_deposit_receipt,
prepare_deposit_request,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_block_hash,
@ -38,8 +38,8 @@ def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True)
# Check that deposits are applied
if valid:
expected_pubkeys = [d.data.pubkey for d in block.body.deposits]
deposit_receipts = block.body.execution_payload.deposit_receipts
expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_receipts if (d.pubkey not in top_up_keys)]
deposit_requests = block.body.execution_payload.deposit_requests
expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_requests if (d.pubkey not in top_up_keys)]
actual_pubkeys = [v.pubkey for v in state.validators[len(state.validators) - len(expected_pubkeys):]]
assert actual_pubkeys == expected_pubkeys
@ -48,12 +48,12 @@ def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True)
def prepare_state_and_block(spec,
state,
deposit_cnt,
deposit_receipt_cnt,
first_deposit_receipt_index=0,
deposit_receipts_start_index=None,
deposit_request_cnt,
first_deposit_request_index=0,
deposit_requests_start_index=None,
eth1_data_deposit_count=None):
deposits = []
deposit_receipts = []
deposit_requests = []
keypair_index = len(state.validators)
# Prepare deposits
@ -83,26 +83,26 @@ def prepare_state_and_block(spec,
deposit_count=eth1_data_deposit_count,
block_hash=state.eth1_data.block_hash)
# Prepare deposit receipts
for offset in range(deposit_receipt_cnt):
deposit_receipt = prepare_deposit_receipt(spec,
# Prepare deposit requests
for offset in range(deposit_request_cnt):
deposit_request = prepare_deposit_request(spec,
keypair_index,
# use max effective balance
spec.MAX_EFFECTIVE_BALANCE,
first_deposit_receipt_index + offset,
first_deposit_request_index + offset,
signed=True)
deposit_receipts.append(deposit_receipt)
deposit_requests.append(deposit_request)
keypair_index += 1
# Set start index if defined
if deposit_receipts_start_index:
state.deposit_receipts_start_index = deposit_receipts_start_index
if deposit_requests_start_index:
state.deposit_requests_start_index = deposit_requests_start_index
block = build_empty_block_for_next_slot(spec, state)
# Assign deposits and deposit receipts
# Assign deposits and deposit requests
block.body.deposits = deposits
block.body.execution_payload.deposit_receipts = deposit_receipts
block.body.execution_payload.deposit_requests = deposit_requests
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
return state, block
@ -111,27 +111,27 @@ def prepare_state_and_block(spec,
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__start_index_is_set(spec, state):
# 0 deposits, 2 deposit receipts, unset deposit_receipts_start_index
# 0 deposits, 2 deposit requests, unset deposit_requests_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=0,
deposit_receipt_cnt=2,
first_deposit_receipt_index=state.eth1_data.deposit_count + 11)
deposit_request_cnt=2,
first_deposit_request_index=state.eth1_data.deposit_count + 11)
yield from run_deposit_transition_block(spec, state, block)
# deposit_receipts_start_index must be set to the index of the first receipt
assert state.deposit_receipts_start_index == block.body.execution_payload.deposit_receipts[0].index
# deposit_requests_start_index must be set to the index of the first request
assert state.deposit_requests_start_index == block.body.execution_payload.deposit_requests[0].index
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__process_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index
# 3 deposits, 1 deposit request, state.eth1_data.deposit_count < state.deposit_requests_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7)
deposit_request_cnt=1,
first_deposit_request_index=11,
deposit_requests_start_index=7)
yield from run_deposit_transition_block(spec, state, block)
@ -139,13 +139,13 @@ def test_deposit_transition__process_eth1_deposits(spec, state):
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__process_max_eth1_deposits(spec, state):
# spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index
# state.deposit_receipts_start_index == spec.MAX_DEPOSITS
# spec.MAX_DEPOSITS deposits, 1 deposit request, state.eth1_data.deposit_count > state.deposit_requests_start_index
# state.deposit_requests_start_index == spec.MAX_DEPOSITS
state, block = prepare_state_and_block(spec, state,
deposit_cnt=spec.MAX_DEPOSITS,
deposit_receipt_cnt=1,
first_deposit_receipt_index=spec.MAX_DEPOSITS + 1,
deposit_receipts_start_index=spec.MAX_DEPOSITS,
deposit_request_cnt=1,
first_deposit_request_index=spec.MAX_DEPOSITS + 1,
deposit_requests_start_index=spec.MAX_DEPOSITS,
eth1_data_deposit_count=23)
yield from run_deposit_transition_block(spec, state, block)
@ -154,12 +154,12 @@ def test_deposit_transition__process_max_eth1_deposits(spec, state):
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__process_eth1_deposits_up_to_start_index(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count == state.deposit_receipts_start_index
# 3 deposits, 1 deposit request, state.eth1_data.deposit_count == state.deposit_requests_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=7,
deposit_receipts_start_index=3)
deposit_request_cnt=1,
first_deposit_request_index=7,
deposit_requests_start_index=3)
yield from run_deposit_transition_block(spec, state, block)
@ -167,12 +167,12 @@ def test_deposit_transition__process_eth1_deposits_up_to_start_index(spec, state
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__invalid_not_enough_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index
# 3 deposits, 1 deposit request, state.eth1_data.deposit_count < state.deposit_requests_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=29,
deposit_receipts_start_index=23,
deposit_request_cnt=1,
first_deposit_request_index=29,
deposit_requests_start_index=23,
eth1_data_deposit_count=17)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@ -181,12 +181,12 @@ def test_deposit_transition__invalid_not_enough_eth1_deposits(spec, state):
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__invalid_too_many_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.eth1_data_index
# 3 deposits, 1 deposit request, state.eth1_data.deposit_count < state.eth1_data_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7,
deposit_request_cnt=1,
first_deposit_request_index=11,
deposit_requests_start_index=7,
eth1_data_deposit_count=2)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@ -195,13 +195,13 @@ def test_deposit_transition__invalid_too_many_eth1_deposits(spec, state):
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__invalid_eth1_deposits_overlap_in_protocol_deposits(spec, state):
# spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index
# state.deposit_receipts_start_index == spec.MAX_DEPOSITS - 1
# spec.MAX_DEPOSITS deposits, 1 deposit request, state.eth1_data.deposit_count > state.deposit_requests_start_index
# state.deposit_requests_start_index == spec.MAX_DEPOSITS - 1
state, block = prepare_state_and_block(spec, state,
deposit_cnt=spec.MAX_DEPOSITS,
deposit_receipt_cnt=1,
first_deposit_receipt_index=spec.MAX_DEPOSITS,
deposit_receipts_start_index=spec.MAX_DEPOSITS - 1,
deposit_request_cnt=1,
first_deposit_request_index=spec.MAX_DEPOSITS,
deposit_requests_start_index=spec.MAX_DEPOSITS - 1,
eth1_data_deposit_count=23)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@ -210,16 +210,16 @@ def test_deposit_transition__invalid_eth1_deposits_overlap_in_protocol_deposits(
@with_phases([ELECTRA])
@spec_state_test
def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
# 1 deposit, 1 deposit receipt that top ups deposited validator
# 1 deposit, 1 deposit request that top ups deposited validator
state, block = prepare_state_and_block(spec, state,
deposit_cnt=1,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7)
deposit_request_cnt=1,
first_deposit_request_index=11,
deposit_requests_start_index=7)
# Artificially assign deposit's pubkey to a deposit receipt of the same block
# Artificially assign deposit's pubkey to a deposit request of the same block
top_up_keys = [block.body.deposits[0].data.pubkey]
block.body.execution_payload.deposit_receipts[0].pubkey = top_up_keys[0]
block.body.execution_payload.deposit_requests[0].pubkey = top_up_keys[0]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
pre_pending_deposits = len(state.pending_balance_deposits)
@ -229,5 +229,5 @@ def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
# Check the top up
assert len(state.pending_balance_deposits) == pre_pending_deposits + 2
assert state.pending_balance_deposits[pre_pending_deposits].amount == block.body.deposits[0].data.amount
amount_from_deposit = block.body.execution_payload.deposit_receipts[0].amount
amount_from_deposit = block.body.execution_payload.deposit_requests[0].amount
assert state.pending_balance_deposits[pre_pending_deposits + 1].amount == amount_from_deposit

View File

@ -1,61 +0,0 @@
from eth2spec.utils import bls
from eth2spec.test.context import expect_assertion_error
from eth2spec.test.helpers.keys import privkeys
def prepare_signed_consolidations(spec, state, index_pairs, fork_version=None):
def create_signed_consolidation(source_index, target_index):
consolidation = spec.Consolidation(
epoch=spec.get_current_epoch(state),
source_index=source_index,
target_index=target_index,
)
return sign_consolidation(spec, state, consolidation, privkeys[source_index], privkeys[target_index],
fork_version=fork_version)
return [create_signed_consolidation(source_index, target_index) for (source_index, target_index) in index_pairs]
def sign_consolidation(spec, state, consolidation, source_privkey, target_privkey, fork_version=None):
domain = spec.compute_domain(spec.DOMAIN_CONSOLIDATION, genesis_validators_root=state.genesis_validators_root)
signing_root = spec.compute_signing_root(consolidation, domain)
return spec.SignedConsolidation(
message=consolidation,
signature=bls.Aggregate([bls.Sign(source_privkey, signing_root), bls.Sign(target_privkey, signing_root)])
)
def run_consolidation_processing(spec, state, signed_consolidation, valid=True):
"""
Run ``process_consolidation``, yielding:
- pre-state ('pre')
- consolidation ('consolidation')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
source_validator = state.validators[signed_consolidation.message.source_index]
target_validator = state.validators[signed_consolidation.message.target_index]
yield 'pre', state
yield 'consolidation', signed_consolidation
if not valid:
expect_assertion_error(lambda: spec.process_consolidation(state, signed_consolidation))
yield 'post', None
return
pre_exit_epoch = source_validator.exit_epoch
spec.process_consolidation(state, signed_consolidation)
yield 'post', state
assert source_validator.withdrawal_credentials[1:] == target_validator.withdrawal_credentials[1:]
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validators[signed_consolidation.message.source_index].exit_epoch < spec.FAR_FUTURE_EPOCH
assert state.validators[signed_consolidation.message.source_index].exit_epoch == state.earliest_consolidation_epoch
assert state.pending_consolidations[len(state.pending_consolidations) - 1] == spec.PendingConsolidation(
source_index=signed_consolidation.message.source_index,
target_index=signed_consolidation.message.target_index
)

View File

@ -171,7 +171,7 @@ def prepare_state_and_deposit(spec, state, validator_index, amount,
return deposit
def build_deposit_receipt(spec,
def build_deposit_request(spec,
index,
pubkey,
privkey,
@ -179,7 +179,7 @@ def build_deposit_receipt(spec,
withdrawal_credentials,
signed):
deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, signed=signed)
return spec.DepositReceipt(
return spec.DepositRequest(
pubkey=deposit_data.pubkey,
withdrawal_credentials=deposit_data.withdrawal_credentials,
amount=deposit_data.amount,
@ -187,14 +187,14 @@ def build_deposit_receipt(spec,
index=index)
def prepare_deposit_receipt(spec, validator_index, amount,
def prepare_deposit_request(spec, validator_index, amount,
index=None,
pubkey=None,
privkey=None,
withdrawal_credentials=None,
signed=False):
"""
Create a deposit receipt for the given validator, depositing the given amount.
Create a deposit request for the given validator, depositing the given amount.
"""
if index is None:
index = validator_index
@ -209,7 +209,7 @@ def prepare_deposit_receipt(spec, validator_index, amount,
if withdrawal_credentials is None:
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:]
return build_deposit_receipt(
return build_deposit_request(
spec,
index,
pubkey,
@ -320,11 +320,11 @@ def run_deposit_processing_with_specific_fork_version(
yield from run_deposit_processing(spec, state, deposit, validator_index, valid=valid, effective=effective)
def run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, valid=True, effective=True):
def run_deposit_request_processing(spec, state, deposit_request, validator_index, valid=True, effective=True):
"""
Run ``process_deposit_receipt``, yielding:
Run ``process_deposit_request``, yielding:
- pre-state ('pre')
- deposit_receipt ('deposit_receipt')
- deposit_request ('deposit_request')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
@ -340,18 +340,18 @@ def run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index
pre_pending_deposits = len(state.pending_balance_deposits)
yield 'pre', state
yield 'deposit_receipt', deposit_receipt
yield 'deposit_request', deposit_request
if not valid:
expect_assertion_error(lambda: spec.process_deposit_receipt(state, deposit_receipt))
expect_assertion_error(lambda: spec.process_deposit_request(state, deposit_request))
yield 'post', None
return
spec.process_deposit_receipt(state, deposit_receipt)
spec.process_deposit_request(state, deposit_request)
yield 'post', state
if not effective or not bls.KeyValidate(deposit_receipt.pubkey):
if not effective or not bls.KeyValidate(deposit_request.pubkey):
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
if is_top_up:
@ -368,11 +368,11 @@ def run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index
assert len(state.balances) == pre_validator_count + 1
assert len(state.pending_balance_deposits) == pre_pending_deposits + 1
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit_receipt.amount
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit_request.amount
assert state.pending_balance_deposits[pre_pending_deposits].index == validator_index
def run_deposit_receipt_processing_with_specific_fork_version(
def run_deposit_request_processing_with_specific_fork_version(
spec,
state,
fork_version,
@ -391,17 +391,17 @@ def run_deposit_receipt_processing_with_specific_fork_version(
pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount,
signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain))
)
deposit_receipt = spec.DepositReceipt(
deposit_request = spec.DepositRequest(
pubkey=deposit_data.pubkey,
withdrawal_credentials=deposit_data.withdrawal_credentials,
amount=deposit_data.amount,
signature=deposit_data.signature,
index=validator_index)
yield from run_deposit_receipt_processing(
yield from run_deposit_request_processing(
spec,
state,
deposit_receipt,
deposit_request,
validator_index,
valid=valid,
effective=effective

View File

@ -1,39 +0,0 @@
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_withdrawal_request_processing(spec, state, withdrawal_request, valid=True, success=True):
"""
Run ``process_execution_layer_withdrawal_request``, yielding:
- pre-state ('pre')
- withdrawal_request ('withdrawal_request')
- 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, withdrawal_request.validator_pubkey)
yield 'pre', state
yield 'withdrawal_request', withdrawal_request
if not valid:
expect_assertion_error(lambda: spec.process_withdrawal_request(state, withdrawal_request))
yield 'post', None
return
pre_exit_epoch = state.validators[validator_index].exit_epoch
spec.process_withdrawal_request(state, withdrawal_request)
yield 'post', state
if success:
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[validator_index].exit_epoch == pre_exit_epoch

View File

@ -35,8 +35,9 @@ def get_execution_payload_header(spec, execution_payload):
payload_header.blob_gas_used = execution_payload.blob_gas_used
payload_header.excess_blob_gas = execution_payload.excess_blob_gas
if is_post_electra(spec):
payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts)
payload_header.deposit_requests_root = spec.hash_tree_root(execution_payload.deposit_requests)
payload_header.withdrawal_requests_root = spec.hash_tree_root(execution_payload.withdrawal_requests)
payload_header.consolidation_requests_root = spec.hash_tree_root(execution_payload.consolidation_requests)
return payload_header
@ -150,23 +151,39 @@ def get_withdrawal_request_rlp_bytes(withdrawal_request):
return b"\x01" + encode(values, sedes)
def get_deposit_receipt_rlp_bytes(deposit_receipt):
deposit_receipt_rlp = [
# pubkey
(Binary(48, 48), deposit_receipt.pubkey),
# withdrawal_credentials
(Binary(32, 32), deposit_receipt.withdrawal_credentials),
# amount
(big_endian_int, deposit_receipt.amount),
# pubkey
(Binary(96, 96), deposit_receipt.signature),
# index
(big_endian_int, deposit_receipt.index),
# https://eips.ethereum.org/EIPS/eip-7251
def get_consolidation_request_rlp_bytes(consolidation_request):
consolidation_request_rlp = [
# source_address
(Binary(20, 20), consolidation_request.source_address),
# source_pubkey
(Binary(48, 48), consolidation_request.source_pubkey),
# target_pubkey
(Binary(48, 48), consolidation_request.target_pubkey),
]
sedes = List([schema for schema, _ in deposit_receipt_rlp])
values = [value for _, value in deposit_receipt_rlp]
return b"\x00" + encode(values, sedes)
sedes = List([schema for schema, _ in consolidation_request_rlp])
values = [value for _, value in consolidation_request_rlp]
return b"\x01" + encode(values, sedes)
def get_deposit_request_rlp_bytes(spec, deposit_request):
deposit_request_rlp = [
# pubkey
(Binary(48, 48), deposit_request.pubkey),
# withdrawal_credentials
(Binary(32, 32), deposit_request.withdrawal_credentials),
# amount
(big_endian_int, deposit_request.amount),
# pubkey
(Binary(96, 96), deposit_request.signature),
# index
(big_endian_int, deposit_request.index),
]
sedes = List([schema for schema, _ in deposit_request_rlp])
values = [value for _, value in deposit_request_rlp]
return encode(values, sedes)
def compute_el_block_hash(spec, payload):
@ -180,8 +197,10 @@ def compute_el_block_hash(spec, payload):
withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded)
if is_post_electra(spec):
requests_encoded = []
requests_encoded += [get_deposit_receipt_rlp_bytes(receipt) for receipt in payload.deposit_receipts]
requests_encoded += [get_deposit_request_rlp_bytes(receipt) for receipt in payload.deposit_requests]
requests_encoded += [get_withdrawal_request_rlp_bytes(request) for request in payload.withdrawal_requests]
requests_encoded += [get_consolidation_request_rlp_bytes(request) for request in payload.consolidation_requests]
requests_trie_root = compute_trie_root_from_indexed_data(requests_encoded)
payload_header = get_execution_payload_header(spec, payload)
@ -227,8 +246,9 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
payload.blob_gas_used = 0
payload.excess_blob_gas = 0
if is_post_electra(spec):
payload.deposit_receipts = []
payload.deposit_requests = []
payload.withdrawal_requests = []
payload.consolidation_requests = []
payload.block_hash = compute_el_block_hash(spec, payload)

View File

@ -131,7 +131,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
)
if is_post_electra(spec):
state.deposit_receipts_start_index = spec.UNSET_DEPOSIT_RECEIPTS_START_INDEX
state.deposit_requests_start_index = spec.UNSET_DEPOSIT_REQUESTS_START_INDEX
if is_post_whisk(spec):
vc = len(state.validators)

View File

@ -10,3 +10,4 @@ from remerkleable.core import BasicView, View, Path
Bytes20 = ByteVector[20] # type: ignore
Bytes31 = ByteVector[31] # type: ignore

View File

@ -45,8 +45,9 @@ Operations:
| `execution_payload` | `BeaconBlockBody` | **`body`** | `process_execution_payload(state, body)` (new in Bellatrix) |
| `withdrawals` | `ExecutionPayload` | `execution_payload` | `process_withdrawals(state, execution_payload)` (new in Capella) |
| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `address_change` | `process_bls_to_execution_change(state, address_change)` (new in Capella) |
| `deposit_receipt` | `DepositReceipt` | `deposit_receipt` | `process_deposit_receipt(state, deposit_receipt)` (new in Electra) |
| `exits` | `ExecutionLayerExit` | `execution_layer_exit` | `process_execution_layer_exit(state, execution_layer_exit)` (new in Electra) |
| `deposit_request` | `DepositRequest` | `deposit_request` | `process_deposit_request(state, deposit_request)` (new in Electra) |
| `withdrawal_request` | `WithdrawalRequest` | `withdrawal_request` | `process_withdrawal_request(state, withdrawal_request)` (new in Electra) |
| `consolidation_request` | `ConsolidationRequest` | `consolidation_request` | `process_consolidation_request(state, consolidation_request)` (new in Electra) |
Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.

View File

@ -45,10 +45,10 @@ if __name__ == "__main__":
_new_electra_mods = {key: 'eth2spec.test.electra.block_processing.test_process_' + key for key in [
'attestation',
'consolidation',
'deposit_receipt',
'execution_layer_withdrawal_request',
'consolidation_request',
'deposit_request',
'voluntary_exit'
'withdrawal_request',
]}
electra_mods = combine_mods(_new_electra_mods, deneb_mods)