Resolve conflicts

This commit is contained in:
Hsiao-Wei Wang 2021-12-27 18:30:12 +08:00
commit 171a9ccc81
No known key found for this signature in database
GPG Key ID: 1111A8A81778319E
74 changed files with 1077 additions and 538 deletions

View File

@ -116,7 +116,7 @@ jobs:
command: make citest fork=altair
- store_test_results:
path: tests/core/pyspec/test-reports
test-merge:
test-bellatrix:
docker:
- image: circleci/python:3.8
working_directory: ~/specs-repo
@ -126,7 +126,7 @@ jobs:
- restore_pyspec_cached_venv
- run:
name: Run py-tests
command: make citest fork=merge
command: make citest fork=bellatrix
- store_test_results:
path: tests/core/pyspec/test-reports
test-capella:
@ -254,7 +254,7 @@ workflows:
- test-altair:
requires:
- install_pyspec_test
- test-merge:
- test-bellatrix:
requires:
- install_pyspec_test
- test-capella:

2
.gitignore vendored
View File

@ -17,7 +17,7 @@ consensus-spec-tests/
# Dynamically built from Markdown spec
tests/core/pyspec/eth2spec/phase0/
tests/core/pyspec/eth2spec/altair/
tests/core/pyspec/eth2spec/merge/
tests/core/pyspec/eth2spec/bellatrix/
tests/core/pyspec/eth2spec/capella/
# coverage reports

View File

@ -24,7 +24,7 @@ GENERATOR_VENVS = $(patsubst $(GENERATOR_DIR)/%, $(GENERATOR_DIR)/%venv, $(GENER
#$(info $$GENERATOR_TARGETS is [${GENERATOR_TARGETS}])
MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/phase0/*.md) $(wildcard $(SPEC_DIR)/altair/*.md) $(wildcard $(SSZ_DIR)/*.md) \
$(wildcard $(SPEC_DIR)/merge/*.md) \
$(wildcard $(SPEC_DIR)/bellatrix/*.md) \
$(wildcard $(SPEC_DIR)/custody/*.md) \
$(wildcard $(SPEC_DIR)/das/*.md) \
$(wildcard $(SPEC_DIR)/sharding/*.md)
@ -59,7 +59,7 @@ partial_clean:
rm -rf $(DEPOSIT_CONTRACT_TESTER_DIR)/.pytest_cache
rm -rf $(ETH2SPEC_MODULE_DIR)/phase0
rm -rf $(ETH2SPEC_MODULE_DIR)/altair
rm -rf $(ETH2SPEC_MODULE_DIR)/merge
rm -rf $(ETH2SPEC_MODULE_DIR)/bellatrix
rm -rf $(COV_HTML_OUT_DIR)
rm -rf $(TEST_REPORT_DIR)
rm -rf eth2spec.egg-info dist build
@ -97,12 +97,12 @@ install_test:
# Testing against `minimal` config by default
test: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.merge.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
# Testing against `minimal` config by default
find_test: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.merge.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
citest: pyspec
mkdir -p tests/core/pyspec/test-reports/eth2spec;
@ -129,11 +129,11 @@ check_toc: $(MARKDOWN_FILES:=.toc)
codespell:
codespell . --skip ./.git -I .codespell-whitelist
# TODO: add future merge, sharding, etc. packages to linting.
# TODO: add future protocol upgrade patch packages to linting.
lint: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \
flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.merge -p eth2spec.capella
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.bellatrix -p eth2spec.capella
lint_generators: pyspec
. venv/bin/activate; cd $(TEST_GENERATORS_DIR); \

View File

@ -33,24 +33,24 @@ The current features are:
* [Honest Validator guide changes](specs/altair/validator.md)
* [P2P Networking](specs/altair/p2p-interface.md)
### Merge
### Bellatrix (as known as The Merge)
The merge is still actively in development. The exact specification has not been formally accepted as final and details are still subject to change.
The Bellatrix protocol upgrade is still actively in development. The exact specification has not been formally accepted as final and details are still subject to change.
* Background material:
* An [ethresear.ch](https://ethresear.ch) post [describing the basic mechanism](https://ethresear.ch/t/the-eth1-eth2-transition/6265)
* [ethereum.org](https://ethereum.org) high-level description of the merge [here](https://ethereum.org/en/eth2/docking/)
* An [ethresear.ch](https://ethresear.ch) post [describing the basic mechanism of the CL+EL merge](https://ethresear.ch/t/the-eth1-eth2-transition/6265)
* [ethereum.org](https://ethereum.org) high-level description of the CL+EL merge [here](https://ethereum.org/en/eth2/docking/)
* Specifications:
* [Beacon Chain changes](specs/merge/beacon-chain.md)
* [Merge fork](specs/merge/fork.md)
* [Fork Choice changes](specs/merge/fork-choice.md)
* [Validator additions](specs/merge/validator.md)
* [Client settings](specs/merge/client-settings.md)
* [P2P Networking](specs/merge/p2p-interface.md)
* [Beacon Chain changes](specs/bellatrix/beacon-chain.md)
* [Bellatrix fork](specs/bellatrix/fork.md)
* [Fork Choice changes](specs/bellatrix/fork-choice.md)
* [Validator additions](specs/bellatrix/validator.md)
* [Client settings](specs/bellatrix/client-settings.md)
* [P2P Networking](specs/bellatrix/p2p-interface.md)
### Sharding
Sharding follows the merge, and is divided into three parts:
Sharding follows Bellatrix, and is divided into three parts:
* Sharding base functionality - In early engineering phase
* [Beacon Chain changes](specs/sharding/beacon-chain.md)

View File

@ -34,9 +34,9 @@ GENESIS_DELAY: 604800
# Altair
ALTAIR_FORK_VERSION: 0x01000000
ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC
# Merge
MERGE_FORK_VERSION: 0x02000000
MERGE_FORK_EPOCH: 18446744073709551615
# Bellatrix
BELLATRIX_FORK_VERSION: 0x02000000
BELLATRIX_FORK_EPOCH: 18446744073709551615
# Capella
CAPELLA_FORK_VERSION: 0x03000000
CAPELLA_FORK_EPOCH: 18446744073709551615

View File

@ -33,9 +33,9 @@ GENESIS_DELAY: 300
# Altair
ALTAIR_FORK_VERSION: 0x01000001
ALTAIR_FORK_EPOCH: 18446744073709551615
# Merge
MERGE_FORK_VERSION: 0x02000001
MERGE_FORK_EPOCH: 18446744073709551615
# Bellatrix
BELLATRIX_FORK_VERSION: 0x02000001
BELLATRIX_FORK_EPOCH: 18446744073709551615
# Capella
CAPELLA_FORK_VERSION: 0x03000001
CAPELLA_FORK_EPOCH: 18446744073709551615

View File

@ -22,3 +22,5 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256
# ---------------------------------------------------------------
# 1
MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
# SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD (= 32 * 256)
UPDATE_TIMEOUT: 8192

View File

@ -1,13 +1,13 @@
# Mainnet preset - The Merge
# Mainnet preset - Bellatrix
# Updated penalty values
# ---------------------------------------------------------------
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT_MERGE: 16777216
INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216
# 2**5 (= 32)
MIN_SLASHING_PENALTY_QUOTIENT_MERGE: 32
MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32
# 3
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3
PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3
# Execution
# ---------------------------------------------------------------

View File

@ -22,3 +22,5 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8
# ---------------------------------------------------------------
# 1
MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
# SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD (= 8 * 8)
UPDATE_TIMEOUT: 64

View File

@ -1,13 +1,13 @@
# Minimal preset - The Merge
# Minimal preset - Bellatrix
# Updated penalty values
# ---------------------------------------------------------------
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT_MERGE: 16777216
INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216
# 2**5 (= 32)
MIN_SLASHING_PENALTY_QUOTIENT_MERGE: 32
MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32
# 3
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3
PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3
# Execution
# ---------------------------------------------------------------

View File

@ -40,9 +40,10 @@ from marko.ext.gfm.elements import Table
# Definitions in context.py
PHASE0 = 'phase0'
ALTAIR = 'altair'
MERGE = 'merge'
BELLATRIX = 'bellatrix'
CAPELLA = 'capella'
# The helper functions that are used when defining constants
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = '''
def ceillog2(x: int) -> uint64:
@ -488,10 +489,10 @@ def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariab
return super().implement_optimizations(functions)
#
# MergeSpecBuilder
# BellatrixSpecBuilder
#
class MergeSpecBuilder(AltairSpecBuilder):
fork: str = MERGE
class BellatrixSpecBuilder(AltairSpecBuilder):
fork: str = BELLATRIX
@classmethod
def imports(cls, preset_name: str):
@ -552,19 +553,19 @@ EXECUTION_ENGINE = NoopExecutionEngine()"""
#
# CapellaSpecBuilder
#
class CapellaSpecBuilder(MergeSpecBuilder):
class CapellaSpecBuilder(BellatrixSpecBuilder):
fork: str = CAPELLA
@classmethod
def imports(cls, preset_name: str):
return super().imports(preset_name) + f'''
from eth2spec.merge import {preset_name} as merge
from eth2spec.bellatrix import {preset_name} as bellatrix
'''
spec_builders = {
builder.fork: builder
for builder in (Phase0SpecBuilder, AltairSpecBuilder, MergeSpecBuilder, CapellaSpecBuilder)
for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder)
}
@ -697,6 +698,7 @@ ignored_dependencies = [
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
'bytes', 'byte', 'ByteList', 'ByteVector',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
'Optional',
]
@ -859,14 +861,14 @@ class PySpecCommand(Command):
if len(self.md_doc_paths) == 0:
print("no paths were specified, using default markdown file paths for pyspec"
" build (spec fork: %s)" % self.spec_fork)
if self.spec_fork in (PHASE0, ALTAIR, MERGE, CAPELLA):
if self.spec_fork in (PHASE0, ALTAIR, BELLATRIX, CAPELLA):
self.md_doc_paths = """
specs/phase0/beacon-chain.md
specs/phase0/fork-choice.md
specs/phase0/validator.md
specs/phase0/weak-subjectivity.md
"""
if self.spec_fork in (ALTAIR, MERGE, CAPELLA):
if self.spec_fork in (ALTAIR, BELLATRIX, CAPELLA):
self.md_doc_paths += """
specs/altair/beacon-chain.md
specs/altair/bls.md
@ -875,12 +877,12 @@ class PySpecCommand(Command):
specs/altair/p2p-interface.md
specs/altair/sync-protocol.md
"""
if self.spec_fork in (MERGE, CAPELLA):
if self.spec_fork in (BELLATRIX, CAPELLA):
self.md_doc_paths += """
specs/merge/beacon-chain.md
specs/merge/fork.md
specs/merge/fork-choice.md
specs/merge/validator.md
specs/bellatrix/beacon-chain.md
specs/bellatrix/fork.md
specs/bellatrix/fork-choice.md
specs/bellatrix/validator.md
"""
if self.spec_fork == CAPELLA:
self.md_doc_paths += """

View File

@ -13,12 +13,14 @@
- [Preset](#preset)
- [Misc](#misc)
- [Containers](#containers)
- [`LightClientSnapshot`](#lightclientsnapshot)
- [`LightClientUpdate`](#lightclientupdate)
- [`LightClientStore`](#lightclientstore)
- [Helper functions](#helper-functions)
- [`get_subtree_index`](#get_subtree_index)
- [`get_active_header`](#get_active_header)
- [`get_safety_threshold`](#get_safety_threshold)
- [Light client state updates](#light-client-state-updates)
- [`process_slot_for_light_client_store`](#process_slot_for_light_client_store)
- [`validate_light_client_update`](#validate_light_client_update)
- [`apply_light_client_update`](#apply_light_client_update)
- [`process_light_client_update`](#process_light_client_update)
@ -47,38 +49,27 @@ uses sync committees introduced in [this beacon chain extension](./beacon-chain.
### Misc
| Name | Value |
| - | - |
| `MIN_SYNC_COMMITTEE_PARTICIPANTS` | `1` |
| Name | Value | Notes |
| - | - | - |
| `MIN_SYNC_COMMITTEE_PARTICIPANTS` | `1` | |
| `UPDATE_TIMEOUT` | `SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD` | ~27.3 hours |
## Containers
### `LightClientSnapshot`
```python
class LightClientSnapshot(Container):
# Beacon block header
header: BeaconBlockHeader
# Sync committees corresponding to the header
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
```
### `LightClientUpdate`
```python
class LightClientUpdate(Container):
# Update beacon block header
header: BeaconBlockHeader
# Next sync committee corresponding to the header
# The beacon block header that is attested to by the sync committee
attested_header: BeaconBlockHeader
# Next sync committee corresponding to the active header
next_sync_committee: SyncCommittee
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]
# Finality proof for the update header
finality_header: BeaconBlockHeader
# The finalized beacon block header attested to by Merkle branch
finalized_header: BeaconBlockHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE]
sync_committee_signature: BLSSignature
sync_committee_aggregate: SyncAggregate
# Fork version for the aggregate signature
fork_version: Version
```
@ -88,8 +79,18 @@ class LightClientUpdate(Container):
```python
@dataclass
class LightClientStore(object):
snapshot: LightClientSnapshot
valid_updates: Set[LightClientUpdate]
# Beacon block header that is finalized
finalized_header: BeaconBlockHeader
# Sync committees corresponding to the header
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Best available header to switch finalized head to if we see nothing else
best_valid_update: Optional[LightClientUpdate]
# Most recent available reasonably-safe header
optimistic_header: BeaconBlockHeader
# Max number of active participants in a sync committee (used to calculate safety threshold)
previous_max_active_participants: uint64
current_max_active_participants: uint64
```
## Helper functions
@ -101,95 +102,157 @@ def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
```
### `get_active_header`
```python
def get_active_header(update: LightClientUpdate) -> BeaconBlockHeader:
# The "active header" is the header that the update is trying to convince us
# to accept. If a finalized header is present, it's the finalized header,
# otherwise it's the attested header
if update.finalized_header != BeaconBlockHeader():
return update.finalized_header
else:
return update.attested_header
```
### `get_safety_threshold`
```python
def get_safety_threshold(store: LightClientStore) -> uint64:
return max(
store.previous_max_active_participants,
store.current_max_active_participants,
) // 2
```
## Light client state updates
A light client maintains its state in a `store` object of type `LightClientStore` and receives `update` objects of type `LightClientUpdate`. Every `update` triggers `process_light_client_update(store, update, current_slot)` where `current_slot` is the current slot based on some local clock.
A light client maintains its state in a `store` object of type `LightClientStore` and receives `update` objects of type `LightClientUpdate`. Every `update` triggers `process_light_client_update(store, update, current_slot)` where `current_slot` is the current slot based on some local clock. `process_slot_for_light_client_store` is processed every time the current slot increments.
#### `process_slot_for_light_client_store`
```python
def process_slot_for_light_client_store(store: LightClientStore, current_slot: Slot) -> None:
if current_slot % UPDATE_TIMEOUT == 0:
store.previous_max_active_participants = store.current_max_active_participants
store.current_max_active_participants = 0
if (
current_slot > store.finalized_header.slot + UPDATE_TIMEOUT
and store.best_valid_update is not None
):
# Forced best update when the update timeout has elapsed
apply_light_client_update(store, store.best_valid_update)
store.best_valid_update = None
```
#### `validate_light_client_update`
```python
def validate_light_client_update(snapshot: LightClientSnapshot,
def validate_light_client_update(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
# Verify update slot is larger than snapshot slot
assert update.header.slot > snapshot.header.slot
# Verify update slot is larger than slot of current best finalized header
active_header = get_active_header(update)
assert current_slot >= active_header.slot > store.finalized_header.slot
# Verify update does not skip a sync committee period
snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert update_period in (snapshot_period, snapshot_period + 1)
finalized_period = compute_epoch_at_slot(store.finalized_header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(active_header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert update_period in (finalized_period, finalized_period + 1)
# Verify update header root is the finalized root of the finality header, if specified
if update.finality_header == BeaconBlockHeader():
signed_header = update.header
# Verify that the `finalized_header`, if present, actually is the finalized header saved in the
# state of the `attested header`
if update.finalized_header == BeaconBlockHeader():
assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
else:
signed_header = update.finality_header
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.header),
leaf=hash_tree_root(update.finalized_header),
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
root=update.finality_header.state_root,
root=update.attested_header.state_root,
)
# Verify update next sync committee if the update period incremented
if update_period == snapshot_period:
sync_committee = snapshot.current_sync_committee
if update_period == finalized_period:
sync_committee = store.current_sync_committee
assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
else:
sync_committee = snapshot.next_sync_committee
sync_committee = store.next_sync_committee
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
root=update.header.state_root,
root=active_header.state_root,
)
sync_aggregate = update.sync_committee_aggregate
# Verify sync committee has sufficient participants
assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
assert sum(sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
# Verify sync committee aggregate signature
participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit]
participant_pubkeys = [
pubkey for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys)
if bit
]
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root)
signing_root = compute_signing_root(signed_header, domain)
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature)
signing_root = compute_signing_root(update.attested_header, domain)
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
```
#### `apply_light_client_update`
```python
def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None:
snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if update_period == snapshot_period + 1:
snapshot.current_sync_committee = snapshot.next_sync_committee
snapshot.next_sync_committee = update.next_sync_committee
snapshot.header = update.header
def apply_light_client_update(store: LightClientStore, update: LightClientUpdate) -> None:
active_header = get_active_header(update)
finalized_period = compute_epoch_at_slot(store.finalized_header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(active_header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if update_period == finalized_period + 1:
store.current_sync_committee = store.next_sync_committee
store.next_sync_committee = update.next_sync_committee
store.finalized_header = active_header
```
#### `process_light_client_update`
```python
def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot,
def process_light_client_update(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
validate_light_client_update(store.snapshot, update, genesis_validators_root)
store.valid_updates.add(update)
validate_light_client_update(store, update, current_slot, genesis_validators_root)
update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
sync_committee_bits = update.sync_committee_aggregate.sync_committee_bits
# Update the best update in case we have to force-update to it if the timeout elapses
if (
sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2
and update.finality_header != BeaconBlockHeader()
store.best_valid_update is None
or sum(sync_committee_bits) > sum(store.best_valid_update.sync_committee_aggregate.sync_committee_bits)
):
# Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof.
# Note that (2) means that the current light client design needs finality.
# It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182.
apply_light_client_update(store.snapshot, update)
store.valid_updates = set()
elif current_slot > store.snapshot.header.slot + update_timeout:
# Forced best update when the update timeout has elapsed
apply_light_client_update(store.snapshot,
max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits)))
store.valid_updates = set()
store.best_valid_update = update
# Track the maximum number of active participants in the committee signatures
store.current_max_active_participants = max(
store.current_max_active_participants,
sum(sync_committee_bits),
)
# Update the optimistic header
if (
sum(sync_committee_bits) > get_safety_threshold(store)
and update.attested_header.slot > store.optimistic_header.slot
):
store.optimistic_header = update.attested_header
# Update finalized header
if (
sum(sync_committee_bits) * 3 >= len(sync_committee_bits) * 2
and update.finalized_header != BeaconBlockHeader()
):
# Normal update through 2/3 threshold
apply_light_client_update(store, update)
store.best_valid_update = None
```

View File

@ -1,4 +1,4 @@
# The Merge -- The Beacon Chain
# Bellatrix -- The Beacon Chain
**Notice**: This document is a work-in-progress for researchers and implementers.
@ -48,7 +48,7 @@
## Introduction
This upgrade adds transaction execution to the beacon chain as part of the Merge fork.
This upgrade adds transaction execution to the beacon chain as part of Bellatrix upgrade.
Additionally, this upgrade introduces the following minor changes:
* Penalty parameter updates to their planned maximally punitive values
@ -75,15 +75,15 @@ Additionally, this upgrade introduces the following minor changes:
### Updated penalty values
The Merge updates a few configuration values to move penalty parameters to their final, maximum security values.
Bellatrix updates a few configuration values to move penalty parameters to their final, maximum security values.
*Note*: The spec does *not* override previous configuration values but instead creates new values and replaces usage throughout.
| Name | Value |
| - | - |
| `INACTIVITY_PENALTY_QUOTIENT_MERGE` | `uint64(2**24)` (= 16,777,216) |
| `MIN_SLASHING_PENALTY_QUOTIENT_MERGE` | `uint64(2**5)` (= 32) |
| `PROPORTIONAL_SLASHING_MULTIPLIER_MERGE` | `uint64(3)` |
| `INACTIVITY_PENALTY_QUOTIENT_BELLATRIX` | `uint64(2**24)` (= 16,777,216) |
| `MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX` | `uint64(2**5)` (= 32) |
| `PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX` | `uint64(3)` |
## Configuration
@ -114,7 +114,7 @@ class BeaconBlockBody(Container):
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
# Execution
execution_payload: ExecutionPayload # [New in Merge]
execution_payload: ExecutionPayload # [New in Bellatrix]
```
#### `BeaconState`
@ -156,7 +156,7 @@ class BeaconState(Container):
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader # [New in Merge]
latest_execution_payload_header: ExecutionPayloadHeader # [New in Bellatrix]
```
### New containers
@ -246,7 +246,7 @@ def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64:
#### Modified `get_inactivity_penalty_deltas`
*Note*: The function `get_inactivity_penalty_deltas` is modified to use `INACTIVITY_PENALTY_QUOTIENT_MERGE`.
*Note*: The function `get_inactivity_penalty_deltas` is modified to use `INACTIVITY_PENALTY_QUOTIENT_BELLATRIX`.
```python
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
@ -260,8 +260,8 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
for index in get_eligible_validator_indices(state):
if index not in matching_target_indices:
penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index]
# [Modified in Merge]
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE
# [Modified in Bellatrix]
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
return rewards, penalties
```
@ -270,7 +270,7 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
#### Modified `slash_validator`
*Note*: The function `slash_validator` is modified to use `MIN_SLASHING_PENALTY_QUOTIENT_MERGE`.
*Note*: The function `slash_validator` is modified to use `MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX`.
```python
def slash_validator(state: BeaconState,
@ -285,7 +285,7 @@ def slash_validator(state: BeaconState,
validator.slashed = True
validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge]
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX # [Modified in Bellatrix]
decrease_balance(state, slashed_index, slashing_penalty)
# Apply proposer and whistleblower rewards
@ -332,7 +332,7 @@ def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload)
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
if is_execution_enabled(state, block.body):
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge]
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Bellatrix]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body)
@ -377,14 +377,14 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
#### Slashings
*Note*: The function `process_slashings` is modified to use `PROPORTIONAL_SLASHING_MULTIPLIER_MERGE`.
*Note*: The function `process_slashings` is modified to use `PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX`.
```python
def process_slashings(state: BeaconState) -> None:
epoch = get_current_epoch(state)
total_balance = get_total_active_balance(state)
adjusted_total_slashing_balance = min(
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_MERGE, # [Modified in Merge]
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX, # [Modified in Bellatrix]
total_balance
)
for index, validator in enumerate(state.validators):
@ -397,10 +397,10 @@ def process_slashings(state: BeaconState) -> None:
## Testing
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Merge testing only.
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Bellatrix testing only.
Modifications include:
1. Use `MERGE_FORK_VERSION` as the current fork version.
2. Utilize the Merge `BeaconBlockBody` when constructing the initial `latest_block_header`.
1. Use `BELLATRIX_FORK_VERSION` as the current fork version.
2. Utilize the Bellatrix `BeaconBlockBody` when constructing the initial `latest_block_header`.
3. Initialize `latest_execution_payload_header`.
If `execution_payload_header == ExecutionPayloadHeader()`, then the Merge has not yet occurred.
Else, the Merge starts from genesis and the transition is incomplete.
@ -412,8 +412,8 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader()
) -> BeaconState:
fork = Fork(
previous_version=MERGE_FORK_VERSION, # [Modified in Merge] for testing only
current_version=MERGE_FORK_VERSION, # [Modified in Merge]
previous_version=BELLATRIX_FORK_VERSION, # [Modified in Bellatrix] for testing only
current_version=BELLATRIX_FORK_VERSION, # [Modified in Bellatrix]
epoch=GENESIS_EPOCH,
)
state = BeaconState(
@ -447,7 +447,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
state.current_sync_committee = get_next_sync_committee(state)
state.next_sync_committee = get_next_sync_committee(state)
# [New in Merge] Initialize the execution payload header
# [New in Bellatrix] Initialize the execution payload header
# If empty, will initialize a chain that has not yet gone through the Merge transition
state.latest_execution_payload_header = execution_payload_header

View File

@ -1,4 +1,4 @@
# The Merge -- Fork Choice
# Bellatrix -- Fork Choice
**Notice**: This document is a work-in-progress for researchers and implementers.
@ -170,7 +170,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
state = pre_state.copy()
state_transition(state, signed_block, True)
# [New in Merge]
# [New in Bellatrix]
if is_merge_transition_block(pre_state, block.body):
validate_merge_block(block)

View File

@ -1,4 +1,4 @@
# The Merge -- Fork Logic
# Bellatrix -- Fork Logic
**Notice**: This document is a work-in-progress for researchers and implementers.
@ -9,7 +9,7 @@
- [Introduction](#introduction)
- [Configuration](#configuration)
- [Fork to Merge](#fork-to-merge)
- [Fork to Bellatrix](#fork-to-bellatrix)
- [Fork trigger](#fork-trigger)
- [Upgrading the state](#upgrading-the-state)
@ -17,7 +17,7 @@
## Introduction
This document describes the process of the Merge upgrade.
This document describes the process of Bellatrix upgrade.
## Configuration
@ -25,30 +25,30 @@ Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `MERGE_FORK_VERSION` | `Version('0x02000000')` |
| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
| `BELLATRIX_FORK_VERSION` | `Version('0x02000000')` |
| `BELLATRIX_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
## Fork to Merge
## Fork to Bellatrix
### Fork trigger
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at epoch `MERGE_FORK_EPOCH`.
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at epoch `BELLATRIX_FORK_EPOCH`.
Note that for the pure Merge networks, we don't apply `upgrade_to_merge` since it starts with Merge version logic.
Note that for the pure Bellatrix networks, we don't apply `upgrade_to_bellatrix` since it starts with Bellatrix version logic.
### Upgrading the state
As with the Phase0-to-Altair upgrade, the `state_transition` is modified to upgrade the `BeaconState`.
The `BeaconState` upgrade runs as part of `process_slots`, slots with missing block proposals do not affect the upgrade time.
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, an irregular state change is made to upgrade to Merge.
The upgrade occurs after the completion of the inner loop of `process_slots` that sets `state.slot` equal to `MERGE_FORK_EPOCH * SLOTS_PER_EPOCH`.
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == BELLATRIX_FORK_EPOCH`, an irregular state change is made to upgrade to Bellatrix.
The upgrade occurs after the completion of the inner loop of `process_slots` that sets `state.slot` equal to `BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH`.
When multiple upgrades are scheduled for the same epoch (common for test-networks),
all the upgrades run in sequence before resuming the regular state transition.
```python
def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState:
def upgrade_to_bellatrix(pre: altair.BeaconState) -> BeaconState:
epoch = altair.get_current_epoch(pre)
post = BeaconState(
# Versioning
@ -57,7 +57,7 @@ def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState:
slot=pre.slot,
fork=Fork(
previous_version=pre.fork.current_version,
current_version=MERGE_FORK_VERSION,
current_version=BELLATRIX_FORK_VERSION,
epoch=epoch,
),
# History

View File

@ -1,6 +1,6 @@
# The Merge -- Networking
# Bellatrix -- Networking
This document contains the networking specification for the Merge.
This document contains the networking specification for the Bellatrix.
The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. This document should be viewed as additive to the documents from [Phase 0](../phase0/p2p-interface.md) and from [Altair](../altair/p2p-interface.md)
and will be referred to as the "Phase 0 document" and "Altair document" respectively, hereafter.
@ -13,7 +13,7 @@ Readers should understand the Phase 0 and Altair documents and use them as a bas
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Warning](#warning)
- [Modifications in the Merge](#modifications-in-the-merge)
- [Modifications in Bellatrix](#modifications-in-bellatrix)
- [Configuration](#configuration)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
@ -26,19 +26,19 @@ Readers should understand the Phase 0 and Altair documents and use them as a bas
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
- [Design decision rationale](#design-decision-rationale)
- [Gossipsub](#gossipsub)
- [Why was the max gossip message size increased at the Merge?](#why-was-the-max-gossip-message-size-increased-at-the-merge)
- [Why was the max gossip message size increased at Bellatrix?](#why-was-the-max-gossip-message-size-increased-at-bellatrix)
- [Req/Resp](#reqresp)
- [Why was the max chunk response size increased at the Merge?](#why-was-the-max-chunk-response-size-increased-at-the-merge)
- [Why was the max chunk response size increased at Bellatrix?](#why-was-the-max-chunk-response-size-increased-at-bellatrix)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Warning
This document is currently illustrative for early Merge testnets and some parts are subject to change.
This document is currently illustrative for early Bellatrix testnets and some parts are subject to change.
Refer to the note in the [validator guide](./validator.md) for further details.
# Modifications in the Merge
# Modifications in Bellatrix
## Configuration
@ -46,12 +46,12 @@ This section outlines modifications constants that are used in this spec.
| Name | Value | Description |
|---|---|---|
| `GOSSIP_MAX_SIZE_MERGE` | `10 * 2**20` (= 10,485,760, 10 MiB) | The maximum allowed size of uncompressed gossip messages starting at the Merge upgrade. |
| `MAX_CHUNK_SIZE_MERGE` | `10 * 2**20` (= 10,485,760, 10 MiB) | The maximum allowed size of uncompressed req/resp chunked responses starting at the Merge upgrade. |
| `GOSSIP_MAX_SIZE_BELLATRIX` | `10 * 2**20` (= 10,485,760, 10 MiB) | The maximum allowed size of uncompressed gossip messages starting at Bellatrix upgrade. |
| `MAX_CHUNK_SIZE_BELLATRIX` | `10 * 2**20` (= 10,485,760, 10 MiB) | The maximum allowed size of uncompressed req/resp chunked responses starting at Bellatrix upgrade. |
## The gossip domain: gossipsub
Some gossip meshes are upgraded in the Merge to support upgraded types.
Some gossip meshes are upgraded in Bellatrix to support upgraded types.
### Topics and messages
@ -60,8 +60,8 @@ All topics remain stable except the beacon block topic which is updated with the
The specification around the creation, validation, and dissemination of messages has not changed from the Phase 0 and Altair documents unless explicitly noted here.
Starting at the Merge upgrade, each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto#L17-L24)
has a maximum size of `GOSSIP_MAX_SIZE_MERGE`.
Starting at Bellatrix upgrade, each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto#L17-L24)
has a maximum size of `GOSSIP_MAX_SIZE_BELLATRIX`.
Clients MUST reject (fail validation) messages that are over this size limit.
Likewise, clients MUST NOT emit or propagate messages larger than this limit.
@ -77,13 +77,13 @@ Note that the `ForkDigestValue` path segment of the topic separates the old and
#### Global topics
The Merge changes the type of the global beacon block topic.
Bellatrix changes the type of the global beacon block topic.
##### `beacon_block`
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in the Merge.
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in Bellatrix.
Specifically, this type changes with the addition of `execution_payload` to the inner `BeaconBlockBody`.
See the Merge [state transition document](./beacon-chain.md#beaconblockbody) for further details.
See Bellatrix [state transition document](./beacon-chain.md#beaconblockbody) for further details.
In addition to the gossip validations for this topic from prior specifications,
the following validations MUST pass before forwarding the `signed_beacon_block` on the network.
@ -96,7 +96,7 @@ Alias `block = signed_beacon_block.message`, `execution_payload = block.body.exe
### Transitioning the gossip
See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for
details on how to handle transitioning gossip topics for the Merge.
details on how to handle transitioning gossip topics for Bellatrix.
## The Req/Resp domain
@ -108,11 +108,11 @@ details on how to handle transitioning gossip topics for the Merge.
Request and Response remain unchanged unless explicitly noted here.
Starting at the Merge upgrade,
a global maximum uncompressed byte size of `MAX_CHUNK_SIZE_MERGE` MUST be applied to all method response chunks
Starting at Bellatrix upgrade,
a global maximum uncompressed byte size of `MAX_CHUNK_SIZE_BELLATRIX` MUST be applied to all method response chunks
regardless of type specific bounds that *MUST* also be respected.
The Merge fork-digest is introduced to the `context` enum to specify the Merge block type.
Bellatrix fork-digest is introduced to the `context` enum to specify Bellatrix block type.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
@ -122,14 +122,14 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
| ------------------------ | -------------------------- |
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
| `MERGE_FORK_VERSION` | `merge.SignedBeaconBlock` |
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
#### BeaconBlocksByRoot v2
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/`
Request and Response remain unchanged.
The Merge fork-digest is introduced to the `context` enum to specify the Merge block type.
Bellatrix fork-digest is introduced to the `context` enum to specify Bellatrix block type.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
@ -139,13 +139,13 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
| ------------------------ | -------------------------- |
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
| `MERGE_FORK_VERSION` | `merge.SignedBeaconBlock` |
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
# Design decision rationale
## Gossipsub
### Why was the max gossip message size increased at the Merge?
### Why was the max gossip message size increased at Bellatrix?
With the addition of `ExecutionPayload` to `BeaconBlock`s, there is a dynamic
field -- `transactions` -- which can validly exceed the `GOSSIP_MAX_SIZE` limit (1 MiB) put in place in
@ -156,9 +156,9 @@ current mainnet conditions.
Geth currently has a [max gossip message size](https://github.com/ethereum/go-ethereum/blob/3ce9f6d96f38712f5d6756e97b59ccc20cc403b3/eth/protocols/eth/protocol.go#L49) of 10 MiB.
To support backward compatibility with this previously defined network limit,
we adopt `GOSSIP_MAX_SIZE_MERGE` of 10 MiB for maximum gossip sizes at the
point of the Merge and beyond. Note, that clients SHOULD still reject objects
that exceed their maximum theoretical bounds which in most cases is less than `GOSSIP_MAX_SIZE_MERGE`.
we adopt `GOSSIP_MAX_SIZE_BELLATRIX` of 10 MiB for maximum gossip sizes at the
point of Bellatrix and beyond. Note, that clients SHOULD still reject objects
that exceed their maximum theoretical bounds which in most cases is less than `GOSSIP_MAX_SIZE_BELLATRIX`.
Note, that due to additional size induced by the `BeaconBlock` contents (e.g.
proposer signature, operations lists, etc) this does reduce the
@ -170,7 +170,7 @@ impact on network functionality and security.
## Req/Resp
### Why was the max chunk response size increased at the Merge?
### Why was the max chunk response size increased at Bellatrix?
Similar to the discussion about the maximum gossip size increase, the
`ExecutionPayload` type can cause `BeaconBlock`s to exceed the 1 MiB bounds put

View File

@ -1,4 +1,4 @@
# The Merge -- Honest Validator
# Bellatrix -- Honest Validator
**Notice**: This document is a work-in-progress for researchers and implementers.
@ -33,7 +33,7 @@ This document represents the changes to be made in the code of an "honest valida
This document is an extension of the [Altair -- Honest Validator](../altair/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 [The Merge](./beacon-chain.md) are requisite for this document and used throughout.
All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [Bellatrix](./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.
## Helpers

View File

@ -39,8 +39,8 @@ Care must be taken when transitioning through the fork boundary as implementatio
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_capella(pre: merge.BeaconState) -> BeaconState:
epoch = merge.get_current_epoch(pre)
def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState:
epoch = bellatrix.get_current_epoch(pre)
post = BeaconState(
# Versioning
genesis_time=pre.genesis_time,

View File

@ -44,7 +44,7 @@ These configurations are updated for releases and may be out of sync during `dev
## Staking deposit contract
The initial deployment phases of Ethereum proof-of-stake are implemented without consensus changes to the existing Ethereum proof-of-work chain. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to the Ethereum proof-of-work chain defined by the [chain-id](https://eips.ethereum.org/EIPS/eip-155) -- `DEPOSIT_CHAIN_ID` -- and the network-id -- `DEPOSIT_NETWORK_ID` -- for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the execution-layer in a followup fork after the Merge.
The initial deployment phases of Ethereum proof-of-stake are implemented without consensus changes to the existing Ethereum proof-of-work chain. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to the Ethereum proof-of-work chain defined by the [chain-id](https://eips.ethereum.org/EIPS/eip-155) -- `DEPOSIT_CHAIN_ID` -- and the network-id -- `DEPOSIT_NETWORK_ID` -- for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the execution-layer in a followup fork after Bellatrix upgrade.
_Note_: See [here](https://chainid.network/) for a comprehensive list of public Ethereum chain chain-id's and network-id's.
@ -54,7 +54,7 @@ The deposit contract has a public `deposit` function to make deposits. It takes
#### Deposit amount
The amount of ETH (rounded down to the closest Gwei) sent to the deposit contract is the deposit amount, which must be of size at least `MIN_DEPOSIT_AMOUNT` Gwei. Note that ETH consumed by the deposit contract is no longer usable on the execution-layer until sometime after the Merge.
The amount of ETH (rounded down to the closest Gwei) sent to the deposit contract is the deposit amount, which must be of size at least `MIN_DEPOSIT_AMOUNT` Gwei. Note that ETH consumed by the deposit contract is no longer usable on the execution-layer until sometime after Bellatrix upgrade.
#### Withdrawal credentials

View File

@ -181,15 +181,19 @@ def get_latest_attesting_balance(store: Store, root: Root) -> Gwei:
if (i in store.latest_messages
and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
))
if store.proposer_boost_root == Root():
# Return only attestation score if ``proposer_boost_root`` is not set
return attestation_score
# Calculate proposer score if ``proposer_boost_root`` is set
proposer_score = Gwei(0)
if store.proposer_boost_root != Root():
block = store.blocks[root]
if get_ancestor(store, root, block.slot) == store.proposer_boost_root:
num_validators = len(get_active_validator_indices(state, get_current_epoch(state)))
avg_balance = get_total_active_balance(state) // num_validators
committee_size = num_validators // SLOTS_PER_EPOCH
committee_weight = committee_size * avg_balance
proposer_score = (committee_weight * PROPOSER_SCORE_BOOST) // 100
# Boost is applied if ``root`` is an ancestor of ``proposer_boost_root``
if get_ancestor(store, store.proposer_boost_root, store.blocks[root].slot) == root:
num_validators = len(get_active_validator_indices(state, get_current_epoch(state)))
avg_balance = get_total_active_balance(state) // num_validators
committee_size = num_validators // SLOTS_PER_EPOCH
committee_weight = committee_size * avg_balance
proposer_score = (committee_weight * PROPOSER_SCORE_BOOST) // 100
return attestation_score + proposer_score
```
@ -263,6 +267,7 @@ def get_head(store: Store) -> Root:
if len(children) == 0:
return head
# Sort by latest attesting balance with ties broken lexicographically
# Ties broken by favoring block with lexicographically higher root
head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root))
```

View File

@ -101,7 +101,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Notes |
| - | - | - |
| `PRIMITIVE_ROOT_OF_UNITY` | `5` | Primitive root of unity of the BLS12_381 (inner) modulus |
| `PRIMITIVE_ROOT_OF_UNITY` | `7` | Primitive root of unity of the BLS12_381 (inner) modulus |
| `DATA_AVAILABILITY_INVERSE_CODING_RATE` | `2**1` (= 2) | Factor by which samples are extended for data availability encoding |
| `POINTS_PER_SAMPLE` | `uint64(2**3)` (= 8) | 31 * 8 = 248 bytes |
| `MODULUS` | `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (curve order of BLS12_381) |
@ -207,7 +207,7 @@ class AttestationData(Container):
### `BeaconBlockBody`
```python
class BeaconBlockBody(merge.BeaconBlockBody): # [extends The Merge block body]
class BeaconBlockBody(bellatrix.BeaconBlockBody): # [extends Bellatrix block body]
shard_proposer_slashings: List[ShardProposerSlashing, MAX_SHARD_PROPOSER_SLASHINGS]
shard_headers: List[SignedShardBlobHeader, MAX_SHARDS * MAX_SHARD_HEADERS_PER_SHARD]
```
@ -215,7 +215,7 @@ class BeaconBlockBody(merge.BeaconBlockBody): # [extends The Merge block body]
### `BeaconState`
```python
class BeaconState(merge.BeaconState):
class BeaconState(bellatrix.BeaconState):
# Blob builder registry.
blob_builders: List[Builder, BLOB_BUILDER_REGISTRY_LIMIT]
blob_builder_balances: List[Gwei, BLOB_BUILDER_REGISTRY_LIMIT]
@ -804,7 +804,7 @@ def process_shard_proposer_slashing(state: BeaconState, proposer_slashing: Shard
### Epoch transition
This epoch transition overrides the Merge epoch transition:
This epoch transition overrides Bellatrix epoch transition:
```python
def process_epoch(state: BeaconState) -> None:

View File

@ -31,10 +31,10 @@ To read more about creating the environment, [see here](core/pyspec/README.md).
cd ~/consensus-specs
. venv/bin/activate
```
1. Run a sanity check test:
1. Run a sanity check test against Altair fork:
```sh
cd tests/core/pyspec
python -m pytest -k test_empty_block_transition --fork Merge eth2spec
python -m pytest -k test_empty_block_transition --fork altair eth2spec
```
1. The output should be similar to:
```
@ -44,7 +44,7 @@ To read more about creating the environment, [see here](core/pyspec/README.md).
plugins: cov-2.12.1, forked-1.3.0, xdist-2.3.0
collected 629 items / 626 deselected / 3 selected
eth2spec/test/merge/sanity/test_blocks.py . [ 33%]
eth2spec/test/bellatrix/sanity/test_blocks.py . [ 33%]
eth2spec/test/phase0/sanity/test_blocks.py .. [100%]
=============================== warnings summary ===============================
@ -448,13 +448,13 @@ def test_almost_after_epoch_slots(spec, state):
```
Add this function to the file `consensus-specs/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py`,
and run the test:
and run the test against Altair fork:
```sh
cd ~/consensus-specs
. venv/bin/activate
cd tests/core/pyspec
python -m pytest -k almost_after --fork Merge eth2spec
python -m pytest -k almost_after --fork altair eth2spec
```
You should see it ran successfully (although you might get a warning, you can ignore it)

View File

@ -1 +1 @@
1.1.6
1.1.8

View File

@ -1,3 +1,5 @@
from copy import deepcopy
from eth2spec.test.context import (
spec_state_test,
with_presets,
@ -19,20 +21,24 @@ from eth2spec.test.helpers.sync_committee import (
from eth2spec.test.helpers.merkle import build_proof
@with_altair_and_later
@spec_state_test
def test_process_light_client_update_not_updated(spec, state):
pre_snapshot = spec.LightClientSnapshot(
header=spec.BeaconBlockHeader(),
def _initialize_light_client_store(spec, state):
return spec.LightClientStore(
finalized_header=spec.BeaconBlockHeader(),
current_sync_committee=state.current_sync_committee,
next_sync_committee=state.next_sync_committee,
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=set(),
best_valid_update=None,
optimistic_header=spec.BeaconBlockHeader(),
previous_max_active_participants=0,
current_max_active_participants=0,
)
# Block at slot 1 doesn't increase sync committee period, so it won't update snapshot
@with_altair_and_later
@spec_state_test
def test_process_light_client_update_not_timeout(spec, state):
store = _initialize_light_client_store(spec, state)
# Block at slot 1 doesn't increase sync committee period, so it won't force update store.finalized_header
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
block_header = spec.BeaconBlockHeader(
@ -52,6 +58,10 @@ def test_process_light_client_update_not_updated(spec, state):
block_header.slot,
committee,
)
sync_committee_aggregate = spec.SyncAggregate(
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
)
next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))]
# Ensure that finality checkpoint is genesis
@ -61,40 +71,34 @@ def test_process_light_client_update_not_updated(spec, state):
finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))]
update = spec.LightClientUpdate(
header=block_header,
attested_header=block_header,
next_sync_committee=state.next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finality_header=finality_header,
finalized_header=finality_header,
finality_branch=finality_branch,
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
sync_committee_aggregate=sync_committee_aggregate,
fork_version=state.fork.current_version,
)
pre_store = deepcopy(store)
spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root)
assert len(store.valid_updates) == 1
assert store.valid_updates.pop() == update
assert store.snapshot == pre_snapshot
assert store.current_max_active_participants > 0
assert store.optimistic_header == update.attested_header
assert store.finalized_header == pre_store.finalized_header
assert store.best_valid_update == update
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
def test_process_light_client_update_timeout(spec, state):
pre_snapshot = spec.LightClientSnapshot(
header=spec.BeaconBlockHeader(),
current_sync_committee=state.current_sync_committee,
next_sync_committee=state.next_sync_committee,
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=set(),
)
store = _initialize_light_client_store(spec, state)
# Forward to next sync committee period
next_slots(spec, state, spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD))
snapshot_period = spec.compute_epoch_at_slot(pre_snapshot.header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
next_slots(spec, state, spec.UPDATE_TIMEOUT)
snapshot_period = spec.compute_epoch_at_slot(store.optimistic_header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = spec.compute_epoch_at_slot(state.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert snapshot_period + 1 == update_period
@ -119,6 +123,10 @@ def test_process_light_client_update_timeout(spec, state):
committee,
block_root=spec.Root(block_header.hash_tree_root()),
)
sync_committee_aggregate = spec.SyncAggregate(
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
)
# Sync committee is updated
next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX)
@ -127,36 +135,30 @@ def test_process_light_client_update_timeout(spec, state):
finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))]
update = spec.LightClientUpdate(
header=block_header,
attested_header=block_header,
next_sync_committee=state.next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finality_header=finality_header,
finalized_header=finality_header,
finality_branch=finality_branch,
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
sync_committee_aggregate=sync_committee_aggregate,
fork_version=state.fork.current_version,
)
pre_store = deepcopy(store)
spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root)
# snapshot has been updated
assert len(store.valid_updates) == 0
assert store.snapshot.header == update.header
assert store.current_max_active_participants > 0
assert store.optimistic_header == update.attested_header
assert store.best_valid_update == update
assert store.finalized_header == pre_store.finalized_header
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
def test_process_light_client_update_finality_updated(spec, state):
pre_snapshot = spec.LightClientSnapshot(
header=spec.BeaconBlockHeader(),
current_sync_committee=state.current_sync_committee,
next_sync_committee=state.next_sync_committee,
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=set(),
)
store = _initialize_light_client_store(spec, state)
# Change finality
blocks = []
@ -167,7 +169,7 @@ def test_process_light_client_update_finality_updated(spec, state):
# Ensure that finality checkpoint has changed
assert state.finalized_checkpoint.epoch == 3
# Ensure that it's same period
snapshot_period = spec.compute_epoch_at_slot(pre_snapshot.header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
snapshot_period = spec.compute_epoch_at_slot(store.optimistic_header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = spec.compute_epoch_at_slot(state.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert snapshot_period == update_period
@ -199,20 +201,24 @@ def test_process_light_client_update_finality_updated(spec, state):
committee,
block_root=spec.Root(block_header.hash_tree_root()),
)
update = spec.LightClientUpdate(
header=finalized_block_header,
next_sync_committee=state.next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finality_header=block_header, # block_header is the signed header
finality_branch=finality_branch,
sync_committee_aggregate = spec.SyncAggregate(
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
)
update = spec.LightClientUpdate(
attested_header=block_header,
next_sync_committee=state.next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finalized_header=finalized_block_header,
finality_branch=finality_branch,
sync_committee_aggregate=sync_committee_aggregate,
fork_version=state.fork.current_version,
)
spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root)
# snapshot has been updated
assert len(store.valid_updates) == 0
assert store.snapshot.header == update.header
assert store.current_max_active_participants > 0
assert store.optimistic_header == update.attested_header
assert store.finalized_header == update.finalized_header
assert store.best_valid_update is None

View File

@ -4,7 +4,7 @@ from eth2spec.test.helpers.execution_payload import (
build_state_with_incomplete_transition,
build_state_with_complete_transition,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_bellatrix_and_later
from eth2spec.test.helpers.state import next_slot
@ -46,7 +46,7 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True,
assert state.latest_execution_payload_header == get_execution_payload_header(spec, execution_payload)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_success_first_payload(spec, state):
# pre-state
@ -59,7 +59,7 @@ def test_success_first_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_success_regular_payload(spec, state):
# pre-state
@ -72,7 +72,7 @@ def test_success_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_success_first_payload_with_gap_slot(spec, state):
# pre-state
@ -86,7 +86,7 @@ def test_success_first_payload_with_gap_slot(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_success_regular_payload_with_gap_slot(spec, state):
# pre-state
@ -100,7 +100,7 @@ def test_success_regular_payload_with_gap_slot(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_execution_first_payload(spec, state):
# completely valid payload, but execution itself fails (e.g. block exceeds gas limit)
@ -115,7 +115,7 @@ def test_bad_execution_first_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_execution_regular_payload(spec, state):
# completely valid payload, but execution itself fails (e.g. block exceeds gas limit)
@ -130,7 +130,7 @@ def test_bad_execution_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_parent_hash_regular_payload(spec, state):
# pre-state
@ -144,7 +144,7 @@ def test_bad_parent_hash_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_random_first_payload(spec, state):
# pre-state
@ -158,7 +158,7 @@ def test_bad_random_first_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_random_regular_payload(spec, state):
# pre-state
@ -172,7 +172,7 @@ def test_bad_random_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_everything_regular_payload(spec, state):
# pre-state
@ -188,7 +188,7 @@ def test_bad_everything_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_timestamp_first_payload(spec, state):
# pre-state
@ -202,7 +202,7 @@ def test_bad_timestamp_first_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_bad_timestamp_regular_payload(spec, state):
# pre-state

View File

@ -7,76 +7,76 @@ from eth2spec.test.context import (
)
from eth2spec.test.utils import with_meta_tags
from eth2spec.test.helpers.constants import (
ALTAIR, MERGE,
ALTAIR, BELLATRIX,
MINIMAL,
)
from eth2spec.test.helpers.state import (
next_epoch,
next_epoch_via_block,
)
from eth2spec.test.helpers.merge.fork import (
MERGE_FORK_TEST_META_TAGS,
from eth2spec.test.helpers.bellatrix.fork import (
BELLATRIX_FORK_TEST_META_TAGS,
run_fork_test,
)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_base_state(spec, phases, state):
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_next_epoch(spec, phases, state):
next_epoch(spec, state)
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_next_epoch_with_block(spec, phases, state):
next_epoch_via_block(spec, state)
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_many_next_epoch(spec, phases, state):
for _ in range(3):
next_epoch(spec, state)
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@spec_test
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_random_low_balances(spec, phases, state):
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@spec_test
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_random_misc_balances(spec, phases, state):
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@spec_test
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_fork_random_large_validator_set(spec, phases, state):
yield from run_fork_test(phases[MERGE], state)
yield from run_fork_test(phases[BELLATRIX], state)

View File

@ -0,0 +1,84 @@
from random import Random
from eth2spec.test.context import (
with_phases,
with_custom_state,
with_presets,
spec_test, with_state,
low_balances, misc_balances, large_validator_set,
)
from eth2spec.test.utils import with_meta_tags
from eth2spec.test.helpers.constants import (
ALTAIR, BELLATRIX,
MINIMAL,
)
from eth2spec.test.helpers.bellatrix.fork import (
BELLATRIX_FORK_TEST_META_TAGS,
run_fork_test,
)
from eth2spec.test.helpers.random import randomize_state
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_0(spec, phases, state):
randomize_state(spec, state, rng=Random(1010))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_1(spec, phases, state):
randomize_state(spec, state, rng=Random(2020))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_2(spec, phases, state):
randomize_state(spec, state, rng=Random(3030))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_state
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_3(spec, phases, state):
randomize_state(spec, state, rng=Random(4040))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_low_balances(spec, phases, state):
randomize_state(spec, state, rng=Random(5050))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@spec_test
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_misc_balances(spec, phases, state):
randomize_state(spec, state, rng=Random(6060))
yield from run_fork_test(phases[BELLATRIX], state)
@with_phases(phases=[ALTAIR], other_phases=[BELLATRIX])
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(BELLATRIX_FORK_TEST_META_TAGS)
def test_bellatrix_fork_random_large_validator_set(spec, phases, state):
randomize_state(spec, state, rng=Random(7070))
yield from run_fork_test(phases[BELLATRIX], state)

View File

@ -1,6 +1,6 @@
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.exceptions import BlockNotFoundException
from eth2spec.test.context import spec_state_test, with_phases, MERGE
from eth2spec.test.context import spec_state_test, with_phases, BELLATRIX
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
)
@ -47,7 +47,7 @@ def with_pow_block_patch(spec, blocks, func):
assert is_called.value
@with_phases([MERGE])
@with_phases([BELLATRIX])
@spec_state_test
def test_all_valid(spec, state):
test_steps = []
@ -81,7 +81,7 @@ def test_all_valid(spec, state):
yield 'steps', test_steps
@with_phases([MERGE])
@with_phases([BELLATRIX])
@spec_state_test
def test_block_lookup_failed(spec, state):
test_steps = []
@ -111,7 +111,7 @@ def test_block_lookup_failed(spec, state):
yield 'steps', test_steps
@with_phases([MERGE])
@with_phases([BELLATRIX])
@spec_state_test
def test_too_early_for_merge(spec, state):
test_steps = []
@ -143,7 +143,7 @@ def test_too_early_for_merge(spec, state):
yield 'steps', test_steps
@with_phases([MERGE])
@with_phases([BELLATRIX])
@spec_state_test
def test_too_late_for_merge(spec, state):
test_steps = []

View File

@ -1,10 +1,10 @@
from eth2spec.test.context import (
MERGE,
BELLATRIX,
single_phase,
spec_test,
with_presets,
with_phases,
with_merge_and_later,
with_bellatrix_and_later,
)
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.helpers.deposits import (
@ -22,7 +22,7 @@ def eth1_init_data(eth1_block_hash, eth1_timestamp):
}
@with_phases([MERGE])
@with_phases([BELLATRIX])
@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")
@ -50,7 +50,7 @@ def test_initialize_pre_transition_no_param(spec):
yield 'state', state
@with_merge_and_later
@with_bellatrix_and_later
@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")
@ -86,7 +86,7 @@ def test_initialize_pre_transition_empty_payload(spec):
yield 'state', state
@with_merge_and_later
@with_bellatrix_and_later
@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")

View File

@ -5,11 +5,11 @@ from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot
)
from eth2spec.test.context import (
with_merge_and_later, spec_state_test
with_bellatrix_and_later, spec_state_test
)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_empty_block_transition_no_tx(spec, state):
yield 'pre', state
@ -25,7 +25,7 @@ def test_empty_block_transition_no_tx(spec, state):
# TODO: tests with EVM, mock or replacement?
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_is_execution_enabled_false(spec, state):
# Set `latest_execution_payload_header` to empty

View File

@ -3,7 +3,7 @@ from eth2spec.test.context import (
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
AFTER_MERGE_PRE_POST_FORKS,
AFTER_BELLATRIX_PRE_POST_FORKS,
)
from eth2spec.test.helpers.fork_transition import (
do_fork,
@ -13,7 +13,7 @@ from eth2spec.test.helpers.fork_transition import (
@with_fork_metas([
ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in AFTER_MERGE_PRE_POST_FORKS
ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in AFTER_BELLATRIX_PRE_POST_FORKS
])
def test_sample_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
transition_until_fork(spec, state, fork_epoch)

View File

@ -4,11 +4,11 @@ from eth2spec.test.helpers.pow_block import (
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
with_bellatrix_and_later,
)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_success_valid(spec, state):
parent_block = prepare_random_pow_block(spec)
@ -20,7 +20,7 @@ def test_is_valid_terminal_pow_block_success_valid(spec, state):
assert spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_before_terminal(spec, state):
parent_block = prepare_random_pow_block(spec)
@ -32,7 +32,7 @@ def test_is_valid_terminal_pow_block_fail_before_terminal(spec, state):
assert not spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
parent_block = prepare_random_pow_block(spec)

View File

@ -5,18 +5,18 @@ from eth2spec.test.helpers.execution_payload import (
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later
with_bellatrix_and_later
)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_fail_merge_complete(spec, state):
state = build_state_with_incomplete_transition(spec, state)
assert not spec.is_merge_transition_complete(state)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_success_merge_complete(spec, state):
state = build_state_with_complete_transition(spec, state)
@ -32,7 +32,7 @@ expected_results = [
]
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_is_merge_block_and_is_execution_enabled(spec, state):
for result in expected_results:

View File

@ -8,7 +8,7 @@ from eth2spec.test.helpers.pow_block import (
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
with_bellatrix_and_later,
spec_configured_state_test
)
@ -49,7 +49,7 @@ def run_validate_merge_block(spec, pow_chain, beacon_block, valid=True):
assert assertion_error_caught
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_success(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
@ -60,7 +60,7 @@ def test_validate_merge_block_success(spec, state):
run_validate_merge_block(spec, pow_chain, block)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_fail_block_lookup(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
@ -70,7 +70,7 @@ def test_validate_merge_block_fail_block_lookup(spec, state):
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_fail_parent_block_lookup(spec, state):
pow_chain = prepare_random_pow_chain(spec, 1)
@ -80,7 +80,7 @@ def test_validate_merge_block_fail_parent_block_lookup(spec, state):
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_fail_after_terminal(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
@ -91,7 +91,7 @@ def test_validate_merge_block_fail_after_terminal(spec, state):
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '0'
@ -107,7 +107,7 @@ def test_validate_merge_block_tbh_override_success(spec, state):
run_validate_merge_block(spec, pow_chain, block)
@with_merge_and_later
@with_bellatrix_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '0'
@ -122,7 +122,7 @@ def test_validate_merge_block_fail_parent_hash_is_not_tbh(spec, state):
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '1'
@ -138,7 +138,7 @@ def test_validate_merge_block_terminal_block_hash_fail_activation_not_reached(sp
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@with_bellatrix_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '1'

View File

@ -5,7 +5,7 @@ from eth2spec.test.helpers.pow_block import (
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
with_bellatrix_and_later,
)
@ -29,7 +29,7 @@ expected_results = [
# it would return the first block (IS_HEAD_PARENT_BLOCK).
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_get_pow_block_at_terminal_total_difficulty(spec, state):
for result in expected_results:
@ -89,7 +89,7 @@ prepare_execution_payload_expected_results = [
]
@with_merge_and_later
@with_bellatrix_and_later
@spec_state_test
def test_prepare_execution_payload(spec, state):
for result in prepare_execution_payload_expected_results:

View File

@ -1,18 +1,19 @@
import pytest
from copy import deepcopy
from dataclasses import dataclass
import importlib
from eth_utils import encode_hex
from eth2spec.phase0 import mainnet as spec_phase0_mainnet, minimal as spec_phase0_minimal
from eth2spec.altair import mainnet as spec_altair_mainnet, minimal as spec_altair_minimal
from eth2spec.merge import mainnet as spec_merge_mainnet, minimal as spec_merge_minimal
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.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, MERGE, CAPELLA,
PHASE0, ALTAIR, BELLATRIX, CAPELLA,
MINIMAL, MAINNET,
ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE, FORKS_BEFORE_CAPELLA,
ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_BELLATRIX, FORKS_BEFORE_CAPELLA,
ALL_FORK_UPGRADES,
)
from .helpers.typing import SpecForkName, PresetBaseName
@ -54,7 +55,7 @@ class SpecAltair(Spec):
...
class SpecMerge(Spec):
class SpecBellatrix(Spec):
...
@ -73,13 +74,13 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
MINIMAL: {
PHASE0: spec_phase0_minimal,
ALTAIR: spec_altair_minimal,
MERGE: spec_merge_minimal,
BELLATRIX: spec_bellatrix_minimal,
CAPELLA: spec_capella_minimal,
},
MAINNET: {
PHASE0: spec_phase0_mainnet,
ALTAIR: spec_altair_mainnet,
MERGE: spec_merge_mainnet,
BELLATRIX: spec_bellatrix_mainnet,
CAPELLA: spec_capella_mainnet,
},
}
@ -88,16 +89,15 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
class SpecForks(TypedDict, total=False):
PHASE0: SpecPhase0
ALTAIR: SpecAltair
MERGE: SpecMerge
BELLATRIX: SpecBellatrix
CAPELLA: SpecCapella
def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int],
spec: Spec, phases: SpecForks):
phase = phases[spec.fork]
balances = balances_fn(phase)
activation_threshold = threshold_fn(phase)
state = create_genesis_state(spec=phase, validator_balances=balances,
balances = balances_fn(spec)
activation_threshold = threshold_fn(spec)
state = create_genesis_state(spec=spec, validator_balances=balances,
activation_threshold=activation_threshold)
return state
@ -473,6 +473,32 @@ def with_presets(preset_bases, reason=None):
return decorator
def _get_basic_dict(ssz_dict: Dict[str, Any]) -> Dict[str, Any]:
"""
Get dict of Python built-in types from a dict of SSZ objects.
"""
result = {}
for k, v in ssz_dict.items():
if isinstance(v, int):
value = int(v)
elif isinstance(v, bytes):
value = encode_hex(v)
else:
value = str(v)
result[k] = value
return result
def _get_copy_of_spec(spec):
fork = spec.fork
preset = spec.config.PRESET_BASE
module_path = f"eth2spec.{fork}.{preset}"
module_spec = importlib.util.find_spec(module_path)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
def with_config_overrides(config_overrides):
"""
WARNING: the spec_test decorator must wrap this, to ensure the decorated test actually runs.
@ -483,20 +509,20 @@ def with_config_overrides(config_overrides):
"""
def decorator(fn):
def wrapper(*args, spec: Spec, **kw):
# remember the old config
old_config = spec.config
spec = _get_copy_of_spec(spec)
# apply our overrides to a copy of it, and apply it to the spec
tmp_config = deepcopy(old_config._asdict()) # not a private method, there are multiple
tmp_config.update(config_overrides)
config = spec.config._asdict()
config.update(config_overrides)
config_types = spec.Configuration.__annotations__
# Retain types of all config values
test_config = {k: config_types[k](v) for k, v in tmp_config.items()}
modified_config = {k: config_types[k](v) for k, v in config.items()}
# Output the config for test vectors (TODO: check config YAML encoding)
yield 'config', 'data', test_config
# To output the changed config to could be serialized with yaml test vectors,
# the dict SSZ objects have to be converted into Python built-in types.
output_config = _get_basic_dict(modified_config)
yield 'config', 'data', output_config
spec.config = spec.Configuration(**test_config)
spec.config = spec.Configuration(**modified_config)
# Run the function
out = fn(*args, spec=spec, **kw)
@ -504,10 +530,6 @@ def with_config_overrides(config_overrides):
# it's generating things, and we need to complete it before setting back the config.
if out is not None:
yield from out
# Restore the old config and apply it
spec.config = old_config
return wrapper
return decorator
@ -516,8 +538,8 @@ def is_post_altair(spec):
return spec.fork not in FORKS_BEFORE_ALTAIR
def is_post_merge(spec):
return spec.fork not in FORKS_BEFORE_MERGE
def is_post_bellatrix(spec):
return spec.fork not in FORKS_BEFORE_BELLATRIX
def is_post_capella(spec):
@ -525,8 +547,8 @@ def is_post_capella(spec):
with_altair_and_later = with_all_phases_except([PHASE0])
with_merge_and_later = with_all_phases_except([PHASE0, ALTAIR])
with_capella_and_later = with_all_phases_except([PHASE0, ALTAIR, MERGE])
with_bellatrix_and_later = with_all_phases_except([PHASE0, ALTAIR])
with_capella_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX])
def only_generator(reason):

View File

@ -1,12 +1,12 @@
MERGE_FORK_TEST_META_TAGS = {
'fork': 'merge',
BELLATRIX_FORK_TEST_META_TAGS = {
'fork': 'bellatrix',
}
def run_fork_test(post_spec, pre_state):
yield 'pre', pre_state
post_state = post_spec.upgrade_to_merge(pre_state)
post_state = post_spec.upgrade_to_bellatrix(pre_state)
# Stable fields
stable_fields = [
@ -39,7 +39,7 @@ def run_fork_test(post_spec, pre_state):
assert getattr(pre_state, field) != getattr(post_state, field)
assert pre_state.fork.current_version == post_state.fork.previous_version
assert post_state.fork.current_version == post_spec.config.MERGE_FORK_VERSION
assert post_state.fork.current_version == post_spec.config.BELLATRIX_FORK_VERSION
assert post_state.fork.epoch == post_spec.get_current_epoch(post_state)
assert post_state.latest_execution_payload_header == post_spec.ExecutionPayloadHeader()

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.context import is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
@ -98,7 +98,7 @@ def build_empty_block(spec, state, slot=None):
if is_post_altair(spec):
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
if is_post_merge(spec):
if is_post_bellatrix(spec):
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
return empty_block

View File

@ -27,7 +27,7 @@ def get_process_calls(spec):
# Altair
'process_sync_aggregate':
lambda state, block: spec.process_sync_aggregate(state, block.body.sync_aggregate),
# Merge
# Bellatrix
'process_application_payload':
lambda state, block: spec.process_application_payload(state, block.body),
# TODO: add sharding processing functions when spec stabilizes.

View File

@ -7,7 +7,7 @@ from .typing import SpecForkName, PresetBaseName
# Some of the Spec module functionality is exposed here to deal with phase-specific changes.
PHASE0 = SpecForkName('phase0')
ALTAIR = SpecForkName('altair')
MERGE = SpecForkName('merge')
BELLATRIX = SpecForkName('bellatrix')
CAPELLA = SpecForkName('capella')
# Experimental phases (not included in default "ALL_PHASES"):
@ -16,22 +16,22 @@ CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
# The forks that pytest runs with.
ALL_PHASES = (PHASE0, ALTAIR, MERGE, CAPELLA)
ALL_PHASES = (PHASE0, ALTAIR, BELLATRIX, CAPELLA)
# The forks that output to the test vectors.
TESTGEN_FORKS = (PHASE0, ALTAIR, MERGE)
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX)
FORKS_BEFORE_ALTAIR = (PHASE0,)
FORKS_BEFORE_MERGE = (PHASE0, ALTAIR)
FORKS_BEFORE_CAPELLA = (PHASE0, ALTAIR, MERGE)
FORKS_BEFORE_BELLATRIX = (PHASE0, ALTAIR)
FORKS_BEFORE_CAPELLA = (PHASE0, ALTAIR, BELLATRIX)
ALL_FORK_UPGRADES = {
# pre_fork_name: post_fork_name
PHASE0: ALTAIR,
ALTAIR: MERGE,
MERGE: CAPELLA,
ALTAIR: BELLATRIX,
BELLATRIX: CAPELLA,
}
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
AFTER_MERGE_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key not in FORKS_BEFORE_ALTAIR}
AFTER_MERGE_PRE_POST_FORKS = AFTER_MERGE_UPGRADES.items()
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key not in FORKS_BEFORE_ALTAIR}
AFTER_BELLATRIX_PRE_POST_FORKS = AFTER_BELLATRIX_UPGRADES.items()
#
# Config

View File

@ -42,6 +42,12 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True,
return post_state
def add_attestation(spec, store, attestation, test_steps, is_from_block=False):
spec.on_attestation(store, attestation, is_from_block=is_from_block)
yield get_attestation_file_name(attestation), attestation
test_steps.append({'attestation': get_attestation_file_name(attestation)})
def tick_and_run_on_attestation(spec, store, attestation, test_steps, is_from_block=False):
parent_block = store.blocks[attestation.data.beacon_block_root]
pre_state = store.block_states[spec.hash_tree_root(parent_block)]
@ -52,9 +58,7 @@ def tick_and_run_on_attestation(spec, store, attestation, test_steps, is_from_bl
spec.on_tick(store, next_epoch_time)
test_steps.append({'tick': int(next_epoch_time)})
spec.on_attestation(store, attestation, is_from_block=is_from_block)
yield get_attestation_file_name(attestation), attestation
test_steps.append({'attestation': get_attestation_file_name(attestation)})
yield from add_attestation(spec, store, attestation, test_steps, is_from_block)
def run_on_attestation(spec, store, attestation, is_from_block=False, valid=True):

View File

@ -11,7 +11,7 @@ from eth2spec.test.helpers.block import (
)
from eth2spec.test.helpers.constants import (
ALTAIR,
MERGE,
BELLATRIX,
CAPELLA,
)
from eth2spec.test.helpers.deposits import (
@ -146,8 +146,8 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=
if post_spec.fork == ALTAIR:
state = post_spec.upgrade_to_altair(state)
elif post_spec.fork == MERGE:
state = post_spec.upgrade_to_merge(state)
elif post_spec.fork == BELLATRIX:
state = post_spec.upgrade_to_bellatrix(state)
elif post_spec.fork == CAPELLA:
state = post_spec.upgrade_to_capella(state)
@ -156,11 +156,11 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=
if post_spec.fork == ALTAIR:
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
elif post_spec.fork == MERGE:
elif post_spec.fork == BELLATRIX:
assert state.fork.previous_version == post_spec.config.ALTAIR_FORK_VERSION
assert state.fork.current_version == post_spec.config.MERGE_FORK_VERSION
assert state.fork.current_version == post_spec.config.BELLATRIX_FORK_VERSION
elif post_spec.fork == CAPELLA:
assert state.fork.previous_version == post_spec.config.MERGE_FORK_VERSION
assert state.fork.previous_version == post_spec.config.BELLATRIX_FORK_VERSION
assert state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION
if with_block:

View File

@ -1,6 +1,6 @@
from eth2spec.test.helpers.constants import (
ALTAIR, MERGE,
FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE, FORKS_BEFORE_CAPELLA,
ALTAIR, BELLATRIX,
FORKS_BEFORE_ALTAIR, FORKS_BEFORE_BELLATRIX, FORKS_BEFORE_CAPELLA,
)
from eth2spec.test.helpers.keys import pubkeys
@ -53,9 +53,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
if spec.fork == ALTAIR:
current_version = spec.config.ALTAIR_FORK_VERSION
elif spec.fork == MERGE:
elif spec.fork == BELLATRIX:
previous_version = spec.config.ALTAIR_FORK_VERSION
current_version = spec.config.MERGE_FORK_VERSION
current_version = spec.config.BELLATRIX_FORK_VERSION
state = spec.BeaconState(
genesis_time=0,
@ -98,7 +98,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
state.current_sync_committee = spec.get_next_sync_committee(state)
state.next_sync_committee = spec.get_next_sync_committee(state)
if spec.fork not in FORKS_BEFORE_MERGE:
if spec.fork not in FORKS_BEFORE_BELLATRIX:
# Initialize the execution payload header (with block number and genesis time set to 0)
state.latest_execution_payload_header = get_sample_genesis_execution_payload_header(
spec,

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.context import is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.block_header import sign_block_header
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.state import get_balance
@ -9,8 +9,8 @@ from eth2spec.test.helpers.sync_committee import (
def get_min_slashing_penalty_quotient(spec):
if is_post_merge(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_MERGE
if is_post_bellatrix(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX
elif is_post_altair(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR
else:

View File

@ -2,7 +2,7 @@ from random import Random
from lru import LRU
from eth2spec.phase0.mainnet import VALIDATOR_REGISTRY_LIMIT # equal everywhere, fine to import
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.context import is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.state import (
next_epoch,
)
@ -22,8 +22,8 @@ class Deltas(Container):
def get_inactivity_penalty_quotient(spec):
if is_post_merge(spec):
return spec.INACTIVITY_PENALTY_QUOTIENT_MERGE
if is_post_bellatrix(spec):
return spec.INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
elif is_post_altair(spec):
return spec.INACTIVITY_PENALTY_QUOTIENT_ALTAIR
else:

View File

@ -1,84 +0,0 @@
from random import Random
from eth2spec.test.context import (
with_phases,
with_custom_state,
with_presets,
spec_test, with_state,
low_balances, misc_balances, large_validator_set,
)
from eth2spec.test.utils import with_meta_tags
from eth2spec.test.helpers.constants import (
ALTAIR, MERGE,
MINIMAL,
)
from eth2spec.test.helpers.merge.fork import (
MERGE_FORK_TEST_META_TAGS,
run_fork_test,
)
from eth2spec.test.helpers.random import randomize_state
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_0(spec, phases, state):
randomize_state(spec, state, rng=Random(1010))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_1(spec, phases, state):
randomize_state(spec, state, rng=Random(2020))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_2(spec, phases, state):
randomize_state(spec, state, rng=Random(3030))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_state
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_3(spec, phases, state):
randomize_state(spec, state, rng=Random(4040))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_low_balances(spec, phases, state):
randomize_state(spec, state, rng=Random(5050))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@spec_test
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_misc_balances(spec, phases, state):
randomize_state(spec, state, rng=Random(6060))
yield from run_fork_test(phases[MERGE], state)
@with_phases(phases=[ALTAIR], other_phases=[MERGE])
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
def test_merge_fork_random_large_validator_set(spec, phases, state):
randomize_state(spec, state, rng=Random(7070))
yield from run_fork_test(phases[MERGE], state)

View File

@ -1,5 +1,5 @@
from random import Random
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_altair, is_post_merge
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with, run_epoch_processing_to
)
@ -31,8 +31,8 @@ def slash_validators(spec, state, indices, out_epochs):
def get_slashing_multiplier(spec):
if is_post_merge(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER_MERGE
if is_post_bellatrix(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX
elif is_post_altair(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR
else:

View File

@ -0,0 +1,421 @@
from eth2spec.test.context import (
MAINNET,
spec_state_test,
with_all_phases,
with_presets,
)
from eth2spec.test.helpers.attestations import (
get_valid_attestation,
sign_attestation,
)
from eth2spec.test.helpers.block import (
build_empty_block,
)
from eth2spec.test.helpers.fork_choice import (
get_genesis_forkchoice_store_and_block,
on_tick_and_append_step,
add_attestation,
add_block,
tick_and_add_block,
)
from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
)
def _apply_base_block_a(spec, state, store, test_steps):
# On receiving block A at slot `N`
block = build_empty_block(spec, state, slot=state.slot + 1)
signed_block_a = state_transition_and_sign_block(spec, state, block)
yield from tick_and_add_block(spec, store, signed_block_a, test_steps)
assert spec.get_head(store) == signed_block_a.message.hash_tree_root()
@with_all_phases
@spec_state_test
def test_ex_ante_vanilla(spec, state):
"""
With a single adversarial attestation
Objects:
Block A - slot N
Block B (parent A) - slot N+1
Block C (parent A) - slot N+2
Attestation_1 (Block B); size `1` - slot N+1
Steps:
Block A received at N A is head
Block C received at N+2 C is head
Block B received at N+2 C is head
Attestation_1 received at N+2 C is head
"""
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
yield 'anchor_state', state
yield 'anchor_block', anchor_block
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time
# On receiving block A at slot `N`
yield from _apply_base_block_a(spec, state, store, test_steps)
state_a = state.copy()
# Block B at slot `N + 1`, parent is A
state_b = state_a.copy()
block = build_empty_block(spec, state_a, slot=state_a.slot + 1)
signed_block_b = state_transition_and_sign_block(spec, state_b, block)
# Block C at slot `N + 2`, parent is A
state_c = state_a.copy()
block = build_empty_block(spec, state_c, slot=state_a.slot + 2)
signed_block_c = state_transition_and_sign_block(spec, state_c, block)
# Attestation_1 at slot `N + 1` voting for block B
def _filter_participant_set(participants):
return [next(iter(participants))]
attestation = get_valid_attestation(
spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set
)
attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root()
assert len([i for i in attestation.aggregation_bits if i == 1]) == 1
sign_attestation(spec, state_b, attestation)
# Block C received at N+2 — C is head
time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_c, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block B received at N+2 — C is head due to proposer score boost
yield from add_block(spec, store, signed_block_b, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Attestation_1 received at N+2 — C is head
yield from add_attestation(spec, store, attestation, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
yield 'steps', test_steps
def _get_greater_than_proposer_boost_score(spec, store, state, proposer_boost_root, root):
"""
Return the minimum attestation participant count such that attestation_score > proposer_score
"""
# calculate proposer boost score
block = store.blocks[root]
proposer_score = 0
if spec.get_ancestor(store, root, block.slot) == proposer_boost_root:
num_validators = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state)))
avg_balance = spec.get_total_active_balance(state) // num_validators
committee_size = num_validators // spec.SLOTS_PER_EPOCH
committee_weight = committee_size * avg_balance
proposer_score = (committee_weight * spec.config.PROPOSER_SCORE_BOOST) // 100
# calculate minimum participant count such that attestation_score > proposer_score
base_effective_balance = state.validators[0].effective_balance
return proposer_score // base_effective_balance + 1
@with_all_phases
@with_presets([MAINNET], reason="to create non-duplicate committee")
@spec_state_test
def test_ex_ante_attestations_is_greater_than_proposer_boost_with_boost(spec, state):
"""
Adversarial attestations > proposer boost
Objects:
Block A - slot N
Block B (parent A) - slot N+1
Block C (parent A) - slot N+2
Attestation_set_1 (Block B); size `proposer_boost + 1` - slot N+1
Steps:
Block A received at N A is head
Block C received at N+2 C is head
Block B received at N+2 C is head
Attestation_1 received at N+2 B is head
"""
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
yield 'anchor_state', state
yield 'anchor_block', anchor_block
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time
# On receiving block A at slot `N`
yield from _apply_base_block_a(spec, state, store, test_steps)
state_a = state.copy()
# Block B at slot `N + 1`, parent is A
state_b = state_a.copy()
block = build_empty_block(spec, state_a, slot=state_a.slot + 1)
signed_block_b = state_transition_and_sign_block(spec, state_b, block)
# Block C at slot `N + 2`, parent is A
state_c = state_a.copy()
block = build_empty_block(spec, state_c, slot=state_a.slot + 2)
signed_block_c = state_transition_and_sign_block(spec, state_c, block)
# Block C received at N+2 — C is head
time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_c, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block B received at N+2 — C is head due to proposer score boost
yield from add_block(spec, store, signed_block_b, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Attestation_set_1 at slot `N + 1` voting for block B
proposer_boost_root = signed_block_b.message.hash_tree_root()
root = signed_block_b.message.hash_tree_root()
participant_num = _get_greater_than_proposer_boost_score(spec, store, state, proposer_boost_root, root)
def _filter_participant_set(participants):
return [index for i, index in enumerate(participants) if i < participant_num]
attestation = get_valid_attestation(
spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set
)
attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root()
assert len([i for i in attestation.aggregation_bits if i == 1]) == participant_num
sign_attestation(spec, state_b, attestation)
# Attestation_set_1 received at N+2 — B is head because B's attestation_score > C's proposer_score.
# (B's proposer_score = C's attestation_score = 0)
yield from add_attestation(spec, store, attestation, test_steps)
assert spec.get_head(store) == signed_block_b.message.hash_tree_root()
yield 'steps', test_steps
@with_all_phases
@spec_state_test
def test_ex_ante_sandwich_without_attestations(spec, state):
"""
Simple Sandwich test with boost and no attestations.
Obejcts:
Block A - slot N
Block B (parent A) - slot N+1
Block C (parent A) - slot N+2
Block D (parent B) - slot N+3
Steps:
Block A received at N A is head
Block C received at N+2 C is head
Block B received at N+2 C is head (with boost)
Block D received at N+3 D is head (with boost)
"""
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
yield 'anchor_state', state
yield 'anchor_block', anchor_block
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time
# On receiving block A at slot `N`
yield from _apply_base_block_a(spec, state, store, test_steps)
state_a = state.copy()
# Block B at slot `N + 1`, parent is A
state_b = state_a.copy()
block = build_empty_block(spec, state_a, slot=state_a.slot + 1)
signed_block_b = state_transition_and_sign_block(spec, state_b, block)
# Block C at slot `N + 2`, parent is A
state_c = state_a.copy()
block = build_empty_block(spec, state_c, slot=state_a.slot + 2)
signed_block_c = state_transition_and_sign_block(spec, state_c, block)
# Block D at slot `N + 3`, parent is B
state_d = state_b.copy()
block = build_empty_block(spec, state_d, slot=state_a.slot + 3)
signed_block_d = state_transition_and_sign_block(spec, state_d, block)
# Block C received at N+2 — C is head
time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_c, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block B received at N+2 — C is head, it has proposer score boost
yield from add_block(spec, store, signed_block_b, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block D received at N+3 - D is head, it has proposer score boost
time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_d, test_steps)
assert spec.get_head(store) == signed_block_d.message.hash_tree_root()
yield 'steps', test_steps
@with_all_phases
@spec_state_test
def test_ex_ante_sandwich_with_honest_attestation(spec, state):
"""
Boosting necessary to sandwich attack.
Objects:
Block A - slot N
Block B (parent A) - slot N+1
Block C (parent A) - slot N+2
Block D (parent B) - slot N+3
Attestation_1 (Block C); size 1 - slot N+2 (honest)
Steps:
Block A received at N A is head
Block C received at N+2 C is head
Block B received at N+2 C is head
Attestation_1 received at N+3 C is head
Block D received at N+3 D is head
"""
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
yield 'anchor_state', state
yield 'anchor_block', anchor_block
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time
# On receiving block A at slot `N`
yield from _apply_base_block_a(spec, state, store, test_steps)
state_a = state.copy()
# Block B at slot `N + 1`, parent is A
state_b = state_a.copy()
block = build_empty_block(spec, state_a, slot=state_a.slot + 1)
signed_block_b = state_transition_and_sign_block(spec, state_b, block)
# Block C at slot `N + 2`, parent is A
state_c = state_a.copy()
block = build_empty_block(spec, state_c, slot=state_a.slot + 2)
signed_block_c = state_transition_and_sign_block(spec, state_c, block)
# Attestation_1 at N+2 voting for block C
def _filter_participant_set(participants):
return [next(iter(participants))]
attestation = get_valid_attestation(
spec, state_c, slot=state_c.slot, signed=False, filter_participant_set=_filter_participant_set
)
attestation.data.beacon_block_root = signed_block_c.message.hash_tree_root()
assert len([i for i in attestation.aggregation_bits if i == 1]) == 1
sign_attestation(spec, state_c, attestation)
# Block D at slot `N + 3`, parent is B
state_d = state_b.copy()
block = build_empty_block(spec, state_d, slot=state_a.slot + 3)
signed_block_d = state_transition_and_sign_block(spec, state_d, block)
# Block C received at N+2 — C is head
time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_c, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block B received at N+2 — C is head, it has proposer score boost
yield from add_block(spec, store, signed_block_b, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Attestation_1 received at N+3 — C is head
time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_attestation(spec, store, attestation, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block D received at N+3 - D is head, it has proposer score boost
yield from add_block(spec, store, signed_block_d, test_steps)
assert spec.get_head(store) == signed_block_d.message.hash_tree_root()
yield 'steps', test_steps
@with_all_phases
@with_presets([MAINNET], reason="to create non-duplicate committee")
@spec_state_test
def test_ex_ante_sandwich_with_boost_not_sufficient(spec, state):
"""
Boost not sufficient to sandwich attack.
Objects:
Block A - slot N
Block B (parent A) - slot N+1
Block C (parent A) - slot N+2
Block D (parent B) - slot N+3
Attestation_set_1 (Block C); size proposer_boost + 1 - slot N+2
Steps:
Block A received at N A is head
Block C received at N+2 C is head
Block B received at N+2 C is head
Attestation_set_1 received C is head
Block D received at N+3 C is head
"""
test_steps = []
# Initialization
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
yield 'anchor_state', state
yield 'anchor_block', anchor_block
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time
# On receiving block A at slot `N`
yield from _apply_base_block_a(spec, state, store, test_steps)
state_a = state.copy()
# Block B at slot `N + 1`, parent is A
state_b = state_a.copy()
block = build_empty_block(spec, state_a, slot=state_a.slot + 1)
signed_block_b = state_transition_and_sign_block(spec, state_b, block)
# Block C at slot `N + 2`, parent is A
state_c = state_a.copy()
block = build_empty_block(spec, state_c, slot=state_a.slot + 2)
signed_block_c = state_transition_and_sign_block(spec, state_c, block)
# Block D at slot `N + 3`, parent is B
state_d = state_b.copy()
block = build_empty_block(spec, state_d, slot=state_a.slot + 3)
signed_block_d = state_transition_and_sign_block(spec, state_d, block)
# Block C received at N+2 — C is head
time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block_c, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block B received at N+2 — C is head, it has proposer score boost
yield from add_block(spec, store, signed_block_b, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Attestation_set_1 at N+2 voting for block C
proposer_boost_root = signed_block_c.message.hash_tree_root()
root = signed_block_c.message.hash_tree_root()
participant_num = _get_greater_than_proposer_boost_score(spec, store, state, proposer_boost_root, root)
def _filter_participant_set(participants):
return [index for i, index in enumerate(participants) if i < participant_num]
attestation = get_valid_attestation(
spec, state_c, slot=state_c.slot, signed=False, filter_participant_set=_filter_participant_set
)
attestation.data.beacon_block_root = signed_block_c.message.hash_tree_root()
assert len([i for i in attestation.aggregation_bits if i == 1]) == participant_num
sign_attestation(spec, state_c, attestation)
# Attestation_1 received at N+3 — B is head because B's attestation_score > C's proposer_score.
# (B's proposer_score = C's attestation_score = 0)
time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_attestation(spec, store, attestation, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
# Block D received at N+3 - C is head, D's boost not sufficient!
yield from add_block(spec, store, signed_block_d, test_steps)
assert spec.get_head(store) == signed_block_c.message.hash_tree_root()
yield 'steps', test_steps

View File

@ -26,6 +26,9 @@ from eth2spec.test.helpers.state import (
)
rng = random.Random(1001)
@with_all_phases
@spec_state_test
def test_genesis(spec, state):
@ -166,6 +169,9 @@ def test_shorter_chain_but_heavier_weight(spec, state):
signed_short_block = state_transition_and_sign_block(spec, short_state, short_block)
yield from tick_and_add_block(spec, store, signed_short_block, test_steps)
# Since the long chain has higher proposer_score at slot 1, the latest long block is the head
assert spec.get_head(store) == spec.hash_tree_root(long_block)
short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True)
yield from tick_and_run_on_attestation(spec, store, short_attestation, test_steps)
@ -300,7 +306,7 @@ def test_proposer_boost_correct_head(spec, state):
block_2 = build_empty_block_for_next_slot(spec, state_2)
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2):
block_2.body.graffiti = spec.Bytes32(hex(random.getrandbits(8 * 32))[2:].zfill(64))
block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64))
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
assert spec.hash_tree_root(block_1) < spec.hash_tree_root(block_2)

View File

@ -39,7 +39,7 @@ from eth2spec.test.context import (
with_custom_state,
large_validator_set,
is_post_altair,
is_post_merge,
is_post_bellatrix,
)
@ -143,7 +143,7 @@ def process_and_sign_block_without_header_validations(spec, state, block):
state_root=spec.Bytes32(),
body_root=block.body.hash_tree_root(),
)
if is_post_merge(spec):
if is_post_bellatrix(spec):
if spec.is_execution_enabled(state, block.body):
spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE)
@ -195,7 +195,7 @@ def test_parent_from_same_slot(spec, state):
child_block = parent_block.copy()
child_block.parent_root = state.latest_block_header.hash_tree_root()
if is_post_merge(spec):
if is_post_bellatrix(spec):
child_block.body.execution_payload = build_empty_execution_payload(spec, state)
# Show that normal path through transition fails

View File

@ -1,7 +1,7 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE, CAPELLA
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot
from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store
@ -19,7 +19,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
spec.on_attestation(store, attestation)
sample_index = indexed_attestation.attesting_indices[0]
if spec.fork in (PHASE0, ALTAIR, MERGE, CAPELLA):
if spec.fork in (PHASE0, ALTAIR, BELLATRIX, CAPELLA):
latest_message = spec.LatestMessage(
epoch=attestation.data.target.epoch,
root=attestation.data.beacon_block_root,

View File

@ -1,7 +1,7 @@
from eth2spec.test.context import (
spec_state_test,
with_all_phases,
is_post_altair, is_post_merge,
is_post_altair, is_post_bellatrix,
)
from eth2spec.test.helpers.constants import MAX_UINT_64
@ -52,8 +52,8 @@ def test_hysteresis_quotient(spec, state):
@spec_state_test
def test_incentives(spec, state):
# Ensure no ETH is minted in slash_validator
if is_post_merge(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_MERGE <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
if is_post_bellatrix(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
elif is_post_altair(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
else:

View File

@ -23,7 +23,7 @@ Key of valid `fork` strings that might be found in `meta.yaml`
| String ID | Pre-fork | Post-fork | Function |
| - | - | - | - |
| `altair` | Phase 0 | Altair | `upgrade_to_altair` |
| `merge` | Phase 0 | Merge | `upgrade_to_merge` |
| `bellatrix` | Altair | Bellatrix | `upgrade_to_bellatrix` |
### `pre.ssz_snappy`

View File

@ -29,7 +29,7 @@ Each file is a SSZ-snappy encoded `Deposit` object.
### `execution_payload_header.ssz_snappy`
*Note*: Param added only for the Merge and subsequent forks.
*Note*: Param added only for Bellatrix and subsequent forks.
The execution payload header that state is initialized with. An SSZ-snappy encoded `BeaconState` object.

View File

@ -42,7 +42,7 @@ Operations:
| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` |
| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` |
| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) |
| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Merge) |
| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) |
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, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
@ -23,8 +23,9 @@ if __name__ == "__main__":
]}
altair_mods = combine_mods(_new_altair_mods, phase_0_mods)
# No epoch-processing changes in Merge and previous testing repeats with new types, so no additional tests required.
merge_mods = altair_mods
# No epoch-processing changes in Bellatrix and previous testing repeats with new types,
# so no additional tests required.
bellatrix_mods = altair_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 [
@ -36,7 +37,7 @@ if __name__ == "__main__":
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_mods,
}
run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods)

View File

@ -1,16 +1,16 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'}
altair_mods = phase_0_mods # No additional Altair specific finality tests
merge_mods = altair_mods # No additional Merge specific finality tests
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_mods,
}
run_state_test_generators(runner_name="finality", all_mods=all_mods)

View File

@ -1,26 +1,27 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
phase_0_mods = {key: 'eth2spec.test.phase0.fork_choice.test_' + key for key in [
'get_head',
'on_block',
'ex_ante',
]}
# No additional Altair specific finality tests, yet.
altair_mods = phase_0_mods
# For merge `on_merge_block` test kind added with `pow_block_N.ssz` files with several
# PowBlock's which should be resolved by `get_pow_block(hash: Hash32) -> PowBlock` function
_new_merge_mods = {key: 'eth2spec.test.merge.fork_choice.test_' + key for key in [
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.fork_choice.test_' + key for key in [
'on_merge_block',
]}
merge_mods = combine_mods(_new_merge_mods, altair_mods)
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_mods,
}
run_state_test_generators(runner_name="fork_choice", all_mods=all_mods)

View File

@ -1,8 +1,9 @@
from typing import Iterable
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MINIMAL, MAINNET
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, MINIMAL, MAINNET
from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName
from eth2spec.test.altair.fork import test_altair_fork_basic, test_altair_fork_random
from eth2spec.test.bellatrix.fork import test_bellatrix_fork_basic, test_bellatrix_fork_random
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
@ -26,10 +27,13 @@ def create_provider(tests_src, preset_name: PresetBaseName,
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
def _get_fork_tests_providers():
for preset in [MINIMAL, MAINNET]:
yield create_provider(test_altair_fork_basic, preset, PHASE0, ALTAIR)
yield create_provider(test_altair_fork_random, preset, PHASE0, ALTAIR)
yield create_provider(test_bellatrix_fork_basic, preset, ALTAIR, BELLATRIX)
yield create_provider(test_bellatrix_fork_random, preset, ALTAIR, BELLATRIX)
if __name__ == "__main__":
gen_runner.run_generator("forks", [
create_provider(test_altair_fork_basic, MINIMAL, PHASE0, ALTAIR),
create_provider(test_altair_fork_basic, MAINNET, PHASE0, ALTAIR),
create_provider(test_altair_fork_random, MINIMAL, PHASE0, ALTAIR),
create_provider(test_altair_fork_random, MAINNET, PHASE0, ALTAIR),
])
gen_runner.run_generator("forks", list(_get_fork_tests_providers()))

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, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
@ -11,15 +11,15 @@ if __name__ == "__main__":
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
altair_mods = phase_0_mods
_new_merge_mods = {key: 'eth2spec.test.merge.genesis.test_' + key for key in [
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.genesis.test_' + key for key in [
'initialization',
]}
merge_mods = combine_mods(_new_merge_mods, altair_mods)
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_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, MERGE
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
@ -6,11 +6,11 @@ if __name__ == "__main__":
altair_mods = {key: 'eth2spec.test.altair.merkle.test_' + key for key in [
'single_proof',
]}
merge_mods = altair_mods
bellatrix_mods = altair_mods
all_mods = {
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_mods,
}
run_state_test_generators(runner_name="merkle", 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, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
@ -17,10 +17,10 @@ if __name__ == "__main__":
]}
altair_mods = combine_mods(_new_altair_mods, phase_0_mods)
_new_merge_mods = {key: 'eth2spec.test.merge.block_processing.test_process_' + key for key in [
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.block_processing.test_process_' + key for key in [
'execution_payload',
]}
merge_mods = combine_mods(_new_merge_mods, altair_mods)
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_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 [
@ -35,7 +35,7 @@ if __name__ == "__main__":
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_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, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
if __name__ == "__main__":
@ -8,18 +8,18 @@ if __name__ == "__main__":
'leak',
'random',
]}
# No additional altair specific rewards tests, yet.
# No additional Altair specific rewards tests, yet.
altair_mods = phase_0_mods
# No additional merge specific rewards tests, yet.
# No additional Bellatrix specific rewards tests, yet.
# Note: Block rewards are non-epoch rewards and are tested as part of block processing tests.
# Transaction fees are part of the execution-layer.
merge_mods = altair_mods
bellatrix_mods = altair_mods
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_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, MERGE
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
@ -13,15 +13,15 @@ if __name__ == "__main__":
]}
altair_mods = combine_mods(_new_altair_mods, phase_0_mods)
_new_merge_mods = {key: 'eth2spec.test.merge.sanity.test_' + key for key in [
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.sanity.test_' + key for key in [
'blocks',
]}
merge_mods = combine_mods(_new_merge_mods, altair_mods)
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
BELLATRIX: bellatrix_mods,
}
run_state_test_generators(runner_name="sanity", all_mods=all_mods)

View File

@ -16,8 +16,8 @@ from eth2spec.test.altair.transition import (
test_slashing as test_altair_slashing,
test_operations as test_altair_operations,
)
from eth2spec.test.merge.transition import (
test_transition as test_merge_transition,
from eth2spec.test.bellatrix.transition import (
test_transition as test_bellatrix_transition,
)
@ -47,10 +47,10 @@ if __name__ == "__main__":
test_altair_slashing,
test_altair_operations,
)
merge_tests = (
test_merge_transition,
bellatrix_tests = (
test_bellatrix_transition,
)
all_tests = altair_tests + merge_tests
all_tests = altair_tests + bellatrix_tests
for transition_test_module in all_tests:
for pre_fork, post_fork in ALL_PRE_POST_FORKS:
gen_runner.run_generator("transition", [