From d9e897bbcbc281dfc298b5396c0c6d8c8d962713 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 28 Dec 2018 12:04:03 -0600 Subject: [PATCH 01/14] Status code / slot reorganization * Added `activation_slot`, `exit_slot`, `penalized_slot`, `withdrawal_slot`, use these to determine if a validator is active * Universal min activation/exit delay of 256 slots * Min exit time of ~1 day, but penalization delays this to ~18 days * Penalty calculation period of `[time penalized - 18 days, time penalized + 18 days]`; made the total penalties array fixed size and wraparound to make calculation more fine-grained * Processes withdrawals in all epochs, not just dynasty-changing epochs * Change `get_shuffling` function to take slot as argument Not yet done: * Removed `shard_committees` from the state * Removed persistent committees from the state --- specs/core/0_beacon-chain.md | 237 +++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 106 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 045a914b0f..fcc521181a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -66,7 +66,7 @@ - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) - [`split`](#split) - - [`get_new_shuffling`](#get_new_shuffling) + - [`get_shuffling`](#get_shuffling) - [`get_shard_committees_at_slot`](#get_shard_committees_at_slot) - [`get_block_root`](#get_block_root) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) @@ -168,6 +168,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `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_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | cycles | ~36 days | | `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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.) @@ -188,6 +189,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `INITIAL_FORK_VERSION` | `0` | | `INITIAL_SLOT_NUMBER` | `0` | | `ZERO_HASH` | `bytes([0] * 32)` | +| `FAR_FUTURE_SLOT` | `2**63` | ### Time parameters @@ -196,9 +198,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `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 | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | `2**17` (= 131,072) | slots | ~9 days | -| `COLLECTIVE_PENALTY_CALCULATION_PERIOD` | `2**20` (= 1,048,576) | slots | ~73 days | | `ZERO_BALANCE_VALIDATOR_TTL` | `2**22` (= 4,194,304) | slots | ~291 days | ### Reward and penalty quotients @@ -213,15 +216,13 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. -### Status codes +### Status flags | Name | Value | | - | - | -| `PENDING_ACTIVATION` | `0` | -| `ACTIVE` | `1` | -| `ACTIVE_PENDING_EXIT` | `2` | -| `EXITED_WITHOUT_PENALTY` | `3` | -| `EXITED_WITH_PENALTY` | `4` | +| `INITIATED_EXIT` | `2**0` (= 1) | +| `WITHDRAWABLE` | `2**1` (= 2) | + ### Max operations per block @@ -521,12 +522,18 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'randao_commitment': 'hash32', # Slots the proposer has skipped (i.e. layers of RANDAO expected) 'randao_layers': 'uint64', - # Status code - 'status': 'uint64', - # Slot when validator last changed status (or 0) - 'latest_status_change_slot': 'uint64', - # Exit counter when validator exited (or 0) + # 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', + # Exit counter when validator exited 'exit_count': 'uint64', + # Status flags + 'status_flags': 'uint64', # Proof of custody commitment 'poc_commitment': 'hash32', # Slot the proof of custody seed was last changed @@ -618,6 +625,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted latest_registry_delta_root: 'hash32', validator_index: 'uint24', pubkey: 'uint384', + slot: 'uint64', flag: 'uint64', } ``` @@ -738,7 +746,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)] + get_active_validator_indices(validators, start.state.slot)] attestation_targets = [get_latest_attestation_target(store, validator) for validator in active_validators] def get_vote_count(block): @@ -778,21 +786,21 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere #### `is_active_validator` ```python -def is_active_validator(validator: ValidatorRecord) -> bool: +def is_active_validator(validator: ValidatorRecord, slot: int) -> bool: """ Checks if ``validator`` is active. """ - return validator.status in [ACTIVE, ACTIVE_PENDING_EXIT] + return validator.activation_slot <= slot < validator.exit_slot ``` #### `get_active_validator_indices` ```python -def get_active_validator_indices(validators: [ValidatorRecord]) -> List[int]: +def get_active_validator_indices(validators: [ValidatorRecord], slot: int) -> List[int]: """ Gets indices of active validators from ``validators``. """ - return [i for i, v in enumerate(validators) if is_active_validator(v)] + return [i for i, v in enumerate(validators) if is_active_validator(v, slot)] ``` #### `shuffle` @@ -862,16 +870,17 @@ def split(values: List[Any], split_count: int) -> List[Any]: ] ``` -#### `get_new_shuffling` +#### `get_shuffling` ```python -def get_new_shuffling(seed: Hash32, - validators: List[ValidatorRecord], - crosslinking_start_shard: int) -> List[List[ShardCommittee]]: +def get_shuffling(seed: Hash32, + validators: List[ValidatorRecord], + crosslinking_start_shard: int, + slot: int) -> List[List[ShardCommittee]]: """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ - active_validator_indices = get_active_validator_indices(validators) + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( 1, @@ -907,6 +916,8 @@ def get_new_shuffling(seed: Hash32, return output ``` +**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 1 and in the future. + Here's a diagram of what is going on: ![](http://vitalik.ca/files/ShuffleAndAssign.png?1) @@ -1008,6 +1019,7 @@ def get_effective_balance(state: State, index: int) -> int: def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_chain_tip: Hash32, validator_index: int, pubkey: int, + slot: int, flag: int) -> Hash32: """ Compute the next root in the validator registry delta chain. @@ -1017,6 +1029,7 @@ def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_ latest_registry_delta_root=current_validator_registry_delta_chain_tip, validator_index=validator_index, pubkey=pubkey, + slot=slot, flag=flag, ) ) @@ -1196,7 +1209,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Recent state latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], - latest_penalized_exit_balances=[], + latest_penalized_exit_balances=[0 for _ in LATEST_PENALIZED_EXIT_LENGTH], latest_attestations=[], batched_block_roots=[], @@ -1217,14 +1230,14 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: - update_validator_status(state, validator_index, ACTIVE) + activate_validator(state, validator_index) # set initial committee shuffling - initial_shuffling = get_new_shuffling(ZERO_HASH, state.validator_registry, 0) + initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, INITIAL_SLOT_NUMBER) state.shard_committees_at_slots = initial_shuffling + initial_shuffling # set initial persistent shuffling - active_validator_indices = get_active_validator_indices(state.validator_registry) + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) state.persistent_committees = split(shuffle(active_validator_indices, ZERO_HASH), SHARD_COUNT) return state @@ -1238,8 +1251,8 @@ First, two helper functions: def min_empty_validator_index(validators: List[ValidatorRecord], validator_balances: List[int], current_slot: int) -> int: - for i, (v, vbal) in enumerate(zip(validators, validator_balances)): - if vbal == 0 and v.latest_status_change_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: + for i, v in enumerate(validators): + if v.withdrawal_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: return i return None ``` @@ -1304,8 +1317,10 @@ def process_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, randao_layers=0, - status=PENDING_ACTIVATION, - latest_status_change_slot=state.slot, + activation_slot=FAR_FUTURE_SLOT, + exit_slot=FAR_FUTURE_SLOT, + withdrawal_slot=FAR_FUTURE_SLOT, + penalized_slot=FAR_FUTURE_SLOT, exit_count=0, poc_commitment=poc_commitment, last_poc_change_slot=0, @@ -1330,26 +1345,7 @@ def process_deposit(state: BeaconState, return index ``` -### Routine for updating validator status - -```python -def update_validator_status(state: BeaconState, - index: int, - new_status: int) -> None: - """ - Update the validator status with the given ``index`` to ``new_status``. - Handle other general accounting related to this status update. - Note that this function mutates ``state``. - """ - if new_status == ACTIVE: - activate_validator(state, index) - if new_status == ACTIVE_PENDING_EXIT: - initiate_validator_exit(state, index) - if new_status in [EXITED_WITH_PENALTY, EXITED_WITHOUT_PENALTY]: - exit_validator(state, index, new_status) -``` - -The following are helpers and should only be called via `update_validator_status`: +### Routines for updating validator status ```python def activate_validator(state: BeaconState, @@ -1359,15 +1355,15 @@ def activate_validator(state: BeaconState, Note that this function mutates ``state``. """ validator = state.validator_registry[index] - if validator.status != PENDING_ACTIVATION: + if validator.activation_slot <= state.slot: return - validator.status = ACTIVE - validator.latest_status_change_slot = state.slot + validator.activation_slot = state.slot + ENTRY_EXIT_DELAY state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, validator_index=index, pubkey=validator.pubkey, + slot=validator.activation_slot, flag=ACTIVATION, ) ``` @@ -1380,40 +1376,22 @@ def initiate_validator_exit(state: BeaconState, Note that this function mutates ``state``. """ validator = state.validator_registry[index] - if validator.status != ACTIVE: - return - - validator.status = ACTIVE_PENDING_EXIT - validator.latest_status_change_slot = state.slot + validator.status_flags |= INITIATED_EXIT ``` ```python def exit_validator(state: BeaconState, - index: int, - new_status: int) -> None: + index: int) -> None: """ Exit the validator with the given ``index``. Note that this function mutates ``state``. """ validator = state.validator_registry[index] - prev_status = validator.status - - if prev_status == EXITED_WITH_PENALTY: - return - validator.status = new_status - validator.latest_status_change_slot = state.slot - - if new_status == EXITED_WITH_PENALTY: - state.latest_penalized_exit_balances[state.slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += 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 - - if prev_status == EXITED_WITHOUT_PENALTY: + if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: return + + validator.exit_slot = state.slot + ENTRY_EXIT_DELAY # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 @@ -1422,8 +1400,10 @@ def exit_validator(state: BeaconState, current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, validator_index=index, pubkey=validator.pubkey, + slot=validator.exit_slot, flag=EXIT, ) + state.validator_registry_exit_count += 1 # Remove validator from persistent committees for committee in state.persistent_committees: @@ -1433,6 +1413,27 @@ def exit_validator(state: BeaconState, break ``` +```python +def penalize_validator(state: BeaconState, + index: int) -> None: + + exit_validator(state, index) + state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % 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 + state.validator_registry[index].penalized_slot = state.slot +``` + +```python +def set_validator_withdrawable(state: BeaconState, + index: int) -> None: + + state.validator_registry[index].status_flags |= WITHDRAWABLE +``` + ## Per-slot processing Below are the processing steps that happen at every slot. @@ -1492,7 +1493,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer.status != EXITED_WITH_PENALTY`. * 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_data, 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_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. -* Run `update_validator_status(state, proposer_slashing.proposer_index, new_status=EXITED_WITH_PENALTY)`. +* Run `penalize_validator(state, proposer_slashing.proposer_index)`. #### Casper slashings @@ -1509,7 +1510,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`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `update_validator_status(state, i, new_status=EXITED_WITH_PENALTY)` +* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `penalize_validator(state, i)`. #### Attestations @@ -1573,11 +1574,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.status == ACTIVE`. +* Verify that `validator.exit_slot > state.slot + ENTRY_EXIT_DELAY`. * Verify that `state.slot >= exit.slot`. -* Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`. * Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. -* Run `update_validator_status(state, validator_index, new_status=ACTIVE_PENDING_EXIT)`. +* Run `initiate_validator_exit(state, validator_index)`. #### Miscellaneous @@ -1591,7 +1591,7 @@ The steps below happen when `state.slot % EPOCH_LENGTH == 0`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry)`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1718,7 +1718,7 @@ def process_ejections(state: BeaconState) -> None: """ for index in active_validator_indices(state.validator_registry): if state.validator_balances[index] < EJECTION_BALANCE: - update_validator_status(state, index, new_status=EXITED_WITHOUT_PENALTY) + exit_validator(state, index) ``` ### Validator registry @@ -1737,7 +1737,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) + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1750,65 +1750,89 @@ 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.status == PENDING_ACTIVATION and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: + if validator.activation_slot > state.slot and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: break # Activate validator - update_validator_status(state, index, new_status=ACTIVE) + activate_validator(state, index) # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.status == ACTIVE_PENDING_EXIT: + if validator.exit_slot > state.slot + ENTRY_EXIT_DELAY 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: break # Exit validator - update_validator_status(state, index, new_status=EXITED_WITHOUT_PENALTY) + exit_validator(state, index) +``` +Regardless of whether the above conditions are satisfied, run the following: - # Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods - period_index = current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD - total_penalties = ( - (latest_penalized_exit_balances[period_index]) + - (latest_penalized_exit_balances[period_index - 1] if period_index >= 1 else 0) + - (latest_penalized_exit_balances[period_index - 2] if period_index >= 2 else 0) - ) +```python +def process_penalties_and_exits(state: BeaconState) -> None: - # Calculate penalties for slashed validators - def to_penalize(index): - return state.validator_registry[index].status == EXITED_WITH_PENALTY - validators_to_penalize = filter(to_penalize, range(len(validator_registry))) - for index in validators_to_penalize: - state.validator_balances[index] -= get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance + # The active validators + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) + # The total effective balance of active validators + total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) - return validator_registry, latest_penalized_exit_balances, validator_registry_delta_chain_tip + for i, validator in enumerate(validators): + if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: + e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH + total_at_start = state.latest_penalized_exit_balances[(e+1)%LATEST_PENALIZED_EXIT_LENGTH] + total_at_end = state.latest_penalized_exit_balances[e] + total_penalties = total_at_end - total_at_start + penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance + state.validator_balances[index] -= penalty + + 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 + else: + return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME + + eligible_indices = filter(eligible, all_indices) + sorted_indices = sorted(eligible_indices, filter=lambda index: state.validator_registry[index].exit_count) + withdrawn_so_far = 0 + for index in sorted_indices: + validator = state.validator_registry[index] + if validator.status == EXITED_WITH_PENALTY: + # TODO: calculate and apply penalties for slashed validators + penalty = 1 + state.validator_balances[index] -= penalty + set_validator_withdrawable(state, index) + withdrawn_so_far += 1 + if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: + break ``` Also perform the following updates: * Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. -* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. +* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. If a validator registry update does _not_ happen do the following: * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_latest_change_slot) // EPOCH_LENGTH`. * Let `start_shard = state.shard_committees_at_slots[0][0].shard`. -* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard)`. Note that `start_shard` is not changed from the last epoch. +* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. ### Proposer reshuffling Run the following code to update the shard proposer set: ```python -active_validator_indices = get_active_validator_indices(state.validator_registry) +active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) num_validators_to_reshuffle = len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD for i in range(num_validators_to_reshuffle): # Multiplying i to 2 to ensure we have different input to all the required hashes in the shuffling @@ -1832,6 +1856,7 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com ### Final updates +* Let `e = state.slot // EPOCH_LENGTH`. Set `state.latest_penalized_exit_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_exit_balances[e % LATEST_PENALIZED_EXIT_LENGTH]` * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. ## State root processing From 85d001001c710a99e3d6b2acd9618b27e255a456 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 28 Dec 2018 12:08:19 -0600 Subject: [PATCH 02/14] Fixed seed lookahead --- 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 fcc521181a..1c76d22399 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1818,7 +1818,7 @@ Also perform the following updates: * Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. -* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. +* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. If a validator registry update does _not_ happen do the following: From bd5b32ec5abb7e0d140054b953d8fd25f8961da1 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 29 Dec 2018 13:44:51 -0600 Subject: [PATCH 03/14] Initialize status_flags --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1c76d22399..b075decd80 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1322,6 +1322,7 @@ def process_deposit(state: BeaconState, withdrawal_slot=FAR_FUTURE_SLOT, penalized_slot=FAR_FUTURE_SLOT, exit_count=0, + status_flags=0, poc_commitment=poc_commitment, last_poc_change_slot=0, second_last_poc_change_slot=0, From 14f49aaacb9d8fcb14f32192ab6e7aae4bc2bbff Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 29 Dec 2018 13:47:10 -0600 Subject: [PATCH 04/14] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b075decd80..7f5573492a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -168,7 +168,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `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_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | cycles | ~36 days | +| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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.) From 4a9337402584bf24808ead3238b7cd25b2686ad5 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 30 Dec 2018 09:15:23 -0600 Subject: [PATCH 05/14] Fixed as per hww's comments --- specs/core/0_beacon-chain.md | 103 +++++++++++------------------------ 1 file changed, 33 insertions(+), 70 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7f5573492a..b87556a6f7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -186,8 +186,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | Name | Value | | - | - | -| `INITIAL_FORK_VERSION` | `0` | -| `INITIAL_SLOT_NUMBER` | `0` | +| `GENESIS_FORK_VERSION` | `0` | +| `GENESIS_SLOT` | `0` | | `ZERO_HASH` | `bytes([0] * 32)` | | `FAR_FUTURE_SLOT` | `2**63` | @@ -1013,28 +1013,6 @@ def get_effective_balance(state: State, index: int) -> int: return min(state.validator_balances[index], MAX_DEPOSIT * GWEI_PER_ETH) ``` -#### `get_new_validator_registry_delta_chain_tip` - -```python -def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_chain_tip: Hash32, - validator_index: int, - pubkey: int, - slot: int, - flag: int) -> Hash32: - """ - Compute the next root in the validator registry delta chain. - """ - return hash_tree_root( - ValidatorRegistryDeltaBlock( - latest_registry_delta_root=current_validator_registry_delta_chain_tip, - validator_index=validator_index, - pubkey=pubkey, - slot=slot, - flag=flag, - ) - ) -``` - #### `get_fork_version` ```python @@ -1144,11 +1122,11 @@ def integer_squareroot(n: int) -> int: ### On startup -A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. +A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. ```python { - slot=INITIAL_SLOT_NUMBER, + slot=GENESIS_SLOT, parent_root=ZERO_HASH, state_root=STARTUP_STATE_ROOT, randao_reveal=ZERO_HASH, @@ -1175,18 +1153,18 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], processed_pow_receipt_root: Hash32) -> BeaconState: state = BeaconState( # Misc - slot=INITIAL_SLOT_NUMBER, + slot=GENESIS_SLOT, genesis_time=genesis_time, fork_data=ForkData( - pre_fork_version=INITIAL_FORK_VERSION, - post_fork_version=INITIAL_FORK_VERSION, - fork_slot=INITIAL_SLOT_NUMBER, + pre_fork_version=GENESIS_FORK_VERSION, + post_fork_version=GENESIS_FORK_VERSION, + fork_slot=GENESIS_SLOT, ), # Validator registry validator_registry=[], validator_balances=[], - validator_registry_latest_change_slot=INITIAL_SLOT_NUMBER, + validator_registry_latest_change_slot=GENESIS_SLOT, validator_registry_exit_count=0, validator_registry_delta_chain_tip=ZERO_HASH, @@ -1201,13 +1179,13 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_challenges=[], # Finality - previous_justified_slot=INITIAL_SLOT_NUMBER, - justified_slot=INITIAL_SLOT_NUMBER, + previous_justified_slot=GENESIS_SLOT, + justified_slot=GENESIS_SLOT, justification_bitfield=0, - finalized_slot=INITIAL_SLOT_NUMBER, + finalized_slot=GENESIS_SLOT, # Recent state - latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[CrosslinkRecord(slot=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_penalized_exit_balances=[0 for _ in LATEST_PENALIZED_EXIT_LENGTH], latest_attestations=[], @@ -1230,10 +1208,10 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: - activate_validator(state, validator_index) + activate_validator(state, validator_index, True) # set initial committee shuffling - initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, INITIAL_SLOT_NUMBER) + initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, GENESIS_SLOT) state.shard_committees_at_slots = initial_shuffling + initial_shuffling # set initial persistent shuffling @@ -1348,45 +1326,34 @@ def process_deposit(state: BeaconState, ### Routines for updating validator status +_Note that all functions in this section mutate `state`_. + ```python -def activate_validator(state: BeaconState, - index: int) -> None: - """ - Activate the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def activate_validator(state: BeaconState, index: int, immediate: bool) -> None: validator = state.validator_registry[index] if validator.activation_slot <= state.slot: return - validator.activation_slot = state.slot + ENTRY_EXIT_DELAY - state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( - current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, - validator_index=index, - pubkey=validator.pubkey, - slot=validator.activation_slot, - flag=ACTIVATION, + validator.activation_slot = state.slot + (0 if immediate else ENTRY_EXIT_DELAY) + state.validator_registry_delta_chain_tip = hash_tree_root( + ValidatorRegistryDeltaBlock( + current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, + validator_index=index, + pubkey=validator.pubkey, + slot=validator.activation_slot, + flag=ACTIVATION, + ) ) ``` ```python -def initiate_validator_exit(state: BeaconState, - index: int) -> None: - """ - Initiate exit for the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def initiate_validator_exit(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] validator.status_flags |= INITIATED_EXIT ``` ```python -def exit_validator(state: BeaconState, - index: int) -> None: - """ - Exit the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: @@ -1415,9 +1382,7 @@ def exit_validator(state: BeaconState, ``` ```python -def penalize_validator(state: BeaconState, - index: int) -> None: - +def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) @@ -1429,9 +1394,7 @@ def penalize_validator(state: BeaconState, ``` ```python -def set_validator_withdrawable(state: BeaconState, - index: int) -> None: - +def prepare_validator_for_withdrawal(state: BeaconState, index: int) -> None: state.validator_registry[index].status_flags |= WITHDRAWABLE ``` @@ -1758,7 +1721,7 @@ def update_validator_registry(state: BeaconState) -> None: break # Activate validator - activate_validator(state, index) + activate_validator(state, index, False) # Exit validators within the allowable balance churn balance_churn = 0 @@ -1809,7 +1772,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: # TODO: calculate and apply penalties for slashed validators penalty = 1 state.validator_balances[index] -= penalty - set_validator_withdrawable(state, index) + prepare_validator_for_withdrawal(state, index) withdrawn_so_far += 1 if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: break From 011970169c5907d8016e0dea8726407f5b8c63c3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 30 Dec 2018 20:42:05 -0600 Subject: [PATCH 06/14] Edited as per Justin's comments --- specs/core/0_beacon-chain.md | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b87556a6f7..dbc5c9ded0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -916,7 +916,7 @@ def get_shuffling(seed: Hash32, return output ``` -**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 1 and in the future. +**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. Here's a diagram of what is going on: @@ -1207,7 +1207,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) - if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: + if get_effective_balance(state, validator_index) >= MAX_DEPOSIT * GWEI_PER_ETH: activate_validator(state, validator_index, True) # set initial committee shuffling @@ -1229,9 +1229,8 @@ First, two helper functions: def min_empty_validator_index(validators: List[ValidatorRecord], validator_balances: List[int], current_slot: int) -> int: - for i, v in enumerate(validators): - if v.withdrawal_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: - return i + # In phase 2, add logic that seeks the lowest-index validator that + # has been withdrawn for more than ~1 year return None ``` @@ -1329,12 +1328,10 @@ def process_deposit(state: BeaconState, _Note that all functions in this section mutate `state`_. ```python -def activate_validator(state: BeaconState, index: int, immediate: bool) -> None: +def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: validator = state.validator_registry[index] - if validator.activation_slot <= state.slot: - return - validator.activation_slot = state.slot + (0 if immediate else ENTRY_EXIT_DELAY) + validator.activation_slot = GENESIS_SLOT if genesis else (state.slot + ENTRY_EXIT_DELAY) state.validator_registry_delta_chain_tip = hash_tree_root( ValidatorRegistryDeltaBlock( current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, @@ -1364,12 +1361,14 @@ def exit_validator(state: BeaconState, index: int) -> None: # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count - state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( - current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, - validator_index=index, - pubkey=validator.pubkey, - slot=validator.exit_slot, - flag=EXIT, + state.validator_registry_delta_chain_tip = hash_tree_root( + ValidatorRegistryDeltaBlock( + current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, + validator_index=index, + pubkey=validator.pubkey, + slot=validator.exit_slot, + flag=EXIT, + ) ) state.validator_registry_exit_count += 1 @@ -1384,18 +1383,20 @@ def exit_validator(state: BeaconState, index: int) -> None: ```python def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) + validator = state.validator_registry[index] state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % 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 - state.validator_registry[index].penalized_slot = state.slot + validator.penalized_slot = state.slot ``` ```python def prepare_validator_for_withdrawal(state: BeaconState, index: int) -> None: - state.validator_registry[index].status_flags |= WITHDRAWABLE + validator = state.validator_registry[index] + validator.status_flags |= WITHDRAWABLE ``` ## Per-slot processing @@ -1749,7 +1750,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: for i, validator in enumerate(validators): if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH - total_at_start = state.latest_penalized_exit_balances[(e+1)%LATEST_PENALIZED_EXIT_LENGTH] + total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] total_penalties = total_at_end - total_at_start penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance From d36b403c2e9691a43463ad7ec40fb815b4fb6550 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 31 Dec 2018 15:14:14 +0000 Subject: [PATCH 07/14] Cleanups and fixes Cleanups * (typo) Remove `get_new_validator_registry_delta_chain_tip` from table of contents * (typo) Update "Routines for updating validator status" in table of contents * Update `FAR_FUTURE_SLOT` from `2**63` to `2**64 - 1` * Put more constants in "Initial values", homogenise * Cleanup note formatting * Remove `ZERO_BALANCE_VALIDATOR_TTL` logic (to be possibly reintroduced in phase 2). * Cleanup `min_empty_validator_index` * Rename `deposit` to `amount` in `process_deposit` and `DepositData`. * (typo) Remove new line under `process_penalties_and_exits` * (typo) "Status codes" => "Status flags" in the table of contents * (typo) `(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH` => Use `SEED_LOOKAHEAD` instead. * Put `state.validator_registry_latest_change_slot = state.slot` in `update_validator_registry`. * Use `GENESIS_SLOT` for `last_poc_change_slot=0` and `second_last_poc_change_slot=0`. Bugfixes * (typo) `validator_exit` => `exit.validator_index` * Separate initial deposits and initial activations to avoid double activations * Replace `proposer.status != EXITED_WITH_PENALTY` with `validator.penalized_slot > state.slot` in two different places. * Replace `status == EXITED_WITH_PENALTY` with `validator.penalized_slot <= state.slot` (and validator active) in two different places. --- specs/core/0_beacon-chain.md | 86 ++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dbc5c9ded0..b8a0f62bed 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -16,7 +16,7 @@ - [Initial values](#initial-values) - [Time parameters](#time-parameters) - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status codes](#status-codes) + - [Status flags](#status-flags) - [Max operations per block](#max-operations-per-block) - [Validator registry delta flags](#validator-registry-delta-flags) - [Signature domains](#signature-domains) @@ -74,7 +74,6 @@ - [`get_attestation_participants`](#get_attestation_participants) - [`bytes1`, `bytes2`, ...](#bytes1-bytes2-) - [`get_effective_balance`](#get_effective_balance) - - [`get_new_validator_registry_delta_chain_tip`](#get_new_validator_registry_delta_chain_tip) - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`verify_slashable_vote_data`](#verify_slashable_vote_data) @@ -86,7 +85,7 @@ - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - [On startup](#on-startup) - [Routine for processing deposits](#routine-for-processing-deposits) - - [Routine for updating validator status](#routine-for-updating-validator-status) + - [Routines for updating validator status](#routines-for-updating-validator-status) - [Per-slot processing](#per-slot-processing) - [Misc counters](#misc-counters) - [Block roots](#block-roots) @@ -164,12 +163,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `GWEI_PER_ETH` | `10**9` | Gwei/ETH | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | -| `BLS_WITHDRAWAL_PREFIX_BYTE` | `0x00` | - | | `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_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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.) @@ -188,8 +185,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | - | - | | `GENESIS_FORK_VERSION` | `0` | | `GENESIS_SLOT` | `0` | -| `ZERO_HASH` | `bytes([0] * 32)` | -| `FAR_FUTURE_SLOT` | `2**63` | +| `FAR_FUTURE_SLOT` | `2**64 - 1` | +| `ZERO_HASH` | `bytes32(0)` | +| `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | +| `BLS_WITHDRAWAL_PREFIX_BYTE` | `bytes1(0)` | ### Time parameters @@ -202,7 +201,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | `2**17` (= 131,072) | slots | ~9 days | -| `ZERO_BALANCE_VALIDATOR_TTL` | `2**22` (= 4,194,304) | slots | ~291 days | ### Reward and penalty quotients @@ -373,8 +371,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted { # Deposit parameters 'deposit_input': DepositInput, - # Value in Gwei - 'value': 'uint64', + # Amount in Gwei + 'amount': 'uint64', # Timestamp from deposit contract 'timestamp': 'uint64', } @@ -1196,25 +1194,28 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], candidate_pow_receipt_roots=[], ) - # handle initial deposits and activations + # Process initial deposits for deposit in initial_validator_deposits: validator_index = process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, - deposit=deposit.deposit_data.value, + amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) + + # Process initial activations + for validator_index, _ in enumerate(state.validator_registry): if get_effective_balance(state, validator_index) >= MAX_DEPOSIT * GWEI_PER_ETH: activate_validator(state, validator_index, True) - # set initial committee shuffling + # Set initial committee shuffling initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, GENESIS_SLOT) state.shard_committees_at_slots = initial_shuffling + initial_shuffling - # set initial persistent shuffling + # Set initial persistent shuffling active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) state.persistent_committees = split(shuffle(active_validator_indices, ZERO_HASH), SHARD_COUNT) @@ -1223,16 +1224,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], ### Routine for processing deposits -First, two helper functions: - -```python -def min_empty_validator_index(validators: List[ValidatorRecord], - validator_balances: List[int], - current_slot: int) -> int: - # In phase 2, add logic that seeks the lowest-index validator that - # has been withdrawn for more than ~1 year - return None -``` +First, a helper function: ```python def validate_proof_of_possession(state: BeaconState, @@ -1266,7 +1258,7 @@ Now, to add a [validator](#dfn-validator) or top up an existing [validator](#dfn ```python def process_deposit(state: BeaconState, pubkey: int, - deposit: int, + amount: int, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, @@ -1301,31 +1293,27 @@ def process_deposit(state: BeaconState, exit_count=0, status_flags=0, poc_commitment=poc_commitment, - last_poc_change_slot=0, - second_last_poc_change_slot=0, + last_poc_change_slot=GENESIS_SLOT, + second_last_poc_change_slot=GENESIS_SLOT, ) - index = min_empty_validator_index(state.validator_registry, state.validator_balances, state.slot) - if index is None: - state.validator_registry.append(validator) - state.validator_balances.append(deposit) - index = len(state.validator_registry) - 1 - else: - state.validator_registry[index] = validator - state.validator_balances[index] = deposit + # Note: In phase 2 registry indices that has been withdrawn for a long time will be recycled. + index = len(state.validator_registry) + state.validator_registry.append(validator) + state.validator_balances.append(amount) else: - # Increase balance by deposit + # Increase balance by deposit amount index = validator_pubkeys.index(pubkey) assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - state.validator_balances[index] += deposit + state.validator_balances[index] += amount return index ``` ### Routines for updating validator status -_Note that all functions in this section mutate `state`_. +Note: All functions in this section mutate `state`. ```python def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: @@ -1455,7 +1443,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.status != EXITED_WITH_PENALTY`. +* Verify that `validator.penalized_slot > state.slot`. * 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_data, 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_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. @@ -1475,7 +1463,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`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `penalize_validator(state, i)`. +* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_slot > state.slot`. #### Attestations @@ -1503,7 +1491,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: -* Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. +* Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.amount` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. * Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_pow_receipt_root`: ```python @@ -1517,14 +1505,13 @@ def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, return value == root ``` -* Verify that `state.slot - (deposit.deposit_data.timestamp - state.genesis_time) // SLOT_DURATION < ZERO_BALANCE_VALIDATOR_TTL`. * Run the following: ```python process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, - deposit=deposit.deposit_data.value, + amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, @@ -1542,7 +1529,7 @@ For each `exit` in `block.body.exits`: * Verify that `validator.exit_slot > state.slot + ENTRY_EXIT_DELAY`. * Verify that `state.slot >= exit.slot`. * Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. -* Run `initiate_validator_exit(state, validator_index)`. +* Run `initiate_validator_exit(state, exit.validator_index)`. #### Miscellaneous @@ -1657,7 +1644,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 [validator](#dfn-validator) `index` with `status == EXITED_WITH_PENALTY`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + 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 [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 @@ -1735,13 +1722,15 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) + + + state.validator_registry_latest_change_slot = state.slot ``` Regardless of whether the above conditions are satisfied, 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) # The total effective balance of active validators @@ -1769,7 +1758,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: withdrawn_so_far = 0 for index in sorted_indices: validator = state.validator_registry[index] - if validator.status == EXITED_WITH_PENALTY: + if validator.penalized_slot <= state.slot: # TODO: calculate and apply penalties for slashed validators penalty = 1 state.validator_balances[index] -= penalty @@ -1781,7 +1770,6 @@ def process_penalties_and_exits(state: BeaconState) -> None: Also perform the following updates: -* Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. @@ -1790,7 +1778,7 @@ If a validator registry update does _not_ happen do the following: * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_latest_change_slot) // EPOCH_LENGTH`. * Let `start_shard = state.shard_committees_at_slots[0][0].shard`. -* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. +* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. ### Proposer reshuffling From fdb1b6775dd9783830890702001371a0865c8d5d Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 31 Dec 2018 14:08:59 -0600 Subject: [PATCH 08/14] Removed extraneous variable setting --- 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 dce5f08545..71f8a1df3e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1197,7 +1197,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Process initial deposits for deposit in initial_validator_deposits: - validator_index = process_deposit( + process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, amount=deposit.deposit_data.amount, From 2105614059a236e2b037539f10a7d4d59ad9e1f4 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 1 Jan 2019 22:37:43 -0600 Subject: [PATCH 09/14] Bunch of fixes --- specs/core/0_beacon-chain.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 71f8a1df3e..a5ed4c7865 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -200,6 +200,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `SEED_LOOKAHEAD` | `2**6` (= 64) | slots | 6.4 minutes | | `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | +| `MIN_VALIDATOR_WITHDRAWAL_TIME` | `2**14` (= 16,384) | slots | ~27 hours | ### Reward and penalty quotients @@ -875,6 +876,10 @@ def get_shuffling(seed: Hash32, """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ + + # Normalizes slot to start of epoch boundary + slot -= slot % EPOCH_LENGTH + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( @@ -1725,8 +1730,8 @@ def process_penalties_and_exits(state: BeaconState) -> None: # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) - for i, validator in enumerate(validators): - if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: + 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 total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] @@ -1742,15 +1747,12 @@ def process_penalties_and_exits(state: BeaconState) -> None: else: return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME + all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) sorted_indices = sorted(eligible_indices, filter=lambda index: state.validator_registry[index].exit_count) withdrawn_so_far = 0 for index in sorted_indices: validator = state.validator_registry[index] - if validator.penalized_slot <= state.slot: - # TODO: calculate and apply penalties for slashed validators - penalty = 1 - state.validator_balances[index] -= penalty prepare_validator_for_withdrawal(state, index) withdrawn_so_far += 1 if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: From 696bf23b5e9ea5d357356a0b4341d672e603c83f Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:41:32 +0000 Subject: [PATCH 10/14] Remove returned value for process_deposit Also trim trailing whitespace --- specs/core/0_beacon-chain.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a5ed4c7865..20471ad805 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -221,7 +221,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `INITIATED_EXIT` | `2**0` (= 1) | | `WITHDRAWABLE` | `2**1` (= 2) | - ### Max operations per block | Name | Value | @@ -876,17 +875,17 @@ def get_shuffling(seed: Hash32, """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ - + # Normalizes slot to start of epoch boundary slot -= slot % EPOCH_LENGTH - + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( 1, min( SHARD_COUNT // EPOCH_LENGTH, - len(active_validator_indices) // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, + len(active_validator_indices) // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, ) ) @@ -965,7 +964,7 @@ def get_beacon_proposer_index(state: BeaconState, #### `merkle_root` ```python -def merkle_root(values): +def merkle_root(values): """ Merkleize ``values`` (where ``len(values)`` is a power of two) and return the Merkle root. """ @@ -1264,7 +1263,7 @@ def process_deposit(state: BeaconState, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, - poc_commitment: Hash32) -> int: + poc_commitment: Hash32) -> None: """ Process a deposit from Ethereum 1.0. Note that this function mutates ``state``. @@ -1309,8 +1308,6 @@ def process_deposit(state: BeaconState, assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials state.validator_balances[index] += amount - - return index ``` ### Routines for updating validator status @@ -1345,7 +1342,7 @@ def exit_validator(state: BeaconState, index: int) -> None: if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: return - + validator.exit_slot = state.slot + ENTRY_EXIT_DELAY # The following updates only occur if not previous exited @@ -1592,7 +1589,6 @@ If `state.slot % POW_RECEIPT_ROOT_VOTING_PERIOD == 0`: * 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.finalized_slot = state.previous_justified_slot` if any of the following are true: * `state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3` @@ -1716,8 +1712,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - - + state.validator_registry_latest_change_slot = state.slot ``` @@ -1787,10 +1782,10 @@ This section is divided into Normative and Informative references. Normative re ## Normative ## Informative - _**casper-ffg**_ + _**casper-ffg**_   _Casper the Friendly Finality Gadget_. V. Buterin and V. Griffith. URL: https://arxiv.org/abs/1710.09437 - _**python-poc**_ + _**python-poc**_   _Python proof-of-concept implementation_. Ethereum Foundation. URL: https://github.com/ethereum/beacon_chain # Copyright From 93b18d5160d02d040d5c23dd18c979a1c1b3d0d9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:51:57 +0000 Subject: [PATCH 11/14] Fix typo --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 20471ad805..33fcc5307d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1731,7 +1731,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] total_penalties = total_at_end - total_at_start - penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance + penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance state.validator_balances[index] -= penalty def eligible(index): From 3f4be7abb728048cd36b94507ed52507cefaa488 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 3 Jan 2019 19:42:59 -0600 Subject: [PATCH 12/14] Fixed activation slot conditional. --- 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 2881890c12..1daa1e7896 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1676,7 +1676,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 > state.slot and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: + if validator.activation_slot > state.slot + ENTRY_EXIT_DELAY and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: From a5b1f2cc0ab204eae782632accf6e7a62601f630 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 3 Jan 2019 19:44:39 -0600 Subject: [PATCH 13/14] Moved comment and changed conditional --- 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 1daa1e7896..e86256c29c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1324,12 +1324,12 @@ def initiate_validator_exit(state: BeaconState, index: int) -> None: def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] - if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: + # The following updates only occur if not previous exited + if validator.exit_slot <= state.slot + ENTRY_EXIT_DELAY: return validator.exit_slot = state.slot + ENTRY_EXIT_DELAY - # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count state.validator_registry_delta_chain_tip = hash_tree_root( From c24f9070880583dcf3c1fd8fbc62f24b89895d82 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 4 Jan 2019 09:22:59 -0600 Subject: [PATCH 14/14] Put back spaces --- 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 e86256c29c..e8db4268c6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1766,10 +1766,10 @@ This section is divided into Normative and Informative references. Normative re ## Normative ## Informative - _**casper-ffg**_ + _**casper-ffg**_   _Casper the Friendly Finality Gadget_. V. Buterin and V. Griffith. URL: https://arxiv.org/abs/1710.09437 - _**python-poc**_ + _**python-poc**_   _Python proof-of-concept implementation_. Ethereum Foundation. URL: https://github.com/ethereum/beacon_chain # Copyright