mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-20 14:28:22 +00:00
Add merge/fork.md with upgrade_to_merge definition
This commit is contained in:
parent
d857935d6c
commit
fd4369dc7c
@ -43,6 +43,7 @@ while the details are in review and may change.
|
||||
* [ethereum.org](https://ethereum.org) high-level description of the 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)
|
||||
|
||||
|
1
setup.py
1
setup.py
@ -865,6 +865,7 @@ class PySpecCommand(Command):
|
||||
specs/phase0/validator.md
|
||||
specs/phase0/weak-subjectivity.md
|
||||
specs/merge/beacon-chain.md
|
||||
specs/merge/fork.md
|
||||
specs/merge/fork-choice.md
|
||||
specs/merge/validator.md
|
||||
"""
|
||||
|
@ -70,8 +70,6 @@ Warning: this configuration is not definitive.
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MERGE_FORK_VERSION` | `Version('0x02000000')` |
|
||||
| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
|
||||
| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** |
|
||||
|
||||
## Containers
|
||||
|
89
specs/merge/fork.md
Normal file
89
specs/merge/fork.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Ethereum 2.0 The Merge
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Configuration](#configuration)
|
||||
- [Fork to Merge](#fork-to-merge)
|
||||
- [Fork trigger](#fork-trigger)
|
||||
- [Upgrading the state](#upgrading-the-state)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Introduction
|
||||
|
||||
This document describes the process of the Merge upgrade.
|
||||
|
||||
## Configuration
|
||||
|
||||
Warning: this configuration is not definitive.
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MERGE_FORK_VERSION` | `Version('0x02000000')` |
|
||||
| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
|
||||
|
||||
## Fork to Merge
|
||||
|
||||
### 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`.
|
||||
|
||||
Note that for the pure Merge networks, we don't apply `upgrade_to_merge` since it starts with Merge version logic.
|
||||
|
||||
### Upgrading the state
|
||||
|
||||
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`.
|
||||
Care must be taken when transitioning through the fork boundary as implementations will need a modified [state transition function](../phase0/beacon-chain.md#beacon-chain-state-transition-function) that deviates from the Phase 0 document.
|
||||
In particular, the outer `state_transition` function defined in the Phase 0 document will not expose the precise fork slot to execute the upgrade in the presence of skipped slots at the fork boundary. Instead the logic must be within `process_slots`.
|
||||
|
||||
```python
|
||||
def upgrade_to_merge(pre: phase0.BeaconState) -> BeaconState:
|
||||
epoch = phase0.get_current_epoch(pre)
|
||||
post = BeaconState(
|
||||
# Versioning
|
||||
genesis_time=pre.genesis_time,
|
||||
genesis_validators_root=pre.genesis_validators_root,
|
||||
slot=pre.slot,
|
||||
fork=Fork(
|
||||
previous_version=pre.fork.current_version,
|
||||
current_version=MERGE_FORK_VERSION,
|
||||
epoch=epoch,
|
||||
),
|
||||
# History
|
||||
latest_block_header=pre.latest_block_header,
|
||||
block_roots=pre.block_roots,
|
||||
state_roots=pre.state_roots,
|
||||
historical_roots=pre.historical_roots,
|
||||
# Eth1
|
||||
eth1_data=pre.eth1_data,
|
||||
eth1_data_votes=pre.eth1_data_votes,
|
||||
eth1_deposit_index=pre.eth1_deposit_index,
|
||||
# Registry
|
||||
validators=pre.validators,
|
||||
balances=pre.balances,
|
||||
# Randomness
|
||||
randao_mixes=pre.randao_mixes,
|
||||
# Slashings
|
||||
slashings=pre.slashings,
|
||||
# Attestations
|
||||
previous_epoch_attestations=pre.previous_epoch_attestations,
|
||||
current_epoch_attestations=pre.current_epoch_attestations,
|
||||
# Finality
|
||||
justification_bits=pre.justification_bits,
|
||||
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
||||
current_justified_checkpoint=pre.current_justified_checkpoint,
|
||||
finalized_checkpoint=pre.finalized_checkpoint,
|
||||
# Execution-layer
|
||||
latest_execution_payload_header=ExecutionPayloadHeader(),
|
||||
)
|
||||
|
||||
return post
|
||||
```
|
45
tests/core/pyspec/eth2spec/test/helpers/merge/fork.py
Normal file
45
tests/core/pyspec/eth2spec/test/helpers/merge/fork.py
Normal file
@ -0,0 +1,45 @@
|
||||
MERGE_FORK_TEST_META_TAGS = {
|
||||
'fork': 'merge',
|
||||
}
|
||||
|
||||
|
||||
def run_fork_test(post_spec, pre_state):
|
||||
# Clean up state to be more realistic
|
||||
pre_state.current_epoch_attestations = []
|
||||
|
||||
yield 'pre', pre_state
|
||||
|
||||
post_state = post_spec.upgrade_to_merge(pre_state)
|
||||
|
||||
# Stable fields
|
||||
stable_fields = [
|
||||
'genesis_time', 'genesis_validators_root', 'slot',
|
||||
# History
|
||||
'latest_block_header', 'block_roots', 'state_roots', 'historical_roots',
|
||||
# Eth1
|
||||
'eth1_data', 'eth1_data_votes', 'eth1_deposit_index',
|
||||
# Registry
|
||||
'validators', 'balances',
|
||||
# Randomness
|
||||
'randao_mixes',
|
||||
# Slashings
|
||||
'slashings',
|
||||
# Attestations
|
||||
'previous_epoch_attestations', 'current_epoch_attestations',
|
||||
# Finality
|
||||
'justification_bits', 'previous_justified_checkpoint', 'current_justified_checkpoint', 'finalized_checkpoint',
|
||||
]
|
||||
for field in stable_fields:
|
||||
assert getattr(pre_state, field) == getattr(post_state, field)
|
||||
|
||||
# Modified fields
|
||||
modified_fields = ['fork']
|
||||
for field in modified_fields:
|
||||
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.epoch == post_spec.get_current_epoch(post_state)
|
||||
assert post_state.latest_execution_payload_header == post_spec.ExecutionPayloadHeader()
|
||||
|
||||
yield 'post', post_state
|
@ -0,0 +1,82 @@
|
||||
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 (
|
||||
PHASE0, MERGE,
|
||||
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,
|
||||
run_fork_test,
|
||||
)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
|
||||
def test_fork_base_state(spec, phases, state):
|
||||
yield from run_fork_test(phases[MERGE], state)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
|
||||
def test_fork_next_epoch(spec, phases, state):
|
||||
next_epoch(spec, state)
|
||||
yield from run_fork_test(phases[MERGE], state)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_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)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_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)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@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)
|
||||
def test_fork_random_low_balances(spec, phases, state):
|
||||
yield from run_fork_test(phases[MERGE], state)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@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)
|
||||
def test_fork_random_misc_balances(spec, phases, state):
|
||||
yield from run_fork_test(phases[MERGE], state)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@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)
|
||||
def test_fork_random_large_validator_set(spec, phases, state):
|
||||
yield from run_fork_test(phases[MERGE], state)
|
@ -0,0 +1,120 @@
|
||||
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 (
|
||||
PHASE0, 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,
|
||||
randomize_attestation_participation,
|
||||
)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], 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=[PHASE0], 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=[PHASE0], 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=[PHASE0], 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=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
|
||||
def test_merge_fork_random_duplicate_attestations(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(1111))
|
||||
# Note: `run_fork_test` empties `current_epoch_attestations`
|
||||
state.previous_epoch_attestations = state.previous_epoch_attestations + state.previous_epoch_attestations
|
||||
yield from run_fork_test(phases[MERGE], state)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], other_phases=[MERGE])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(MERGE_FORK_TEST_META_TAGS)
|
||||
def test_merge_fork_random_mismatched_attestations(spec, phases, state):
|
||||
# Create a random state
|
||||
randomize_state(spec, state, rng=Random(2222))
|
||||
|
||||
# Now make two copies
|
||||
state_0 = state.copy()
|
||||
state_1 = state.copy()
|
||||
|
||||
# Randomize attestation participation of both
|
||||
randomize_attestation_participation(spec, state_0, rng=Random(3333))
|
||||
randomize_attestation_participation(spec, state_1, rng=Random(4444))
|
||||
|
||||
# Note: `run_fork_test` empties `current_epoch_attestations`
|
||||
# Use pending attestations from both random states in a single state for testing
|
||||
state_0.previous_epoch_attestations = state_0.previous_epoch_attestations + state_1.previous_epoch_attestations
|
||||
yield from run_fork_test(phases[MERGE], state_0)
|
||||
|
||||
|
||||
@with_phases(phases=[PHASE0], 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=[PHASE0], 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=[PHASE0], 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)
|
@ -23,6 +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` |
|
||||
|
||||
### `pre.ssz_snappy`
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user