Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Withdrawal queue -> exit queue #850

Merged
merged 34 commits into from
Apr 14, 2019
Merged
Changes from 7 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1aaa003
Withdrawal queue -> exit queue
vbuterin Mar 28, 2019
deb0e32
Fixes to make Justin happy
vbuterin Mar 28, 2019
aa4bbcc
Bugfix
hwwhww Mar 28, 2019
9c4e034
Merge branch 'dev' into vbuterin-patch-13
hwwhww Mar 29, 2019
a2dae9a
Fix after merging
hwwhww Mar 29, 2019
15498f2
Fixed exit epoch conditional
vbuterin Mar 31, 2019
2529cb1
Update 0_beacon-chain.md
JustinDrake Apr 3, 2019
169579c
Update 0_beacon-chain.md
JustinDrake Apr 6, 2019
7f0a93f
Update 0_beacon-chain.md
JustinDrake Apr 6, 2019
63412d9
Update 0_beacon-chain.md
JustinDrake Apr 6, 2019
5ea5746
Fix `get_genesis_beacon_state` and minor refactoring
hwwhww Apr 6, 2019
8958cf8
Merge branch 'dev' into vbuterin-patch-13
hwwhww Apr 6, 2019
ebba3f5
Fix typo
hwwhww Apr 6, 2019
00872e0
Updated tests
hwwhww Apr 6, 2019
47464f2
Update 0_beacon-chain.md
JustinDrake Apr 6, 2019
4630b13
Fix/Remove pointless assertion
hwwhww Apr 7, 2019
846e2d6
Remove `force_registry_change_at_next_epoch`
hwwhww Apr 7, 2019
cc2d005
Merge branch 'dev' into vbuterin-patch-13
vbuterin Apr 13, 2019
f7c5b0a
set activation_eligibility_epoch during process_deposit
djrtwo Apr 13, 2019
3700440
add exit queue test
djrtwo Apr 13, 2019
bade9ff
enhance exit queue test
djrtwo Apr 13, 2019
f85e7ac
Added churn limit logic
vbuterin Apr 14, 2019
0d64483
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
d01fb80
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
15bb967
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
7705ecf
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
da4a143
fix test
djrtwo Apr 14, 2019
229af3d
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
0b77012
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
06807cf
fix tests and off by one error
djrtwo Apr 14, 2019
704ea7c
Merge branch 'vbuterin-patch-13' of github.com:ethereum/eth2.0-specs …
djrtwo Apr 14, 2019
0908ffa
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
875b2ba
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
3394368
Update 0_beacon-chain.md
JustinDrake Apr 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 65 additions & 131 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,7 @@
- [Routines for updating validator status](#routines-for-updating-validator-status)
- [`activate_validator`](#activate_validator)
- [`initiate_validator_exit`](#initiate_validator_exit)
- [`exit_validator`](#exit_validator)
- [`slash_validator`](#slash_validator)
- [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
- [Deposit arguments](#deposit-arguments)
- [Withdrawal credentials](#withdrawal-credentials)
Expand All @@ -123,8 +121,8 @@
- [Justification and finalization](#justification-and-finalization)
- [Crosslinks](#crosslinks-1)
- [Apply rewards](#apply-rewards)
- [Ejections](#ejections)
- [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data)
- [Balance-driven status transitions](#balance-driven-status-transitions)
- [Validator registry and start shard](#validator-registry-and-start-shard)
- [Slashings and exit queue](#slashings-and-exit-queue)
- [Final updates](#final-updates)
- [Per-slot processing](#per-slot-processing)
Expand Down Expand Up @@ -184,7 +182,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| - | - |
| `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) |
| `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
| `SHUFFLE_ROUND_COUNT` | 90 |
Expand Down Expand Up @@ -419,14 +416,14 @@ The types are defined topologically to aid in facilitating an executable version
'pubkey': 'bytes48',
# Withdrawal credentials
'withdrawal_credentials': 'bytes32',
# Epoch when became eligible for activation
'activation_eligibility_epoch': 'uint64',
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
# Epoch when validator activated
'activation_epoch': 'uint64',
# Epoch when validator exited
'exit_epoch': 'uint64',
# Epoch when validator is eligible to withdraw
'withdrawable_epoch': 'uint64',
# Did the validator initiate an exit
'initiated_exit': 'bool',
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
# Was the validator slashed
'slashed': 'bool',
# Rounded balance
Expand Down Expand Up @@ -592,11 +589,14 @@ The types are defined topologically to aid in facilitating an executable version
# Validator registry
'validator_registry': [Validator],
'balances': ['uint64'],
'validator_registry_update_epoch': 'uint64',

# Randomness and committees
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
'latest_start_shard': 'uint64',

# Exit queue
'exit_epoch': 'uint64',
'exit_queue_filled': 'uint64',

# Finality
'previous_epoch_attestations': [PendingAttestation],
Expand Down Expand Up @@ -1333,10 +1333,10 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
validator = Validator(
pubkey=pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
withdrawable_epoch=FAR_FUTURE_EPOCH,
initiated_exit=False,
slashed=False,
high_balance=0
)
Expand Down Expand Up @@ -1377,22 +1377,23 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
Note that this function mutates ``state``.
"""
validator = state.validator_registry[index]
validator.initiated_exit = True
```
# Operation is a no-op if validator is already in the queue
if validator.exit_epoch == FAR_FUTURE_EPOCH:
# Update exit queue counters
delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
if state.exit_epoch < delayed_activation_exit_epoch:
state.exit_epoch = delayed_activation_exit_epoch

#### `exit_validator`
if state.exit_queue_filled >= MAX_EXIT_DEQUEUES_PER_EPOCH:
state.exit_epoch += 1
state.exit_queue_filled = 0

```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]
# Set validator exit epoch and withdrawable epoch
validator.exit_epoch = state.exit_epoch
validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY

# Update validator exit epoch if not previously exited
if validator.exit_epoch == FAR_FUTURE_EPOCH:
validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
# Extend queue
state.exit_queue_filled += 1
```

#### `slash_validator`
Expand All @@ -1403,7 +1404,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
Slash the validator with index ``slashed_index``.
Note that this function mutates ``state``.
"""
exit_validator(state, slashed_index)
initiate_validator_exit(state, slashed_index)
state.validator_registry[slashed_index].slashed = True
state.validator_registry[slashed_index].withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
slashed_balance = get_effective_balance(state, slashed_index)
Expand All @@ -1419,19 +1420,6 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
decrease_balance(state, slashed_index, whistleblowing_reward)
```

#### `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

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.
Expand Down Expand Up @@ -1536,11 +1524,14 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Validator registry
validator_registry=[],
balances=[],
validator_registry_update_epoch=GENESIS_EPOCH,

# Randomness and committees
latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]),
latest_start_shard=GENESIS_START_SHARD,

# Exit queue
exit_epoch=GENESIS_EPOCH,
exit_queue_filled=0,

# Finality
previous_epoch_attestations=[],
Expand Down Expand Up @@ -1719,16 +1710,16 @@ def get_previous_total_balance(state: BeaconState) -> Gwei:
```

```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()
for a in attestations:
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
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
Expand Down Expand Up @@ -1776,7 +1767,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple
# lexicographically higher hash
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
Expand Down Expand Up @@ -1933,7 +1924,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
for index in eligible_validators:
JustinDrake marked this conversation as resolved.
Show resolved Hide resolved
base_reward = get_base_reward(state, index)
# 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
# Inclusion speed bonus
rewards[index] += (
Expand All @@ -1943,17 +1934,17 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
else:
penalties[index] += base_reward
# 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
else:
penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality)
# 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
else:
penalties[index] += base_reward
# 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))
rewards[proposer_index] += base_reward // PROPOSER_REWARD_QUOTIENT
# Take away max rewards if we're not finalizing
Expand Down Expand Up @@ -2002,80 +1993,49 @@ def apply_rewards(state: BeaconState) -> None:
)
```

#### Ejections
#### Balance-driven status transitions

Run `process_ejections(state)`.
Run `process_balance_driven_status_transitions(state)`.

```python
def process_ejections(state: BeaconState) -> None:
def process_balance_driven_status_transitions(state: BeaconState) -> None:
"""
Iterate through the validator registry
and eject active validators with balance below ``EJECTION_BALANCE``.
"""
for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)):
if get_balance(state, index) < EJECTION_BALANCE:
initiate_validator_exit(state, index)
```

#### Validator registry and shuffling seed data

```python
def update_validator_registry(state: BeaconState) -> None:
and deposit or eject active validators with sufficiently high or low balances
"""
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
balance = get_balance(state, index)
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT:
state.activation_eligibility_epoch = get_current_epoch(state)

# Exit validator
exit_validator(state, index)

state.validator_registry_update_epoch = current_epoch
if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE:
initiate_validator_exit(state, index)
```

#### Validator registry and start shard

Run the following function:

```python
def update_registry(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
# Check if we should update, and if so, update
if state.finalized_epoch > state.validator_registry_update_epoch:
update_validator_registry(state)

activations_since_finalization = len([index in state.validator_registry if
state.validator_registry[index].activation_epoch > state.finalized_epoch + ACTIVATION_EXIT_DELAY
])
JustinDrake marked this conversation as resolved.
Show resolved Hide resolved
if MAX_EXIT_DEQUEUES_PER_EPOCH > activations_since_finalization:
# 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 - activations_since_finalization]:
activate_validator(state, index, is_genesis=False)

state.latest_start_shard = (
state.latest_start_shard +
get_current_epoch_committee_count(state)
Expand All @@ -2086,7 +2046,7 @@ def update_registry(state: BeaconState) -> None:

#### Slashings and exit queue

Run `process_slashings(state)` and `process_exit_queue(state)`:
Run `process_slashings(state)`:

```python
def process_slashings(state: BeaconState) -> None:
Expand All @@ -2112,30 +2072,6 @@ def process_slashings(state: BeaconState) -> None:
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

Run the following function:
Expand Down Expand Up @@ -2375,8 +2311,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
assert is_active_validator(validator, get_current_epoch(state))
# Verify the validator has not yet exited
assert validator.exit_epoch == FAR_FUTURE_EPOCH
# Verify the validator has not initiated an exit
assert validator.initiated_exit is False
# Exits must specify an epoch when they become valid; they are not valid before then
assert get_current_epoch(state) >= exit.epoch
# Verify the validator has been active long enough
Expand Down