Merge branch 'dev' into start-at-zero
This commit is contained in:
commit
0656817346
|
@ -67,8 +67,8 @@
|
||||||
- [`get_permuted_index`](#get_permuted_index)
|
- [`get_permuted_index`](#get_permuted_index)
|
||||||
- [`get_split_offset`](#get_split_offset)
|
- [`get_split_offset`](#get_split_offset)
|
||||||
- [`get_epoch_committee_count`](#get_epoch_committee_count)
|
- [`get_epoch_committee_count`](#get_epoch_committee_count)
|
||||||
|
- [`get_shard_delta`](#get_shard_delta)
|
||||||
- [`compute_committee`](#compute_committee)
|
- [`compute_committee`](#compute_committee)
|
||||||
- [`get_current_epoch_committee_count`](#get_current_epoch_committee_count)
|
|
||||||
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
||||||
- [`get_block_root`](#get_block_root)
|
- [`get_block_root`](#get_block_root)
|
||||||
- [`get_state_root`](#get_state_root)
|
- [`get_state_root`](#get_state_root)
|
||||||
|
@ -93,16 +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)
|
||||||
- [`process_deposit`](#process_deposit)
|
|
||||||
- [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)
|
||||||
|
@ -123,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)
|
||||||
|
@ -140,10 +138,6 @@
|
||||||
- [Voluntary exits](#voluntary-exits)
|
- [Voluntary exits](#voluntary-exits)
|
||||||
- [Transfers](#transfers)
|
- [Transfers](#transfers)
|
||||||
- [State root verification](#state-root-verification)
|
- [State root verification](#state-root-verification)
|
||||||
- [References](#references)
|
|
||||||
- [Normative](#normative)
|
|
||||||
- [Informative](#informative)
|
|
||||||
- [Copyright](#copyright)
|
|
||||||
|
|
||||||
<!-- /TOC -->
|
<!-- /TOC -->
|
||||||
|
|
||||||
|
@ -172,7 +166,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
||||||
* **Crosslink** - a set of signatures from a committee attesting to a block in a shard chain that can be included into the beacon chain. Crosslinks are the main means by which the beacon chain "learns about" the updated state of shard chains.
|
* **Crosslink** - a set of signatures from a committee attesting to a block in a shard chain that can be included into the beacon chain. Crosslinks are the main means by which the beacon chain "learns about" the updated state of shard chains.
|
||||||
* **Slot** - a period during which one proposer has the ability to create a beacon chain block and some attesters have the ability to make attestations.
|
* **Slot** - a period during which one proposer has the ability to create a beacon chain block and some attesters have the ability to make attestations.
|
||||||
* **Epoch** - an aligned span of slots during which all [validators](#dfn-validator) get exactly one chance to make an attestation.
|
* **Epoch** - an aligned span of slots during which all [validators](#dfn-validator) get exactly one chance to make an attestation.
|
||||||
* **Finalized**, **justified** - see Casper FFG finalization [[casper-ffg]](#ref-casper-ffg).
|
* **Finalized**, **justified** - see the [Casper FFG paper](https://arxiv.org/abs/1710.09437).
|
||||||
* **Withdrawal period** - the number of slots between a [validator](#dfn-validator) exit and the [validator](#dfn-validator) balance being withdrawable.
|
* **Withdrawal period** - the number of slots between a [validator](#dfn-validator) exit and the [validator](#dfn-validator) balance being withdrawable.
|
||||||
* **Genesis time** - the Unix time of the genesis beacon chain block at slot 0.
|
* **Genesis time** - the Unix time of the genesis beacon chain block at slot 0.
|
||||||
|
|
||||||
|
@ -188,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.)
|
||||||
|
@ -237,11 +231,10 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
|
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
|
||||||
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
|
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
|
||||||
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
|
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
|
||||||
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) |
|
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours |
|
||||||
|
|
||||||
* `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 |
|
||||||
|
@ -287,7 +280,7 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
|
|
||||||
## Data structures
|
## Data structures
|
||||||
|
|
||||||
The following data structures are defined as [SimpleSerialize (SSZ)](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) objects.
|
The following data structures are defined as [SimpleSerialize (SSZ)](../simple-serialize.md) objects.
|
||||||
|
|
||||||
The types are defined topologically to aid in facilitating an executable version of the spec.
|
The types are defined topologically to aid in facilitating an executable version of the spec.
|
||||||
|
|
||||||
|
@ -420,14 +413,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
|
||||||
|
@ -593,12 +586,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],
|
||||||
|
@ -660,11 +652,11 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere
|
||||||
|
|
||||||
### `hash_tree_root`
|
### `hash_tree_root`
|
||||||
|
|
||||||
`def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#tree-hash).
|
`def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](../simple-serialize.md#merkleization).
|
||||||
|
|
||||||
### `signing_root`
|
### `signing_root`
|
||||||
|
|
||||||
`def signing_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) to compute signing messages.
|
`def signing_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers) to compute signing messages.
|
||||||
|
|
||||||
### `get_temporary_block_header`
|
### `get_temporary_block_header`
|
||||||
|
|
||||||
|
@ -748,11 +740,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`
|
||||||
|
@ -832,29 +824,37 @@ 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`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_epoch_committee_count(active_validator_count: int) -> int:
|
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, epoch)
|
||||||
return max(
|
return max(
|
||||||
1,
|
1,
|
||||||
min(
|
min(
|
||||||
SHARD_COUNT // SLOTS_PER_EPOCH,
|
SHARD_COUNT // SLOTS_PER_EPOCH,
|
||||||
active_validator_count // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
len(active_validators) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||||
)
|
)
|
||||||
) * SLOTS_PER_EPOCH
|
) * SLOTS_PER_EPOCH
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `get_shard_delta`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_shard_delta(state: BeaconState, epoch: Epoch) -> int:
|
||||||
|
return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)
|
||||||
|
```
|
||||||
|
|
||||||
### `compute_committee`
|
### `compute_committee`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -876,20 +876,6 @@ def compute_committee(validator_indices: List[ValidatorIndex],
|
||||||
|
|
||||||
**Note**: this definition and the next few definitions are highly inefficient as algorithms, as they re-calculate many sub-expressions. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.
|
**Note**: this definition and the next few definitions are highly inefficient as algorithms, as they re-calculate many sub-expressions. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.
|
||||||
|
|
||||||
### `get_current_epoch_committee_count`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_current_epoch_committee_count(state: BeaconState) -> int:
|
|
||||||
"""
|
|
||||||
Return the number of committees in the current epoch of the given ``state``.
|
|
||||||
"""
|
|
||||||
current_active_validators = get_active_validator_indices(
|
|
||||||
state.validator_registry,
|
|
||||||
get_current_epoch(state),
|
|
||||||
)
|
|
||||||
return get_epoch_committee_count(len(current_active_validators))
|
|
||||||
```
|
|
||||||
|
|
||||||
### `get_crosslink_committees_at_slot`
|
### `get_crosslink_committees_at_slot`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -904,20 +890,18 @@ 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,
|
|
||||||
)
|
|
||||||
committees_per_epoch = get_epoch_committee_count(len(indices))
|
|
||||||
|
|
||||||
if epoch == current_epoch:
|
if epoch == current_epoch:
|
||||||
start_shard = state.latest_start_shard
|
start_shard = state.latest_start_shard
|
||||||
elif epoch == previous_epoch:
|
elif epoch == previous_epoch:
|
||||||
start_shard = (state.latest_start_shard - committees_per_epoch) % SHARD_COUNT
|
previous_shard_delta = get_shard_delta(state, previous_epoch)
|
||||||
|
start_shard = (state.latest_start_shard - previous_shard_delta) % SHARD_COUNT
|
||||||
elif epoch == next_epoch:
|
elif epoch == next_epoch:
|
||||||
current_epoch_committees = get_current_epoch_committee_count(state)
|
current_shard_delta = get_shard_delta(state, current_epoch)
|
||||||
start_shard = (state.latest_start_shard + current_epoch_committees) % SHARD_COUNT
|
start_shard = (state.latest_start_shard + current_shard_delta) % SHARD_COUNT
|
||||||
|
|
||||||
|
committees_per_epoch = get_epoch_committee_count(state, epoch)
|
||||||
committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH
|
committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH
|
||||||
offset = slot % SLOTS_PER_EPOCH
|
offset = slot % SLOTS_PER_EPOCH
|
||||||
slot_start_shard = (start_shard + committees_per_slot * offset) % SHARD_COUNT
|
slot_start_shard = (start_shard + committees_per_slot * offset) % SHARD_COUNT
|
||||||
|
@ -1198,7 +1182,7 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA
|
||||||
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
|
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
|
||||||
return False
|
return False
|
||||||
|
|
||||||
total_attesting_indices = len(custody_bit_0_indices + custody_bit_1_indices)
|
total_attesting_indices = len(custody_bit_0_indices) + len(custody_bit_1_indices)
|
||||||
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
|
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1277,87 +1261,27 @@ 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](https://github.com/ethereum/eth2.0-specs/blob/master/specs/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).
|
||||||
|
|
||||||
### `bls_verify_multiple`
|
### `bls_verify_multiple`
|
||||||
|
|
||||||
`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, defined in the [BLS Signature spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_verify_multiple).
|
`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, defined in the [BLS Signature spec](../bls_signature.md#bls_verify_multiple).
|
||||||
|
|
||||||
### `bls_aggregate_pubkeys`
|
### `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](https://github.com/ethereum/eth2.0-specs/blob/master/specs/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).
|
||||||
|
|
||||||
### `process_deposit`
|
|
||||||
|
|
||||||
Used to add a [validator](#dfn-validator) or top up an existing [validator](#dfn-validator)'s balance by some `deposit` amount:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|
||||||
"""
|
|
||||||
Process a deposit from Ethereum 1.0.
|
|
||||||
Note that this function mutates ``state``.
|
|
||||||
"""
|
|
||||||
# Deposits must be processed in order
|
|
||||||
assert deposit.index == state.deposit_index
|
|
||||||
|
|
||||||
# Verify the Merkle branch
|
|
||||||
merkle_branch_is_valid = verify_merkle_branch(
|
|
||||||
leaf=hash(serialize(deposit.data)), # 48 + 32 + 8 + 96 = 184 bytes serialization
|
|
||||||
proof=deposit.proof,
|
|
||||||
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
|
||||||
index=deposit.index,
|
|
||||||
root=state.latest_eth1_data.deposit_root,
|
|
||||||
)
|
|
||||||
assert merkle_branch_is_valid
|
|
||||||
|
|
||||||
# Increment the next deposit index we are expecting. Note that this
|
|
||||||
# needs to be done here because while the deposit contract will never
|
|
||||||
# create an invalid Merkle branch, it may admit an invalid deposit
|
|
||||||
# object, and we need to be able to skip over it
|
|
||||||
state.deposit_index += 1
|
|
||||||
|
|
||||||
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
|
||||||
pubkey = deposit.data.pubkey
|
|
||||||
amount = deposit.data.amount
|
|
||||||
|
|
||||||
if pubkey not in validator_pubkeys:
|
|
||||||
# Verify the proof of possession
|
|
||||||
proof_is_valid = bls_verify(
|
|
||||||
pubkey=pubkey,
|
|
||||||
message_hash=signing_root(deposit.data),
|
|
||||||
signature=deposit.data.proof_of_possession,
|
|
||||||
domain=get_domain(
|
|
||||||
state.fork,
|
|
||||||
get_current_epoch(state),
|
|
||||||
DOMAIN_DEPOSIT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if not proof_is_valid:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Add new validator
|
|
||||||
validator = Validator(
|
|
||||||
pubkey=pubkey,
|
|
||||||
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
|
||||||
activation_epoch=FAR_FUTURE_EPOCH,
|
|
||||||
exit_epoch=FAR_FUTURE_EPOCH,
|
|
||||||
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
|
||||||
initiated_exit=False,
|
|
||||||
slashed=False,
|
|
||||||
high_balance=0
|
|
||||||
)
|
|
||||||
|
|
||||||
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
|
||||||
state.validator_registry.append(validator)
|
|
||||||
state.balances.append(0)
|
|
||||||
set_balance(state, len(state.validator_registry) - 1, amount)
|
|
||||||
else:
|
|
||||||
# Increase balance by deposit amount
|
|
||||||
index = validator_pubkeys.index(pubkey)
|
|
||||||
increase_balance(state, index, amount)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Routines for updating validator status
|
### Routines for updating validator status
|
||||||
|
|
||||||
|
@ -1373,8 +1297,9 @@ def activate_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
||||||
"""
|
"""
|
||||||
validator = state.validator_registry[index]
|
validator = state.validator_registry[index]
|
||||||
if state.slot == GENESIS_SLOT:
|
if state.slot == GENESIS_SLOT:
|
||||||
|
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||||
validator.activation_epoch = GENESIS_EPOCH
|
validator.activation_epoch = GENESIS_EPOCH
|
||||||
else:
|
else:
|
||||||
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
|
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1386,23 +1311,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`
|
||||||
|
@ -1413,7 +1336,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)
|
||||||
|
@ -1429,19 +1352,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.
|
||||||
|
@ -1546,7 +1456,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)]),
|
||||||
|
@ -1583,11 +1492,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)
|
activate_validator(state, validator_index)
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -1683,9 +1592,9 @@ We now define the state transition function. At a high level, the state transiti
|
||||||
4. The per-block transitions, which happens at every block.
|
4. The per-block transitions, which happens at every block.
|
||||||
|
|
||||||
Transition section notes:
|
Transition section notes:
|
||||||
* The state caching caches the state root of the previous slot.
|
* The state caching caches the state root of the previous slot and updates block and state roots records.
|
||||||
* The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization.
|
* The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization.
|
||||||
* The per-slot transitions focus on the slot counter and block roots records updates.
|
* The per-slot transitions focus on the slot counter.
|
||||||
* The per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`.
|
* The per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`.
|
||||||
|
|
||||||
Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid.
|
Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid.
|
||||||
|
@ -1721,25 +1630,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
|
||||||
|
@ -1787,7 +1696,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
|
||||||
|
@ -1821,8 +1730,8 @@ def update_justification_and_finalization(state: BeaconState) -> None:
|
||||||
new_justified_epoch = state.current_justified_epoch
|
new_justified_epoch = state.current_justified_epoch
|
||||||
new_finalized_epoch = state.finalized_epoch
|
new_finalized_epoch = state.finalized_epoch
|
||||||
|
|
||||||
# Rotate the justification bitfield up one epoch to make room for the current epoch
|
# Rotate the justification bitfield up one epoch to make room for the current epoch (and limit to 64 bits)
|
||||||
state.justification_bitfield <<= 1
|
state.justification_bitfield = (state.justification_bitfield << 1) % 2**64
|
||||||
# If the previous epoch gets justified, fill the second last bit
|
# If the previous epoch gets justified, fill the second last bit
|
||||||
previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state))
|
previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state))
|
||||||
if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2:
|
if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2:
|
||||||
|
@ -1946,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] += (
|
||||||
|
@ -1956,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
|
||||||
|
@ -2018,91 +1927,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)
|
|
||||||
|
|
||||||
# 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_current_epoch_committee_count(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:
|
||||||
|
@ -2111,7 +1978,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`
|
||||||
|
@ -2128,30 +1995,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:
|
||||||
|
@ -2163,7 +2006,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] = (
|
||||||
|
@ -2373,7 +2216,77 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
|
|
||||||
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`.
|
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`.
|
||||||
|
|
||||||
For each `deposit` in `block.body.deposits`, run `process_deposit(state, deposit)`.
|
For each `deposit` in `block.body.deposits`, run the following function:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||||
|
"""
|
||||||
|
Process a deposit from Ethereum 1.0.
|
||||||
|
Used to add a validator or top up an existing validator's
|
||||||
|
balance by some ``deposit`` amount.
|
||||||
|
|
||||||
|
Note that this function mutates ``state``.
|
||||||
|
"""
|
||||||
|
# Deposits must be processed in order
|
||||||
|
assert deposit.index == state.deposit_index
|
||||||
|
|
||||||
|
# Verify the Merkle branch
|
||||||
|
merkle_branch_is_valid = verify_merkle_branch(
|
||||||
|
leaf=hash(serialize(deposit.data)), # 48 + 32 + 8 + 96 = 184 bytes serialization
|
||||||
|
proof=deposit.proof,
|
||||||
|
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
||||||
|
index=deposit.index,
|
||||||
|
root=state.latest_eth1_data.deposit_root,
|
||||||
|
)
|
||||||
|
assert merkle_branch_is_valid
|
||||||
|
|
||||||
|
# Increment the next deposit index we are expecting. Note that this
|
||||||
|
# needs to be done here because while the deposit contract will never
|
||||||
|
# create an invalid Merkle branch, it may admit an invalid deposit
|
||||||
|
# object, and we need to be able to skip over it
|
||||||
|
state.deposit_index += 1
|
||||||
|
|
||||||
|
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
||||||
|
pubkey = deposit.data.pubkey
|
||||||
|
amount = deposit.data.amount
|
||||||
|
|
||||||
|
if pubkey not in validator_pubkeys:
|
||||||
|
# Verify the proof of possession
|
||||||
|
proof_is_valid = bls_verify(
|
||||||
|
pubkey=pubkey,
|
||||||
|
message_hash=signing_root(deposit.data),
|
||||||
|
signature=deposit.data.proof_of_possession,
|
||||||
|
domain=get_domain(
|
||||||
|
state.fork,
|
||||||
|
get_current_epoch(state),
|
||||||
|
DOMAIN_DEPOSIT,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not proof_is_valid:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Add new validator
|
||||||
|
validator = Validator(
|
||||||
|
pubkey=pubkey,
|
||||||
|
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||||
|
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
||||||
|
activation_epoch=FAR_FUTURE_EPOCH,
|
||||||
|
exit_epoch=FAR_FUTURE_EPOCH,
|
||||||
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||||
|
initiated_exit=False,
|
||||||
|
slashed=False,
|
||||||
|
high_balance=0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
||||||
|
state.validator_registry.append(validator)
|
||||||
|
state.balances.append(0)
|
||||||
|
set_balance(state, len(state.validator_registry) - 1, amount)
|
||||||
|
else:
|
||||||
|
# Increase balance by deposit amount
|
||||||
|
index = validator_pubkeys.index(pubkey)
|
||||||
|
increase_balance(state, index, amount)
|
||||||
|
```
|
||||||
|
|
||||||
##### Voluntary exits
|
##### Voluntary exits
|
||||||
|
|
||||||
|
@ -2392,8 +2305,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
|
||||||
|
@ -2431,6 +2342,8 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
||||||
get_balance(state, transfer.sender) == transfer.amount + transfer.fee or
|
get_balance(state, transfer.sender) == transfer.amount + transfer.fee or
|
||||||
get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
|
get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
|
||||||
)
|
)
|
||||||
|
# No self-transfers (to enforce >= MIN_DEPOSIT_AMOUNT or zero balance invariant)
|
||||||
|
assert transfer.sender != transfer.recipient
|
||||||
# A transfer is valid in only one slot
|
# A transfer is valid in only one slot
|
||||||
assert state.slot == transfer.slot
|
assert state.slot == transfer.slot
|
||||||
# Only withdrawn or not-yet-deposited accounts can transfer
|
# Only withdrawn or not-yet-deposited accounts can transfer
|
||||||
|
@ -2464,17 +2377,3 @@ Verify the block's `state_root` by running the following function:
|
||||||
def verify_block_state_root(state: BeaconState, block: BeaconBlock) -> None:
|
def verify_block_state_root(state: BeaconState, block: BeaconBlock) -> None:
|
||||||
assert block.state_root == hash_tree_root(state)
|
assert block.state_root == hash_tree_root(state)
|
||||||
```
|
```
|
||||||
|
|
||||||
# References
|
|
||||||
|
|
||||||
This section is divided into Normative and Informative references. Normative references are those that must be read in order to implement this specification, while Informative references are merely helpful information. An example of the former might be the details of a required consensus algorithm, and an example of the latter might be a pointer to research that demonstrates why a particular consensus algorithm might be better suited for inclusion in the standard than another.
|
|
||||||
|
|
||||||
## Normative
|
|
||||||
|
|
||||||
## Informative
|
|
||||||
<a id="ref-casper-ffg"></a> _**casper-ffg**_ </br> _Casper the Friendly Finality Gadget_. V. Buterin and V. Griffith. URL: https://arxiv.org/abs/1710.09437
|
|
||||||
|
|
||||||
<a id="ref-python-poc"></a> _**python-poc**_ </br> _Python proof-of-concept implementation_. Ethereum Foundation. URL: https://github.com/ethereum/beacon_chain
|
|
||||||
|
|
||||||
# Copyright
|
|
||||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
- [`ShardAttestation`](#shardattestation)
|
- [`ShardAttestation`](#shardattestation)
|
||||||
- [Helper functions](#helper-functions)
|
- [Helper functions](#helper-functions)
|
||||||
- [`get_period_committee`](#get_period_committee)
|
- [`get_period_committee`](#get_period_committee)
|
||||||
|
- [`get_switchover_epoch`](#get_switchover_epoch)
|
||||||
- [`get_persistent_committee`](#get_persistent_committee)
|
- [`get_persistent_committee`](#get_persistent_committee)
|
||||||
- [`get_shard_proposer_index`](#get_shard_proposer_index)
|
- [`get_shard_proposer_index`](#get_shard_proposer_index)
|
||||||
- [`get_shard_header`](#get_shard_header)
|
- [`get_shard_header`](#get_shard_header)
|
||||||
|
@ -137,6 +138,14 @@ def get_period_committee(state: BeaconState,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `get_switchover_epoch`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_switchover_epoch(state: BeaconState, epoch: Epoch, index: ValidatorIndex):
|
||||||
|
earlier_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2
|
||||||
|
return bytes_to_int(hash(generate_seed(state, earlier_start_epoch) + bytes3(index))[0:8]) % PERSISTENT_COMMITTEE_PERIOD
|
||||||
|
```
|
||||||
|
|
||||||
### `get_persistent_committee`
|
### `get_persistent_committee`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -146,6 +155,7 @@ def get_persistent_committee(state: BeaconState,
|
||||||
"""
|
"""
|
||||||
Return the persistent committee for the given ``shard`` at the given ``slot``.
|
Return the persistent committee for the given ``shard`` at the given ``slot``.
|
||||||
"""
|
"""
|
||||||
|
epoch = slot_to_epoch(slot)
|
||||||
earlier_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2
|
earlier_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2
|
||||||
later_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD
|
later_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD
|
||||||
|
|
||||||
|
@ -160,14 +170,11 @@ def get_persistent_committee(state: BeaconState,
|
||||||
earlier_committee = get_period_committee(state, shard, earlier_start_epoch, index, committee_count)
|
earlier_committee = get_period_committee(state, shard, earlier_start_epoch, index, committee_count)
|
||||||
later_committee = get_period_committee(state, shard, later_start_epoch, index, committee_count)
|
later_committee = get_period_committee(state, shard, later_start_epoch, index, committee_count)
|
||||||
|
|
||||||
def get_switchover_epoch(index):
|
|
||||||
return bytes_to_int(hash(earlier_seed + bytes3(index))[0:8]) % PERSISTENT_COMMITTEE_PERIOD
|
|
||||||
|
|
||||||
# Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from
|
# Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from
|
||||||
# later committee; return a sorted list of the union of the two, deduplicated
|
# later committee; return a sorted list of the union of the two, deduplicated
|
||||||
return sorted(list(set(
|
return sorted(list(set(
|
||||||
[i for i in earlier_committee if epoch % PERSISTENT_COMMITTEE_PERIOD < get_switchover_epoch(i)] +
|
[i for i in earlier_committee if epoch % PERSISTENT_COMMITTEE_PERIOD < get_switchover_epoch(state, epoch, i)] +
|
||||||
[i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(i)]
|
[i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(state, epoch, i)]
|
||||||
)))
|
)))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -310,8 +317,8 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock],
|
||||||
assert len(block.attestations) <= MAX_SHARD_ATTESTIONS
|
assert len(block.attestations) <= MAX_SHARD_ATTESTIONS
|
||||||
for _, attestation in enumerate(block.attestations):
|
for _, attestation in enumerate(block.attestations):
|
||||||
assert max(GENESIS_SHARD_SLOT, block.slot - SLOTS_PER_EPOCH) <= attestation.data.slot
|
assert max(GENESIS_SHARD_SLOT, block.slot - SLOTS_PER_EPOCH) <= attestation.data.slot
|
||||||
assert attesation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
assert attestation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
assert attetation.data.shart == block.shard
|
assert attestation.data.shard == block.shard
|
||||||
verify_shard_attestation_signature(beacon_state, attestation)
|
verify_shard_attestation_signature(beacon_state, attestation)
|
||||||
|
|
||||||
# Check signature
|
# Check signature
|
||||||
|
|
|
@ -9,7 +9,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD",
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification), and [Beacon Chain](../core/0_beacon-chain.md) specifications.
|
This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification.md), and [Beacon Chain](../core/0_beacon-chain.md) specifications.
|
||||||
|
|
||||||
# Specification
|
# Specification
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ Message body schemas are notated like this:
|
||||||
|
|
||||||
Embedded types are serialized as SSZ Containers unless otherwise noted.
|
Embedded types are serialized as SSZ Containers unless otherwise noted.
|
||||||
|
|
||||||
All referenced data structures can be found in the [0-beacon-chain](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#data-structures) specification.
|
All referenced data structures can be found in the [0-beacon-chain](../core/0_beacon-chain.md#data-structures) specification.
|
||||||
|
|
||||||
## `libp2p` Protocol Names
|
## `libp2p` Protocol Names
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ This document defines the YAML format and structure used for ETH 2.0 testing.
|
||||||
* [About](#about)
|
* [About](#about)
|
||||||
* [Glossary](#glossary)
|
* [Glossary](#glossary)
|
||||||
* [Test format philosophy](#test-format-philosophy)
|
* [Test format philosophy](#test-format-philosophy)
|
||||||
* [Test Suite](#yaml-suite)
|
* [Test Suite](#test-suite)
|
||||||
* [Config](#config)
|
* [Config](#config)
|
||||||
* [Fork-timeline](#fork-timeline)
|
* [Fork-timeline](#fork-timeline)
|
||||||
* [Config sourcing](#config-sourcing)
|
* [Config sourcing](#config-sourcing)
|
||||||
|
@ -28,7 +28,7 @@ The particular formats of specific types of tests (test suites) are defined in s
|
||||||
- `suite`: a YAML file with:
|
- `suite`: a YAML file with:
|
||||||
- a header: describes the `suite`, and defines what the `suite` is for
|
- a header: describes the `suite`, and defines what the `suite` is for
|
||||||
- a list of test cases
|
- a list of test cases
|
||||||
- `runner`: where a generator is a *"producer"*, this is the *"consumer"**.
|
- `runner`: where a generator is a *"producer"*, this is the *"consumer"*.
|
||||||
- A `runner` focuses on *only one* `type`, and each type has *only one* `runner`.
|
- A `runner` focuses on *only one* `type`, and each type has *only one* `runner`.
|
||||||
- `handler`: a `runner` may be too limited sometimes, you may have a `suite` with a specific focus that requires a different format.
|
- `handler`: a `runner` may be too limited sometimes, you may have a `suite` with a specific focus that requires a different format.
|
||||||
To facilitate this, you specify a `handler`: the runner can deal with the format by using the specified handler.
|
To facilitate this, you specify a `handler`: the runner can deal with the format by using the specified handler.
|
||||||
|
@ -84,7 +84,7 @@ The aim is to provide clients with a well-defined scope of work to run a particu
|
||||||
|
|
||||||
- Clients that are complete are expected to contribute to testing, seeking for better resources to get conformance with the spec, and other clients.
|
- Clients that are complete are expected to contribute to testing, seeking for better resources to get conformance with the spec, and other clients.
|
||||||
- Clients that are not complete in functionality can choose to ignore suites that use certain test-runners, or specific handlers of these test-runners.
|
- Clients that are not complete in functionality can choose to ignore suites that use certain test-runners, or specific handlers of these test-runners.
|
||||||
- Clients that are on older versions can test there work based on older releases of the generated tests, and catch up with newer releases when possible.
|
- Clients that are on older versions can test their work based on older releases of the generated tests, and catch up with newer releases when possible.
|
||||||
|
|
||||||
## Test Suite
|
## Test Suite
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Ethereum 2.0 Phase 0 -- Honest Validator
|
# Ethereum 2.0 Phase 0 -- Honest Validator
|
||||||
|
|
||||||
__NOTICE__: This document is a work-in-progress for researchers and implementers. This is an accompanying document to [Ethereum 2.0 Phase 0 -- The Beacon Chain](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md) that describes the expected actions of a "validator" participating in the Ethereum 2.0 protocol.
|
__NOTICE__: This document is a work-in-progress for researchers and implementers. This is an accompanying document to [Ethereum 2.0 Phase 0 -- The Beacon Chain](../core/0_beacon-chain.md) that describes the expected actions of a "validator" participating in the Ethereum 2.0 protocol.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ A validator is an entity that participates in the consensus of the Ethereum 2.0
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
All terminology, constants, functions, and protocol mechanics defined in the [Phase 0 -- The Beacon Chain](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md) doc are requisite for this document and used throughout. Please see the Phase 0 doc before continuing and use as a reference throughout.
|
All terminology, constants, functions, and protocol mechanics defined in the [Phase 0 -- The Beacon Chain](../core/0_beacon-chain.md) doc are requisite for this document and used throughout. Please see the Phase 0 doc before continuing and use as a reference throughout.
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ A validator must initialize many parameters locally before submitting a deposit
|
||||||
|
|
||||||
#### BLS public key
|
#### BLS public key
|
||||||
|
|
||||||
Validator public keys are [G1 points](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#g1-points) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator.
|
Validator public keys are [G1 points](../bls_signature.md#g1-points) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator.
|
||||||
|
|
||||||
#### BLS withdrawal key
|
#### BLS withdrawal key
|
||||||
|
|
||||||
|
@ -96,15 +96,16 @@ The validator constructs their `withdrawal_credentials` via the following:
|
||||||
|
|
||||||
### Submit deposit
|
### Submit deposit
|
||||||
|
|
||||||
In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`.
|
In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](../core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`.
|
||||||
|
|
||||||
To submit a deposit:
|
To submit a deposit:
|
||||||
|
|
||||||
* Pack the validator's [initialization parameters](#initialization) into `deposit_input`, a [`DepositInput`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositinput) SSZ object.
|
* Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object.
|
||||||
* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`.
|
* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`.
|
||||||
* Set `deposit_input.proof_of_possession = proof_of_possession`.
|
* Set `deposit_data.proof_of_possession = proof_of_possession`.
|
||||||
* Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`.
|
* Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`.
|
||||||
* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit_input)` as the singular `bytes` input along with a deposit `amount` in Gwei.
|
* Set `deposit_data.amount = amount`.
|
||||||
|
* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit(deposit_input: bytes[512])` along with `serialize(deposit_data)` as the singular `bytes` input along with a deposit of `amount` Gwei.
|
||||||
|
|
||||||
_Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`.
|
_Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`.
|
||||||
|
|
||||||
|
@ -114,13 +115,13 @@ Deposits cannot be processed into the beacon chain until the eth1.0 block in whi
|
||||||
|
|
||||||
### Validator index
|
### Validator index
|
||||||
|
|
||||||
Once a validator has been processed and added to the beacon state's `validator_registry`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally.
|
Once a validator has been processed and added to the beacon state's `validator_registry`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](../core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally.
|
||||||
|
|
||||||
### Activation
|
### Activation
|
||||||
|
|
||||||
In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes).
|
In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes).
|
||||||
|
|
||||||
The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows:
|
The function [`is_active_validator`](../core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
shuffling_epoch = state.current_shuffling_epoch
|
shuffling_epoch = state.current_shuffling_epoch
|
||||||
|
@ -138,7 +139,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b
|
||||||
|
|
||||||
### Block proposal
|
### Block proposal
|
||||||
|
|
||||||
A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function).
|
A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
|
||||||
|
|
||||||
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks).
|
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks).
|
||||||
|
|
||||||
|
@ -212,25 +213,25 @@ block_signature = bls_sign(
|
||||||
|
|
||||||
##### Proposer slashings
|
##### Proposer slashings
|
||||||
|
|
||||||
Up to `MAX_PROPOSER_SLASHINGS` [`ProposerSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included.
|
Up to `MAX_PROPOSER_SLASHINGS` [`ProposerSlashing`](../core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](../core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included.
|
||||||
|
|
||||||
##### Attester slashings
|
##### Attester slashings
|
||||||
|
|
||||||
Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attesterslashing) objects can be included in the `block`. The attester slashings must satisfy the verification conditions found in [Attester slashings processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attester-slashings). The validator receives a small "whistleblower" reward for each attester slashing found and included.
|
Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](../core/0_beacon-chain.md#attesterslashing) objects can be included in the `block`. The attester slashings must satisfy the verification conditions found in [Attester slashings processing](../core/0_beacon-chain.md#attester-slashings). The validator receives a small "whistleblower" reward for each attester slashing found and included.
|
||||||
|
|
||||||
##### Attestations
|
##### Attestations
|
||||||
|
|
||||||
Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain.
|
Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](../core/0_beacon-chain.md#attestations). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain.
|
||||||
|
|
||||||
##### Deposits
|
##### Deposits
|
||||||
|
|
||||||
If there are any unprocessed deposits for the existing `state.latest_eth1_data` (i.e. `state.latest_eth1_data.deposit_count > state.deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. These [`deposits`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits).
|
If there are any unprocessed deposits for the existing `state.latest_eth1_data` (i.e. `state.latest_eth1_data.deposit_count > state.deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. These [`deposits`](../core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](../core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](../core/0_beacon-chain.md#deposits).
|
||||||
|
|
||||||
The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation.
|
The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation.
|
||||||
|
|
||||||
##### Voluntary exits
|
##### Voluntary exits
|
||||||
|
|
||||||
Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntary-exits).
|
Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](../core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](../core/0_beacon-chain.md#voluntary-exits).
|
||||||
|
|
||||||
### Attestations
|
### Attestations
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ A validator should create and broadcast the attestation halfway through the `slo
|
||||||
|
|
||||||
#### Attestation data
|
#### Attestation data
|
||||||
|
|
||||||
First the validator should construct `attestation_data`, an [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot.
|
First the validator should construct `attestation_data`, an [`AttestationData`](../core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot.
|
||||||
|
|
||||||
* Let `head_block` be the result of running the fork choice during the assigned slot.
|
* Let `head_block` be the result of running the fork choice during the assigned slot.
|
||||||
* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot.
|
* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot.
|
||||||
|
@ -285,7 +286,7 @@ Set `attestation_data.source_root = head_state.current_justified_root`.
|
||||||
|
|
||||||
#### Construct attestation
|
#### Construct attestation
|
||||||
|
|
||||||
Next the validator creates `attestation`, an [`Attestation`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestation) object.
|
Next the validator creates `attestation`, an [`Attestation`](../core/0_beacon-chain.md#attestation) object.
|
||||||
|
|
||||||
##### Data
|
##### Data
|
||||||
|
|
||||||
|
@ -399,7 +400,7 @@ _Note_: Signed data must be within a sequential `Fork` context to conflict. Mess
|
||||||
|
|
||||||
### Proposer slashing
|
### Proposer slashing
|
||||||
|
|
||||||
To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as two distinct blocks within the same epoch.
|
To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) where conflicting is defined as two distinct blocks within the same epoch.
|
||||||
|
|
||||||
_In phase 0, as long as the validator does not sign two different beacon blocks for the same epoch, the validator is safe against proposer slashings._
|
_In phase 0, as long as the validator does not sign two different beacon blocks for the same epoch, the validator is safe against proposer slashings._
|
||||||
|
|
||||||
|
@ -411,7 +412,7 @@ If the software crashes at some point within this routine, then when the validat
|
||||||
|
|
||||||
### Attester slashing
|
### Attester slashing
|
||||||
|
|
||||||
To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_surround_vote).
|
To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](../core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](../core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](../core/0_beacon-chain.md#is_surround_vote).
|
||||||
|
|
||||||
Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order:
|
Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order:
|
||||||
1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.source_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`.
|
1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.source_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,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
|
|
||||||
|
|
|
@ -28,13 +28,13 @@ 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,
|
||||||
get_epoch_start_slot,
|
get_epoch_start_slot,
|
||||||
get_genesis_beacon_state,
|
get_genesis_beacon_state,
|
||||||
get_previous_epoch,
|
get_previous_epoch,
|
||||||
|
get_shard_delta,
|
||||||
slot_to_epoch,
|
slot_to_epoch,
|
||||||
verify_merkle_branch,
|
verify_merkle_branch,
|
||||||
hash,
|
hash,
|
||||||
|
@ -113,14 +113,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
|
||||||
|
@ -232,7 +224,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
|
||||||
|
|
||||||
|
@ -273,7 +265,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),
|
||||||
|
@ -284,7 +276,13 @@ def get_valid_attester_slashing(state):
|
||||||
def get_valid_attestation(state, slot=None):
|
def get_valid_attestation(state, slot=None):
|
||||||
if slot is None:
|
if slot is None:
|
||||||
slot = state.slot
|
slot = state.slot
|
||||||
shard = state.latest_start_shard + slot % spec.SLOTS_PER_EPOCH
|
|
||||||
|
if slot_to_epoch(slot) == get_current_epoch(state):
|
||||||
|
shard = (state.latest_start_shard + slot) % spec.SLOTS_PER_EPOCH
|
||||||
|
else:
|
||||||
|
previous_shard_delta = get_shard_delta(state, get_previous_epoch(state))
|
||||||
|
shard = (state.latest_start_shard - previous_shard_delta + slot) % spec.SHARD_COUNT
|
||||||
|
|
||||||
attestation_data = build_attestation_data(state, slot, shard)
|
attestation_data = build_attestation_data(state, slot, shard)
|
||||||
|
|
||||||
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
|
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
|
||||||
|
|
|
@ -40,7 +40,6 @@ from tests.phase0.helpers import (
|
||||||
build_deposit_data,
|
build_deposit_data,
|
||||||
build_empty_block_for_next_slot,
|
build_empty_block_for_next_slot,
|
||||||
fill_aggregate_attestation,
|
fill_aggregate_attestation,
|
||||||
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,
|
||||||
|
@ -153,6 +152,7 @@ def test_full_attestations_finalizing(state):
|
||||||
|
|
||||||
for epoch in range(5):
|
for epoch in range(5):
|
||||||
for slot in range(spec.SLOTS_PER_EPOCH):
|
for slot in range(spec.SLOTS_PER_EPOCH):
|
||||||
|
print(test_state.slot)
|
||||||
attestation = get_valid_attestation(test_state, test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
attestation = get_valid_attestation(test_state, test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||||
fill_aggregate_attestation(test_state, attestation)
|
fill_aggregate_attestation(test_state, attestation)
|
||||||
block = build_empty_block_for_next_slot(test_state)
|
block = build_empty_block_for_next_slot(test_state)
|
||||||
|
@ -183,11 +183,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
|
||||||
|
@ -209,11 +207,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
|
||||||
|
@ -338,14 +334,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)
|
||||||
|
|
||||||
|
@ -371,9 +365,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
|
||||||
|
@ -390,7 +382,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]
|
||||||
|
|
||||||
|
@ -399,14 +391,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)
|
||||||
|
|
||||||
|
@ -417,7 +401,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
|
||||||
|
@ -426,8 +409,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)
|
||||||
|
@ -474,11 +457,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
|
||||||
|
|
||||||
|
@ -493,7 +476,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