Merge branch 'dev' into deneb-tests

This commit is contained in:
Hsiao-Wei Wang 2023-05-04 18:04:20 +08:00
commit f1765dfef3
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
46 changed files with 1340 additions and 89 deletions

View File

@ -60,7 +60,7 @@ commands:
jobs:
checkout_specs:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
# Restore git repo at point close to target branch/revision, to speed up checkout
@ -80,7 +80,7 @@ jobs:
- ~/specs-repo
install_pyspec_test:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -92,7 +92,7 @@ jobs:
- save_pyspec_cached_venv
test-phase0:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -105,7 +105,7 @@ jobs:
path: tests/core/pyspec/test-reports
test-altair:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -118,7 +118,7 @@ jobs:
path: tests/core/pyspec/test-reports
test-bellatrix:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -131,7 +131,7 @@ jobs:
path: tests/core/pyspec/test-reports
test-capella:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -144,7 +144,7 @@ jobs:
path: tests/core/pyspec/test-reports
test-deneb:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -155,6 +155,19 @@ jobs:
command: make citest fork=deneb
- store_test_results:
path: tests/core/pyspec/test-reports
test-eip6110:
docker:
- image: circleci/python:3.8
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Run py-tests
command: make citest fork=eip6110
- store_test_results:
path: tests/core/pyspec/test-reports
table_of_contents:
docker:
- image: circleci/node:10.16.3
@ -166,7 +179,7 @@ jobs:
command: sudo npm install -g doctoc@2 && make check_toc
codespell:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- checkout
@ -175,7 +188,7 @@ jobs:
command: pip install 'codespell<3.0.0,>=2.0.0' --user && make codespell
lint:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -231,7 +244,7 @@ jobs:
- /nix
install_deposit_contract_web3_tester:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -243,7 +256,7 @@ jobs:
- save_deposit_contract_tester_cached_venv
test_deposit_contract_web3_tests:
docker:
- image: circleci/python:3.8
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
@ -275,6 +288,9 @@ workflows:
- test-deneb:
requires:
- install_pyspec_test
- test-eip6110:
requires:
- install_pyspec_test
- table_of_contents
- codespell
- lint:

View File

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

View File

@ -50,8 +50,9 @@ CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC
# Deneb
DENEB_FORK_VERSION: 0x04000000
DENEB_FORK_EPOCH: 18446744073709551615
# EIP6110
EIP6110_FORK_VERSION: 0x05000000 # temporary stub
EIP6110_FORK_EPOCH: 18446744073709551615
# Time parameters

View File

@ -49,6 +49,9 @@ CAPELLA_FORK_EPOCH: 18446744073709551615
# DENEB
DENEB_FORK_VERSION: 0x04000001
DENEB_FORK_EPOCH: 18446744073709551615
# EIP6110
EIP6110_FORK_VERSION: 0x05000001
EIP6110_FORK_EPOCH: 18446744073709551615
# Time parameters

View File

@ -0,0 +1,6 @@
# Mainnet preset - EIP6110
# Execution
# ---------------------------------------------------------------
# 2**13 (= 8192) receipts
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192

View File

@ -0,0 +1,6 @@
# Minimal preset - EIP6110
# Execution
# ---------------------------------------------------------------
# [customized]
MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4

View File

