diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 681d82457..838d9d681 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -25,7 +25,6 @@ - [`Fork`](#fork) - [`Checkpoint`](#checkpoint) - [`Validator`](#validator) - - [`Crosslink`](#crosslink) - [`AttestationData`](#attestationdata) - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - [`IndexedAttestation`](#indexedattestation) @@ -84,8 +83,6 @@ - [`get_seed`](#get_seed) - [`get_committee_count`](#get_committee_count) - [`get_crosslink_committee`](#get_crosslink_committee) - - [`get_start_shard`](#get_start_shard) - - [`get_shard_delta`](#get_shard_delta) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_attestation_data_slot`](#get_attestation_data_slot) - [`get_total_balance`](#get_total_balance) @@ -105,7 +102,6 @@ - [Epoch processing](#epoch-processing) - [Helper functions](#helper-functions-1) - [Justification and finalization](#justification-and-finalization) - - [Crosslinks](#crosslinks) - [Rewards and penalties](#rewards-and-penalties-1) - [Registry updates](#registry-updates) - [Slashings](#slashings) @@ -174,6 +170,7 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | +| `COMMITTEES_PER_SLOT` | `2**5` (= 32) | | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_VALIDATORS_PER_COMMITTEE` | `2**12` (= 4,096) | @@ -305,29 +302,18 @@ class Validator(Container): withdrawable_epoch: Epoch # When validator can withdraw or transfer funds ``` -#### `Crosslink` - -```python -class Crosslink(Container): - shard: Shard - parent_root: Hash - # Crosslinking data - start_epoch: Epoch - end_epoch: Epoch - data_root: Hash -``` - #### `AttestationData` ```python class AttestationData(Container): + slot: Slot # LMD GHOST vote beacon_block_root: Hash # FFG vote source: Checkpoint target: Checkpoint - # Crosslink vote - crosslink: Crosslink + # Index -- Maybe remove + index: uint64 ``` #### `AttestationDataAndCustodyBit` @@ -507,16 +493,12 @@ class BeaconState(Container): validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] # Shuffling - start_shard: Shard randao_mixes: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR] # Slashings slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances # Attestations previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] - # Crosslinks - previous_crosslinks: Vector[Crosslink, SHARD_COUNT] # Previous epoch snapshot - current_crosslinks: Vector[Crosslink, SHARD_COUNT] # Finality justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch previous_justified_checkpoint: Checkpoint # Previous epoch snapshot @@ -885,54 +867,32 @@ def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64: """ Return the number of committees at ``epoch``. """ + # Consider not hard coding but just return committees per slot for now + """ committees_per_slot = max(1, min( SHARD_COUNT // SLOTS_PER_EPOCH, len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, )) return committees_per_slot * SLOTS_PER_EPOCH + """ + return COMMITTEES_PER_SLOT ``` #### `get_crosslink_committee` ```python -def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]: +def get_crosslink_committee(state: BeaconState, epoch: Epoch, index: uint64) -> Sequence[ValidatorIndex]: """ - Return the crosslink committee at ``epoch`` for ``shard``. + Return the crosslink committee at ``epoch`` for ``index``. """ return compute_committee( indices=get_active_validator_indices(state, epoch), seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), - index=(shard + SHARD_COUNT - get_start_shard(state, epoch)) % SHARD_COUNT, + index=index, count=get_committee_count(state, epoch), ) ``` -#### `get_start_shard` - -```python -def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard: - """ - Return the start shard of the 0th committee at ``epoch``. - """ - assert epoch <= get_current_epoch(state) + 1 - check_epoch = Epoch(get_current_epoch(state) + 1) - shard = Shard((state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT) - while check_epoch > epoch: - check_epoch -= Epoch(1) - shard = Shard((shard + SHARD_COUNT - get_shard_delta(state, check_epoch)) % SHARD_COUNT) - return shard -``` - -#### `get_shard_delta` - -```python -def get_shard_delta(state: BeaconState, epoch: Epoch) -> uint64: - """ - Return the number of shards to increment ``state.start_shard`` at ``epoch``. - """ - return min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) -``` - #### `get_beacon_proposer_index` ```python @@ -946,18 +906,6 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: return compute_proposer_index(state, indices, seed) ``` -#### `get_attestation_data_slot` - -```python -def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot: - """ - Return the slot corresponding to the attestation ``data``. - """ - committee_count = get_committee_count(state, data.target.epoch) - offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT - return Slot(compute_start_slot_of_epoch(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) -``` - #### `get_total_balance` ```python @@ -1019,7 +967,7 @@ def get_attesting_indices(state: BeaconState, """ Return the set of attesting indices corresponding to ``data`` and ``bits``. """ - committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard) + committee = get_crosslink_committee(state, data.target.epoch, data.index) return set(index for i, index in enumerate(committee) if bits[i]) ``` @@ -1199,7 +1147,6 @@ def process_slot(state: BeaconState) -> None: ```python def process_epoch(state: BeaconState) -> None: process_justification_and_finalization(state) - process_crosslinks(state) process_rewards_and_penalties(state) process_registry_updates(state) # @process_reveal_deadlines @@ -1230,7 +1177,7 @@ def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> Sequen def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]: return [ a for a in get_matching_source_attestations(state, epoch) - if a.data.beacon_block_root == get_block_root_at_slot(state, get_attestation_data_slot(state, a.data)) + if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot) ] ``` @@ -1248,23 +1195,6 @@ def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAtte return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) ``` -```python -def get_winning_crosslink_and_attesting_indices(state: BeaconState, - epoch: Epoch, - shard: Shard) -> Tuple[Crosslink, Set[ValidatorIndex]]: - attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] - crosslinks = filter( - lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)), - [a.data.crosslink for a in attestations] - ) - # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) - winning_crosslink = max(crosslinks, key=lambda c: ( - get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root - ), default=Crosslink()) - winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink] - return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations) -``` - #### Justification and finalization ```python @@ -1308,20 +1238,6 @@ def process_justification_and_finalization(state: BeaconState) -> None: state.finalized_checkpoint = old_current_justified_checkpoint ``` -#### Crosslinks - -```python -def process_crosslinks(state: BeaconState) -> None: - state.previous_crosslinks = [c for c in state.current_crosslinks] - for epoch in (get_previous_epoch(state), get_current_epoch(state)): - for offset in range(get_committee_count(state, epoch)): - shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) - crosslink_committee = set(get_crosslink_committee(state, epoch, shard)) - winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard) - if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee): - state.current_crosslinks[shard] = winning_crosslink -``` - #### Rewards and penalties ```python @@ -1384,36 +1300,15 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence return rewards, penalties ``` -```python -def get_crosslink_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: - rewards = [Gwei(0) for _ in range(len(state.validators))] - penalties = [Gwei(0) for _ in range(len(state.validators))] - epoch = get_previous_epoch(state) - for offset in range(get_committee_count(state, epoch)): - shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) - crosslink_committee = set(get_crosslink_committee(state, epoch, shard)) - winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard) - attesting_balance = get_total_balance(state, attesting_indices) - committee_balance = get_total_balance(state, crosslink_committee) - for index in crosslink_committee: - base_reward = get_base_reward(state, index) - if index in attesting_indices: - rewards[index] += base_reward * attesting_balance // committee_balance - else: - penalties[index] += base_reward - return rewards, penalties -``` - ```python def process_rewards_and_penalties(state: BeaconState) -> None: if get_current_epoch(state) == GENESIS_EPOCH: return rewards1, penalties1 = get_attestation_deltas(state) - rewards2, penalties2 = get_crosslink_deltas(state) for index in range(len(state.validators)): - increase_balance(state, ValidatorIndex(index), rewards1[index] + rewards2[index]) - decrease_balance(state, ValidatorIndex(index), penalties1[index] + penalties2[index]) + increase_balance(state, ValidatorIndex(index), rewards1[index]) + decrease_balance(state, ValidatorIndex(index), penalties1[index]) ``` #### Registry updates @@ -1481,8 +1376,6 @@ def process_final_updates(state: BeaconState) -> None: if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots) state.historical_roots.append(hash_tree_root(historical_batch)) - # Update start shard - state.start_shard = Shard((state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT) # Rotate current/previous epoch attestations state.previous_epoch_attestations = state.current_epoch_attestations state.current_epoch_attestations = [] @@ -1609,37 +1502,28 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: data = attestation.data - assert data.crosslink.shard < SHARD_COUNT + assert data.index < COMMITTEES_PER_SLOT assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) - attestation_slot = get_attestation_data_slot(state, data) - assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH + assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH - committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard) + committee = get_crosslink_committee(state, data.target.epoch, data.index) assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee) pending_attestation = PendingAttestation( data=data, aggregation_bits=attestation.aggregation_bits, - inclusion_delay=state.slot - attestation_slot, + inclusion_delay=state.slot - data.slot, proposer_index=get_beacon_proposer_index(state), ) if data.target.epoch == get_current_epoch(state): assert data.source == state.current_justified_checkpoint - parent_crosslink = state.current_crosslinks[data.crosslink.shard] state.current_epoch_attestations.append(pending_attestation) else: assert data.source == state.previous_justified_checkpoint - parent_crosslink = state.previous_crosslinks[data.crosslink.shard] state.previous_epoch_attestations.append(pending_attestation) - # Check crosslink against expected parent crosslink - assert data.crosslink.parent_root == hash_tree_root(parent_crosslink) - assert data.crosslink.start_epoch == parent_crosslink.end_epoch - assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK) - assert data.crosslink.data_root == Bytes32() # [to be removed in phase 1] - # Check signature assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) ``` diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index ad0590685..8e25fe8f3 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -192,7 +192,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None: # Attestations can only affect the fork choice of subsequent slots. # Delay consideration in the fork choice until their slot is in the past. - attestation_slot = get_attestation_data_slot(target_state, attestation.data) + attestation_slot = attestation.data.slot assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT # Get state at the `target` to validate attestation and calculate the committees diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 8a9cf1b5d..d43200463 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -41,7 +41,6 @@ - [Attestation data](#attestation-data) - [LMD GHOST vote](#lmd-ghost-vote) - [FFG vote](#ffg-vote) - - [Crosslink vote](#crosslink-vote) - [Construct attestation](#construct-attestation) - [Data](#data) - [Aggregation bits](#aggregation-bits) @@ -135,28 +134,25 @@ A validator can get committee assignments for a given epoch using the following ```python def get_committee_assignment(state: BeaconState, epoch: Epoch, - validator_index: ValidatorIndex) -> Optional[Tuple[Sequence[ValidatorIndex], Shard, Slot]]: + validator_index: ValidatorIndex + ) -> Optional[Tuple[Sequence[ValidatorIndex], uint64, Slot]]: """ Return the committee assignment in the ``epoch`` for ``validator_index``. ``assignment`` returned is a tuple of the following form: * ``assignment[0]`` is the list of validators in the committee - * ``assignment[1]`` is the shard to which the committee is assigned + * ``assignment[1]`` is the index to which the committee is assigned * ``assignment[2]`` is the slot at which the committee is assigned Return None if no assignment. """ next_epoch = get_current_epoch(state) + 1 assert epoch <= next_epoch - committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH start_slot = compute_start_slot_of_epoch(epoch) for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH): - offset = committees_per_slot * (slot % SLOTS_PER_EPOCH) - slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT - for i in range(committees_per_slot): - shard = Shard((slot_start_shard + i) % SHARD_COUNT) - committee = get_crosslink_committee(state, epoch, shard) + for index in range(COMMITTEES_PER_SLOT): + committee = get_crosslink_committee(state, epoch, index) if validator_index in committee: - return committee, shard, Slot(slot) + return committee, index, Slot(slot) return None ``` @@ -176,7 +172,7 @@ def is_proposer(state: BeaconState, The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question. -`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest and also which shard they should begin syncing (in Phase 1+). +`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest. Specifically, a validator should call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments. @@ -278,7 +274,7 @@ Up to `MAX_VOLUNTARY_EXITS`, [`VoluntaryExit`](../core/0_beacon-chain.md#volunta ### Attestations -A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `shard`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`. +A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `index`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`. A validator should create and broadcast the attestation halfway through the `slot` during which the validator is assigned―that is, `SECONDS_PER_SLOT * 0.5` seconds after the start of `slot`. @@ -303,16 +299,9 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`. - Let `start_slot = compute_start_slot_of_epoch(get_current_epoch(head_state))`. - Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`. -##### Crosslink vote +##### Index -Construct `attestation_data.crosslink` via the following. - -- Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee. -- Let `parent_crosslink = head_state.current_crosslinks[shard]`. -- Set `attestation_data.crosslink.start_epoch = parent_crosslink.end_epoch`. -- Set `attestation_data.crosslink.end_epoch = min(attestation_data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)`. -- Set `attestation_data.crosslink.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`. -- Set `attestation_data.crosslink.data_root = ZERO_HASH`. *Note*: This is a stub for Phase 0. +Set `attestation_data.index = index` where `index` is the index associated with the validator's committee. #### Construct attestation