Merge pull request #2323 from ethereum/prepare_state

Simplify Altair "genesis"
This commit is contained in:
Danny Ryan 2021-04-27 12:32:13 -06:00 committed by GitHub
commit 2628721f96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 21 deletions

View File

@ -52,6 +52,7 @@
- [Slashings](#slashings) - [Slashings](#slashings)
- [Participation flags updates](#participation-flags-updates) - [Participation flags updates](#participation-flags-updates)
- [Sync committee updates](#sync-committee-updates) - [Sync committee updates](#sync-committee-updates)
- [Initialize state for pure Altair testnets and test vectors](#initialize-state-for-pure-altair-testnets-and-test-vectors)
<!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC --> <!-- /TOC -->
@ -661,3 +662,51 @@ def process_sync_committee_updates(state: BeaconState) -> None:
state.current_sync_committee = state.next_sync_committee state.current_sync_committee = state.next_sync_committee
state.next_sync_committee = get_sync_committee(state, next_epoch + EPOCHS_PER_SYNC_COMMITTEE_PERIOD) state.next_sync_committee = get_sync_committee(state, next_epoch + EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
``` ```
## Initialize state for pure Altair testnets and test vectors
This helper function is only for initializing the state for pure Altair testnets and tests.
*Note*: The function `initialize_beacon_state_from_eth1` is modified: (1) using `ALTAIR_FORK_VERSION` as the current fork version, (2) utilizing the Altair `BeaconBlockBody` when constructing the initial `latest_block_header`, and (3) adding initial sync committees.
```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
eth1_timestamp: uint64,
deposits: Sequence[Deposit]) -> BeaconState:
fork = Fork(
previous_version=GENESIS_FORK_VERSION,
current_version=ALTAIR_FORK_VERSION, # [Modified in Altair]
epoch=GENESIS_EPOCH,
)
state = BeaconState(
genesis_time=eth1_timestamp + GENESIS_DELAY,
fork=fork,
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
)
# Process deposits
leaves = list(map(lambda deposit: deposit.data, deposits))
for index, deposit in enumerate(deposits):
deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
process_deposit(state, deposit)
# Process activations
for index, validator in enumerate(state.validators):
balance = state.balances[index]
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH
# Set genesis validators root for domain separation and chain versioning
state.genesis_validators_root = hash_tree_root(state.validators)
# [New in Altair] Fill in sync committees
state.current_sync_committee = get_sync_committee(state, get_current_epoch(state))
state.next_sync_committee = get_sync_committee(state, get_current_epoch(state) + EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
return state
```

View File

@ -34,6 +34,8 @@ Warning: this configuration is not definitive.
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 `ALTAIR_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 `ALTAIR_FORK_EPOCH`.
Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since it starts with Altair version logic.
### Upgrading the state ### Upgrading the state
After `process_slots` of Phase 0 finishes, if `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == ALTAIR_FORK_EPOCH`, an irregular state change is made to upgrade to Altair. After `process_slots` of Phase 0 finishes, if `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == ALTAIR_FORK_EPOCH`, an irregular state change is made to upgrade to Altair.

View File

@ -7,8 +7,8 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest from .exceptions import SkippedTest
from .helpers.constants import ( from .helpers.constants import (
PHASE0, ALTAIR, MERGE, SHARDING, CUSTODY_GAME, DAS, PHASE0, ALTAIR,
ALL_PHASES, ALL_PHASES, FORKS_BEFORE_ALTAIR,
) )
from .helpers.genesis import create_genesis_state from .helpers.genesis import create_genesis_state
from .utils import vector_test, with_meta_tags from .utils import vector_test, with_meta_tags
@ -55,17 +55,11 @@ class SpecForks(TypedDict, total=False):
def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int], def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int],
spec: Spec, phases: SpecForks): spec: Spec, phases: SpecForks):
phase = phases[spec.fork]
p0 = phases[PHASE0] balances = balances_fn(phase)
balances = balances_fn(p0) activation_threshold = threshold_fn(phase)
activation_threshold = threshold_fn(p0) state = create_genesis_state(spec=phase, validator_balances=balances,
state = create_genesis_state(spec=p0, validator_balances=balances,
activation_threshold=activation_threshold) activation_threshold=activation_threshold)
# TODO: upgrade to merge spec, and later sharding.
if spec.fork == ALTAIR:
state = phases[ALTAIR].upgrade_to_altair(state)
return state return state
@ -368,8 +362,6 @@ def with_configs(configs, reason=None):
def is_post_altair(spec): def is_post_altair(spec):
# TODO: everything runs in parallel to Altair. if spec.fork in FORKS_BEFORE_ALTAIR:
# After features are rebased on the Altair fork, this can be reduced to just PHASE0.
if spec.fork in [PHASE0, MERGE, SHARDING, CUSTODY_GAME, DAS]:
return False return False
return True return True

View File

@ -18,7 +18,9 @@ DAS = SpecForkName('das')
ALL_PHASES = (PHASE0, ALTAIR) ALL_PHASES = (PHASE0, ALTAIR)
# The forks that output to the test vectors. # The forks that output to the test vectors.
TESTGEN_FORKS = (PHASE0, ALTAIR) TESTGEN_FORKS = (PHASE0, ALTAIR)
# TODO: everything runs in parallel to Altair.
# After features are rebased on the Altair fork, this can be reduced to just PHASE0.
FORKS_BEFORE_ALTAIR = (PHASE0, MERGE, SHARDING, CUSTODY_GAME, DAS)
# #
# Config # Config

View File

@ -1,7 +1,5 @@
from eth_utils import encode_hex from eth_utils import encode_hex
from eth2spec.phase0 import spec as phase0_spec
def get_anchor_root(spec, state): def get_anchor_root(spec, state):
anchor_block_header = state.latest_block_header.copy() anchor_block_header = state.latest_block_header.copy()
@ -58,8 +56,7 @@ def get_genesis_forkchoice_store(spec, genesis_state):
def get_genesis_forkchoice_store_and_block(spec, genesis_state): def get_genesis_forkchoice_store_and_block(spec, genesis_state):
assert genesis_state.slot == spec.GENESIS_SLOT assert genesis_state.slot == spec.GENESIS_SLOT
# The genesis block must be a Phase 0 `BeaconBlock` genesis_block = spec.BeaconBlock(state_root=genesis_state.hash_tree_root())
genesis_block = phase0_spec.BeaconBlock(state_root=genesis_state.hash_tree_root())
return spec.get_forkchoice_store(genesis_state, genesis_block), genesis_block return spec.get_forkchoice_store(genesis_state, genesis_block), genesis_block

View File

@ -1,3 +1,7 @@
from eth2spec.test.helpers.constants import (
ALTAIR,
FORKS_BEFORE_ALTAIR,
)
from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.keys import pubkeys
@ -20,6 +24,11 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
deposit_root = b'\x42' * 32 deposit_root = b'\x42' * 32
eth1_block_hash = b'\xda' * 32 eth1_block_hash = b'\xda' * 32
current_version = spec.GENESIS_FORK_VERSION
if spec.fork == ALTAIR:
current_version = spec.ALTAIR_FORK_VERSION
state = spec.BeaconState( state = spec.BeaconState(
genesis_time=0, genesis_time=0,
eth1_deposit_index=len(validator_balances), eth1_deposit_index=len(validator_balances),
@ -30,7 +39,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
), ),
fork=spec.Fork( fork=spec.Fork(
previous_version=spec.GENESIS_FORK_VERSION, previous_version=spec.GENESIS_FORK_VERSION,
current_version=spec.GENESIS_FORK_VERSION, current_version=current_version,
epoch=spec.GENESIS_EPOCH, epoch=spec.GENESIS_EPOCH,
), ),
latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())), latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())),
@ -47,8 +56,19 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
if validator.effective_balance >= activation_threshold: if validator.effective_balance >= activation_threshold:
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
validator.activation_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_EPOCH
if spec.fork not in FORKS_BEFORE_ALTAIR:
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(spec.uint64(0))
# Set genesis validators root for domain separation and chain versioning # Set genesis validators root for domain separation and chain versioning
state.genesis_validators_root = spec.hash_tree_root(state.validators) state.genesis_validators_root = spec.hash_tree_root(state.validators)
if spec.fork not in FORKS_BEFORE_ALTAIR:
# Fill in sync committees
state.current_sync_committee = spec.get_sync_committee(state, spec.get_current_epoch(state))
state.next_sync_committee = (
spec.get_sync_committee(state, spec.get_current_epoch(state) + spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
)
return state return state

View File

@ -139,6 +139,8 @@ E.g. `pre.ssz_snappy`, `deposit.ssz_snappy`, `post.ssz_snappy`.
Diffing a `pre.ssz_snappy` and `post.ssz_snappy` provides all the information for testing, when decompressed and decoded. Diffing a `pre.ssz_snappy` and `post.ssz_snappy` provides all the information for testing, when decompressed and decoded.
Then the difference between pre and post can be compared to anything that changes the pre state, e.g. `deposit.ssz_snappy` Then the difference between pre and post can be compared to anything that changes the pre state, e.g. `deposit.ssz_snappy`
Note that by default, the SSZ data is in the given test case's <fork or phase name> version, e.g., if it's `altair` test case, use `altair.BeaconState` container to deserialize the given state.
YAML is generally used for test metadata, and for tests that do not use SSZ: e.g. shuffling and BLS tests. YAML is generally used for test metadata, and for tests that do not use SSZ: e.g. shuffling and BLS tests.
In this case, there is no point in adding special SSZ types. And the size and efficiency of YAML is acceptable. In this case, there is no point in adding special SSZ types. And the size and efficiency of YAML is acceptable.