Merge branch 'dev' into pytest-use-config
This commit is contained in:
commit
8e08a6fb60
|
@ -10,7 +10,7 @@ SHARD_COUNT: 1024
|
||||||
# 2**7 (= 128)
|
# 2**7 (= 128)
|
||||||
TARGET_COMMITTEE_SIZE: 128
|
TARGET_COMMITTEE_SIZE: 128
|
||||||
# 2**12 (= 4,096)
|
# 2**12 (= 4,096)
|
||||||
MAX_ATTESTATION_PARTICIPANTS: 4096
|
MAX_INDICES_PER_ATTESTATION: 4096
|
||||||
# 2**2 (= 4)
|
# 2**2 (= 4)
|
||||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||||
# 2**16 (= 65,536)
|
# 2**16 (= 65,536)
|
||||||
|
@ -32,7 +32,7 @@ DEPOSIT_CONTRACT_TREE_DEPTH: 32
|
||||||
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
||||||
MIN_DEPOSIT_AMOUNT: 1000000000
|
MIN_DEPOSIT_AMOUNT: 1000000000
|
||||||
# 2**5 * 10**9 (= 32,000,000,000) Gwei
|
# 2**5 * 10**9 (= 32,000,000,000) Gwei
|
||||||
MAX_DEPOSIT_AMOUNT: 32000000000
|
MAX_EFFECTIVE_BALANCE: 32000000000
|
||||||
# 2**4 * 10**9 (= 16,000,000,000) Gwei
|
# 2**4 * 10**9 (= 16,000,000,000) Gwei
|
||||||
EJECTION_BALANCE: 16000000000
|
EJECTION_BALANCE: 16000000000
|
||||||
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
||||||
|
@ -44,7 +44,6 @@ HIGH_BALANCE_INCREMENT: 1000000000
|
||||||
GENESIS_FORK_VERSION: 0x00000000
|
GENESIS_FORK_VERSION: 0x00000000
|
||||||
# 0, GENESIS_EPOCH is derived from this constant
|
# 0, GENESIS_EPOCH is derived from this constant
|
||||||
GENESIS_SLOT: 0
|
GENESIS_SLOT: 0
|
||||||
GENESIS_START_SHARD: 0
|
|
||||||
# 2**64 - 1
|
# 2**64 - 1
|
||||||
FAR_FUTURE_EPOCH: 18446744073709551615
|
FAR_FUTURE_EPOCH: 18446744073709551615
|
||||||
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
||||||
|
@ -110,8 +109,8 @@ MAX_ATTESTATIONS: 128
|
||||||
MAX_DEPOSITS: 16
|
MAX_DEPOSITS: 16
|
||||||
# 2**4 (= 16)
|
# 2**4 (= 16)
|
||||||
MAX_VOLUNTARY_EXITS: 16
|
MAX_VOLUNTARY_EXITS: 16
|
||||||
# 2**4 (= 16)
|
# Originally 2**4 (= 16), disabled for now.
|
||||||
MAX_TRANSFERS: 16
|
MAX_TRANSFERS: 0
|
||||||
|
|
||||||
|
|
||||||
# Signature domains
|
# Signature domains
|
||||||
|
|
|
@ -10,7 +10,7 @@ SHARD_COUNT: 8
|
||||||
# [customized] unsecure, but fast
|
# [customized] unsecure, but fast
|
||||||
TARGET_COMMITTEE_SIZE: 4
|
TARGET_COMMITTEE_SIZE: 4
|
||||||
# 2**12 (= 4,096)
|
# 2**12 (= 4,096)
|
||||||
MAX_ATTESTATION_PARTICIPANTS: 4096
|
MAX_INDICES_PER_ATTESTATION: 4096
|
||||||
# 2**2 (= 4)
|
# 2**2 (= 4)
|
||||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||||
# 2**16 (= 65,536)
|
# 2**16 (= 65,536)
|
||||||
|
@ -32,7 +32,7 @@ DEPOSIT_CONTRACT_TREE_DEPTH: 32
|
||||||
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
||||||
MIN_DEPOSIT_AMOUNT: 1000000000
|
MIN_DEPOSIT_AMOUNT: 1000000000
|
||||||
# 2**5 * 10**9 (= 32,000,000,000) Gwei
|
# 2**5 * 10**9 (= 32,000,000,000) Gwei
|
||||||
MAX_DEPOSIT_AMOUNT: 32000000000
|
MAX_EFFECTIVE_BALANCE: 32000000000
|
||||||
# 2**4 * 10**9 (= 16,000,000,000) Gwei
|
# 2**4 * 10**9 (= 16,000,000,000) Gwei
|
||||||
EJECTION_BALANCE: 16000000000
|
EJECTION_BALANCE: 16000000000
|
||||||
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
# 2**0 * 10**9 (= 1,000,000,000) Gwei
|
||||||
|
@ -44,7 +44,6 @@ HIGH_BALANCE_INCREMENT: 1000000000
|
||||||
GENESIS_FORK_VERSION: 0x00000000
|
GENESIS_FORK_VERSION: 0x00000000
|
||||||
# 0, GENESIS_EPOCH is derived from this constant
|
# 0, GENESIS_EPOCH is derived from this constant
|
||||||
GENESIS_SLOT: 0
|
GENESIS_SLOT: 0
|
||||||
GENESIS_START_SHARD: 0
|
|
||||||
# 2**64 - 1
|
# 2**64 - 1
|
||||||
FAR_FUTURE_EPOCH: 18446744073709551615
|
FAR_FUTURE_EPOCH: 18446744073709551615
|
||||||
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
||||||
|
@ -110,8 +109,8 @@ MAX_ATTESTATIONS: 128
|
||||||
MAX_DEPOSITS: 16
|
MAX_DEPOSITS: 16
|
||||||
# 2**4 (= 16)
|
# 2**4 (= 16)
|
||||||
MAX_VOLUNTARY_EXITS: 16
|
MAX_VOLUNTARY_EXITS: 16
|
||||||
# 2**4 (= 16)
|
# Originally 2**4 (= 16), disabled for now.
|
||||||
MAX_TRANSFERS: 16
|
MAX_TRANSFERS: 0
|
||||||
|
|
||||||
|
|
||||||
# Signature domains
|
# Signature domains
|
||||||
|
|
|
@ -58,8 +58,6 @@
|
||||||
- [`is_active_validator`](#is_active_validator)
|
- [`is_active_validator`](#is_active_validator)
|
||||||
- [`is_slashable_validator`](#is_slashable_validator)
|
- [`is_slashable_validator`](#is_slashable_validator)
|
||||||
- [`get_active_validator_indices`](#get_active_validator_indices)
|
- [`get_active_validator_indices`](#get_active_validator_indices)
|
||||||
- [`get_balance`](#get_balance)
|
|
||||||
- [`set_balance`](#set_balance)
|
|
||||||
- [`increase_balance`](#increase_balance)
|
- [`increase_balance`](#increase_balance)
|
||||||
- [`decrease_balance`](#decrease_balance)
|
- [`decrease_balance`](#decrease_balance)
|
||||||
- [`get_permuted_index`](#get_permuted_index)
|
- [`get_permuted_index`](#get_permuted_index)
|
||||||
|
@ -68,6 +66,7 @@
|
||||||
- [`get_shard_delta`](#get_shard_delta)
|
- [`get_shard_delta`](#get_shard_delta)
|
||||||
- [`compute_committee`](#compute_committee)
|
- [`compute_committee`](#compute_committee)
|
||||||
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
||||||
|
- [`get_block_root_at_slot`](#get_block_root_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)
|
||||||
- [`get_randao_mix`](#get_randao_mix)
|
- [`get_randao_mix`](#get_randao_mix)
|
||||||
|
@ -78,7 +77,6 @@
|
||||||
- [`get_attesting_indices`](#get_attesting_indices)
|
- [`get_attesting_indices`](#get_attesting_indices)
|
||||||
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
||||||
- [`bytes_to_int`](#bytes_to_int)
|
- [`bytes_to_int`](#bytes_to_int)
|
||||||
- [`get_effective_balance`](#get_effective_balance)
|
|
||||||
- [`get_total_balance`](#get_total_balance)
|
- [`get_total_balance`](#get_total_balance)
|
||||||
- [`get_domain`](#get_domain)
|
- [`get_domain`](#get_domain)
|
||||||
- [`get_bitfield_bit`](#get_bitfield_bit)
|
- [`get_bitfield_bit`](#get_bitfield_bit)
|
||||||
|
@ -175,6 +173,7 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| `MAX_INDICES_PER_ATTESTATION` | `2**12` (= 4,096) |
|
| `MAX_INDICES_PER_ATTESTATION` | `2**12` (= 4,096) |
|
||||||
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
|
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
|
||||||
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
|
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
|
||||||
|
| `BASE_REWARDS_PER_EPOCH` | `5` |
|
||||||
| `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.)
|
||||||
|
@ -193,7 +192,7 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
|
| `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
|
||||||
| `MAX_EFFECTIVE_BALANCE` | `2**5 * 10**9` (= 32,000,000,000) | Gwei |
|
| `MAX_EFFECTIVE_BALANCE` | `2**5 * 10**9` (= 32,000,000,000) | Gwei |
|
||||||
| `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei |
|
| `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei |
|
||||||
| `HIGH_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
|
| `EFFECTIVE_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
|
||||||
|
|
||||||
### Initial values
|
### Initial values
|
||||||
|
|
||||||
|
@ -219,6 +218,7 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| `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) | epochs | ~7 hours |
|
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours |
|
||||||
|
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |
|
||||||
|
|
||||||
* `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`
|
||||||
|
|
||||||
|
@ -237,8 +237,8 @@ These configurations are updated for releases, but may be out of sync during `de
|
||||||
| `BASE_REWARD_QUOTIENT` | `2**5` (= 32) |
|
| `BASE_REWARD_QUOTIENT` | `2**5` (= 32) |
|
||||||
| `WHISTLEBLOWING_REWARD_QUOTIENT` | `2**9` (= 512) |
|
| `WHISTLEBLOWING_REWARD_QUOTIENT` | `2**9` (= 512) |
|
||||||
| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) |
|
| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) |
|
||||||
| `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) |
|
| `INACTIVITY_PENALTY_QUOTIENT` | `2**25` (= 33,554,432) |
|
||||||
| `MIN_PENALTY_QUOTIENT` | `2**5` (= 32) |
|
| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) |
|
||||||
|
|
||||||
* **The `BASE_REWARD_QUOTIENT` is NOT final. Once all other protocol details are finalized it will be adjusted, to target a theoretical maximum total issuance of `2**21` ETH per year if `2**27` ETH is validating (and therefore `2**20` per year if `2**25` ETH is validating, etc etc)**
|
* **The `BASE_REWARD_QUOTIENT` is NOT final. Once all other protocol details are finalized it will be adjusted, to target a theoretical maximum total issuance of `2**21` ETH per year if `2**27` ETH is validating (and therefore `2**20` per year if `2**25` ETH is validating, etc etc)**
|
||||||
* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
|
* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
|
||||||
|
@ -401,8 +401,8 @@ The types are defined topologically to aid in facilitating an executable version
|
||||||
'withdrawable_epoch': 'uint64',
|
'withdrawable_epoch': 'uint64',
|
||||||
# Was the validator slashed
|
# Was the validator slashed
|
||||||
'slashed': 'bool',
|
'slashed': 'bool',
|
||||||
# Rounded balance
|
# Effective balance
|
||||||
'high_balance': 'uint64'
|
'effective_balance': 'uint64',
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -416,6 +416,8 @@ The types are defined topologically to aid in facilitating an executable version
|
||||||
'data': AttestationData,
|
'data': AttestationData,
|
||||||
# Inclusion slot
|
# Inclusion slot
|
||||||
'inclusion_slot': 'uint64',
|
'inclusion_slot': 'uint64',
|
||||||
|
# Proposer index
|
||||||
|
'proposer_index': 'uint64',
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -678,6 +680,7 @@ def get_epoch_start_slot(epoch: Epoch) -> Slot:
|
||||||
```
|
```
|
||||||
|
|
||||||
### `is_active_validator`
|
### `is_active_validator`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -687,15 +690,14 @@ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||||
```
|
```
|
||||||
|
|
||||||
### `is_slashable_validator`
|
### `is_slashable_validator`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if ``validator`` is slashable.
|
Check if ``validator`` is slashable.
|
||||||
"""
|
"""
|
||||||
return (
|
return validator.slashed is False and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
|
||||||
validator.activation_epoch <= epoch < validator.withdrawable_epoch and
|
|
||||||
validator.slashed is False
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_active_validator_indices`
|
### `get_active_validator_indices`
|
||||||
|
@ -708,39 +710,14 @@ def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> List[Valid
|
||||||
return [i for i, v in enumerate(state.validator_registry) if is_active_validator(v, epoch)]
|
return [i for i, v in enumerate(state.validator_registry) if is_active_validator(v, epoch)]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_balance`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_balance(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
|
||||||
"""
|
|
||||||
Return the balance for a validator with the given ``index``.
|
|
||||||
"""
|
|
||||||
return state.balances[index]
|
|
||||||
```
|
|
||||||
|
|
||||||
### `set_balance`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def set_balance(state: BeaconState, index: ValidatorIndex, balance: Gwei) -> None:
|
|
||||||
"""
|
|
||||||
Set the balance for a validator with the given ``index`` in both ``BeaconState``
|
|
||||||
and validator's rounded balance ``high_balance``.
|
|
||||||
"""
|
|
||||||
validator = state.validator_registry[index]
|
|
||||||
HALF_INCREMENT = HIGH_BALANCE_INCREMENT // 2
|
|
||||||
if validator.high_balance > balance or validator.high_balance + 3 * HALF_INCREMENT < balance:
|
|
||||||
validator.high_balance = balance - balance % HIGH_BALANCE_INCREMENT
|
|
||||||
state.balances[index] = balance
|
|
||||||
```
|
|
||||||
|
|
||||||
### `increase_balance`
|
### `increase_balance`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||||
"""
|
"""
|
||||||
Increase the balance for a validator with the given ``index`` by ``delta``.
|
Increase validator balance by ``delta``.
|
||||||
"""
|
"""
|
||||||
set_balance(state, index, get_balance(state, index) + delta)
|
state.balances[index] += delta
|
||||||
```
|
```
|
||||||
|
|
||||||
### `decrease_balance`
|
### `decrease_balance`
|
||||||
|
@ -748,11 +725,9 @@ def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) ->
|
||||||
```python
|
```python
|
||||||
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||||
"""
|
"""
|
||||||
Decrease the balance for a validator with the given ``index`` by ``delta``.
|
Decrease validator balance by ``delta`` with underflow protection.
|
||||||
Set to ``0`` when underflow.
|
|
||||||
"""
|
"""
|
||||||
current_balance = get_balance(state, index)
|
state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
||||||
set_balance(state, index, current_balance - delta if current_balance >= delta else 0)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_permuted_index`
|
### `get_permuted_index`
|
||||||
|
@ -799,12 +774,12 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int:
|
||||||
"""
|
"""
|
||||||
Return the number of committees at ``epoch``.
|
Return the number of committees at ``epoch``.
|
||||||
"""
|
"""
|
||||||
active_validators = get_active_validator_indices(state, epoch)
|
active_validator_indices = get_active_validator_indices(state, epoch)
|
||||||
return max(
|
return max(
|
||||||
1,
|
1,
|
||||||
min(
|
min(
|
||||||
SHARD_COUNT // SLOTS_PER_EPOCH,
|
SHARD_COUNT // SLOTS_PER_EPOCH,
|
||||||
len(active_validators) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
len(active_validator_indices) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||||
)
|
)
|
||||||
) * SLOTS_PER_EPOCH
|
) * SLOTS_PER_EPOCH
|
||||||
```
|
```
|
||||||
|
@ -838,7 +813,7 @@ 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_crosslink_committees_at_slot`
|
### `get_crosslink_committees_at_slot`
|
||||||
|
|
||||||
|
@ -880,11 +855,11 @@ def get_crosslink_committees_at_slot(state: BeaconState,
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_block_root`
|
### `get_block_root_at_slot`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_block_root(state: BeaconState,
|
def get_block_root_at_slot(state: BeaconState,
|
||||||
slot: Slot) -> Bytes32:
|
slot: Slot) -> Bytes32:
|
||||||
"""
|
"""
|
||||||
Return the block root at a recent ``slot``.
|
Return the block root at a recent ``slot``.
|
||||||
"""
|
"""
|
||||||
|
@ -892,7 +867,16 @@ def get_block_root(state: BeaconState,
|
||||||
return state.latest_block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
return state.latest_block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||||
```
|
```
|
||||||
|
|
||||||
`get_block_root(_, s)` should always return `signing_root` of the block in the beacon chain at slot `s`, and `get_crosslink_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes.
|
### `get_block_root`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_block_root(state: BeaconState,
|
||||||
|
epoch: Epoch) -> Bytes32:
|
||||||
|
"""
|
||||||
|
Return the block root at a recent ``epoch``.
|
||||||
|
"""
|
||||||
|
return get_block_root_at_slot(state, get_epoch_start_slot(epoch))
|
||||||
|
```
|
||||||
|
|
||||||
### `get_state_root`
|
### `get_state_root`
|
||||||
|
|
||||||
|
@ -905,6 +889,7 @@ def get_state_root(state: BeaconState,
|
||||||
assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||||
return state.latest_state_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
return state.latest_state_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_randao_mix`
|
### `get_randao_mix`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -952,14 +937,15 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
||||||
Return the beacon proposer index at ``state.slot``.
|
Return the beacon proposer index at ``state.slot``.
|
||||||
"""
|
"""
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
|
|
||||||
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
|
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
|
||||||
|
MAX_RANDOM_BYTE = 2**8 - 1
|
||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
candidate = first_committee[(current_epoch + i) % len(first_committee)]
|
candidate_index = first_committee[(current_epoch + i) % len(first_committee)]
|
||||||
random_byte = hash(generate_seed(state, current_epoch) + int_to_bytes8(i // 32))[i % 32]
|
random_byte = hash(generate_seed(state, current_epoch) + int_to_bytes8(i // 32))[i % 32]
|
||||||
if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * random_byte:
|
effective_balance = state.validator_registry[candidate_index].effective_balance
|
||||||
return candidate
|
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||||
|
return candidate_index
|
||||||
i += 1
|
i += 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1006,24 +992,14 @@ def bytes_to_int(data: bytes) -> int:
|
||||||
return int.from_bytes(data, 'little')
|
return int.from_bytes(data, 'little')
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_effective_balance`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_effective_balance(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
|
||||||
"""
|
|
||||||
Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
|
||||||
"""
|
|
||||||
return min(get_balance(state, index), MAX_EFFECTIVE_BALANCE)
|
|
||||||
```
|
|
||||||
|
|
||||||
### `get_total_balance`
|
### `get_total_balance`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei:
|
def get_total_balance(state: BeaconState, indices: List[ValidatorIndex]) -> Gwei:
|
||||||
"""
|
"""
|
||||||
Return the combined effective balance of an array of ``validators``.
|
Return the combined effective balance of an array of ``validators``.
|
||||||
"""
|
"""
|
||||||
return sum([get_effective_balance(state, i) for i in validators])
|
return sum([state.validator_registry[index].effective_balance for index in indices])
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_domain`
|
### `get_domain`
|
||||||
|
@ -1240,11 +1216,12 @@ 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``.
|
||||||
"""
|
"""
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
initiate_validator_exit(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 = current_epoch + LATEST_SLASHED_EXIT_LENGTH
|
||||||
slashed_balance = get_effective_balance(state, slashed_index)
|
slashed_balance = state.validator_registry[slashed_index].effective_balance
|
||||||
state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance
|
state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance
|
||||||
|
|
||||||
proposer_index = get_beacon_proposer_index(state)
|
proposer_index = get_beacon_proposer_index(state)
|
||||||
if whistleblower_index is None:
|
if whistleblower_index is None:
|
||||||
|
@ -1327,7 +1304,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
|
||||||
|
|
||||||
# Process genesis activations
|
# Process genesis activations
|
||||||
for index, validator in enumerate(state.validator_registry):
|
for index, validator in enumerate(state.validator_registry):
|
||||||
if get_effective_balance(state, index) >= MAX_EFFECTIVE_BALANCE:
|
if validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||||
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||||
validator.activation_epoch = GENESIS_EPOCH
|
validator.activation_epoch = GENESIS_EPOCH
|
||||||
|
|
||||||
|
@ -1395,12 +1372,10 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock)
|
||||||
active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
|
active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
|
||||||
attestation_targets = [(i, get_latest_attestation_target(store, i)) for i in active_validator_indices]
|
attestation_targets = [(i, get_latest_attestation_target(store, i)) for i in active_validator_indices]
|
||||||
|
|
||||||
# Use the rounded-balance-with-hysteresis supplied by the protocol for fork
|
# Use the effective balance for fork choice voting to reduce recomputations and save bandwidth
|
||||||
# choice voting. This reduces the number of recomputations that need to be
|
|
||||||
# made for optimized implementations that precompute and save data
|
|
||||||
def get_vote_count(block: BeaconBlock) -> int:
|
def get_vote_count(block: BeaconBlock) -> int:
|
||||||
return sum(
|
return sum(
|
||||||
start_state.validator_registry[validator_index].high_balance
|
start_state.validator_registry[validator_index].effective_balance
|
||||||
for validator_index, target in attestation_targets
|
for validator_index, target in attestation_targets
|
||||||
if get_ancestor(store, target, block.slot) == block
|
if get_ancestor(store, target, block.slot) == block
|
||||||
)
|
)
|
||||||
|
@ -1431,7 +1406,7 @@ Transition section notes:
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
_Note_: If there are skipped slots between a block and its parent block, run the steps in the [state-root](#state-caching), [per-epoch](#per-epoch-processing), and [per-slot](#per-slot-processing) sections once for each skipped slot and then once for the slot containing the new block.
|
Note: If there are skipped slots between a block and its parent block, run the steps in the [state-root](#state-caching), [per-epoch](#per-epoch-processing), and [per-slot](#per-slot-processing) sections once for each skipped slot and then once for the slot containing the new block.
|
||||||
|
|
||||||
### State caching
|
### State caching
|
||||||
|
|
||||||
|
@ -1461,13 +1436,30 @@ The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SL
|
||||||
We define epoch transition helper functions:
|
We define epoch transition helper functions:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_current_total_balance(state: BeaconState) -> Gwei:
|
def get_total_active_balance(state: BeaconState) -> Gwei:
|
||||||
return get_total_balance(state, get_active_validator_indices(state, 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_matching_source_attestations(state: BeaconState, epoch: Epoch) -> List[PendingAttestation]:
|
||||||
return get_total_balance(state, get_active_validator_indices(state, get_previous_epoch(state)))
|
assert epoch in (get_current_epoch(state), get_previous_epoch(state))
|
||||||
|
return state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> List[PendingAttestation]:
|
||||||
|
return [
|
||||||
|
a for a in get_matching_source_attestations(state, epoch)
|
||||||
|
if a.data.target_root == get_block_root(state, epoch)
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> List[PendingAttestation]:
|
||||||
|
return [
|
||||||
|
a for a in get_matching_source_attestations(state, epoch)
|
||||||
|
if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot)
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -1483,32 +1475,6 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
|
||||||
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
|
||||||
def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]:
|
|
||||||
return [
|
|
||||||
a for a in state.current_epoch_attestations
|
|
||||||
if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state)))
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]:
|
|
||||||
return [
|
|
||||||
a for a in state.previous_epoch_attestations
|
|
||||||
if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[PendingAttestation]:
|
|
||||||
return [
|
|
||||||
a for a in state.previous_epoch_attestations
|
|
||||||
if a.data.beacon_block_root == get_block_root(state, a.data.slot)
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
|
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
|
||||||
return Crosslink(
|
return Crosslink(
|
||||||
|
@ -1519,9 +1485,8 @@ def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationDat
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
|
def get_winning_crosslink_and_attesting_indices(state: BeaconState, shard: Shard, epoch: Epoch) -> Tuple[Crosslink, List[ValidatorIndex]]:
|
||||||
pending_attestations = state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations
|
shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.shard == shard]
|
||||||
shard_attestations = [a for a in pending_attestations if a.data.shard == shard]
|
|
||||||
shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations]
|
shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations]
|
||||||
candidate_crosslinks = [
|
candidate_crosslinks = [
|
||||||
c for c in shard_crosslinks
|
c for c in shard_crosslinks
|
||||||
|
@ -1556,6 +1521,8 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
||||||
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
previous_epoch = get_previous_epoch(state)
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
old_previous_justified_epoch = state.previous_justified_epoch
|
old_previous_justified_epoch = state.previous_justified_epoch
|
||||||
old_current_justified_epoch = state.current_justified_epoch
|
old_current_justified_epoch = state.current_justified_epoch
|
||||||
|
|
||||||
|
@ -1563,36 +1530,35 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
||||||
state.previous_justified_epoch = state.current_justified_epoch
|
state.previous_justified_epoch = state.current_justified_epoch
|
||||||
state.previous_justified_root = state.current_justified_root
|
state.previous_justified_root = state.current_justified_root
|
||||||
state.justification_bitfield = (state.justification_bitfield << 1) % 2**64
|
state.justification_bitfield = (state.justification_bitfield << 1) % 2**64
|
||||||
previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state))
|
previous_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, previous_epoch))
|
||||||
if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2:
|
if previous_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
|
||||||
state.current_justified_epoch = get_previous_epoch(state)
|
state.current_justified_epoch = previous_epoch
|
||||||
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
|
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
|
||||||
state.justification_bitfield |= (1 << 1)
|
state.justification_bitfield |= (1 << 1)
|
||||||
current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state))
|
current_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, current_epoch))
|
||||||
if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2:
|
if current_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
|
||||||
state.current_justified_epoch = get_current_epoch(state)
|
state.current_justified_epoch = current_epoch
|
||||||
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
|
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
|
||||||
state.justification_bitfield |= (1 << 0)
|
state.justification_bitfield |= (1 << 0)
|
||||||
|
|
||||||
# Process finalizations
|
# Process finalizations
|
||||||
bitfield = state.justification_bitfield
|
bitfield = state.justification_bitfield
|
||||||
current_epoch = get_current_epoch(state)
|
|
||||||
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
||||||
if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3:
|
if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3:
|
||||||
state.finalized_epoch = old_previous_justified_epoch
|
state.finalized_epoch = old_previous_justified_epoch
|
||||||
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
|
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||||
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
||||||
if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2:
|
if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2:
|
||||||
state.finalized_epoch = old_previous_justified_epoch
|
state.finalized_epoch = old_previous_justified_epoch
|
||||||
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
|
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||||
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
||||||
if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch == current_epoch - 2:
|
if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch == current_epoch - 2:
|
||||||
state.finalized_epoch = old_current_justified_epoch
|
state.finalized_epoch = old_current_justified_epoch
|
||||||
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
|
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||||
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
||||||
if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1:
|
if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1:
|
||||||
state.finalized_epoch = old_current_justified_epoch
|
state.finalized_epoch = old_current_justified_epoch
|
||||||
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
|
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Crosslinks
|
#### Crosslinks
|
||||||
|
@ -1605,8 +1571,9 @@ def process_crosslinks(state: BeaconState) -> None:
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
next_epoch = get_current_epoch(state) + 1
|
next_epoch = get_current_epoch(state) + 1
|
||||||
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
|
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
|
||||||
|
epoch = slot_to_epoch(slot)
|
||||||
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
||||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
|
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
||||||
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
||||||
state.current_crosslinks[shard] = winning_crosslink
|
state.current_crosslinks[shard] = winning_crosslink
|
||||||
```
|
```
|
||||||
|
@ -1615,74 +1582,55 @@ def process_crosslinks(state: BeaconState) -> None:
|
||||||
|
|
||||||
First, we define additional helpers:
|
First, we define additional helpers:
|
||||||
|
|
||||||
```python
|
|
||||||
def get_base_reward_from_total_balance(state: BeaconState, total_balance: Gwei, index: ValidatorIndex) -> Gwei:
|
|
||||||
if total_balance == 0:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
adjusted_quotient = integer_squareroot(total_balance) // BASE_REWARD_QUOTIENT
|
|
||||||
return get_effective_balance(state, index) // adjusted_quotient // 5
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||||
return get_base_reward_from_total_balance(state, get_previous_total_balance(state), index)
|
adjusted_quotient = integer_squareroot(get_total_active_balance(state)) // BASE_REWARD_QUOTIENT
|
||||||
|
if adjusted_quotient == 0:
|
||||||
|
return 0
|
||||||
|
return state.validator_registry[index].effective_balance // adjusted_quotient // BASE_REWARDS_PER_EPOCH
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
|
def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
if epochs_since_finality <= 4:
|
previous_epoch = get_previous_epoch(state)
|
||||||
extra_penalty = 0
|
total_balance = get_total_active_balance(state)
|
||||||
else:
|
|
||||||
extra_penalty = get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2
|
|
||||||
return get_base_reward(state, index) + extra_penalty
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
|
||||||
current_epoch = get_current_epoch(state)
|
|
||||||
epochs_since_finality = current_epoch + 1 - state.finalized_epoch
|
|
||||||
rewards = [0 for index in range(len(state.validator_registry))]
|
rewards = [0 for index in range(len(state.validator_registry))]
|
||||||
penalties = [0 for index in range(len(state.validator_registry))]
|
penalties = [0 for index in range(len(state.validator_registry))]
|
||||||
# Some helper variables
|
eligible_validator_indices = [
|
||||||
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
index for index, v in enumerate(state.validator_registry)
|
||||||
boundary_attesting_balance = get_attesting_balance(state, boundary_attestations)
|
if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
|
||||||
total_balance = get_previous_total_balance(state)
|
|
||||||
total_attesting_balance = get_attesting_balance(state, state.previous_epoch_attestations)
|
|
||||||
matching_head_attestations = get_previous_epoch_matching_head_attestations(state)
|
|
||||||
matching_head_balance = get_attesting_balance(state, matching_head_attestations)
|
|
||||||
eligible_validators = [
|
|
||||||
index for index, validator in enumerate(state.validator_registry)
|
|
||||||
if (
|
|
||||||
is_active_validator(validator, current_epoch) or
|
|
||||||
(validator.slashed and current_epoch < validator.withdrawable_epoch)
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
# Process rewards or penalties for all validators
|
|
||||||
for index in eligible_validators:
|
# Micro-incentives for matching FFG source, FFG target, and head
|
||||||
base_reward = get_base_reward(state, index)
|
matching_source_attestations = get_matching_source_attestations(state, previous_epoch)
|
||||||
# Expected FFG source
|
matching_target_attestations = get_matching_target_attestations(state, previous_epoch)
|
||||||
if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
|
matching_head_attestations = get_matching_head_attestations(state, previous_epoch)
|
||||||
rewards[index] += base_reward * total_attesting_balance // total_balance
|
for attestations in (matching_source_attestations, matching_target_attestations, matching_head_attestations):
|
||||||
# Inclusion speed bonus
|
unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations)
|
||||||
earliest_attestation = get_earliest_attestation(state, state.previous_epoch_attestations, index)
|
attesting_balance = get_attesting_balance(state, attestations)
|
||||||
inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot
|
for index in eligible_validator_indices:
|
||||||
rewards[index] += base_reward * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
|
if index in unslashed_attesting_indices:
|
||||||
else:
|
rewards[index] += get_base_reward(state, index) * attesting_balance // total_balance
|
||||||
penalties[index] += base_reward
|
else:
|
||||||
# Expected FFG target
|
penalties[index] += get_base_reward(state, index)
|
||||||
if index in get_unslashed_attesting_indices(state, boundary_attestations):
|
|
||||||
rewards[index] += base_reward * boundary_attesting_balance // total_balance
|
# Proposer and inclusion delay micro-rewards
|
||||||
else:
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
||||||
penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality)
|
earliest_attestation = get_earliest_attestation(state, matching_source_attestations, index)
|
||||||
# Expected head
|
rewards[earliest_attestation.proposer_index] += get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT
|
||||||
if index in get_unslashed_attesting_indices(state, matching_head_attestations):
|
inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot
|
||||||
rewards[index] += base_reward * matching_head_balance // total_balance
|
rewards[index] += get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
|
||||||
else:
|
|
||||||
penalties[index] += base_reward
|
# Inactivity penalty
|
||||||
# Take away max rewards if we're not finalizing
|
finality_delay = previous_epoch - state.finalized_epoch
|
||||||
if epochs_since_finality > 4:
|
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||||
penalties[index] += base_reward * 4
|
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
|
||||||
|
for index in eligible_validator_indices:
|
||||||
|
penalties[index] += BASE_REWARDS_PER_EPOCH * get_base_reward(state, index)
|
||||||
|
if index not in matching_target_attesting_indices:
|
||||||
|
penalties[index] += state.validator_registry[index].effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT
|
||||||
|
|
||||||
return [rewards, penalties]
|
return [rewards, penalties]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1691,15 +1639,17 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
rewards = [0 for index in range(len(state.validator_registry))]
|
rewards = [0 for index in range(len(state.validator_registry))]
|
||||||
penalties = [0 for index in range(len(state.validator_registry))]
|
penalties = [0 for index in range(len(state.validator_registry))]
|
||||||
for slot in range(get_epoch_start_slot(get_previous_epoch(state)), get_epoch_start_slot(get_current_epoch(state))):
|
for slot in range(get_epoch_start_slot(get_previous_epoch(state)), get_epoch_start_slot(get_current_epoch(state))):
|
||||||
|
epoch = slot_to_epoch(slot)
|
||||||
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
||||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
|
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
||||||
attesting_balance = get_total_balance(state, attesting_indices)
|
attesting_balance = get_total_balance(state, attesting_indices)
|
||||||
committee_balance = get_total_balance(state, crosslink_committee)
|
committee_balance = get_total_balance(state, crosslink_committee)
|
||||||
for index in crosslink_committee:
|
for index in crosslink_committee:
|
||||||
|
base_reward = get_base_reward(state, index)
|
||||||
if index in attesting_indices:
|
if index in attesting_indices:
|
||||||
rewards[index] += get_base_reward(state, index) * attesting_balance // committee_balance
|
rewards[index] += base_reward * attesting_balance // committee_balance
|
||||||
else:
|
else:
|
||||||
penalties[index] += get_base_reward(state, index)
|
penalties[index] += base_reward
|
||||||
return [rewards, penalties]
|
return [rewards, penalties]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1710,7 +1660,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||||
return
|
return
|
||||||
|
|
||||||
rewards1, penalties1 = get_justification_and_finalization_deltas(state)
|
rewards1, penalties1 = get_attestation_deltas(state)
|
||||||
rewards2, penalties2 = get_crosslink_deltas(state)
|
rewards2, penalties2 = get_crosslink_deltas(state)
|
||||||
for i in range(len(state.validator_registry)):
|
for i in range(len(state.validator_registry)):
|
||||||
increase_balance(state, i, rewards1[i] + rewards2[i])
|
increase_balance(state, i, rewards1[i] + rewards2[i])
|
||||||
|
@ -1725,11 +1675,10 @@ Run the following function:
|
||||||
def process_registry_updates(state: BeaconState) -> None:
|
def process_registry_updates(state: BeaconState) -> None:
|
||||||
# Process activation eligibility and ejections
|
# Process activation eligibility and ejections
|
||||||
for index, validator in enumerate(state.validator_registry):
|
for index, validator in enumerate(state.validator_registry):
|
||||||
balance = get_balance(state, index)
|
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||||
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_EFFECTIVE_BALANCE:
|
|
||||||
validator.activation_eligibility_epoch = get_current_epoch(state)
|
validator.activation_eligibility_epoch = get_current_epoch(state)
|
||||||
|
|
||||||
if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE:
|
if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
|
||||||
initiate_validator_exit(state, index)
|
initiate_validator_exit(state, index)
|
||||||
|
|
||||||
# Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
|
# Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
|
||||||
|
@ -1762,8 +1711,8 @@ def process_slashings(state: BeaconState) -> None:
|
||||||
for index, validator in enumerate(state.validator_registry):
|
for index, validator in enumerate(state.validator_registry):
|
||||||
if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2:
|
if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2:
|
||||||
penalty = max(
|
penalty = max(
|
||||||
get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance,
|
validator.effective_balance * min(total_penalties * 3, total_balance) // total_balance,
|
||||||
get_effective_balance(state, index) // MIN_PENALTY_QUOTIENT
|
validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT
|
||||||
)
|
)
|
||||||
decrease_balance(state, index, penalty)
|
decrease_balance(state, index, penalty)
|
||||||
```
|
```
|
||||||
|
@ -1779,6 +1728,12 @@ def process_final_updates(state: BeaconState) -> None:
|
||||||
# Reset eth1 data votes
|
# Reset eth1 data votes
|
||||||
if state.slot % SLOTS_PER_ETH1_VOTING_PERIOD == 0:
|
if state.slot % SLOTS_PER_ETH1_VOTING_PERIOD == 0:
|
||||||
state.eth1_data_votes = []
|
state.eth1_data_votes = []
|
||||||
|
# Update effective balances with hysteresis
|
||||||
|
for index, validator in enumerate(state.validator_registry):
|
||||||
|
balance = min(state.balances[index], MAX_EFFECTIVE_BALANCE)
|
||||||
|
HALF_INCREMENT = EFFECTIVE_BALANCE_INCREMENT // 2
|
||||||
|
if balance < validator.effective_balance or validator.effective_balance + 3 * HALF_INCREMENT < balance:
|
||||||
|
validator.effective_balance = balance - balance % EFFECTIVE_BALANCE_INCREMENT
|
||||||
# Update start shard
|
# Update start shard
|
||||||
state.latest_start_shard = (state.latest_start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT
|
state.latest_start_shard = (state.latest_start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT
|
||||||
# Set active index root
|
# Set active index root
|
||||||
|
@ -1962,7 +1917,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
pending_attestation = PendingAttestation(
|
pending_attestation = PendingAttestation(
|
||||||
data=data,
|
data=data,
|
||||||
aggregation_bitfield=attestation.aggregation_bitfield,
|
aggregation_bitfield=attestation.aggregation_bitfield,
|
||||||
inclusion_slot=state.slot
|
inclusion_slot=state.slot,
|
||||||
|
proposer_index=get_beacon_proposer_index(state),
|
||||||
)
|
)
|
||||||
if target_epoch == get_current_epoch(state):
|
if target_epoch == get_current_epoch(state):
|
||||||
state.current_epoch_attestations.append(pending_attestation)
|
state.current_epoch_attestations.append(pending_attestation)
|
||||||
|
@ -1970,18 +1926,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
state.previous_epoch_attestations.append(pending_attestation)
|
state.previous_epoch_attestations.append(pending_attestation)
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `process_proposer_attestation_rewards(state)`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def process_proposer_attestation_rewards(state: BeaconState) -> None:
|
|
||||||
proposer_index = get_beacon_proposer_index(state)
|
|
||||||
for pending_attestations in (state.previous_epoch_attestations, state.current_epoch_attestations):
|
|
||||||
for index in get_unslashed_attesting_indices(state, pending_attestations):
|
|
||||||
if get_earliest_attestation(state, pending_attestations, index).inclusion_slot == state.slot:
|
|
||||||
base_reward = get_base_reward_from_total_balance(state, get_current_total_balance(state), index)
|
|
||||||
increase_balance(state, proposer_index, base_reward // PROPOSER_REWARD_QUOTIENT)
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Deposits
|
##### Deposits
|
||||||
|
|
||||||
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index)`.
|
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index)`.
|
||||||
|
@ -1991,54 +1935,41 @@ For each `deposit` in `block.body.deposits`, run the following function:
|
||||||
```python
|
```python
|
||||||
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||||
"""
|
"""
|
||||||
Process a deposit from Ethereum 1.0.
|
Process an Eth1 deposit, registering a validator or increasing its balance.
|
||||||
Used to add a validator or top up an existing validator's
|
|
||||||
balance by some ``deposit`` amount.
|
|
||||||
|
|
||||||
Note that this function mutates ``state``.
|
Note that this function mutates ``state``.
|
||||||
"""
|
"""
|
||||||
# Deposits must be processed in order
|
|
||||||
assert deposit.index == state.deposit_index
|
|
||||||
|
|
||||||
# Verify the Merkle branch
|
# Verify the Merkle branch
|
||||||
merkle_branch_is_valid = verify_merkle_branch(
|
assert verify_merkle_branch(
|
||||||
leaf=hash_tree_root(deposit.data),
|
leaf=hash_tree_root(deposit.data),
|
||||||
proof=deposit.proof,
|
proof=deposit.proof,
|
||||||
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
||||||
index=deposit.index,
|
index=deposit.index,
|
||||||
root=state.latest_eth1_data.deposit_root,
|
root=state.latest_eth1_data.deposit_root,
|
||||||
)
|
)
|
||||||
assert merkle_branch_is_valid
|
|
||||||
|
|
||||||
# Increment the next deposit index we are expecting. Note that this
|
# Deposits must be processed in order
|
||||||
# needs to be done here because while the deposit contract will never
|
assert deposit.index == state.deposit_index
|
||||||
# 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
|
state.deposit_index += 1
|
||||||
|
|
||||||
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
|
||||||
pubkey = deposit.data.pubkey
|
pubkey = deposit.data.pubkey
|
||||||
amount = deposit.data.amount
|
amount = deposit.data.amount
|
||||||
|
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
||||||
if pubkey not in validator_pubkeys:
|
if pubkey not in validator_pubkeys:
|
||||||
# Verify the deposit signature (proof of possession)
|
# Verify the deposit signature (proof of possession)
|
||||||
if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, get_domain(state, DOMAIN_DEPOSIT)):
|
if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, get_domain(state, DOMAIN_DEPOSIT)):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add new validator
|
# Add validator and balance entries
|
||||||
validator = Validator(
|
state.validator_registry.append(Validator(
|
||||||
pubkey=pubkey,
|
pubkey=pubkey,
|
||||||
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||||
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
||||||
activation_epoch=FAR_FUTURE_EPOCH,
|
activation_epoch=FAR_FUTURE_EPOCH,
|
||||||
exit_epoch=FAR_FUTURE_EPOCH,
|
exit_epoch=FAR_FUTURE_EPOCH,
|
||||||
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||||
)
|
effective_balance=amount - amount % EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
))
|
||||||
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
state.balances.append(amount)
|
||||||
state.validator_registry.append(validator)
|
|
||||||
state.balances.append(0)
|
|
||||||
set_balance(state, len(state.validator_registry) - 1, amount)
|
|
||||||
else:
|
else:
|
||||||
# Increase balance by deposit amount
|
# Increase balance by deposit amount
|
||||||
index = validator_pubkeys.index(pubkey)
|
index = validator_pubkeys.index(pubkey)
|
||||||
|
@ -2085,15 +2016,15 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
||||||
Process ``Transfer`` operation.
|
Process ``Transfer`` operation.
|
||||||
Note that this function mutates ``state``.
|
Note that this function mutates ``state``.
|
||||||
"""
|
"""
|
||||||
# Verify the amount and fee aren't individually too big (for anti-overflow purposes)
|
# Verify the amount and fee are not individually too big (for anti-overflow purposes)
|
||||||
assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee)
|
assert state.balances[transfer.sender] >= max(transfer.amount, transfer.fee)
|
||||||
# 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
|
||||||
# Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE
|
# Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE
|
||||||
assert (
|
assert (
|
||||||
state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or
|
state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or
|
||||||
get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
|
get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
|
||||||
transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(state, transfer.sender)
|
transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender]
|
||||||
)
|
)
|
||||||
# Verify that the pubkey is valid
|
# Verify that the pubkey is valid
|
||||||
assert (
|
assert (
|
||||||
|
@ -2107,8 +2038,8 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
||||||
increase_balance(state, transfer.recipient, transfer.amount)
|
increase_balance(state, transfer.recipient, transfer.amount)
|
||||||
increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
|
increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
|
||||||
# Verify balances are not dust
|
# Verify balances are not dust
|
||||||
assert not (0 < get_balance(state, transfer.sender) < MIN_DEPOSIT_AMOUNT)
|
assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
|
||||||
assert not (0 < get_balance(state, transfer.recipient) < MIN_DEPOSIT_AMOUNT)
|
assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### State root verification
|
#### State root verification
|
||||||
|
|
|
@ -180,8 +180,8 @@ def verify_block_validity_proof(proof: BlockValidityProof, validator_memory: Val
|
||||||
assert proof.shard_parent_block.beacon_chain_root == hash_tree_root(proof.header)
|
assert proof.shard_parent_block.beacon_chain_root == hash_tree_root(proof.header)
|
||||||
committee = compute_committee(proof.header, validator_memory)
|
committee = compute_committee(proof.header, validator_memory)
|
||||||
# Verify that we have >=50% support
|
# Verify that we have >=50% support
|
||||||
support_balance = sum([v.high_balance for i, v in enumerate(committee) if get_bitfield_bit(proof.shard_bitfield, i) is True])
|
support_balance = sum([v.effective_balance for i, v in enumerate(committee) if get_bitfield_bit(proof.shard_bitfield, i) is True])
|
||||||
total_balance = sum([v.high_balance for i, v in enumerate(committee)])
|
total_balance = sum([v.effective_balance for i, v in enumerate(committee)])
|
||||||
assert support_balance * 2 > total_balance
|
assert support_balance * 2 > total_balance
|
||||||
# Verify shard attestations
|
# Verify shard attestations
|
||||||
group_public_key = bls_aggregate_pubkeys([
|
group_public_key = bls_aggregate_pubkeys([
|
||||||
|
|
|
@ -138,7 +138,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`](../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).
|
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)` 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 (e.g. 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 (e.g. at 312500 validators = 10 million ETH, that's once per ~3 weeks).
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ def get_committee_assignment(
|
||||||
return assignment
|
return assignment
|
||||||
```
|
```
|
||||||
|
|
||||||
A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the slot in question and can not reliably be used to predict in advance.
|
A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the slot in question. Proposer selection is only stable within the context of the current epoch.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def is_proposer_at_slot(state: BeaconState,
|
def is_proposer_at_slot(state: BeaconState,
|
||||||
|
|
|
@ -11,7 +11,6 @@ from .spec import (
|
||||||
BeaconState,
|
BeaconState,
|
||||||
BeaconBlock,
|
BeaconBlock,
|
||||||
Slot,
|
Slot,
|
||||||
process_proposer_attestation_rewards,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +51,6 @@ def process_operations(state: BeaconState, block: BeaconBlock) -> None:
|
||||||
spec.MAX_ATTESTATIONS,
|
spec.MAX_ATTESTATIONS,
|
||||||
spec.process_attestation,
|
spec.process_attestation,
|
||||||
)
|
)
|
||||||
process_proposer_attestation_rewards(state)
|
|
||||||
|
|
||||||
assert len(block.body.deposits) == expected_deposit_count(state)
|
assert len(block.body.deposits) == expected_deposit_count(state)
|
||||||
process_operation_type(
|
process_operation_type(
|
||||||
|
|
|
@ -3,11 +3,11 @@ import pytest
|
||||||
|
|
||||||
import eth2spec.phase0.spec as spec
|
import eth2spec.phase0.spec as spec
|
||||||
from eth2spec.phase0.spec import (
|
from eth2spec.phase0.spec import (
|
||||||
get_balance,
|
|
||||||
get_beacon_proposer_index,
|
get_beacon_proposer_index,
|
||||||
process_attester_slashing,
|
process_attester_slashing,
|
||||||
)
|
)
|
||||||
from tests.helpers import (
|
from tests.helpers import (
|
||||||
|
get_balance,
|
||||||
get_valid_attester_slashing,
|
get_valid_attester_slashing,
|
||||||
next_epoch,
|
next_epoch,
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,11 +4,11 @@ import pytest
|
||||||
import eth2spec.phase0.spec as spec
|
import eth2spec.phase0.spec as spec
|
||||||
|
|
||||||
from eth2spec.phase0.spec import (
|
from eth2spec.phase0.spec import (
|
||||||
get_balance,
|
|
||||||
ZERO_HASH,
|
ZERO_HASH,
|
||||||
process_deposit,
|
process_deposit,
|
||||||
)
|
)
|
||||||
from tests.helpers import (
|
from tests.helpers import (
|
||||||
|
get_balance,
|
||||||
build_deposit,
|
build_deposit,
|
||||||
privkeys,
|
privkeys,
|
||||||
pubkeys,
|
pubkeys,
|
||||||
|
|
|
@ -3,11 +3,11 @@ import pytest
|
||||||
|
|
||||||
import eth2spec.phase0.spec as spec
|
import eth2spec.phase0.spec as spec
|
||||||
from eth2spec.phase0.spec import (
|
from eth2spec.phase0.spec import (
|
||||||
get_balance,
|
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
process_proposer_slashing,
|
process_proposer_slashing,
|
||||||
)
|
)
|
||||||
from tests.helpers import (
|
from tests.helpers import (
|
||||||
|
get_balance,
|
||||||
get_valid_proposer_slashing,
|
get_valid_proposer_slashing,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,9 @@ import eth2spec.phase0.spec as spec
|
||||||
|
|
||||||
from eth2spec.phase0.spec import (
|
from eth2spec.phase0.spec import (
|
||||||
get_active_validator_indices,
|
get_active_validator_indices,
|
||||||
get_balance,
|
|
||||||
get_beacon_proposer_index,
|
get_beacon_proposer_index,
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
process_transfer,
|
process_transfer,
|
||||||
set_balance,
|
|
||||||
)
|
)
|
||||||
from tests.helpers import (
|
from tests.helpers import (
|
||||||
get_valid_transfer,
|
get_valid_transfer,
|
||||||
|
@ -75,7 +73,7 @@ def test_success_withdrawable(state):
|
||||||
def test_success_active_above_max_effective(state):
|
def test_success_active_above_max_effective(state):
|
||||||
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
||||||
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE + amount)
|
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + amount
|
||||||
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
||||||
|
|
||||||
pre_state, post_state = run_transfer_processing(state, transfer)
|
pre_state, post_state = run_transfer_processing(state, transfer)
|
||||||
|
@ -86,7 +84,7 @@ def test_success_active_above_max_effective(state):
|
||||||
def test_active_but_transfer_past_effective_balance(state):
|
def test_active_but_transfer_past_effective_balance(state):
|
||||||
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
||||||
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE)
|
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||||
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
||||||
|
|
||||||
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
@ -107,7 +105,7 @@ def test_incorrect_slot(state):
|
||||||
def test_insufficient_balance(state):
|
def test_insufficient_balance(state):
|
||||||
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
amount = spec.MAX_EFFECTIVE_BALANCE
|
amount = spec.MAX_EFFECTIVE_BALANCE
|
||||||
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE)
|
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||||
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0)
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0)
|
||||||
|
|
||||||
# un-activate so validator can transfer
|
# un-activate so validator can transfer
|
||||||
|
|
|
@ -26,9 +26,9 @@ from eth2spec.phase0.spec import (
|
||||||
# functions
|
# functions
|
||||||
convert_to_indexed,
|
convert_to_indexed,
|
||||||
get_active_validator_indices,
|
get_active_validator_indices,
|
||||||
get_balance,
|
|
||||||
get_attesting_indices,
|
get_attesting_indices,
|
||||||
get_block_root,
|
get_block_root,
|
||||||
|
get_block_root_at_slot,
|
||||||
get_crosslink_committees_at_slot,
|
get_crosslink_committees_at_slot,
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
get_domain,
|
get_domain,
|
||||||
|
@ -53,6 +53,10 @@ pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
|
||||||
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
|
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_balance(state, index):
|
||||||
|
return state.balances[index]
|
||||||
|
|
||||||
|
|
||||||
def set_bitfield_bit(bitfield, i):
|
def set_bitfield_bit(bitfield, i):
|
||||||
"""
|
"""
|
||||||
Set the bit in ``bitfield`` at position ``i`` to ``1``.
|
Set the bit in ``bitfield`` at position ``i`` to ``1``.
|
||||||
|
@ -151,16 +155,15 @@ def build_attestation_data(state, slot, shard):
|
||||||
if slot == state.slot:
|
if slot == state.slot:
|
||||||
block_root = build_empty_block_for_next_slot(state).previous_block_root
|
block_root = build_empty_block_for_next_slot(state).previous_block_root
|
||||||
else:
|
else:
|
||||||
block_root = get_block_root(state, slot)
|
block_root = get_block_root_at_slot(state, slot)
|
||||||
|
|
||||||
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
|
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
|
||||||
if slot < current_epoch_start_slot:
|
if slot < current_epoch_start_slot:
|
||||||
print(slot)
|
epoch_boundary_root = get_block_root(state, get_previous_epoch(state))
|
||||||
epoch_boundary_root = get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
|
|
||||||
elif slot == current_epoch_start_slot:
|
elif slot == current_epoch_start_slot:
|
||||||
epoch_boundary_root = block_root
|
epoch_boundary_root = block_root
|
||||||
else:
|
else:
|
||||||
epoch_boundary_root = get_block_root(state, current_epoch_start_slot)
|
epoch_boundary_root = get_block_root(state, get_current_epoch(state))
|
||||||
|
|
||||||
if slot < current_epoch_start_slot:
|
if slot < current_epoch_start_slot:
|
||||||
justified_epoch = state.previous_justified_epoch
|
justified_epoch = state.previous_justified_epoch
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import eth2spec.phase0.spec as spec
|
||||||
|
|
||||||
|
from eth2spec.phase0.state_transition import (
|
||||||
|
state_transition,
|
||||||
|
)
|
||||||
|
from .helpers import (
|
||||||
|
build_empty_block_for_next_slot,
|
||||||
|
fill_aggregate_attestation,
|
||||||
|
get_current_epoch,
|
||||||
|
get_epoch_start_slot,
|
||||||
|
get_valid_attestation,
|
||||||
|
next_epoch,
|
||||||
|
)
|
||||||
|
|
||||||
|
# mark entire file as 'state'
|
||||||
|
pytestmark = pytest.mark.state
|
||||||
|
|
||||||
|
|
||||||
|
def check_finality(state,
|
||||||
|
prev_state,
|
||||||
|
current_justified_changed,
|
||||||
|
previous_justified_changed,
|
||||||
|
finalized_changed):
|
||||||
|
if current_justified_changed:
|
||||||
|
assert state.current_justified_epoch > prev_state.current_justified_epoch
|
||||||
|
assert state.current_justified_root != prev_state.current_justified_root
|
||||||
|
else:
|
||||||
|
assert state.current_justified_epoch == prev_state.current_justified_epoch
|
||||||
|
assert state.current_justified_root == prev_state.current_justified_root
|
||||||
|
|
||||||
|
if previous_justified_changed:
|
||||||
|
assert state.previous_justified_epoch > prev_state.previous_justified_epoch
|
||||||
|
assert state.previous_justified_root != prev_state.previous_justified_root
|
||||||
|
else:
|
||||||
|
assert state.previous_justified_epoch == prev_state.previous_justified_epoch
|
||||||
|
assert state.previous_justified_root == prev_state.previous_justified_root
|
||||||
|
|
||||||
|
if finalized_changed:
|
||||||
|
assert state.finalized_epoch > prev_state.finalized_epoch
|
||||||
|
assert state.finalized_root != prev_state.finalized_root
|
||||||
|
else:
|
||||||
|
assert state.finalized_epoch == prev_state.finalized_epoch
|
||||||
|
assert state.finalized_root == prev_state.finalized_root
|
||||||
|
|
||||||
|
|
||||||
|
def next_epoch_with_attestations(state,
|
||||||
|
fill_cur_epoch,
|
||||||
|
fill_prev_epoch):
|
||||||
|
post_state = deepcopy(state)
|
||||||
|
blocks = []
|
||||||
|
for _ in range(spec.SLOTS_PER_EPOCH):
|
||||||
|
block = build_empty_block_for_next_slot(post_state)
|
||||||
|
if fill_cur_epoch:
|
||||||
|
slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
|
||||||
|
if slot_to_attest >= get_epoch_start_slot(get_current_epoch(post_state)):
|
||||||
|
cur_attestation = get_valid_attestation(post_state, slot_to_attest)
|
||||||
|
fill_aggregate_attestation(post_state, cur_attestation)
|
||||||
|
block.body.attestations.append(cur_attestation)
|
||||||
|
|
||||||
|
if fill_prev_epoch:
|
||||||
|
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
|
||||||
|
prev_attestation = get_valid_attestation(post_state, slot_to_attest)
|
||||||
|
fill_aggregate_attestation(post_state, prev_attestation)
|
||||||
|
block.body.attestations.append(prev_attestation)
|
||||||
|
|
||||||
|
state_transition(post_state, block)
|
||||||
|
blocks.append(block)
|
||||||
|
|
||||||
|
return state, blocks, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_finality_rule_4(state):
|
||||||
|
test_state = deepcopy(state)
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
for epoch in range(4):
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
|
||||||
|
blocks += new_blocks
|
||||||
|
|
||||||
|
# justification/finalization skipped at GENESIS_EPOCH
|
||||||
|
if epoch == 0:
|
||||||
|
check_finality(test_state, prev_state, False, False, False)
|
||||||
|
# justification/finalization skipped at GENESIS_EPOCH + 1
|
||||||
|
elif epoch == 1:
|
||||||
|
check_finality(test_state, prev_state, False, False, False)
|
||||||
|
elif epoch == 2:
|
||||||
|
check_finality(test_state, prev_state, True, False, False)
|
||||||
|
elif epoch >= 3:
|
||||||
|
# rule 4 of finality
|
||||||
|
check_finality(test_state, prev_state, True, True, True)
|
||||||
|
assert test_state.finalized_epoch == prev_state.current_justified_epoch
|
||||||
|
assert test_state.finalized_root == prev_state.current_justified_root
|
||||||
|
|
||||||
|
return state, blocks, test_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_finality_rule_1(state):
|
||||||
|
# get past first two epochs that finality does not run on
|
||||||
|
next_epoch(state)
|
||||||
|
next_epoch(state)
|
||||||
|
|
||||||
|
pre_state = deepcopy(state)
|
||||||
|
test_state = deepcopy(state)
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
for epoch in range(3):
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
|
||||||
|
blocks += new_blocks
|
||||||
|
|
||||||
|
if epoch == 0:
|
||||||
|
check_finality(test_state, prev_state, True, False, False)
|
||||||
|
elif epoch == 1:
|
||||||
|
check_finality(test_state, prev_state, True, True, False)
|
||||||
|
elif epoch == 2:
|
||||||
|
# finalized by rule 1
|
||||||
|
check_finality(test_state, prev_state, True, True, True)
|
||||||
|
assert test_state.finalized_epoch == prev_state.previous_justified_epoch
|
||||||
|
assert test_state.finalized_root == prev_state.previous_justified_root
|
||||||
|
|
||||||
|
return pre_state, blocks, test_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_finality_rule_2(state):
|
||||||
|
# get past first two epochs that finality does not run on
|
||||||
|
next_epoch(state)
|
||||||
|
next_epoch(state)
|
||||||
|
|
||||||
|
pre_state = deepcopy(state)
|
||||||
|
test_state = deepcopy(state)
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
for epoch in range(3):
|
||||||
|
if epoch == 0:
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
|
||||||
|
check_finality(test_state, prev_state, True, False, False)
|
||||||
|
elif epoch == 1:
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
|
||||||
|
check_finality(test_state, prev_state, False, True, False)
|
||||||
|
elif epoch == 2:
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
|
||||||
|
# finalized by rule 2
|
||||||
|
check_finality(test_state, prev_state, True, False, True)
|
||||||
|
assert test_state.finalized_epoch == prev_state.previous_justified_epoch
|
||||||
|
assert test_state.finalized_root == prev_state.previous_justified_root
|
||||||
|
|
||||||
|
blocks += new_blocks
|
||||||
|
|
||||||
|
return pre_state, blocks, test_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_finality_rule_3(state):
|
||||||
|
"""
|
||||||
|
Test scenario described here
|
||||||
|
https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892
|
||||||
|
"""
|
||||||
|
|
||||||
|
# get past first two epochs that finality does not run on
|
||||||
|
next_epoch(state)
|
||||||
|
next_epoch(state)
|
||||||
|
|
||||||
|
pre_state = deepcopy(state)
|
||||||
|
test_state = deepcopy(state)
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
|
||||||
|
blocks += new_blocks
|
||||||
|
check_finality(test_state, prev_state, True, False, False)
|
||||||
|
|
||||||
|
# In epoch N, JE is set to N, prev JE is set to N-1
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
|
||||||
|
blocks += new_blocks
|
||||||
|
check_finality(test_state, prev_state, True, True, True)
|
||||||
|
|
||||||
|
# In epoch N+1, JE is N, prev JE is N-1, and not enough messages get in to do anything
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
|
||||||
|
blocks += new_blocks
|
||||||
|
check_finality(test_state, prev_state, False, True, False)
|
||||||
|
|
||||||
|
# In epoch N+2, JE is N, prev JE is N, and enough messages from the previous epoch get in to justify N+1.
|
||||||
|
# N+1 now becomes the JE. Not enough messages from epoch N+2 itself get in to justify N+2
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
|
||||||
|
blocks += new_blocks
|
||||||
|
# rule 2
|
||||||
|
check_finality(test_state, prev_state, True, False, True)
|
||||||
|
|
||||||
|
# In epoch N+3, LJE is N+1, prev LJE is N, and enough messages get in to justify epochs N+2 and N+3.
|
||||||
|
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, True)
|
||||||
|
blocks += new_blocks
|
||||||
|
# rule 3
|
||||||
|
check_finality(test_state, prev_state, True, True, True)
|
||||||
|
assert test_state.finalized_epoch == prev_state.current_justified_epoch
|
||||||
|
assert test_state.finalized_root == prev_state.current_justified_root
|
||||||
|
|
||||||
|
return pre_state, blocks, test_state
|
|
@ -15,16 +15,13 @@ from eth2spec.phase0.spec import (
|
||||||
VoluntaryExit,
|
VoluntaryExit,
|
||||||
# functions
|
# functions
|
||||||
get_active_validator_indices,
|
get_active_validator_indices,
|
||||||
get_balance,
|
|
||||||
get_beacon_proposer_index,
|
get_beacon_proposer_index,
|
||||||
get_block_root,
|
get_block_root_at_slot,
|
||||||
|
get_state_root,
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
get_domain,
|
get_domain,
|
||||||
get_state_root,
|
|
||||||
advance_slot,
|
advance_slot,
|
||||||
cache_state,
|
cache_state,
|
||||||
set_balance,
|
|
||||||
slot_to_epoch,
|
|
||||||
verify_merkle_branch,
|
verify_merkle_branch,
|
||||||
hash,
|
hash,
|
||||||
)
|
)
|
||||||
|
@ -37,6 +34,7 @@ from eth2spec.utils.merkle_minimal import (
|
||||||
get_merkle_root,
|
get_merkle_root,
|
||||||
)
|
)
|
||||||
from .helpers import (
|
from .helpers import (
|
||||||
|
get_balance,
|
||||||
build_deposit_data,
|
build_deposit_data,
|
||||||
build_empty_block_for_next_slot,
|
build_empty_block_for_next_slot,
|
||||||
fill_aggregate_attestation,
|
fill_aggregate_attestation,
|
||||||
|
@ -53,33 +51,6 @@ from .helpers import (
|
||||||
pytestmark = pytest.mark.sanity
|
pytestmark = pytest.mark.sanity
|
||||||
|
|
||||||
|
|
||||||
def check_finality(state,
|
|
||||||
prev_state,
|
|
||||||
current_justified_changed,
|
|
||||||
previous_justified_changed,
|
|
||||||
finalized_changed):
|
|
||||||
if current_justified_changed:
|
|
||||||
assert state.current_justified_epoch > prev_state.current_justified_epoch
|
|
||||||
assert state.current_justified_root != prev_state.current_justified_root
|
|
||||||
else:
|
|
||||||
assert state.current_justified_epoch == prev_state.current_justified_epoch
|
|
||||||
assert state.current_justified_root == prev_state.current_justified_root
|
|
||||||
|
|
||||||
if previous_justified_changed:
|
|
||||||
assert state.previous_justified_epoch > prev_state.previous_justified_epoch
|
|
||||||
assert state.previous_justified_root != prev_state.previous_justified_root
|
|
||||||
else:
|
|
||||||
assert state.previous_justified_epoch == prev_state.previous_justified_epoch
|
|
||||||
assert state.previous_justified_root == prev_state.previous_justified_root
|
|
||||||
|
|
||||||
if finalized_changed:
|
|
||||||
assert state.finalized_epoch > prev_state.finalized_epoch
|
|
||||||
assert state.finalized_root != prev_state.finalized_root
|
|
||||||
else:
|
|
||||||
assert state.finalized_epoch == prev_state.finalized_epoch
|
|
||||||
assert state.finalized_root == prev_state.finalized_root
|
|
||||||
|
|
||||||
|
|
||||||
def test_slot_transition(state):
|
def test_slot_transition(state):
|
||||||
test_state = deepcopy(state)
|
test_state = deepcopy(state)
|
||||||
cache_state(test_state)
|
cache_state(test_state)
|
||||||
|
@ -96,7 +67,7 @@ def test_empty_block_transition(state):
|
||||||
state_transition(test_state, block)
|
state_transition(test_state, block)
|
||||||
|
|
||||||
assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1
|
assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1
|
||||||
assert get_block_root(test_state, state.slot) == block.previous_block_root
|
assert get_block_root_at_slot(test_state, state.slot) == block.previous_block_root
|
||||||
|
|
||||||
return state, [block], test_state
|
return state, [block], test_state
|
||||||
|
|
||||||
|
@ -110,7 +81,7 @@ def test_skipped_slots(state):
|
||||||
|
|
||||||
assert test_state.slot == block.slot
|
assert test_state.slot == block.slot
|
||||||
for slot in range(state.slot, test_state.slot):
|
for slot in range(state.slot, test_state.slot):
|
||||||
assert get_block_root(test_state, slot) == block.previous_block_root
|
assert get_block_root_at_slot(test_state, slot) == block.previous_block_root
|
||||||
|
|
||||||
return state, [block], test_state
|
return state, [block], test_state
|
||||||
|
|
||||||
|
@ -124,7 +95,7 @@ def test_empty_epoch_transition(state):
|
||||||
|
|
||||||
assert test_state.slot == block.slot
|
assert test_state.slot == block.slot
|
||||||
for slot in range(state.slot, test_state.slot):
|
for slot in range(state.slot, test_state.slot):
|
||||||
assert get_block_root(test_state, slot) == block.previous_block_root
|
assert get_block_root_at_slot(test_state, slot) == block.previous_block_root
|
||||||
|
|
||||||
return state, [block], test_state
|
return state, [block], test_state
|
||||||
|
|
||||||
|
@ -144,33 +115,6 @@ def test_empty_epoch_transition_not_finalizing(state):
|
||||||
return state, [block], test_state
|
return state, [block], test_state
|
||||||
|
|
||||||
|
|
||||||
def test_full_attestations_finalizing(state):
|
|
||||||
test_state = deepcopy(state)
|
|
||||||
|
|
||||||
for slot in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
|
||||||
next_slot(test_state)
|
|
||||||
|
|
||||||
for epoch in range(5):
|
|
||||||
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)
|
|
||||||
fill_aggregate_attestation(test_state, attestation)
|
|
||||||
block = build_empty_block_for_next_slot(test_state)
|
|
||||||
block.body.attestations.append(attestation)
|
|
||||||
state_transition(test_state, block)
|
|
||||||
|
|
||||||
if epoch == 0:
|
|
||||||
check_finality(test_state, state, False, False, False)
|
|
||||||
elif epoch == 1:
|
|
||||||
check_finality(test_state, state, False, False, False)
|
|
||||||
elif epoch == 2:
|
|
||||||
check_finality(test_state, state, True, False, False)
|
|
||||||
elif epoch == 3:
|
|
||||||
check_finality(test_state, state, True, True, False)
|
|
||||||
elif epoch == 4:
|
|
||||||
check_finality(test_state, state, True, True, True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_proposer_slashing(state):
|
def test_proposer_slashing(state):
|
||||||
test_state = deepcopy(state)
|
test_state = deepcopy(state)
|
||||||
proposer_slashing = get_valid_proposer_slashing(state)
|
proposer_slashing = get_valid_proposer_slashing(state)
|
||||||
|
@ -303,6 +247,7 @@ def test_deposit_top_up(state):
|
||||||
|
|
||||||
|
|
||||||
def test_attestation(state):
|
def test_attestation(state):
|
||||||
|
state.slot = spec.SLOTS_PER_EPOCH
|
||||||
test_state = deepcopy(state)
|
test_state = deepcopy(state)
|
||||||
attestation = get_valid_attestation(state)
|
attestation = get_valid_attestation(state)
|
||||||
|
|
||||||
|
@ -316,8 +261,6 @@ def test_attestation(state):
|
||||||
|
|
||||||
assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
|
assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
|
||||||
|
|
||||||
proposer_index = get_beacon_proposer_index(test_state)
|
|
||||||
assert test_state.balances[proposer_index] > state.balances[proposer_index]
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Epoch transition should move to previous_epoch_attestations
|
# Epoch transition should move to previous_epoch_attestations
|
||||||
|
@ -441,7 +384,7 @@ def test_balance_driven_status_transitions(state):
|
||||||
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
|
||||||
|
|
||||||
# set validator balance to below ejection threshold
|
# set validator balance to below ejection threshold
|
||||||
set_balance(pre_state, validator_index, spec.EJECTION_BALANCE - 1)
|
pre_state.validator_registry[validator_index].effective_balance = spec.EJECTION_BALANCE
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
post_state = deepcopy(pre_state)
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
from copy import deepcopy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
from build.phase0.spec import (
|
|
||||||
get_beacon_proposer_index,
|
|
||||||
cache_state,
|
|
||||||
advance_slot,
|
|
||||||
process_block_header,
|
|
||||||
)
|
|
||||||
from tests.phase0.helpers import (
|
|
||||||
build_empty_block_for_next_slot,
|
|
||||||
)
|
|
||||||
|
|
||||||
# mark entire file as 'header'
|
|
||||||
pytestmark = pytest.mark.header
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_state_for_header_processing(state):
|
|
||||||
cache_state(state)
|
|
||||||
advance_slot(state)
|
|
||||||
|
|
||||||
|
|
||||||
def run_block_header_processing(state, block, valid=True):
|
|
||||||
"""
|
|
||||||
Run ``process_block_header`` returning the pre and post state.
|
|
||||||
If ``valid == False``, run expecting ``AssertionError``
|
|
||||||
"""
|
|
||||||
prepare_state_for_header_processing(state)
|
|
||||||
post_state = deepcopy(state)
|
|
||||||
|
|
||||||
if not valid:
|
|
||||||
with pytest.raises(AssertionError):
|
|
||||||
process_block_header(post_state, block)
|
|
||||||
return state, None
|
|
||||||
|
|
||||||
process_block_header(post_state, block)
|
|
||||||
return state, post_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_success(state):
|
|
||||||
block = build_empty_block_for_next_slot(state)
|
|
||||||
pre_state, post_state = run_block_header_processing(state, block)
|
|
||||||
return state, block, post_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_slot(state):
|
|
||||||
block = build_empty_block_for_next_slot(state)
|
|
||||||
block.slot = state.slot + 2 # invalid slot
|
|
||||||
|
|
||||||
pre_state, post_state = run_block_header_processing(state, block, valid=False)
|
|
||||||
return pre_state, block, None
|
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_previous_block_root(state):
|
|
||||||
block = build_empty_block_for_next_slot(state)
|
|
||||||
block.previous_block_root = b'\12'*32 # invalid prev root
|
|
||||||
|
|
||||||
pre_state, post_state = run_block_header_processing(state, block, valid=False)
|
|
||||||
return pre_state, block, None
|
|
|
@ -1,140 +0,0 @@
|
||||||
from copy import deepcopy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import build.phase0.spec as spec
|
|
||||||
|
|
||||||
from build.phase0.spec import (
|
|
||||||
ZERO_HASH,
|
|
||||||
process_deposit,
|
|
||||||
)
|
|
||||||
from tests.phase0.helpers import (
|
|
||||||
build_deposit,
|
|
||||||
privkeys,
|
|
||||||
pubkeys,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# mark entire file as 'voluntary_exits'
|
|
||||||
pytestmark = pytest.mark.voluntary_exits
|
|
||||||
|
|
||||||
|
|
||||||
def test_success(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
# fill previous deposits with zero-hash
|
|
||||||
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
|
||||||
|
|
||||||
index = len(deposit_data_leaves)
|
|
||||||
pubkey = pubkeys[index]
|
|
||||||
privkey = privkeys[index]
|
|
||||||
deposit, root, deposit_data_leaves = build_deposit(
|
|
||||||
pre_state,
|
|
||||||
deposit_data_leaves,
|
|
||||||
pubkey,
|
|
||||||
privkey,
|
|
||||||
spec.MAX_DEPOSIT_AMOUNT,
|
|
||||||
)
|
|
||||||
|
|
||||||
pre_state.latest_eth1_data.deposit_root = root
|
|
||||||
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
|
||||||
|
|
||||||
process_deposit(post_state, deposit)
|
|
||||||
|
|
||||||
assert len(post_state.validator_registry) == len(state.validator_registry) + 1
|
|
||||||
assert len(post_state.validator_balances) == len(state.validator_balances) + 1
|
|
||||||
assert post_state.validator_registry[index].pubkey == pubkeys[index]
|
|
||||||
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
|
|
||||||
|
|
||||||
return pre_state, deposit, post_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_success_top_up(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
|
||||||
|
|
||||||
validator_index = 0
|
|
||||||
amount = spec.MAX_DEPOSIT_AMOUNT // 4
|
|
||||||
pubkey = pubkeys[validator_index]
|
|
||||||
privkey = privkeys[validator_index]
|
|
||||||
deposit, root, deposit_data_leaves = build_deposit(
|
|
||||||
pre_state,
|
|
||||||
deposit_data_leaves,
|
|
||||||
pubkey,
|
|
||||||
privkey,
|
|
||||||
amount,
|
|
||||||
)
|
|
||||||
|
|
||||||
pre_state.latest_eth1_data.deposit_root = root
|
|
||||||
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
|
|
||||||
pre_balance = pre_state.validator_balances[validator_index]
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
|
||||||
|
|
||||||
process_deposit(post_state, deposit)
|
|
||||||
|
|
||||||
assert len(post_state.validator_registry) == len(state.validator_registry)
|
|
||||||
assert len(post_state.validator_balances) == len(state.validator_balances)
|
|
||||||
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
|
|
||||||
assert post_state.validator_balances[validator_index] == pre_balance + amount
|
|
||||||
|
|
||||||
return pre_state, deposit, post_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_index(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
|
||||||
|
|
||||||
|
|
||||||
index = len(deposit_data_leaves)
|
|
||||||
pubkey = pubkeys[index]
|
|
||||||
privkey = privkeys[index]
|
|
||||||
deposit, root, deposit_data_leaves = build_deposit(
|
|
||||||
pre_state,
|
|
||||||
deposit_data_leaves,
|
|
||||||
pubkey,
|
|
||||||
privkey,
|
|
||||||
spec.MAX_DEPOSIT_AMOUNT,
|
|
||||||
)
|
|
||||||
|
|
||||||
# mess up deposit_index
|
|
||||||
deposit.index = pre_state.deposit_index + 1
|
|
||||||
|
|
||||||
pre_state.latest_eth1_data.deposit_root = root
|
|
||||||
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
|
||||||
process_deposit(post_state, deposit)
|
|
||||||
|
|
||||||
return pre_state, deposit, None
|
|
||||||
|
|
||||||
|
|
||||||
def test_bad_merkle_proof(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
|
||||||
|
|
||||||
index = len(deposit_data_leaves)
|
|
||||||
pubkey = pubkeys[index]
|
|
||||||
privkey = privkeys[index]
|
|
||||||
deposit, root, deposit_data_leaves = build_deposit(
|
|
||||||
pre_state,
|
|
||||||
deposit_data_leaves,
|
|
||||||
pubkey,
|
|
||||||
privkey,
|
|
||||||
spec.MAX_DEPOSIT_AMOUNT,
|
|
||||||
)
|
|
||||||
|
|
||||||
# mess up merkle branch
|
|
||||||
deposit.proof[-1] = spec.ZERO_HASH
|
|
||||||
|
|
||||||
pre_state.latest_eth1_data.deposit_root = root
|
|
||||||
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
|
||||||
process_deposit(post_state, deposit)
|
|
||||||
|
|
||||||
return pre_state, deposit, None
|
|
|
@ -1,175 +0,0 @@
|
||||||
from copy import deepcopy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import build.phase0.spec as spec
|
|
||||||
|
|
||||||
from build.phase0.spec import (
|
|
||||||
get_active_validator_indices,
|
|
||||||
get_current_epoch,
|
|
||||||
process_voluntary_exit,
|
|
||||||
)
|
|
||||||
from tests.phase0.helpers import (
|
|
||||||
build_voluntary_exit,
|
|
||||||
pubkey_to_privkey,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# mark entire file as 'voluntary_exits'
|
|
||||||
pytestmark = pytest.mark.voluntary_exits
|
|
||||||
|
|
||||||
|
|
||||||
def test_success(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
#
|
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
|
|
||||||
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
|
||||||
|
|
||||||
#
|
|
||||||
# build voluntary exit
|
|
||||||
#
|
|
||||||
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]
|
|
||||||
|
|
||||||
voluntary_exit = build_voluntary_exit(
|
|
||||||
pre_state,
|
|
||||||
current_epoch,
|
|
||||||
validator_index,
|
|
||||||
privkey,
|
|
||||||
)
|
|
||||||
|
|
||||||
post_state = deepcopy(pre_state)
|
|
||||||
|
|
||||||
#
|
|
||||||
# test valid exit
|
|
||||||
#
|
|
||||||
process_voluntary_exit(post_state, voluntary_exit)
|
|
||||||
|
|
||||||
assert not pre_state.validator_registry[validator_index].initiated_exit
|
|
||||||
assert post_state.validator_registry[validator_index].initiated_exit
|
|
||||||
|
|
||||||
return pre_state, voluntary_exit, post_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_validator_not_active(state):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
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]
|
|
||||||
|
|
||||||
#
|
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
pre_state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
|
||||||
|
|
||||||
#
|
|
||||||
# build and test 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_already_exited(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 exited
|
|
||||||
pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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_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):
|
|
||||||
pre_state = deepcopy(state)
|
|
||||||
#
|
|
||||||
# setup pre_state
|
|
||||||
#
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
|
||||||
current_epoch - pre_state.validator_registry[validator_index].activation_epoch <
|
|
||||||
spec.PERSISTENT_COMMITTEE_PERIOD
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(AssertionError):
|
|
||||||
process_voluntary_exit(pre_state, voluntary_exit)
|
|
||||||
|
|
||||||
return pre_state, voluntary_exit, None
|
|
|
@ -1,203 +0,0 @@
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from py_ecc import bls
|
|
||||||
|
|
||||||
import build.phase0.spec as spec
|
|
||||||
from build.phase0.utils.minimal_ssz import signed_root
|
|
||||||
from build.phase0.spec import (
|
|
||||||
# constants
|
|
||||||
EMPTY_SIGNATURE,
|
|
||||||
# SSZ
|
|
||||||
AttestationData,
|
|
||||||
Deposit,
|
|
||||||
DepositInput,
|
|
||||||
DepositData,
|
|
||||||
Eth1Data,
|
|
||||||
VoluntaryExit,
|
|
||||||
# functions
|
|
||||||
get_block_root,
|
|
||||||
get_current_epoch,
|
|
||||||
get_domain,
|
|
||||||
get_empty_block,
|
|
||||||
get_epoch_start_slot,
|
|
||||||
get_genesis_beacon_state,
|
|
||||||
verify_merkle_branch,
|
|
||||||
hash,
|
|
||||||
)
|
|
||||||
from build.phase0.utils.merkle_minimal import (
|
|
||||||
calc_merkle_tree_from_leaves,
|
|
||||||
get_merkle_proof,
|
|
||||||
get_merkle_root,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
privkeys = [i + 1 for i in range(1000)]
|
|
||||||
pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
|
|
||||||
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
|
|
||||||
|
|
||||||
|
|
||||||
def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None):
|
|
||||||
if not deposit_data_leaves:
|
|
||||||
deposit_data_leaves = []
|
|
||||||
deposit_timestamp = 0
|
|
||||||
proof_of_possession = b'\x33' * 96
|
|
||||||
|
|
||||||
deposit_data_list = []
|
|
||||||
for i in range(num_validators):
|
|
||||||
pubkey = pubkeys[i]
|
|
||||||
deposit_data = DepositData(
|
|
||||||
amount=spec.MAX_DEPOSIT_AMOUNT,
|
|
||||||
timestamp=deposit_timestamp,
|
|
||||||
deposit_input=DepositInput(
|
|
||||||
pubkey=pubkey,
|
|
||||||
# insecurely use pubkey as withdrawal key as well
|
|
||||||
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
|
|
||||||
proof_of_possession=proof_of_possession,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
item = hash(deposit_data.serialize())
|
|
||||||
deposit_data_leaves.append(item)
|
|
||||||
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
|
|
||||||
root = get_merkle_root((tuple(deposit_data_leaves)))
|
|
||||||
proof = list(get_merkle_proof(tree, item_index=i))
|
|
||||||
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, i, root)
|
|
||||||
deposit_data_list.append(deposit_data)
|
|
||||||
|
|
||||||
genesis_validator_deposits = []
|
|
||||||
for i in range(num_validators):
|
|
||||||
genesis_validator_deposits.append(Deposit(
|
|
||||||
proof=list(get_merkle_proof(tree, item_index=i)),
|
|
||||||
index=i,
|
|
||||||
deposit_data=deposit_data_list[i]
|
|
||||||
))
|
|
||||||
return genesis_validator_deposits, root
|
|
||||||
|
|
||||||
|
|
||||||
def create_genesis_state(num_validators, deposit_data_leaves=None):
|
|
||||||
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(
|
|
||||||
num_validators,
|
|
||||||
deposit_data_leaves,
|
|
||||||
)
|
|
||||||
return get_genesis_beacon_state(
|
|
||||||
initial_deposits,
|
|
||||||
genesis_time=0,
|
|
||||||
genesis_eth1_data=Eth1Data(
|
|
||||||
deposit_root=deposit_root,
|
|
||||||
deposit_count=len(initial_deposits),
|
|
||||||
block_hash=spec.ZERO_HASH,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
empty_block = get_empty_block()
|
|
||||||
empty_block.slot = state.slot + 1
|
|
||||||
previous_block_header = deepcopy(state.latest_block_header)
|
|
||||||
if previous_block_header.state_root == spec.ZERO_HASH:
|
|
||||||
previous_block_header.state_root = state.hash_tree_root()
|
|
||||||
empty_block.previous_block_root = signed_root(previous_block_header)
|
|
||||||
return empty_block
|
|
||||||
|
|
||||||
|
|
||||||
def build_deposit_data(state, pubkey, privkey, amount):
|
|
||||||
deposit_input = DepositInput(
|
|
||||||
pubkey=pubkey,
|
|
||||||
# insecurely use pubkey as withdrawal key as well
|
|
||||||
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
|
|
||||||
proof_of_possession=EMPTY_SIGNATURE,
|
|
||||||
)
|
|
||||||
proof_of_possession = bls.sign(
|
|
||||||
message_hash=signed_root(deposit_input),
|
|
||||||
privkey=privkey,
|
|
||||||
domain=get_domain(
|
|
||||||
state.fork,
|
|
||||||
get_current_epoch(state),
|
|
||||||
spec.DOMAIN_DEPOSIT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deposit_input.proof_of_possession = proof_of_possession
|
|
||||||
deposit_data = DepositData(
|
|
||||||
amount=amount,
|
|
||||||
timestamp=0,
|
|
||||||
deposit_input=deposit_input,
|
|
||||||
)
|
|
||||||
return deposit_data
|
|
||||||
|
|
||||||
|
|
||||||
def build_attestation_data(state, slot, shard):
|
|
||||||
assert state.slot >= slot
|
|
||||||
|
|
||||||
block_root = build_empty_block_for_next_slot(state).previous_block_root
|
|
||||||
|
|
||||||
epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
|
|
||||||
if epoch_start_slot == slot:
|
|
||||||
epoch_boundary_root = block_root
|
|
||||||
else:
|
|
||||||
get_block_root(state, epoch_start_slot)
|
|
||||||
|
|
||||||
if slot < epoch_start_slot:
|
|
||||||
justified_block_root = state.previous_justified_root
|
|
||||||
else:
|
|
||||||
justified_block_root = state.current_justified_root
|
|
||||||
|
|
||||||
return AttestationData(
|
|
||||||
slot=slot,
|
|
||||||
shard=shard,
|
|
||||||
beacon_block_root=block_root,
|
|
||||||
source_epoch=state.current_justified_epoch,
|
|
||||||
source_root=justified_block_root,
|
|
||||||
target_root=epoch_boundary_root,
|
|
||||||
crosslink_data_root=spec.ZERO_HASH,
|
|
||||||
previous_crosslink=deepcopy(state.latest_crosslinks[shard]),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def build_voluntary_exit(state, epoch, validator_index, privkey):
|
|
||||||
voluntary_exit = VoluntaryExit(
|
|
||||||
epoch=epoch,
|
|
||||||
validator_index=validator_index,
|
|
||||||
signature=EMPTY_SIGNATURE,
|
|
||||||
)
|
|
||||||
voluntary_exit.signature = bls.sign(
|
|
||||||
message_hash=signed_root(voluntary_exit),
|
|
||||||
privkey=privkey,
|
|
||||||
domain=get_domain(
|
|
||||||
fork=state.fork,
|
|
||||||
epoch=epoch,
|
|
||||||
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return voluntary_exit
|
|
||||||
|
|
||||||
|
|
||||||
def build_deposit(state,
|
|
||||||
deposit_data_leaves,
|
|
||||||
pubkey,
|
|
||||||
privkey,
|
|
||||||
amount):
|
|
||||||
deposit_data = build_deposit_data(state, pubkey, privkey, amount)
|
|
||||||
|
|
||||||
item = hash(deposit_data.serialize())
|
|
||||||
index = len(deposit_data_leaves)
|
|
||||||
deposit_data_leaves.append(item)
|
|
||||||
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
|
|
||||||
root = get_merkle_root((tuple(deposit_data_leaves)))
|
|
||||||
proof = list(get_merkle_proof(tree, item_index=index))
|
|
||||||
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
|
|
||||||
|
|
||||||
deposit = Deposit(
|
|
||||||
proof=list(proof),
|
|
||||||
index=index,
|
|
||||||
deposit_data=deposit_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
return deposit, root, deposit_data_leaves
|
|
|
@ -1,52 +0,0 @@
|
||||||
from .minimal_ssz import hash_tree_root
|
|
||||||
|
|
||||||
|
|
||||||
def jsonize(value, typ, include_hash_tree_roots=False):
|
|
||||||
if isinstance(typ, str) and typ[:4] == 'uint':
|
|
||||||
return value
|
|
||||||
elif typ == 'bool':
|
|
||||||
assert value in (True, False)
|
|
||||||
return value
|
|
||||||
elif isinstance(typ, list):
|
|
||||||
return [jsonize(element, typ[0], include_hash_tree_roots) for element in value]
|
|
||||||
elif isinstance(typ, str) and typ[:4] == 'byte':
|
|
||||||
return '0x' + value.hex()
|
|
||||||
elif hasattr(typ, 'fields'):
|
|
||||||
ret = {}
|
|
||||||
for field, subtype in typ.fields.items():
|
|
||||||
ret[field] = jsonize(getattr(value, field), subtype, include_hash_tree_roots)
|
|
||||||
if include_hash_tree_roots:
|
|
||||||
ret[field + "_hash_tree_root"] = '0x' + hash_tree_root(getattr(value, field), subtype).hex()
|
|
||||||
if include_hash_tree_roots:
|
|
||||||
ret["hash_tree_root"] = '0x' + hash_tree_root(value, typ).hex()
|
|
||||||
return ret
|
|
||||||
else:
|
|
||||||
print(value, typ)
|
|
||||||
raise Exception("Type not recognized")
|
|
||||||
|
|
||||||
|
|
||||||
def dejsonize(json, typ):
|
|
||||||
if isinstance(typ, str) and typ[:4] == 'uint':
|
|
||||||
return json
|
|
||||||
elif typ == 'bool':
|
|
||||||
assert json in (True, False)
|
|
||||||
return json
|
|
||||||
elif isinstance(typ, list):
|
|
||||||
return [dejsonize(element, typ[0]) for element in json]
|
|
||||||
elif isinstance(typ, str) and typ[:4] == 'byte':
|
|
||||||
return bytes.fromhex(json[2:])
|
|
||||||
elif hasattr(typ, 'fields'):
|
|
||||||
temp = {}
|
|
||||||
for field, subtype in typ.fields.items():
|
|
||||||
temp[field] = dejsonize(json[field], subtype)
|
|
||||||
if field + "_hash_tree_root" in json:
|
|
||||||
assert(json[field + "_hash_tree_root"][2:] ==
|
|
||||||
hash_tree_root(temp[field], subtype).hex())
|
|
||||||
ret = typ(**temp)
|
|
||||||
if "hash_tree_root" in json:
|
|
||||||
assert(json["hash_tree_root"][2:] ==
|
|
||||||
hash_tree_root(ret, typ).hex())
|
|
||||||
return ret
|
|
||||||
else:
|
|
||||||
print(json, typ)
|
|
||||||
raise Exception("Type not recognized")
|
|
Loading…
Reference in New Issue