From a212038cd3c37a05fc99fb6da48a2c10678993b1 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 17:33:15 -0700 Subject: [PATCH 01/16] begin translation to epochs --- specs/core/0_beacon-chain.md | 200 ++++++++++++++++++----------------- 1 file changed, 103 insertions(+), 97 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ed0ecf444..0f283103b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -60,6 +60,7 @@ - [Helper functions](#helper-functions) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) + - [`slot_to_epoch`](#slot_to_epoch) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) @@ -84,7 +85,7 @@ - [`is_double_vote`](#is_double_vote) - [`is_surround_vote`](#is_surround_vote) - [`integer_squareroot`](#integer_squareroot) - - [`entry_exit_effect_slot`](#entry_exit_effect_slot) + - [`entry_exit_effect_epoch`](#entry_exit_effect_epoch) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) @@ -191,7 +192,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `GENESIS_FORK_VERSION` | `0` | | `GENESIS_SLOT` | `0` | | `GENESIS_START_SHARD` | `0` | -| `FAR_FUTURE_SLOT` | `2**64 - 1` | +| `FAR_FUTURE_EPOCH` | `2**64 - 1` | | `ZERO_HASH` | `int_to_bytes32(0)` | | `EMPTY_SIGNATURE` | `int_to_bytes96(0)` | | `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` | @@ -203,10 +204,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `SLOT_DURATION` | `6` | seconds | 6 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | -| `SEED_LOOKAHEAD` | `2**6` (= 64) | slots | 6.4 minutes | -| `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | +| `SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | +| `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | -| `MIN_VALIDATOR_WITHDRAWAL_TIME` | `2**14` (= 16,384) | slots | ~27 hours | +| `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | ### Reward and penalty quotients @@ -468,7 +469,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be # Validator registry 'validator_registry': [Validator], 'validator_balances': ['uint64'], - 'validator_registry_update_slot': 'uint64', + 'validator_registry_update_epoch': 'uint64', 'validator_registry_exit_count': 'uint64', # Randomness and committees @@ -476,8 +477,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be 'latest_vdf_outputs': ['bytes32'], 'previous_epoch_start_shard': 'uint64', 'current_epoch_start_shard': 'uint64', - 'previous_epoch_calculation_slot': 'uint64', - 'current_epoch_calculation_slot': 'uint64', + 'previous_calculation_epoch': 'uint64', + 'current_calculation_epoch': 'uint64', 'previous_epoch_seed': 'bytes32', 'current_epoch_seed': 'bytes32', @@ -514,14 +515,14 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be 'withdrawal_credentials': 'bytes32', # Number of proposer slots since genesis 'proposer_slots': 'uint64', - # Slot when validator activated - 'activation_slot': 'uint64', - # Slot when validator exited - 'exit_slot': 'uint64', - # Slot when validator withdrew - 'withdrawal_slot': 'uint64', - # Slot when validator was penalized - 'penalized_slot': 'uint64', + # Epoch when validator activated + 'activation_epoch': 'uint64', + # Epoch when validator exited + 'exit_epoch': 'uint64', + # Epoch when validator withdrew + 'withdrawal_epoch': 'uint64', + # Epoch when validator was penalized + 'penalized_epoch': 'uint64', # Exit counter when validator exited 'exit_count': 'uint64', # Status flags @@ -762,13 +763,20 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere `hash_tree_root` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#tree-hash). +#### `slot_to_epoch` + +```python +def slot_to_epoch(slot: int) -> int: + return slot // EPOCH_LENGTH +``` + #### `is_active_validator` ```python def is_active_validator(validator: Validator, slot: int) -> bool: """ Checks if ``validator`` is active. """ - return validator.activation_slot <= slot < validator.exit_slot + return validator.activation_epoch <= slot_to_epoch(slot) < validator.exit_epoch ``` #### `get_active_validator_indices` @@ -866,29 +874,26 @@ def get_committee_count_per_slot(active_validator_count: int) -> int: ```python def get_shuffling(seed: Bytes32, validators: List[Validator], - slot: int) -> List[List[int]] + epoch: int) -> List[List[int]] """ - Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``slot``. + Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. Returns a list of ``EPOCH_LENGTH * committees_per_slot`` committees where each committee is itself a list of validator indices. """ - # Normalizes slot to start of epoch boundary - slot -= slot % EPOCH_LENGTH - active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = get_committee_count_per_slot(len(active_validator_indices)) # Shuffle - seed = xor(seed, int_to_bytes32(slot)) + seed = xor(seed, int_to_bytes32(epoch)) shuffled_active_validator_indices = shuffle(active_validator_indices, seed) # Split the shuffled list into epoch_length * committees_per_slot pieces return split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH) ``` -**Invariant**: if `get_shuffling(seed, validators, slot)` returns some value `x` for some `slot <= state.slot + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `slot` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. +**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= slot_to_epoch(state.slot) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. **Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work. @@ -898,7 +903,7 @@ def get_shuffling(seed: Bytes32, def get_previous_epoch_committee_count_per_slot(state: BeaconState) -> int: previous_active_validators = get_active_validator_indices( state.validator_registry, - state.previous_epoch_calculation_slot, + state.previous_calculation_epoch, ) return get_committee_count_per_slot(len(previous_active_validators)) ``` @@ -909,7 +914,7 @@ def get_previous_epoch_committee_count_per_slot(state: BeaconState) -> int: def get_current_epoch_committee_count_per_slot(state: BeaconState) -> int: current_active_validators = get_active_validator_indices( state.validator_registry, - state.current_epoch_calculation_slot, + state.current_calculation_epoch, ) return get_committee_count_per_slot(len(current_active_validators)) ``` @@ -929,18 +934,18 @@ def get_crosslink_committees_at_slot(state: BeaconState, if slot < state_epoch_slot: committees_per_slot = get_previous_epoch_committee_count_per_slot(state) seed = state.previous_epoch_seed - shuffling_slot = state.previous_epoch_calculation_slot + shuffling_epoch = state.previous_calculation_epoch shuffling_start_shard = state.previous_epoch_start_shard else: committees_per_slot = get_current_epoch_committee_count_per_slot(state) seed = state.current_epoch_seed - shuffling_slot = state.current_epoch_calculation_slot + shuffling_epoch = state.current_calculation_epoch shuffling_start_shard = state.current_epoch_start_shard shuffling = get_shuffling( seed, state.validator_registry, - shuffling_slot, + shuffling_epoch, ) offset = slot % EPOCH_LENGTH slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT @@ -988,33 +993,32 @@ def get_randao_mix(state: BeaconState, ```python def get_active_index_root(state: BeaconState, - slot: int) -> Bytes32: + epoch: int) -> Bytes32: """ Returns the index root at a recent ``slot``. """ - state_epoch = state.slot // EPOCH_LENGTH - given_epoch = slot // EPOCH_LENGTH - assert state_epoch < given_epoch + LATEST_INDEX_ROOTS_LENGTH - assert given_epoch <= state_epoch - return state.latest_index_roots[given_epoch % LATEST_INDEX_ROOTS_LENGTH] + state_epoch = slot_to_epoch(state.slot) + assert state_epoch < epoch + LATEST_INDEX_ROOTS_LENGTH + assert epoch <= state_epoch + return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] ``` #### `generate_seed` ```python def generate_seed(state: BeaconState, - slot: int) -> Bytes32: + epoch: int) -> Bytes32: """ - Generate a seed for the given ``slot``. + Generate a seed for the given ``epoch``. """ - if slot < SEED_LOOKAHEAD: - randao_mix_slot = GENESIS_SLOT + if epoch < SEED_LOOKAHEAD: + randao_mix_epoch = slot_to_epoch(GENESIS_SLOT) else: - randao_mix_slot = slot - SEED_LOOKAHEAD + randao_mix_epoch = epoch - SEED_LOOKAHEAD return hash( - get_randao_mix(state, randao_mix_slot) + - get_active_index_root(state, slot) + get_randao_mix(state, randao_mix_epoch * EPOCH_LENGTH) + + get_active_index_root(state, epoch) ) ``` @@ -1142,8 +1146,8 @@ def is_double_vote(attestation_data_1: AttestationData, Returns True if the provided ``AttestationData`` are slashable due to a 'double vote'. """ - target_epoch_1 = attestation_data_1.slot // EPOCH_LENGTH - target_epoch_2 = attestation_data_2.slot // EPOCH_LENGTH + target_epoch_1 = slot_to_epoch(attestation_data_1.slot) + target_epoch_2 = slot_to_epoch(attestation_data_2.slot) return target_epoch_1 == target_epoch_2 ``` @@ -1159,10 +1163,10 @@ def is_surround_vote(attestation_data_1: AttestationData, Note: parameter order matters as this function only checks that ``attestation_data_1`` surrounds ``attestation_data_2``. """ - source_epoch_1 = attestation_data_1.justified_slot // EPOCH_LENGTH - source_epoch_2 = attestation_data_2.justified_slot // EPOCH_LENGTH - target_epoch_1 = attestation_data_1.slot // EPOCH_LENGTH - target_epoch_2 = attestation_data_2.slot // EPOCH_LENGTH + source_epoch_1 = slot_to_epoch(attestation_data_1.justified_slot) + source_epoch_2 = slot_to_epoch(attestation_data_2.justified_slot) + target_epoch_1 = slot_to_epoch(attestation_data_1.slot) + target_epoch_2 = slot_to_epoch(attestation_data_2.slot) return ( (source_epoch_1 < source_epoch_2) and (source_epoch_2 + 1 == target_epoch_2) and @@ -1186,15 +1190,15 @@ def integer_squareroot(n: int) -> int: return x ``` -#### `entry_exit_effect_slot` +#### `entry_exit_effect_epoch` ```python -def entry_exit_effect_slot(n: int) -> int: +def entry_exit_effect_epoch(slot: int) -> int: """ An entry or exit triggered in the slot given by the input takes effect at - the slot given by the output. + the epoch given by the output. """ - return (n - n % EPOCH_LENGTH) + EPOCH_LENGTH + ENTRY_EXIT_DELAY + return slot_to_epoch(slot) + 1 + ENTRY_EXIT_DELAY ``` #### `bls_verify` @@ -1256,7 +1260,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Validator registry validator_registry=[], validator_balances=[], - validator_registry_update_slot=GENESIS_SLOT, + validator_registry_update_epoch=slot_to_epoch(GENESIS_SLOT), validator_registry_exit_count=0, # Randomness and committees @@ -1264,8 +1268,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_vdf_outputs=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH // EPOCH_LENGTH)], previous_epoch_start_shard=GENESIS_START_SHARD, current_epoch_start_shard=GENESIS_START_SHARD, - previous_epoch_calculation_slot=GENESIS_SLOT, - current_epoch_calculation_slot=GENESIS_SLOT, + previous_calculation_epoch=slot_to_epoch(GENESIS_SLOT), + current_calculation_epoch=slot_to_epoch(GENESIS_SLOT), previous_epoch_seed=ZERO_HASH, current_epoch_seed=ZERO_HASH, @@ -1306,8 +1310,9 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, True) - state.latest_index_roots[GENESIS_SLOT // EPOCH_LENGTH % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, GENESIS_SLOT)) - state.current_epoch_seed = generate_seed(state, GENESIS_SLOT) + genesis_epoch = slot_to_epoch(GENESIS_SLOT) + state.latest_index_roots[genesis_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, genesis_epoch)) + state.current_epoch_seed = generate_seed(state, genesis_epoch) return state ``` @@ -1367,10 +1372,10 @@ def process_deposit(state: BeaconState, pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, proposer_slots=0, - activation_slot=FAR_FUTURE_SLOT, - exit_slot=FAR_FUTURE_SLOT, - withdrawal_slot=FAR_FUTURE_SLOT, - penalized_slot=FAR_FUTURE_SLOT, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epch=FAR_FUTURE_EPOCH, + withdrawal_epoch=FAR_FUTURE_EPOCH, + penalized_epoch=FAR_FUTURE_EPOCH, exit_count=0, status_flags=0, latest_custody_reseed_slot=GENESIS_SLOT, @@ -1396,7 +1401,7 @@ Note: All functions in this section mutate `state`. def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: validator = state.validator_registry[index] - validator.activation_slot = GENESIS_SLOT if genesis else entry_exit_effect_slot(state.slot) + validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else entry_exit_effect_epoch(state.slot) ``` ```python @@ -1410,10 +1415,10 @@ def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] # The following updates only occur if not previous exited - if validator.exit_slot <= entry_exit_effect_slot(state.slot): + if validator.exit_epoch <= entry_exit_effect_epoch(state.slot): return - validator.exit_slot = entry_exit_effect_slot(state.slot) + validator.exit_epoch = entry_exit_effect_epoch(state.slot) state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count @@ -1423,13 +1428,13 @@ def exit_validator(state: BeaconState, index: int) -> None: def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) validator = state.validator_registry[index] - state.latest_penalized_balances[(state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + state.latest_penalized_balances[slot_to_epoch(state.slot) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.penalized_slot = state.slot + validator.penalized_epoch = slot_to_epoch(state.slot) ``` ```python @@ -1573,7 +1578,7 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.exit_slot > entry_exit_effect_slot(state.slot)`. +* Verify that `validator.exit_epoch > entry_exit_effect_epoch(state.slot)`. * Verify that `state.slot >= exit.slot`. * Let `exit_message = hash_tree_root(Exit(slot=exit.slot, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. * Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.slot, DOMAIN_EXIT))`. @@ -1589,15 +1594,16 @@ The steps below happen when `state.slot % EPOCH_LENGTH == EPOCH_LENGTH - 1`. ### Helpers -* Let `next_epoch_start_slot = state.slot + 1`. -* Let `current_epoch_start_slot = state.slot - (EPOCH_LENGTH + 1)`. -* Let `previous_epoch_start_slot = state.slot - 2 * EPOCH_LENGTH + 1` if `state.slot > EPOCH_LENGTH` else `current_epoch_start_slot`. -* Let `next_epoch = next_epoch_start_slot // EPOCH_LENGTH`. -* Let `current_epoch = current_epoch_start_slot // EPOCH_LENGTH`. +* Let `current_epoch = slot_to_epoch(state.slot)`. +* Let `current_epoch_start_slot = current_epoch * EPOCH_LENGTH`. +* Let `next_epoch = slot_to_epoch(state.slot + 1)`. +* Let `next_epoch_start_slot = next_epoch * EPOCH_LENGTH`. +* Let `previous_epoch = current_epoch - 1 if current_epoch > slot_to_epoch(GENESIS_SLOT) else current_epoch`. +* Let `previous_epoch_start_slot = previous_epoch * EPOCH_LENGTH`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch_start_slot)`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch)`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1688,7 +1694,7 @@ First, we define some additional helpers: Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. -* Let `epochs_since_finality = (next_epoch_start_slot - state.finalized_slot) // EPOCH_LENGTH`. +* Let `epochs_since_finality = next_epoch - slot_to_epoch(state.finalized_slot)`. Case 1: `epochs_since_finality <= 4`: @@ -1709,7 +1715,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_slot <= current_epoch_start_slot`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, 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 @@ -1733,7 +1739,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_start_slot): + for index in get_active_validator_indices(state.validator_registry, current_epoch): if state.validator_balances[index] < EJECTION_BALANCE: exit_validator(state, index) ``` @@ -1742,15 +1748,15 @@ def process_ejections(state: BeaconState) -> None: First, update the following: -* Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot` -* Set `state.previous_epoch_start_shard = state.current_epoch_start_shard` -* Set `state.previous_epoch_seed = state.current_epoch_seed` -* Set `state.latest_index_roots[next_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch_start_slot))` +* Set `state.previous_calculation_epoch = state.current_calculation_epoch`. +* Set `state.previous_epoch_start_shard = state.current_epoch_start_shard`. +* Set `state.previous_epoch_seed = state.current_epoch_seed`. +* Set `state.latest_index_roots[next_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch_start_slot))`. If the following are satisfied: -* `state.finalized_slot > state.validator_registry_update_slot` -* `state.latest_crosslinks[shard].slot > state.validator_registry_update_slot` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) +* `slot_to_epoch(state.finalized_slot) > state.validator_registry_update_epoch` +* `slot_to_epoch(state.latest_crosslinks[shard].slot) > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) update the validator registry and associated fields by running @@ -1761,7 +1767,7 @@ def update_validator_registry(state: BeaconState) -> None: Note that this function mutates ``state``. """ # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch_start_slot) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1774,7 +1780,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_slot > entry_exit_effect_slot(state.slot) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch > entry_exit_effect_epoch(state.slot) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1786,7 +1792,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_slot > entry_exit_effect_slot(state.slot) and validator.status_flags & INITIATED_EXIT: + if validator.exit_epoch > entry_exit_effect_epoch(state.slot) and validator.status_flags & INITIATED_EXIT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1795,21 +1801,21 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - state.validator_registry_update_slot = state.slot - (state.slot % EPOCH_LENGTH) + state.validator_registry_update_epoch = slot_to_epoch(state.slot) ``` and perform the following updates: -* Set `state.current_epoch_calculation_slot = next_epoch_start_slot` +* Set `state.current_calculation_epoch = next_epoch` * Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) % SHARD_COUNT` -* Set `state.current_epoch_seed = generate_seed(state, state.current_epoch_calculation_slot)` +* Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` If a validator registry update does _not_ happen do the following: -* Let `epochs_since_last_registry_change = (current_epoch_start_slot - state.validator_registry_update_slot) // EPOCH_LENGTH`. +* Let `epochs_since_last_registry_change = (current_epoch - state.validator_registry_update_epoch)`. * If `epochs_since_last_registry_change` is an exact power of 2: - * Set `state.current_epoch_calculation_slot = next_epoch_start_slot`. - * Set `state.current_epoch_seed = generate_seed(state, state.current_epoch_calculation_slot)` + * Set `state.current_calculation_epoch = next_epoch`. + * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` * _Note_ that `state.current_epoch_start_shard` is left unchanged. **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. @@ -1824,8 +1830,8 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) for index, validator in enumerate(state.validator_registry): - if (state.slot // EPOCH_LENGTH) == (validator.penalized_slot // EPOCH_LENGTH) + LATEST_PENALIZED_EXIT_LENGTH // 2: - e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH + if slot_to_epoch(state.slot) == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: + e = slot_to_epoch(state.slot) % LATEST_PENALIZED_EXIT_LENGTH total_at_start = state.latest_penalized_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_balances[e] total_penalties = total_at_end - total_at_start @@ -1834,11 +1840,11 @@ def process_penalties_and_exits(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] - if validator.penalized_slot <= state.slot: - PENALIZED_WITHDRAWAL_TIME = LATEST_PENALIZED_EXIT_LENGTH * EPOCH_LENGTH // 2 - return state.slot >= validator.penalized_slot + PENALIZED_WITHDRAWAL_TIME + if validator.penalized_epoch <= slot_to_epoch(state.slot): + PENALIZED_WITHDRAWAL_EPOCHS = LATEST_PENALIZED_EXIT_LENGTH // 2 + return slot_to_epoch(state.slot) >= validator.penalized_epoch + PENALIZED_WITHDRAWAL_EPOCHS else: - return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME + return slot_to_epoch(state.slot) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) From f943361a23883b387cd52c00bea24d7605fccc5b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 17:40:44 -0700 Subject: [PATCH 02/16] convert voting period to epochs --- 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 0f283103b..372191e8f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -206,7 +206,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | | `SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `ETH1_DATA_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | +| `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | ### Reward and penalty quotients @@ -1590,7 +1590,7 @@ For each `exit` in `block.body.exits`: ## Per-epoch processing -The steps below happen when `state.slot % EPOCH_LENGTH == EPOCH_LENGTH - 1`. +The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. ### Helpers @@ -1650,9 +1650,9 @@ Define the following helpers to process attestation inclusion rewards and inclus ### Eth1 data -If `state.slot % ETH1_DATA_VOTING_PERIOD == ETH1_DATA_VOTING_PERIOD - 1`: +If `slot_to_epoch(state.slot) % ETH1_DATA_VOTING_PERIOD == 0`: -* Set `state.latest_eth1_data = eth1_data_vote.data` if `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD` for some `eth1_data_vote` in `state.eth1_data_votes`. +* Set `state.latest_eth1_data = eth1_data_vote.data` if `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes`. * Set `state.eth1_data_votes = []`. ### Justification @@ -1812,7 +1812,7 @@ and perform the following updates: If a validator registry update does _not_ happen do the following: -* Let `epochs_since_last_registry_change = (current_epoch - state.validator_registry_update_epoch)`. +* Let `epochs_since_last_registry_change = current_epoch - state.validator_registry_update_epoch`. * If `epochs_since_last_registry_change` is an exact power of 2: * Set `state.current_calculation_epoch = next_epoch`. * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` From 0e90dd8ad021735669c219281b94fb8e39ac0e06 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 07:31:09 -0700 Subject: [PATCH 03/16] add current_epoch helper --- specs/core/0_beacon-chain.md | 53 ++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 372191e8f..49b4d0077 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -61,6 +61,7 @@ - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`slot_to_epoch`](#slot_to_epoch) + - [`current_epoch`](#current_epoch) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) @@ -770,6 +771,14 @@ def slot_to_epoch(slot: int) -> int: return slot // EPOCH_LENGTH ``` +#### `current_epoch` + +```python +def current_epoch(state: BeaconState) -> int: + return slot_to_epoch(state.slot) +``` + + #### `is_active_validator` ```python def is_active_validator(validator: Validator, slot: int) -> bool: @@ -893,7 +902,7 @@ def get_shuffling(seed: Bytes32, return split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH) ``` -**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= slot_to_epoch(state.slot) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. +**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= current_epoch(state) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. **Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work. @@ -997,9 +1006,8 @@ def get_active_index_root(state: BeaconState, """ Returns the index root at a recent ``slot``. """ - state_epoch = slot_to_epoch(state.slot) - assert state_epoch < epoch + LATEST_INDEX_ROOTS_LENGTH - assert epoch <= state_epoch + assert current_epoch(state) < epoch + LATEST_INDEX_ROOTS_LENGTH + assert epoch <= current_epoch(state) return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] ``` @@ -1428,13 +1436,13 @@ def exit_validator(state: BeaconState, index: int) -> None: def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) validator = state.validator_registry[index] - state.latest_penalized_balances[slot_to_epoch(state.slot) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + state.latest_penalized_balances[current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.penalized_epoch = slot_to_epoch(state.slot) + validator.penalized_epoch = current_epoch(state) ``` ```python @@ -1594,16 +1602,15 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. ### Helpers -* Let `current_epoch = slot_to_epoch(state.slot)`. -* Let `current_epoch_start_slot = current_epoch * EPOCH_LENGTH`. -* Let `next_epoch = slot_to_epoch(state.slot + 1)`. +* Let `current_epoch_start_slot = current_epoch(state) * EPOCH_LENGTH`. +* Let `next_epoch = current_epoch(state) + 1`. * Let `next_epoch_start_slot = next_epoch * EPOCH_LENGTH`. -* Let `previous_epoch = current_epoch - 1 if current_epoch > slot_to_epoch(GENESIS_SLOT) else current_epoch`. +* Let `previous_epoch = current_epoch(state) - 1 if current_epoch(state) > slot_to_epoch(GENESIS_SLOT) else current_epoch(state)`. * Let `previous_epoch_start_slot = previous_epoch * EPOCH_LENGTH`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch)`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state))`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1650,7 +1657,7 @@ Define the following helpers to process attestation inclusion rewards and inclus ### Eth1 data -If `slot_to_epoch(state.slot) % ETH1_DATA_VOTING_PERIOD == 0`: +If `current_epoch(state) % ETH1_DATA_VOTING_PERIOD == 0`: * Set `state.latest_eth1_data = eth1_data_vote.data` if `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes`. * Set `state.eth1_data_votes = []`. @@ -1715,7 +1722,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch(state)`, 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 @@ -1739,7 +1746,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): + for index in get_active_validator_indices(state.validator_registry, current_epoch(state)): if state.validator_balances[index] < EJECTION_BALANCE: exit_validator(state, index) ``` @@ -1767,7 +1774,7 @@ def update_validator_registry(state: BeaconState) -> None: Note that this function mutates ``state``. """ # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state)) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1801,7 +1808,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - state.validator_registry_update_epoch = slot_to_epoch(state.slot) + state.validator_registry_update_epoch = current_epoch(state) ``` and perform the following updates: @@ -1812,7 +1819,7 @@ and perform the following updates: If a validator registry update does _not_ happen do the following: -* Let `epochs_since_last_registry_change = current_epoch - state.validator_registry_update_epoch`. +* Let `epochs_since_last_registry_change = current_epoch(state) - state.validator_registry_update_epoch`. * If `epochs_since_last_registry_change` is an exact power of 2: * Set `state.current_calculation_epoch = next_epoch`. * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` @@ -1830,8 +1837,8 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) for index, validator in enumerate(state.validator_registry): - if slot_to_epoch(state.slot) == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: - e = slot_to_epoch(state.slot) % LATEST_PENALIZED_EXIT_LENGTH + if current_epoch(state) == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: + e = current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH total_at_start = state.latest_penalized_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_balances[e] total_penalties = total_at_end - total_at_start @@ -1840,11 +1847,11 @@ def process_penalties_and_exits(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] - if validator.penalized_epoch <= slot_to_epoch(state.slot): + if validator.penalized_epoch <= current_epoch(state): PENALIZED_WITHDRAWAL_EPOCHS = LATEST_PENALIZED_EXIT_LENGTH // 2 - return slot_to_epoch(state.slot) >= validator.penalized_epoch + PENALIZED_WITHDRAWAL_EPOCHS + return current_epoch(state) >= validator.penalized_epoch + PENALIZED_WITHDRAWAL_EPOCHS else: - return slot_to_epoch(state.slot) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS + return current_epoch(state) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) @@ -1859,7 +1866,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates -* Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch % LATEST_PENALIZED_EXIT_LENGTH]`. +* Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH]`. * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < current_epoch_start_slot`. ## State root processing From c9494dbf8813f65b0d138f9b173aea2959f4a051 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 07:55:32 -0700 Subject: [PATCH 04/16] change crosslink.slot to crosslink.epoch --- specs/core/0_beacon-chain.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 49b4d0077..76f69f274 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -539,8 +539,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be ```python { - # Slot number - 'slot': 'uint64', + # Epoch number + 'epoch': 'uint64', # Shard block root 'shard_block_root': 'bytes32', } @@ -1291,7 +1291,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], finalized_slot=GENESIS_SLOT, # Recent state - latest_crosslinks=[Crosslink(slot=GENESIS_SLOT, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[Crosslink(epoch=slot_to_epoch(GENESIS_SLOT), shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)], latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)], @@ -1318,7 +1318,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, True) - genesis_epoch = slot_to_epoch(GENESIS_SLOT) + genesis_epoch = current_epoch(state) state.latest_index_roots[genesis_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, genesis_epoch)) state.current_epoch_seed = generate_seed(state, genesis_epoch) @@ -1687,7 +1687,7 @@ Finally, update the following: For every `slot in range(previous_epoch_start_slot, next_epoch_start_slot)`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: -* Set `state.latest_crosslinks[shard] = Crosslink(slot=current_epoch_start_slot, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. +* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch(state), shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. ### Rewards and penalties @@ -1763,7 +1763,7 @@ First, update the following: If the following are satisfied: * `slot_to_epoch(state.finalized_slot) > state.validator_registry_update_epoch` -* `slot_to_epoch(state.latest_crosslinks[shard].slot) > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) +* `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) update the validator registry and associated fields by running From ae5dfab217e48e20c6f50543f017e0aa0ede11f1 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 08:16:32 -0700 Subject: [PATCH 05/16] convert finality vars to epochs and do some more epohh cleaning --- specs/core/0_beacon-chain.md | 108 +++++++++++++++++------------------ 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 76f69f274..57158fdd4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -333,8 +333,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be 'shard_block_root': 'bytes32', # Last crosslink's hash of root 'latest_crosslink_root': 'bytes32', - # Slot of the last justified beacon block - 'justified_slot': 'uint64', + # Last justified epoch in the beacon state + 'justified_epoch': 'uint64', # Hash of the last justified beacon block 'justified_block_root': 'bytes32', } @@ -487,10 +487,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be 'custody_challenges': [CustodyChallenge], # Finality - 'previous_justified_slot': 'uint64', - 'justified_slot': 'uint64', + 'previous_justified_epoch': 'uint64', + 'justified_epoch': 'uint64', 'justification_bitfield': 'uint64', - 'finalized_slot': 'uint64', + 'finalized_epoch': 'uint64', # Recent state 'latest_crosslinks': [Crosslink], @@ -726,7 +726,7 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi def lmd_ghost(store, start): validators = start.state.validator_registry active_validators = [validators[i] for i in - get_active_validator_indices(validators, start.state.slot)] + get_active_validator_indices(validators, current_epoch(start.state))] attestation_targets = [get_latest_attestation_target(store, validator) for validator in active_validators] def get_vote_count(block): @@ -781,21 +781,21 @@ def current_epoch(state: BeaconState) -> int: #### `is_active_validator` ```python -def is_active_validator(validator: Validator, slot: int) -> bool: +def is_active_validator(validator: Validator, epoch: int) -> bool: """ Checks if ``validator`` is active. """ - return validator.activation_epoch <= slot_to_epoch(slot) < validator.exit_epoch + return validator.activation_epoch <= epoch < validator.exit_epoch ``` #### `get_active_validator_indices` ```python -def get_active_validator_indices(validators: [Validator], slot: int) -> List[int]: +def get_active_validator_indices(validators: [Validator], epoch: int) -> List[int]: """ Gets indices of active validators from ``validators``. """ - return [i for i, v in enumerate(validators) if is_active_validator(v, slot)] + return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` #### `shuffle` @@ -890,7 +890,7 @@ def get_shuffling(seed: Bytes32, committee is itself a list of validator indices. """ - active_validator_indices = get_active_validator_indices(validators, slot) + active_validator_indices = get_active_validator_indices(validators, epoch) committees_per_slot = get_committee_count_per_slot(len(active_validator_indices)) @@ -936,11 +936,11 @@ def get_crosslink_committees_at_slot(state: BeaconState, """ Returns the list of ``(committee, shard)`` tuples for the ``slot``. """ - state_epoch_slot = state.slot - (state.slot % EPOCH_LENGTH) - assert state_epoch_slot <= slot + EPOCH_LENGTH - assert slot < state_epoch_slot + EPOCH_LENGTH + current_epoch_slot = current_epoch(state) * EPOCH_LENGTH + assert current_epoch_slot <= slot + EPOCH_LENGTH + assert slot < current_epoch_slot + EPOCH_LENGTH - if slot < state_epoch_slot: + if slot < current_epoch_slot: committees_per_slot = get_previous_epoch_committee_count_per_slot(state) seed = state.previous_epoch_seed shuffling_epoch = state.previous_calculation_epoch @@ -1171,8 +1171,8 @@ def is_surround_vote(attestation_data_1: AttestationData, Note: parameter order matters as this function only checks that ``attestation_data_1`` surrounds ``attestation_data_2``. """ - source_epoch_1 = slot_to_epoch(attestation_data_1.justified_slot) - source_epoch_2 = slot_to_epoch(attestation_data_2.justified_slot) + source_epoch_1 = attestation_data_1.justified_epoch + source_epoch_2 = attestation_data_2.justified_epoch target_epoch_1 = slot_to_epoch(attestation_data_1.slot) target_epoch_2 = slot_to_epoch(attestation_data_2.slot) return ( @@ -1201,12 +1201,12 @@ def integer_squareroot(n: int) -> int: #### `entry_exit_effect_epoch` ```python -def entry_exit_effect_epoch(slot: int) -> int: +def entry_exit_effect_epoch(epoch: int) -> int: """ - An entry or exit triggered in the slot given by the input takes effect at + An entry or exit triggered in the ``epoch`` given by the input takes effect at the epoch given by the output. """ - return slot_to_epoch(slot) + 1 + ENTRY_EXIT_DELAY + return epoch + 1 + ENTRY_EXIT_DELAY ``` #### `bls_verify` @@ -1255,6 +1255,7 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_time: int, latest_eth1_data: Eth1Data) -> BeaconState: + genesis_epoch = slot_to_epoch(GENESIS_SLOT) state = BeaconState( # Misc slot=GENESIS_SLOT, @@ -1268,7 +1269,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Validator registry validator_registry=[], validator_balances=[], - validator_registry_update_epoch=slot_to_epoch(GENESIS_SLOT), + validator_registry_update_epoch=genesis_epoch, validator_registry_exit_count=0, # Randomness and committees @@ -1276,8 +1277,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_vdf_outputs=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH // EPOCH_LENGTH)], previous_epoch_start_shard=GENESIS_START_SHARD, current_epoch_start_shard=GENESIS_START_SHARD, - previous_calculation_epoch=slot_to_epoch(GENESIS_SLOT), - current_calculation_epoch=slot_to_epoch(GENESIS_SLOT), + previous_calculation_epoch=genesis_epoch, + current_calculation_epoch=genesis_epoch, previous_epoch_seed=ZERO_HASH, current_epoch_seed=ZERO_HASH, @@ -1285,13 +1286,13 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], custody_challenges=[], # Finality - previous_justified_slot=GENESIS_SLOT, - justified_slot=GENESIS_SLOT, + previous_justified_epoch=genesis_epoch, + justified_epoch=genesis_epoch, justification_bitfield=0, - finalized_slot=GENESIS_SLOT, + finalized_epoch=genesis_epoch, # Recent state - latest_crosslinks=[Crosslink(epoch=slot_to_epoch(GENESIS_SLOT), shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[Crosslink(epoch=genesis_epoch, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)], latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)], @@ -1318,7 +1319,6 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, True) - genesis_epoch = current_epoch(state) state.latest_index_roots[genesis_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, genesis_epoch)) state.current_epoch_seed = generate_seed(state, genesis_epoch) @@ -1409,7 +1409,7 @@ Note: All functions in this section mutate `state`. def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: validator = state.validator_registry[index] - validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else entry_exit_effect_epoch(state.slot) + validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else entry_exit_effect_epoch(current_epoch(state)) ``` ```python @@ -1423,10 +1423,10 @@ def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] # The following updates only occur if not previous exited - if validator.exit_epoch <= entry_exit_effect_epoch(state.slot): + if validator.exit_epoch <= entry_exit_effect_epoch(current_epoch(state)): return - validator.exit_epoch = entry_exit_effect_epoch(state.slot) + validator.exit_epoch = entry_exit_effect_epoch(current_epoch(state)) state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count @@ -1504,7 +1504,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot`. * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. -* Verify that `proposer.penalized_slot > state.slot`. +* Verify that `proposer.penalized_epoch > current_epoch(state)`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. @@ -1524,7 +1524,7 @@ For each `casper_slashing` in `block.body.casper_slashings`: * Verify that `is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)` or `is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_1)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_2)`. -* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_slot > state.slot`. +* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > current_epoch(state)`. #### Attestations @@ -1534,8 +1534,8 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. * Verify that `attestation.data.slot + EPOCH_LENGTH >= state.slot`. -* Verify that `attestation.data.justified_slot` is equal to `state.justified_slot if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_slot`. -* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, attestation.data.justified_slot)`. +* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_epoch`. +* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, attestation.data.justified_epoch * EPOCH_LENGTH)`. * Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`. * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)`. @@ -1586,7 +1586,7 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.exit_epoch > entry_exit_effect_epoch(state.slot)`. +* Verify that `validator.exit_epoch > entry_exit_effect_epoch(current_epoch(state))`. * Verify that `state.slot >= exit.slot`. * Let `exit_message = hash_tree_root(Exit(slot=exit.slot, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. * Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.slot, DOMAIN_EXIT))`. @@ -1617,7 +1617,7 @@ All [validators](#dfn-validator): * Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch_start_slot <= a.data.slot < next_epoch_start_slot]`. (Note: this is the set of attestations of slots in the epoch `current_epoch_start_slot...next_epoch_start_slot`, _not_ attestations that got included in the chain during the epoch `current_epoch_start_slot...next_epoch_start_slot`.) * 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, current_epoch_start_slot) and a.data.justified_slot == state.justified_slot]`. + * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, current_epoch_start_slot) and a.data.justified_epoch == state.justified_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 = sum([get_effective_balance(state, i) for i in current_epoch_boundary_attester_indices])`. @@ -1627,7 +1627,7 @@ All [validators](#dfn-validator): * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch_start_slot <= a.data.slot < current_epoch_start_slot]`. * 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]`. * Validators targeting the previous justified slot: - * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_slot == state.previous_justified_slot]`. + * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_epoch == state.previous_justified_epoch]`. * Let `previous_epoch_justified_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_justified_attestations]`. * Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_justified_attester_indices])`. * Validators justifying the epoch boundary block at the start of the previous epoch: @@ -1666,22 +1666,22 @@ If `current_epoch(state) % ETH1_DATA_VOTING_PERIOD == 0`: First, update the justification bitfield: -* Let `new_justified_slot = state.justified_slot`. +* Let `new_justified_epoch = state.justified_epoch`. * Set `state.justification_bitfield = (state.justification_bitfield * 2) % 2**64`. -* Set `state.justification_bitfield |= 2` and `new_justified_slot = previous_epoch_start_slot` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. -* Set `state.justification_bitfield |= 1` and `new_justified_slot = current_epoch_start_slot` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. +* Set `state.justification_bitfield |= 2` and `new_justified_epoch = previous_epoch` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. +* Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch(state)` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. Next, update last finalized slot if possible: -* Set `state.finalized_slot = state.previous_justified_slot` if `(state.justification_bitfield >> 1) % 8 == 0b111 and state.previous_justified_slot == previous_epoch_start_slot - 2 * EPOCH_LENGTH`. -* Set `state.finalized_slot = state.previous_justified_slot` if `(state.justification_bitfield >> 1) % 4 == 0b11 and state.previous_justified_slot == previous_epoch_start_slot - EPOCH_LENGTH`. -* Set `state.finalized_slot = state.justified_slot` if `(state.justification_bitfield >> 0) % 8 == 0b111 and state.justified_slot == previous_epoch_start_slot - EPOCH_LENGTH`. -* Set `state.finalized_slot = state.justified_slot` if `(state.justification_bitfield >> 0) % 4 == 0b11 and state.justified_slot == previous_epoch_start_slot`. +* 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_slot = state.justified_slot`. -* Set `state.justified_slot = justified_slot`. +* Set `state.previous_justified_epoch = state.justified_epoch`. +* Set `state.justified_epoch = new_justified_epoch`. ### Crosslinks @@ -1701,7 +1701,7 @@ First, we define some additional helpers: Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. -* Let `epochs_since_finality = next_epoch - slot_to_epoch(state.finalized_slot)`. +* Let `epochs_since_finality = next_epoch - state.finalized_epoch`. Case 1: `epochs_since_finality <= 4`: @@ -1758,11 +1758,11 @@ First, update the following: * Set `state.previous_calculation_epoch = state.current_calculation_epoch`. * Set `state.previous_epoch_start_shard = state.current_epoch_start_shard`. * Set `state.previous_epoch_seed = state.current_epoch_seed`. -* Set `state.latest_index_roots[next_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch_start_slot))`. +* Set `state.latest_index_roots[next_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch))`. If the following are satisfied: -* `slot_to_epoch(state.finalized_slot) > state.validator_registry_update_epoch` +* `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_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) update the validator registry and associated fields by running @@ -1787,7 +1787,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > entry_exit_effect_epoch(state.slot) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch > entry_exit_effect_epoch(current_epoch(state)) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1799,7 +1799,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > entry_exit_effect_epoch(state.slot) and validator.status_flags & INITIATED_EXIT: + if validator.exit_epoch > entry_exit_effect_epoch(current_epoch(state)) and validator.status_flags & INITIATED_EXIT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1832,7 +1832,7 @@ Regardless of whether or not a validator set change happens, run the following: ```python def process_penalties_and_exits(state: BeaconState) -> None: # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state)) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1867,7 +1867,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates * Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH]`. -* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < current_epoch_start_slot`. +* Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch(state)`. ## State root processing From b1ea3b26674d352861544099dc56d0feabf76790 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 15:27:50 -0700 Subject: [PATCH 06/16] address pr feedback --- specs/core/0_beacon-chain.md | 155 ++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 76 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 57158fdd4..d919ff824 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -61,7 +61,7 @@ - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`slot_to_epoch`](#slot_to_epoch) - - [`current_epoch`](#current_epoch) + - [`get_current_epoch`](#get_current_epoch) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) @@ -86,7 +86,7 @@ - [`is_double_vote`](#is_double_vote) - [`is_surround_vote`](#is_surround_vote) - [`integer_squareroot`](#integer_squareroot) - - [`entry_exit_effect_epoch`](#entry_exit_effect_epoch) + - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) @@ -192,6 +192,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | - | - | | `GENESIS_FORK_VERSION` | `0` | | `GENESIS_SLOT` | `0` | +| `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` | | `GENESIS_START_SHARD` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | | `ZERO_HASH` | `int_to_bytes32(0)` | @@ -398,8 +399,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be ```python { - # Minimum slot for processing exit - 'slot': 'uint64', + # Minimum epoch for processing exit + 'epoch': 'uint64', # Index of the exiting validator 'validator_index': 'uint24', # Validator signature @@ -569,8 +570,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be 'previous_version': 'uint64', # Current fork version 'current_version': 'uint64', - # Fork slot number - 'slot': 'uint64', + # Fork epoch number + 'epoch': 'uint64', } ``` @@ -715,8 +716,8 @@ Beacon block production is significantly different because of the proof of stake The beacon chain fork choice rule is a hybrid that combines justification and finality with Latest Message Driven (LMD) Greediest Heaviest Observed SubTree (GHOST). At any point in time a [validator](#dfn-validator) `v` subjectively calculates the beacon chain head as follows. * Let `store` be the set of attestations and blocks that the [validator](#dfn-validator) `v` has observed and verified (in particular, block ancestors must be recursively verified). Attestations not part of any chain are still included in `store`. -* Let `finalized_head` be the finalized block with the highest slot number. (A block `B` is finalized if there is a descendant of `B` in `store` the processing of which sets `B` as finalized.) -* Let `justified_head` be the descendant of `finalized_head` with the highest slot number that has been justified for at least `EPOCH_LENGTH` slots. (A block `B` is justified if there is a descendant of `B` in `store` the processing of which sets `B` as justified.) If no such descendant exists set `justified_head` to `finalized_head`. +* Let `finalized_head` be the finalized block with the highest epoch. (A block `B` is finalized if there is a descendant of `B` in `store` the processing of which sets `B` as finalized.) +* Let `justified_head` be the descendant of `finalized_head` with the highest epoch that has been justified for at least 1 epoch. (A block `B` is justified if there is a descendant of `B` in `store` the processing of which sets `B` as justified.) If no such descendant exists set `justified_head` to `finalized_head`. * Let `get_ancestor(store, block, slot)` be the ancestor of `block` with slot number `slot`. The `get_ancestor` function can be defined recursively as `def get_ancestor(store, block, slot): return block if block.slot == slot else get_ancestor(store, store.get_parent(block), slot)`. * Let `get_latest_attestation(store, validator)` be the attestation with the highest slot number in `store` from `validator`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first. * Let `get_latest_attestation_target(store, validator)` be the target block in the attestation `get_latest_attestation(store, validator)`. @@ -726,7 +727,7 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi def lmd_ghost(store, start): validators = start.state.validator_registry active_validators = [validators[i] for i in - get_active_validator_indices(validators, current_epoch(start.state))] + get_active_validator_indices(validators, get_current_epoch(start.state))] attestation_targets = [get_latest_attestation_target(store, validator) for validator in active_validators] def get_vote_count(block): @@ -771,10 +772,10 @@ def slot_to_epoch(slot: int) -> int: return slot // EPOCH_LENGTH ``` -#### `current_epoch` +#### `get_current_epoch` ```python -def current_epoch(state: BeaconState) -> int: +def get_current_epoch(state: BeaconState) -> int: return slot_to_epoch(state.slot) ``` @@ -902,7 +903,7 @@ def get_shuffling(seed: Bytes32, return split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH) ``` -**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= current_epoch(state) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. +**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. **Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work. @@ -936,7 +937,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, """ Returns the list of ``(committee, shard)`` tuples for the ``slot``. """ - current_epoch_slot = current_epoch(state) * EPOCH_LENGTH + current_epoch_slot = get_current_epoch(state) * EPOCH_LENGTH assert current_epoch_slot <= slot + EPOCH_LENGTH assert slot < current_epoch_slot + EPOCH_LENGTH @@ -1004,10 +1005,10 @@ def get_randao_mix(state: BeaconState, def get_active_index_root(state: BeaconState, epoch: int) -> Bytes32: """ - Returns the index root at a recent ``slot``. + Returns the index root at a recent ``epoch``. """ - assert current_epoch(state) < epoch + LATEST_INDEX_ROOTS_LENGTH - assert epoch <= current_epoch(state) + assert get_current_epoch(state) < epoch + LATEST_INDEX_ROOTS_LENGTH + assert epoch <= get_current_epoch(state) return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] ``` @@ -1100,8 +1101,8 @@ def get_effective_balance(state: State, index: int) -> int: ```python def get_fork_version(fork: Fork, - slot: int) -> int: - if slot < fork.slot: + epoch: int) -> int: + if epoch < fork.epoch: return fork.previous_version else: return fork.current_version @@ -1111,11 +1112,11 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, - slot: int, + epoch: int, domain_type: int) -> int: return get_fork_version( fork, - slot + epoch, ) * 2**32 + domain_type ``` @@ -1138,7 +1139,7 @@ def verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVoteData) signature=vote_data.aggregate_signature, domain=get_domain( state.fork, - vote_data.data.slot, + slot_to_epoch(vote_data.data.slot), DOMAIN_ATTESTATION, ), ) @@ -1198,10 +1199,10 @@ def integer_squareroot(n: int) -> int: return x ``` -#### `entry_exit_effect_epoch` +#### `get_entry_exit_effect_epoch` ```python -def entry_exit_effect_epoch(epoch: int) -> int: +def get_entry_exit_effect_epoch(epoch: int) -> int: """ An entry or exit triggered in the ``epoch`` given by the input takes effect at the epoch given by the output. @@ -1255,7 +1256,6 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_time: int, latest_eth1_data: Eth1Data) -> BeaconState: - genesis_epoch = slot_to_epoch(GENESIS_SLOT) state = BeaconState( # Misc slot=GENESIS_SLOT, @@ -1263,13 +1263,13 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], fork=Fork( previous_version=GENESIS_FORK_VERSION, current_version=GENESIS_FORK_VERSION, - slot=GENESIS_SLOT, + epoch=GENESIS_EPOCH, ), # Validator registry validator_registry=[], validator_balances=[], - validator_registry_update_epoch=genesis_epoch, + validator_registry_update_epoch=GENESIS_EPOCH, validator_registry_exit_count=0, # Randomness and committees @@ -1277,8 +1277,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_vdf_outputs=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH // EPOCH_LENGTH)], previous_epoch_start_shard=GENESIS_START_SHARD, current_epoch_start_shard=GENESIS_START_SHARD, - previous_calculation_epoch=genesis_epoch, - current_calculation_epoch=genesis_epoch, + previous_calculation_epoch=GENESIS_EPOCH, + current_calculation_epoch=GENESIS_EPOCH, previous_epoch_seed=ZERO_HASH, current_epoch_seed=ZERO_HASH, @@ -1286,13 +1286,13 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], custody_challenges=[], # Finality - previous_justified_epoch=genesis_epoch, - justified_epoch=genesis_epoch, + previous_justified_epoch=GENESIS_EPOCH, + justified_epoch=GENESIS_EPOCH, justification_bitfield=0, - finalized_epoch=genesis_epoch, + finalized_epoch=GENESIS_EPOCH, # Recent state - latest_crosslinks=[Crosslink(epoch=genesis_epoch, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)], latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)], @@ -1319,8 +1319,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, True) - state.latest_index_roots[genesis_epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, genesis_epoch)) - state.current_epoch_seed = generate_seed(state, genesis_epoch) + state.latest_index_roots[GENESIS_EPOCH % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) + state.current_epoch_seed = generate_seed(state, GENESIS_EPOCH) return state ``` @@ -1346,7 +1346,7 @@ def validate_proof_of_possession(state: BeaconState, signature=proof_of_possession, domain=get_domain( state.fork, - state.slot, + get_current_epoch(state), DOMAIN_DEPOSIT, ) ) @@ -1381,7 +1381,7 @@ def process_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, proposer_slots=0, activation_epoch=FAR_FUTURE_EPOCH, - exit_epch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, withdrawal_epoch=FAR_FUTURE_EPOCH, penalized_epoch=FAR_FUTURE_EPOCH, exit_count=0, @@ -1409,7 +1409,7 @@ Note: All functions in this section mutate `state`. def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: validator = state.validator_registry[index] - validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else entry_exit_effect_epoch(current_epoch(state)) + validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) ``` ```python @@ -1423,10 +1423,10 @@ def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] # The following updates only occur if not previous exited - if validator.exit_epoch <= entry_exit_effect_epoch(current_epoch(state)): + if validator.exit_epoch <= get_entry_exit_effect_epoch(get_current_epoch(state)): return - validator.exit_epoch = entry_exit_effect_epoch(current_epoch(state)) + validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state)) state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count @@ -1436,13 +1436,13 @@ def exit_validator(state: BeaconState, index: int) -> None: def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) validator = state.validator_registry[index] - state.latest_penalized_balances[current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.penalized_epoch = current_epoch(state) + validator.penalized_epoch = get_current_epoch(state) ``` ```python @@ -1459,7 +1459,7 @@ Below are the processing steps that happen at every slot. * Set `state.slot += 1`. * Set `state.validator_registry[get_beacon_proposer_index(state, state.slot)].proposer_slots += 1`. -* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = state.latest_randao_mixes[(state.slot - 1) % LATEST_RANDAO_MIXES_LENGTH]` +* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, state.slot - 1)`. ### Block roots @@ -1479,13 +1479,13 @@ Below are the processing steps that happen at every `block`. * Let `block_without_signature_root` be the `hash_tree_root` of `block` where `block.signature` is set to `EMPTY_SIGNATURE`. * Let `proposal_root = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_without_signature_root))`. -* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message=proposal_root, signature=block.signature, domain=get_domain(state.fork, state.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. ### RANDAO * Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=int_to_bytes32(proposer.proposer_slots), signature=block.randao_reveal, domain=get_domain(state.fork, state.slot, DOMAIN_RANDAO))`. -* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = hash(state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] + block.randao_reveal)`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message=int_to_bytes32(proposer.proposer_slots), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. +* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = hash(get_randao_mix(state, state.slot) + block.randao_reveal)`. ### Eth1 data @@ -1504,9 +1504,9 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot`. * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. -* Verify that `proposer.penalized_epoch > current_epoch(state)`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. +* Verify that `proposer.penalized_epoch > get_current_epoch(state)`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. #### Casper slashings @@ -1524,7 +1524,7 @@ For each `casper_slashing` in `block.body.casper_slashings`: * Verify that `is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)` or `is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_1)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_2)`. -* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > current_epoch(state)`. +* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > get_current_epoch(state)`. #### Attestations @@ -1540,7 +1540,7 @@ For each `attestation` in `block.body.attestations`: * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)`. * Let `group_public_key = bls_aggregate_pubkeys([state.validator_registry[v].pubkey for v in participants])`. - * Verify that `bls_verify(pubkey=group_public_key, message=hash_tree_root(AttestationDataAndCustodyBit(attestation.data, False)), signature=attestation.aggregate_signature, domain=get_domain(state.fork, attestation.data.slot, DOMAIN_ATTESTATION))`. + * Verify that `bls_verify(pubkey=group_public_key, message=hash_tree_root(AttestationDataAndCustodyBit(attestation.data, False)), signature=attestation.aggregate_signature, domain=get_domain(state.fork, slot_to_epoch(attestation.data.slot), DOMAIN_ATTESTATION))`. * [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`. * Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`. @@ -1586,10 +1586,10 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.exit_epoch > entry_exit_effect_epoch(current_epoch(state))`. -* Verify that `state.slot >= exit.slot`. -* Let `exit_message = hash_tree_root(Exit(slot=exit.slot, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.slot, DOMAIN_EXIT))`. +* Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. +* Verify that `get_current_epoch(state) >= exit.epoch`. +* Let `exit_message = hash_tree_root(Exit(epoch=exit.epoch, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. +* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. #### Custody @@ -1602,15 +1602,16 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. ### Helpers -* Let `current_epoch_start_slot = current_epoch(state) * EPOCH_LENGTH`. -* Let `next_epoch = current_epoch(state) + 1`. -* Let `next_epoch_start_slot = next_epoch * EPOCH_LENGTH`. -* Let `previous_epoch = current_epoch(state) - 1 if current_epoch(state) > slot_to_epoch(GENESIS_SLOT) else current_epoch(state)`. +* Let `current_epoch = get_current_epoch(state)`. +* Let `current_epoch_start_slot = current_epoch * EPOCH_LENGTH`. +* Let `previous_epoch = current_epoch - 1 if current_epoch > slot_to_epoch(GENESIS_SLOT) else current_epoch`. * Let `previous_epoch_start_slot = previous_epoch * EPOCH_LENGTH`. +* Let `next_epoch = current_epoch + 1`. +* Let `next_epoch_start_slot = next_epoch * EPOCH_LENGTH`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state))`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch)`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1657,7 +1658,7 @@ Define the following helpers to process attestation inclusion rewards and inclus ### Eth1 data -If `current_epoch(state) % ETH1_DATA_VOTING_PERIOD == 0`: +If `current_epoch % ETH1_DATA_VOTING_PERIOD == 0`: * Set `state.latest_eth1_data = eth1_data_vote.data` if `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes`. * Set `state.eth1_data_votes = []`. @@ -1669,7 +1670,7 @@ First, update the justification bitfield: * Let `new_justified_epoch = state.justified_epoch`. * Set `state.justification_bitfield = (state.justification_bitfield * 2) % 2**64`. * Set `state.justification_bitfield |= 2` and `new_justified_epoch = previous_epoch` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. -* Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch(state)` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. +* Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. Next, update last finalized slot if possible: @@ -1687,7 +1688,7 @@ Finally, update the following: For every `slot in range(previous_epoch_start_slot, next_epoch_start_slot)`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: -* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch(state), shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. +* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. ### Rewards and penalties @@ -1722,7 +1723,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch(state)`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, 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 @@ -1773,8 +1774,9 @@ def update_validator_registry(state: BeaconState) -> None: Update validator registry. Note that this function mutates ``state``. """ + current_epoch = get_current_epoch(state) # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state)) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1787,7 +1789,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > entry_exit_effect_epoch(current_epoch(state)) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch > get_entry_exit_effect_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1799,7 +1801,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > entry_exit_effect_epoch(current_epoch(state)) and validator.status_flags & INITIATED_EXIT: + if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.status_flags & INITIATED_EXIT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1808,7 +1810,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - state.validator_registry_update_epoch = current_epoch(state) + state.validator_registry_update_epoch = current_epoch ``` and perform the following updates: @@ -1819,7 +1821,7 @@ and perform the following updates: If a validator registry update does _not_ happen do the following: -* Let `epochs_since_last_registry_change = current_epoch(state) - state.validator_registry_update_epoch`. +* Let `epochs_since_last_registry_change = current_epoch - state.validator_registry_update_epoch`. * If `epochs_since_last_registry_change` is an exact power of 2: * Set `state.current_calculation_epoch = next_epoch`. * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` @@ -1831,14 +1833,15 @@ Regardless of whether or not a validator set change happens, run the following: ```python def process_penalties_and_exits(state: BeaconState) -> None: + current_epoch = get_current_epoch(state) # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch(state)) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) for index, validator in enumerate(state.validator_registry): - if current_epoch(state) == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: - e = current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH + if current_epoch == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: + e = current_epoch % LATEST_PENALIZED_EXIT_LENGTH total_at_start = state.latest_penalized_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_balances[e] total_penalties = total_at_end - total_at_start @@ -1847,11 +1850,11 @@ def process_penalties_and_exits(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] - if validator.penalized_epoch <= current_epoch(state): + if validator.penalized_epoch <= current_epoch: PENALIZED_WITHDRAWAL_EPOCHS = LATEST_PENALIZED_EXIT_LENGTH // 2 - return current_epoch(state) >= validator.penalized_epoch + PENALIZED_WITHDRAWAL_EPOCHS + return current_epoch >= validator.penalized_epoch + PENALIZED_WITHDRAWAL_EPOCHS else: - return current_epoch(state) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS + return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) @@ -1866,8 +1869,8 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates -* Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH]`. -* Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch(state)`. +* Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch % LATEST_PENALIZED_EXIT_LENGTH]`. +* Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch`. ## State root processing From 8da467df7ca72aef010f3130d3bf2f03e2b5ae01 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 15:39:57 -0700 Subject: [PATCH 07/16] pr feedback --- specs/core/0_beacon-chain.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d919ff824..3344d295d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -937,11 +937,14 @@ def get_crosslink_committees_at_slot(state: BeaconState, """ Returns the list of ``(committee, shard)`` tuples for the ``slot``. """ - current_epoch_slot = get_current_epoch(state) * EPOCH_LENGTH - assert current_epoch_slot <= slot + EPOCH_LENGTH - assert slot < current_epoch_slot + EPOCH_LENGTH + epoch = slot_to_epoch(slot) + current_epoch = get_current_epoch(state) + previous_epoch = current_epoch - 1 if epoch > GENESIS_EPOCH else current_epoch + next_epoch = current_epoch + 1 - if slot < current_epoch_slot: + assert previous_epoch <= epoch < next_epoch + + if epoch < current_epoch: committees_per_slot = get_previous_epoch_committee_count_per_slot(state) seed = state.previous_epoch_seed shuffling_epoch = state.previous_calculation_epoch From e1e1e2359a46e46c19ab7f68c92f70e94704ff49 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 07:54:46 -0700 Subject: [PATCH 08/16] pr feedback --- specs/core/0_beacon-chain.md | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 225c7addc..97e7a7dd1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -63,6 +63,7 @@ - [`hash_tree_root`](#hash_tree_root) - [`slot_to_epoch`](#slot_to_epoch) - [`get_current_epoch`](#get_current_epoch) + - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) @@ -794,17 +795,23 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere #### `slot_to_epoch` ```python -def slot_to_epoch(slot: int) -> int: +def slot_to_epoch(slot: SlotNumber) -> EpochNumber: return slot // EPOCH_LENGTH ``` #### `get_current_epoch` ```python -def get_current_epoch(state: BeaconState) -> int: +def get_current_epoch(state: BeaconState) -> EpochNumber: return slot_to_epoch(state.slot) ``` +#### `get_epoch_start_slot` + +```python +def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: + return slot_to_epoch(state.slot) +``` #### `is_active_validator` ```python @@ -1050,12 +1057,12 @@ def generate_seed(state: BeaconState, Generate a seed for the given ``epoch``. """ if epoch < SEED_LOOKAHEAD: - randao_mix_epoch = slot_to_epoch(GENESIS_SLOT) + randao_mix_epoch = GENESIS_EPOCH else: randao_mix_epoch = epoch - SEED_LOOKAHEAD return hash( - get_randao_mix(state, randao_mix_epoch * EPOCH_LENGTH) + + get_randao_mix(state, get_epoch_start_slot(randao_mix_epoch)) + get_active_index_root(state, epoch) ) @@ -1437,7 +1444,7 @@ Note: All functions in this section mutate `state`. def activate_validator(state: BeaconState, index: ValidatorIndex, genesis: bool) -> None: validator = state.validator_registry[index] - validator.activation_epoch = slot_to_epoch(GENESIS_SLOT) if genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.activation_epoch = GENESIS_EPOCH if genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) ``` ```python @@ -1562,7 +1569,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. * Verify that `attestation.data.slot + EPOCH_LENGTH >= state.slot`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_epoch`. -* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, attestation.data.justified_epoch * EPOCH_LENGTH)`. +* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. * Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`. * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)`. @@ -1630,11 +1637,8 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. ### Helpers * Let `current_epoch = get_current_epoch(state)`. -* Let `current_epoch_start_slot = current_epoch * EPOCH_LENGTH`. -* Let `previous_epoch = current_epoch - 1 if current_epoch > slot_to_epoch(GENESIS_SLOT) else current_epoch`. -* Let `previous_epoch_start_slot = previous_epoch * EPOCH_LENGTH`. +* Let `previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch`. * Let `next_epoch = current_epoch + 1`. -* Let `next_epoch_start_slot = next_epoch * EPOCH_LENGTH`. All [validators](#dfn-validator): @@ -1643,23 +1647,23 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) attesting during the current epoch: -* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch_start_slot <= a.data.slot < next_epoch_start_slot]`. (Note: this is the set of attestations of slots in the epoch `current_epoch_start_slot...next_epoch_start_slot`, _not_ attestations that got included in the chain during the epoch `current_epoch_start_slot...next_epoch_start_slot`.) +* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) * 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, current_epoch_start_slot) and a.data.justified_epoch == state.justified_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)) and a.data.justified_epoch == state.justified_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 = sum([get_effective_balance(state, i) for i in current_epoch_boundary_attester_indices])`. [Validators](#dfn-Validator) attesting during the previous epoch: * Validators that made an attestation during the previous epoch: - * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch_start_slot <= a.data.slot < current_epoch_start_slot]`. + * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. * 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]`. * Validators targeting the previous justified slot: * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_epoch == state.previous_justified_epoch]`. * Let `previous_epoch_justified_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_justified_attestations]`. * Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_justified_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_justified_attestations if a.data.epoch_boundary_root == get_block_root(state, previous_epoch_start_slot)]`. + * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_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 = sum([get_effective_balance(state, i) for i in previous_epoch_boundary_attester_indices])`. * Validators attesting to the expected beacon chain head during the previous epoch: @@ -1669,7 +1673,7 @@ All [validators](#dfn-validator): **Note**: `previous_epoch_boundary_attesting_balance` balance might be marginally different than `current_epoch_boundary_attesting_balance` 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. -For every `slot in range(previous_epoch_start_slot, next_epoch_start_slot)`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +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: * Let `shard_block_root` be `state.latest_crosslinks[shard].shard_block_root` * Let `attesting_validator_indices(crosslink_committee, shard_block_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.shard_block_root == shard_block_root]`. @@ -1699,7 +1703,7 @@ First, update the justification bitfield: * Set `state.justification_bitfield |= 2` and `new_justified_epoch = previous_epoch` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. * Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. -Next, update last finalized slot if possible: +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`. @@ -1713,7 +1717,7 @@ Finally, update the following: ### Crosslinks -For every `slot in range(previous_epoch_start_slot, next_epoch_start_slot)`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +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: * Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. @@ -1759,7 +1763,7 @@ For each `index` in `previous_epoch_attester_indices`, we determine the proposer #### Crosslinks -For every `slot in range(previous_epoch_start_slot, current_epoch_start_slot)`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // total_balance(crosslink_committee))`. * If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. From 9d18760521ae05c1edfd0ca6a1fb104c02506af9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 09:56:26 -0700 Subject: [PATCH 09/16] convert some functions to be per epoch --- specs/core/0_beacon-chain.md | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 97e7a7dd1..2ead71784 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -68,10 +68,10 @@ - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) - [`split`](#split) - - [`get_committee_count_per_slot`](#get_committee_count_per_slot) + - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) - - [`get_previous_epoch_committee_count_per_slot`](#get_previous_epoch_committee_count_per_slot) - - [`get_current_epoch_committee_count_per_slot`](#get_current_epoch_committee_count_per_slot) + - [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count) + - [`get_current_epoch_committee_count`](#get_current_epoch_committee_count) - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) - [`get_block_root`](#get_block_root) - [`get_randao_mix`](#get_randao_mix) @@ -899,17 +899,17 @@ def split(values: List[Any], split_count: int) -> List[List[Any]]: ] ``` -#### `get_committee_count_per_slot` +#### `get_epoch_committee_count` ```python -def get_committee_count_per_slot(active_validator_count: int) -> int: +def get_epoch_committee_count(active_validator_count: int) -> int: return max( 1, min( SHARD_COUNT // EPOCH_LENGTH, active_validator_count // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, ) - ) + ) * EPOCH_LENGTH ``` #### `get_shuffling` @@ -926,40 +926,40 @@ def get_shuffling(seed: Bytes32, active_validator_indices = get_active_validator_indices(validators, epoch) - committees_per_slot = get_committee_count_per_slot(len(active_validator_indices)) + committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle seed = xor(seed, int_to_bytes32(epoch)) shuffled_active_validator_indices = shuffle(active_validator_indices, seed) - # Split the shuffled list into epoch_length * committees_per_slot pieces - return split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH) + # Split the shuffled list into committees_per_epoch pieces + return split(shuffled_active_validator_indices, committees_per_epoch) ``` **Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. **Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work. -#### `get_previous_epoch_committee_count_per_slot` +#### `get_previous_epoch_committee_count` ```python -def get_previous_epoch_committee_count_per_slot(state: BeaconState) -> int: +def get_previous_epoch_committee_count(state: BeaconState) -> int: previous_active_validators = get_active_validator_indices( state.validator_registry, state.previous_calculation_epoch, ) - return get_committee_count_per_slot(len(previous_active_validators)) + return get_epoch_committee_count(len(previous_active_validators)) ``` -#### `get_current_epoch_committee_count_per_slot` +#### `get_current_epoch_committee_count` ```python -def get_current_epoch_committee_count_per_slot(state: BeaconState) -> int: +def get_current_epoch_committee_count(state: BeaconState) -> int: current_active_validators = get_active_validator_indices( state.validator_registry, state.current_calculation_epoch, ) - return get_committee_count_per_slot(len(current_active_validators)) + return get_epoch_committee_count(len(current_active_validators)) ``` #### `get_crosslink_committees_at_slot` @@ -978,12 +978,12 @@ def get_crosslink_committees_at_slot(state: BeaconState, assert previous_epoch <= epoch < next_epoch if epoch < current_epoch: - committees_per_slot = get_previous_epoch_committee_count_per_slot(state) + committees_per_epoch = get_previous_epoch_committee_count(state) seed = state.previous_epoch_seed shuffling_epoch = state.previous_calculation_epoch shuffling_start_shard = state.previous_epoch_start_shard else: - committees_per_slot = get_current_epoch_committee_count_per_slot(state) + committees_per_epoch = get_current_epoch_committee_count(state) seed = state.current_epoch_seed shuffling_epoch = state.current_calculation_epoch shuffling_start_shard = state.current_epoch_start_shard @@ -994,6 +994,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, shuffling_epoch, ) offset = slot % EPOCH_LENGTH + committees_per_slot = committees_per_epoch // EPOCH_LENGTH slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT return [ @@ -1795,7 +1796,7 @@ First, update the following: 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_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH)]` (that is, for every shard in the current committees) +* `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_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 @@ -1847,7 +1848,7 @@ def update_validator_registry(state: BeaconState) -> None: and perform the following updates: * Set `state.current_calculation_epoch = next_epoch` -* Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) % SHARD_COUNT` +* Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count(state)) % SHARD_COUNT` * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` If a validator registry update does _not_ happen do the following: From 12619995bbe05fde92e1be5f239a9fae583dfe23 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 10:00:03 -0700 Subject: [PATCH 10/16] pr feedback --- 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 2ead71784..44ca758b4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -810,7 +810,7 @@ def get_current_epoch(state: BeaconState) -> EpochNumber: ```python def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: - return slot_to_epoch(state.slot) + return epoch * EPOCH_LENGTH ``` #### `is_active_validator` @@ -920,7 +920,7 @@ def get_shuffling(seed: Bytes32, epoch: EpochNumber) -> List[List[ValidatorIndex]] """ Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. - Returns a list of ``EPOCH_LENGTH * committees_per_slot`` committees where each + Returns a list of ``committees_per_epoch`` committees where each committee is itself a list of validator indices. """ @@ -1053,7 +1053,7 @@ def get_active_index_root(state: BeaconState, ```python def generate_seed(state: BeaconState, - epoch: int) -> Bytes32: + epoch: EpochNumber) -> Bytes32: """ Generate a seed for the given ``epoch``. """ From 8fbaa255963b03236b665d4b5d65379862ec2c6e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 10:01:04 -0700 Subject: [PATCH 11/16] pr feedback --- 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 44ca758b4..95289793d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1239,7 +1239,7 @@ def integer_squareroot(n: int) -> int: #### `get_entry_exit_effect_epoch` ```python -def get_entry_exit_effect_epoch(epoch: int) -> int: +def get_entry_exit_effect_epoch(epoch: EpochNumber) -> EpochNumber: """ An entry or exit triggered in the ``epoch`` given by the input takes effect at the epoch given by the output. From e41eeeedb61e45a154c0559949dc7308e7f93d93 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 10:22:27 -0700 Subject: [PATCH 12/16] make randao_mixes per epoch --- specs/core/0_beacon-chain.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 95289793d..068584fa5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -171,9 +171,9 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | -| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots | -| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes | -| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | index roots | +| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | +| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | +| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | | `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | @@ -1027,13 +1027,13 @@ def get_block_root(state: BeaconState, ```python def get_randao_mix(state: BeaconState, - slot: SlotNumber) -> Bytes32: + epoch: EpochNumber) -> Bytes32: """ Returns the randao mix at a recent ``slot``. """ - assert state.slot < slot + LATEST_RANDAO_MIXES_LENGTH - assert slot <= state.slot - return state.latest_randao_mixes[slot % LATEST_RANDAO_MIXES_LENGTH] + assert get_current_epoch(state) < epoch + LATEST_RANDAO_MIXES_LENGTH + assert epoch <= get_current_epoch(state) + return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] ``` #### `get_active_index_root` @@ -1494,7 +1494,6 @@ Below are the processing steps that happen at every slot. ### Misc counters * Set `state.slot += 1`. -* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, state.slot - 1)`. ### Block roots @@ -1520,7 +1519,7 @@ Below are the processing steps that happen at every `block`. * Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. -* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = xor(get_randao_mix(state, state.slot), hash(block.randao_reveal))`. +* 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))`. ### Eth1 data @@ -1902,6 +1901,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates * Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch % LATEST_PENALIZED_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`. ## State root processing From b0e71f4c80e0d3a82d6c5a62220c6ccbaa002b90 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 10:23:28 -0700 Subject: [PATCH 13/16] pr feedback --- 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 068584fa5..6659ee3f4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1689,7 +1689,7 @@ Define the following helpers to process attestation inclusion rewards and inclus ### Eth1 data -If `current_epoch % ETH1_DATA_VOTING_PERIOD == 0`: +If `next_epoch % ETH1_DATA_VOTING_PERIOD == 0`: * Set `state.latest_eth1_data = eth1_data_vote.data` if `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes`. * Set `state.eth1_data_votes = []`. From ad46b9cb1248f2d1023899b4a2fe4fd033e53389 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 10:31:01 -0700 Subject: [PATCH 14/16] pr feedback --- 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 6659ee3f4..35435ed7f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -774,7 +774,7 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) We now define the state transition function. At a high level the state transition is made up of two parts: 1. The per-slot transitions, which happens every slot, and only affects a parts of the `state`. -2. The per-epoch transitions, which happens at every epoch boundary (i.e. `state.slot % EPOCH_LENGTH == 0`), and affects the entire `state`. +2. The per-epoch transitions, which happens in the last slot of every epoch (i.e. `(state.slot + 1) % EPOCH_LENGTH == 0`), and affects the entire `state`. The per-slot transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-slot activity in the `BeaconState`. The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization. @@ -1057,13 +1057,10 @@ def generate_seed(state: BeaconState, """ Generate a seed for the given ``epoch``. """ - if epoch < SEED_LOOKAHEAD: - randao_mix_epoch = GENESIS_EPOCH - else: - randao_mix_epoch = epoch - SEED_LOOKAHEAD + randao_mix_epoch = epoch + LATEST_RANDAO_MIXES_LENGTH - SEED_LOOKAHEAD return hash( - get_randao_mix(state, get_epoch_start_slot(randao_mix_epoch)) + + get_randao_mix(state, randao_mix_epoch) + get_active_index_root(state, epoch) ) @@ -1568,7 +1565,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. * Verify that `attestation.data.slot + EPOCH_LENGTH >= state.slot`. -* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_epoch`. +* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. * Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`. * `aggregate_signature` verification: From f9b53e858369f6c889f3a99f4850a02b34f51e0f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 17:20:15 -0700 Subject: [PATCH 15/16] pr feedback --- specs/core/0_beacon-chain.md | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 35435ed7f..21744ecbc 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -96,10 +96,10 @@ - [Routine for processing deposits](#routine-for-processing-deposits) - [Routines for updating validator status](#routines-for-updating-validator-status) - [Per-slot processing](#per-slot-processing) - - [Misc counters](#misc-counters) + - [Slot](#slot) - [Block roots](#block-roots) - [Per-block processing](#per-block-processing) - - [Slot](#slot) + - [Slot](#slot-1) - [Proposer signature](#proposer-signature) - [RANDAO](#randao) - [Eth1 data](#eth1-data) @@ -1488,7 +1488,7 @@ def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) Below are the processing steps that happen at every slot. -### Misc counters +### Slot * Set `state.slot += 1`. @@ -1637,13 +1637,9 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. * Let `previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch`. * Let `next_epoch = current_epoch + 1`. -All [validators](#dfn-validator): - -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch)`. -* Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. - [Validators](#dfn-Validator) attesting during the current epoch: +* Let `current_total_balance = sum([get_effective_balance(state, i) for i in 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: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) * 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)) and a.data.justified_epoch == state.justified_epoch]`. @@ -1652,6 +1648,7 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) attesting during the previous epoch: +* Let `previous_total_balance = sum([get_effective_balance(state, i) for i in get_active_validator_indices(state.validator_registry, previous_epoch)])`. * Validators that made an attestation during the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. * 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]`. @@ -1668,7 +1665,7 @@ All [validators](#dfn-validator): * 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 = sum([get_effective_balance(state, i) for i in previous_epoch_head_attester_indices])`. -**Note**: `previous_epoch_boundary_attesting_balance` balance might be marginally different than `current_epoch_boundary_attesting_balance` 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. +**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. 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: @@ -1696,9 +1693,9 @@ If `next_epoch % ETH1_DATA_VOTING_PERIOD == 0`: First, update the justification bitfield: * Let `new_justified_epoch = state.justified_epoch`. -* Set `state.justification_bitfield = (state.justification_bitfield * 2) % 2**64`. -* Set `state.justification_bitfield |= 2` and `new_justified_epoch = previous_epoch` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. -* Set `state.justification_bitfield |= 1` and `new_justified_epoch = current_epoch` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. +* 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`. Next, update last finalized epoch if possible: @@ -1722,7 +1719,7 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s First, we define some additional helpers: -* Let `base_reward_quotient = integer_squareroot(total_balance) // BASE_REWARD_QUOTIENT`. +* 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`. @@ -1735,13 +1732,13 @@ Note: When applying penalties in the following balance recalculations implemente Case 1: `epochs_since_finality <= 4`: * Expected FFG source: - * Any [validator](#dfn-validator) `index` in `previous_epoch_justified_attester_indices` gains `base_reward(state, index) * previous_epoch_justified_attesting_balance // total_balance`. + * Any [validator](#dfn-validator) `index` in `previous_epoch_justified_attester_indices` gains `base_reward(state, index) * previous_epoch_justified_attesting_balance // previous_total_balance`. * Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_justified_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 // total_balance`. + * 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` 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 // total_balance)`. + * 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` 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)` From 1947fc0ff32b522f1c0d8bff9159a9f90848bc71 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 17:25:29 -0700 Subject: [PATCH 16/16] pr feedback --- specs/core/0_beacon-chain.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 21744ecbc..10a41506f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -15,6 +15,7 @@ - [Deposit contract](#deposit-contract) - [Initial values](#initial-values) - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) - [Reward and penalty quotients](#reward-and-penalty-quotients) - [Status flags](#status-flags) - [Max operations per block](#max-operations-per-block) @@ -171,10 +172,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | -| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | -| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | -| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | -| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -213,6 +210,14 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | +### State list lengths + +| Name | Value | Unit | +| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | +| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | +| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | +| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | + ### Reward and penalty quotients | Name | Value | @@ -1757,10 +1762,12 @@ For each `index` in `previous_epoch_attester_indices`, we determine the proposer #### Crosslinks -For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: +For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch))`: -* If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // total_balance(crosslink_committee))`. -* If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. +* Let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. +* For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`: + * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // total_balance(crosslink_committee))`. + * If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. ### Ejections