@ -671,13 +671,13 @@ def retrieve_blobs_and_proofs(beacon_block_root: Root) -> PyUnion[Tuple[Blob, KZ
#
# EIP6110SpecBuilder
#
class EIP6110SpecBuilder(CapellaSpecBuilder):
class EIP6110SpecBuilder(DenebSpecBuilder):
fork: str = EIP6110
@classmethod
def imports(cls, preset_name: str):
return super().imports(preset_name) + f'''
from eth2spec.capella import {preset_name} as capella
from eth2spec.deneb import {preset_name} as deneb
'''
@ -1022,7 +1022,7 @@ class PySpecCommand(Command):
specs/capella/validator.md
specs/capella/p2p-interface.md
"""
if self.spec_fork == DENEB:
if self.spec_fork in (DENEB, EIP6110):
self.md_doc_paths += """
specs/deneb/light-client/fork.md
specs/deneb/light-client/full-node.md
@ -1037,6 +1037,10 @@ class PySpecCommand(Command):
"""
if self.spec_fork == EIP6110:
self.md_doc_paths += """
specs/_features/eip6110/light-client/fork.md
specs/_features/eip6110/light-client/full-node.md
specs/_features/eip6110/light-client/p2p-interface.md
specs/_features/eip6110/light-client/sync-protocol.md
specs/_features/eip6110/beacon-chain.md
specs/_features/eip6110/fork.md
"""
@ -1176,7 +1180,7 @@ setup(
packages=find_packages(where='tests/core/pyspec') + ['configs', 'specs'],
py_modules=["eth2spec"],
cmdclass=commands,
python_requires=">=3.8, <4",
python_requires=">=3.9, <4",
extras_require={
"test": ["pytest>=4.4", "pytest-cov", "pytest-xdist"],
"lint": ["flake8==5.0.4", "mypy==0.981", "pylint==2.15.3"],

View File

@ -33,7 +33,7 @@
This is the beacon chain specification of in-protocol deposits processing mechanism.
This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110).
*Note:* This specification is built upon [Capella](../../capella/beacon-chain.md) and is under active development.
*Note:* This specification is built upon [Deneb](../../deneb/beacon-chain.md) and is under active development.
## Constants
@ -91,7 +91,8 @@ class ExecutionPayload(Container):
block_hash: Hash32
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in EIP-6110]
excess_data_gas: uint256
deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in EIP6110]
```
#### `ExecutionPayloadHeader`
@ -115,7 +116,8 @@ class ExecutionPayloadHeader(Container):
block_hash: Hash32
transactions_root: Root
withdrawals_root: Root
deposit_receipts_root: Root # [New in EIP-6110]
excess_data_gas: uint256
deposit_receipts_root: Root # [New in EIP6110]
```
#### `BeaconState`
@ -157,13 +159,13 @@ class BeaconState(Container):
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in EIP6110]
# Withdrawals
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
# [New in EIP-6110]
# [New in EIP6110]
deposit_receipts_start_index: uint64
```
@ -176,11 +178,12 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
if is_execution_enabled(state, block.body):
process_withdrawals(state, block.body.execution_payload)
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-6110]
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP6110]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in EIP-6110]
process_operations(state, block.body) # [Modified in EIP6110]
process_sync_aggregate(state, block.body.sync_aggregate)
process_blob_kzg_commitments(block.body)
```
#### Modified `process_operations`
@ -189,7 +192,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
```python
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# [Modified in EIP-6110]
# [Modified in EIP6110]
# Disable former deposit mechanism once all prior deposits are processed
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index)
if state.eth1_deposit_index < eth1_deposit_index_limit:
@ -204,11 +207,11 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.proposer_slashings, process_proposer_slashing)
for_ops(body.attester_slashings, process_attester_slashing)
for_ops(body.attestations, process_attestation)
for_ops(body.deposits, process_deposit) # [Modified in EIP-6110]
for_ops(body.deposits, process_deposit)
for_ops(body.voluntary_exits, process_voluntary_exit)
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
# [New in EIP-6110]
# [New in EIP6110]
if is_execution_enabled(state, body):
for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt)
```
@ -262,7 +265,8 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
block_hash=payload.block_hash,
transactions_root=hash_tree_root(payload.transactions),
withdrawals_root=hash_tree_root(payload.withdrawals),
deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in EIP-6110]
excess_data_gas=payload.excess_data_gas,
deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in EIP6110]
)
```

View File

@ -43,7 +43,9 @@ def compute_fork_version(epoch: Epoch) -> Version:
Return the fork version at the given ``epoch``.
"""
if epoch >= EIP6110_FORK_EPOCH:
return EIP6110_FORK_EPOCH
return EIP6110_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:
@ -68,8 +70,8 @@ If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) ==
an irregular state change is made to upgrade to EIP-6110.
```python
def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState:
epoch = capella.get_current_epoch(pre)
def upgrade_to_eip6110(pre: deneb.BeaconState) -> BeaconState:
epoch = deneb.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,
@ -86,6 +88,7 @@ def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState:
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,
excess_data_gas=uint256(0),
deposit_receipts_root=Root(), # [New in EIP-6110]
)
post = BeaconState(

View File

@ -0,0 +1,112 @@
# EIP-6110 Light Client -- Fork Logic
## 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)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This document describes how to upgrade existing light client objects based on the [Deneb specification](../../deneb/light-client/sync-protocol.md) to eip6110. This is necessary when processing pre-eip6110 data with a post-eip6110 `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.
### Upgrading light client data
A eip6110 `LightClientStore` can still process earlier light client data. In order to do so, that pre-eip6110 data needs to be locally upgraded to eip6110 before processing.
```python
def upgrade_lc_header_to_eip6110(pre: deneb.LightClientHeader) -> LightClientHeader:
return LightClientHeader(
beacon=pre.beacon,
execution=ExecutionPayloadHeader(
parent_hash=pre.execution.parent_hash,
fee_recipient=pre.execution.fee_recipient,
state_root=pre.execution.state_root,
receipts_root=pre.execution.receipts_root,
logs_bloom=pre.execution.logs_bloom,
prev_randao=pre.execution.prev_randao,
block_number=pre.execution.block_number,
gas_limit=pre.execution.gas_limit,
gas_used=pre.execution.gas_used,
timestamp=pre.execution.timestamp,
extra_data=pre.execution.extra_data,
base_fee_per_gas=pre.execution.base_fee_per_gas,
block_hash=pre.execution.block_hash,
transactions_root=pre.execution.transactions_root,
withdrawals_root=pre.execution.withdrawals_root,
excess_data_gas=pre.execution.excess_data_gas,
deposit_receipts_root=Root(), # [New in EIP6110]
),
execution_branch=pre.execution_branch,
)
```
```python
def upgrade_lc_bootstrap_to_eip6110(pre: deneb.LightClientBootstrap) -> LightClientBootstrap:
return LightClientBootstrap(
header=upgrade_lc_header_to_eip6110(pre.header),
current_sync_committee=pre.current_sync_committee,
current_sync_committee_branch=pre.current_sync_committee_branch,
)
```
```python
def upgrade_lc_update_to_eip6110(pre: deneb.LightClientUpdate) -> LightClientUpdate:
return LightClientUpdate(
attested_header=upgrade_lc_header_to_eip6110(pre.attested_header),
next_sync_committee=pre.next_sync_committee,
next_sync_committee_branch=pre.next_sync_committee_branch,
finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header),
finality_branch=pre.finality_branch,
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```
```python
def upgrade_lc_finality_update_to_eip6110(pre: deneb.LightClientFinalityUpdate) -> LightClientFinalityUpdate:
return LightClientFinalityUpdate(
attested_header=upgrade_lc_header_to_eip6110(pre.attested_header),
finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header),
finality_branch=pre.finality_branch,
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```
```python
def upgrade_lc_optimistic_update_to_eip6110(pre: deneb.LightClientOptimisticUpdate) -> LightClientOptimisticUpdate:
return LightClientOptimisticUpdate(
attested_header=upgrade_lc_header_to_eip6110(pre.attested_header),
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```
### Upgrading the store
Existing `LightClientStore` objects based on Deneb MUST be upgraded to eip6110 before eip6110 based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `EIP6110_FORK_EPOCH`.
```python
def upgrade_lc_store_to_eip6110(pre: deneb.LightClientStore) -> LightClientStore:
if pre.best_valid_update is None:
best_valid_update = None
else:
best_valid_update = upgrade_lc_update_to_eip6110(pre.best_valid_update)
return LightClientStore(
finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header),
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
best_valid_update=best_valid_update,
optimistic_header=upgrade_lc_header_to_eip6110(pre.optimistic_header),
previous_max_active_participants=pre.previous_max_active_participants,
current_max_active_participants=pre.current_max_active_participants,
)
```

View File

@ -0,0 +1,77 @@
# EIP-6110 Light Client -- Full Node
**Notice**: This document is a work-in-progress for researchers and implementers.
## 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)
- [Helper functions](#helper-functions)
- [Modified `block_to_light_client_header`](#modified-block_to_light_client_header)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This upgrade adds information about the execution payload to light client data as part of the EIP-6110 upgrade.
## Helper functions
### Modified `block_to_light_client_header`
```python
def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
epoch = compute_epoch_at_slot(block.message.slot)
if epoch >= CAPELLA_FORK_EPOCH:
payload = block.message.body.execution_payload
execution_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),
)
if epoch >= DENEB_FORK_EPOCH:
execution_header.excess_data_gas = payload.excess_data_gas
# [New in EIP6110]
if epoch >= EIP6110_FORK_EPOCH:
execution_header.deposit_receipts_root = hash_tree_root(payload.deposit_receipts)
execution_branch = compute_merkle_proof_for_block_body(block.message.body, EXECUTION_PAYLOAD_INDEX)
else:
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
# it was not included in the corresponding light client data. To ensure compatibility
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
execution_header = ExecutionPayloadHeader()
execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
return LightClientHeader(
beacon=BeaconBlockHeader(
slot=block.message.slot,
proposer_index=block.message.proposer_index,
parent_root=block.message.parent_root,
state_root=block.message.state_root,
body_root=hash_tree_root(block.message.body),
),
execution=execution_header,
execution_branch=execution_branch,
)
```

View File

@ -0,0 +1,111 @@
# EIP-6110 Light Client -- Networking
**Notice**: This document is a work-in-progress for researchers and implementers.
## 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 -->
- [Networking](#networking)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
- [Global topics](#global-topics)
- [`light_client_finality_update`](#light_client_finality_update)
- [`light_client_optimistic_update`](#light_client_optimistic_update)
- [The Req/Resp domain](#the-reqresp-domain)
- [Messages](#messages)
- [GetLightClientBootstrap](#getlightclientbootstrap)
- [LightClientUpdatesByRange](#lightclientupdatesbyrange)
- [GetLightClientFinalityUpdate](#getlightclientfinalityupdate)
- [GetLightClientOptimisticUpdate](#getlightclientoptimisticupdate)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Networking
The [Deneb light client networking specification](../../deneb/light-client/p2p-interface.md) is extended to exchange [EIP-6110 light client data](./sync-protocol.md).
### The gossip domain: gossipsub
#### Topics and messages
##### Global topics
###### `light_client_finality_update`
[0]: # (eth2spec: skip)
| `fork_version` | Message SSZ type |
|--------------------------------------------------------|-------------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` |
| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientFinalityUpdate` |
###### `light_client_optimistic_update`
[0]: # (eth2spec: skip)
| `fork_version` | Message SSZ type |
|--------------------------------------------------------|---------------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` |
| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientOptimisticUpdate` |
### The Req/Resp domain
#### Messages
##### GetLightClientBootstrap
[0]: # (eth2spec: skip)
| `fork_version` | Response SSZ type |
|--------------------------------------------------------|------------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientBootstrap` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientBootstrap` |
| `DENEB_FORK_VERSION` | `deneb.LightClientBootstrap` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientBootstrap` |
##### LightClientUpdatesByRange
[0]: # (eth2spec: skip)
| `fork_version` | Response chunk SSZ type |
|--------------------------------------------------------|----------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientUpdate` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientUpdate` |
| `DENEB_FORK_VERSION` | `deneb.LightClientUpdate` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientUpdate` |
##### GetLightClientFinalityUpdate
[0]: # (eth2spec: skip)
| `fork_version` | Response SSZ type |
|--------------------------------------------------------|-------------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` |
| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientFinalityUpdate` |
##### GetLightClientOptimisticUpdate
[0]: # (eth2spec: skip)
| `fork_version` | Response SSZ type |
|--------------------------------------------------------|---------------------------------------|
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` |
| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` |
| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` |
| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientOptimisticUpdate` |

View File

@ -0,0 +1,89 @@
# EIP-6110 Light Client -- Sync Protocol
**Notice**: This document is a work-in-progress for researchers and implementers.
## 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)
- [Helper functions](#helper-functions)
- [Modified `get_lc_execution_root`](#modified-get_lc_execution_root)
- [Modified `is_valid_light_client_header`](#modified-is_valid_light_client_header)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This upgrade updates light client data to include the EIP-6110 changes to the [`ExecutionPayload`](../beacon-chain.md) structure. It extends the [Deneb Light Client specifications](../../deneb/light-client/sync-protocol.md). The [fork document](./fork.md) explains how to upgrade existing Deneb based deployments to EIP-6110.
Additional documents describes the impact of the upgrade on certain roles:
- [Full node](./full-node.md)
- [Networking](./p2p-interface.md)
## Helper functions
### Modified `get_lc_execution_root`
```python
def get_lc_execution_root(header: LightClientHeader) -> Root:
epoch = compute_epoch_at_slot(header.beacon.slot)
if epoch >= DENEB_FORK_EPOCH:
return hash_tree_root(header.execution)
if epoch >= CAPELLA_FORK_EPOCH:
execution_header = capella.ExecutionPayloadHeader(
parent_hash=header.execution.parent_hash,
fee_recipient=header.execution.fee_recipient,
state_root=header.execution.state_root,
receipts_root=header.execution.receipts_root,
logs_bloom=header.execution.logs_bloom,
prev_randao=header.execution.prev_randao,
block_number=header.execution.block_number,
gas_limit=header.execution.gas_limit,
gas_used=header.execution.gas_used,
timestamp=header.execution.timestamp,
extra_data=header.execution.extra_data,
base_fee_per_gas=header.execution.base_fee_per_gas,
block_hash=header.execution.block_hash,
transactions_root=header.execution.transactions_root,
withdrawals_root=header.execution.withdrawals_root,
)
return hash_tree_root(execution_header)
return Root()
```
### Modified `is_valid_light_client_header`
```python
def is_valid_light_client_header(header: LightClientHeader) -> bool:
epoch = compute_epoch_at_slot(header.beacon.slot)
# [New in EIP-6110]
if epoch < EIP6110_FORK_EPOCH:
if header.execution.deposit_receipts_root != Root():
return False
if epoch < DENEB_FORK_EPOCH:
if header.execution.excess_data_gas != uint256(0):
return False
if epoch < CAPELLA_FORK_EPOCH:
return (
header.execution == ExecutionPayloadHeader()
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
)
return is_valid_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
root=header.beacon.body_root,
)
```

View File

@ -20,7 +20,7 @@ This document represents the changes to be made in the code of an "honest valida
## Prerequisites
This document is an extension of the [Capella -- Honest Validator](../../capella/validator.md) guide.
This document is an extension of the [Deneb -- Honest Validator](../../deneb/validator.md) guide.
All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden.
All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [EIP-6110](./beacon-chain.md) are requisite for this document and used throughout.

View File

@ -170,7 +170,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert block.slot > finalized_slot
# Check block is a descendant of the finalized block at the checkpoint finalized slot
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
finalized_checkpoint_block = get_checkpoint_block(
store,
block.parent_root,
store.finalized_checkpoint.epoch,
)
assert store.finalized_checkpoint.root == finalized_checkpoint_block
# Check the block is valid and compute the post-state
state = pre_state.copy()

View File

@ -82,7 +82,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert block.slot > finalized_slot
# Check block is a descendant of the finalized block at the checkpoint finalized slot
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
finalized_checkpoint_block = get_checkpoint_block(
store,
block.parent_root,
store.finalized_checkpoint.epoch,
)
assert store.finalized_checkpoint.root == finalized_checkpoint_block
# [New in Deneb]
# Check if blob data is available

View File

@ -41,6 +41,7 @@ def upgrade_lc_header_to_deneb(pre: capella.LightClientHeader) -> LightClientHea
block_hash=pre.execution.block_hash,
transactions_root=pre.execution.transactions_root,
withdrawals_root=pre.execution.withdrawals_root,
excess_data_gas=uint256(0), # [New in Deneb]
),
execution_branch=pre.execution_branch,
)

View File

@ -18,6 +18,7 @@
- [`get_current_slot`](#get_current_slot)
- [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start)
- [`get_ancestor`](#get_ancestor)
- [`get_checkpoint_block`](#get_checkpoint_block)
- [`get_weight`](#get_weight)
- [`get_voting_source`](#get_voting_source)
- [`filter_block_tree`](#filter_block_tree)
@ -190,6 +191,17 @@ def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
return root
```
#### `get_checkpoint_block`
```python
def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root:
"""
Compute the checkpoint block for epoch ``epoch`` in the chain of block ``root``
"""
epoch_first_slot = compute_start_slot_at_epoch(epoch)
return get_ancestor(store, root, epoch_first_slot)
```
#### `get_weight`
```python
@ -276,10 +288,15 @@ def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconB
voting_source.epoch + 2 >= current_epoch
)
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
finalized_checkpoint_block = get_checkpoint_block(
store,
block_root,
store.finalized_checkpoint.epoch,
)
correct_finalized = (
store.finalized_checkpoint.epoch == GENESIS_EPOCH
or store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot)
or store.finalized_checkpoint.root == finalized_checkpoint_block
)
# If expected finalized/justified, add to viable block-tree and signal viability to parent.
@ -440,8 +457,7 @@ def validate_on_attestation(store: Store, attestation: Attestation, is_from_bloc
assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
# LMD vote must be consistent with FFG vote target
target_slot = compute_start_slot_at_epoch(target.epoch)
assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot)
assert target.root == get_checkpoint_block(store, attestation.data.beacon_block_root, target.epoch)
# Attestations can only affect the fork choice of subsequent slots.
# Delay consideration in the fork choice until their slot is in the past.
@ -504,7 +520,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert block.slot > finalized_slot
# Check block is a descendant of the finalized block at the checkpoint finalized slot
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
finalized_checkpoint_block = get_checkpoint_block(
store,
block.parent_root,
store.finalized_checkpoint.epoch,
)
assert store.finalized_checkpoint.root == finalized_checkpoint_block
# Check the block is valid and compute the post-state
state = pre_state.copy()

View File

@ -317,7 +317,7 @@ The following validations MUST pass before forwarding the `signed_beacon_block`
- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation.
- _[REJECT]_ The block is from a higher slot than its parent.
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of `block` -- i.e.
`get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
`get_checkpoint_block(store, block.parent_root, store.finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
- _[REJECT]_ The block is proposed by the expected `proposer_index` for the block's slot
in the context of the current shuffling (defined by `parent_root`/`slot`).
@ -356,7 +356,7 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_
(a client MAY queue aggregates for processing once block is retrieved).
- _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
- _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e.
`get_ancestor(store, aggregate.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
`get_checkpoint_block(store, aggregate.data.beacon_block_root, finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
@ -425,9 +425,9 @@ The following validations MUST pass before forwarding the `attestation` on the s
(a client MAY queue attestations for processing once block is retrieved).
- _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation.
- _[REJECT]_ The attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(attestation.data.target.epoch)) == attestation.data.target.root`
`get_checkpoint_block(store, attestation.data.beacon_block_root, attestation.data.target.epoch) == attestation.data.target.root`
- _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e.
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
`get_checkpoint_block(store, attestation.data.beacon_block_root, store.finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`

View File

@ -26,6 +26,7 @@ from eth2spec.test.helpers.fork_transition import (
from eth2spec.test.helpers.forks import (
is_post_capella, is_post_deneb,
is_post_fork,
is_post_eip6110,
)
from eth2spec.test.helpers.light_client import (
get_sync_aggregate,
@ -57,6 +58,10 @@ def needs_upgrade_to_deneb(d_spec, s_spec):
return is_post_deneb(s_spec) and not is_post_deneb(d_spec)
def needs_upgrade_to_eip6110(d_spec, s_spec):
return is_post_eip6110(s_spec) and not is_post_eip6110(d_spec)
def check_lc_header_equal(d_spec, s_spec, data, upgraded):
assert upgraded.beacon.slot == data.beacon.slot
assert upgraded.beacon.hash_tree_root() == data.beacon.hash_tree_root()
@ -84,6 +89,10 @@ def upgrade_lc_bootstrap_to_store(d_spec, s_spec, data):
upgraded = s_spec.upgrade_lc_bootstrap_to_deneb(upgraded)
check_lc_bootstrap_equal(d_spec, s_spec, data, upgraded)
if needs_upgrade_to_eip6110(d_spec, s_spec):
upgraded = s_spec.upgrade_lc_bootstrap_to_eip6110(upgraded)
check_lc_bootstrap_equal(d_spec, s_spec, data, upgraded)
return upgraded
@ -145,6 +154,8 @@ class LightClientSyncTest(object):
def get_store_fork_version(s_spec):
if is_post_eip6110(s_spec):
return s_spec.config.EIP6110_FORK_VERSION
if is_post_deneb(s_spec):
return s_spec.config.DENEB_FORK_VERSION
if is_post_capella(s_spec):

View File

@ -8,11 +8,13 @@ from eth2spec.altair import mainnet as spec_altair_mainnet, minimal as spec_alta
from eth2spec.bellatrix import mainnet as spec_bellatrix_mainnet, minimal as spec_bellatrix_minimal
from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal
from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal
from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal
from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
EIP6110,
MINIMAL, MAINNET,
ALL_PHASES,
ALL_FORK_UPGRADES,
@ -79,13 +81,15 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
BELLATRIX: spec_bellatrix_minimal,
CAPELLA: spec_capella_minimal,
DENEB: spec_deneb_minimal,
EIP6110: spec_eip6110_minimal,
},
MAINNET: {
PHASE0: spec_phase0_mainnet,
ALTAIR: spec_altair_mainnet,
BELLATRIX: spec_bellatrix_mainnet,
CAPELLA: spec_capella_mainnet,
DENEB: spec_deneb_mainnet
DENEB: spec_deneb_mainnet,
EIP6110: spec_eip6110_mainnet,
},
}
@ -428,6 +432,7 @@ with_altair_and_later = with_all_phases_from(ALTAIR)
with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
with_capella_and_later = with_all_phases_from(CAPELLA)
with_deneb_and_later = with_all_phases_from(DENEB)
with_eip6110_and_later = with_all_phases_from(EIP6110)
def _get_preset_targets(kw):

View File

@ -4,7 +4,8 @@ from eth2spec.test.context import (
spec_test,
single_phase,
with_deneb_and_later,
expect_assertion_error
expect_assertion_error,
always_bls
)
from eth2spec.test.helpers.sharding import (
get_sample_blob,
@ -263,6 +264,7 @@ def test_validate_kzg_g1_neutral_element(spec):
@with_deneb_and_later
@spec_test
@single_phase
@always_bls
def test_validate_kzg_g1_not_in_g1(spec):
"""
Verify that `validate_kzg_g1` fails on point not in G1
@ -274,6 +276,7 @@ def test_validate_kzg_g1_not_in_g1(spec):
@with_deneb_and_later
@spec_test
@single_phase
@always_bls
def test_validate_kzg_g1_not_on_curve(spec):
"""
Verify that `validate_kzg_g1` fails on point not in G1

View File

@ -0,0 +1,282 @@
from eth2spec.test.context import spec_state_test, always_bls, with_eip6110_and_later
from eth2spec.test.helpers.deposits import (
prepare_deposit_receipt,
run_deposit_receipt_processing,
run_deposit_receipt_processing_with_specific_fork_version
)
from eth2spec.test.helpers.state import next_epoch_via_block
from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable
@with_eip6110_and_later
@spec_state_test
def test_new_deposit_under_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement.
amount = spec.MAX_EFFECTIVE_BALANCE - 1
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_new_deposit_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be exactly the same as balance.
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_new_deposit_over_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing
amount = spec.MAX_EFFECTIVE_BALANCE + 1
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_new_deposit_eth1_withdrawal_credentials(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
withdrawal_credentials = (
spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
+ b'\x00' * 11 # specified 0s
+ b'\x59' * 20 # a 20-byte eth1 address
)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(
spec,
validator_index,
amount,
withdrawal_credentials=withdrawal_credentials,
signed=True,
)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_new_deposit_non_versioned_withdrawal_credentials(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
withdrawal_credentials = (
b'\xFF' # Non specified withdrawal credentials version
+ b'\x02' * 31 # Garabage bytes
)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(
spec,
validator_index,
amount,
withdrawal_credentials=withdrawal_credentials,
signed=True,
)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
@always_bls
def test_correct_sig_but_forked_state(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# deposits will always be valid, regardless of the current fork
state.fork.current_version = spec.Version('0x1234abcd')
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_new_deposit(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, effective=False)
@with_eip6110_and_later
@spec_state_test
def test_top_up__max_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE
state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
@with_eip6110_and_later
@spec_state_test
def test_top_up__less_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000
initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_eip6110_and_later
@spec_state_test
def test_top_up__zero_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True)
initial_balance = 0
initial_effective_balance = 0
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_eip6110_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_incorrect_withdrawal_credentials_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:]
deposit_receipt = prepare_deposit_receipt(
spec,
validator_index,
amount,
withdrawal_credentials=withdrawal_credentials
)
# inconsistent withdrawal credentials, in top-ups, are allowed!
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_key_validate_invalid_subgroup(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey = b'\x00' * 48
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
def test_key_validate_invalid_decompression(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case.
# This pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
pubkey = bytes.fromhex(pubkey_hex)
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
@with_eip6110_and_later
@spec_state_test
@always_bls
def test_ineffective_deposit_with_previous_fork_version(spec, state):
# Since deposits are valid across forks, the domain is always set with `GENESIS_FORK_VERSION`.
# It's an ineffective deposit because it fails at BLS sig verification.
# NOTE: it was effective in Altair.
assert state.fork.previous_version != state.fork.current_version
yield from run_deposit_receipt_processing_with_specific_fork_version(
spec,
state,
fork_version=state.fork.previous_version,
effective=False,
)
@with_eip6110_and_later
@spec_state_test
@always_bls
def test_effective_deposit_with_genesis_fork_version(spec, state):
assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version)
yield from run_deposit_receipt_processing_with_specific_fork_version(
spec,
state,
fork_version=spec.config.GENESIS_FORK_VERSION,
)
@with_eip6110_and_later
@spec_state_test
def test_success_top_up_to_withdrawn_validator(spec, state):
validator_index = 0
# Fully withdraw validator
set_validator_fully_withdrawable(spec, state, validator_index)
assert state.balances[validator_index] > 0
next_epoch_via_block(spec, state)
assert state.balances[validator_index] == 0
assert state.validators[validator_index].effective_balance > 0
next_epoch_via_block(spec, state)
assert state.validators[validator_index].effective_balance == 0
# Make a top-up balance to validator
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, len(state.validators), signed=True)
yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index)
assert state.balances[validator_index] == amount
assert state.validators[validator_index].effective_balance == 0
validator = state.validators[validator_index]
balance = state.balances[validator_index]
current_epoch = spec.get_current_epoch(state)
assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch)

View File

@ -0,0 +1 @@
from .test_deposit_transition import * # noqa: F401 F403

View File

@ -0,0 +1,229 @@
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
)
from eth2spec.test.context import (
spec_state_test,
with_phases,
EIP6110,
)
from eth2spec.test.helpers.deposits import (
build_deposit_data,
deposit_from_context,
prepare_deposit_receipt,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_block_hash,
)
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.state import (
state_transition_and_sign_block
)
def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True):
"""
Run ``process_block``, yielding:
- pre-state ('pre')
- block ('block')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
yield 'pre', state
signed_block = state_transition_and_sign_block(spec, state, block, not valid)
yield 'blocks', [signed_block]
yield 'post', state if valid else None
# Check that deposits are applied
if valid:
expected_pubkeys = [d.data.pubkey for d in block.body.deposits]
deposit_receipts = block.body.execution_payload.deposit_receipts
expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_receipts if (d.pubkey not in top_up_keys)]
actual_pubkeys = [v.pubkey for v in state.validators[len(state.validators) - len(expected_pubkeys):]]
assert actual_pubkeys == expected_pubkeys
def prepare_state_and_block(spec,
state,
deposit_cnt,
deposit_receipt_cnt,
first_deposit_receipt_index=0,
deposit_receipts_start_index=None,
eth1_data_deposit_count=None):
deposits = []
deposit_receipts = []
keypair_index = len(state.validators)
# Prepare deposits
deposit_data_list = []
for index in range(deposit_cnt):
deposit_data = build_deposit_data(spec,
pubkeys[keypair_index],
privkeys[keypair_index],
# use max effective balance
spec.MAX_EFFECTIVE_BALANCE,
# insecurely use pubkey as withdrawal key
spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[keypair_index])[1:],
signed=True)
deposit_data_list.append(deposit_data)
keypair_index += 1
deposit_root = None
for index in range(deposit_cnt):
deposit, deposit_root, _ = deposit_from_context(spec, deposit_data_list, index)
deposits.append(deposit)
if deposit_root:
state.eth1_deposit_index = 0
if not eth1_data_deposit_count:
eth1_data_deposit_count = deposit_cnt
state.eth1_data = spec.Eth1Data(deposit_root=deposit_root,
deposit_count=eth1_data_deposit_count,
block_hash=state.eth1_data.block_hash)
# Prepare deposit receipts
for offset in range(deposit_receipt_cnt):
deposit_receipt = prepare_deposit_receipt(spec,
keypair_index,
# use max effective balance
spec.MAX_EFFECTIVE_BALANCE,
first_deposit_receipt_index + offset,
signed=True)
deposit_receipts.append(deposit_receipt)
keypair_index += 1
# Set start index if defined
if deposit_receipts_start_index:
state.deposit_receipts_start_index = deposit_receipts_start_index
block = build_empty_block_for_next_slot(spec, state)
# Assign deposits and deposit receipts
block.body.deposits = deposits
block.body.execution_payload.deposit_receipts = deposit_receipts
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
return state, block
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__start_index_is_set(spec, state):
# 0 deposits, 2 deposit receipts, unset deposit_receipts_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=0,
deposit_receipt_cnt=2,
first_deposit_receipt_index=state.eth1_data.deposit_count + 11)
yield from run_deposit_transition_block(spec, state, block)
# deposit_receipts_start_index must be set to the index of the first receipt
assert state.deposit_receipts_start_index == block.body.execution_payload.deposit_receipts[0].index
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__process_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7)
yield from run_deposit_transition_block(spec, state, block)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__process_max_eth1_deposits(spec, state):
# spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index
# state.deposit_receipts_start_index == spec.MAX_DEPOSITS
state, block = prepare_state_and_block(spec, state,
deposit_cnt=spec.MAX_DEPOSITS,
deposit_receipt_cnt=1,
first_deposit_receipt_index=spec.MAX_DEPOSITS + 1,
deposit_receipts_start_index=spec.MAX_DEPOSITS,
eth1_data_deposit_count=23)
yield from run_deposit_transition_block(spec, state, block)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__process_eth1_deposits_up_to_start_index(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count == state.deposit_receipts_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=7,
deposit_receipts_start_index=3)
yield from run_deposit_transition_block(spec, state, block)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__invalid_not_enough_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=29,
deposit_receipts_start_index=23,
eth1_data_deposit_count=17)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__invalid_too_many_eth1_deposits(spec, state):
# 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.eth1_data_index
state, block = prepare_state_and_block(spec, state,
deposit_cnt=3,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7,
eth1_data_deposit_count=2)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__invalid_eth1_deposits_overlap_in_protocol_deposits(spec, state):
# spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index
# state.deposit_receipts_start_index == spec.MAX_DEPOSITS - 1
state, block = prepare_state_and_block(spec, state,
deposit_cnt=spec.MAX_DEPOSITS,
deposit_receipt_cnt=1,
first_deposit_receipt_index=spec.MAX_DEPOSITS,
deposit_receipts_start_index=spec.MAX_DEPOSITS - 1,
eth1_data_deposit_count=23)
yield from run_deposit_transition_block(spec, state, block, valid=False)
@with_phases([EIP6110])
@spec_state_test
def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
# 1 deposit, 1 deposit receipt that top ups deposited validator
state, block = prepare_state_and_block(spec, state,
deposit_cnt=1,
deposit_receipt_cnt=1,
first_deposit_receipt_index=11,
deposit_receipts_start_index=7)
# Artificially assign deposit's pubkey to a deposit receipt of the same block
top_up_keys = [block.body.deposits[0].data.pubkey]
block.body.execution_payload.deposit_receipts[0].pubkey = top_up_keys[0]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
yield from run_deposit_transition_block(spec, state, block, top_up_keys=top_up_keys)
# Check the top up
expected_balance = block.body.deposits[0].data.amount + block.body.execution_payload.deposit_receipts[0].amount
assert state.balances[len(state.balances) - 1] == expected_balance

View File

@ -9,30 +9,31 @@ PHASE0 = SpecForkName('phase0')
ALTAIR = SpecForkName('altair')
BELLATRIX = SpecForkName('bellatrix')
CAPELLA = SpecForkName('capella')
DENEB = SpecForkName('deneb')
# Experimental phases (not included in default "ALL_PHASES"):
SHARDING = SpecForkName('sharding')
CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
DENEB = SpecForkName('deneb')
EIP6110 = SpecForkName('eip6110')
# The forks that pytest can run with.
ALL_PHASES = (
# Formal forks
PHASE0, ALTAIR, BELLATRIX, CAPELLA,
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
# Experimental patches
DENEB,
EIP6110,
)
# The forks that output to the test vectors.
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB)
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110)
# TODO: no DENEB fork tests now. Should add when we figure out the content of Capella.
ALL_FORK_UPGRADES = {
# pre_fork_name: post_fork_name
PHASE0: ALTAIR,
ALTAIR: BELLATRIX,
BELLATRIX: CAPELLA,
CAPELLA: DENEB,
DENEB: EIP6110,
}
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0}

View File

@ -171,6 +171,54 @@ def prepare_state_and_deposit(spec, state, validator_index, amount,
return deposit
def build_deposit_receipt(spec,
index,
pubkey,
privkey,
amount,
withdrawal_credentials,
signed):
deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, signed=signed)
return spec.DepositReceipt(
pubkey=deposit_data.pubkey,
withdrawal_credentials=deposit_data.withdrawal_credentials,
amount=deposit_data.amount,
signature=deposit_data.signature,
index=index)
def prepare_deposit_receipt(spec, validator_index, amount,
index=None,
pubkey=None,
privkey=None,
withdrawal_credentials=None,
signed=False):
"""
Create a deposit receipt for the given validator, depositing the given amount.
"""
if index is None:
index = validator_index
if pubkey is None:
pubkey = pubkeys[validator_index]
if privkey is None:
privkey = privkeys[validator_index]
# insecurely use pubkey as withdrawal key if no credentials provided
if withdrawal_credentials is None:
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:]
return build_deposit_receipt(
spec,
index,
pubkey,
privkey,
amount,
withdrawal_credentials,
signed,
)
#
# Run processing
#
@ -255,3 +303,90 @@ def run_deposit_processing_with_specific_fork_version(
state.eth1_data.deposit_count = 1
yield from run_deposit_processing(spec, state, deposit, validator_index, valid=valid, effective=effective)
def run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, valid=True, effective=True):
"""
Run ``process_deposit_receipt``, yielding:
- pre-state ('pre')
- deposit_receipt ('deposit_receipt')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
pre_validator_count = len(state.validators)
pre_balance = 0
is_top_up = False
# is a top-up
if validator_index < pre_validator_count:
is_top_up = True
pre_balance = get_balance(state, validator_index)
pre_effective_balance = state.validators[validator_index].effective_balance
yield 'pre', state
yield 'deposit_receipt', deposit_receipt
if not valid:
expect_assertion_error(lambda: spec.process_deposit_receipt(state, deposit_receipt))
yield 'post', None
return
spec.process_deposit_receipt(state, deposit_receipt)
yield 'post', state
if not effective or not bls.KeyValidate(deposit_receipt.pubkey):
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
if is_top_up:
assert get_balance(state, validator_index) == pre_balance
else:
if is_top_up:
# Top-ups do not change effective balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
assert len(state.validators) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit_receipt.amount)
effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT
assert state.validators[validator_index].effective_balance == effective_balance
assert get_balance(state, validator_index) == pre_balance + deposit_receipt.amount
def run_deposit_receipt_processing_with_specific_fork_version(
spec,
state,
fork_version,
valid=True,
effective=True):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:]
deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version)
deposit_data = spec.DepositData(
pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount,
signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain))
)
deposit_receipt = spec.DepositReceipt(
pubkey=deposit_data.pubkey,
withdrawal_credentials=deposit_data.withdrawal_credentials,
amount=deposit_data.amount,
signature=deposit_data.signature,
index=validator_index)
yield from run_deposit_receipt_processing(
spec,
state,
deposit_receipt,
validator_index,
valid=valid,
effective=effective
)

View File

@ -4,7 +4,11 @@ from rlp import encode
from rlp.sedes import big_endian_int, Binary, List
from eth2spec.debug.random_value import get_random_bytes_list
from eth2spec.test.helpers.forks import is_post_capella, is_post_deneb
from eth2spec.test.helpers.forks import (
is_post_capella,
is_post_deneb,
is_post_eip6110,
)
def get_execution_payload_header(spec, execution_payload):
@ -28,6 +32,8 @@ def get_execution_payload_header(spec, execution_payload):
payload_header.withdrawals_root = spec.hash_tree_root(execution_payload.withdrawals)
if is_post_deneb(spec):
payload_header.excess_data_gas = execution_payload.excess_data_gas
if is_post_eip6110(spec):
payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts)
return payload_header
@ -48,7 +54,8 @@ def compute_trie_root_from_indexed_data(data):
def compute_el_header_block_hash(spec,
payload_header,
transactions_trie_root,
withdrawals_trie_root=None):
withdrawals_trie_root=None,
deposit_receipts_trie_root=None):
"""
Computes the RLP execution block hash described by an `ExecutionPayloadHeader`.
"""
@ -92,6 +99,10 @@ def compute_el_header_block_hash(spec,
if is_post_deneb(spec):
# excess_data_gas
execution_payload_header_rlp.append((big_endian_int, payload_header.excess_data_gas))
if is_post_eip6110(spec):
# deposit_receipts_root
assert deposit_receipts_trie_root is not None
execution_payload_header_rlp.append((Binary(32, 32), deposit_receipts_trie_root))
sedes = List([schema for schema, _ in execution_payload_header_rlp])
values = [value for _, value in execution_payload_header_rlp]
@ -118,14 +129,37 @@ def get_withdrawal_rlp(spec, withdrawal):
return encode(values, sedes)
def get_deposit_receipt_rlp(spec, deposit_receipt):
deposit_receipt_rlp = [
# pubkey
(Binary(48, 48), deposit_receipt.pubkey),
# withdrawal_credentials
(Binary(32, 32), deposit_receipt.withdrawal_credentials),
# amount
(big_endian_int, deposit_receipt.amount),
# pubkey
(Binary(96, 96), deposit_receipt.signature),
# index
(big_endian_int, deposit_receipt.index),
]
sedes = List([schema for schema, _ in deposit_receipt_rlp])
values = [value for _, value in deposit_receipt_rlp]
return encode(values, sedes)
def compute_el_block_hash(spec, payload):
transactions_trie_root = compute_trie_root_from_indexed_data(payload.transactions)
withdrawals_trie_root = None
deposit_receipts_trie_root = None
if is_post_capella(spec):
withdrawals_encoded = [get_withdrawal_rlp(spec, withdrawal) for withdrawal in payload.withdrawals]
withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded)
else:
withdrawals_trie_root = None
if is_post_eip6110(spec):
deposit_receipts_encoded = [get_deposit_receipt_rlp(spec, receipt) for receipt in payload.deposit_receipts]
deposit_receipts_trie_root = compute_trie_root_from_indexed_data(deposit_receipts_encoded)
payload_header = get_execution_payload_header(spec, payload)
@ -134,6 +168,7 @@ def compute_el_block_hash(spec, payload):
payload_header,
transactions_trie_root,
withdrawals_trie_root,
deposit_receipts_trie_root,
)
@ -167,6 +202,9 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
payload.withdrawals = spec.get_expected_withdrawals(state)
if is_post_deneb(spec):
payload.excess_data_gas = 0
if is_post_eip6110(spec):
# just to be clear
payload.deposit_receipts = []
payload.block_hash = compute_el_block_hash(spec, payload)

View File

@ -15,6 +15,7 @@ from eth2spec.test.helpers.constants import (
BELLATRIX,
CAPELLA,
DENEB,
EIP6110,
)
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
@ -158,6 +159,8 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=
state = post_spec.upgrade_to_capella(state)
elif post_spec.fork == DENEB:
state = post_spec.upgrade_to_deneb(state)
elif post_spec.fork == EIP6110:
state = post_spec.upgrade_to_eip6110(state)
assert state.fork.epoch == fork_epoch
@ -173,6 +176,9 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=
elif post_spec.fork == DENEB:
assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION
assert state.fork.current_version == post_spec.config.DENEB_FORK_VERSION
elif post_spec.fork == EIP6110:
assert state.fork.previous_version == post_spec.config.DENEB_FORK_VERSION
assert state.fork.current_version == post_spec.config.EIP6110_FORK_VERSION
if with_block:
return state, _state_transition_and_sign_block_at_slot(

View File

@ -1,9 +1,12 @@
from .constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
EIP6110,
)
def is_post_fork(a, b):
if a == EIP6110:
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110]
if a == DENEB:
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB]
if a == CAPELLA:
@ -31,3 +34,7 @@ def is_post_capella(spec):
def is_post_deneb(spec):
return is_post_fork(spec.fork, DENEB)
def is_post_eip6110(spec):
return is_post_fork(spec.fork, EIP6110)

View File

@ -1,11 +1,11 @@
from eth2spec.test.helpers.constants import (
ALTAIR, BELLATRIX, CAPELLA, DENEB,
ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_header_block_hash,
)
from eth2spec.test.helpers.forks import (
is_post_altair, is_post_bellatrix, is_post_capella,
is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110,
)
from eth2spec.test.helpers.keys import pubkeys
@ -47,17 +47,20 @@ def get_sample_genesis_execution_payload_header(spec,
)
transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
withdrawals_trie_root = None
deposit_receipts_trie_root = None
if is_post_capella(spec):
withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
else:
withdrawals_trie_root = None
if is_post_eip6110(spec):
deposit_receipts_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
payload_header.block_hash = compute_el_header_block_hash(
spec,
payload_header,
transactions_trie_root,
withdrawals_trie_root,
deposit_receipts_trie_root,
)
return payload_header
@ -80,6 +83,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
elif spec.fork == DENEB:
previous_version = spec.config.CAPELLA_FORK_VERSION
current_version = spec.config.DENEB_FORK_VERSION
elif spec.fork == EIP6110:
previous_version = spec.config.DENEB_FORK_VERSION
current_version = spec.config.EIP6110_FORK_VERSION
state = spec.BeaconState(
genesis_time=0,
@ -129,4 +135,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
eth1_block_hash=eth1_block_hash,
)
if is_post_eip6110(spec):
state.deposit_receipts_start_index = spec.UNSET_DEPOSIT_RECEIPTS_START_INDEX
return state

View File

@ -479,7 +479,7 @@ def test_voting_source_within_two_epoch(spec, state):
- store.voting_source[block_root].epoch != store.justified_checkpoint.epoch, and
- store.unrealized_justifications[block_root].epoch >= store.justified_checkpoint.epoch, and
- store.voting_source[block_root].epoch + 2 >= current_epoch, and
- store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot)
- store.finalized_checkpoint.root == get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch)
"""
test_steps = []
# Initialization
@ -536,8 +536,11 @@ def test_voting_source_within_two_epoch(spec, state):
assert store.unrealized_justifications[last_fork_block_root].epoch >= store.justified_checkpoint.epoch
# assert store.voting_source[last_fork_block_root].epoch + 2 >= \
# spec.compute_epoch_at_slot(spec.get_current_slot(store))
finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert store.finalized_checkpoint.root == spec.get_ancestor(store, last_fork_block_root, finalized_slot)
assert store.finalized_checkpoint.root == spec.get_checkpoint_block(
store,
last_fork_block_root,
store.finalized_checkpoint.epoch
)
assert spec.get_head(store) == last_fork_block_root
yield 'steps', test_steps
@ -552,7 +555,7 @@ def test_voting_source_beyond_two_epoch(spec, state):
- store.voting_source[block_root].epoch != store.justified_checkpoint.epoch, and
- store.unrealized_justifications[block_root].epoch >= store.justified_checkpoint.epoch, and
- store.voting_source[block_root].epoch + 2 < current_epoch, and
- store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot)
- store.finalized_checkpoint.root == get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch)
"""
test_steps = []
# Initialization
@ -617,8 +620,11 @@ def test_voting_source_beyond_two_epoch(spec, state):
assert store.unrealized_justifications[last_fork_block_root].epoch >= store.justified_checkpoint.epoch
# assert store.voting_source[last_fork_block_root].epoch + 2 < \
# spec.compute_epoch_at_slot(spec.get_current_slot(store))
finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert store.finalized_checkpoint.root == spec.get_ancestor(store, last_fork_block_root, finalized_slot)
assert store.finalized_checkpoint.root == spec.get_checkpoint_block(
store,
last_fork_block_root,
store.finalized_checkpoint.epoch
)
assert spec.get_head(store) == correct_head
yield 'steps', test_steps
@ -641,7 +647,7 @@ def test_incorrect_finalized(spec, state):
# Check that the store doesn't allow for a head block that has:
# - store.voting_source[block_root].epoch == store.justified_checkpoint.epoch, and
# - store.finalized_checkpoint.epoch != GENESIS_EPOCH, and
# - store.finalized_checkpoint.root != get_ancestor(store, block_root, finalized_slot)
# - store.finalized_checkpoint.root != get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch)
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
@ -718,7 +724,11 @@ def test_incorrect_finalized(spec, state):
assert store.voting_source[last_fork_block_root].epoch == store.justified_checkpoint.epoch
assert store.finalized_checkpoint.epoch != spec.GENESIS_EPOCH
finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
assert store.finalized_checkpoint.root != spec.get_ancestor(store, last_fork_block_root, finalized_slot)
assert store.finalized_checkpoint.root != spec.get_checkpoint_block(
store,
block_root,
store.finalized_checkpoint.epoch
)
assert spec.get_head(store) != last_fork_block_root
assert spec.get_head(store) == head_root

View File

@ -352,8 +352,11 @@ def test_new_finalized_slot_is_not_justified_checkpoint_ancestor(spec, state):
# NOTE: Do not call `on_tick` here
yield from add_block(spec, store, block, test_steps)
finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
ancestor_at_finalized_slot = spec.get_ancestor(store, pre_store_justified_checkpoint_root, finalized_slot)
ancestor_at_finalized_slot = spec.get_checkpoint_block(
store,
pre_store_justified_checkpoint_root,
store.finalized_checkpoint.epoch
)
assert ancestor_at_finalized_slot != store.finalized_checkpoint.root
assert store.finalized_checkpoint == another_state.finalized_checkpoint
@ -428,8 +431,11 @@ def test_new_finalized_slot_is_justified_checkpoint_ancestor(spec, state):
for block in all_blocks:
yield from tick_and_add_block(spec, store, block, test_steps)
finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
ancestor_at_finalized_slot = spec.get_ancestor(store, pre_store_justified_checkpoint_root, finalized_slot)
ancestor_at_finalized_slot = spec.get_checkpoint_block(
store,
pre_store_justified_checkpoint_root,
store.finalized_checkpoint.epoch
)
assert ancestor_at_finalized_slot == store.finalized_checkpoint.root
assert store.finalized_checkpoint == another_state.finalized_checkpoint
@ -857,10 +863,18 @@ def test_incompatible_justification_update_start_of_epoch(spec, state):
# Now add the blocks & check that justification update was triggered
for signed_block in signed_blocks:
yield from tick_and_add_block(spec, store, signed_block, test_steps)
finalized_slot = spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
assert spec.get_ancestor(store, last_block_root, finalized_slot) == state.finalized_checkpoint.root
justified_slot = spec.compute_start_slot_at_epoch(state.current_justified_checkpoint.epoch)
assert spec.get_ancestor(store, last_block_root, justified_slot) != state.current_justified_checkpoint.root
finalized_checkpoint_block = spec.get_checkpoint_block(
store,
last_block_root,
state.finalized_checkpoint.epoch,
)
assert finalized_checkpoint_block == state.finalized_checkpoint.root
justified_checkpoint_block = spec.get_checkpoint_block(
store,
last_block_root,
state.current_justified_checkpoint.epoch,
)
assert justified_checkpoint_block != state.current_justified_checkpoint.root
assert store.finalized_checkpoint.epoch == 4
assert store.justified_checkpoint.epoch == 6
@ -934,10 +948,18 @@ def test_incompatible_justification_update_end_of_epoch(spec, state):
# Now add the blocks & check that justification update was triggered
for signed_block in signed_blocks:
yield from tick_and_add_block(spec, store, signed_block, test_steps)
finalized_slot = spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
assert spec.get_ancestor(store, last_block_root, finalized_slot) == state.finalized_checkpoint.root
justified_slot = spec.compute_start_slot_at_epoch(state.current_justified_checkpoint.epoch)
assert spec.get_ancestor(store, last_block_root, justified_slot) != state.current_justified_checkpoint.root
finalized_checkpoint_block = spec.get_checkpoint_block(
store,
last_block_root,
state.finalized_checkpoint.epoch,
)
assert finalized_checkpoint_block == state.finalized_checkpoint.root
justified_checkpoint_block = spec.get_checkpoint_block(
store,
last_block_root,
state.current_justified_checkpoint.epoch,
)
assert justified_checkpoint_block != state.current_justified_checkpoint.root
assert store.finalized_checkpoint.epoch == 4
assert store.justified_checkpoint.epoch == 6

View File

@ -45,6 +45,7 @@ Operations:
| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) |
| `withdrawals` | `ExecutionPayload` | `execution_payload` | `process_withdrawals(state, execution_payload)` (new in Capella) |
| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `address_change` | `process_bls_to_execution_change(state, address_change)` (new in Capella) |
| `deposit_receipt` | `DepositReceipt` | `deposit_receipt` | `process_deposit_receipt(state, deposit_receipt)` (new in EIP6110) |
Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -34,6 +34,8 @@ if __name__ == "__main__":
deneb_mods = capella_mods
eip6110_mods = deneb_mods
# TODO Custody Game testgen is disabled for now
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [
# 'reveal_deadlines',
@ -47,6 +49,7 @@ if __name__ == "__main__":
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -8,6 +8,7 @@ if __name__ == "__main__":
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
capella_mods = bellatrix_mods # No additional Capella specific finality tests
deneb_mods = capella_mods # No additional Deneb specific finality tests
eip6110_mods = deneb_mods # No additional EIP6110 specific finality tests
all_mods = {
PHASE0: phase_0_mods,
@ -15,6 +16,7 @@ if __name__ == "__main__":
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="finality", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -19,13 +19,15 @@ if __name__ == "__main__":
]}
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
capella_mods = bellatrix_mods # No additional Capella specific fork choice tests
deneb_mods = capella_mods # No additional Capella specific fork choice tests
deneb_mods = capella_mods # No additional Deneb specific fork choice tests
eip6110_mods = deneb_mods # No additional EIP6110 specific fork choice tests
all_mods = {
ALTAIR: altair_mods,
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="fork_choice", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -17,12 +17,14 @@ if __name__ == "__main__":
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
capella_mods = bellatrix_mods # No additional Capella specific genesis tests
deneb_mods = capella_mods # No additional Deneb specific genesis tests
eip6110_mods = deneb_mods # No additional EIP6110 specific genesis tests
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="genesis", all_mods=all_mods)

View File

@ -1,4 +1,4 @@
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
from eth2spec.gen_helpers.gen_from_tests.gen import combine_mods, run_state_test_generators
@ -15,12 +15,14 @@ if __name__ == "__main__":
]}
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
deneb_mods = capella_mods
eip6110_mods = deneb_mods
all_mods = {
ALTAIR: altair_mods,
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="light_client", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -38,6 +38,11 @@ if __name__ == "__main__":
deneb_mods = capella_mods
_new_eip6110_mods = {key: 'eth2spec.test.eip6110.block_processing.test_process_' + key for key in [
'deposit_receipt',
]}
eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods)
# TODO Custody Game testgen is disabled for now
# _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [
# 'attestation',
@ -54,6 +59,7 @@ if __name__ == "__main__":
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="operations", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -17,6 +17,7 @@ if __name__ == "__main__":
bellatrix_mods = altair_mods
capella_mods = bellatrix_mods
deneb_mods = capella_mods
eip6110_mods = deneb_mods
all_mods = {
PHASE0: phase_0_mods,
@ -24,6 +25,7 @@ if __name__ == "__main__":
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="rewards", all_mods=all_mods)

View File

@ -1,4 +1,4 @@
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
@ -28,12 +28,18 @@ if __name__ == "__main__":
]}
deneb_mods = combine_mods(_new_deneb_mods, capella_mods)
_new_eip6110_mods = {key: 'eth2spec.test.eip6110.sanity.' + key for key in [
'blocks',
]}
eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods)
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="sanity", all_mods=all_mods)

View File

@ -1,5 +1,5 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, DENEB
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, DENEB, EIP6110
if __name__ == "__main__":
@ -8,11 +8,13 @@ if __name__ == "__main__":
]}
capella_mods = bellatrix_mods
deneb_mods = capella_mods
eip6110_mods = deneb_mods
all_mods = {
BELLATRIX: bellatrix_mods,
CAPELLA: capella_mods,
DENEB: deneb_mods,
EIP6110: eip6110_mods,
}
run_state_test_generators(runner_name="sync", all_mods=all_mods)