From 5ceec70dd32b568fc95ea804d00c8d9df056f052 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 1 Mar 2019 07:59:52 -0600 Subject: [PATCH 01/53] Pythonize epoch transitions (in progress) --- specs/core/0_beacon-chain.md | 342 +++++++++++++++++++++++++---------- 1 file changed, 249 insertions(+), 93 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3ea4d4101..2b6c3bd07 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1772,135 +1772,291 @@ For each `transfer` in `block.body.transfers`: The steps below happen when `(state.slot + 1) % SLOTS_PER_EPOCH == 0`. -#### Helper variables +#### Helper functions + +We define some helper functions: -* Let `current_epoch = get_current_epoch(state)`. -* Let `previous_epoch = get_previous_epoch(state)`. -* Let `next_epoch = current_epoch + 1`. +```python +def get_current_attestations(state: BeaconState) -> List[PendingAttestation]: + return [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)] +``` -[Validators](#dfn-Validator) attesting during the current epoch: +```python +def get_current_total_balance(state: BeaconState): + return get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch)) +``` -* Let `current_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. -* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: Each of these attestations votes for the current justified epoch/block root because of the [attestation block validity rules](#attestations-1).) -* Validators justifying the epoch boundary block at the start of the current epoch: - * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch))]`. - * Let `current_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_boundary_attestations]`. - * Let `current_epoch_boundary_attesting_balance = get_total_balance(state, current_epoch_boundary_attester_indices)`. +```python +def get_previous_total_balance(state: BeaconState): + return get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch)) +``` -[Validators](#dfn-Validator) attesting during the previous epoch: +```python +def get_previous_attestations(state: BeaconState) -> List[PendingAttestation]: + return [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)] +``` -* Let `previous_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. -* Validators that made an attestation during the previous epoch, targeting the previous justified slot: - * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. (Note: Each of these attestations votes for the previous justified epoch/block root because of the [attestation block validity rules](#attestations-1).) - * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_attestations]`. - * Let `previous_epoch_attesting_balance = get_total_balance(state, previous_epoch_attester_indices)`. -* Validators justifying the epoch boundary block at the start of the previous epoch: - * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(previous_epoch))]`. - * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_boundary_attestations]`. - * Let `previous_epoch_boundary_attesting_balance = get_total_balance(state, previous_epoch_boundary_attester_indices)`. -* Validators attesting to the expected beacon chain head during the previous epoch: - * Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.data.beacon_block_root == get_block_root(state, a.data.slot)]`. - * Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_head_attestations]`. - * Let `previous_epoch_head_attesting_balance = get_total_balance(state, previous_epoch_head_attester_indices)`. +```python +def get_attesting_indices(attestations: List[PendingAttestation], state: BeaconState) -> List[int]: + output = set({}) + for a in attestations: + output = output.union([get_attestation_participants(state, a.data, a.aggregation_bitfield)]) + return sorted(list(output)) +``` -**Note**: `previous_total_balance` and `previous_epoch_boundary_attesting_balance` balance might be marginally different than the actual balances during previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. +```python +def get_attesting_balance(attestations: List[PendingAttestation], state: BeaconState) -> List[int]: + return get_total_balance(state, get_attesting_indices(attestations, state)) +``` -For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +```python +def get_current_epoch_boundary_attestations(state: BeaconState): + return [ + a for a in get_current_attestations(state) if + a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) + ] +``` -* Let `crosslink_data_root` be `state.latest_crosslinks[shard].crosslink_data_root` -* Let `attesting_validator_indices(crosslink_committee, crosslink_data_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_attestations + previous_epoch_attestations if a.data.shard == shard and a.data.crosslink_data_root == crosslink_data_root]`. -* Let `winning_root(crosslink_committee)` be equal to the value of `crosslink_data_root` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))` is maximized (ties broken by favoring lexicographically smallest `crosslink_data_root`). -* Let `attesting_validators(crosslink_committee)` be equal to `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience. -* Let `total_attesting_balance(crosslink_committee) = get_total_balance(state, attesting_validators(crosslink_committee))`. +```python +def get_previous_epoch_boundary_attestations(state: BeaconState): + return [ + a for a in get_previous_attestations(state) if + a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) + ] +``` -Define the following helpers to process attestation inclusion rewards and inclusion distance reward/penalty. For every attestation `a` in `previous_epoch_attestations`: +**Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. -* Let `inclusion_slot(state, index) = a.inclusion_slot` for the attestation `a` where `index` is in `get_attestation_participants(state, a.data, a.aggregation_bitfield)`. If multiple attestations are applicable, the attestation with lowest `inclusion_slot` is considered. -* Let `inclusion_distance(state, index) = a.inclusion_slot - a.data.slot` where `a` is the above attestation. +```python +def get_winning_root_and_participants(state: BeaconState, shard: Shard): + all_attestations = get_current_attestations(state) + get_previous_attestations(state) + valid_attestations = [ + a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] + ] + all_roots = [a.data.crosslink_data_root for a in valid_attestations] + + def get_attestations_for(root): + return [a for a in valid_attestations if a.data.crosslink_data_root == root] + + winning_root = max(all_roots, key=lambda r: get_attesting_balance(get_attestations_for(r))) + + return winning_root, get_attesting_indices(get_attestations_for(winning_root)) +``` -#### Eth1 data +```python +def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex): + return min([ + a for a in state.previous_epoch_attestations if + validator_index in get_attestation_participants(state, a.data, a.aggregation_bitfield) + ], key = lambda a: a.inclusion_slot) +``` -If `next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0`: +```python +def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex): + return earliest_attestation(state, validator_index).inclusion_slot +``` -* If `eth1_data_vote.vote_count * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH` for some `eth1_data_vote` in `state.eth1_data_votes` (ie. more than half the votes in this voting period were for that value), set `state.latest_eth1_data = eth1_data_vote.eth1_data`. -* Set `state.eth1_data_votes = []`. +```python +def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex): + attestation = earliest_attestation(state, validator_index) + return attestation.inclusion_slot - attestation.data_slot +``` #### Justification -First, update the justification bitfield: +Run the following function: -* Let `new_justified_epoch = state.justified_epoch`. -* Set `state.justification_bitfield = state.justification_bitfield << 1`. -* Set `state.justification_bitfield |= 2` and `new_justified_epoch = previous_epoch` if `3 * previous_epoch_boundary_attesting_balance >= 2 * previous_total_balance`. -* Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch` if `3 * current_epoch_boundary_attesting_balance >= 2 * current_total_balance`. +```python +def update_justification_and_finalization(state): -Next, update last finalized epoch if possible: - -* Set `state.finalized_epoch = state.previous_justified_epoch` if `(state.justification_bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == previous_epoch - 2`. -* Set `state.finalized_epoch = state.previous_justified_epoch` if `(state.justification_bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == previous_epoch - 1`. -* Set `state.finalized_epoch = state.justified_epoch` if `(state.justification_bitfield >> 0) % 8 == 0b111 and state.justified_epoch == previous_epoch - 1`. -* Set `state.finalized_epoch = state.justified_epoch` if `(state.justification_bitfield >> 0) % 4 == 0b11 and state.justified_epoch == previous_epoch`. - -Finally, update the following: - -* Set `state.previous_justified_epoch = state.justified_epoch`. -* Set `state.justified_epoch = new_justified_epoch`. + new_justified_epoch = state.justified_epoch + # Rotate the justification bitfield up one epoch to make room for the current epoch + state.justification_bitfield <<= 1 + # If the previous epoch gets justified, fill the second last bit + previous_boundary_attesting_balance = get_attesting_balance(get_previous_epoch_boundary_attestations(state)) + if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2: + new_justified_epoch = get_current_epoch(state) - 1 + state.justification_bitfield |= 2 + # If the current epoch gets justified, fill the last bit + current_boundary_attesting_balance = get_attesting_balance(get_current_epoch_boundary_attestations(state)) + if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2: + new_justified_epoch = get_current_epoch(state) + state.justification_bitfield |= 1 + + # Process finalizations + bitfield = state.justification_bitfield + current_epoch = get_current_epoch(state) + # The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source + if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3: + state.finalized_epoch = state.previous_justified_epoch + # The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source + if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2: + state.finalized_epoch = state.previous_justified_epoch + # The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source + if (bitfield >> 0) % 8 == 0b111 and state.justified_epoch == current_epoch - 2: + state.finalized_epoch = state.justified_epoch + # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source + if (bitfield >> 0) % 4 == 0b11 and state.justified_epoch == current_epoch - 1: + state.finalized_epoch = state.justified_epoch + + # Rotate justified epochs + state.previous_justified_epoch = state.justified_epoch + state.justified_epoch = new_justified_epoch +``` #### Crosslinks -For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +Run the following function: -* Set `state.latest_crosslinks[shard] = Crosslink(epoch=slot_to_epoch(slot), crosslink_data_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. +```python +for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): + for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): + winning_root, participants = get_winning_root_and_participants(state, shard) + if 3 * total_balance(participants) >= 2 * total_balance(crosslink_committee): + state.latest_crosslinks[shard] = Crosslink( + epoch=slot_to_epoch(slot), + crosslink_data_root=winning_root + ) +``` + +#### Eth1 data + +Run the following function: + +```python +def maybe_reset_eth1_period(state): + if (get_current_epoch(state) + 1) % EPOCHS_PER_ETH1_VOTING_PERIOD == 0: + for eth1_data_vote in state.eth1_data_votes: + # If a majority of all votes were for a particular eth1_data value, + # then set that as the new canonical value + if eth1_data_vote.vote_count * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH: + state.latest_eth1_data = eth1_data_vote.eth1_data + state.eth1_data_votes = [] +``` #### Rewards and penalties First, we define some additional helpers: -* Let `base_reward_quotient = integer_squareroot(previous_total_balance) // BASE_REWARD_QUOTIENT`. -* Let `base_reward(state, index) = get_effective_balance(state, index) // base_reward_quotient // 5` for any validator with the given `index`. -* Let `inactivity_penalty(state, index, epochs_since_finality) = base_reward(state, index) + get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2` for any validator with the given `index`. +```python +def get_base_reward(state: BeaconState, index: ValidatorIndex) -> int: + adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT + return get_effective_balance(state, index) // adjusted_quotient // 5 +``` + +```python +def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex) -> int: + return ( + base_reward(state, index) + + get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2 + ) +``` Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. ##### Justification and finalization -* Let `previous_active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch)` -* Let `epochs_since_finality = next_epoch - state.finalized_epoch`. - -Case 1: `epochs_since_finality <= 4`: - -* Expected FFG source: - * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` gains `base_reward(state, index) * previous_epoch_attesting_balance // previous_total_balance`. - * Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_attester_indices` loses `base_reward(state, index)`. -* Expected FFG target: - * Any [validator](#dfn-validator) `index` in `previous_epoch_boundary_attester_indices` gains `base_reward(state, index) * previous_epoch_boundary_attesting_balance // previous_total_balance`. - * Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_boundary_attester_indices` loses `base_reward(state, index)`. -* Expected beacon chain head: - * Any [validator](#dfn-validator) `index` in `previous_epoch_head_attester_indices` gains `base_reward(state, index) * previous_epoch_head_attesting_balance // previous_total_balance)`. - * Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_head_attester_indices` loses `base_reward(state, index)`. -* Inclusion distance: - * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` gains `base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` - -Case 2: `epochs_since_finality > 4`: - -* Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. -* Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. -* Any [active validator](#dfn-active-validator) `index` from `previous_active_validator_indices` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active validator](#dfn-active-validator) `index` with `validator.slashed is True`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. -* Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` - -##### Attestation inclusion - -For each `index` in `previous_epoch_attester_indices`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))` and set `state.validator_balances[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT`. +```python +def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Int -> Int]: + previous_active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) + epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch + active_validator_indices = [ + i for i in range(len(state.validator_registry)) if + is_active_validator(state.validator_registry[i], get_current_epoch(state) - 1) + ] + # Initialize deltas that we are returning to zero + deltas = {index: 0 for index in range(len(state.validator_registry))} + # Some helper variables + boundary_attestations = get_previous_epoch_boundary_attestations(state) + boundary_attesting_balance = get_attesting_balance(boundary_attestations) + total_balance = get_previous_total_balance(state) + total_attesting_balance = get_attesting_balance(get_previous_epoch_attestations(state)) + matching_head_attestations = [ + a for a in get_previous_epoch_attestations(state) if + a.data.beacon_block_root == get_block_root(state, a.data.slot) + ] + matching_head_balance = get_attesting_balance(matching_head_attestations) + # Normal case + if epochs_since_finality <= 4: + for index in active_validator_indices: + # Expected FFG source + if index in get_attesting_indices(get_previous_epoch_attestations(state)): + deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance + # Inclusion speed bonus + deltas[index] += ( + get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // + inclusion_distance(state, index) + ) + else: + deltas[index] -= get_base_reward(state, index) + # Expected FFG target + if index in get_attesting_indices(boundary_attestations): + deltas[index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance + else: + deltas[index] -= get_base_reward(state, index) + # Expected head + if index in matching_head_attestations: + deltas[index] += get_base_reward(state, index) * matching_head_balance // total_balance + else: + deltas[index] -= get_base_reward(state, index) + # Proposer bonus + proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) + deltas[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT + # Quadratic leak case + else: + for index in active_validator_indices: + if index not in get_attesting_indices(get_previous_epoch_attestations(state)): + deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + else: + # If a validator did attest, apply a small penalty for getting attestations included late + deltas[index] += ( + base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // + inclusion_distance(state, index) + ) + deltas[index] -= base_reward(state, index) + + if index not in get_attesting_indices(boundary_attestations): + deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + if index not in matching_head_attestations: + deltas[index] -= get_base_reward(state, index) + for index in range(len(state.validator_registry)): + if index not in active_validator_indices and state.validator_registry[index].slashed: + deltas[index] -= ( + 2 * get_inactivity_penalty(state, index, epochs_since_finality) + + get_base_reward(state, index) + ) + return deltas +``` ##### Crosslinks -For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch))`: +```python +def get_crosslink_reward_deltas(state: BeaconState) -> Dict[int, int]: + deltas = {index: 0 for index in range(len(state.validator_registry))} + for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): + for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): + winning_root, participants = get_winning_root_and_participants(state, shard) + participating_balance = get_total_balance(state, participants) + total_balance = get_total_balance(state, committee) + for index in crosslink_committee: + if index in participants: + deltas[index] += get_base_reward(state, index) * participating_balance // total_balance + else: + deltas[index] -= get_base_reward(state, index) + return deltas +``` -* Let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. -* For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot` and every `index` in `crosslink_committee`: - * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // get_total_balance(state, crosslink_committee))`. - * If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. +#### Apply rewards + +Run the following: + +```python +def apply_rewards(state: BeaconState): + deltas1 = get_justification_and_finalization_deltas(state) + deltas2 = get_crosslink_deltas(state) + for i in range(len(state.validator_registry)): + state.validator_balances[i] += deltas1[i] + deltas2[i] +``` #### Ejections From 6c1abbc2fc8cfa3a74f6b69ff5dbaa65cdbc878e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Mar 2019 19:12:11 -0600 Subject: [PATCH 02/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2b6c3bd07..bf65b66c1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1782,7 +1782,7 @@ def get_current_attestations(state: BeaconState) -> List[PendingAttestation]: ``` ```python -def get_current_total_balance(state: BeaconState): +def get_current_total_balance(state: BeaconState) -> Gwei: return get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch)) ``` From 46269310ee228591708fa27fdbd14c3896d38756 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Mar 2019 19:12:21 -0600 Subject: [PATCH 03/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bf65b66c1..09f8b7280 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1787,7 +1787,7 @@ def get_current_total_balance(state: BeaconState) -> Gwei: ``` ```python -def get_previous_total_balance(state: BeaconState): +def get_previous_total_balance(state: BeaconState) -> Gwei: return get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch)) ``` From e0b8eea7d6c27512dea0e67120f2badc8eec5257 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Mar 2019 19:12:31 -0600 Subject: [PATCH 04/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 09f8b7280..ab4866bba 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1793,7 +1793,7 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: ```python def get_previous_attestations(state: BeaconState) -> List[PendingAttestation]: - return [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)] + return [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)] ``` ```python From 4cc738dc539aa2f9498923562b9089cf029abf4b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 1 Mar 2019 19:17:07 -0600 Subject: [PATCH 05/53] Apply suggestions from code review Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ab4866bba..7a66ade84 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1797,7 +1797,7 @@ def get_previous_attestations(state: BeaconState) -> List[PendingAttestation]: ``` ```python -def get_attesting_indices(attestations: List[PendingAttestation], state: BeaconState) -> List[int]: +def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: output = set({}) for a in attestations: output = output.union([get_attestation_participants(state, a.data, a.aggregation_bitfield)]) @@ -1805,12 +1805,12 @@ def get_attesting_indices(attestations: List[PendingAttestation], state: BeaconS ``` ```python -def get_attesting_balance(attestations: List[PendingAttestation], state: BeaconState) -> List[int]: +def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: return get_total_balance(state, get_attesting_indices(attestations, state)) ``` ```python -def get_current_epoch_boundary_attestations(state: BeaconState): +def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ a for a in get_current_attestations(state) if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) @@ -1818,7 +1818,7 @@ def get_current_epoch_boundary_attestations(state: BeaconState): ``` ```python -def get_previous_epoch_boundary_attestations(state: BeaconState): +def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ a for a in get_previous_attestations(state) if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) @@ -1828,14 +1828,14 @@ def get_previous_epoch_boundary_attestations(state: BeaconState): **Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. ```python -def get_winning_root_and_participants(state: BeaconState, shard: Shard): +def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: all_attestations = get_current_attestations(state) + get_previous_attestations(state) valid_attestations = [ a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] ] all_roots = [a.data.crosslink_data_root for a in valid_attestations] - def get_attestations_for(root): + def get_attestations_for(root) -> List[PendingAttestation]: return [a for a in valid_attestations if a.data.crosslink_data_root == root] winning_root = max(all_roots, key=lambda r: get_attesting_balance(get_attestations_for(r))) @@ -1844,22 +1844,22 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard): ``` ```python -def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex): +def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex) -> PendingAttestation: return min([ a for a in state.previous_epoch_attestations if validator_index in get_attestation_participants(state, a.data, a.aggregation_bitfield) - ], key = lambda a: a.inclusion_slot) + ], key=lambda a: a.inclusion_slot) ``` ```python -def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex): +def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex) -> Slot: return earliest_attestation(state, validator_index).inclusion_slot ``` ```python -def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex): +def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex) -> int: attestation = earliest_attestation(state, validator_index) - return attestation.inclusion_slot - attestation.data_slot + return attestation.inclusion_slot - attestation.data.slot ``` #### Justification @@ -1867,7 +1867,7 @@ def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex): Run the following function: ```python -def update_justification_and_finalization(state): +def update_justification_and_finalization(state: BeaconState) -> None: new_justified_epoch = state.justified_epoch # Rotate the justification bitfield up one epoch to make room for the current epoch @@ -1924,7 +1924,7 @@ for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(nex Run the following function: ```python -def maybe_reset_eth1_period(state): +def maybe_reset_eth1_period(state: BeaconState) -> None: if (get_current_epoch(state) + 1) % EPOCHS_PER_ETH1_VOTING_PERIOD == 0: for eth1_data_vote in state.eth1_data_votes: # If a majority of all votes were for a particular eth1_data value, @@ -1939,15 +1939,15 @@ def maybe_reset_eth1_period(state): First, we define some additional helpers: ```python -def get_base_reward(state: BeaconState, index: ValidatorIndex) -> int: +def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT return get_effective_balance(state, index) // adjusted_quotient // 5 ``` ```python -def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex) -> int: +def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex) -> Gwei: return ( - base_reward(state, index) + + get_base_reward(state, index) + get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2 ) ``` @@ -1957,7 +1957,7 @@ Note: When applying penalties in the following balance recalculations implemente ##### Justification and finalization ```python -def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Int -> Int]: +def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[ValidatorIndex, Gwei]: previous_active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch active_validator_indices = [ @@ -1995,7 +1995,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Int -> else: deltas[index] -= get_base_reward(state, index) # Expected head - if index in matching_head_attestations: + if index in get_attesting_indices(matching_head_attestations): deltas[index] += get_base_reward(state, index) * matching_head_balance // total_balance else: deltas[index] -= get_base_reward(state, index) @@ -2017,7 +2017,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Int -> if index not in get_attesting_indices(boundary_attestations): deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) - if index not in matching_head_attestations: + if index not in get_attesting_indices(matching_head_attestations): deltas[index] -= get_base_reward(state, index) for index in range(len(state.validator_registry)): if index not in active_validator_indices and state.validator_registry[index].slashed: @@ -2031,9 +2031,9 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Int -> ##### Crosslinks ```python -def get_crosslink_reward_deltas(state: BeaconState) -> Dict[int, int]: +def get_crosslink_deltas(state: BeaconState) -> Dict[ValidatorIndex, Gwei]: deltas = {index: 0 for index in range(len(state.validator_registry))} - for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): + for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) participating_balance = get_total_balance(state, participants) @@ -2051,7 +2051,7 @@ def get_crosslink_reward_deltas(state: BeaconState) -> Dict[int, int]: Run the following: ```python -def apply_rewards(state: BeaconState): +def apply_rewards(state: BeaconState) -> None: deltas1 = get_justification_and_finalization_deltas(state) deltas2 = get_crosslink_deltas(state) for i in range(len(state.validator_registry)): From 7c6232a602c8cea2eabcd359f46026b18d157962 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 1 Mar 2019 19:22:24 -0600 Subject: [PATCH 06/53] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7a66ade84..aa57d2d8e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1958,12 +1958,8 @@ Note: When applying penalties in the following balance recalculations implemente ```python def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[ValidatorIndex, Gwei]: - previous_active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) + active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch - active_validator_indices = [ - i for i in range(len(state.validator_registry)) if - is_active_validator(state.validator_registry[i], get_current_epoch(state) - 1) - ] # Initialize deltas that we are returning to zero deltas = {index: 0 for index in range(len(state.validator_registry))} # Some helper variables @@ -1980,7 +1976,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Valida if epochs_since_finality <= 4: for index in active_validator_indices: # Expected FFG source - if index in get_attesting_indices(get_previous_epoch_attestations(state)): + if index in get_attesting_indices(get_previous_attestations(state)): deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance # Inclusion speed bonus deltas[index] += ( @@ -2019,6 +2015,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[Valida deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) if index not in get_attesting_indices(matching_head_attestations): deltas[index] -= get_base_reward(state, index) + # Penalize slashed-but-inactive validators as though they were active but offline for index in range(len(state.validator_registry)): if index not in active_validator_indices and state.validator_registry[index].slashed: deltas[index] -= ( @@ -2055,7 +2052,7 @@ def apply_rewards(state: BeaconState) -> None: deltas1 = get_justification_and_finalization_deltas(state) deltas2 = get_crosslink_deltas(state) for i in range(len(state.validator_registry)): - state.validator_balances[i] += deltas1[i] + deltas2[i] + state.validator_balances[i] = max(0, state.validator_balances[i] + deltas1[i] + deltas2[i]) ``` #### Ejections From 2d4c8af4893ce362ebff5d95c789016cf070a877 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 1 Mar 2019 19:34:26 -0600 Subject: [PATCH 07/53] Split up big function into two smaller functions --- specs/core/0_beacon-chain.md | 139 ++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 59 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index aa57d2d8e..0e0662f39 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1825,6 +1825,14 @@ def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[Pending ] ``` +```python +def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[PendingAttestation]: + return [ + a for a in get_previous_epoch_attestations(state) if + a.data.beacon_block_root == get_block_root(state, a.data.slot) + ] +``` + **Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. ```python @@ -1957,79 +1965,92 @@ Note: When applying penalties in the following balance recalculations implemente ##### Justification and finalization ```python -def get_justification_and_finalization_deltas(state: BeaconState) -> Dict[ValidatorIndex, Gwei]: - active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) +def get_justification_and_finalization_deltas(state: BeaconState) -> List[Gwei]: epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch - # Initialize deltas that we are returning to zero - deltas = {index: 0 for index in range(len(state.validator_registry))} + if epochs_since_finality <= 4: + return compute_normal_justification_and_finalization_deltas(state) + else: + return compute_inactivity_leak_deltas(state) +``` + +When blocks are finalizing normally... + +```python +def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> List[Gwei]: + deltas = [0 for index in range(len(state.validator_registry))] # Some helper variables boundary_attestations = get_previous_epoch_boundary_attestations(state) boundary_attesting_balance = get_attesting_balance(boundary_attestations) total_balance = get_previous_total_balance(state) total_attesting_balance = get_attesting_balance(get_previous_epoch_attestations(state)) - matching_head_attestations = [ - a for a in get_previous_epoch_attestations(state) if - a.data.beacon_block_root == get_block_root(state, a.data.slot) - ] + matching_head_attestations = get_previous_epoch_matching_head_attestations(state) matching_head_balance = get_attesting_balance(matching_head_attestations) - # Normal case - if epochs_since_finality <= 4: - for index in active_validator_indices: - # Expected FFG source - if index in get_attesting_indices(get_previous_attestations(state)): - deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance - # Inclusion speed bonus - deltas[index] += ( - get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // - inclusion_distance(state, index) - ) - else: - deltas[index] -= get_base_reward(state, index) - # Expected FFG target - if index in get_attesting_indices(boundary_attestations): - deltas[index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance - else: - deltas[index] -= get_base_reward(state, index) - # Expected head - if index in get_attesting_indices(matching_head_attestations): - deltas[index] += get_base_reward(state, index) * matching_head_balance // total_balance - else: - deltas[index] -= get_base_reward(state, index) - # Proposer bonus - proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) - deltas[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT - # Quadratic leak case - else: - for index in active_validator_indices: - if index not in get_attesting_indices(get_previous_epoch_attestations(state)): - deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) - else: - # If a validator did attest, apply a small penalty for getting attestations included late - deltas[index] += ( - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // - inclusion_distance(state, index) - ) - deltas[index] -= base_reward(state, index) + # Process rewards or penalties for all validators + for index in get_active_validator_indices(state.validator_registry, previous_epoch): + # Expected FFG source + if index in get_attesting_indices(get_previous_attestations(state)): + deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance + # Inclusion speed bonus + deltas[index] += ( + get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // + inclusion_distance(state, index) + ) + else: + deltas[index] -= get_base_reward(state, index) + # Expected FFG target + if index in get_attesting_indices(boundary_attestations): + deltas[index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance + else: + deltas[index] -= get_base_reward(state, index) + # Expected head + if index in get_attesting_indices(matching_head_attestations): + deltas[index] += get_base_reward(state, index) * matching_head_balance // total_balance + else: + deltas[index] -= get_base_reward(state, index) + # Proposer bonus + proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) + deltas[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT +``` + +When blocks are not finalizing normally... + +```python +def compute_inactivity_leak_deltas(state: BeaconState) -> List[Gwei]: + deltas = [0 for index in range(len(state.validator_registry))] + boundary_attestations = get_previous_epoch_boundary_attestations(state) + matching_head_attestations = get_previous_epoch_matching_head_attestations(state) + active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) + epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch + for index in active_validator_indices: + if index not in get_attesting_indices(get_previous_epoch_attestations(state)): + deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + else: + # If a validator did attest, apply a small penalty for getting attestations included late + deltas[index] += ( + base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // + inclusion_distance(state, index) + ) + deltas[index] -= base_reward(state, index) - if index not in get_attesting_indices(boundary_attestations): - deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) - if index not in get_attesting_indices(matching_head_attestations): - deltas[index] -= get_base_reward(state, index) - # Penalize slashed-but-inactive validators as though they were active but offline - for index in range(len(state.validator_registry)): - if index not in active_validator_indices and state.validator_registry[index].slashed: - deltas[index] -= ( - 2 * get_inactivity_penalty(state, index, epochs_since_finality) + - get_base_reward(state, index) - ) + if index not in get_attesting_indices(boundary_attestations): + deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + if index not in get_attesting_indices(matching_head_attestations): + deltas[index] -= get_base_reward(state, index) + # Penalize slashed-but-inactive validators as though they were active but offline + for index in range(len(state.validator_registry)): + if index not in active_validator_indices and state.validator_registry[index].slashed: + deltas[index] -= ( + 2 * get_inactivity_penalty(state, index, epochs_since_finality) + + get_base_reward(state, index) + ) return deltas ``` ##### Crosslinks ```python -def get_crosslink_deltas(state: BeaconState) -> Dict[ValidatorIndex, Gwei]: - deltas = {index: 0 for index in range(len(state.validator_registry))} +def get_crosslink_deltas(state: BeaconState) -> List[Gwei]: + deltas = [0 for index in range(len(state.validator_registry))] for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) From 4ad3529e4eb5ef100845b255aa42171900e17e3d Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 1 Mar 2019 19:49:28 -0600 Subject: [PATCH 08/53] Finished epoch transition --- specs/core/0_beacon-chain.md | 96 +++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0e0662f39..a75218329 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -137,6 +137,7 @@ - [Crosslinks](#crosslinks-1) - [Ejections](#ejections) - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Slashings and exit queue](#slashings-and-exit-queue) - [Final updates](#final-updates) - [State root verification](#state-root-verification) - [References](#references) @@ -2093,18 +2094,21 @@ def process_ejections(state: BeaconState) -> None: #### Validator registry and shuffling seed data -First, update the following: - -* Set `state.previous_shuffling_epoch = state.current_shuffling_epoch`. -* Set `state.previous_shuffling_start_shard = state.current_shuffling_start_shard`. -* Set `state.previous_shuffling_seed = state.current_shuffling_seed`. - -If the following are satisfied: - -* `state.finalized_epoch > state.validator_registry_update_epoch` -* `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_shuffling_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state))]` (that is, for every shard in the current committees) - -update the validator registry and associated fields by running +```python +def should_update_validator_registry(state: BeaconState) -> bool: + # Must have finalized a new block + if state.finalized_epoch <= state.validator_registry_update_epoch: + return False + # Must have processed new crosslinks on all shards of the current epoch + shards_to_check [ + (state.current_shuffling_start_shard + i) % SHARD_COUNT + for i in range(get_current_epoch_committee_count(state)) + ] + for shard in shards_to_check: + if state.latest_crosslinks[shard].epoch <= state.validator_registry_update_epoch: + return False + return True +``` ```python def update_validator_registry(state: BeaconState) -> None: @@ -2151,23 +2155,38 @@ def update_validator_registry(state: BeaconState) -> None: state.validator_registry_update_epoch = current_epoch ``` -and perform the following updates: +Run the following function: -* Set `state.current_shuffling_epoch = next_epoch` -* Set `state.current_shuffling_start_shard = (state.current_shuffling_start_shard + get_current_epoch_committee_count(state)) % SHARD_COUNT` -* Set `state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)` - -If a validator registry update does _not_ happen do the following: - -* Let `epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch`. -* If `epochs_since_last_registry_update > 1` and `is_power_of_two(epochs_since_last_registry_update)`: - * Set `state.current_shuffling_epoch = next_epoch`. - * Set `state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)` - * _Note_ that `state.current_shuffling_start_shard` is left unchanged. +```python +def update_registry_and_shuffling_data(state: BeaconState) -> None: + # First set previous shuffling data to current shuffling data + state.previous_shuffling_epoch = state.current_shuffling_epoch + state.previous_shuffling_start_shard = state.current_shuffling_start_shard + state.previous_shuffling_seed = state.current_shuffling_seed + # Check if we should update, and if so, update + if should_update_validator_registry(state): + update_validator_registry(state) + # If we update the registry, update the shuffling data and shards as well + state.current_shuffling_epoch = next_epoch + state.current_shuffling_start_shard = ( + state.current_shuffling_start_shard + + get_current_epoch_committee_count(state)) % SHARD_COUNT + ) + state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) + else: + # If processing at least one crosslink keeps failing, then reshuffle every power of two, + # but don't update the current_shuffling_start_shard + epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch + if epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update): + state.current_shuffling_epoch = next_epoch + state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) +``` **Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. -Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_exit_queue(state)`: +#### Slashings and exit queue + +Run `process_slashings(state)` and `process_exit_queue(state)`: ```python def process_slashings(state: BeaconState) -> None: @@ -2218,10 +2237,29 @@ def process_exit_queue(state: BeaconState) -> None: #### Final updates -* Set `state.latest_active_index_roots[(next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY))`. -* Set `state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH]`. -* Set `state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch)`. -* Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch`. +Run the following function: + +```python +def finish_epoch_update(state: BeaconState) -> None: + current_epoch = get_current_epoch(state) + next_epoch = current_epoch + 1 + # Set active index root + index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH + state.latest_active_index_roots[index_root_position] = hash_tree_root( + get_active_validator_indices(state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY) + ) + # Set total slashed balances + state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = ( + state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + ) + # Set randao mix + state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) + # Remove previous epoch attestations + state.latest_attestations = [ + a for a in state.latest_attestations if + slot_to_epoch(attestation.data.slot) == current_epoch + ] +``` ### State root verification From 0134e560101d15284dd3403f242b20a9be1e58ee Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 2 Mar 2019 00:40:43 -0600 Subject: [PATCH 09/53] Fixed table of contents --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a75218329..fac0cb640 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -127,10 +127,10 @@ - [Voluntary exits](#voluntary-exits-1) - [Transfers](#transfers-1) - [Per-epoch processing](#per-epoch-processing) - - [Helper variables](#helper-variables) - - [Eth1 data](#eth1-data-1) + - [Helper functions](#helper-functions-1) - [Justification](#justification) - [Crosslinks](#crosslinks) + - [Eth1 data](#eth1-data-1) - [Rewards and penalties](#rewards-and-penalties) - [Justification and finalization](#justification-and-finalization) - [Attestation inclusion](#attestation-inclusion) From ce5599db2fb4a8f96325af9900c379efb7c0e60f Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 2 Mar 2019 00:42:20 -0600 Subject: [PATCH 10/53] Made crosslink processing a function --- specs/core/0_beacon-chain.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index fac0cb640..70ffcf858 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1918,14 +1918,15 @@ def update_justification_and_finalization(state: BeaconState) -> None: Run the following function: ```python -for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): - for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): - winning_root, participants = get_winning_root_and_participants(state, shard) - if 3 * total_balance(participants) >= 2 * total_balance(crosslink_committee): - state.latest_crosslinks[shard] = Crosslink( - epoch=slot_to_epoch(slot), - crosslink_data_root=winning_root - ) +def process_crosslinks(state: BeaconState) -> None: + for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): + for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): + winning_root, participants = get_winning_root_and_participants(state, shard) + if 3 * total_balance(participants) >= 2 * total_balance(crosslink_committee): + state.latest_crosslinks[shard] = Crosslink( + epoch=slot_to_epoch(slot), + crosslink_data_root=winning_root + ) ``` #### Eth1 data From fde66a7105ab2b38c755cc564aac3d66c33069be Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 2 Mar 2019 20:11:33 -0600 Subject: [PATCH 11/53] Update to make CC happy --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 70ffcf858..023d64339 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1847,7 +1847,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple def get_attestations_for(root) -> List[PendingAttestation]: return [a for a in valid_attestations if a.data.crosslink_data_root == root] - winning_root = max(all_roots, key=lambda r: get_attesting_balance(get_attestations_for(r))) + winning_root = max(all_roots, key=lambda r: get_attesting_balance(state, get_attestations_for(r))) return winning_root, get_attesting_indices(get_attestations_for(winning_root)) ``` From c42cab87a790cc94422ddce90c364fd2b24f90b2 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:55:20 -0600 Subject: [PATCH 12/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 023d64339..801be22f0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1789,7 +1789,7 @@ def get_current_total_balance(state: BeaconState) -> Gwei: ```python def get_previous_total_balance(state: BeaconState) -> Gwei: - return get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch)) + return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_previous_epoch(state))) ``` ```python From 2d6697eb62372d74c87ba2aae36a52558053de81 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:56:12 -0600 Subject: [PATCH 13/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 801be22f0..da107dad5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1794,7 +1794,7 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: ```python def get_previous_attestations(state: BeaconState) -> List[PendingAttestation]: - return [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)] + return [a for a in state.latest_attestations if get_previous_epoch(state) == slot_to_epoch(a.data.slot)] ``` ```python From fa613b401f8b2f1df85fc96832c5cf5aaeadbc74 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:56:54 -0600 Subject: [PATCH 14/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index da107dad5..bd14fa079 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2164,6 +2164,8 @@ def update_registry_and_shuffling_data(state: BeaconState) -> None: state.previous_shuffling_epoch = state.current_shuffling_epoch state.previous_shuffling_start_shard = state.current_shuffling_start_shard state.previous_shuffling_seed = state.current_shuffling_seed + current_epoch = get_current_epoch(state) + next_epoch = current_epoch + 1 # Check if we should update, and if so, update if should_update_validator_registry(state): update_validator_registry(state) From 1103fd1ee86ddb2ef8b382454bc48cd4cdf07773 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:57:27 -0600 Subject: [PATCH 15/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bd14fa079..a84d6fde8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1779,7 +1779,7 @@ We define some helper functions: ```python def get_current_attestations(state: BeaconState) -> List[PendingAttestation]: - return [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)] + return [a for a in state.latest_attestations if get_current_epoch(state) == slot_to_epoch(a.data.slot)] ``` ```python From 07599b3a4532bdb4b3d6712d1fad2926ab6b0f3a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:57:48 -0600 Subject: [PATCH 16/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a84d6fde8..dadd9d3a5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1784,7 +1784,7 @@ def get_current_attestations(state: BeaconState) -> List[PendingAttestation]: ```python def get_current_total_balance(state: BeaconState) -> Gwei: - return get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch)) + return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_current_epoch(state))) ``` ```python From 19924f314873d61e04f35160b02d04a10f5eccdf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:58:18 -0600 Subject: [PATCH 17/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dadd9d3a5..5b3af73bf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2021,7 +2021,7 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> List[Gwei]: deltas = [0 for index in range(len(state.validator_registry))] boundary_attestations = get_previous_epoch_boundary_attestations(state) matching_head_attestations = get_previous_epoch_matching_head_attestations(state) - active_validator_indices = get_active_validator_indices(state.validator_registry, previous_epoch) + active_validator_indices = get_active_validator_indices(state.validator_registry, get_previous_epoch(state)) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch for index in active_validator_indices: if index not in get_attesting_indices(get_previous_epoch_attestations(state)): From cd7c7228c908f6e271b7d937eb5bad5f3f165ffb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 03:59:12 -0600 Subject: [PATCH 18/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 5b3af73bf..c10f1d6f5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2053,7 +2053,9 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> List[Gwei]: ```python def get_crosslink_deltas(state: BeaconState) -> List[Gwei]: deltas = [0 for index in range(len(state.validator_registry))] - for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch)): + previous_epoch_start_slot = get_epoch_start_slot(get_previous_epoch(state)) + current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) + for slot in range(previous_epoch_start_slot, current_epoch_start_slot): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) participating_balance = get_total_balance(state, participants) From 868359a6747d05c60774244d234caa764947069c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 04:00:09 -0600 Subject: [PATCH 19/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c10f1d6f5..23dccc4e3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1877,7 +1877,6 @@ Run the following function: ```python def update_justification_and_finalization(state: BeaconState) -> None: - new_justified_epoch = state.justified_epoch # Rotate the justification bitfield up one epoch to make room for the current epoch state.justification_bitfield <<= 1 From 46b1d4eef5e5971e0e820348d28a7091db6e2a27 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 04:28:13 -0600 Subject: [PATCH 20/53] Assimilated #697 --- specs/core/0_beacon-chain.md | 38 ++++++++++++++---------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 23dccc4e3..20d8ffcec 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -533,6 +533,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'current_shuffling_seed': 'bytes32', # Finality + 'previous_epoch_attestations': [PendingAttestation], + 'current_epoch_attestations': [PendingAttestation], 'previous_justified_epoch': 'uint64', 'justified_epoch': 'uint64', 'justification_bitfield': 'uint64', @@ -543,7 +545,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'latest_block_roots': ['bytes32'], 'latest_active_index_roots': ['bytes32'], 'latest_slashed_balances': ['uint64'], # Balances slashed at every withdrawal period - 'latest_attestations': [PendingAttestation], 'batched_block_roots': ['bytes32'], # Ethereum 1.0 chain data @@ -1461,6 +1462,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], current_shuffling_seed=ZERO_HASH, # Finality + previous_epoch_attestations=[], + current_epoch_attestations=[], previous_justified_epoch=GENESIS_EPOCH, justified_epoch=GENESIS_EPOCH, justification_bitfield=0, @@ -1471,7 +1474,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_active_index_roots=[ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)], latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], - latest_attestations=[], batched_block_roots=[], # Ethereum 1.0 chain data @@ -1702,7 +1704,9 @@ For each `attestation` in `block.body.attestations`: ``` * [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`. -* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)` to `state.latest_attestations`. +* Let `pending_attestation = PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)`. +* Append `pending_attestation` to `state.previous_epoch_attestations` if `slot_to_epoch(attestation.data.slot) == get_previous_epoch(state)`. +* Append `pending_attestation` to `state.current_epoch_attestations` if `slot_to_epoch(attestation.data.slot) == get_current_epoch(state)`. ##### Deposits @@ -1777,11 +1781,6 @@ The steps below happen when `(state.slot + 1) % SLOTS_PER_EPOCH == 0`. We define some helper functions: -```python -def get_current_attestations(state: BeaconState) -> List[PendingAttestation]: - return [a for a in state.latest_attestations if get_current_epoch(state) == slot_to_epoch(a.data.slot)] -``` - ```python def get_current_total_balance(state: BeaconState) -> Gwei: return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_current_epoch(state))) @@ -1792,11 +1791,6 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_previous_epoch(state))) ``` -```python -def get_previous_attestations(state: BeaconState) -> List[PendingAttestation]: - return [a for a in state.latest_attestations if get_previous_epoch(state) == slot_to_epoch(a.data.slot)] -``` - ```python def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: output = set({}) @@ -1813,7 +1807,7 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat ```python def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ - a for a in get_current_attestations(state) if + a for a in state.current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) ] ``` @@ -1821,7 +1815,7 @@ def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingA ```python def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ - a for a in get_previous_attestations(state) if + a for a in state.previous_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) ] ``` @@ -1829,7 +1823,7 @@ def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[Pending ```python def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[PendingAttestation]: return [ - a for a in get_previous_epoch_attestations(state) if + a for a in state.previous_epoch_attestations if a.data.beacon_block_root == get_block_root(state, a.data.slot) ] ``` @@ -1838,7 +1832,7 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe ```python def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: - all_attestations = get_current_attestations(state) + get_previous_attestations(state) + all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations valid_attestations = [ a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] ] @@ -1989,7 +1983,7 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> # Process rewards or penalties for all validators for index in get_active_validator_indices(state.validator_registry, previous_epoch): # Expected FFG source - if index in get_attesting_indices(get_previous_attestations(state)): + if index in get_attesting_indices(state.previous_epoch_attestations): deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance # Inclusion speed bonus deltas[index] += ( @@ -2258,11 +2252,9 @@ def finish_epoch_update(state: BeaconState) -> None: ) # Set randao mix state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) - # Remove previous epoch attestations - state.latest_attestations = [ - a for a in state.latest_attestations if - slot_to_epoch(attestation.data.slot) == current_epoch - ] + # Rotate current/previous epoch attestations + state.previous_epoch_attestations = state.current_epoch_attestations + state.current_epoch_attestations = [] ``` ### State root verification From d0fc455a1f3953fedaab3ccacc8ecebd3586bf19 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 05:04:28 -0600 Subject: [PATCH 21/53] Assimilated #649 --- specs/core/0_beacon-chain.md | 253 +++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 97 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 569635879..3d473f669 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -41,8 +41,8 @@ - [`Transfer`](#transfer) - [Beacon chain blocks](#beacon-chain-blocks) - [`BeaconBlock`](#beaconblock) + - [`BeaconBlockHeader`](#beaconblockheader) - [`BeaconBlockBody`](#beaconblockbody) - - [`Proposal`](#proposal) - [Beacon chain state](#beacon-chain-state) - [`BeaconState`](#beaconstate) - [`Validator`](#validator) @@ -56,6 +56,7 @@ - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`signed_root`](#signed_root) + - [`get_temporary_block_header`](#get_temporary_block_header) - [`slot_to_epoch`](#slot_to_epoch) - [`get_previous_epoch`](#get_previous_epoch) - [`get_current_epoch`](#get_current_epoch) @@ -115,8 +116,7 @@ - [Slot](#slot) - [Block roots](#block-roots) - [Per-block processing](#per-block-processing) - - [Slot](#slot-1) - - [Block signature](#block-signature) + - [Block header](#block-header) - [RANDAO](#randao) - [Eth1 data](#eth1-data) - [Transactions](#transactions) @@ -185,7 +185,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | -| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | | `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | | `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | | `SHUFFLE_ROUND_COUNT` | 90 | @@ -233,13 +232,13 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | +| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | ### State list lengths | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | ~13 hours | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `LATEST_ACTIVE_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `LATEST_SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | @@ -273,12 +272,12 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | | - | - | -| `DOMAIN_DEPOSIT` | `0` | -| `DOMAIN_ATTESTATION` | `1` | -| `DOMAIN_PROPOSAL` | `2` | -| `DOMAIN_EXIT` | `3` | -| `DOMAIN_RANDAO` | `4` | -| `DOMAIN_TRANSFER` | `5` | +| `DOMAIN_BEACON_BLOCK` | `0` | +| `DOMAIN_RANDAO` | `1` | +| `DOMAIN_ATTESTATION` | `2` | +| `DOMAIN_DEPOSIT` | `3` | +| `DOMAIN_VOLUNTARY_EXIT` | `4` | +| `DOMAIN_TRANSFER` | `5` | ## Data structures @@ -294,10 +293,10 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git { # Proposer index 'proposer_index': 'uint64', - # First proposal - 'proposal_1': Proposal, - # Second proposal - 'proposal_2': Proposal, + # First block header + 'header_1': BeaconBlockHeader, + # Second block header + 'header_2': BeaconBlockHeader, } ``` @@ -467,14 +466,21 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git { # Header 'slot': 'uint64', - 'parent_root': 'bytes32', + 'previous_block_root': 'bytes32', 'state_root': 'bytes32', - 'randao_reveal': 'bytes96', - 'eth1_data': Eth1Data, - - # Body 'body': BeaconBlockBody, - # Signature + 'signature': 'bytes96', +} +``` + +#### `BeaconBlockHeader` + +```python +{ + 'slot': 'uint64', + 'previous_block_root': 'bytes32', + 'state_root': 'bytes32', + 'block_body_root': 'bytes32', 'signature': 'bytes96', } ``` @@ -483,6 +489,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { + 'randao_reveal': 'bytes96', + 'eth1_data': Eth1Data, 'proposer_slashings': [ProposerSlashing], 'attester_slashings': [AttesterSlashing], 'attestations': [Attestation], @@ -492,21 +500,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` -#### `Proposal` - -```python -{ - # Slot number - 'slot': 'uint64', - # Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain) - 'shard': 'uint64', - # Block root - 'block_root': 'bytes32', - # Signature - 'signature': 'bytes96', -} -``` - ### Beacon chain state #### `BeaconState` @@ -543,9 +536,11 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Recent state 'latest_crosslinks': [Crosslink], 'latest_block_roots': ['bytes32'], + 'latest_state_roots': ['bytes32'], 'latest_active_index_roots': ['bytes32'], 'latest_slashed_balances': ['uint64'], # Balances slashed at every withdrawal period - 'batched_block_roots': ['bytes32'], + 'latest_block_header': BeaconBlockHeader, # `latest_block_header.state_root == ZERO_HASH` temporarily + 'historical_roots': ['bytes32'], # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, @@ -669,6 +664,22 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere `def signed_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#signed-roots) to compute signed messages. +### `get_temporary_block_header` + +```python +def get_temporary_block_header(block: BeaconBlock) -> BeaconBlockHeader: + """ + Return the block header corresponding to a block with ``state_root`` set to ``ZERO_HASH``. + """ + return BeaconBlockHeader( + slot=block.slot, + previous_block_root=block.previous_block_root, + state_root=ZERO_HASH, + block_body_root=hash_tree_root(block.body), + signature=block.signature, + ) +``` + ### `slot_to_epoch` ```python @@ -920,9 +931,8 @@ def get_block_root(state: BeaconState, """ Return the block root at a recent ``slot``. """ - assert state.slot <= slot + LATEST_BLOCK_ROOTS_LENGTH - assert slot < state.slot - return state.latest_block_roots[slot % LATEST_BLOCK_ROOTS_LENGTH] + assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT + return state.latest_block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] ``` `get_block_root(_, s)` should always return `hash_tree_root` of the block in the beacon chain at slot `s`, and `get_crosslink_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes. @@ -1405,35 +1415,47 @@ For convenience, we provide the interface to the contract here: ## On genesis -A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. +When enough full deposits have been made to the deposit contract, an `Eth2Genesis` log is emitted. Construct a corresponding `genesis_state` and `genesis_block` as follows: + +* Let `genesis_validator_deposits` be the list of deposits, ordered chronologically, up to and including the deposit that triggered the `Eth2Genesis` log. +* Let `genesis_time` be the timestamp specified in the `Eth2Genesis` log. +* Let `genesis_eth1_data` be the `Eth1Data` object where: + * `genesis_eth1_data.deposit_root` is the `deposit_root` contained in the `Eth2Genesis` log. + * `genesis_eth1_data.block_hash` is the hash of the Ethereum 1.0 block that emitted the `Eth2Genesis` log. +* Let `genesis_state = get_genesis_beacon_state(genesis_validator_deposits, genesis_time, genesis_eth1_data)`. +* Let `genesis_block = get_empty_block()`. +* Set `genesis_block.state_root = hash_tree_root(genesis_state)`. ```python -{ - slot=GENESIS_SLOT, - parent_root=ZERO_HASH, - state_root=GENESIS_STATE_ROOT, - randao_reveal=EMPTY_SIGNATURE, - eth1_data=Eth1Data( - deposit_root=ZERO_HASH, - block_hash=ZERO_HASH - ), - signature=EMPTY_SIGNATURE, - body=BeaconBlockBody( - proposer_slashings=[], - attester_slashings=[], - attestations=[], - deposits=[], - exits=[], - ), -} +def get_empty_block() -> BeaconBlock: + """ + Get an empty ``BeaconBlock``. + """ + return BeaconBlock( + slot=GENESIS_SLOT, + previous_block_root=ZERO_HASH, + state_root=ZERO_HASH, + body=BeaconBlockBody( + randao_reveal=EMPTY_SIGNATURE, + eth1_data=Eth1Data( + deposit_root=ZERO_HASH, + block_hash=ZERO_HASH, + ), + proposer_slashings=[], + attester_slashings=[], + attestations=[], + deposits=[], + exits=[], + transfers=[], + ), + signature=EMPTY_SIGNATURE, + ) ``` -`GENESIS_STATE_ROOT` (in the above "genesis block") is generated from the `get_genesis_beacon_state` function below. When enough full deposits have been made to the deposit contract and the `Eth2Genesis` log has been emitted, `get_genesis_beacon_state` will execute to compute the `hash_tree_root` of `BeaconState`. - ```python def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], genesis_time: int, - latest_eth1_data: Eth1Data) -> BeaconState: + genesis_eth1_data: Eth1Data) -> BeaconState: """ Get the genesis ``BeaconState``. """ @@ -1471,13 +1493,15 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Recent state latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)], - latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], + latest_block_roots=[ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)], + latest_state_roots=[ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)], latest_active_index_roots=[ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)], latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], - batched_block_roots=[], + latest_block_header=get_temporary_block_header(get_empty_block()), + historical_roots=[], # Ethereum 1.0 chain data - latest_eth1_data=latest_eth1_data, + latest_eth1_data=genesis_eth1_data, eth1_data_votes=[], deposit_index=len(genesis_validator_deposits) ) @@ -1511,7 +1535,7 @@ Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Cli For a beacon chain block, `block`, to be processed by a node, the following conditions must be met: -* The parent block with root `block.parent_root` has been processed and accepted. +* The parent block with root `block.previous_block_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. * The node's Unix time is greater than or equal to `state.genesis_time + (block.slot - GENESIS_SLOT) * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.) @@ -1590,41 +1614,73 @@ _Note_: If there are skipped slots between a block and its parent block, run the ### Per-slot processing -Below are the processing steps that happen at every slot. +At every `slot > GENESIS_SLOT` run the following function: -#### Slot - -* Set `state.slot += 1`. - -#### Block roots - -* Let `previous_block_root` be the `hash_tree_root` of the previous beacon block processed in the chain. -* Set `state.latest_block_roots[(state.slot - 1) % LATEST_BLOCK_ROOTS_LENGTH] = previous_block_root`. -* If `state.slot % LATEST_BLOCK_ROOTS_LENGTH == 0` append `merkle_root(state.latest_block_roots)` to `state.batched_block_roots`. +```python +def advance_slot(state: BeaconState) -> None: + state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state) + state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = get_block_root(state, state.slot - 1) + if state.latest_block_header.state_root == ZERO_HASH: + state.latest_block_header.state_root = get_state_root(state, state.slot) + state.slot += 1 +``` ### Per-block processing -Below are the processing steps that happen at every `block`. +For every `block` except the genesis block, run `process_block_header(state, block)`, `process_randao(state, block)` and `process_eth1_data(state, block)`. -#### Slot +#### Block header -* Verify that `block.slot == state.slot`. - -#### Block signature - -* Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. -* Let `proposal = Proposal(block.slot, BEACON_CHAIN_SHARD_NUMBER, signed_root(block, "signature"), block.signature)`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposal, "signature"), signature=proposal.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. +```python +def process_block_header(state: BeaconState, block: BeaconBlock) -> None: + # Verify that the slots match + assert block.slot == state.slot + # Verify that the parent matches + assert block.previous_block_root == hash_tree_root(state.latest_block_header) + # Save previous block root + state.latest_block_roots[(state.slot - 1) % SLOTS_PER_HISTORICAL_ROOT] = block.previous_block_root + # Save current block as the new latest block + state.latest_block_header = get_temporary_block_header(block) + # Verify proposer signature + proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)] + assert bls_verify( + pubkey=proposer.pubkey, + message_hash=signed_root(block, "signature"), + signature=block.signature, + domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_BEACON_BLOCK) + ) +``` #### RANDAO -* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. -* Set `state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = xor(get_randao_mix(state, get_current_epoch(state)), hash(block.randao_reveal))`. +```python +def process_randao(state: BeaconState, block: BeaconBlock) -> None: + # Verify that the provided randao value is valid + assert bls_verify( + pubkey=proposer.pubkey, + message_hash=hash_tree_root(get_current_epoch(state)), + signature=block.body.randao_reveal, + domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO) + ) + # Mix it in + state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = ( + xor(get_randao_mix(state, get_current_epoch(state)), + hash(block.body.randao_reveal)) + ) +``` #### Eth1 data -* If there exists an `eth1_data_vote` in `state.eth1_data_votes` for which `eth1_data_vote.eth1_data == block.eth1_data` (there will be at most one), set `eth1_data_vote.vote_count += 1`. -* Otherwise, append to `state.eth1_data_votes` a new `Eth1DataVote(eth1_data=block.eth1_data, vote_count=1)`. +```python +def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None: + for eth1_data_vote in state.eth1_data_votes: + # If someone else has already voted for the same hash, add to its counter + if eth1_data_vote.eth1_data == block.body.eth1_data: + eth1_data_vote.vote_count += 1 + return + # If we're seeing this hash for the first time, make a new counter + state.eth1_data_votes.append(Eth1DataVote(eth1_data=block.body.eth1_data, vote_count=1)) +``` #### Transactions @@ -1643,20 +1699,20 @@ def process_proposer_slashing(state: BeaconState, """ proposer = state.validator_registry[proposer_slashing.proposer_index] # Verify that the slot is the same - assert proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot + assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot # Verify that the shard is the same (or that both proposals are beacon chain proposals) - assert proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard + assert proposer_slashing.header_1.shard == proposer_slashing.header_2.shard # But the roots are different! - assert proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root + assert proposer_slashing.header_1.block_root != proposer_slashing.header_2.block_root # Proposer is not yet slashed assert proposer.slashed is False # Signatures are valid - for proposal in (proposer_slashing.proposal_1, proposer_slashing.proposal_2): + for header in (proposer_slashing.header_1, proposer_slashing.header_2): assert bls_verify( pubkey=proposer.pubkey, - message_hash=signed_root(proposal, "signature"), - signature=proposal.signature, - domain=get_domain(state.fork, slot_to_epoch(proposal.slot), DOMAIN_PROPOSAL) + message_hash=signed_root(header, "signature"), + signature=header.signature, + domain=get_domain(state.fork, slot_to_epoch(header.slot), DOMAIN_PROPOSAL) ) slash_validator(state, proposer_slashing.proposer_index) ``` @@ -1836,7 +1892,7 @@ def process_exit(state: BeaconState, exit: VoluntaryExit) -> None: pubkey=validator.pubkey, message_hash=signed_root(exit, "signature"), signature=exit.signature, - domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT) + domain=get_domain(state.fork, exit.epoch, DOMAIN_VOLUNTARY_EXIT) ) # Run the exit initiate_validator_exit(state, exit.validator_index) @@ -2368,6 +2424,9 @@ def finish_epoch_update(state: BeaconState) -> None: ) # Set randao mix state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) + # Set historical root accumulator + if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: + state.historical_roots.append(merkle_root(state.latest_block_roots + state.latest_state_roots)) # Rotate current/previous epoch attestations state.previous_epoch_attestations = state.current_epoch_attestations state.current_epoch_attestations = [] From 73f9ad0512c8dfd06a11dcb3078be092f9fe8bb4 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 05:54:58 -0600 Subject: [PATCH 22/53] Added missing colon --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3d473f669..89e7584b6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -800,7 +800,7 @@ def get_epoch_committee_count(active_validator_count: int) -> int: ```python def get_shuffling(seed: Bytes32, validators: List[Validator], - epoch: Epoch) -> List[List[ValidatorIndex]] + epoch: Epoch) -> List[List[ValidatorIndex]]: """ Shuffle active validators and split into crosslink committees. Return a list of committees (each a list of validator indices). From 68bc91bb3a7fab36b11ea8d56f356c8197687449 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 05:56:57 -0600 Subject: [PATCH 23/53] Fixed custody bit participants typo --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 89e7584b6..a4bb8908e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1807,7 +1807,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # Verify aggregate signature participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield) custody_bit_1_participants = get_attestation_participants(state, attestation.data, attestation.custody_bitfield) - custody_bit_0_participants = [i in participants for i not in custody_bit_1_participants] + custody_bit_0_participants = [i for i in participants if i not in custody_bit_1_participants] assert bls_verify_multiple( pubkeys=[ From 805ea5dad51e68f640a2cf7869c1d80b1f7ecacd Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 05:59:11 -0600 Subject: [PATCH 24/53] Transfer from -> sender Needed to avoid triggering errors in python --- specs/core/0_beacon-chain.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a4bb8908e..6e4e74b63 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -442,7 +442,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { # Sender index - 'from': 'uint64', + 'sender': 'uint64', # Recipient index 'to': 'uint64', # Amount in Gwei @@ -1913,23 +1913,23 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: Note that this function mutates ``state``. """ # Verify the amount and fee aren't individually too big (for anti-overflow purposes) - assert state.validator_balances[transfer.from] >= max(transfer.amount, transfer.fee) + assert state.validator_balances[transfer.sender] >= max(transfer.amount, transfer.fee) # Verify that we have enough ETH to send, and that after the transfer the balance will be either # exactly zero or at least MIN_DEPOSIT_AMOUNT assert ( - state.validator_balances[transfer.from] == transfer.amount + transfer.fee or - state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT + state.validator_balances[transfer.sender] == transfer.amount + transfer.fee or + state.validator_balances[transfer.sender] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT ) # A transfer is valid in only one slot assert state.slot == transfer.slot # Only withdrawn or not-yet-deposited accounts can transfer assert ( - get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch or - state.validator_registry[transfer.from].activation_epoch == FAR_FUTURE_EPOCH + get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or + state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH ) # Verify that the pubkey is valid assert ( - state.validator_registry[transfer.from].withdrawal_credentials == + state.validator_registry[transfer.sender].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:] ) # Verify that the signature is valid @@ -1940,7 +1940,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER) ) # Process the transfer - state.validator_balances[transfer.from] -= transfer.amount + transfer.fee + state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee state.validator_balances[transfer.to] += transfer.amount state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee ``` From 38e166d4a3233449542bf421d40a67e3ef947355 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 06:01:03 -0600 Subject: [PATCH 25/53] More python typos! --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6e4e74b63..369c74a20 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2268,7 +2268,7 @@ def should_update_validator_registry(state: BeaconState) -> bool: if state.finalized_epoch <= state.validator_registry_update_epoch: return False # Must have processed new crosslinks on all shards of the current epoch - shards_to_check [ + shards_to_check = [ (state.current_shuffling_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state)) ] @@ -2340,7 +2340,7 @@ def update_registry_and_shuffling_data(state: BeaconState) -> None: state.current_shuffling_epoch = next_epoch state.current_shuffling_start_shard = ( state.current_shuffling_start_shard + - get_current_epoch_committee_count(state)) % SHARD_COUNT + get_current_epoch_committee_count(state) % SHARD_COUNT ) state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) else: From 1444687798bec9c01b1f88400479a501bacb8ef7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 3 Mar 2019 20:14:03 -0600 Subject: [PATCH 26/53] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 88a8f12eb..6ef5e449f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -277,7 +277,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `DOMAIN_ATTESTATION` | `2` | | `DOMAIN_DEPOSIT` | `3` | | `DOMAIN_VOLUNTARY_EXIT` | `4` | -| `DOMAIN_TRANSFER` | `5` | +| `DOMAIN_TRANSFER` | `5` | ## Data structures From 044c87fe3988daa7c7e2770903897720ad95cef8 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Mar 2019 22:41:23 -0600 Subject: [PATCH 27/53] fix State -> BeaconState --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6ef5e449f..c82e2aefd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1055,7 +1055,7 @@ def bytes_to_int(data: bytes) -> int: ### `get_effective_balance` ```python -def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: +def get_effective_balance(state: BeaconState, index: ValidatorIndex) -> Gwei: """ Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. """ From 5282b289cf6eed3ac8cf89e50d600ef1d2bded84 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 4 Mar 2019 04:49:24 -0600 Subject: [PATCH 28/53] exits -> voluntary_exits --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c82e2aefd..e0b7ae61f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1448,7 +1448,7 @@ def get_empty_block() -> BeaconBlock: attester_slashings=[], attestations=[], deposits=[], - exits=[], + voluntary_exits=[], transfers=[], ), signature=EMPTY_SIGNATURE, From 908d7c42ad3ea9ce7c912fd7d0051bc2264e7987 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 09:17:43 -0700 Subject: [PATCH 29/53] pr feedback --- specs/core/0_beacon-chain.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e0b7ae61f..19b729f30 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1658,6 +1658,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: ```python def process_randao(state: BeaconState, block: BeaconBlock) -> None: + proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)] # Verify that the provided randao value is valid assert bls_verify( pubkey=proposer.pubkey, @@ -1968,7 +1969,7 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: ```python def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: - output = set({}) + output = set() for a in attestations: output = output.union([get_attestation_participants(state, a.data, a.aggregation_bitfield)]) return sorted(list(output)) @@ -2087,6 +2088,9 @@ Run the following function: ```python def process_crosslinks(state: BeaconState) -> None: + current_epoch = get_current_epoch(state) + previous_epoch = current_epoch - 1 + next_epoch = current_epoch + 1 for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) @@ -2124,6 +2128,7 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: ```python def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex) -> Gwei: + epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch return ( get_base_reward(state, index) + get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2 From 831f04e3ea03c619f790794b7f236aba4aea531c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 09:45:55 -0700 Subject: [PATCH 30/53] separate our rewards/penalty deltas --- specs/core/0_beacon-chain.md | 67 ++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 19b729f30..4e8f9a25e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2140,7 +2140,7 @@ Note: When applying penalties in the following balance recalculations implemente ##### Justification and finalization ```python -def get_justification_and_finalization_deltas(state: BeaconState) -> List[Gwei]: +def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch if epochs_since_finality <= 4: return compute_normal_justification_and_finalization_deltas(state) @@ -2151,8 +2151,13 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> List[Gwei]: When blocks are finalizing normally... ```python -def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> List[Gwei]: - deltas = [0 for index in range(len(state.validator_registry))] +def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: + # deltas[0] for rewards + # deltas[1] for penalties + deltas = [ + [0 for index in range(len(state.validator_registry))], + [0 for index in range(len(state.validator_registry))] + ] # Some helper variables boundary_attestations = get_previous_epoch_boundary_attestations(state) boundary_attesting_balance = get_attesting_balance(boundary_attestations) @@ -2164,57 +2169,61 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> for index in get_active_validator_indices(state.validator_registry, previous_epoch): # Expected FFG source if index in get_attesting_indices(state.previous_epoch_attestations): - deltas[index] += get_base_reward(state, index) * total_attesting_balance // total_balance + deltas[0][index] += get_base_reward(state, index) * total_attesting_balance // total_balance # Inclusion speed bonus - deltas[index] += ( + deltas[0][index] += ( get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index) ) else: - deltas[index] -= get_base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) # Expected FFG target if index in get_attesting_indices(boundary_attestations): - deltas[index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance + deltas[0][index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance else: - deltas[index] -= get_base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) # Expected head if index in get_attesting_indices(matching_head_attestations): - deltas[index] += get_base_reward(state, index) * matching_head_balance // total_balance + deltas[0][index] += get_base_reward(state, index) * matching_head_balance // total_balance else: - deltas[index] -= get_base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) # Proposer bonus proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) - deltas[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT + deltas[0][proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT ``` When blocks are not finalizing normally... ```python -def compute_inactivity_leak_deltas(state: BeaconState) -> List[Gwei]: - deltas = [0 for index in range(len(state.validator_registry))] +def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: + # deltas[0] for rewards + # deltas[1] for penalties + deltas = [ + [0 for index in range(len(state.validator_registry))], + [0 for index in range(len(state.validator_registry))] + ] boundary_attestations = get_previous_epoch_boundary_attestations(state) matching_head_attestations = get_previous_epoch_matching_head_attestations(state) active_validator_indices = get_active_validator_indices(state.validator_registry, get_previous_epoch(state)) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch for index in active_validator_indices: if index not in get_attesting_indices(get_previous_epoch_attestations(state)): - deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) else: # If a validator did attest, apply a small penalty for getting attestations included late - deltas[index] += ( + deltas[0][index] += ( base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index) ) - deltas[index] -= base_reward(state, index) - + deltas[1][index] += base_reward(state, index) if index not in get_attesting_indices(boundary_attestations): - deltas[index] -= get_inactivity_penalty(state, index, epochs_since_finality) + deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) if index not in get_attesting_indices(matching_head_attestations): - deltas[index] -= get_base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) # Penalize slashed-but-inactive validators as though they were active but offline for index in range(len(state.validator_registry)): if index not in active_validator_indices and state.validator_registry[index].slashed: - deltas[index] -= ( + deltas[1][index] += ( 2 * get_inactivity_penalty(state, index, epochs_since_finality) + get_base_reward(state, index) ) @@ -2224,8 +2233,13 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> List[Gwei]: ##### Crosslinks ```python -def get_crosslink_deltas(state: BeaconState) -> List[Gwei]: - deltas = [0 for index in range(len(state.validator_registry))] +def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: + # deltas[0] for rewards + # deltas[1] for penalties + deltas = [ + [0 for index in range(len(state.validator_registry))], + [0 for index in range(len(state.validator_registry))] + ] previous_epoch_start_slot = get_epoch_start_slot(get_previous_epoch(state)) current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) for slot in range(previous_epoch_start_slot, current_epoch_start_slot): @@ -2235,9 +2249,9 @@ def get_crosslink_deltas(state: BeaconState) -> List[Gwei]: total_balance = get_total_balance(state, committee) for index in crosslink_committee: if index in participants: - deltas[index] += get_base_reward(state, index) * participating_balance // total_balance + deltas[0][index] += get_base_reward(state, index) * participating_balance // total_balance else: - deltas[index] -= get_base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) return deltas ``` @@ -2250,7 +2264,10 @@ def apply_rewards(state: BeaconState) -> None: deltas1 = get_justification_and_finalization_deltas(state) deltas2 = get_crosslink_deltas(state) for i in range(len(state.validator_registry)): - state.validator_balances[i] = max(0, state.validator_balances[i] + deltas1[i] + deltas2[i]) + state.validator_balances[i] = max( + 0, + state.validator_balances[i] + deltas1[0][i] + deltas2[0][i] - deltas1[1][i] - deltas2[1][i] + ) ``` #### Ejections From a561656b68923cc1ae4d2e751eeeca119a13130d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 11:45:41 -0700 Subject: [PATCH 31/53] add get_state_root helper --- specs/core/0_beacon-chain.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0d330c32b..a0cbb063f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -72,6 +72,7 @@ - [`get_next_epoch_committee_count`](#get_next_epoch_committee_count) - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) - [`get_block_root`](#get_block_root) + - [`get_state_root`](#get_state_root) - [`get_randao_mix`](#get_randao_mix) - [`get_active_index_root`](#get_active_index_root) - [`generate_seed`](#generate_seed) @@ -941,6 +942,17 @@ def get_block_root(state: BeaconState, `get_block_root(_, s)` should always return `hash_tree_root` of the block in the beacon chain at slot `s`, and `get_crosslink_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes. +### `get_state_root` + +```python +def get_state_root(state: BeaconState, + slot: Slot) -> Bytes32: + """ + Return the state root at a recent ``slot``. + """ + assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT + return state.latest_state_roots[slot % SLOTS_PER_HISTORICAL_ROOT] +``` ### `get_randao_mix` ```python @@ -1674,9 +1686,9 @@ At every `slot > GENESIS_SLOT` run the following function: def advance_slot(state: BeaconState) -> None: state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state) state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = get_block_root(state, state.slot - 1) - if state.latest_block_header.state_root == ZERO_HASH: - state.latest_block_header.state_root = get_state_root(state, state.slot) state.slot += 1 + if state.latest_block_header.state_root == ZERO_HASH: + state.latest_block_header.state_root = get_state_root(state, state.slot - 1) ``` ### Per-block processing From f7397a5970819b2aaaf122a61901da499c65e582 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 15:49:21 -0700 Subject: [PATCH 32/53] add xor def --- specs/core/0_beacon-chain.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a0cbb063f..5f50b67ca 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -53,6 +53,7 @@ - [`Eth1DataVote`](#eth1datavote) - [Custom Types](#custom-types) - [Helper functions](#helper-functions) + - [`xor`](#xor) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`signed_root`](#signed_root) @@ -652,6 +653,13 @@ We define the following Python custom types for type hinting and readability: Note: The definitions below are for specification purposes and are not necessarily optimal implementations. +### `xor` + +```python +def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32: + return bytes(a ^ b for a, b in zip(bytes1, bytes2)) +``` + ### `hash` The hash function is denoted by `hash`. In Phase 0 the beacon chain is deployed with the same hash function as Ethereum 1.0, i.e. Keccak-256 (also incorrectly known as SHA3). From 8802f57790df8d2fe6e6a149d0cc75c763a76665 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 17:11:38 -0700 Subject: [PATCH 33/53] add missing var to get_attesting_balance calls --- specs/core/0_beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 5f50b67ca..d3ca1c96c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2094,12 +2094,12 @@ def update_justification_and_finalization(state: BeaconState) -> None: # Rotate the justification bitfield up one epoch to make room for the current epoch state.justification_bitfield <<= 1 # If the previous epoch gets justified, fill the second last bit - previous_boundary_attesting_balance = get_attesting_balance(get_previous_epoch_boundary_attestations(state)) + previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state)) if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2: new_justified_epoch = get_current_epoch(state) - 1 state.justification_bitfield |= 2 # If the current epoch gets justified, fill the last bit - current_boundary_attesting_balance = get_attesting_balance(get_current_epoch_boundary_attestations(state)) + current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state)) if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2: new_justified_epoch = get_current_epoch(state) state.justification_bitfield |= 1 @@ -2203,11 +2203,11 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> ] # Some helper variables boundary_attestations = get_previous_epoch_boundary_attestations(state) - boundary_attesting_balance = get_attesting_balance(boundary_attestations) + boundary_attesting_balance = get_attesting_balance(state, boundary_attestations) total_balance = get_previous_total_balance(state) - total_attesting_balance = get_attesting_balance(get_previous_epoch_attestations(state)) + total_attesting_balance = get_attesting_balance(state, get_previous_epoch_attestations(state)) matching_head_attestations = get_previous_epoch_matching_head_attestations(state) - matching_head_balance = get_attesting_balance(matching_head_attestations) + matching_head_balance = get_attesting_balance(state, matching_head_attestations) # Process rewards or penalties for all validators for index in get_active_validator_indices(state.validator_registry, previous_epoch): # Expected FFG source From d4a4c735996f2903fccb35ab9be0fbb2a28c216f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 17:17:31 -0700 Subject: [PATCH 34/53] fix calls to get_attesting_indices --- specs/core/0_beacon-chain.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d3ca1c96c..8e9cfb6fb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2020,7 +2020,7 @@ def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestat ```python def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: - return get_total_balance(state, get_attesting_indices(attestations, state)) + return get_total_balance(state, get_attesting_indices(state, attestations)) ``` ```python @@ -2062,7 +2062,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple winning_root = max(all_roots, key=lambda r: get_attesting_balance(state, get_attestations_for(r))) - return winning_root, get_attesting_indices(get_attestations_for(winning_root)) + return winning_root, get_attesting_indices(state, get_attestations_for(winning_root)) ``` ```python @@ -2211,7 +2211,7 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> # Process rewards or penalties for all validators for index in get_active_validator_indices(state.validator_registry, previous_epoch): # Expected FFG source - if index in get_attesting_indices(state.previous_epoch_attestations): + if index in get_attesting_indices(state, state.previous_epoch_attestations): deltas[0][index] += get_base_reward(state, index) * total_attesting_balance // total_balance # Inclusion speed bonus deltas[0][index] += ( @@ -2221,12 +2221,12 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> else: deltas[1][index] += get_base_reward(state, index) # Expected FFG target - if index in get_attesting_indices(boundary_attestations): + if index in get_attesting_indices(state, boundary_attestations): deltas[0][index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance else: deltas[1][index] += get_base_reward(state, index) # Expected head - if index in get_attesting_indices(matching_head_attestations): + if index in get_attesting_indices(state, matching_head_attestations): deltas[0][index] += get_base_reward(state, index) * matching_head_balance // total_balance else: deltas[1][index] += get_base_reward(state, index) @@ -2250,7 +2250,7 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List active_validator_indices = get_active_validator_indices(state.validator_registry, get_previous_epoch(state)) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch for index in active_validator_indices: - if index not in get_attesting_indices(get_previous_epoch_attestations(state)): + if index not in get_attesting_indices(state, get_previous_epoch_attestations(state)): deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) else: # If a validator did attest, apply a small penalty for getting attestations included late @@ -2259,9 +2259,9 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List inclusion_distance(state, index) ) deltas[1][index] += base_reward(state, index) - if index not in get_attesting_indices(boundary_attestations): + if index not in get_attesting_indices(state, boundary_attestations): deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) - if index not in get_attesting_indices(matching_head_attestations): + if index not in get_attesting_indices(state, matching_head_attestations): deltas[1][index] += get_base_reward(state, index) # Penalize slashed-but-inactive validators as though they were active but offline for index in range(len(state.validator_registry)): From 66105b01e4169509af2688aab755190f05cd7208 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 17:27:53 -0700 Subject: [PATCH 35/53] previous epoch uses -1 in all cases --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8e9cfb6fb..4525c609b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -707,7 +707,7 @@ def get_previous_epoch(state: BeaconState) -> Epoch: """` Return the previous epoch of the given ``state``. """ - return max(get_current_epoch(state) - 1, GENESIS_EPOCH) + return get_current_epoch(state) - 1 ``` ### `get_current_epoch` From 47cca60148dbf74af01ef491e7b58fbac0241b75 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 17:56:50 -0700 Subject: [PATCH 36/53] handle when no attestations availble for crosslinking (#717) --- specs/core/0_beacon-chain.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4525c609b..4c608c0b3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2056,12 +2056,16 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] ] all_roots = [a.data.crosslink_data_root for a in valid_attestations] - + + # handle when no attestations for shard available + if len(all_roots) == 0: + return ZERO_HASH, [] + def get_attestations_for(root) -> List[PendingAttestation]: return [a for a in valid_attestations if a.data.crosslink_data_root == root] - + winning_root = max(all_roots, key=lambda r: get_attesting_balance(state, get_attestations_for(r))) - + return winning_root, get_attesting_indices(state, get_attestations_for(winning_root)) ``` From 738acf0e05cb85b2aa4f43c2d402d05ac0ef449f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 18:00:38 -0700 Subject: [PATCH 37/53] fix calls for get_total_balance --- specs/core/0_beacon-chain.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4525c609b..e69fcf458 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2137,7 +2137,9 @@ def process_crosslinks(state: BeaconState) -> None: for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) - if 3 * total_balance(participants) >= 2 * total_balance(crosslink_committee): + participating_balance = get_total_balance(state, participants) + total_balance = get_total_balance(state, crosslink_committee) + if 3 * participating_balance >= 2 * total_balance: state.latest_crosslinks[shard] = Crosslink( epoch=slot_to_epoch(slot), crosslink_data_root=winning_root @@ -2434,7 +2436,7 @@ def process_slashings(state: BeaconState) -> None: """ current_epoch = get_current_epoch(state) active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) - total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) + total_balance = get_total_balance(state, active_validator_indices) # Compute `total_penalties` epoch_index = current_epoch % LATEST_SLASHED_EXIT_LENGTH From 0fdfbc473e15fbc318ce2e9222f2982ea6db9211 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 18:53:31 -0700 Subject: [PATCH 38/53] minor variable issues in helpers --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8c28adbf6..0e836df1f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2211,11 +2211,11 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> boundary_attestations = get_previous_epoch_boundary_attestations(state) boundary_attesting_balance = get_attesting_balance(state, boundary_attestations) total_balance = get_previous_total_balance(state) - total_attesting_balance = get_attesting_balance(state, get_previous_epoch_attestations(state)) + total_attesting_balance = get_attesting_balance(state, state.previous_epoch_attestations) matching_head_attestations = get_previous_epoch_matching_head_attestations(state) matching_head_balance = get_attesting_balance(state, matching_head_attestations) # Process rewards or penalties for all validators - for index in get_active_validator_indices(state.validator_registry, previous_epoch): + for index in get_active_validator_indices(state.validator_registry, get_previous_epoch(state)): # Expected FFG source if index in get_attesting_indices(state, state.previous_epoch_attestations): deltas[0][index] += get_base_reward(state, index) * total_attesting_balance // total_balance @@ -2256,7 +2256,7 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List active_validator_indices = get_active_validator_indices(state.validator_registry, get_previous_epoch(state)) epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch for index in active_validator_indices: - if index not in get_attesting_indices(state, get_previous_epoch_attestations(state)): + if index not in get_attesting_indices(state, state.previous_epoch_attestations): deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) else: # If a validator did attest, apply a small penalty for getting attestations included late @@ -2295,7 +2295,7 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) participating_balance = get_total_balance(state, participants) - total_balance = get_total_balance(state, committee) + total_balance = get_total_balance(state, crossling_committee) for index in crosslink_committee: if index in participants: deltas[0][index] += get_base_reward(state, index) * participating_balance // total_balance From 02e8e8974049f1965df3f7b0b9b874a46a26c150 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 19:05:00 -0700 Subject: [PATCH 39/53] handle get_base_reward when no previous balance --- specs/core/0_beacon-chain.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0e836df1f..13eae0432 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2171,6 +2171,9 @@ First, we define some additional helpers: ```python def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: + if get_previous_total_balance(state) == 0: + return 0 + adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT return get_effective_balance(state, index) // adjusted_quotient // 5 ``` From bd936fa6abc11454b769e7053c9052477d7c1dc8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 19:07:24 -0700 Subject: [PATCH 40/53] add missing return for justification/finalization reward deltas --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0e836df1f..b44200226 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2239,6 +2239,7 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> # Proposer bonus proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) deltas[0][proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT + return deltas ``` When blocks are not finalizing normally... From d2f7fa9a7eec82b7210cf7b935f39aa003c78d81 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 4 Mar 2019 19:09:31 -0700 Subject: [PATCH 41/53] fix call to get_current_epoch --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b44200226..151fa6273 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2330,7 +2330,7 @@ def process_ejections(state: BeaconState) -> None: Iterate through the validator registry and eject active validators with balance below ``EJECTION_BALANCE``. """ - for index in get_active_validator_indices(state.validator_registry, current_epoch(state)): + for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)): if state.validator_balances[index] < EJECTION_BALANCE: exit_validator(state, index) ``` From ecb76420ecd124c39849aef478d71165633acc68 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 5 Mar 2019 17:30:42 +0800 Subject: [PATCH 42/53] `1e9` is float in Python, change it to `10**9`. --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 151fa6273..81a757db2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -205,10 +205,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | Unit | | - | - | :-: | -| `MIN_DEPOSIT_AMOUNT` | `2**0 * 1e9` (= 1,000,000,000) | Gwei | -| `MAX_DEPOSIT_AMOUNT` | `2**5 * 1e9` (= 32,000,000,000) | Gwei | -| `FORK_CHOICE_BALANCE_INCREMENT` | `2**0 * 1e9` (= 1,000,000,000) | Gwei | -| `EJECTION_BALANCE` | `2**4 * 1e9` (= 16,000,000,000) | Gwei | +| `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei | +| `MAX_DEPOSIT_AMOUNT` | `2**5 * 10**9` (= 32,000,000,000) | Gwei | +| `FORK_CHOICE_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei | +| `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei | ### Initial values From e2a045210827a80e57e1cc2815bb183c48d3950b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 5 Mar 2019 06:07:00 -0700 Subject: [PATCH 43/53] fix previous root (#716) --- specs/core/0_beacon-chain.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 81a757db2..ff1ec1ea1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1693,10 +1693,10 @@ At every `slot > GENESIS_SLOT` run the following function: ```python def advance_slot(state: BeaconState) -> None: state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state) - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = get_block_root(state, state.slot - 1) - state.slot += 1 if state.latest_block_header.state_root == ZERO_HASH: - state.latest_block_header.state_root = get_state_root(state, state.slot - 1) + state.latest_block_header.state_root = get_state_root(state, state.slot) + state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state.latest_block_header) + state.slot += 1 ``` ### Per-block processing @@ -1711,8 +1711,6 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: assert block.slot == state.slot # Verify that the parent matches assert block.previous_block_root == hash_tree_root(state.latest_block_header) - # Save previous block root - state.latest_block_roots[(state.slot - 1) % SLOTS_PER_HISTORICAL_ROOT] = block.previous_block_root # Save current block as the new latest block state.latest_block_header = get_temporary_block_header(block) # Verify proposer signature From 99da6fe14148fb21ed55ef2eaa0ec33256ed5809 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 5 Mar 2019 08:50:51 -0700 Subject: [PATCH 44/53] fix advance_slot ordering issue --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ff1ec1ea1..36fbad847 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1693,10 +1693,10 @@ At every `slot > GENESIS_SLOT` run the following function: ```python def advance_slot(state: BeaconState) -> None: state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state) - if state.latest_block_header.state_root == ZERO_HASH: - state.latest_block_header.state_root = get_state_root(state, state.slot) - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state.latest_block_header) state.slot += 1 + if state.latest_block_header.state_root == ZERO_HASH: + state.latest_block_header.state_root = get_state_root(state, state.slot - 1) + state.latest_block_roots[(state.slot - 1) % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state.latest_block_header) ``` ### Per-block processing From 0eacabc2732a6532f8b54a834ff63078484825b1 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 5 Mar 2019 08:51:34 -0700 Subject: [PATCH 45/53] fix minor typo --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 36fbad847..ca37fa457 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2294,7 +2294,7 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): winning_root, participants = get_winning_root_and_participants(state, shard) participating_balance = get_total_balance(state, participants) - total_balance = get_total_balance(state, crossling_committee) + total_balance = get_total_balance(state, crosslink_committee) for index in crosslink_committee: if index in participants: deltas[0][index] += get_base_reward(state, index) * participating_balance // total_balance From b5f050959568b6cdedcf73ea17aceaf2168191e8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 5 Mar 2019 11:29:40 -0700 Subject: [PATCH 46/53] fix proposal slashing minor bugs --- specs/core/0_beacon-chain.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 43a679833..6cd56231d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1773,19 +1773,17 @@ def process_proposer_slashing(state: BeaconState, proposer = state.validator_registry[proposer_slashing.proposer_index] # Verify that the slot is the same assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot - # Verify that the shard is the same (or that both proposals are beacon chain proposals) - assert proposer_slashing.header_1.shard == proposer_slashing.header_2.shard # But the roots are different! - assert proposer_slashing.header_1.block_root != proposer_slashing.header_2.block_root + assert hash_tree_root(proposer_slashing.header_1) != hash_tree_root(proposer_slashing.header_2) # Proposer is not yet slashed assert proposer.slashed is False # Signatures are valid for header in (proposer_slashing.header_1, proposer_slashing.header_2): assert bls_verify( pubkey=proposer.pubkey, - message_hash=signed_root(header, "signature"), + message_hash=signed_root(header, "signature"), signature=header.signature, - domain=get_domain(state.fork, slot_to_epoch(header.slot), DOMAIN_PROPOSAL) + domain=get_domain(state.fork, slot_to_epoch(header.slot), DOMAIN_BEACON_BLOCK) ) slash_validator(state, proposer_slashing.proposer_index) ``` From b36e70040bef5b966f60b06d275e83014f046d63 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 5 Mar 2019 16:18:41 -0700 Subject: [PATCH 47/53] fix call to union --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6cd56231d..b94a74e84 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2010,7 +2010,7 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: output = set() for a in attestations: - output = output.union([get_attestation_participants(state, a.data, a.aggregation_bitfield)]) + output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield)) return sorted(list(output)) ``` From c9e06d31f381640a826016340970de6b56a40115 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Wed, 6 Mar 2019 07:29:06 -0700 Subject: [PATCH 48/53] Apply suggestions from code review base_reward -> get_base_reward Co-Authored-By: djrtwo --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b94a74e84..81af1a90e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2237,7 +2237,7 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> deltas[1][index] += get_base_reward(state, index) # Proposer bonus proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) - deltas[0][proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT + deltas[0][proposer_index] += get_base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT return deltas ``` @@ -2261,10 +2261,10 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List else: # If a validator did attest, apply a small penalty for getting attestations included late deltas[0][index] += ( - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // + get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index) ) - deltas[1][index] += base_reward(state, index) + deltas[1][index] += get_base_reward(state, index) if index not in get_attesting_indices(state, boundary_attestations): deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality) if index not in get_attesting_indices(state, matching_head_attestations): From b4779cd83dad62f8c2505c96e955fa9d8605bd61 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Wed, 6 Mar 2019 08:55:39 -0700 Subject: [PATCH 49/53] Apply suggestions from code review Co-Authored-By: djrtwo --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 81af1a90e..b0a7ea999 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -447,7 +447,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Sender index 'sender': 'uint64', # Recipient index - 'to': 'uint64', + 'recipient': 'uint64', # Amount in Gwei 'amount': 'uint64', # Fee in Gwei for block proposer @@ -1984,7 +1984,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: ) # Process the transfer state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee - state.validator_balances[transfer.to] += transfer.amount + state.validator_balances[transfer.recipient] += transfer.amount state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee ``` From 663bc489b60ea89708e6115763ca185628eb3edc Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Mar 2019 22:54:52 -0600 Subject: [PATCH 50/53] Added lexicographic tiebreaking --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 081e877c3..49f8ba3e9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2060,7 +2060,9 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple def get_attestations_for(root) -> List[PendingAttestation]: return [a for a in valid_attestations if a.data.crosslink_data_root == root] - winning_root = max(all_roots, key=lambda r: get_attesting_balance(state, get_attestations_for(r))) + # Winning crosslink root is the root with the most votes for it, ties broken in favor of + # lexicographically higher hash + winning_root = max(all_roots, key=lambda r: (get_attesting_balance(state, get_attestations_for(r)), r)) return winning_root, get_attesting_indices(state, get_attestations_for(winning_root)) ``` From ec12460b8bae49c2272870b4490039a68f3c4761 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 7 Mar 2019 01:21:17 -0600 Subject: [PATCH 51/53] Make wthdrawn validators immune to inactivity leak --- specs/core/0_beacon-chain.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 49f8ba3e9..bd709218a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2273,7 +2273,12 @@ def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List deltas[1][index] += get_base_reward(state, index) # Penalize slashed-but-inactive validators as though they were active but offline for index in range(len(state.validator_registry)): - if index not in active_validator_indices and state.validator_registry[index].slashed: + eligible = ( + index not in active_validator_indices and + state.validator_registry[index].slashed and + get_current_epoch(state) < state.validator_registry[index].withdrawable_epoch + ) + if eligible: deltas[1][index] += ( 2 * get_inactivity_penalty(state, index, epochs_since_finality) + get_base_reward(state, index) From 987c741bea582fce0839bc2ad2e14abe50a15d66 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Mar 2019 09:44:55 -0700 Subject: [PATCH 52/53] fix state types --- specs/core/0_beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d4739173e..796305953 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -543,8 +543,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH], 'latest_slashed_balances': ['uint64', LATEST_SLASHED_EXIT_LENGTH], # Balances slashed at every withdrawal period 'latest_block_header': BeaconBlockHeader, # `latest_block_header.state_root == ZERO_HASH` temporarily - 'latest_attestations': [PendingAttestation], - 'batched_block_roots': ['bytes32'], + 'historical_roots': ['bytes32'], # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, From 250455a67e18e51abe5566a0ea129873922c7427 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 7 Mar 2019 10:03:25 -0700 Subject: [PATCH 53/53] Apply suggestions from code review Co-Authored-By: djrtwo --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 796305953..002770913 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1899,7 +1899,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, - inclusion_slot=state.slot + inclusion_slot=state.slot, ) if slot_to_epoch(attestation.data.slot) == get_current_epoch(state): state.current_epoch_attestations.append(pending_attestation) @@ -2057,7 +2057,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple if len(all_roots) == 0: return ZERO_HASH, [] - def get_attestations_for(root) -> List[PendingAttestation]: + def get_attestations_for(root: Bytes32) -> List[PendingAttestation]: return [a for a in valid_attestations if a.data.crosslink_data_root == root] # Winning crosslink root is the root with the most votes for it, ties broken in favor of @@ -2134,7 +2134,7 @@ Run the following function: ```python def process_crosslinks(state: BeaconState) -> None: current_epoch = get_current_epoch(state) - previous_epoch = current_epoch - 1 + previous_epoch = get_previous_epoch(state) next_epoch = current_epoch + 1 for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): @@ -2144,7 +2144,7 @@ def process_crosslinks(state: BeaconState) -> None: if 3 * participating_balance >= 2 * total_balance: state.latest_crosslinks[shard] = Crosslink( epoch=slot_to_epoch(slot), - crosslink_data_root=winning_root + crosslink_data_root=winning_root, ) ```