Merge pull request #245 from ethereum/cleanup-validator-change

Cleanup validator change
This commit is contained in:
Danny Ryan 2018-12-06 09:43:04 -06:00 committed by GitHub
commit f122dd79e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 52 additions and 50 deletions

View File

@ -88,8 +88,7 @@
- [Crosslinks](#crosslinks)
- [Justification and finalization rewards and penalties](#justification-and-finalization-rewards-and-penalties)
- [Crosslink rewards and penalties](#crosslink-rewards-and-penalties)
- [Validator registry change](#validator-registry-change)
- [If a validator registry change does NOT happen](#if-a-validator-registry-change-does-not-happen)
- [Validator registry](#validator-registry)
- [Proposer reshuffling](#proposer-reshuffling)
- [Final updates](#final-updates)
- [Appendix](#appendix)
@ -1444,27 +1443,48 @@ For every `shard_committee` in `state.shard_committees_at_slots[:EPOCH_LENGTH]`
* If `v in attesting_validators(shard_committee)`, `v.balance += adjust_for_inclusion_distance(base_reward(v) * total_attesting_balance(shard_committee) // total_balance(shard_committee)), inclusion_distance(v))`.
* If `v not in attesting_validators(shard_committee)`, `v.balance -= base_reward(v)`.
### Validator registry change
### Validator registry
A [validator](#dfn-validator) registry change occurs if all of the following criteria are satisfied:
If the following are satisfied:
* `state.finalized_slot > state.validator_registry_latest_change_slot`
* For every shard number `shard` in `state.shard_committees_at_slots`, `latest_crosslinks[shard].slot > state.validator_registry_latest_change_slot`
* `state.latest_crosslinks[shard].slot > state.validator_registry_latest_change_slot` for every shard number `shard` in `state.shard_committees_at_slots`
A helper function is defined as:
update the validator registry and associated fields by running
```python
def get_changed_validators(validators: List[ValidatorRecord],
def change_validators(state: BeaconState,
current_slot: int) -> None:
"""
Change validator registry.
Note that this function mutates ``state``.
"""
state.validator_registry, state.latest_penalized_exit_balances, state.validator_registry_delta_chain_tip = get_updated_validator_registry(
state.validator_registry,
state.latest_penalized_exit_balances,
state.validator_registry_delta_chain_tip,
current_slot
)
```
which utilizes the following helper
```python
def get_updated_validator_registry(validator_registry: List[ValidatorRecord],
latest_penalized_exit_balances: List[int],
validator_registry_delta_chain_tip: int,
current_slot: int) -> Tuple[List[ValidatorRecord], List[int], int]:
"""
Return changed validator registry and ``latest_penalized_exit_balances``, ``validator_registry_delta_chain_tip``.
return the validator registry, as well as ``latest_penalized_exit_balances`` and ``validator_registry_delta_chain_tip``.
"""
# make copies to prevent mutating inputs
validator_registry = copy.deepcopy(state.validator_registry)
latest_penalized_exit_balances = copy.deepcopy(latest_penalized_exit_balances)
# The active validators
active_validator_indices = get_active_validator_indices(validators)
active_validator_indices = get_active_validator_indices(validator_registry)
# The total effective balance of active validators
total_balance = sum([get_effective_balance(v) for i, v in enumerate(validators) if i in active_validator_indices])
total_balance = sum([get_effective_balance(v) for i, v in enumerate(validator_registry) if i in active_validator_indices])
# The maximum balance churn in Gwei (for deposits and exits separately)
max_balance_churn = max(
@ -1474,39 +1494,39 @@ def get_changed_validators(validators: List[ValidatorRecord],
# Activate validators within the allowable balance churn
balance_churn = 0
for i in range(len(validators)):
if validators[i].status == PENDING_ACTIVATION and validators[i].balance >= MAX_DEPOSIT:
for i, validator in enumerate(validator_registry):
if validator.status == PENDING_ACTIVATION and validator.balance >= MAX_DEPOSIT:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(validators[i])
balance_churn += get_effective_balance(validator)
if balance_churn > max_balance_churn:
break
# Activate validator
validators[i].status = ACTIVE
validators[i].latest_status_change_slot = current_slot
validator.status = ACTIVE
validator.latest_status_change_slot = current_slot
validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip(
validator_registry_delta_chain_tip=validator_registry_delta_chain_tip,
index=i,
pubkey=validators[i].pubkey,
pubkey=validator.pubkey,
flag=ACTIVATION,
)
# Exit validators within the allowable balance churn
balance_churn = 0
for i in range(len(validators)):
if validators[i].status == ACTIVE_PENDING_EXIT:
for i, validator in enumerate(validator_registry):
if validator.status == ACTIVE_PENDING_EXIT:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(validators[i])
balance_churn += get_effective_balance(validator)
if balance_churn > max_balance_churn:
break
# Exit validator
validators[i].status = EXITED_WITHOUT_PENALTY
validators[i].latest_status_change_slot = current_slot
validator.status = EXITED_WITHOUT_PENALTY
validator.latest_status_change_slot = current_slot
validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip(
validator_registry_delta_chain_tip=validator_registry_delta_chain_tip,
index=i,
pubkey=validators[i].pubkey,
pubkey=validator.pubkey,
flag=EXIT,
)
@ -1521,44 +1541,26 @@ def get_changed_validators(validators: List[ValidatorRecord],
# Calculate penalties for slashed validators
def to_penalize(v):
return v.status == EXITED_WITH_PENALTY
validators_to_penalize = filter(to_penalize, validators)
validators_to_penalize = filter(to_penalize, validator_registry)
for v in validators_to_penalize:
v.balance -= get_effective_balance(v) * min(total_penalties * 3, total_balance) // total_balance
return validators, latest_penalized_exit_balances, validator_registry_delta_chain_tip
return validator_registry, latest_penalized_exit_balances, validator_registry_delta_chain_tip
```
Then, run the following algorithm to update the [validator](#dfn-validator) registry:
```python
def change_validators(state: BeaconState,
current_slot: int) -> None:
"""
Change validator registry.
Note that this function mutates ``state``.
"""
state.validator_registry, state.latest_penalized_exit_balances = get_changed_validators(
copy.deepcopy(state.validator_registry),
copy.deepcopy(state.latest_penalized_exit_balances),
state.validator_registry_delta_chain_tip,
current_slot
)
```
And perform the following updates to the `state`:
Also perform the following updates:
* Set `state.validator_registry_latest_change_slot = s + EPOCH_LENGTH`.
* Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`.
* Let `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`.
* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.next_seed, state.validator_registry, next_start_shard)`.
* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.next_seed, state.validator_registry, next_start_shard)` where next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`.
* Set `state.next_seed = state.randao_mix`.
### If a validator registry change does NOT happen
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 `time_since_finality = block.slot - state.validator_registry_latest_change_slot`.
* Let `slots_since_finality = s + EPOCH_LENGTH - state.validator_registry_latest_change_slot`.
* Let `start_shard = state.shard_committees_at_slots[0][0].shard`.
* If `time_since_finality * EPOCH_LENGTH <= MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL` or `time_since_finality` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.next_seed, state.validator_registry, start_shard)` and set `state.next_seed = state.randao_mix`. Note that `start_shard` is not changed from the last epoch.
* If `slots_since_finality * EPOCH_LENGTH <= MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL` or `slots_since_finality` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.next_seed, state.validator_registry, start_shard)` and set `state.next_seed = state.randao_mix`. Note that `start_shard` is not changed from the last epoch.
### Proposer reshuffling