Withdrawal queue -> exit queue

This commit is contained in:
vbuterin 2019-03-28 08:58:18 -05:00 committed by GitHub
parent c978bb4a67
commit 1aaa0030fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 55 additions and 126 deletions

View File

@ -98,9 +98,7 @@
- [Routines for updating validator status](#routines-for-updating-validator-status) - [Routines for updating validator status](#routines-for-updating-validator-status)
- [`activate_validator`](#activate_validator) - [`activate_validator`](#activate_validator)
- [`initiate_validator_exit`](#initiate_validator_exit) - [`initiate_validator_exit`](#initiate_validator_exit)
- [`exit_validator`](#exit_validator)
- [`slash_validator`](#slash_validator) - [`slash_validator`](#slash_validator)
- [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
- [Deposit arguments](#deposit-arguments) - [Deposit arguments](#deposit-arguments)
- [Withdrawal credentials](#withdrawal-credentials) - [Withdrawal credentials](#withdrawal-credentials)
@ -121,8 +119,8 @@
- [Justification and finalization](#justification-and-finalization) - [Justification and finalization](#justification-and-finalization)
- [Crosslinks](#crosslinks-1) - [Crosslinks](#crosslinks-1)
- [Apply rewards](#apply-rewards) - [Apply rewards](#apply-rewards)
- [Ejections](#ejections) - [Balance-driven status transitions](#balance-driven-status-transitions)
- [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) - [Validator registry and start shard](#validator-registry-and-start-shard)
- [Slashings and exit queue](#slashings-and-exit-queue) - [Slashings and exit queue](#slashings-and-exit-queue)
- [Final updates](#final-updates) - [Final updates](#final-updates)
- [Per-slot processing](#per-slot-processing) - [Per-slot processing](#per-slot-processing)
@ -182,7 +180,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| - | - | | - | - |
| `SHARD_COUNT` | `2**10` (= 1,024) | | `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) |
| `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) | | `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | | `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
| `SHUFFLE_ROUND_COUNT` | 90 | | `SHUFFLE_ROUND_COUNT` | 90 |
@ -418,14 +415,14 @@ The types are defined topologically to aid in facilitating an executable version
'pubkey': 'bytes48', 'pubkey': 'bytes48',
# Withdrawal credentials # Withdrawal credentials
'withdrawal_credentials': 'bytes32', 'withdrawal_credentials': 'bytes32',
# Epoch when became eligible for activation
'activation_eligibility_epoch': 'uint64',
# Epoch when validator activated # Epoch when validator activated
'activation_epoch': 'uint64', 'activation_epoch': 'uint64',
# Epoch when validator exited # Epoch when validator exited
'exit_epoch': 'uint64', 'exit_epoch': 'uint64',
# Epoch when validator is eligible to withdraw # Epoch when validator is eligible to withdraw
'withdrawable_epoch': 'uint64', 'withdrawable_epoch': 'uint64',
# Did the validator initiate an exit
'initiated_exit': 'bool',
# Was the validator slashed # Was the validator slashed
'slashed': 'bool', 'slashed': 'bool',
# Rounded balance # Rounded balance
@ -597,6 +594,10 @@ The types are defined topologically to aid in facilitating an executable version
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
'latest_start_shard': 'uint64', 'latest_start_shard': 'uint64',
# Exit queue
'exit_epoch': 'uint64',
'exit_queue_filled': 'uint64'
# Finality # Finality
'previous_epoch_attestations': [PendingAttestation], 'previous_epoch_attestations': [PendingAttestation],
'current_epoch_attestations': [PendingAttestation], 'current_epoch_attestations': [PendingAttestation],
@ -1352,22 +1353,20 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
validator = state.validator_registry[index] validator = state.validator_registry[index]
validator.initiated_exit = True # Operation is a no-op if validator is already in the queue
```
#### `exit_validator`
```python
def exit_validator(state: BeaconState, index: ValidatorIndex) -> None:
"""
Exit the validator with the given ``index``.
Note that this function mutates ``state``.
"""
validator = state.validator_registry[index]
# Update validator exit epoch if not previously exited
if validator.exit_epoch == FAR_FUTURE_EPOCH: if validator.exit_epoch == FAR_FUTURE_EPOCH:
validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) # Update exit queue counters
if state.exit_epoch < get_delayed_activation_exit_epoch(get_current_epoch(state)):
state.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
if state.exit_queue_filled >= MAX_EXIT_DEQUEUES_PER_EPOCH:
state.exit_epoch += 1
state.exit_queue_filled = 0
# Set validator exit epoch and withdrawable epoch
if validator.exit_epoch > state.exit_epoch:
validator.exit_epoch = state.exit_epoch
validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
# Extend queue
state.exit_queue_filled += 1
``` ```
#### `slash_validator` #### `slash_validator`
@ -1379,7 +1378,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None:
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
validator = state.validator_registry[index] validator = state.validator_registry[index]
exit_validator(state, index) initiate_validator_exit(state, index)
state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index) state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index)
whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_index = get_beacon_proposer_index(state, state.slot)
@ -1390,19 +1389,6 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None:
validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
``` ```
#### `prepare_validator_for_withdrawal`
```python
def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) -> None:
"""
Set the validator with the given ``index`` as withdrawable
``MIN_VALIDATOR_WITHDRAWABILITY_DELAY`` after the current epoch.
Note that this function mutates ``state``.
"""
validator = state.validator_registry[index]
validator.withdrawable_epoch = get_current_epoch(state) + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
```
## Ethereum 1.0 deposit contract ## Ethereum 1.0 deposit contract
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in phase 2, i.e. when the EVM2.0 is deployed and the shards have state. The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in phase 2, i.e. when the EVM2.0 is deployed and the shards have state.
@ -1513,6 +1499,10 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]), latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]),
latest_start_shard=GENESIS_START_SHARD, latest_start_shard=GENESIS_START_SHARD,
# Exit queue
exit_epoch=GENESIS_EPOCH,
exit_queue_filled=0,
# Finality # Finality
previous_epoch_attestations=[], previous_epoch_attestations=[],
current_epoch_attestations=[], current_epoch_attestations=[],
@ -1690,16 +1680,16 @@ def get_previous_total_balance(state: BeaconState) -> Gwei:
``` ```
```python ```python
def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]:
output = set() output = set()
for a in attestations: for a in attestations:
output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield)) output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield))
return sorted(list(output)) return sorted(filter(lambda index: not state.validator_registry[index].is_slashed, list(output)))
``` ```
```python ```python
def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> Gwei: def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> Gwei:
return get_total_balance(state, get_attesting_indices(state, attestations)) return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
``` ```
```python ```python
@ -1747,7 +1737,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple
# lexicographically higher hash # lexicographically higher hash
winning_root = max(all_roots, key=lambda r: (get_attesting_balance(state, get_attestations_for(r)), r)) winning_root = max(all_roots, key=lambda r: (get_attesting_balance(state, get_attestations_for(r)), r))
return winning_root, get_attesting_indices(state, get_attestations_for(winning_root)) return winning_root, get_unslashed_attesting_indices(state, get_attestations_for(winning_root))
``` ```
```python ```python
@ -1904,7 +1894,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
for index in eligible_validators: for index in eligible_validators:
base_reward = get_base_reward(state, index) base_reward = get_base_reward(state, index)
# Expected FFG source # Expected FFG source
if index in get_attesting_indices(state, state.previous_epoch_attestations): if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
rewards[index] += base_reward * total_attesting_balance // total_balance rewards[index] += base_reward * total_attesting_balance // total_balance
# Inclusion speed bonus # Inclusion speed bonus
rewards[index] += ( rewards[index] += (
@ -1914,17 +1904,17 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
else: else:
penalties[index] += base_reward penalties[index] += base_reward
# Expected FFG target # Expected FFG target
if index in get_attesting_indices(state, boundary_attestations): if index in get_unslashed_attesting_indices(state, boundary_attestations):
rewards[index] += base_reward * boundary_attesting_balance // total_balance rewards[index] += base_reward * boundary_attesting_balance // total_balance
else: else:
penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality) penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality)
# Expected head # Expected head
if index in get_attesting_indices(state, matching_head_attestations): if index in get_unslashed_attesting_indices(state, matching_head_attestations):
rewards[index] += base_reward * matching_head_balance // total_balance rewards[index] += base_reward * matching_head_balance // total_balance
else: else:
penalties[index] += base_reward penalties[index] += base_reward
# Proposer bonus # Proposer bonus
if index in get_attesting_indices(state, state.previous_epoch_attestations): if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
rewards[proposer_index] += base_reward // ATTESTATION_INCLUSION_REWARD_QUOTIENT rewards[proposer_index] += base_reward // ATTESTATION_INCLUSION_REWARD_QUOTIENT
# Take away max rewards if we're not finalizing # Take away max rewards if we're not finalizing
@ -1973,72 +1963,24 @@ def apply_rewards(state: BeaconState) -> None:
) )
``` ```
#### Ejections #### Balance-driven status transitions
Run `process_ejections(state)`. Run `process_balance_driven_status_transitions(state)`.
```python ```python
def process_ejections(state: BeaconState) -> None: def process_ejections(state: BeaconState) -> None:
""" """
Iterate through the validator registry Iterate through the validator registry
and eject active validators with balance below ``EJECTION_BALANCE``. and deposit or eject active validators with sufficiently high or low balances
""" """
for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)): for index, validator in enumeratE(state.validator_registry):
if get_balance(state, index) < EJECTION_BALANCE: if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT:
state.activation_eligibility_epoch = get_current_epoch(state)
if is_active(validator, get_current_epoch(state)) and get_balance(state, index) < EJECTION_BALANCE:
initiate_validator_exit(state, index) initiate_validator_exit(state, index)
``` ```
#### Validator registry and shuffling seed data #### Validator registry and start shard
```python
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)
# The total effective balance of active validators
total_balance = get_total_balance(state, active_validator_indices)
# The maximum balance churn in Gwei (for deposits and exits separately)
max_balance_churn = max(
MAX_DEPOSIT_AMOUNT,
total_balance // (2 * MAX_BALANCE_CHURN_QUOTIENT)
)
# Activate validators within the allowable balance churn
balance_churn = 0
for index, validator in enumerate(state.validator_registry):
if validator.activation_epoch == FAR_FUTURE_EPOCH and get_balance(state, 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:
break
# Activate validator
activate_validator(state, index, is_genesis=False)
# Exit validators within the allowable balance churn
if current_epoch < state.validator_registry_update_epoch + LATEST_SLASHED_EXIT_LENGTH:
balance_churn = (
state.latest_slashed_balances[state.validator_registry_update_epoch % LATEST_SLASHED_EXIT_LENGTH] -
state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH]
)
for index, validator in enumerate(state.validator_registry):
if validator.exit_epoch == FAR_FUTURE_EPOCH and validator.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
exit_validator(state, index)
state.validator_registry_update_epoch = current_epoch
```
Run the following function: Run the following function:
@ -2046,7 +1988,18 @@ Run the following function:
def update_registry(state: BeaconState) -> None: def update_registry(state: BeaconState) -> None:
# Check if we should update, and if so, update # Check if we should update, and if so, update
if state.finalized_epoch > state.validator_registry_update_epoch: if state.finalized_epoch > state.validator_registry_update_epoch:
update_validator_registry(state) # Validator indices that could be activated
indices_for_activation = sorted(
filter(
lambda index: state.validator_registry[index].activation_epoch == FAR_FUTURE_EPOCH
get_active_validator_indices(state.validator_registry, current_epoch),
),
key=lambda index: state.validator_registry[index].activation_eligibility_epoch
)
for index in indices_for_activation[:MAX_EXIT_DEQUEUES_PER_EPOCH]:
activate_validator(state, index, is_genesis=False)
state.validator_registry_update_epoch = current_epoch
state.latest_start_shard = ( state.latest_start_shard = (
state.latest_start_shard + state.latest_start_shard +
get_current_epoch_committee_count(state) get_current_epoch_committee_count(state)
@ -2057,7 +2010,7 @@ def update_registry(state: BeaconState) -> None:
#### Slashings and exit queue #### Slashings and exit queue
Run `process_slashings(state)` and `process_exit_queue(state)`: Run `process_slashings(state)`:
```python ```python
def process_slashings(state: BeaconState) -> None: def process_slashings(state: BeaconState) -> None:
@ -2083,30 +2036,6 @@ def process_slashings(state: BeaconState) -> None:
decrease_balance(state, index, penalty) decrease_balance(state, index, penalty)
``` ```
```python
def process_exit_queue(state: BeaconState) -> None:
"""
Process the exit queue.
Note that this function mutates ``state``.
"""
def eligible(index):
validator = state.validator_registry[index]
# Filter out dequeued validators
if validator.withdrawable_epoch != FAR_FUTURE_EPOCH:
return False
# Dequeue if the minimum amount of time has passed
else:
return get_current_epoch(state) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
eligible_indices = filter(eligible, list(range(len(state.validator_registry))))
# Sort in order of exit epoch, and validators that exit within the same epoch exit in order of validator index
sorted_indices = sorted(eligible_indices, key=lambda index: state.validator_registry[index].exit_epoch)
for dequeues, index in enumerate(sorted_indices):
if dequeues >= MAX_EXIT_DEQUEUES_PER_EPOCH:
break
prepare_validator_for_withdrawal(state, index)
```
#### Final updates #### Final updates
Run the following function: Run the following function: