Merge pull request #253 from ethereum/JustinDrake-patch-8
Bug fixes and cleanups around exit_validator
This commit is contained in:
commit
ebc136a3bd
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue