Merge pull request #850 from ethereum/vbuterin-patch-13
Withdrawal queue -> exit queue
This commit is contained in:
commit
7840d29f2d
|
@ -93,15 +93,14 @@
|
||||||
- [`is_surround_vote`](#is_surround_vote)
|
- [`is_surround_vote`](#is_surround_vote)
|
||||||
- [`integer_squareroot`](#integer_squareroot)
|
- [`integer_squareroot`](#integer_squareroot)
|
||||||
- [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch)
|
- [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch)
|
||||||
|
- [`get_churn_limit`](#get_churn_limit)
|
||||||
- [`bls_verify`](#bls_verify)
|
- [`bls_verify`](#bls_verify)
|
||||||
- [`bls_verify_multiple`](#bls_verify_multiple)
|
- [`bls_verify_multiple`](#bls_verify_multiple)
|
||||||
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
|
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
|
||||||
- [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)
|
||||||
|
@ -122,9 +121,9 @@
|
||||||
- [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)
|
- [Activation queue and start shard](#activation-queue-and-start-shard)
|
||||||
- [Slashings and exit queue](#slashings-and-exit-queue)
|
- [Slashings](#slashings)
|
||||||
- [Final updates](#final-updates)
|
- [Final updates](#final-updates)
|
||||||
- [Per-slot processing](#per-slot-processing)
|
- [Per-slot processing](#per-slot-processing)
|
||||||
- [Per-block processing](#per-block-processing)
|
- [Per-block processing](#per-block-processing)
|
||||||
|
@ -183,9 +182,9 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| - | - |
|
| - | - |
|
||||||
| `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_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
|
| `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
|
||||||
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
|
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
|
||||||
|
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
|
||||||
| `SHUFFLE_ROUND_COUNT` | 90 |
|
| `SHUFFLE_ROUND_COUNT` | 90 |
|
||||||
|
|
||||||
* For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
* For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||||
|
@ -238,7 +237,6 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
|
|
||||||
* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
|
* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
|
||||||
|
|
||||||
|
|
||||||
### State list lengths
|
### State list lengths
|
||||||
|
|
||||||
| Name | Value | Unit | Duration |
|
| Name | Value | Unit | Duration |
|
||||||
|
@ -417,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
|
||||||
|
@ -590,12 +588,11 @@ The types are defined topologically to aid in facilitating an executable version
|
||||||
# Validator registry
|
# Validator registry
|
||||||
'validator_registry': [Validator],
|
'validator_registry': [Validator],
|
||||||
'balances': ['uint64'],
|
'balances': ['uint64'],
|
||||||
'validator_registry_update_epoch': 'uint64',
|
|
||||||
|
|
||||||
# Randomness and committees
|
# Randomness and committees
|
||||||
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
|
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
|
||||||
'latest_start_shard': 'uint64',
|
'latest_start_shard': 'uint64',
|
||||||
|
|
||||||
# Finality
|
# Finality
|
||||||
'previous_epoch_attestations': [PendingAttestation],
|
'previous_epoch_attestations': [PendingAttestation],
|
||||||
'current_epoch_attestations': [PendingAttestation],
|
'current_epoch_attestations': [PendingAttestation],
|
||||||
|
@ -744,11 +741,11 @@ def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||||
### `get_active_validator_indices`
|
### `get_active_validator_indices`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_active_validator_indices(validators: List[Validator], epoch: Epoch) -> List[ValidatorIndex]:
|
def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> List[ValidatorIndex]:
|
||||||
"""
|
"""
|
||||||
Get indices of active validators from ``validators``.
|
Get active validator indices at ``epoch``.
|
||||||
"""
|
"""
|
||||||
return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)]
|
return [i for i, v in enumerate(state.validator_registry) if is_active_validator(v, epoch)]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_balance`
|
### `get_balance`
|
||||||
|
@ -828,11 +825,11 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_split_offset(list_size: int, chunks: int, index: int) -> int:
|
def get_split_offset(list_size: int, chunks: int, index: int) -> int:
|
||||||
"""
|
"""
|
||||||
Returns a value such that for a list L, chunk count k and index i,
|
Returns a value such that for a list L, chunk count k and index i,
|
||||||
split(L, k)[i] == L[get_split_offset(len(L), k, i): get_split_offset(len(L), k, i+1)]
|
split(L, k)[i] == L[get_split_offset(len(L), k, i): get_split_offset(len(L), k, i+1)]
|
||||||
"""
|
"""
|
||||||
return (list_size * index) // chunks
|
return (list_size * index) // chunks
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_epoch_committee_count`
|
### `get_epoch_committee_count`
|
||||||
|
@ -842,7 +839,7 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int:
|
||||||
"""
|
"""
|
||||||
Return the number of committees in one epoch.
|
Return the number of committees in one epoch.
|
||||||
"""
|
"""
|
||||||
active_validators = get_active_validator_indices(state.validator_registry, epoch)
|
active_validators = get_active_validator_indices(state, epoch)
|
||||||
return max(
|
return max(
|
||||||
1,
|
1,
|
||||||
min(
|
min(
|
||||||
|
@ -894,10 +891,7 @@ def get_crosslink_committees_at_slot(state: BeaconState,
|
||||||
next_epoch = current_epoch + 1
|
next_epoch = current_epoch + 1
|
||||||
|
|
||||||
assert previous_epoch <= epoch <= next_epoch
|
assert previous_epoch <= epoch <= next_epoch
|
||||||
indices = get_active_validator_indices(
|
indices = get_active_validator_indices(state, epoch)
|
||||||
state.validator_registry,
|
|
||||||
epoch,
|
|
||||||
)
|
|
||||||
|
|
||||||
if epoch == current_epoch:
|
if epoch == current_epoch:
|
||||||
start_shard = state.latest_start_shard
|
start_shard = state.latest_start_shard
|
||||||
|
@ -1037,9 +1031,9 @@ def get_crosslink_committee_for_attestation(state: BeaconState,
|
||||||
attestation_data: AttestationData) -> List[ValidatorIndex]:
|
attestation_data: AttestationData) -> List[ValidatorIndex]:
|
||||||
"""
|
"""
|
||||||
Return the crosslink committee corresponding to ``attestation_data``.
|
Return the crosslink committee corresponding to ``attestation_data``.
|
||||||
"""
|
"""
|
||||||
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
||||||
|
|
||||||
# Find the committee in the list with the desired shard
|
# Find the committee in the list with the desired shard
|
||||||
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
||||||
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
||||||
|
@ -1268,6 +1262,16 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
||||||
return epoch + 1 + ACTIVATION_EXIT_DELAY
|
return epoch + 1 + ACTIVATION_EXIT_DELAY
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `get_churn_limit`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_churn_limit(state: BeaconState) -> int:
|
||||||
|
return max(
|
||||||
|
MIN_PER_EPOCH_CHURN_LIMIT,
|
||||||
|
len(get_active_validator_indices(state, get_current_epoch(state))) // CHURN_LIMIT_QUOTIENT
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### `bls_verify`
|
### `bls_verify`
|
||||||
|
|
||||||
`bls_verify` is a function for verifying a BLS signature, defined in the [BLS Signature spec](../bls_signature.md#bls_verify).
|
`bls_verify` is a function for verifying a BLS signature, defined in the [BLS Signature spec](../bls_signature.md#bls_verify).
|
||||||
|
@ -1280,7 +1284,6 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
||||||
|
|
||||||
`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys).
|
`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys).
|
||||||
|
|
||||||
|
|
||||||
### Routines for updating validator status
|
### Routines for updating validator status
|
||||||
|
|
||||||
Note: All functions in this section mutate `state`.
|
Note: All functions in this section mutate `state`.
|
||||||
|
@ -1295,7 +1298,11 @@ def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bo
|
||||||
"""
|
"""
|
||||||
validator = state.validator_registry[index]
|
validator = state.validator_registry[index]
|
||||||
|
|
||||||
validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_delayed_activation_exit_epoch(get_current_epoch(state))
|
if is_genesis:
|
||||||
|
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||||
|
validator.activation_epoch = GENESIS_EPOCH
|
||||||
|
else:
|
||||||
|
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `initiate_validator_exit`
|
#### `initiate_validator_exit`
|
||||||
|
@ -1306,23 +1313,21 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
||||||
Initiate the validator of the given ``index``.
|
Initiate the validator of the given ``index``.
|
||||||
Note that this function mutates ``state``.
|
Note that this function mutates ``state``.
|
||||||
"""
|
"""
|
||||||
|
# Return if validator already initiated exit
|
||||||
validator = state.validator_registry[index]
|
validator = state.validator_registry[index]
|
||||||
validator.initiated_exit = True
|
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
||||||
```
|
return
|
||||||
|
|
||||||
#### `exit_validator`
|
# Compute exit queue epoch
|
||||||
|
exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH]
|
||||||
|
exit_queue_epoch = sorted(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])[-1]
|
||||||
|
exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch])
|
||||||
|
if exit_queue_churn >= get_churn_limit(state):
|
||||||
|
exit_queue_epoch += 1
|
||||||
|
|
||||||
```python
|
# Set validator exit epoch and withdrawable epoch
|
||||||
def exit_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
validator.exit_epoch = exit_queue_epoch
|
||||||
"""
|
validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||||
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:
|
|
||||||
validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `slash_validator`
|
#### `slash_validator`
|
||||||
|
@ -1333,7 +1338,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
|
||||||
Slash the validator with index ``slashed_index``.
|
Slash the validator with index ``slashed_index``.
|
||||||
Note that this function mutates ``state``.
|
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].slashed = True
|
||||||
state.validator_registry[slashed_index].withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
|
state.validator_registry[slashed_index].withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
|
||||||
slashed_balance = get_effective_balance(state, slashed_index)
|
slashed_balance = get_effective_balance(state, slashed_index)
|
||||||
|
@ -1349,19 +1354,6 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
|
||||||
decrease_balance(state, slashed_index, whistleblowing_reward)
|
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
|
## 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.
|
||||||
|
@ -1466,7 +1458,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
|
||||||
# Validator registry
|
# Validator registry
|
||||||
validator_registry=[],
|
validator_registry=[],
|
||||||
balances=[],
|
balances=[],
|
||||||
validator_registry_update_epoch=GENESIS_EPOCH,
|
|
||||||
|
|
||||||
# Randomness and committees
|
# Randomness and committees
|
||||||
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)]),
|
||||||
|
@ -1503,11 +1494,11 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
|
||||||
process_deposit(state, deposit)
|
process_deposit(state, deposit)
|
||||||
|
|
||||||
# Process genesis activations
|
# Process genesis activations
|
||||||
for validator_index, _ in enumerate(state.validator_registry):
|
for validator_index in range(len(state.validator_registry)):
|
||||||
if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT:
|
if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT:
|
||||||
activate_validator(state, validator_index, is_genesis=True)
|
activate_validator(state, validator_index, is_genesis=True)
|
||||||
|
|
||||||
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH))
|
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH))
|
||||||
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
|
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
|
||||||
state.latest_active_index_roots[index] = genesis_active_index_root
|
state.latest_active_index_roots[index] = genesis_active_index_root
|
||||||
|
|
||||||
|
@ -1641,25 +1632,25 @@ We define some helper functions utilized when processing an epoch transition:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_current_total_balance(state: BeaconState) -> Gwei:
|
def get_current_total_balance(state: BeaconState) -> Gwei:
|
||||||
return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_current_epoch(state)))
|
return get_total_balance(state, get_active_validator_indices(state, get_current_epoch(state)))
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_previous_total_balance(state: BeaconState) -> Gwei:
|
def get_previous_total_balance(state: BeaconState) -> Gwei:
|
||||||
return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_previous_epoch(state)))
|
return get_total_balance(state, get_active_validator_indices(state, get_previous_epoch(state)))
|
||||||
```
|
```
|
||||||
|
|
||||||
```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].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
|
||||||
|
@ -1707,7 +1698,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
|
||||||
|
@ -1864,7 +1855,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] += (
|
||||||
|
@ -1874,17 +1865,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 // PROPOSER_REWARD_QUOTIENT
|
rewards[proposer_index] += base_reward // PROPOSER_REWARD_QUOTIENT
|
||||||
# Take away max rewards if we're not finalizing
|
# Take away max rewards if we're not finalizing
|
||||||
|
@ -1933,91 +1924,49 @@ 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_balance_driven_status_transitions(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:
|
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)
|
||||||
|
|
||||||
|
if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE:
|
||||||
initiate_validator_exit(state, index)
|
initiate_validator_exit(state, index)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Validator registry and shuffling seed data
|
#### Activation queue 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:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def update_registry(state: BeaconState) -> None:
|
def update_registry(state: BeaconState) -> None:
|
||||||
# Check if we should update, and if so, update
|
activation_queue = sorted([
|
||||||
if state.finalized_epoch > state.validator_registry_update_epoch:
|
validator for validator in state.validator_registry if
|
||||||
update_validator_registry(state)
|
validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and
|
||||||
|
validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch)
|
||||||
|
], key=lambda index: state.validator_registry[index].activation_eligibility_epoch)
|
||||||
|
|
||||||
|
for index in activation_queue[:get_churn_limit(state)]:
|
||||||
|
activate_validator(state, index, is_genesis=False)
|
||||||
|
|
||||||
state.latest_start_shard = (
|
state.latest_start_shard = (
|
||||||
state.latest_start_shard +
|
state.latest_start_shard +
|
||||||
get_shard_delta(state, get_current_epoch(state))
|
get_shard_delta(state, get_current_epoch(state))
|
||||||
) % SHARD_COUNT
|
) % SHARD_COUNT
|
||||||
```
|
```
|
||||||
|
|
||||||
**Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch.
|
#### Slashings
|
||||||
|
|
||||||
#### Slashings and exit queue
|
Run `process_slashings(state)`:
|
||||||
|
|
||||||
Run `process_slashings(state)` and `process_exit_queue(state)`:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_slashings(state: BeaconState) -> None:
|
def process_slashings(state: BeaconState) -> None:
|
||||||
|
@ -2026,7 +1975,7 @@ def process_slashings(state: BeaconState) -> None:
|
||||||
Note that this function mutates ``state``.
|
Note that this function mutates ``state``.
|
||||||
"""
|
"""
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch)
|
active_validator_indices = get_active_validator_indices(state, current_epoch)
|
||||||
total_balance = get_total_balance(state, active_validator_indices)
|
total_balance = get_total_balance(state, active_validator_indices)
|
||||||
|
|
||||||
# Compute `total_penalties`
|
# Compute `total_penalties`
|
||||||
|
@ -2043,30 +1992,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:
|
||||||
|
@ -2078,7 +2003,7 @@ def finish_epoch_update(state: BeaconState) -> None:
|
||||||
# Set active index root
|
# Set active index root
|
||||||
index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH
|
index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH
|
||||||
state.latest_active_index_roots[index_root_position] = hash_tree_root(
|
state.latest_active_index_roots[index_root_position] = hash_tree_root(
|
||||||
get_active_validator_indices(state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY)
|
get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY)
|
||||||
)
|
)
|
||||||
# Set total slashed balances
|
# Set total slashed balances
|
||||||
state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = (
|
state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = (
|
||||||
|
@ -2342,6 +2267,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||||
validator = Validator(
|
validator = Validator(
|
||||||
pubkey=pubkey,
|
pubkey=pubkey,
|
||||||
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||||
|
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
||||||
activation_epoch=FAR_FUTURE_EPOCH,
|
activation_epoch=FAR_FUTURE_EPOCH,
|
||||||
exit_epoch=FAR_FUTURE_EPOCH,
|
exit_epoch=FAR_FUTURE_EPOCH,
|
||||||
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||||
|
@ -2377,8 +2303,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
|
||||||
assert is_active_validator(validator, get_current_epoch(state))
|
assert is_active_validator(validator, get_current_epoch(state))
|
||||||
# Verify the validator has not yet exited
|
# Verify the validator has not yet exited
|
||||||
assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
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
|
# Exits must specify an epoch when they become valid; they are not valid before then
|
||||||
assert get_current_epoch(state) >= exit.epoch
|
assert get_current_epoch(state) >= exit.epoch
|
||||||
# Verify the validator has been active long enough
|
# Verify the validator has been active long enough
|
||||||
|
|
|
@ -7,7 +7,6 @@ from build.phase0.state_transition import (
|
||||||
state_transition,
|
state_transition,
|
||||||
)
|
)
|
||||||
from build.phase0.spec import (
|
from build.phase0.spec import (
|
||||||
ZERO_HASH,
|
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
process_attestation,
|
process_attestation,
|
||||||
slot_to_epoch,
|
slot_to_epoch,
|
||||||
|
@ -102,7 +101,7 @@ def test_bad_source_root(state):
|
||||||
attestation = get_valid_attestation(state)
|
attestation = get_valid_attestation(state)
|
||||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
|
||||||
attestation.data.source_root = b'\x42'*32
|
attestation.data.source_root = b'\x42' * 32
|
||||||
|
|
||||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||||
|
|
||||||
|
@ -113,7 +112,7 @@ def test_non_zero_crosslink_data_root(state):
|
||||||
attestation = get_valid_attestation(state)
|
attestation = get_valid_attestation(state)
|
||||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
|
||||||
attestation.data.crosslink_data_root = b'\x42'*32
|
attestation.data.crosslink_data_root = b'\x42' * 32
|
||||||
|
|
||||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True):
|
||||||
|
|
||||||
slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
|
slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
|
||||||
slashed_validator = post_state.validator_registry[slashed_index]
|
slashed_validator = post_state.validator_registry[slashed_index]
|
||||||
assert not slashed_validator.initiated_exit
|
|
||||||
assert slashed_validator.slashed
|
assert slashed_validator.slashed
|
||||||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
|
@ -54,7 +54,7 @@ def test_invalid_slot(state):
|
||||||
|
|
||||||
def test_invalid_previous_block_root(state):
|
def test_invalid_previous_block_root(state):
|
||||||
block = build_empty_block_for_next_slot(state)
|
block = build_empty_block_for_next_slot(state)
|
||||||
block.previous_block_root = b'\12'*32 # invalid prev root
|
block.previous_block_root = b'\12' * 32 # invalid prev root
|
||||||
|
|
||||||
pre_state, post_state = run_block_header_processing(state, block, valid=False)
|
pre_state, post_state = run_block_header_processing(state, block, valid=False)
|
||||||
return pre_state, block, None
|
return pre_state, block, None
|
||||||
|
|
|
@ -15,8 +15,8 @@ from tests.phase0.helpers import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# mark entire file as 'voluntary_exits'
|
# mark entire file as 'deposits'
|
||||||
pytestmark = pytest.mark.voluntary_exits
|
pytestmark = pytest.mark.deposits
|
||||||
|
|
||||||
|
|
||||||
def test_success(state):
|
def test_success(state):
|
||||||
|
|
|
@ -30,7 +30,6 @@ def run_proposer_slashing_processing(state, proposer_slashing, valid=True):
|
||||||
process_proposer_slashing(post_state, proposer_slashing)
|
process_proposer_slashing(post_state, proposer_slashing)
|
||||||
|
|
||||||
slashed_validator = post_state.validator_registry[proposer_slashing.proposer_index]
|
slashed_validator = post_state.validator_registry[proposer_slashing.proposer_index]
|
||||||
assert not slashed_validator.initiated_exit
|
|
||||||
assert slashed_validator.slashed
|
assert slashed_validator.slashed
|
||||||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
|
@ -5,6 +5,7 @@ import build.phase0.spec as spec
|
||||||
|
|
||||||
from build.phase0.spec import (
|
from build.phase0.spec import (
|
||||||
get_active_validator_indices,
|
get_active_validator_indices,
|
||||||
|
get_churn_limit,
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
process_voluntary_exit,
|
process_voluntary_exit,
|
||||||
)
|
)
|
||||||
|
@ -18,158 +19,145 @@ from tests.phase0.helpers import (
|
||||||
pytestmark = pytest.mark.voluntary_exits
|
pytestmark = pytest.mark.voluntary_exits
|
||||||
|
|
||||||
|
|
||||||
def test_success(state):
|
def run_voluntary_exit_processing(state, voluntary_exit, valid=True):
|
||||||
pre_state = deepcopy(state)
|
"""
|
||||||
#
|
Run ``process_voluntary_exit`` returning the pre and post state.
|
||||||
# setup pre_state
|
If ``valid == False``, run expecting ``AssertionError``
|
||||||
#
|
"""
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
post_state = deepcopy(state)
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
|
||||||
|
|
||||||
#
|
if not valid:
|
||||||
# build voluntary exit
|
with pytest.raises(AssertionError):
|
||||||
#
|
process_voluntary_exit(post_state, voluntary_exit)
|
||||||
current_epoch = get_current_epoch(pre_state)
|
return state, None
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
|
||||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
process_voluntary_exit(post_state, voluntary_exit)
|
||||||
|
|
||||||
|
validator_index = voluntary_exit.validator_index
|
||||||
|
assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
||||||
|
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
return state, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_success(state):
|
||||||
|
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
||||||
|
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
|
validator_index = get_active_validator_indices(state, current_epoch)[0]
|
||||||
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
|
|
||||||
voluntary_exit = build_voluntary_exit(
|
voluntary_exit = build_voluntary_exit(
|
||||||
pre_state,
|
state,
|
||||||
current_epoch,
|
current_epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
privkey,
|
privkey,
|
||||||
)
|
)
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit)
|
||||||
|
return pre_state, voluntary_exit, post_state
|
||||||
|
|
||||||
#
|
|
||||||
# test valid exit
|
|
||||||
#
|
|
||||||
process_voluntary_exit(post_state, voluntary_exit)
|
|
||||||
|
|
||||||
assert not pre_state.validator_registry[validator_index].initiated_exit
|
def test_success_exit_queue(state):
|
||||||
assert post_state.validator_registry[validator_index].initiated_exit
|
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
||||||
|
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
|
|
||||||
|
# exit `MAX_EXITS_PER_EPOCH`
|
||||||
|
initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)]
|
||||||
|
post_state = state
|
||||||
|
for index in initial_indices:
|
||||||
|
privkey = pubkey_to_privkey[state.validator_registry[index].pubkey]
|
||||||
|
voluntary_exit = build_voluntary_exit(
|
||||||
|
state,
|
||||||
|
current_epoch,
|
||||||
|
index,
|
||||||
|
privkey,
|
||||||
|
)
|
||||||
|
|
||||||
|
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
|
||||||
|
|
||||||
|
# exit an additional validator
|
||||||
|
validator_index = get_active_validator_indices(state, current_epoch)[-1]
|
||||||
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
|
voluntary_exit = build_voluntary_exit(
|
||||||
|
state,
|
||||||
|
current_epoch,
|
||||||
|
validator_index,
|
||||||
|
privkey,
|
||||||
|
)
|
||||||
|
|
||||||
|
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
post_state.validator_registry[validator_index].exit_epoch ==
|
||||||
|
post_state.validator_registry[initial_indices[0]].exit_epoch + 1
|
||||||
|
)
|
||||||
|
|
||||||
return pre_state, voluntary_exit, post_state
|
return pre_state, voluntary_exit, post_state
|
||||||
|
|
||||||
|
|
||||||
def test_validator_not_active(state):
|
def test_validator_not_active(state):
|
||||||
pre_state = deepcopy(state)
|
current_epoch = get_current_epoch(state)
|
||||||
current_epoch = get_current_epoch(pre_state)
|
validator_index = get_active_validator_indices(state, current_epoch)[0]
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
|
||||||
|
|
||||||
#
|
state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
pre_state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# build and test voluntary exit
|
# build and test voluntary exit
|
||||||
#
|
#
|
||||||
voluntary_exit = build_voluntary_exit(
|
voluntary_exit = build_voluntary_exit(
|
||||||
pre_state,
|
state,
|
||||||
current_epoch,
|
current_epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
privkey,
|
privkey,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
|
||||||
process_voluntary_exit(pre_state, voluntary_exit)
|
return pre_state, voluntary_exit, post_state
|
||||||
|
|
||||||
return pre_state, voluntary_exit, None
|
|
||||||
|
|
||||||
|
|
||||||
def test_validator_already_exited(state):
|
def test_validator_already_exited(state):
|
||||||
pre_state = deepcopy(state)
|
|
||||||
#
|
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
|
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
current_epoch = get_current_epoch(pre_state)
|
current_epoch = get_current_epoch(state)
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
validator_index = get_active_validator_indices(state, current_epoch)[0]
|
||||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
|
|
||||||
# but validator already has exited
|
# but validator already has exited
|
||||||
pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2
|
state.validator_registry[validator_index].exit_epoch = current_epoch + 2
|
||||||
|
|
||||||
#
|
|
||||||
# build voluntary exit
|
|
||||||
#
|
|
||||||
voluntary_exit = build_voluntary_exit(
|
voluntary_exit = build_voluntary_exit(
|
||||||
pre_state,
|
state,
|
||||||
current_epoch,
|
current_epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
privkey,
|
privkey,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
|
||||||
process_voluntary_exit(pre_state, voluntary_exit)
|
return pre_state, voluntary_exit, post_state
|
||||||
|
|
||||||
return pre_state, voluntary_exit, None
|
|
||||||
|
|
||||||
|
|
||||||
def test_validator_already_initiated_exit(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
#
|
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
|
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
|
||||||
|
|
||||||
current_epoch = get_current_epoch(pre_state)
|
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
|
||||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
|
||||||
|
|
||||||
# but validator already has initiated exit
|
|
||||||
pre_state.validator_registry[validator_index].initiated_exit = True
|
|
||||||
|
|
||||||
#
|
|
||||||
# build voluntary exit
|
|
||||||
#
|
|
||||||
voluntary_exit = build_voluntary_exit(
|
|
||||||
pre_state,
|
|
||||||
current_epoch,
|
|
||||||
validator_index,
|
|
||||||
privkey,
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
|
||||||
process_voluntary_exit(pre_state, voluntary_exit)
|
|
||||||
|
|
||||||
return pre_state, voluntary_exit, None
|
|
||||||
|
|
||||||
|
|
||||||
def test_validator_not_active_long_enough(state):
|
def test_validator_not_active_long_enough(state):
|
||||||
pre_state = deepcopy(state)
|
current_epoch = get_current_epoch(state)
|
||||||
#
|
validator_index = get_active_validator_indices(state, current_epoch)[0]
|
||||||
# setup pre_state
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
#
|
|
||||||
current_epoch = get_current_epoch(pre_state)
|
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
|
||||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
|
||||||
|
|
||||||
# but validator already has initiated exit
|
|
||||||
pre_state.validator_registry[validator_index].initiated_exit = True
|
|
||||||
|
|
||||||
#
|
|
||||||
# build voluntary exit
|
|
||||||
#
|
|
||||||
voluntary_exit = build_voluntary_exit(
|
voluntary_exit = build_voluntary_exit(
|
||||||
pre_state,
|
state,
|
||||||
current_epoch,
|
current_epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
privkey,
|
privkey,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
current_epoch - pre_state.validator_registry[validator_index].activation_epoch <
|
current_epoch - state.validator_registry[validator_index].activation_epoch <
|
||||||
spec.PERSISTENT_COMMITTEE_PERIOD
|
spec.PERSISTENT_COMMITTEE_PERIOD
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
|
||||||
process_voluntary_exit(pre_state, voluntary_exit)
|
return pre_state, voluntary_exit, post_state
|
||||||
|
|
||||||
return pre_state, voluntary_exit, None
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ from build.phase0.spec import (
|
||||||
get_attestation_participants,
|
get_attestation_participants,
|
||||||
get_block_root,
|
get_block_root,
|
||||||
get_crosslink_committee_for_attestation,
|
get_crosslink_committee_for_attestation,
|
||||||
get_crosslink_committees_at_slot,
|
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
get_domain,
|
get_domain,
|
||||||
get_empty_block,
|
get_empty_block,
|
||||||
|
@ -96,14 +95,6 @@ def create_genesis_state(num_validators, deposit_data_leaves=None):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def force_registry_change_at_next_epoch(state):
|
|
||||||
# artificially trigger registry update at next epoch transition
|
|
||||||
state.finalized_epoch = get_current_epoch(state) - 1
|
|
||||||
for crosslink in state.latest_crosslinks:
|
|
||||||
crosslink.epoch = state.finalized_epoch
|
|
||||||
state.validator_registry_update_epoch = state.finalized_epoch - 1
|
|
||||||
|
|
||||||
|
|
||||||
def build_empty_block_for_next_slot(state):
|
def build_empty_block_for_next_slot(state):
|
||||||
empty_block = get_empty_block()
|
empty_block = get_empty_block()
|
||||||
empty_block.slot = state.slot + 1
|
empty_block.slot = state.slot + 1
|
||||||
|
@ -208,7 +199,7 @@ def build_deposit(state,
|
||||||
|
|
||||||
def get_valid_proposer_slashing(state):
|
def get_valid_proposer_slashing(state):
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[-1]
|
validator_index = get_active_validator_indices(state, current_epoch)[-1]
|
||||||
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||||
slot = state.slot
|
slot = state.slot
|
||||||
|
|
||||||
|
@ -249,7 +240,7 @@ def get_valid_proposer_slashing(state):
|
||||||
def get_valid_attester_slashing(state):
|
def get_valid_attester_slashing(state):
|
||||||
attestation_1 = get_valid_attestation(state)
|
attestation_1 = get_valid_attestation(state)
|
||||||
attestation_2 = deepcopy(attestation_1)
|
attestation_2 = deepcopy(attestation_1)
|
||||||
attestation_2.data.target_root = b'\x01'*32
|
attestation_2.data.target_root = b'\x01' * 32
|
||||||
|
|
||||||
return AttesterSlashing(
|
return AttesterSlashing(
|
||||||
attestation_1=convert_to_indexed(state, attestation_1),
|
attestation_1=convert_to_indexed(state, attestation_1),
|
||||||
|
|
|
@ -39,7 +39,6 @@ from build.phase0.utils.merkle_minimal import (
|
||||||
from tests.phase0.helpers import (
|
from tests.phase0.helpers import (
|
||||||
build_deposit_data,
|
build_deposit_data,
|
||||||
build_empty_block_for_next_slot,
|
build_empty_block_for_next_slot,
|
||||||
force_registry_change_at_next_epoch,
|
|
||||||
get_valid_attestation,
|
get_valid_attestation,
|
||||||
get_valid_attester_slashing,
|
get_valid_attester_slashing,
|
||||||
get_valid_proposer_slashing,
|
get_valid_proposer_slashing,
|
||||||
|
@ -128,11 +127,9 @@ def test_proposer_slashing(state):
|
||||||
block.body.proposer_slashings.append(proposer_slashing)
|
block.body.proposer_slashings.append(proposer_slashing)
|
||||||
state_transition(test_state, block)
|
state_transition(test_state, block)
|
||||||
|
|
||||||
assert not state.validator_registry[validator_index].initiated_exit
|
|
||||||
assert not state.validator_registry[validator_index].slashed
|
assert not state.validator_registry[validator_index].slashed
|
||||||
|
|
||||||
slashed_validator = test_state.validator_registry[validator_index]
|
slashed_validator = test_state.validator_registry[validator_index]
|
||||||
assert not slashed_validator.initiated_exit
|
|
||||||
assert slashed_validator.slashed
|
assert slashed_validator.slashed
|
||||||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
@ -154,11 +151,9 @@ def test_attester_slashing(state):
|
||||||
block.body.attester_slashings.append(attester_slashing)
|
block.body.attester_slashings.append(attester_slashing)
|
||||||
state_transition(test_state, block)
|
state_transition(test_state, block)
|
||||||
|
|
||||||
assert not state.validator_registry[validator_index].initiated_exit
|
|
||||||
assert not state.validator_registry[validator_index].slashed
|
assert not state.validator_registry[validator_index].slashed
|
||||||
|
|
||||||
slashed_validator = test_state.validator_registry[validator_index]
|
slashed_validator = test_state.validator_registry[validator_index]
|
||||||
assert not slashed_validator.initiated_exit
|
|
||||||
assert slashed_validator.slashed
|
assert slashed_validator.slashed
|
||||||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
@ -283,14 +278,12 @@ def test_attestation(state):
|
||||||
def test_voluntary_exit(state):
|
def test_voluntary_exit(state):
|
||||||
pre_state = deepcopy(state)
|
pre_state = deepcopy(state)
|
||||||
validator_index = get_active_validator_indices(
|
validator_index = get_active_validator_indices(
|
||||||
pre_state.validator_registry,
|
pre_state,
|
||||||
get_current_epoch(pre_state)
|
get_current_epoch(pre_state)
|
||||||
)[-1]
|
)[-1]
|
||||||
|
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
# artificially trigger registry update at next epoch transition
|
|
||||||
force_registry_change_at_next_epoch(pre_state)
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
post_state = deepcopy(pre_state)
|
||||||
|
|
||||||
|
@ -316,9 +309,7 @@ def test_voluntary_exit(state):
|
||||||
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
|
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
|
||||||
state_transition(post_state, initiate_exit_block)
|
state_transition(post_state, initiate_exit_block)
|
||||||
|
|
||||||
assert not pre_state.validator_registry[validator_index].initiated_exit
|
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert post_state.validator_registry[validator_index].initiated_exit
|
|
||||||
assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Process within epoch transition
|
# Process within epoch transition
|
||||||
|
@ -335,7 +326,7 @@ def test_voluntary_exit(state):
|
||||||
def test_no_exit_churn_too_long_since_change(state):
|
def test_no_exit_churn_too_long_since_change(state):
|
||||||
pre_state = deepcopy(state)
|
pre_state = deepcopy(state)
|
||||||
validator_index = get_active_validator_indices(
|
validator_index = get_active_validator_indices(
|
||||||
pre_state.validator_registry,
|
pre_state,
|
||||||
get_current_epoch(pre_state)
|
get_current_epoch(pre_state)
|
||||||
)[-1]
|
)[-1]
|
||||||
|
|
||||||
|
@ -344,14 +335,6 @@ def test_no_exit_churn_too_long_since_change(state):
|
||||||
#
|
#
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
# artificially trigger registry update at next epoch transition
|
|
||||||
force_registry_change_at_next_epoch(pre_state)
|
|
||||||
# make epochs since registry update greater than LATEST_SLASHED_EXIT_LENGTH
|
|
||||||
pre_state.validator_registry_update_epoch = (
|
|
||||||
get_current_epoch(pre_state) - spec.LATEST_SLASHED_EXIT_LENGTH
|
|
||||||
)
|
|
||||||
# set validator to have previously initiated exit
|
|
||||||
pre_state.validator_registry[validator_index].initiated_exit = True
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
post_state = deepcopy(pre_state)
|
||||||
|
|
||||||
|
@ -362,7 +345,6 @@ def test_no_exit_churn_too_long_since_change(state):
|
||||||
block.slot += spec.SLOTS_PER_EPOCH
|
block.slot += spec.SLOTS_PER_EPOCH
|
||||||
state_transition(post_state, block)
|
state_transition(post_state, block)
|
||||||
|
|
||||||
assert post_state.validator_registry_update_epoch == get_current_epoch(post_state) - 1
|
|
||||||
assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
return pre_state, [block], post_state
|
return pre_state, [block], post_state
|
||||||
|
@ -371,8 +353,8 @@ def test_no_exit_churn_too_long_since_change(state):
|
||||||
def test_transfer(state):
|
def test_transfer(state):
|
||||||
pre_state = deepcopy(state)
|
pre_state = deepcopy(state)
|
||||||
current_epoch = get_current_epoch(pre_state)
|
current_epoch = get_current_epoch(pre_state)
|
||||||
sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1]
|
sender_index = get_active_validator_indices(pre_state, current_epoch)[-1]
|
||||||
recipient_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
recipient_index = get_active_validator_indices(pre_state, current_epoch)[0]
|
||||||
transfer_pubkey = pubkeys[-1]
|
transfer_pubkey = pubkeys[-1]
|
||||||
transfer_privkey = privkeys[-1]
|
transfer_privkey = privkeys[-1]
|
||||||
amount = get_balance(pre_state, sender_index)
|
amount = get_balance(pre_state, sender_index)
|
||||||
|
@ -419,11 +401,11 @@ def test_transfer(state):
|
||||||
return pre_state, [block], post_state
|
return pre_state, [block], post_state
|
||||||
|
|
||||||
|
|
||||||
def test_ejection(state):
|
def test_balance_driven_status_transitions(state):
|
||||||
pre_state = deepcopy(state)
|
pre_state = deepcopy(state)
|
||||||
|
|
||||||
current_epoch = get_current_epoch(pre_state)
|
current_epoch = get_current_epoch(pre_state)
|
||||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1]
|
validator_index = get_active_validator_indices(pre_state, current_epoch)[-1]
|
||||||
|
|
||||||
assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
@ -438,7 +420,7 @@ def test_ejection(state):
|
||||||
block.slot += spec.SLOTS_PER_EPOCH
|
block.slot += spec.SLOTS_PER_EPOCH
|
||||||
state_transition(post_state, block)
|
state_transition(post_state, block)
|
||||||
|
|
||||||
assert post_state.validator_registry[validator_index].initiated_exit == True
|
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
return pre_state, [block], post_state
|
return pre_state, [block], post_state
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,9 @@ def process_epoch_transition(state: BeaconState) -> None:
|
||||||
spec.process_crosslinks(state)
|
spec.process_crosslinks(state)
|
||||||
spec.maybe_reset_eth1_period(state)
|
spec.maybe_reset_eth1_period(state)
|
||||||
spec.apply_rewards(state)
|
spec.apply_rewards(state)
|
||||||
spec.process_ejections(state)
|
spec.process_balance_driven_status_transitions(state)
|
||||||
spec.update_registry(state)
|
spec.update_registry(state)
|
||||||
spec.process_slashings(state)
|
spec.process_slashings(state)
|
||||||
spec.process_exit_queue(state)
|
|
||||||
spec.finish_epoch_update(state)
|
spec.finish_epoch_update(state)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue