Skip to content

Commit

Permalink
Merge pull request #2323 from ethereum/prepare_state
Browse files Browse the repository at this point in the history
Simplify Altair "genesis"
  • Loading branch information
djrtwo authored Apr 27, 2021
2 parents c090d6a + ae429a3 commit 2628721
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 21 deletions.
49 changes: 49 additions & 0 deletions specs/altair/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
- [Slashings](#slashings)
- [Participation flags updates](#participation-flags-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 -->
<!-- /TOC -->
Expand Down Expand Up @@ -661,3 +662,51 @@ def process_sync_committee_updates(state: BeaconState) -> None:
state.current_sync_committee = state.next_sync_committee
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
```
2 changes: 2 additions & 0 deletions specs/altair/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since it starts with Altair version logic.

### 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.
Expand Down
22 changes: 7 additions & 15 deletions tests/core/pyspec/eth2spec/test/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, MERGE, SHARDING, CUSTODY_GAME, DAS,
ALL_PHASES,
PHASE0, ALTAIR,
ALL_PHASES, FORKS_BEFORE_ALTAIR,
)
from .helpers.genesis import create_genesis_state
from .utils import vector_test, with_meta_tags
Expand Down Expand Up @@ -55,17 +55,11 @@ class SpecForks(TypedDict, total=False):

def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int],
spec: Spec, phases: SpecForks):

p0 = phases[PHASE0]
balances = balances_fn(p0)
activation_threshold = threshold_fn(p0)

state = create_genesis_state(spec=p0, validator_balances=balances,
phase = phases[spec.fork]
balances = balances_fn(phase)
activation_threshold = threshold_fn(phase)
state = create_genesis_state(spec=phase, validator_balances=balances,
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


Expand Down Expand Up @@ -368,8 +362,6 @@ def wrapper(*args, spec: Spec, **kw):


def is_post_altair(spec):
# TODO: everything runs in parallel to 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]:
if spec.fork in FORKS_BEFORE_ALTAIR:
return False
return True
4 changes: 3 additions & 1 deletion tests/core/pyspec/eth2spec/test/helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
ALL_PHASES = (PHASE0, ALTAIR)
# The forks that output to the test vectors.
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
Expand Down
5 changes: 1 addition & 4 deletions tests/core/pyspec/eth2spec/test/helpers/fork_choice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from eth_utils import encode_hex

from eth2spec.phase0 import spec as phase0_spec


def get_anchor_root(spec, state):
anchor_block_header = state.latest_block_header.copy()
Expand Down Expand Up @@ -58,8 +56,7 @@ def get_genesis_forkchoice_store(spec, genesis_state):

def get_genesis_forkchoice_store_and_block(spec, genesis_state):
assert genesis_state.slot == spec.GENESIS_SLOT
# The genesis block must be a Phase 0 `BeaconBlock`
genesis_block = phase0_spec.BeaconBlock(state_root=genesis_state.hash_tree_root())
genesis_block = spec.BeaconBlock(state_root=genesis_state.hash_tree_root())
return spec.get_forkchoice_store(genesis_state, genesis_block), genesis_block


Expand Down
22 changes: 21 additions & 1 deletion tests/core/pyspec/eth2spec/test/helpers/genesis.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from eth2spec.test.helpers.constants import (
ALTAIR,
FORKS_BEFORE_ALTAIR,
)
from eth2spec.test.helpers.keys import pubkeys


Expand All @@ -20,6 +24,11 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
deposit_root = b'\x42' * 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(
genesis_time=0,
eth1_deposit_index=len(validator_balances),
Expand All @@ -30,7 +39,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
),
fork=spec.Fork(
previous_version=spec.GENESIS_FORK_VERSION,
current_version=spec.GENESIS_FORK_VERSION,
current_version=current_version,
epoch=spec.GENESIS_EPOCH,
),
latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())),
Expand All @@ -47,8 +56,19 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
if validator.effective_balance >= activation_threshold:
validator.activation_eligibility_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
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
2 changes: 2 additions & 0 deletions tests/formats/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
In this case, there is no point in adding special SSZ types. And the size and efficiency of YAML is acceptable.

Expand Down

0 comments on commit 2628721

Please sign in to comment.