Merge branch 'dev' into pr3757
This commit is contained in:
commit
12d95da0da
|
@ -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
|
||||
|
|
2
Makefile
2
Makefile
|
@ -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)
|
||||
|
|
|
@ -159,6 +159,9 @@ NUMBER_OF_COLUMNS: 128
|
|||
MAX_CELLS_IN_EXTENDED_MATRIX: 768
|
||||
DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32
|
||||
MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384
|
||||
SAMPLES_PER_SLOT: 8
|
||||
CUSTODY_REQUIREMENT: 1
|
||||
TARGET_NUMBER_OF_PEERS: 70
|
||||
|
||||
# [New in Electra:EIP7251]
|
||||
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)
|
||||
|
|
|
@ -158,6 +158,9 @@ NUMBER_OF_COLUMNS: 128
|
|||
MAX_CELLS_IN_EXTENDED_MATRIX: 768
|
||||
DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32
|
||||
MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384
|
||||
SAMPLES_PER_SLOT: 8
|
||||
CUSTODY_REQUIREMENT: 1
|
||||
TARGET_NUMBER_OF_PEERS: 70
|
||||
|
||||
# [New in Electra:EIP7251]
|
||||
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000 # 2**6 * 10**9 (= 64,000,000,000)
|
||||
|
|
|
@ -54,10 +54,10 @@ You can refer to the previous fork's `fork.md` file.
|
|||
### 5. Make it executable
|
||||
- Update Pyspec [`constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/tests/core/pyspec/eth2spec/test/helpers/constants.py) with the new feature name.
|
||||
- Update helpers for [`setup.py`](https://github.com/ethereum/consensus-specs/blob/dev/setup.py) for building the spec:
|
||||
- Update [`pysetup/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/constants.py) with the new feature name as Pyspec `constants.py` defined.
|
||||
- Update [`pysetup/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/constants.py) with the new feature name as Pyspec `constants.py` defined.
|
||||
- Update [`pysetup/spec_builders/__init__.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/spec_builders/__init__.py). Implement a new `<FEATURE_NAME>SpecBuilder` in `pysetup/spec_builders/<FEATURE_NAME>.py` with the new feature name. e.g., `EIP9999SpecBuilder`. Append it to the `spec_builders` list.
|
||||
- Update [`pysetup/md_doc_paths.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/md_doc_paths.py): add the path of the new markdown files in `get_md_doc_paths` function if needed.
|
||||
- Update `PREVIOUS_FORK_OF` setting in both [`test/helpers/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/constants.py) and [`pysetup/md_doc_paths.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/md_doc_paths.py).
|
||||
- Update `PREVIOUS_FORK_OF` setting in both [`test/helpers/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/tests/core/pyspec/eth2spec/test/helpers/constants.py) and [`pysetup/md_doc_paths.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/md_doc_paths.py).
|
||||
- NOTE: since these two modules (the pyspec itself and the spec builder tool) must be separate, the fork sequence setting has to be defined again.
|
||||
|
||||
## B: Make it executable for pytest and test generator
|
||||
|
|
|
@ -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
|
|
@ -30,7 +30,7 @@ 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
|
||||
# ---------------------------------------------------------------
|
||||
|
|
|
@ -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
|
|
@ -30,7 +30,7 @@ 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
|
||||
# ---------------------------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
8
setup.py
8
setup.py
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
```
|
|
@ -106,18 +106,19 @@ def get_custody_columns(node_id: NodeID, custody_subnet_count: uint64) -> Sequen
|
|||
assert custody_subnet_count <= DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
|
||||
subnet_ids: List[uint64] = []
|
||||
i = 0
|
||||
current_id = uint256(node_id)
|
||||
while len(subnet_ids) < custody_subnet_count:
|
||||
if node_id == UINT256_MAX:
|
||||
node_id = NodeID(0)
|
||||
|
||||
subnet_id = (
|
||||
bytes_to_uint64(hash(uint_to_bytes(uint256(node_id + i)))[0:8])
|
||||
bytes_to_uint64(hash(uint_to_bytes(uint256(current_id)))[0:8])
|
||||
% DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
)
|
||||
if subnet_id not in subnet_ids:
|
||||
subnet_ids.append(subnet_id)
|
||||
i += 1
|
||||
if current_id == UINT256_MAX:
|
||||
# Overflow prevention
|
||||
current_id = NodeID(0)
|
||||
current_id += 1
|
||||
|
||||
assert len(subnet_ids) == len(set(subnet_ids))
|
||||
|
||||
columns_per_subnet = NUMBER_OF_COLUMNS // DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
- [The Req/Resp domain](#the-reqresp-domain)
|
||||
- [Messages](#messages)
|
||||
- [DataColumnSidecarsByRoot v1](#datacolumnsidecarsbyroot-v1)
|
||||
- [DataColumnSidecarsByRange v1](#datacolumnsidecarsbyrange-v1)
|
||||
- [The discovery domain: discv5](#the-discovery-domain-discv5)
|
||||
- [ENR structure](#enr-structure)
|
||||
- [`custody_subnet_count`](#custody_subnet_count)
|
||||
|
@ -200,6 +201,85 @@ Clients SHOULD include a sidecar in the response as soon as it passes the gossip
|
|||
Clients SHOULD NOT respond with sidecars related to blocks that fail gossip validation rules.
|
||||
Clients SHOULD NOT respond with sidecars related to blocks that fail the beacon chain state transition
|
||||
|
||||
##### DataColumnSidecarsByRange v1
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/data_column_sidecars_by_range/1/`
|
||||
|
||||
The `<context-bytes>` field is calculated as `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
|
||||
[1]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Chunk SSZ type |
|
||||
|--------------------------|-------------------------------|
|
||||
| `EIP7594_FORK_VERSION` | `eip7594.DataColumnSidecar` |
|
||||
|
||||
Request Content:
|
||||
```
|
||||
(
|
||||
start_slot: Slot
|
||||
count: uint64
|
||||
columns: List[ColumnIndex, NUMBER_OF_COLUMNS]
|
||||
)
|
||||
```
|
||||
|
||||
Response Content:
|
||||
```
|
||||
(
|
||||
List[DataColumnSidecar, MAX_REQUEST_DATA_COLUMN_SIDECARS]
|
||||
)
|
||||
```
|
||||
|
||||
Requests data column sidecars in the slot range `[start_slot, start_slot + count)` of the given `columns`, leading up to the current head block as selected by fork choice.
|
||||
|
||||
Before consuming the next response chunk, the response reader SHOULD verify the data column sidecar is well-formatted, has valid inclusion proof, and is correct w.r.t. the expected KZG commitments through `verify_data_column_sidecar_kzg_proofs`.
|
||||
|
||||
`DataColumnSidecarsByRange` is primarily used to sync data columns that may have been missed on gossip and to sync within the `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` window.
|
||||
|
||||
The request MUST be encoded as an SSZ-container.
|
||||
|
||||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `DataColumnSidecar` payload.
|
||||
|
||||
Let `data_column_serve_range` be `[max(current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, EIP7594_FORK_EPOCH), current_epoch]`.
|
||||
Clients MUST keep a record of data column sidecars seen on the epoch range `data_column_serve_range`
|
||||
where `current_epoch` is defined by the current wall-clock time,
|
||||
and clients MUST support serving requests of data columns on this range.
|
||||
|
||||
Peers that are unable to reply to data column sidecar requests within the
|
||||
range `data_column_serve_range` SHOULD respond with error code `3: ResourceUnavailable`.
|
||||
Such peers that are unable to successfully reply to this range of requests MAY get descored
|
||||
or disconnected at any time.
|
||||
|
||||
*Note*: The above requirement implies that nodes that start from a recent weak subjectivity checkpoint
|
||||
MUST backfill the local data columns database to at least the range `data_column_serve_range`
|
||||
to be fully compliant with `DataColumnSidecarsByRange` requests.
|
||||
|
||||
*Note*: Although clients that bootstrap from a weak subjectivity checkpoint can begin
|
||||
participating in the networking immediately, other peers MAY
|
||||
disconnect and/or temporarily ban such an un-synced or semi-synced client.
|
||||
|
||||
Clients MUST respond with at least the data column sidecars of the first blob-carrying block that exists in the range, if they have it, and no more than `MAX_REQUEST_DATA_COLUMN_SIDECARS` sidecars.
|
||||
|
||||
Clients MUST include all data column sidecars of each block from which they include data column sidecars.
|
||||
|
||||
The following data column sidecars, where they exist, MUST be sent in `(slot, column_index)` order.
|
||||
|
||||
Slots that do not contain known data columns MUST be skipped, mimicking the behaviour
|
||||
of the `BlocksByRange` request. Only response chunks with known data columns should
|
||||
therefore be sent.
|
||||
|
||||
Clients MAY limit the number of data column sidecars in the response.
|
||||
|
||||
The response MUST contain no more than `count * NUMBER_OF_COLUMNS` data column sidecars.
|
||||
|
||||
Clients MUST respond with data columns sidecars from their view of the current fork choice
|
||||
-- that is, data column sidecars as included by blocks from the single chain defined by the current head.
|
||||
Of note, blocks from slots before the finalization MUST lead to the finalized block reported in the `Status` handshake.
|
||||
|
||||
Clients MUST respond with data column sidecars that are consistent from a single chain within the context of the request.
|
||||
|
||||
After the initial data column sidecar, clients MAY stop in the process of responding if their fork choice changes the view of the chain in the context of the request.
|
||||
|
||||
### The discovery domain: discv5
|
||||
|
||||
#### ENR structure
|
||||
|
|
|
@ -683,6 +683,9 @@ def recover_all_cells(cell_ids: Sequence[CellID], cells: Sequence[Cell]) -> Sequ
|
|||
# Check that each cell is the correct length
|
||||
for cell in cells:
|
||||
assert len(cell) == BYTES_PER_CELL
|
||||
# Check that the cell ids are within bounds
|
||||
for cell_id in cell_ids:
|
||||
assert cell_id < CELLS_PER_EXT_BLOB
|
||||
|
||||
# Get the extended domain
|
||||
roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
- [`PendingBalanceDeposit`](#pendingbalancedeposit)
|
||||
- [`PendingPartialWithdrawal`](#pendingpartialwithdrawal)
|
||||
- [`ExecutionLayerWithdrawalRequest`](#executionlayerwithdrawalrequest)
|
||||
- [`Consolidation`](#consolidation)
|
||||
- [`SignedConsolidation`](#signedconsolidation)
|
||||
- [`ExecutionLayerConsolidationRequest`](#executionlayerconsolidationrequest)
|
||||
- [`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)
|
||||
|
@ -94,8 +94,8 @@
|
|||
- [New `process_execution_layer_withdrawal_request`](#new-process_execution_layer_withdrawal_request)
|
||||
- [Deposit requests](#deposit-requests)
|
||||
- [New `process_deposit_request`](#new-process_deposit_request)
|
||||
- [Consolidations](#consolidations)
|
||||
- [New `process_consolidation`](#new-process_consolidation)
|
||||
- [Execution layer consolidation requests](#execution-layer-consolidation-requests)
|
||||
- [New `process_execution_layer_consolidation_request`](#new-process_execution_layer_consolidation_request)
|
||||
- [Testing](#testing)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
@ -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_REQUESTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit requests 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
|
||||
|
||||
|
@ -238,25 +238,15 @@ class ExecutionLayerWithdrawalRequest(Container):
|
|||
amount: Gwei
|
||||
```
|
||||
|
||||
#### `Consolidation`
|
||||
#### `ExecutionLayerConsolidationRequest`
|
||||
|
||||
*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 ExecutionLayerConsolidationRequest(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`
|
||||
|
@ -348,6 +337,8 @@ class ExecutionPayload(Container):
|
|||
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]
|
||||
# [New in Electra:EIP7251]
|
||||
consolidation_requests: List[ExecutionLayerConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD]
|
||||
```
|
||||
|
||||
#### `ExecutionPayloadHeader`
|
||||
|
@ -375,6 +366,7 @@ class ExecutionPayloadHeader(Container):
|
|||
excess_blob_gas: uint64
|
||||
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`
|
||||
|
@ -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
|
||||
|
@ -1013,6 +1026,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
|
|||
excess_blob_gas=payload.excess_blob_gas,
|
||||
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]
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -1042,10 +1056,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_requests, process_deposit_request) # [New in Electra:EIP6110]
|
||||
for_ops(body.consolidations, process_consolidation) # [New in Electra:EIP7251]
|
||||
# [New in Electra:EIP7251]
|
||||
for_ops(body.execution_payload.consolidation_requests, process_execution_layer_consolidation_request)
|
||||
```
|
||||
|
||||
##### Attestations
|
||||
|
@ -1292,43 +1307,62 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest)
|
|||
)
|
||||
```
|
||||
|
||||
##### Consolidations
|
||||
##### Execution layer consolidation requests
|
||||
|
||||
###### New `process_consolidation`
|
||||
###### New `process_execution_layer_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_execution_layer_consolidation_request(
|
||||
state: BeaconState,
|
||||
execution_layer_consolidation_request: ExecutionLayerConsolidationRequest
|
||||
) -> 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 = execution_layer_consolidation_request.source_pubkey
|
||||
request_target_pubkey = execution_layer_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:] == execution_layer_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 +1372,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
|
||||
))
|
||||
```
|
||||
|
||||
|
|
|
@ -91,7 +91,8 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
|
|||
blob_gas_used=pre.latest_execution_payload_header.blob_gas_used,
|
||||
excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas,
|
||||
deposit_requests_root=Root(), # [New in Electra:EIP6110]
|
||||
withdrawal_requests_root=Root(), # [New in Electra:EIP7002],
|
||||
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]
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
- [Introduction](#introduction)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Containers](#containers)
|
||||
- [Modified Containers](#modified-containers)
|
||||
- [`AggregateAndProof`](#aggregateandproof)
|
||||
- [`SignedAggregateAndProof`](#signedaggregateandproof)
|
||||
- [Block proposal](#block-proposal)
|
||||
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
|
||||
- [Attester slashings](#attester-slashings)
|
||||
|
@ -34,6 +38,27 @@ All behaviors and definitions defined in this document, and documents it extends
|
|||
All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [Electra](./beacon-chain.md) are requisite for this document and used throughout.
|
||||
Please see related Beacon Chain doc before continuing and use them as a reference throughout.
|
||||
|
||||
## Containers
|
||||
|
||||
### Modified Containers
|
||||
|
||||
#### `AggregateAndProof`
|
||||
|
||||
```python
|
||||
class AggregateAndProof(Container):
|
||||
aggregator_index: ValidatorIndex
|
||||
aggregate: Attestation # [Modified in Electra:EIP7549]
|
||||
selection_proof: BLSSignature
|
||||
```
|
||||
|
||||
#### `SignedAggregateAndProof`
|
||||
|
||||
```python
|
||||
class SignedAggregateAndProof(Container):
|
||||
message: AggregateAndProof # [Modified in Electra:EIP7549]
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
## Block proposal
|
||||
|
||||
### Constructing the `BeaconBlockBody`
|
||||
|
|
|
@ -356,7 +356,7 @@ to subscribing nodes (typically validators) to be included in future blocks.
|
|||
We define the following variables for convenience:
|
||||
- `aggregate_and_proof = signed_aggregate_and_proof.message`
|
||||
- `aggregate = aggregate_and_proof.aggregate`
|
||||
- `index = aggregate.index`
|
||||
- `index = aggregate.data.index`
|
||||
- `aggregation_bits = attestation.aggregation_bits`
|
||||
|
||||
The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network.
|
||||
|
@ -436,7 +436,7 @@ The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated a
|
|||
to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`.
|
||||
|
||||
We define the following variables for convenience:
|
||||
- `index = attestation.index`
|
||||
- `index = attestation.data.index`
|
||||
- `aggregation_bits = attestation.aggregation_bits`
|
||||
|
||||
The following validations MUST pass before forwarding the `attestation` on the subnet.
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.5.0-alpha.1
|
||||
1.5.0-alpha.2
|
||||
|
|
|
@ -65,6 +65,25 @@ def test_get_custody_columns__max_node_id_max_custody_subnet_count(spec):
|
|||
)
|
||||
|
||||
|
||||
@with_eip7594_and_later
|
||||
@spec_test
|
||||
@single_phase
|
||||
def test_get_custody_columns__max_node_id_max_custody_subnet_count_minus_1(spec):
|
||||
rng = random.Random(1111)
|
||||
yield from _run_get_custody_columns(
|
||||
spec, rng, node_id=2**256 - 2,
|
||||
custody_subnet_count=spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT,
|
||||
)
|
||||
|
||||
|
||||
@with_eip7594_and_later
|
||||
@spec_test
|
||||
@single_phase
|
||||
def test_get_custody_columns__short_node_id(spec):
|
||||
rng = random.Random(1111)
|
||||
yield from _run_get_custody_columns(spec, rng, node_id=1048576, custody_subnet_count=1)
|
||||
|
||||
|
||||
@with_eip7594_and_later
|
||||
@spec_test
|
||||
@single_phase
|
||||
|
|
|
@ -11,8 +11,8 @@ from eth2spec.test.context import (
|
|||
def test_invariants(spec):
|
||||
assert spec.FIELD_ELEMENTS_PER_BLOB % spec.FIELD_ELEMENTS_PER_CELL == 0
|
||||
assert spec.FIELD_ELEMENTS_PER_EXT_BLOB % spec.config.NUMBER_OF_COLUMNS == 0
|
||||
assert spec.SAMPLES_PER_SLOT <= spec.config.NUMBER_OF_COLUMNS
|
||||
assert spec.CUSTODY_REQUIREMENT <= spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
assert spec.config.SAMPLES_PER_SLOT <= spec.config.NUMBER_OF_COLUMNS
|
||||
assert spec.config.CUSTODY_REQUIREMENT <= spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
assert spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT <= spec.config.NUMBER_OF_COLUMNS
|
||||
assert spec.config.NUMBER_OF_COLUMNS % spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT == 0
|
||||
assert spec.config.MAX_REQUEST_DATA_COLUMN_SIDECARS == (
|
||||
|
|
|
@ -9,7 +9,7 @@ from eth2spec.test.context import (
|
|||
def run_get_custody_columns(spec, peer_count, custody_subnet_count):
|
||||
assignments = [spec.get_custody_columns(node_id, custody_subnet_count) for node_id in range(peer_count)]
|
||||
|
||||
columns_per_subnet = spec.NUMBER_OF_COLUMNS // spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
columns_per_subnet = spec.config.NUMBER_OF_COLUMNS // spec.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT
|
||||
for assignment in assignments:
|
||||
assert len(assignment) == custody_subnet_count * columns_per_subnet
|
||||
assert len(assignment) == len(set(assignment))
|
||||
|
@ -20,8 +20,8 @@ def run_get_custody_columns(spec, peer_count, custody_subnet_count):
|
|||
@single_phase
|
||||
def test_get_custody_columns_peers_within_number_of_columns(spec):
|
||||
peer_count = 10
|
||||
custody_subnet_count = spec.CUSTODY_REQUIREMENT
|
||||
assert spec.NUMBER_OF_COLUMNS > peer_count
|
||||
custody_subnet_count = spec.config.CUSTODY_REQUIREMENT
|
||||
assert spec.config.NUMBER_OF_COLUMNS > peer_count
|
||||
run_get_custody_columns(spec, peer_count, custody_subnet_count)
|
||||
|
||||
|
||||
|
@ -30,8 +30,8 @@ def test_get_custody_columns_peers_within_number_of_columns(spec):
|
|||
@single_phase
|
||||
def test_get_custody_columns_peers_more_than_number_of_columns(spec):
|
||||
peer_count = 200
|
||||
custody_subnet_count = spec.CUSTODY_REQUIREMENT
|
||||
assert spec.NUMBER_OF_COLUMNS < peer_count
|
||||
custody_subnet_count = spec.config.CUSTODY_REQUIREMENT
|
||||
assert spec.config.NUMBER_OF_COLUMNS < peer_count
|
||||
run_get_custody_columns(spec, peer_count, custody_subnet_count)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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.ExecutionLayerConsolidationRequest(
|
||||
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')
|
||||
- execution_layer_consolidation_request ('execution_layer_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 'execution_layer_consolidation_request', consolidation
|
||||
|
||||
spec.process_execution_layer_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
|
|
@ -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
|
||||
)
|
|
@ -34,7 +34,6 @@ LATEST_FORK = MAINNET_FORKS[-1]
|
|||
ALL_PHASES = (
|
||||
# Formal forks
|
||||
*MAINNET_FORKS,
|
||||
DENEB,
|
||||
ELECTRA,
|
||||
# Experimental patches
|
||||
EIP7594,
|
||||
|
|
|
@ -37,6 +37,7 @@ def get_execution_payload_header(spec, execution_payload):
|
|||
if is_post_electra(spec):
|
||||
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
|
||||
|
||||
|
||||
|
@ -59,7 +60,8 @@ def compute_el_header_block_hash(spec,
|
|||
transactions_trie_root,
|
||||
withdrawals_trie_root=None,
|
||||
deposit_requests_trie_root=None,
|
||||
withdrawal_requests_root=None):
|
||||
withdrawal_requests_root=None,
|
||||
consolidation_requests_root=None):
|
||||
"""
|
||||
Computes the RLP execution block hash described by an `ExecutionPayloadHeader`.
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from py_ecc.bls import G2ProofOfPossession as py_ecc_bls
|
||||
from py_ecc.bls.g2_primatives import signature_to_G2 as _signature_to_G2
|
||||
from py_ecc.bls.g2_primitives import signature_to_G2 as _signature_to_G2
|
||||
from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G1 as py_ecc_G1,
|
||||
G2 as py_ecc_G2,
|
||||
|
|
|
@ -10,3 +10,4 @@ from remerkleable.core import BasicView, View, Path
|
|||
|
||||
|
||||
Bytes20 = ByteVector[20] # type: ignore
|
||||
Bytes31 = ByteVector[31] # type: ignore
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
```yaml
|
||||
description: string -- optional: description of test case, purely for debugging purposes.
|
||||
node_id: int -- argument: the NodeId input.
|
||||
node_id: int -- argument: the NodeID input.
|
||||
custody_subnet_count: int -- argument: the count of custody subnets.
|
||||
result: list of int -- output: the list of resulting column indices.
|
||||
```
|
||||
|
|
|
@ -46,7 +46,8 @@ Operations:
|
|||
| `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_request` | `DepositRequest` | `deposit_request` | `process_deposit_request(state, deposit_request)` (new in Electra) |
|
||||
| `exits` | `ExecutionLayerExit` | `execution_layer_exit` | `process_execution_layer_exit(state, execution_layer_exit)` (new in Electra) |
|
||||
| `execution_layer_withdrawal_request` | `ExecutionLayerWithdrawalRequest` | `execution_layer_withdrawal_request` | `process_execution_layer_withdrawal_request(state, execution_layer_withdrawal_request)` (new in Electra) |
|
||||
| `execution_layer_consolidation_request` | `ExecutionLayerConsolidationRequest` | `execution_layer_consolidation_request` | `process_execution_layer_consolidation_request(state, execution_layer_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.
|
||||
|
||||
|
|
|
@ -710,6 +710,21 @@ def case05_recover_all_cells():
|
|||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: More cells provided than CELLS_PER_EXT_BLOB
|
||||
blob = BLOB_RANDOM_VALID2
|
||||
cells = spec.compute_cells(blob)
|
||||
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB)) + [0]
|
||||
partial_cells = [cells[cell_id] for cell_id in cell_ids]
|
||||
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
|
||||
identifier = make_id(cell_ids, partial_cells)
|
||||
yield f'recover_all_cells_case_invalid_more_cells_than_cells_per_ext_blob_{identifier}', {
|
||||
'input': {
|
||||
'cell_ids': cell_ids,
|
||||
'cells': encode_hex_list(partial_cells),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid cell_id
|
||||
blob = BLOB_RANDOM_VALID1
|
||||
cells = spec.compute_cells(blob)
|
||||
|
@ -782,7 +797,12 @@ def case05_recover_all_cells():
|
|||
# Edge case: Duplicate cell_id
|
||||
blob = BLOB_RANDOM_VALID2
|
||||
cells = spec.compute_cells(blob)
|
||||
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2))
|
||||
# There will be 65 cells, where 64 are unique and 1 is a duplicate.
|
||||
# Depending on the implementation, 63 & 1 might not fail for the right
|
||||
# reason. For example, if the implementation assigns cells in an array
|
||||
# via index, this would result in 63 cells and the test would fail due
|
||||
# to insufficient cell count, not because of a duplicate cell.
|
||||
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2 + 1))
|
||||
partial_cells = [cells[cell_id] for cell_id in cell_ids]
|
||||
# Replace first cell_id with the second cell_id
|
||||
cell_ids[0] = cell_ids[1]
|
||||
|
|
|
@ -45,7 +45,7 @@ if __name__ == "__main__":
|
|||
|
||||
_new_electra_mods = {key: 'eth2spec.test.electra.block_processing.test_process_' + key for key in [
|
||||
'attestation',
|
||||
'consolidation',
|
||||
'execution_layer_consolidation_request',
|
||||
'deposit_request',
|
||||
'execution_layer_withdrawal_request',
|
||||
'voluntary_exit'
|
||||
|
|
Loading…
Reference in New Issue