Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify Altair "genesis" #2323

Merged
merged 12 commits into from
Apr 27, 2021
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.
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

*Note*: The function `initialize_beacon_state_from_eth1` is modified with `ALTAIR_FORK_VERSION` fork version and initial sync committees.
hwwhww marked this conversation as resolved.
Show resolved Hide resolved

```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,
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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 slot `ALTAIR_FORK_SLOT`, where `ALTAIR_FORK_SLOT % SLOTS_PER_EPOCH == 0`.

Note that for the pure Altair testnets, we don't apply `upgrade_to_altair` since it starts with Altair version logic.
hwwhww marked this conversation as resolved.
Show resolved Hide resolved

### Upgrading the state

After `process_slots` of Phase 0 finishes, if `state.slot == ALTAIR_FORK_SLOT`, 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