From a5519a7d3b5c0b675026a2daef9477f06ae795a6 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 22 Jan 2019 17:14:25 +0100 Subject: [PATCH 01/31] SSZ: List serialization with variable element size Fix #232 --- specs/simple-serialize.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 8630c47c6..cca2969e6 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -143,9 +143,9 @@ Lists are a collection of elements of the same homogeneous type. | Length of serialized list fits into 4 bytes | ``len(serialized) < 2**32`` | -1. Get the number of raw bytes to serialize: it is ``len(list) * sizeof(element)``. - * Encode that as a `4-byte` **little endian** `uint32`. -2. Append the elements in a packed manner. +1. Serialize all list elements individually and concatenate them. + +2. Prefix the concatenation with its length encoded as a `4-byte` **little-endian** unsigned integer. * *Note on efficiency*: consider using a container that does not need to iterate over all elements to get its length. For example Python lists, C++ vectors or Rust Vec. From f8670e80692c02a1de4be41bdf9b9ca94948419f Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Tue, 22 Jan 2019 17:16:37 +0100 Subject: [PATCH 02/31] SSZ: Remove note on efficiency --- specs/simple-serialize.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index cca2969e6..eff65f49e 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -147,8 +147,6 @@ Lists are a collection of elements of the same homogeneous type. 2. Prefix the concatenation with its length encoded as a `4-byte` **little-endian** unsigned integer. -* *Note on efficiency*: consider using a container that does not need to iterate over all elements to get its length. For example Python lists, C++ vectors or Rust Vec. - **Example in Python** ```python From dd075edd06e5069b38a0cd929f220db95765472d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 23 Jan 2019 15:30:05 -0600 Subject: [PATCH 03/31] epoch trans at end of epoch and fix finality conditions --- specs/core/0_beacon-chain.md | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d58d49df8..cedd48172 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1570,10 +1570,13 @@ For each `exit` in `block.body.exits`: ## Per-epoch processing -The steps below happen when `state.slot % EPOCH_LENGTH == 0`. +The steps below happen when `state.slot % EPOCH_LENGTH == EPOCH_LENGTH - 1`. ### Helpers +* 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`. + All [validators](#dfn-validator): * Let `active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)`. @@ -1581,23 +1584,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 state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]`. (Note: this is the set of attestations of slots in the epoch `state.slot-EPOCH_LENGTH...state.slot-1`, _not_ attestations that got included in the chain during the epoch `state.slot-EPOCH_LENGTH...state.slot-1`.) +* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch_start_slot <= a.data.slot <= state.slot]`. (Note: this is the set of attestations of slots in the epoch `current_epoch_start_slot...state.slot`, _not_ attestations that got included in the chain during the epoch `current_epoch_start_slot...state.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, state.slot-EPOCH_LENGTH) 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_slot == state.justified_slot]`. * 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 state.slot - 2 * EPOCH_LENGTH <= a.data.slot < state.slot - EPOCH_LENGTH]`. + * 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_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, state.slot - 2 * EPOCH_LENGTH)]`. + * 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_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: @@ -1623,23 +1626,31 @@ Define the following helpers to process attestation inclusion rewards and inclus ### Eth1 data -If `state.slot % ETH1_DATA_VOTING_PERIOD == 0`: +If `state.slot % ETH1_DATA_VOTING_PERIOD == ETH1_DATA_VOTING_PERIOD - 1`: * 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.eth1_data_votes = []`. ### Justification -* Set `state.previous_justified_slot = state.justified_slot`. +First, update the justification bitfield: + +* Let `new_justified_slot = state.justified_slot`. * Set `state.justification_bitfield = (state.justification_bitfield * 2) % 2**64`. -* Set `state.justification_bitfield |= 2` and `state.justified_slot = state.slot - 2 * EPOCH_LENGTH` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. -* Set `state.justification_bitfield |= 1` and `state.justified_slot = state.slot - 1 * EPOCH_LENGTH` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. +* 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.finalized_slot = state.previous_justified_slot` if any of the following are true: +Next, update last finalized slot if possible: -* `state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3` -* `state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and state.justification_bitfield % 8 == 7` -* `state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and state.justification_bitfield % 16 in (15, 14)` +* 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_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.previous_justified_slot` if `(state.justification_bitfield >> 1) % 8 == 0b111 and state.previous_justified_slot == previous_epoch_start_slot - 2 * EPOCH_LENGTH`. + +Finally, update the following: + +* Set `state.previous_justified_slot = state.justified_slot`. +* Set `state.justified_slot = justified_slot`. ### Crosslinks @@ -1819,7 +1830,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates * Let `e = state.slot // EPOCH_LENGTH`. Set `state.latest_penalized_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[e % LATEST_PENALIZED_EXIT_LENGTH]` -* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. +* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < current_epoch_start_slot`. ## State root processing From b18de2c86cdd812d072728ee094b3f7915474460 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 23 Jan 2019 15:41:25 -0600 Subject: [PATCH 04/31] clena up get_crosslink_committees_at_slots --- specs/core/0_beacon-chain.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cedd48172..013676f55 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -942,24 +942,25 @@ def get_crosslink_committees_at_slot(state: BeaconState, state_epoch_slot = state.slot - (state.slot % EPOCH_LENGTH) assert state_epoch_slot <= slot + EPOCH_LENGTH assert slot < state_epoch_slot + EPOCH_LENGTH - offset = slot % EPOCH_LENGTH if slot < state_epoch_slot: committees_per_slot = get_previous_epoch_committee_count_per_slot(state) - shuffling = get_shuffling( - state.previous_epoch_randao_mix, - state.validator_registry, - state.previous_epoch_calculation_slot, - ) - slot_start_shard = (state.previous_epoch_start_shard + committees_per_slot * offset) % SHARD_COUNT + seed = state.previous_epoch_randao_mix + shuffling_slot = state.previous_epoch_calculation_slot + shuffling_start_shard = state.previous_epoch_start_shard else: committees_per_slot = get_current_epoch_committee_count_per_slot(state) - shuffling = get_shuffling( - state.current_epoch_randao_mix, - state.validator_registry, - state.current_epoch_calculation_slot, - ) - slot_start_shard = (state.current_epoch_start_shard + committees_per_slot * offset) % SHARD_COUNT + seed = state.current_epoch_randao_mix + shuffling_slot = state.current_epoch_calculation_slot + shuffling_start_shard = state.current_epoch_start_shard + + shuffling = get_shuffling( + seed, + state.validator_registry, + shuffling_slot, + ) + offset = slot % EPOCH_LENGTH + slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT return [ ( From 1aceaeb793f620de01087fee1221029f486dcd6e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 23 Jan 2019 17:14:38 -0600 Subject: [PATCH 05/31] reverse ordering of finality conditions --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 013676f55..dfb41ab47 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1643,10 +1643,10 @@ First, update the justification bitfield: Next, update last finalized slot if possible: -* 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_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.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.justified_slot` if `(state.justification_bitfield >> 0) % 4 == 0b11 and state.justified_slot == previous_epoch_start_slot`. Finally, update the following: From df85857c2edb700f9882eae888561a6eb99519b6 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 23 Jan 2019 20:35:26 -0600 Subject: [PATCH 06/31] A few more state.slot -> current/previous/next epoch start slot changes --- specs/core/0_beacon-chain.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dfb41ab47..2fa4c151c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1575,12 +1575,13 @@ 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`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch_start_slot)`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1611,7 +1612,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(state.slot - 2 * EPOCH_LENGTH, state.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(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: * 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]`. @@ -1655,9 +1656,9 @@ Finally, update the following: ### Crosslinks -For every `slot in range(state.slot - 2 * EPOCH_LENGTH, state.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(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=state.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(slot=current_epoch_start_slot, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. ### Rewards and penalties @@ -1671,7 +1672,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 = (state.slot - state.finalized_slot) // EPOCH_LENGTH`. +* Let `epochs_since_finality = (next_epoch_start_slot - state.finalized_slot) // EPOCH_LENGTH`. Case 1: `epochs_since_finality <= 4`: @@ -1692,7 +1693,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 <= state.slot`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + 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 [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 @@ -1701,7 +1702,7 @@ For each `index` in `previous_epoch_attester_indices`, we determine the proposer #### Crosslinks -For every `slot in range(state.slot - 2 * EPOCH_LENGTH, state.slot - EPOCH_LENGTH)`, 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(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: * 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)`. @@ -1716,7 +1717,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, state.slot): + for index in get_active_validator_indices(state.validator_registry, current_epoch_start_slot): if state.validator_balances[index] < EJECTION_BALANCE: exit_validator(state, index) ``` @@ -1743,7 +1744,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, state.slot) + active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch_start_slot) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) From ddf420ba3981e16225d708600960eb2d7843f08b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 16:07:54 -0700 Subject: [PATCH 07/31] work through issues with epochs, seeds, etc) --- specs/core/0_beacon-chain.md | 46 ++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8addc31a4..f924d2dc7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -72,6 +72,7 @@ - [`get_block_root`](#get_block_root) - [`get_randao_mix`](#get_randao_mix) - [`get_active_index_root`](#get_active_index_root) + - [`generate_seed`](#generate_seed) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`merkle_root`](#merkle_root) - [`get_attestation_participants`](#get_attestation_participants) @@ -998,6 +999,27 @@ def get_active_index_root(state: BeaconState, return state.latest_index_roots[given_epoch % LATEST_INDEX_ROOTS_LENGTH] ``` +#### `generate_seed` + +```python +def generate_seed(state: BeaconState, + slot: int) -> Bytes32: + """ + Generate a seed for the given ``slot``. + """ + if slot < SEED_LOOKAHEAD: + randao_mix_slot = GENESIS_SLOT + else: + randao_mix_slot = slot - SEED_LOOKAHEAD + + return hash( + get_randao_mix(state, randao_mix_slot) + + get_active_index_root(state, slot) + ) + +``` + + #### `get_beacon_proposer_index` ```python @@ -1285,6 +1307,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) + return state ``` @@ -1568,6 +1593,8 @@ The steps below happen when `state.slot % EPOCH_LENGTH == EPOCH_LENGTH - 1`. * 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`. All [validators](#dfn-validator): @@ -1576,7 +1603,7 @@ 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 <= state.slot]`. (Note: this is the set of attestations of slots in the epoch `current_epoch_start_slot...state.slot`, _not_ attestations that got included in the chain during the epoch `current_epoch_start_slot...state.slot`.) +* 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_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]`. @@ -1719,7 +1746,7 @@ 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[epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, state.slot))` +* 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: @@ -1769,21 +1796,21 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - state.validator_registry_update_slot = state.slot + state.validator_registry_update_slot = state.slot - (state.slot % EPOCH_LENGTH) ``` and perform the following updates: -* Set `state.current_epoch_calculation_slot = state.slot` +* Set `state.current_epoch_calculation_slot = next_epoch_start_slot` * 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 = hash(get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD) + get_active_index_root(state, state.current_epoch_calculation_slot))` +* Set `state.current_epoch_seed = generate_seed(state, state.current_epoch_calculation_slot)` If a validator registry update does _not_ happen do the following: -* Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_update_slot) // EPOCH_LENGTH`. +* Let `epochs_since_last_registry_change = (current_epoch_start_slot - state.validator_registry_update_slot) // EPOCH_LENGTH`. * If `epochs_since_last_registry_change` is an exact power of 2: - * Set `state.current_epoch_calculation_slot = state.slot`. - * Set `state.current_epoch_seed = hash(get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD) + get_active_index_root(state, state.current_epoch_calculation_slot))`. + * Set `state.current_epoch_calculation_slot = next_epoch_start_slot`. + * Set `state.current_epoch_seed = generate_seed(state, state.current_epoch_calculation_slot)` * _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. @@ -1827,8 +1854,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: ### Final updates -* Let `epoch = state.slot // EPOCH_LENGTH`. -* Set `state.latest_penalized_balances[(epoch+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[epoch % LATEST_PENALIZED_EXIT_LENGTH]`. +* 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 `attestation.data.slot < current_epoch_start_slot`. ## State root processing From 6f830179c6b397823b0aa4776a64620b65e35f67 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 16:50:15 -0700 Subject: [PATCH 08/31] remove extra blank line --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f924d2dc7..ed0ecf444 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1019,7 +1019,6 @@ def generate_seed(state: BeaconState, ``` - #### `get_beacon_proposer_index` ```python From e34cfc8de8f9663a9431dcda3ca52c87bf190321 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 17:26:27 -0700 Subject: [PATCH 09/31] fix preivous_epoch_start_slot to be based upon GENESIS_SLOT --- 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 ed0ecf444..20879dd41 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1591,7 +1591,7 @@ The steps below happen when `state.slot % EPOCH_LENGTH == EPOCH_LENGTH - 1`. * 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 `previous_epoch_start_slot = state.slot - 2 * EPOCH_LENGTH + 1` if `state.slot > GENESIS_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`. From a212038cd3c37a05fc99fb6da48a2c10678993b1 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 25 Jan 2019 17:33:15 -0700 Subject: [PATCH 10/31] 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 11/31] 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 12/31] 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 13/31] 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 14/31] 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 1a4107876823f4c6060f25ba79cff89c988a2a1c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 12:13:12 -0700 Subject: [PATCH 15/31] randao reveal is signed epoch number (#498) --- specs/core/0_beacon-chain.md | 8 ++------ specs/validator/0_beacon-chain-validator.md | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c7bef5c25..5a185f63f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -514,8 +514,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'pubkey': 'bytes48', # Withdrawal credentials 'withdrawal_credentials': 'bytes32', - # Number of proposer slots since genesis - 'proposer_slots': 'uint64', # Slot when validator activated 'activation_slot': 'uint64', # Slot when validator exited @@ -1368,7 +1366,6 @@ def process_deposit(state: BeaconState, validator = Validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, - proposer_slots=0, activation_slot=FAR_FUTURE_SLOT, exit_slot=FAR_FUTURE_SLOT, withdrawal_slot=FAR_FUTURE_SLOT, @@ -1447,7 +1444,6 @@ Below are the processing steps that happen at every slot. ### Misc counters * 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]` ### Block roots @@ -1473,8 +1469,8 @@ Below are the processing steps that happen at every `block`. ### 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(state.slot // EPOCH_LENGTH), signature=block.randao_reveal, domain=get_domain(state.fork, state.slot, DOMAIN_RANDAO))`. +* Set `state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH] = xor(state.latest_randao_mixes[state.slot % LATEST_RANDAO_MIXES_LENGTH], hash(block.randao_reveal))`. ### Eth1 data diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 29ec0c7e0..83ebb5751 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -158,12 +158,12 @@ _Note_: To calculate `state_root`, the validator should first run the state tran ##### Randao reveal -Set `block.randao_reveal = reveal_signature` where `reveal_signature` is defined as: +Set `block.randao_reveal = epoch_signature` where `epoch_signature` is defined as: ```python -reveal_signature = bls_sign( +epoch_signature = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=int_to_bytes32(validator.proposer_slots + 1), + message=int_to_bytes32(block.slot // EPOCH_LENGTH), domain=get_domain( fork_data, # `fork_data` is the fork_data at the slot `block.slot` block.slot, From b1ea3b26674d352861544099dc56d0feabf76790 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 26 Jan 2019 15:27:50 -0700 Subject: [PATCH 16/31] 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 17/31] 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 18/31] 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 19/31] 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 20/31] 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 21/31] 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 22/31] 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 23/31] 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 24/31] 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 25/31] 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 26/31] 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 From 7b9c033578eeca3d2e55275c681d25ab7dd44d51 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 20:40:30 -0700 Subject: [PATCH 27/31] remove extra line --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 10a41506f..3ce31da06 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1068,7 +1068,6 @@ def generate_seed(state: BeaconState, get_randao_mix(state, randao_mix_epoch) + get_active_index_root(state, epoch) ) - ``` #### `get_beacon_proposer_index` From c9a2f9258e792c24bdc02d39038984b14367a1c8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 27 Jan 2019 21:29:47 -0700 Subject: [PATCH 28/31] 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 3ce31da06..4d021bc3d 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 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`. +* If `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes` (ie. more than half the votes in this voting period were for that value), set `state.latest_eth1_data = eth1_data_vote.data`. * Set `state.eth1_data_votes = []`. ### Justification From 656eae6f6ad85de5f4b9493ca0a4f8ca16d2e261 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 27 Jan 2019 23:12:37 -0600 Subject: [PATCH 29/31] Minimum slot number, simplify excessive anti-underflow logic Adds a minimum slot number large enough that integer underflows involving epochs and slots will not happen; simplifies some logic that was more complex to handle them. --- specs/core/0_beacon-chain.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4d021bc3d..6e07c3857 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -190,7 +190,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | Name | Value | | - | - | | `GENESIS_FORK_VERSION` | `0` | -| `GENESIS_SLOT` | `0` | +| `GENESIS_SLOT` | `2**19` | | `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` | | `GENESIS_START_SHARD` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | @@ -198,6 +198,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `EMPTY_SIGNATURE` | `int_to_bytes96(0)` | | `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` | +* `GENESIS_SLOT` should be at least as large in terms of time as the largest of the time parameters or state list lengths below (ie. it should be at least as large as any value measured in slots, and at least `EPOCH_LENGTH` times as large as any value measured in epochs). + ### Time parameters | Name | Value | Unit | Duration | @@ -212,11 +214,12 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be ### 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 | +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | ~13 hours | +| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | ### Reward and penalty quotients @@ -1036,8 +1039,7 @@ def get_randao_mix(state: BeaconState, """ Returns the randao mix at a recent ``slot``. """ - assert get_current_epoch(state) < epoch + LATEST_RANDAO_MIXES_LENGTH - assert epoch <= get_current_epoch(state) + assert get_current_epoch(state) - LATEST_RANDAO_MIXES_LENGTH < epoch <= get_current_epoch(state) return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] ``` @@ -1049,8 +1051,7 @@ def get_active_index_root(state: BeaconState, """ Returns the index root at a recent ``epoch``. """ - assert get_current_epoch(state) < epoch + LATEST_INDEX_ROOTS_LENGTH - assert epoch <= get_current_epoch(state) + assert get_current_epoch(state) - LATEST_INDEX_ROOTS_LENGTH < epoch <= get_current_epoch(state) return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] ``` @@ -1062,10 +1063,9 @@ def generate_seed(state: BeaconState, """ Generate a seed for the given ``epoch``. """ - randao_mix_epoch = epoch + LATEST_RANDAO_MIXES_LENGTH - SEED_LOOKAHEAD return hash( - get_randao_mix(state, randao_mix_epoch) + + get_randao_mix(state, epoch - SEED_LOOKAHEAD) + get_active_index_root(state, epoch) ) ``` From ca3d0b8b534b19646fa9936dc8a25c6686c9972e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 27 Jan 2019 23:36:38 -0600 Subject: [PATCH 30/31] slot -> epoch --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6e07c3857..2606389e2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1037,7 +1037,7 @@ def get_block_root(state: BeaconState, def get_randao_mix(state: BeaconState, epoch: EpochNumber) -> Bytes32: """ - Returns the randao mix at a recent ``slot``. + Returns the randao mix at a recent ``epoch``. """ assert get_current_epoch(state) - LATEST_RANDAO_MIXES_LENGTH < epoch <= get_current_epoch(state) return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] From ce73a8327f40fcec81f7582bfed43815c1b417fd Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 28 Jan 2019 08:26:48 -0700 Subject: [PATCH 31/31] Update specs/core/0_beacon-chain.md Co-Authored-By: djrtwo --- 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 2606389e2..a2634c11a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -980,7 +980,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, """ epoch = slot_to_epoch(slot) current_epoch = get_current_epoch(state) - previous_epoch = current_epoch - 1 if epoch > GENESIS_EPOCH else current_epoch + previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch next_epoch = current_epoch + 1 assert previous_epoch <= epoch < next_epoch