Bring forward changes to withdrawability from phase 1 (#615)

* Bring forward changes to withdrawability from phase 1

* The `WITHDRAWABLE` flag is removed; instead, a validator's withdrawability is determined through the `withdrawable_epoch` field (renamed and re-purposed from `withdrawal_epoch` which was not used)
* When a validator passes through the withdrawal queue, the `prepare_validator_for_withdrawal` function does not let them withdraw immediately; instead, they have to wait `MIN_VALIDATOR_WITHDRAWAL_EPOCHS`. This extra minimum delay serves no value in phase 0, but is crucial for phase 1 as the period between a validator passing through the queue and the validator being eligible to withdraw is where proof of custody challenges can come in; adding it in phase 0 is only half a line of code so easier to add it now.
* If a validator is penalized, they are no longer subject to the exit queue; instead, their `withdrawable_epoch` is set `LATEST_PENALIZED_EXIT_LENGTH` into the future and this is used to determine when the validator can withdraw
* Changes the eligibility condition for a transfer to use the `withdrawable_epoch`
This commit is contained in:
vbuterin 2019-02-14 04:20:53 -06:00 committed by Justin
parent 83b67b6097
commit eadfa20b99
1 changed files with 19 additions and 14 deletions

View File

@ -126,6 +126,7 @@
- [Attestations](#attestations-1)
- [Deposits](#deposits-1)
- [Exits](#exits-1)
- [Transfers](#transfers-1)
- [Per-epoch processing](#per-epoch-processing)
- [Helper variables](#helper-variables)
- [Eth1 data](#eth1-data-1)
@ -232,8 +233,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| `SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes |
| `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes |
| `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours |
| `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours |
| `MIN_EXIT_EPOCHS_BEFORE_TRANSFER` | `2**13` (= 8,192) | epochs | ~36 days |
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
### State list lengths
@ -261,7 +261,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| Name | Value |
| - | - |
| `INITIATED_EXIT` | `2**0` (= 1) |
| `WITHDRAWABLE` | `2**1` (= 2) |
### Max operations per block
@ -445,6 +444,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
}
```
#### Transfers
##### `Transfer`
```python
@ -569,8 +570,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
'activation_epoch': 'uint64',
# Epoch when validator exited
'exit_epoch': 'uint64',
# Epoch when validator withdrew
'withdrawal_epoch': 'uint64',
# Epoch when validator is eligible to withdraw
'withdrawable_epoch': 'uint64',
# Epoch when validator was penalized
'penalized_epoch': 'uint64',
# Status flags
@ -1298,7 +1299,7 @@ def process_deposit(state: BeaconState,
withdrawal_credentials=withdrawal_credentials,
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
withdrawal_epoch=FAR_FUTURE_EPOCH,
withdrawable_epoch=FAR_FUTURE_EPOCH,
penalized_epoch=FAR_FUTURE_EPOCH,
status_flags=0,
)
@ -1368,6 +1369,7 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None:
Penalize the validator of the given ``index``.
Note that this function mutates ``state``.
"""
assert state.slot < validator.withdrawable_epoch # [TO BE REMOVED IN PHASE 2]
exit_validator(state, index)
validator = state.validator_registry[index]
state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index)
@ -1377,6 +1379,7 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None:
state.validator_balances[whistleblower_index] += whistleblower_reward
state.validator_balances[index] -= whistleblower_reward
validator.penalized_epoch = get_current_epoch(state)
validator.withdrawable_epoch = get_current_epoch(state) + LATEST_PENALIZED_EXIT_LENGTH
```
#### `prepare_validator_for_withdrawal`
@ -1384,11 +1387,12 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None:
```python
def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) -> None:
"""
Set the validator with the given ``index`` with ``WITHDRAWABLE`` flag.
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.status_flags |= WITHDRAWABLE
validator.withdrawable_epoch = get_current_epoch(state) + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
```
## Ethereum 1.0 deposit contract
@ -1804,8 +1808,8 @@ For each `transfer` in `block.body.transfers`:
* Verify that `state.validator_balances[transfer.from] >= transfer.amount`.
* Verify that `state.validator_balances[transfer.from] >= transfer.fee`.
* Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`.
* Verify that `transfer.slot == state.slot`.
* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`.
* Verify that `state.slot == transfer.slot`.
* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch`.
* Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`.
* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`.
* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`.
@ -2064,11 +2068,12 @@ def process_penalties_and_exits(state: BeaconState) -> None:
def eligible(index):
validator = state.validator_registry[index]
if validator.penalized_epoch <= current_epoch:
penalized_withdrawal_epochs = LATEST_PENALIZED_EXIT_LENGTH // 2
return current_epoch >= validator.penalized_epoch + penalized_withdrawal_epochs
# Cannot exit if you already have
if validator.withdrawable_epoch < FAR_FUTURE_EPOCH:
return False
# Can exit if at least the minimum amount of time has passed
else:
return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS
return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
all_indices = list(range(len(state.validator_registry)))
eligible_indices = filter(eligible, all_indices)