Merge pull request #253 from ethereum/JustinDrake-patch-8

Bug fixes and cleanups around exit_validator
This commit is contained in:
Danny Ryan 2018-12-10 09:15:32 -06:00 committed by GitHub
commit ebc136a3bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 46 additions and 23 deletions

View File

@ -57,6 +57,7 @@
- [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule)
- [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Helper functions](#helper-functions) - [Helper functions](#helper-functions)
- [`is_active_validator`](#is_active_validator)
- [`get_active_validator_indices`](#get_active_validator_indices) - [`get_active_validator_indices`](#get_active_validator_indices)
- [`shuffle`](#shuffle) - [`shuffle`](#shuffle)
- [`split`](#split) - [`split`](#split)
@ -86,6 +87,7 @@
- [Attestations](#attestations-1) - [Attestations](#attestations-1)
- [Deposits](#deposits-1) - [Deposits](#deposits-1)
- [Exits](#exits-1) - [Exits](#exits-1)
- [Ejections](#ejections)
- [Per-epoch processing](#per-epoch-processing) - [Per-epoch processing](#per-epoch-processing)
- [Helpers](#helpers) - [Helpers](#helpers)
- [Receipt roots](#receipt-roots) - [Receipt roots](#receipt-roots)
@ -143,7 +145,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| - | - | :-: | | - | - | :-: |
| `SHARD_COUNT` | `2**10` (= 1,024) | shards | | `SHARD_COUNT` | `2**10` (= 1,024) | shards |
| `TARGET_COMMITTEE_SIZE` | `2**8` (= 256) | [validators](#dfn-validator) | | `TARGET_COMMITTEE_SIZE` | `2**8` (= 256) | [validators](#dfn-validator) |
| `MIN_BALANCE` | `2**4` (= 16) | ETH | | `EJECTION_BALANCE` | `2**4` (= 16) | ETH |
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - |
| `GWEI_PER_ETH` | `10**9` | Gwei/ETH | | `GWEI_PER_ETH` | `10**9` | Gwei/ETH |
| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - |
@ -701,6 +703,15 @@ The per-slot transitions generally focus on verifying aggregate signatures and s
Note: The definitions below are for specification purposes and are not necessarily optimal implementations. Note: The definitions below are for specification purposes and are not necessarily optimal implementations.
#### `is_active_validator`
```python
def is_active_validator(validator: ValidatorRecord) -> bool:
"""
Checks if ``validator`` is active.
"""
return validator.status in [ACTIVE, ACTIVE_PENDING_EXIT]
```
#### `get_active_validator_indices` #### `get_active_validator_indices`
```python ```python
@ -708,7 +719,7 @@ def get_active_validator_indices(validators: [ValidatorRecord]) -> List[int]:
""" """
Gets indices of active validators from ``validators``. Gets indices of active validators from ``validators``.
""" """
return [i for i, v in enumerate(validators) if v.status in [ACTIVE, ACTIVE_PENDING_EXIT]] return [i for i, v in enumerate(validators) if is_active_validator(v)]
``` ```
#### `shuffle` #### `shuffle`
@ -895,7 +906,7 @@ def get_updated_ancestor_hashes(latest_block: BeaconBlock,
#### `get_attestation_participants` #### `get_attestation_participants`
```python ```python
def get_attestation_participants(state: State, def get_attestation_participants(state: BeaconState,
attestation_data: AttestationData, attestation_data: AttestationData,
participation_bitfield: bytes) -> List[int]: participation_bitfield: bytes) -> List[int]:
""" """
@ -963,7 +974,7 @@ def get_domain(fork_data: ForkData,
#### `verify_casper_votes` #### `verify_casper_votes`
```python ```python
def verify_casper_votes(state: State, votes: SlashableVoteData) -> bool: def verify_casper_votes(state: BeaconState, votes: SlashableVoteData) -> bool:
if len(votes.aggregate_signature_poc_0_indices) + len(votes.aggregate_signature_poc_1_indices) > MAX_CASPER_VOTES: if len(votes.aggregate_signature_poc_0_indices) + len(votes.aggregate_signature_poc_1_indices) > MAX_CASPER_VOTES:
return False return False
@ -1032,8 +1043,8 @@ def on_startup(initial_validator_entries: List[Any],
proof_of_possession=proof_of_possession, proof_of_possession=proof_of_possession,
withdrawal_credentials=withdrawal_credentials, withdrawal_credentials=withdrawal_credentials,
randao_commitment=randao_commitment, randao_commitment=randao_commitment,
current_slot=INITIAL_SLOT_NUMBER,
status=ACTIVE, status=ACTIVE,
current_slot=INITIAL_SLOT_NUMBER,
) )
# Setup state # Setup state
@ -1166,8 +1177,7 @@ def process_deposit(state: BeaconState,
proof_of_possession: bytes, proof_of_possession: bytes,
withdrawal_credentials: Hash32, withdrawal_credentials: Hash32,
randao_commitment: Hash32, randao_commitment: Hash32,
status: int, status: int) -> int:
current_slot: int) -> int:
""" """
Process a deposit from Ethereum 1.0. Process a deposit from Ethereum 1.0.
Note that this function mutates ``state``. Note that this function mutates ``state``.
@ -1185,7 +1195,7 @@ def process_deposit(state: BeaconState,
withdrawal_credentials=withdrawal_credentials, withdrawal_credentials=withdrawal_credentials,
randao_commitment=randao_commitment, randao_commitment=randao_commitment,
status=status, status=status,
current_slot=current_slot, current_slot=state.slot,
) )
return index return index
@ -1196,8 +1206,7 @@ def process_deposit(state: BeaconState,
```python ```python
def exit_validator(index: int, def exit_validator(index: int,
state: BeaconState, state: BeaconState,
penalize: bool, new_status: bool) -> None:
current_slot: int) -> None:
""" """
Exit the validator with the given ``index``. Exit the validator with the given ``index``.
Note that this function mutates ``state``. Note that this function mutates ``state``.
@ -1205,7 +1214,8 @@ def exit_validator(index: int,
state.validator_registry_exit_count += 1 state.validator_registry_exit_count += 1
validator = state.validator_registry[index] validator = state.validator_registry[index]
validator.latest_status_change_slot = current_slot validator.status = new_status
validator.latest_status_change_slot = state.slot
validator.exit_count = state.validator_registry_exit_count validator.exit_count = state.validator_registry_exit_count
# Remove validator from persistent committees # Remove validator from persistent committees
@ -1215,16 +1225,13 @@ def exit_validator(index: int,
committee.pop(i) committee.pop(i)
break break
if penalize: if new_status == EXITED_WITH_PENALTY:
validator.status = EXITED_WITH_PENALTY state.latest_penalized_exit_balances[state.slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += get_effective_balance(validator)
state.latest_penalized_exit_balances[current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += get_effective_balance(validator)
whistleblower = state.validator_registry[get_beacon_proposer_index(state, current_slot)] whistleblower = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
whistleblower_reward = validator.balance // WHISTLEBLOWER_REWARD_QUOTIENT whistleblower_reward = validator.balance // WHISTLEBLOWER_REWARD_QUOTIENT
whistleblower.balance += whistleblower_reward whistleblower.balance += whistleblower_reward
validator.balance -= whistleblower_reward validator.balance -= whistleblower_reward
else:
validator.status = ACTIVE_PENDING_EXIT
state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip(
validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip,
@ -1244,11 +1251,13 @@ Below are the processing steps that happen at every slot.
* Set `state.latest_block_hashes = state.latest_block_hashes + [latest_hash]`. (The output of `get_block_hash` should not change, except that it will no longer throw for `state.slot - 1`). * Set `state.latest_block_hashes = state.latest_block_hashes + [latest_hash]`. (The output of `get_block_hash` should not change, except that it will no longer throw for `state.slot - 1`).
If there is a block from the proposer for `state.slot`, we process that incoming block: If there is a block from the proposer for `state.slot`, we process that incoming block:
* Let `block` be that associated incoming block. * Let `block` be that associated incoming block.
* Verify that `block.slot == state.slot` * Verify that `block.slot == state.slot`
* Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_hash)`. * Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_hash)`.
If there is no block from the proposer at state.slot: If there is no block from the proposer at state.slot:
* Set `state.validator_registry[get_beacon_proposer_index(state, state.slot)].randao_skips += 1`. * Set `state.validator_registry[get_beacon_proposer_index(state, state.slot)].randao_skips += 1`.
* Skip all other per-slot processing. Move directly to [per-epoch processing](#per-epoch-processing). * Skip all other per-slot processing. Move directly to [per-epoch processing](#per-epoch-processing).
@ -1287,7 +1296,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`:
* Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`.
* Verify that `proposer_slashing.proposal_data_1.block_hash != proposer_slashing.proposal_data_2.block_hash`. * Verify that `proposer_slashing.proposal_data_1.block_hash != proposer_slashing.proposal_data_2.block_hash`.
* Verify that `proposer.status != EXITED_WITH_PENALTY`. * Verify that `proposer.status != EXITED_WITH_PENALTY`.
* Run `exit_validator(proposer_slashing.proposer_index, state, penalize=True, current_slot=state.slot)`. * Run `exit_validator(proposer_slashing.proposer_index, state, new_status=EXITED_WITH_PENALTY)`.
#### Casper slashings #### Casper slashings
@ -1302,7 +1311,7 @@ For each `casper_slashing` in `block.body.casper_slashings`:
* Let `intersection = [x for x in indices(casper_slashing.votes_1) if x in indices(casper_slashing.votes_2)]`. * Let `intersection = [x for x in indices(casper_slashing.votes_1) if x in indices(casper_slashing.votes_2)]`.
* Verify that `len(intersection) >= 1`. * Verify that `len(intersection) >= 1`.
* Verify that `casper_slashing.votes_1.data.justified_slot + 1 < casper_slashing.votes_2.data.justified_slot + 1 == casper_slashing.votes_2.data.slot < casper_slashing.votes_1.data.slot` or `casper_slashing.votes_1.data.slot == casper_slashing.votes_2.data.slot`. * Verify that `casper_slashing.votes_1.data.justified_slot + 1 < casper_slashing.votes_2.data.justified_slot + 1 == casper_slashing.votes_2.data.slot < casper_slashing.votes_1.data.slot` or `casper_slashing.votes_1.data.slot == casper_slashing.votes_2.data.slot`.
* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `exit_validator(i, state, penalize=True, current_slot=state.slot)` * For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `exit_validator(i, state, new_status=EXITED_WITH_PENALTY)`
#### Attestations #### Attestations
@ -1353,8 +1362,7 @@ process_deposit(
proof_of_possession=deposit.deposit_data.deposit_parameters.proof_of_possession, proof_of_possession=deposit.deposit_data.deposit_parameters.proof_of_possession,
withdrawal_credentials=deposit.deposit_data.deposit_parameters.withdrawal_credentials, withdrawal_credentials=deposit.deposit_data.deposit_parameters.withdrawal_credentials,
randao_commitment=deposit.deposit_data.deposit_parameters.randao_commitment, randao_commitment=deposit.deposit_data.deposit_parameters.randao_commitment,
status=PENDING_ACTIVATION, status=PENDING_ACTIVATION
current_slot=state.slot
) )
``` ```
@ -1369,7 +1377,22 @@ For each `exit` in `block.body.exits`:
* Verify that `validator.status == ACTIVE`. * Verify that `validator.status == ACTIVE`.
* Verify that `state.slot >= exit.slot`. * Verify that `state.slot >= exit.slot`.
* Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`. * Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`.
* Run `exit_validator(validator_index, state, penalize=False, current_slot=state.slot)`. * Run `exit_validator(validator_index, state, new_status=ACTIVE_PENDING_EXIT)`.
### Ejections
* Run `process_ejections(state)`.
```python
def process_ejections(state: BeaconState) -> None:
"""
Iterate through the validator registry
and eject active validators with balance below ``EJECTION_BALANCE``.
"""
for i, v in enumerate(state.validator_registry):
if is_active_validator(v) and v.balance < EJECTION_BALANCE:
exit_validator(i, state, new_status=EXITED_WITHOUT_PENALTY)
```
## Per-epoch processing ## Per-epoch processing
@ -1437,6 +1460,7 @@ If `state.slot % POW_RECEIPT_ROOT_VOTING_PERIOD == 0`:
### Finalization ### Finalization
Set `state.finalized_slot = state.previous_justified_slot` if any of the following are true: 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` * `state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3`
* `state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and state.justification_bitfield % 8 == 7` * `state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and state.justification_bitfield % 8 == 7`
* `state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and state.justification_bitfield % 16 in (15, 14)` * `state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and state.justification_bitfield % 16 in (15, 14)`
@ -1620,7 +1644,6 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com
### Final updates ### Final updates
* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`.
* Run `exit_validator(i, state, penalize=False, current_slot=state.slot)` for indices `i` such that `state.validator_registry[i].status == ACTIVE and state.validator_registry[i].balance < MIN_BALANCE`.
* Set `state.latest_block_hashes = state.latest_block_hashes[EPOCH_LENGTH:]`. * Set `state.latest_block_hashes = state.latest_block_hashes[EPOCH_LENGTH:]`.
## State root processing ## State root processing