Merge branch 'dev' into vbuterin-patch-16
This commit is contained in:
commit
654351faea
|
@ -4,3 +4,4 @@
|
|||
/.pytest_cache
|
||||
|
||||
build/
|
||||
output/
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
- [`AttestationData`](#attestationdata)
|
||||
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
|
||||
- [`SlashableAttestation`](#slashableattestation)
|
||||
- [`DepositInput`](#depositinput)
|
||||
- [`DepositData`](#depositdata)
|
||||
- [`BeaconBlockHeader`](#beaconblockheader)
|
||||
- [`Validator`](#validator)
|
||||
|
@ -59,14 +58,17 @@
|
|||
- [`get_current_epoch`](#get_current_epoch)
|
||||
- [`get_epoch_start_slot`](#get_epoch_start_slot)
|
||||
- [`is_active_validator`](#is_active_validator)
|
||||
- [`is_slashable_validator`](#is_slashable_validator)
|
||||
- [`get_active_validator_indices`](#get_active_validator_indices)
|
||||
- [`get_balance`](#get_balance)
|
||||
- [`set_balance`](#set_balance)
|
||||
- [`increase_balance`](#increase_balance)
|
||||
- [`decrease_balance`](#decrease_balance)
|
||||
- [`get_permuted_index`](#get_permuted_index)
|
||||
- [`get_split_offset`](#get_split_offset)
|
||||
- [`get_epoch_committee_count`](#get_epoch_committee_count)
|
||||
- [`compute_committee`](#compute_committee)
|
||||
- [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count)
|
||||
- [`get_current_epoch_committee_count`](#get_current_epoch_committee_count)
|
||||
- [`get_next_epoch_committee_count`](#get_next_epoch_committee_count)
|
||||
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
||||
- [`get_block_root`](#get_block_root)
|
||||
- [`get_state_root`](#get_state_root)
|
||||
|
@ -76,7 +78,6 @@
|
|||
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
|
||||
- [`verify_merkle_branch`](#verify_merkle_branch)
|
||||
- [`get_attestation_participants`](#get_attestation_participants)
|
||||
- [`is_power_of_two`](#is_power_of_two)
|
||||
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
||||
- [`bytes_to_int`](#bytes_to_int)
|
||||
- [`get_effective_balance`](#get_effective_balance)
|
||||
|
@ -182,7 +183,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
|||
| `SHARD_COUNT` | `2**10` (= 1,024) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) |
|
||||
| `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) |
|
||||
| `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
|
||||
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
|
||||
| `SHUFFLE_ROUND_COUNT` | 90 |
|
||||
|
||||
|
@ -201,8 +202,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
|||
| - | - | :-: |
|
||||
| `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
|
||||
| `MAX_DEPOSIT_AMOUNT` | `2**5 * 10**9` (= 32,000,000,000) | Gwei |
|
||||
| `FORK_CHOICE_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,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 |
|
||||
|
||||
### Initial values
|
||||
|
||||
|
@ -232,6 +233,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
|||
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
|
||||
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
|
||||
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
|
||||
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) |
|
||||
|
||||
* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
|
||||
|
||||
|
||||
### State list lengths
|
||||
|
||||
|
@ -252,7 +257,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
|||
| `MIN_PENALTY_QUOTIENT` | `2**5` (= 32) |
|
||||
|
||||
* The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch.
|
||||
* 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)`.
|
||||
|
||||
|
||||
### Max transactions per block
|
||||
|
@ -379,7 +384,7 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
}
|
||||
```
|
||||
|
||||
#### `DepositInput`
|
||||
#### `DepositData`
|
||||
|
||||
```python
|
||||
{
|
||||
|
@ -387,21 +392,10 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
'pubkey': 'bytes48',
|
||||
# Withdrawal credentials
|
||||
'withdrawal_credentials': 'bytes32',
|
||||
# A BLS signature of this `DepositInput`
|
||||
'proof_of_possession': 'bytes96',
|
||||
}
|
||||
```
|
||||
|
||||
#### `DepositData`
|
||||
|
||||
```python
|
||||
{
|
||||
# Amount in Gwei
|
||||
'amount': 'uint64',
|
||||
# Timestamp from deposit contract
|
||||
'timestamp': 'uint64',
|
||||
# Deposit input
|
||||
'deposit_input': DepositInput,
|
||||
# Container self-signature
|
||||
'proof_of_possession': 'bytes96',
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -416,7 +410,6 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
'signature': 'bytes96',
|
||||
}
|
||||
```
|
||||
|
||||
#### `Validator`
|
||||
|
||||
```python
|
||||
|
@ -435,6 +428,8 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
'initiated_exit': 'bool',
|
||||
# Was the validator slashed
|
||||
'slashed': 'bool',
|
||||
# Rounded balance
|
||||
'high_balance': 'uint64'
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -514,7 +509,7 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
# Index in the deposit tree
|
||||
'index': 'uint64',
|
||||
# Data
|
||||
'deposit_data': DepositData,
|
||||
'data': DepositData,
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -595,17 +590,12 @@ The types are defined topologically to aid in facilitating an executable version
|
|||
|
||||
# Validator registry
|
||||
'validator_registry': [Validator],
|
||||
'validator_balances': ['uint64'],
|
||||
'balances': ['uint64'],
|
||||
'validator_registry_update_epoch': 'uint64',
|
||||
|
||||
# Randomness and committees
|
||||
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
|
||||
'previous_shuffling_start_shard': 'uint64',
|
||||
'current_shuffling_start_shard': 'uint64',
|
||||
'previous_shuffling_epoch': 'uint64',
|
||||
'current_shuffling_epoch': 'uint64',
|
||||
'previous_shuffling_seed': 'bytes32',
|
||||
'current_shuffling_seed': 'bytes32',
|
||||
'latest_start_shard': 'uint64',
|
||||
|
||||
# Finality
|
||||
'previous_epoch_attestations': [PendingAttestation],
|
||||
|
@ -662,7 +652,7 @@ def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32:
|
|||
|
||||
### `hash`
|
||||
|
||||
The hash function is denoted by `hash`. In Phase 0 the beacon chain is deployed with the same hash function as Ethereum 1.0, i.e. Keccak-256 (also incorrectly known as SHA3).
|
||||
The `hash` function is SHA256.
|
||||
|
||||
Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethereum 2.0 deployment phase.
|
||||
|
||||
|
@ -686,7 +676,8 @@ def get_temporary_block_header(block: BeaconBlock) -> BeaconBlockHeader:
|
|||
previous_block_root=block.previous_block_root,
|
||||
state_root=ZERO_HASH,
|
||||
block_body_root=hash_tree_root(block.body),
|
||||
signature=block.signature,
|
||||
# signed_root(block) is used for block id purposes so signature is a stub
|
||||
signature=EMPTY_SIGNATURE,
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -739,6 +730,18 @@ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
|||
return validator.activation_epoch <= epoch < validator.exit_epoch
|
||||
```
|
||||
|
||||
### `is_slashable_validator`
|
||||
```python
|
||||
def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
"""
|
||||
Check if ``validator`` is slashable.
|
||||
"""
|
||||
return (
|
||||
validator.activation_epoch <= epoch < validator.withdrawable_epoch and
|
||||
validator.slashed is False
|
||||
)
|
||||
```
|
||||
|
||||
### `get_active_validator_indices`
|
||||
|
||||
```python
|
||||
|
@ -749,12 +752,59 @@ def get_active_validator_indices(validators: List[Validator], epoch: Epoch) -> L
|
|||
return [i for i, v in enumerate(validators) 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`
|
||||
|
||||
```python
|
||||
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
"""
|
||||
Increase the balance for a validator with the given ``index`` by ``delta``.
|
||||
"""
|
||||
set_balance(state, index, get_balance(state, index) + delta)
|
||||
```
|
||||
|
||||
### `decrease_balance`
|
||||
|
||||
```python
|
||||
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
"""
|
||||
Decrease the balance for a validator with the given ``index`` by ``delta``.
|
||||
Set to ``0`` when underflow.
|
||||
"""
|
||||
current_balance = get_balance(state, index)
|
||||
set_balance(state, index, current_balance - delta if current_balance >= delta else 0)
|
||||
```
|
||||
|
||||
### `get_permuted_index`
|
||||
|
||||
```python
|
||||
def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int:
|
||||
"""
|
||||
Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy.
|
||||
Return `p(index)` in a pseudorandom permutation `p` of `0...list_size - 1` with ``seed`` as entropy.
|
||||
|
||||
Utilizes 'swap or not' shuffling found in
|
||||
https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf
|
||||
|
@ -823,20 +873,6 @@ def compute_committee(validator_indices: List[ValidatorIndex],
|
|||
|
||||
**Note**: this definition and the next few definitions are highly inefficient as algorithms as they re-calculate many sub-expressions. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.
|
||||
|
||||
### `get_previous_epoch_committee_count`
|
||||
|
||||
```python
|
||||
def get_previous_epoch_committee_count(state: BeaconState) -> int:
|
||||
"""
|
||||
Return the number of committees in the previous epoch of the given ``state``.
|
||||
"""
|
||||
previous_active_validators = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
state.previous_shuffling_epoch,
|
||||
)
|
||||
return get_epoch_committee_count(len(previous_active_validators))
|
||||
```
|
||||
|
||||
### `get_current_epoch_committee_count`
|
||||
|
||||
```python
|
||||
|
@ -846,36 +882,18 @@ def get_current_epoch_committee_count(state: BeaconState) -> int:
|
|||
"""
|
||||
current_active_validators = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
state.current_shuffling_epoch,
|
||||
get_current_epoch(state),
|
||||
)
|
||||
return get_epoch_committee_count(len(current_active_validators))
|
||||
```
|
||||
|
||||
### `get_next_epoch_committee_count`
|
||||
|
||||
```python
|
||||
def get_next_epoch_committee_count(state: BeaconState) -> int:
|
||||
"""
|
||||
Return the number of committees in the next epoch of the given ``state``.
|
||||
"""
|
||||
next_active_validators = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
get_current_epoch(state) + 1,
|
||||
)
|
||||
return get_epoch_committee_count(len(next_active_validators))
|
||||
```
|
||||
|
||||
### `get_crosslink_committees_at_slot`
|
||||
|
||||
```python
|
||||
def get_crosslink_committees_at_slot(state: BeaconState,
|
||||
slot: Slot,
|
||||
registry_change: bool=False) -> List[Tuple[List[ValidatorIndex], Shard]]:
|
||||
slot: Slot) -> List[Tuple[List[ValidatorIndex], Shard]]:
|
||||
"""
|
||||
Return the list of ``(committee, shard)`` tuples for the ``slot``.
|
||||
|
||||
Note: There are two possible shufflings for crosslink committees for a
|
||||
``slot`` in the next epoch -- with and without a `registry_change`
|
||||
"""
|
||||
epoch = slot_to_epoch(slot)
|
||||
current_epoch = get_current_epoch(state)
|
||||
|
@ -883,40 +901,24 @@ def get_crosslink_committees_at_slot(state: BeaconState,
|
|||
next_epoch = current_epoch + 1
|
||||
|
||||
assert previous_epoch <= epoch <= next_epoch
|
||||
indices = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
epoch,
|
||||
)
|
||||
committees_per_epoch = get_epoch_committee_count(len(indices))
|
||||
|
||||
if epoch == current_epoch:
|
||||
committees_per_epoch = get_current_epoch_committee_count(state)
|
||||
seed = state.current_shuffling_seed
|
||||
shuffling_epoch = state.current_shuffling_epoch
|
||||
shuffling_start_shard = state.current_shuffling_start_shard
|
||||
start_shard = state.latest_start_shard
|
||||
elif epoch == previous_epoch:
|
||||
committees_per_epoch = get_previous_epoch_committee_count(state)
|
||||
seed = state.previous_shuffling_seed
|
||||
shuffling_epoch = state.previous_shuffling_epoch
|
||||
shuffling_start_shard = state.previous_shuffling_start_shard
|
||||
start_shard = (state.latest_start_shard - committees_per_epoch) % SHARD_COUNT
|
||||
elif epoch == next_epoch:
|
||||
epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
|
||||
if registry_change:
|
||||
committees_per_epoch = get_next_epoch_committee_count(state)
|
||||
seed = generate_seed(state, next_epoch)
|
||||
shuffling_epoch = next_epoch
|
||||
current_committees_per_epoch = get_current_epoch_committee_count(state)
|
||||
shuffling_start_shard = (state.current_shuffling_start_shard + current_committees_per_epoch) % SHARD_COUNT
|
||||
elif epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update):
|
||||
committees_per_epoch = get_next_epoch_committee_count(state)
|
||||
seed = generate_seed(state, next_epoch)
|
||||
shuffling_epoch = next_epoch
|
||||
shuffling_start_shard = state.current_shuffling_start_shard
|
||||
else:
|
||||
committees_per_epoch = get_current_epoch_committee_count(state)
|
||||
seed = state.current_shuffling_seed
|
||||
shuffling_epoch = state.current_shuffling_epoch
|
||||
shuffling_start_shard = state.current_shuffling_start_shard
|
||||
current_epoch_committees = get_current_epoch_committee_count(state)
|
||||
start_shard = (state.latest_start_shard + current_epoch_committees) % SHARD_COUNT
|
||||
|
||||
indices = get_active_validator_indices(state.validator_registry, shuffling_epoch)
|
||||
committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH
|
||||
offset = slot % SLOTS_PER_EPOCH
|
||||
slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT
|
||||
slot_start_shard = (start_shard + committees_per_slot * offset) % SHARD_COUNT
|
||||
seed = generate_seed(state, epoch)
|
||||
|
||||
return [
|
||||
(
|
||||
|
@ -995,20 +997,26 @@ def generate_seed(state: BeaconState,
|
|||
|
||||
```python
|
||||
def get_beacon_proposer_index(state: BeaconState,
|
||||
slot: Slot,
|
||||
registry_change: bool=False) -> ValidatorIndex:
|
||||
slot: Slot) -> ValidatorIndex:
|
||||
"""
|
||||
Return the beacon proposer index for the ``slot``.
|
||||
Due to proposer selection being based upon the validator balances during
|
||||
the epoch in question, this can only be run for the current epoch.
|
||||
"""
|
||||
epoch = slot_to_epoch(slot)
|
||||
current_epoch = get_current_epoch(state)
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
next_epoch = current_epoch + 1
|
||||
assert slot_to_epoch(slot) == current_epoch
|
||||
|
||||
assert previous_epoch <= epoch <= next_epoch
|
||||
|
||||
first_committee, _ = get_crosslink_committees_at_slot(state, slot, registry_change)[0]
|
||||
return first_committee[epoch % len(first_committee)]
|
||||
first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0]
|
||||
i = 0
|
||||
while True:
|
||||
rand_byte = hash(
|
||||
generate_seed(state, current_epoch) +
|
||||
int_to_bytes8(i // 32)
|
||||
)[i % 32]
|
||||
candidate = first_committee[(current_epoch + i) % len(first_committee)]
|
||||
if get_effective_balance(state, candidate) * 256 > MAX_DEPOSIT_AMOUNT * rand_byte:
|
||||
return candidate
|
||||
i += 1
|
||||
```
|
||||
|
||||
### `verify_merkle_branch`
|
||||
|
@ -1054,16 +1062,6 @@ def get_attestation_participants(state: BeaconState,
|
|||
return participants
|
||||
```
|
||||
|
||||
### `is_power_of_two`
|
||||
|
||||
```python
|
||||
def is_power_of_two(value: int) -> bool:
|
||||
"""
|
||||
Check if ``value`` is a power of two integer.
|
||||
"""
|
||||
return (value > 0) and (value & (value - 1) == 0)
|
||||
```
|
||||
|
||||
### `int_to_bytes1`, `int_to_bytes2`, ...
|
||||
|
||||
`int_to_bytes1(x): return x.to_bytes(1, 'little')`, `int_to_bytes2(x): return x.to_bytes(2, 'little')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96.
|
||||
|
@ -1082,7 +1080,7 @@ 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(state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
|
||||
return min(get_balance(state, index), MAX_DEPOSIT_AMOUNT)
|
||||
```
|
||||
|
||||
### `get_total_balance`
|
||||
|
@ -1159,7 +1157,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas
|
|||
if slashable_attestation.custody_bitfield != b'\x00' * len(slashable_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
|
||||
return False
|
||||
|
||||
if len(slashable_attestation.validator_indices) == 0:
|
||||
if not (1 <= len(slashable_attestation.validator_indices) <= MAX_SLASHABLE_ATTESTATION_PARTICIPANTS):
|
||||
return False
|
||||
|
||||
for i in range(len(slashable_attestation.validator_indices) - 1):
|
||||
|
@ -1169,9 +1167,6 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas
|
|||
if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)):
|
||||
return False
|
||||
|
||||
if len(slashable_attestation.validator_indices) > MAX_INDICES_PER_SLASHABLE_VOTE:
|
||||
return False
|
||||
|
||||
custody_bit_0_indices = []
|
||||
custody_bit_1_indices = []
|
||||
for i, validator_index in enumerate(slashable_attestation.validator_indices):
|
||||
|
@ -1271,19 +1266,12 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
Process a deposit from Ethereum 1.0.
|
||||
Note that this function mutates ``state``.
|
||||
"""
|
||||
deposit_input = deposit.deposit_data.deposit_input
|
||||
|
||||
# Should equal 8 bytes for deposit_data.amount +
|
||||
# 8 bytes for deposit_data.timestamp +
|
||||
# 176 bytes for deposit_data.deposit_input
|
||||
# It should match the deposit_data in the eth1.0 deposit contract
|
||||
serialized_deposit_data = serialize(deposit.deposit_data)
|
||||
# Deposits must be processed in order
|
||||
assert deposit.index == state.deposit_index
|
||||
|
||||
# Verify the Merkle branch
|
||||
merkle_branch_is_valid = verify_merkle_branch(
|
||||
leaf=hash(serialized_deposit_data),
|
||||
leaf=hash(serialize(deposit.data)), # 48 + 32 + 8 + 96 = 184 bytes serialization
|
||||
proof=deposit.proof,
|
||||
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
||||
index=deposit.index,
|
||||
|
@ -1298,16 +1286,15 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
state.deposit_index += 1
|
||||
|
||||
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
||||
pubkey = deposit_input.pubkey
|
||||
amount = deposit.deposit_data.amount
|
||||
withdrawal_credentials = deposit_input.withdrawal_credentials
|
||||
pubkey = deposit.data.pubkey
|
||||
amount = deposit.data.amount
|
||||
|
||||
if pubkey not in validator_pubkeys:
|
||||
# Verify the proof of possession
|
||||
proof_is_valid = bls_verify(
|
||||
pubkey=deposit_input.pubkey,
|
||||
message_hash=signed_root(deposit_input),
|
||||
signature=deposit_input.proof_of_possession,
|
||||
pubkey=pubkey,
|
||||
message_hash=signed_root(deposit.data),
|
||||
signature=deposit.data.proof_of_possession,
|
||||
domain=get_domain(
|
||||
state.fork,
|
||||
get_current_epoch(state),
|
||||
|
@ -1320,20 +1307,23 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
# Add new validator
|
||||
validator = Validator(
|
||||
pubkey=pubkey,
|
||||
withdrawal_credentials=withdrawal_credentials,
|
||||
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
activation_epoch=FAR_FUTURE_EPOCH,
|
||||
exit_epoch=FAR_FUTURE_EPOCH,
|
||||
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||
initiated_exit=False,
|
||||
slashed=False,
|
||||
high_balance=0
|
||||
)
|
||||
|
||||
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
||||
state.validator_registry.append(validator)
|
||||
state.validator_balances.append(amount)
|
||||
state.balances.append(0)
|
||||
set_balance(state, len(state.validator_registry) - 1, amount)
|
||||
else:
|
||||
# Increase balance by deposit amount
|
||||
state.validator_balances[validator_pubkeys.index(pubkey)] += amount
|
||||
index = validator_pubkeys.index(pubkey)
|
||||
increase_balance(state, index, amount)
|
||||
```
|
||||
|
||||
### Routines for updating validator status
|
||||
|
@ -1389,14 +1379,13 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
|||
Note that this function mutates ``state``.
|
||||
"""
|
||||
validator = state.validator_registry[index]
|
||||
assert state.slot < get_epoch_start_slot(validator.withdrawable_epoch) # [TO BE REMOVED IN PHASE 2]
|
||||
exit_validator(state, index)
|
||||
state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index)
|
||||
|
||||
whistleblower_index = get_beacon_proposer_index(state, state.slot)
|
||||
whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT
|
||||
state.validator_balances[whistleblower_index] += whistleblower_reward
|
||||
state.validator_balances[index] -= whistleblower_reward
|
||||
increase_balance(state, whistleblower_index, whistleblower_reward)
|
||||
decrease_balance(state, index, whistleblower_reward)
|
||||
validator.slashed = True
|
||||
validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
|
||||
```
|
||||
|
@ -1420,11 +1409,11 @@ The initial deployment phases of Ethereum 2.0 are implemented without consensus
|
|||
|
||||
### Deposit arguments
|
||||
|
||||
The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositInput`.
|
||||
The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositData`.
|
||||
|
||||
### Withdrawal credentials
|
||||
|
||||
One of the `DepositInput` fields is `withdrawal_credentials`. It is a commitment to credentials for withdrawals to shards. The first byte of `withdrawal_credentials` is a version number. As of now the only expected format is as follows:
|
||||
One of the `DepositData` fields is `withdrawal_credentials`. It is a commitment to credentials for withdrawals to shards. The first byte of `withdrawal_credentials` is a version number. As of now the only expected format is as follows:
|
||||
|
||||
* `withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE`
|
||||
* `withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]` where `withdrawal_pubkey` is a BLS pubkey
|
||||
|
@ -1517,17 +1506,12 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
|
|||
|
||||
# Validator registry
|
||||
validator_registry=[],
|
||||
validator_balances=[],
|
||||
balances=[],
|
||||
validator_registry_update_epoch=GENESIS_EPOCH,
|
||||
|
||||
# Randomness and committees
|
||||
latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]),
|
||||
previous_shuffling_start_shard=GENESIS_START_SHARD,
|
||||
current_shuffling_start_shard=GENESIS_START_SHARD,
|
||||
previous_shuffling_epoch=GENESIS_EPOCH - 1,
|
||||
current_shuffling_epoch=GENESIS_EPOCH,
|
||||
previous_shuffling_seed=ZERO_HASH,
|
||||
current_shuffling_seed=ZERO_HASH,
|
||||
latest_start_shard=GENESIS_START_SHARD,
|
||||
|
||||
# Finality
|
||||
previous_epoch_attestations=[],
|
||||
|
@ -1567,7 +1551,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
|
|||
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH))
|
||||
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
|
||||
state.latest_active_index_roots[index] = genesis_active_index_root
|
||||
state.current_shuffling_seed = generate_seed(state, GENESIS_EPOCH)
|
||||
|
||||
return state
|
||||
```
|
||||
|
@ -1632,9 +1615,12 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock)
|
|||
for validator_index in active_validator_indices
|
||||
]
|
||||
|
||||
# Use the rounded-balance-with-hysteresis supplied by the protocol for fork
|
||||
# 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:
|
||||
return sum(
|
||||
get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT
|
||||
start_state.validator_registry[validator_index].high_balance
|
||||
for validator_index, target in attestation_targets
|
||||
if get_ancestor(store, target, block.slot) == block
|
||||
)
|
||||
|
@ -1682,7 +1668,7 @@ def cache_state(state: BeaconState) -> None:
|
|||
state.latest_block_header.state_root = previous_slot_state_root
|
||||
|
||||
# store latest known block for previous slot
|
||||
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state.latest_block_header)
|
||||
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = signed_root(state.latest_block_header)
|
||||
```
|
||||
|
||||
### Per-epoch processing
|
||||
|
@ -1839,7 +1825,7 @@ Run the following function:
|
|||
```python
|
||||
def process_crosslinks(state: BeaconState) -> None:
|
||||
current_epoch = get_current_epoch(state)
|
||||
previous_epoch = current_epoch - 1
|
||||
previous_epoch = max(current_epoch - 1, GENESIS_EPOCH)
|
||||
next_epoch = current_epoch + 1
|
||||
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
|
||||
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
||||
|
@ -1848,7 +1834,7 @@ def process_crosslinks(state: BeaconState) -> None:
|
|||
total_balance = get_total_balance(state, crosslink_committee)
|
||||
if 3 * participating_balance >= 2 * total_balance:
|
||||
state.latest_crosslinks[shard] = Crosslink(
|
||||
epoch=slot_to_epoch(slot),
|
||||
epoch=min(slot_to_epoch(slot), state.latest_crosslinks[shard].epoch + MAX_CROSSLINK_EPOCHS),
|
||||
crosslink_data_root=winning_root
|
||||
)
|
||||
```
|
||||
|
@ -1951,12 +1937,8 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
|
|||
|
||||
```python
|
||||
def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||
# deltas[0] for rewards
|
||||
# deltas[1] for penalties
|
||||
deltas = [
|
||||
[0 for index in range(len(state.validator_registry))],
|
||||
[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))]
|
||||
previous_epoch_start_slot = get_epoch_start_slot(get_previous_epoch(state))
|
||||
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
|
||||
for slot in range(previous_epoch_start_slot, current_epoch_start_slot):
|
||||
|
@ -1966,10 +1948,10 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
|||
total_balance = get_total_balance(state, crosslink_committee)
|
||||
for index in crosslink_committee:
|
||||
if index in participants:
|
||||
deltas[0][index] += get_base_reward(state, index) * participating_balance // total_balance
|
||||
rewards[index] += get_base_reward(state, index) * participating_balance // total_balance
|
||||
else:
|
||||
deltas[1][index] += get_base_reward(state, index)
|
||||
return deltas
|
||||
penalties[index] += get_base_reward(state, index)
|
||||
return [rewards, penalties]
|
||||
```
|
||||
|
||||
#### Apply rewards
|
||||
|
@ -1978,12 +1960,16 @@ Run the following:
|
|||
|
||||
```python
|
||||
def apply_rewards(state: BeaconState) -> None:
|
||||
deltas1 = get_justification_and_finalization_deltas(state)
|
||||
deltas2 = get_crosslink_deltas(state)
|
||||
rewards1, penalties1 = get_justification_and_finalization_deltas(state)
|
||||
rewards2, penalties2 = get_crosslink_deltas(state)
|
||||
for i in range(len(state.validator_registry)):
|
||||
state.validator_balances[i] = max(
|
||||
0,
|
||||
state.validator_balances[i] + deltas1[0][i] + deltas2[0][i] - deltas1[1][i] - deltas2[1][i]
|
||||
set_balance(
|
||||
state,
|
||||
i,
|
||||
max(
|
||||
0,
|
||||
get_balance(state, i) + rewards1[i] + rewards2[i] - penalties1[i] - penalties2[i],
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -1998,28 +1984,12 @@ def process_ejections(state: BeaconState) -> None:
|
|||
and eject active validators with balance below ``EJECTION_BALANCE``.
|
||||
"""
|
||||
for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)):
|
||||
if state.validator_balances[index] < EJECTION_BALANCE:
|
||||
if get_balance(state, index) < EJECTION_BALANCE:
|
||||
initiate_validator_exit(state, index)
|
||||
```
|
||||
|
||||
#### Validator registry and shuffling seed data
|
||||
|
||||
```python
|
||||
def should_update_validator_registry(state: BeaconState) -> bool:
|
||||
# Must have finalized a new block
|
||||
if state.finalized_epoch <= state.validator_registry_update_epoch:
|
||||
return False
|
||||
# Must have processed new crosslinks on all shards of the current epoch
|
||||
shards_to_check = [
|
||||
(state.current_shuffling_start_shard + i) % SHARD_COUNT
|
||||
for i in range(get_current_epoch_committee_count(state))
|
||||
]
|
||||
for shard in shards_to_check:
|
||||
if state.latest_crosslinks[shard].epoch <= state.validator_registry_update_epoch:
|
||||
return False
|
||||
return True
|
||||
```
|
||||
|
||||
```python
|
||||
def update_validator_registry(state: BeaconState) -> None:
|
||||
"""
|
||||
|
@ -2041,7 +2011,7 @@ def update_validator_registry(state: BeaconState) -> None:
|
|||
# Activate validators within the allowable balance churn
|
||||
balance_churn = 0
|
||||
for index, validator in enumerate(state.validator_registry):
|
||||
if validator.activation_epoch == FAR_FUTURE_EPOCH and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT:
|
||||
if validator.activation_epoch == FAR_FUTURE_EPOCH and get_balance(state, index) >= MAX_DEPOSIT_AMOUNT:
|
||||
# Check the balance churn would be within the allowance
|
||||
balance_churn += get_effective_balance(state, index)
|
||||
if balance_churn > max_balance_churn:
|
||||
|
@ -2073,30 +2043,14 @@ def update_validator_registry(state: BeaconState) -> None:
|
|||
Run the following function:
|
||||
|
||||
```python
|
||||
def update_registry_and_shuffling_data(state: BeaconState) -> None:
|
||||
# First set previous shuffling data to current shuffling data
|
||||
state.previous_shuffling_epoch = state.current_shuffling_epoch
|
||||
state.previous_shuffling_start_shard = state.current_shuffling_start_shard
|
||||
state.previous_shuffling_seed = state.current_shuffling_seed
|
||||
current_epoch = get_current_epoch(state)
|
||||
next_epoch = current_epoch + 1
|
||||
def update_registry(state: BeaconState) -> None:
|
||||
# Check if we should update, and if so, update
|
||||
if should_update_validator_registry(state):
|
||||
if state.finalized_epoch > state.validator_registry_update_epoch:
|
||||
update_validator_registry(state)
|
||||
# If we update the registry, update the shuffling data and shards as well
|
||||
state.current_shuffling_epoch = next_epoch
|
||||
state.current_shuffling_start_shard = (
|
||||
state.current_shuffling_start_shard +
|
||||
get_current_epoch_committee_count(state) % SHARD_COUNT
|
||||
)
|
||||
state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)
|
||||
else:
|
||||
# If processing at least one crosslink keeps failing, then reshuffle every power of two,
|
||||
# but don't update the current_shuffling_start_shard
|
||||
epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
|
||||
if epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update):
|
||||
state.current_shuffling_epoch = next_epoch
|
||||
state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)
|
||||
state.latest_start_shard = (
|
||||
state.latest_start_shard +
|
||||
get_current_epoch_committee_count(state)
|
||||
) % SHARD_COUNT
|
||||
```
|
||||
|
||||
**Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch.
|
||||
|
@ -2126,7 +2080,7 @@ def process_slashings(state: BeaconState) -> None:
|
|||
get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance,
|
||||
get_effective_balance(state, index) // MIN_PENALTY_QUOTIENT
|
||||
)
|
||||
state.validator_balances[index] -= penalty
|
||||
decrease_balance(state, index, penalty)
|
||||
```
|
||||
|
||||
```python
|
||||
|
@ -2204,7 +2158,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
|||
# Verify that the slots match
|
||||
assert block.slot == state.slot
|
||||
# Verify that the parent matches
|
||||
assert block.previous_block_root == hash_tree_root(state.latest_block_header)
|
||||
assert block.previous_block_root == signed_root(state.latest_block_header)
|
||||
# Save current block as the new latest block
|
||||
state.latest_block_header = get_temporary_block_header(block)
|
||||
# Verify proposer is not slashed
|
||||
|
@ -2271,8 +2225,8 @@ def process_proposer_slashing(state: BeaconState,
|
|||
assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot)
|
||||
# But the headers are different
|
||||
assert proposer_slashing.header_1 != proposer_slashing.header_2
|
||||
# Proposer is not yet slashed
|
||||
assert proposer.slashed is False
|
||||
# Check proposer is slashable
|
||||
assert is_slashable_validator(proposer, get_current_epoch(state))
|
||||
# Signatures are valid
|
||||
for header in (proposer_slashing.header_1, proposer_slashing.header_2):
|
||||
assert bls_verify(
|
||||
|
@ -2311,7 +2265,7 @@ def process_attester_slashing(state: BeaconState,
|
|||
index for index in attestation1.validator_indices
|
||||
if (
|
||||
index in attestation2.validator_indices and
|
||||
state.validator_registry[index].slashed is False
|
||||
is_slashable_validator(state.validator_registry[index], get_current_epoch(state))
|
||||
)
|
||||
]
|
||||
assert len(slashable_indices) >= 1
|
||||
|
@ -2347,7 +2301,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
attestation.data.previous_crosslink, # Case 1: latest crosslink matches previous crosslink
|
||||
Crosslink( # Case 2: latest crosslink matches current crosslink
|
||||
crosslink_data_root=attestation.data.crosslink_data_root,
|
||||
epoch=target_epoch,
|
||||
epoch=min(slot_to_epoch(attestation.data.slot),
|
||||
attestation.data.previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS)
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -2432,12 +2387,12 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
|||
Note that this function mutates ``state``.
|
||||
"""
|
||||
# Verify the amount and fee aren't individually too big (for anti-overflow purposes)
|
||||
assert state.validator_balances[transfer.sender] >= max(transfer.amount, transfer.fee)
|
||||
assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee)
|
||||
# Verify that we have enough ETH to send, and that after the transfer the balance will be either
|
||||
# exactly zero or at least MIN_DEPOSIT_AMOUNT
|
||||
assert (
|
||||
state.validator_balances[transfer.sender] == transfer.amount + transfer.fee or
|
||||
state.validator_balances[transfer.sender] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
|
||||
get_balance(state, transfer.sender) == transfer.amount + transfer.fee or
|
||||
get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
|
||||
)
|
||||
# A transfer is valid in only one slot
|
||||
assert state.slot == transfer.slot
|
||||
|
@ -2459,9 +2414,9 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
|||
domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)
|
||||
)
|
||||
# Process the transfer
|
||||
state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee
|
||||
state.validator_balances[transfer.recipient] += transfer.amount
|
||||
state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee
|
||||
decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
|
||||
increase_balance(state, transfer.recipient, transfer.amount)
|
||||
increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee)
|
||||
```
|
||||
|
||||
#### State root verification
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
# Beacon chain light client syncing
|
||||
# Beacon Chain Light Client Syncing
|
||||
|
||||
__NOTICE__: This document is a work-in-progress for researchers and implementers. One of the design goals of the eth2 beacon chain is light-client friendlines, both to allow low-resource clients (mobile phones, IoT, etc) to maintain access to the blockchain in a reasonably safe way, but also to facilitate the development of "bridges" between the eth2 beacon chain and other chains.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- TOC -->
|
||||
- [Beacon Chain Light Client Syncing](#beacon-chain-light-client-syncing)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Light client state](#light-client-state)
|
||||
- [Updating the shuffled committee](#updating-the-shuffled-committee)
|
||||
- [Computing the current committee](#computing-the-current-committee)
|
||||
- [Verifying blocks](#verifying-blocks)
|
||||
<!-- /TOC -->
|
||||
|
||||
One of the design goals of the eth2 beacon chain is light-client friendlines, both to allow low-resource clients (mobile phones, IoT, etc) to maintain access to the blockchain in a reasonably safe way, but also to facilitate the development of "bridges" between the eth2 beacon chain and other chains.
|
||||
|
||||
### Preliminaries
|
||||
|
||||
|
@ -40,8 +52,8 @@ def get_later_start_epoch(slot: Slot) -> int:
|
|||
return slot - slot % PERSISTENT_COMMITTEE_PERIOD - PERSISTENT_COMMITTEE_PERIOD
|
||||
|
||||
def get_earlier_period_data(block: ExtendedBeaconBlock, shard_id: Shard) -> PeriodData:
|
||||
period_start = get_earlier_start_epoch(header.slot)
|
||||
validator_count = len(get_active_validator_indices(state, period_start))
|
||||
period_start = get_earlier_start_epoch(block.slot)
|
||||
validator_count = len(get_active_validator_indices(block.state, period_start))
|
||||
committee_count = validator_count // (SHARD_COUNT * TARGET_COMMITTEE_SIZE) + 1
|
||||
indices = get_shuffled_committee(block.state, shard_id, period_start, 0, committee_count)
|
||||
return PeriodData(
|
||||
|
@ -51,8 +63,8 @@ def get_earlier_period_data(block: ExtendedBeaconBlock, shard_id: Shard) -> Peri
|
|||
)
|
||||
|
||||
def get_later_period_data(block: ExtendedBeaconBlock, shard_id: Shard) -> PeriodData:
|
||||
period_start = get_later_start_epoch(header.slot)
|
||||
validator_count = len(get_active_validator_indices(state, period_start))
|
||||
period_start = get_later_start_epoch(block.slot)
|
||||
validator_count = len(get_active_validator_indices(block.state, period_start))
|
||||
committee_count = validator_count // (SHARD_COUNT * TARGET_COMMITTEE_SIZE) + 1
|
||||
indices = get_shuffled_committee(block.state, shard_id, period_start, 0, committee_count)
|
||||
return PeriodData(
|
||||
|
|
|
@ -331,22 +331,19 @@ signed_attestation_data = bls_sign(
|
|||
|
||||
## Validator assignments
|
||||
|
||||
A validator can get the current and previous epoch committee assignments using the following helper via `get_committee_assignment(state, epoch, validator_index)` where `previous_epoch <= epoch <= current_epoch`.
|
||||
A validator can get the current, previous, and next epoch committee assignments using the following helper via `get_committee_assignment(state, epoch, validator_index)` where `previous_epoch <= epoch <= next_epoch`.
|
||||
|
||||
```python
|
||||
def get_committee_assignment(
|
||||
state: BeaconState,
|
||||
epoch: Epoch,
|
||||
validator_index: ValidatorIndex,
|
||||
registry_change: bool=False) -> Tuple[List[ValidatorIndex], Shard, Slot, bool]:
|
||||
validator_index: ValidatorIndex) -> Tuple[List[ValidatorIndex], Shard, Slot]:
|
||||
"""
|
||||
Return the committee assignment in the ``epoch`` for ``validator_index`` and ``registry_change``.
|
||||
Return the committee assignment in the ``epoch`` for ``validator_index``.
|
||||
``assignment`` returned is a tuple of the following form:
|
||||
* ``assignment[0]`` is the list of validators in the committee
|
||||
* ``assignment[1]`` is the shard to which the committee is assigned
|
||||
* ``assignment[2]`` is the slot at which the committee is assigned
|
||||
* ``assignment[3]`` is a bool signalling if the validator is expected to propose
|
||||
a beacon block at the assigned slot.
|
||||
"""
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
next_epoch = get_current_epoch(state) + 1
|
||||
|
@ -357,7 +354,6 @@ def get_committee_assignment(
|
|||
crosslink_committees = get_crosslink_committees_at_slot(
|
||||
state,
|
||||
slot,
|
||||
registry_change=registry_change,
|
||||
)
|
||||
selected_committees = [
|
||||
committee # Tuple[List[ValidatorIndex], Shard]
|
||||
|
@ -367,28 +363,33 @@ def get_committee_assignment(
|
|||
if len(selected_committees) > 0:
|
||||
validators = selected_committees[0][0]
|
||||
shard = selected_committees[0][1]
|
||||
is_proposer = validator_index == get_beacon_proposer_index(state, slot, registry_change=registry_change)
|
||||
|
||||
assignment = (validators, shard, slot, is_proposer)
|
||||
assignment = (validators, shard, slot)
|
||||
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 epoch of the slot in question and can not reliably be used to predict an epoch in advance.
|
||||
|
||||
```python
|
||||
def is_proposer_at_slot(state: BeaconState,
|
||||
slot: Slot,
|
||||
validator_index: ValidatorIndex) -> bool:
|
||||
current_epoch = get_current_epoch(state)
|
||||
assert slot_to_epoch(slot) == current_epoch
|
||||
|
||||
return get_beacon_proposer_index(state, slot) == validator_index
|
||||
```
|
||||
|
||||
_Note_: If a validator is assigned to the 0th slot of an epoch, the validator must run an empty slot transition from the previous epoch into the 0th slot of the epoch to be able to check if they are a proposer at that slot.
|
||||
|
||||
|
||||
### Lookahead
|
||||
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming assignments of proposing and attesting dictated by the shuffling and slot.
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing which must checked during the epoch in question.
|
||||
|
||||
There are three possibilities for the shuffling at the next epoch:
|
||||
1. The shuffling changes due to a "validator registry change".
|
||||
2. The shuffling changes due to `epochs_since_last_registry_update` being an exact power of 2 greater than 1.
|
||||
3. The shuffling remains the same (i.e. the validator is in the same shard committee).
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments which involves noting at which future slot one will have to attest and also which shard one should begin syncing (in phase 1+).
|
||||
|
||||
Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is deterministic based upon `epochs_since_last_registry_update`.
|
||||
|
||||
When querying for assignments in the next epoch there are two options -- with and without a `registry_change` -- which is the optional fourth parameter of the `get_committee_assignment`.
|
||||
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should always plan for assignments from both values of `registry_change` unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+).
|
||||
|
||||
Specifically, a validator should call both `get_committee_assignment(state, next_epoch, validator_index, registry_change=True)` and `get_committee_assignment(state, next_epoch, validator_index, registry_change=False)` when checking for next epoch assignments.
|
||||
Specifically, a validator should call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
|
||||
|
||||
## How to avoid slashing
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
from copy import deepcopy
|
||||
import pytest
|
||||
|
||||
import build.phase0.spec as spec
|
||||
|
||||
from build.phase0.state_transition import (
|
||||
state_transition,
|
||||
)
|
||||
from build.phase0.spec import (
|
||||
ZERO_HASH,
|
||||
get_current_epoch,
|
||||
process_attestation,
|
||||
slot_to_epoch,
|
||||
)
|
||||
from tests.phase0.helpers import (
|
||||
build_empty_block_for_next_slot,
|
||||
get_valid_attestation,
|
||||
)
|
||||
|
||||
|
||||
# mark entire file as 'attestations'
|
||||
pytestmark = pytest.mark.attestations
|
||||
|
||||
|
||||
def run_attestation_processing(state, attestation, valid=True):
|
||||
"""
|
||||
Run ``process_attestation`` returning the pre and post state.
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
post_state = deepcopy(state)
|
||||
|
||||
if not valid:
|
||||
with pytest.raises(AssertionError):
|
||||
process_attestation(post_state, attestation)
|
||||
return state, None
|
||||
|
||||
process_attestation(post_state, attestation)
|
||||
|
||||
current_epoch = get_current_epoch(state)
|
||||
target_epoch = slot_to_epoch(attestation.data.slot)
|
||||
if target_epoch == current_epoch:
|
||||
assert len(post_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
|
||||
else:
|
||||
assert len(post_state.previous_epoch_attestations) == len(state.previous_epoch_attestations) + 1
|
||||
|
||||
return state, post_state
|
||||
|
||||
|
||||
def test_success(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_success_prevous_epoch(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
block = build_empty_block_for_next_slot(state)
|
||||
block.slot = state.slot + spec.SLOTS_PER_EPOCH
|
||||
state_transition(state, block)
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_before_inclusion_delay(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
# do not increment slot to allow for inclusion delay
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_after_epoch_slots(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
block = build_empty_block_for_next_slot(state)
|
||||
# increment past latest inclusion slot
|
||||
block.slot = state.slot + spec.SLOTS_PER_EPOCH + 1
|
||||
state_transition(state, block)
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_bad_source_epoch(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.source_epoch += 10
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_bad_source_root(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.source_root = b'\x42'*32
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_non_zero_crosslink_data_root(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.crosslink_data_root = b'\x42'*32
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_bad_previous_crosslink(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
state.latest_crosslinks[attestation.data.shard].epoch += 10
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_non_empty_custody_bitfield(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.custody_bitfield = b'\x01' + attestation.custody_bitfield[1:]
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
||||
|
||||
|
||||
def test_empty_aggregation_bitfield(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield)
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
return pre_state, attestation, post_state
|
|
@ -4,11 +4,14 @@ import pytest
|
|||
import build.phase0.spec as spec
|
||||
|
||||
from build.phase0.spec import (
|
||||
Deposit,
|
||||
get_balance,
|
||||
ZERO_HASH,
|
||||
process_deposit,
|
||||
)
|
||||
from tests.phase0.helpers import (
|
||||
build_deposit,
|
||||
privkeys,
|
||||
pubkeys,
|
||||
)
|
||||
|
||||
|
||||
|
@ -16,8 +19,10 @@ from tests.phase0.helpers import (
|
|||
pytestmark = pytest.mark.voluntary_exits
|
||||
|
||||
|
||||
def test_success(state, deposit_data_leaves, pubkeys, privkeys):
|
||||
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]
|
||||
|
@ -38,15 +43,17 @@ def test_success(state, deposit_data_leaves, pubkeys, privkeys):
|
|||
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 len(post_state.balances) == len(state.balances) + 1
|
||||
assert post_state.validator_registry[index].pubkey == pubkeys[index]
|
||||
assert get_balance(post_state, index) == spec.MAX_DEPOSIT_AMOUNT
|
||||
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
|
||||
|
||||
return pre_state, deposit, post_state
|
||||
|
||||
|
||||
def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys):
|
||||
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
|
||||
|
@ -62,22 +69,23 @@ def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys):
|
|||
|
||||
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]
|
||||
pre_balance = get_balance(pre_state, 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 len(post_state.balances) == len(state.balances)
|
||||
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
|
||||
assert post_state.validator_balances[validator_index] == pre_balance + amount
|
||||
assert get_balance(post_state, validator_index) == pre_balance + amount
|
||||
|
||||
return pre_state, deposit, post_state
|
||||
|
||||
|
||||
def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys):
|
||||
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]
|
||||
|
@ -104,8 +112,9 @@ def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys):
|
|||
return pre_state, deposit, None
|
||||
|
||||
|
||||
def test_bad_merkle_proof(state, deposit_data_leaves, pubkeys, privkeys):
|
||||
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]
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
from copy import deepcopy
|
||||
import pytest
|
||||
|
||||
import build.phase0.spec as spec
|
||||
from build.phase0.spec import (
|
||||
get_balance,
|
||||
get_current_epoch,
|
||||
process_proposer_slashing,
|
||||
)
|
||||
from tests.phase0.helpers import (
|
||||
get_valid_proposer_slashing,
|
||||
)
|
||||
|
||||
# mark entire file as 'header'
|
||||
pytestmark = pytest.mark.proposer_slashings
|
||||
|
||||
|
||||
def run_proposer_slashing_processing(state, proposer_slashing, valid=True):
|
||||
"""
|
||||
Run ``process_proposer_slashing`` returning the pre and post state.
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
post_state = deepcopy(state)
|
||||
|
||||
if not valid:
|
||||
with pytest.raises(AssertionError):
|
||||
process_proposer_slashing(post_state, proposer_slashing)
|
||||
return state, None
|
||||
|
||||
process_proposer_slashing(post_state, proposer_slashing)
|
||||
|
||||
slashed_validator = post_state.validator_registry[proposer_slashing.proposer_index]
|
||||
assert not slashed_validator.initiated_exit
|
||||
assert slashed_validator.slashed
|
||||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
# lost whistleblower reward
|
||||
assert (
|
||||
get_balance(post_state, proposer_slashing.proposer_index) <
|
||||
get_balance(state, proposer_slashing.proposer_index)
|
||||
)
|
||||
|
||||
return state, post_state
|
||||
|
||||
|
||||
def test_success(state):
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
|
||||
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing)
|
||||
|
||||
return pre_state, proposer_slashing, post_state
|
||||
|
||||
|
||||
def test_epochs_are_different(state):
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
|
||||
# set slots to be in different epochs
|
||||
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
|
||||
|
||||
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
|
||||
|
||||
return pre_state, proposer_slashing, post_state
|
||||
|
||||
|
||||
def test_headers_are_same(state):
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
|
||||
# set headers to be the same
|
||||
proposer_slashing.header_2 = proposer_slashing.header_1
|
||||
|
||||
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
|
||||
|
||||
return pre_state, proposer_slashing, post_state
|
||||
|
||||
|
||||
def test_proposer_is_slashed(state):
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
|
||||
# set proposer to slashed
|
||||
state.validator_registry[proposer_slashing.proposer_index].slashed = True
|
||||
|
||||
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
|
||||
|
||||
return pre_state, proposer_slashing, post_state
|
||||
|
||||
|
||||
def test_proposer_is_withdrawn(state):
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
|
||||
# set proposer withdrawable_epoch in past
|
||||
current_epoch = get_current_epoch(state)
|
||||
proposer_index = proposer_slashing.proposer_index
|
||||
state.validator_registry[proposer_index].withdrawable_epoch = current_epoch - 1
|
||||
|
||||
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
|
||||
|
||||
return pre_state, proposer_slashing, post_state
|
|
@ -10,6 +10,7 @@ from build.phase0.spec import (
|
|||
)
|
||||
from tests.phase0.helpers import (
|
||||
build_voluntary_exit,
|
||||
pubkey_to_privkey,
|
||||
)
|
||||
|
||||
|
||||
|
@ -17,7 +18,7 @@ from tests.phase0.helpers import (
|
|||
pytestmark = pytest.mark.voluntary_exits
|
||||
|
||||
|
||||
def test_success(state, pub_to_priv):
|
||||
def test_success(state):
|
||||
pre_state = deepcopy(state)
|
||||
#
|
||||
# setup pre_state
|
||||
|
@ -30,7 +31,7 @@ def test_success(state, pub_to_priv):
|
|||
#
|
||||
current_epoch = get_current_epoch(pre_state)
|
||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
||||
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
|
||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
pre_state,
|
||||
|
@ -52,11 +53,11 @@ def test_success(state, pub_to_priv):
|
|||
return pre_state, voluntary_exit, post_state
|
||||
|
||||
|
||||
def test_validator_not_active(state, pub_to_priv):
|
||||
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 = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
|
||||
privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey]
|
||||
|
||||
#
|
||||
# setup pre_state
|
||||
|
@ -79,7 +80,7 @@ def test_validator_not_active(state, pub_to_priv):
|
|||
return pre_state, voluntary_exit, None
|
||||
|
||||
|
||||
def test_validator_already_exited(state, pub_to_priv):
|
||||
def test_validator_already_exited(state):
|
||||
pre_state = deepcopy(state)
|
||||
#
|
||||
# setup pre_state
|
||||
|
@ -89,7 +90,7 @@ def test_validator_already_exited(state, pub_to_priv):
|
|||
|
||||
current_epoch = get_current_epoch(pre_state)
|
||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
||||
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
|
||||
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
|
||||
|
@ -110,7 +111,7 @@ def test_validator_already_exited(state, pub_to_priv):
|
|||
return pre_state, voluntary_exit, None
|
||||
|
||||
|
||||
def test_validator_already_initiated_exit(state, pub_to_priv):
|
||||
def test_validator_already_initiated_exit(state):
|
||||
pre_state = deepcopy(state)
|
||||
#
|
||||
# setup pre_state
|
||||
|
@ -120,7 +121,7 @@ def test_validator_already_initiated_exit(state, pub_to_priv):
|
|||
|
||||
current_epoch = get_current_epoch(pre_state)
|
||||
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
||||
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
|
||||
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
|
||||
|
@ -141,14 +142,14 @@ def test_validator_already_initiated_exit(state, pub_to_priv):
|
|||
return pre_state, voluntary_exit, None
|
||||
|
||||
|
||||
def test_validator_not_active_long_enough(state, pub_to_priv):
|
||||
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 = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
|
||||
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
|
||||
|
|
|
@ -3,9 +3,6 @@ import pytest
|
|||
from build.phase0 import spec
|
||||
|
||||
from tests.phase0.helpers import (
|
||||
privkeys_list,
|
||||
pubkeys_list,
|
||||
pubkey_to_privkey,
|
||||
create_genesis_state,
|
||||
)
|
||||
|
||||
|
@ -25,21 +22,6 @@ MINIMAL_CONFIG = {
|
|||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def privkeys():
|
||||
return privkeys_list
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pubkeys():
|
||||
return pubkeys_list
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pub_to_priv():
|
||||
return pubkey_to_privkey
|
||||
|
||||
|
||||
def overwrite_spec_config(config):
|
||||
for field in config:
|
||||
setattr(spec, field, config[field])
|
||||
|
|
|
@ -7,15 +7,22 @@ from build.phase0.utils.minimal_ssz import signed_root
|
|||
from build.phase0.spec import (
|
||||
# constants
|
||||
EMPTY_SIGNATURE,
|
||||
ZERO_HASH,
|
||||
# SSZ
|
||||
Attestation,
|
||||
AttestationData,
|
||||
AttestationDataAndCustodyBit,
|
||||
BeaconBlockHeader,
|
||||
Deposit,
|
||||
DepositInput,
|
||||
DepositData,
|
||||
Eth1Data,
|
||||
ProposerSlashing,
|
||||
VoluntaryExit,
|
||||
# functions
|
||||
get_active_validator_indices,
|
||||
get_attestation_participants,
|
||||
get_block_root,
|
||||
get_crosslink_committees_at_slot,
|
||||
get_current_epoch,
|
||||
get_domain,
|
||||
get_empty_block,
|
||||
|
@ -31,27 +38,25 @@ from build.phase0.utils.merkle_minimal import (
|
|||
)
|
||||
|
||||
|
||||
privkeys_list = [i + 1 for i in range(1000)]
|
||||
pubkeys_list = [bls.privtopub(privkey) for privkey in privkeys_list]
|
||||
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys_list, pubkeys_list)}
|
||||
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):
|
||||
deposit_timestamp = 0
|
||||
def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None):
|
||||
if not deposit_data_leaves:
|
||||
deposit_data_leaves = []
|
||||
proof_of_possession = b'\x33' * 96
|
||||
|
||||
deposit_data_list = []
|
||||
for i in range(num_validators):
|
||||
pubkey = pubkeys_list[i]
|
||||
pubkey = pubkeys[i]
|
||||
deposit_data = DepositData(
|
||||
pubkey=pubkey,
|
||||
# insecurely use pubkey as withdrawal key as well
|
||||
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
|
||||
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,
|
||||
),
|
||||
proof_of_possession=proof_of_possession,
|
||||
)
|
||||
item = hash(deposit_data.serialize())
|
||||
deposit_data_leaves.append(item)
|
||||
|
@ -66,12 +71,12 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves):
|
|||
genesis_validator_deposits.append(Deposit(
|
||||
proof=list(get_merkle_proof(tree, item_index=i)),
|
||||
index=i,
|
||||
deposit_data=deposit_data_list[i]
|
||||
data=deposit_data_list[i]
|
||||
))
|
||||
return genesis_validator_deposits, root
|
||||
|
||||
|
||||
def create_genesis_state(num_validators, deposit_data_leaves):
|
||||
def create_genesis_state(num_validators, deposit_data_leaves=None):
|
||||
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(
|
||||
num_validators,
|
||||
deposit_data_leaves,
|
||||
|
@ -101,19 +106,20 @@ def build_empty_block_for_next_slot(state):
|
|||
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 = previous_block_header.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(
|
||||
deposit_data = DepositData(
|
||||
pubkey=pubkey,
|
||||
# insecurely use pubkey as withdrawal key as well
|
||||
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
|
||||
amount=amount,
|
||||
proof_of_possession=EMPTY_SIGNATURE,
|
||||
)
|
||||
proof_of_possession = bls.sign(
|
||||
message_hash=signed_root(deposit_input),
|
||||
message_hash=signed_root(deposit_data),
|
||||
privkey=privkey,
|
||||
domain=get_domain(
|
||||
state.fork,
|
||||
|
@ -121,12 +127,7 @@ def build_deposit_data(state, pubkey, privkey, amount):
|
|||
spec.DOMAIN_DEPOSIT,
|
||||
)
|
||||
)
|
||||
deposit_input.proof_of_possession = proof_of_possession
|
||||
deposit_data = DepositData(
|
||||
amount=amount,
|
||||
timestamp=0,
|
||||
deposit_input=deposit_input,
|
||||
)
|
||||
deposit_data.proof_of_possession = proof_of_possession
|
||||
return deposit_data
|
||||
|
||||
|
||||
|
@ -195,7 +196,93 @@ def build_deposit(state,
|
|||
deposit = Deposit(
|
||||
proof=list(proof),
|
||||
index=index,
|
||||
deposit_data=deposit_data,
|
||||
data=deposit_data,
|
||||
)
|
||||
|
||||
return deposit, root, deposit_data_leaves
|
||||
|
||||
|
||||
def get_valid_proposer_slashing(state):
|
||||
current_epoch = get_current_epoch(state)
|
||||
validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[-1]
|
||||
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
|
||||
slot = state.slot
|
||||
|
||||
header_1 = BeaconBlockHeader(
|
||||
slot=slot,
|
||||
previous_block_root=ZERO_HASH,
|
||||
state_root=ZERO_HASH,
|
||||
block_body_root=ZERO_HASH,
|
||||
signature=EMPTY_SIGNATURE,
|
||||
)
|
||||
header_2 = deepcopy(header_1)
|
||||
header_2.previous_block_root = b'\x02' * 32
|
||||
header_2.slot = slot + 1
|
||||
|
||||
domain = get_domain(
|
||||
fork=state.fork,
|
||||
epoch=get_current_epoch(state),
|
||||
domain_type=spec.DOMAIN_BEACON_BLOCK,
|
||||
)
|
||||
header_1.signature = bls.sign(
|
||||
message_hash=signed_root(header_1),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
header_2.signature = bls.sign(
|
||||
message_hash=signed_root(header_2),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
|
||||
return ProposerSlashing(
|
||||
proposer_index=validator_index,
|
||||
header_1=header_1,
|
||||
header_2=header_2,
|
||||
)
|
||||
|
||||
|
||||
def get_valid_attestation(state, slot=None):
|
||||
if slot is None:
|
||||
slot = state.slot
|
||||
shard = state.latest_start_shard
|
||||
attestation_data = build_attestation_data(state, slot, shard)
|
||||
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, slot)
|
||||
crosslink_committee = [committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard][0]
|
||||
|
||||
committee_size = len(crosslink_committee)
|
||||
bitfield_length = (committee_size + 7) // 8
|
||||
aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1)
|
||||
custody_bitfield = b'\x00' * bitfield_length
|
||||
attestation = Attestation(
|
||||
aggregation_bitfield=aggregation_bitfield,
|
||||
data=attestation_data,
|
||||
custody_bitfield=custody_bitfield,
|
||||
aggregate_signature=EMPTY_SIGNATURE,
|
||||
)
|
||||
participants = get_attestation_participants(
|
||||
state,
|
||||
attestation.data,
|
||||
attestation.aggregation_bitfield,
|
||||
)
|
||||
assert len(participants) == 1
|
||||
|
||||
validator_index = participants[0]
|
||||
privkey = privkeys[validator_index]
|
||||
|
||||
message_hash = AttestationDataAndCustodyBit(
|
||||
data=attestation.data,
|
||||
custody_bit=0b0,
|
||||
).hash_tree_root()
|
||||
|
||||
attestation.aggregation_signature = bls.sign(
|
||||
message_hash=message_hash,
|
||||
privkey=privkey,
|
||||
domain=get_domain(
|
||||
fork=state.fork,
|
||||
epoch=get_current_epoch(state),
|
||||
domain_type=spec.DOMAIN_ATTESTATION,
|
||||
)
|
||||
)
|
||||
return attestation
|
||||
|
|
|
@ -11,23 +11,19 @@ from build.phase0.spec import (
|
|||
EMPTY_SIGNATURE,
|
||||
ZERO_HASH,
|
||||
# SSZ
|
||||
Attestation,
|
||||
AttestationDataAndCustodyBit,
|
||||
BeaconBlockHeader,
|
||||
Deposit,
|
||||
Transfer,
|
||||
ProposerSlashing,
|
||||
VoluntaryExit,
|
||||
# functions
|
||||
get_active_validator_indices,
|
||||
get_attestation_participants,
|
||||
get_balance,
|
||||
get_block_root,
|
||||
get_crosslink_committees_at_slot,
|
||||
get_current_epoch,
|
||||
get_domain,
|
||||
get_state_root,
|
||||
advance_slot,
|
||||
cache_state,
|
||||
set_balance,
|
||||
verify_merkle_branch,
|
||||
hash,
|
||||
)
|
||||
|
@ -40,10 +36,13 @@ from build.phase0.utils.merkle_minimal import (
|
|||
get_merkle_root,
|
||||
)
|
||||
from tests.phase0.helpers import (
|
||||
build_attestation_data,
|
||||
build_deposit_data,
|
||||
build_empty_block_for_next_slot,
|
||||
force_registry_change_at_next_epoch,
|
||||
get_valid_attestation,
|
||||
get_valid_proposer_slashing,
|
||||
privkeys,
|
||||
pubkeys,
|
||||
)
|
||||
|
||||
|
||||
|
@ -113,44 +112,10 @@ def test_empty_epoch_transition_not_finalizing(state):
|
|||
return state, [block], test_state
|
||||
|
||||
|
||||
def test_proposer_slashing(state, pubkeys, privkeys):
|
||||
def test_proposer_slashing(state):
|
||||
test_state = deepcopy(state)
|
||||
current_epoch = get_current_epoch(test_state)
|
||||
validator_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1]
|
||||
privkey = privkeys[validator_index]
|
||||
slot = spec.GENESIS_SLOT
|
||||
header_1 = BeaconBlockHeader(
|
||||
slot=slot,
|
||||
previous_block_root=ZERO_HASH,
|
||||
state_root=ZERO_HASH,
|
||||
block_body_root=ZERO_HASH,
|
||||
signature=EMPTY_SIGNATURE,
|
||||
)
|
||||
header_2 = deepcopy(header_1)
|
||||
header_2.previous_block_root = b'\x02' * 32
|
||||
header_2.slot = slot + 1
|
||||
|
||||
domain = get_domain(
|
||||
fork=test_state.fork,
|
||||
epoch=get_current_epoch(test_state),
|
||||
domain_type=spec.DOMAIN_BEACON_BLOCK,
|
||||
)
|
||||
header_1.signature = bls.sign(
|
||||
message_hash=signed_root(header_1),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
header_2.signature = bls.sign(
|
||||
message_hash=signed_root(header_2),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
|
||||
proposer_slashing = ProposerSlashing(
|
||||
proposer_index=validator_index,
|
||||
header_1=header_1,
|
||||
header_2=header_2,
|
||||
)
|
||||
proposer_slashing = get_valid_proposer_slashing(state)
|
||||
validator_index = proposer_slashing.proposer_index
|
||||
|
||||
#
|
||||
# Add to state via block transition
|
||||
|
@ -168,14 +133,14 @@ def test_proposer_slashing(state, pubkeys, privkeys):
|
|||
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
# lost whistleblower reward
|
||||
assert test_state.validator_balances[validator_index] < state.validator_balances[validator_index]
|
||||
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)
|
||||
|
||||
return state, [block], test_state
|
||||
|
||||
|
||||
def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys):
|
||||
def test_deposit_in_block(state):
|
||||
pre_state = deepcopy(state)
|
||||
test_deposit_data_leaves = deepcopy(deposit_data_leaves)
|
||||
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
||||
|
||||
index = len(test_deposit_data_leaves)
|
||||
pubkey = pubkeys[index]
|
||||
|
@ -192,7 +157,7 @@ def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys):
|
|||
deposit = Deposit(
|
||||
proof=list(proof),
|
||||
index=index,
|
||||
deposit_data=deposit_data,
|
||||
data=deposit_data,
|
||||
)
|
||||
|
||||
pre_state.latest_eth1_data.deposit_root = root
|
||||
|
@ -203,15 +168,16 @@ def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys):
|
|||
|
||||
state_transition(post_state, block)
|
||||
assert len(post_state.validator_registry) == len(state.validator_registry) + 1
|
||||
assert len(post_state.validator_balances) == len(state.validator_balances) + 1
|
||||
assert len(post_state.balances) == len(state.balances) + 1
|
||||
assert get_balance(post_state, index) == spec.MAX_DEPOSIT_AMOUNT
|
||||
assert post_state.validator_registry[index].pubkey == pubkeys[index]
|
||||
|
||||
return pre_state, [block], post_state
|
||||
|
||||
|
||||
def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves):
|
||||
def test_deposit_top_up(state):
|
||||
pre_state = deepcopy(state)
|
||||
test_deposit_data_leaves = deepcopy(deposit_data_leaves)
|
||||
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
|
||||
|
||||
validator_index = 0
|
||||
amount = spec.MAX_DEPOSIT_AMOUNT // 4
|
||||
|
@ -230,7 +196,7 @@ def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves):
|
|||
deposit = Deposit(
|
||||
proof=list(proof),
|
||||
index=merkle_index,
|
||||
deposit_data=deposit_data,
|
||||
data=deposit_data,
|
||||
)
|
||||
|
||||
pre_state.latest_eth1_data.deposit_root = root
|
||||
|
@ -238,59 +204,19 @@ def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves):
|
|||
block = build_empty_block_for_next_slot(pre_state)
|
||||
block.body.deposits.append(deposit)
|
||||
|
||||
pre_balance = pre_state.validator_balances[validator_index]
|
||||
pre_balance = get_balance(pre_state, validator_index)
|
||||
post_state = deepcopy(pre_state)
|
||||
state_transition(post_state, block)
|
||||
assert len(post_state.validator_registry) == len(pre_state.validator_registry)
|
||||
assert len(post_state.validator_balances) == len(pre_state.validator_balances)
|
||||
assert post_state.validator_balances[validator_index] == pre_balance + amount
|
||||
assert len(post_state.balances) == len(pre_state.balances)
|
||||
assert get_balance(post_state, validator_index) == pre_balance + amount
|
||||
|
||||
return pre_state, [block], post_state
|
||||
|
||||
|
||||
def test_attestation(state, pubkeys, privkeys):
|
||||
def test_attestation(state):
|
||||
test_state = deepcopy(state)
|
||||
slot = state.slot
|
||||
shard = state.current_shuffling_start_shard
|
||||
attestation_data = build_attestation_data(state, slot, shard)
|
||||
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, slot)
|
||||
crosslink_committee = [committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard][0]
|
||||
|
||||
committee_size = len(crosslink_committee)
|
||||
bitfield_length = (committee_size + 7) // 8
|
||||
aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1)
|
||||
custody_bitfield = b'\x00' * bitfield_length
|
||||
attestation = Attestation(
|
||||
aggregation_bitfield=aggregation_bitfield,
|
||||
data=attestation_data,
|
||||
custody_bitfield=custody_bitfield,
|
||||
aggregate_signature=EMPTY_SIGNATURE,
|
||||
)
|
||||
participants = get_attestation_participants(
|
||||
test_state,
|
||||
attestation.data,
|
||||
attestation.aggregation_bitfield,
|
||||
)
|
||||
assert len(participants) == 1
|
||||
|
||||
validator_index = participants[0]
|
||||
privkey = privkeys[validator_index]
|
||||
|
||||
message_hash = AttestationDataAndCustodyBit(
|
||||
data=attestation.data,
|
||||
custody_bit=0b0,
|
||||
).hash_tree_root()
|
||||
|
||||
attestation.aggregation_signature = bls.sign(
|
||||
message_hash=message_hash,
|
||||
privkey=privkey,
|
||||
domain=get_domain(
|
||||
fork=test_state.fork,
|
||||
epoch=get_current_epoch(test_state),
|
||||
domain_type=spec.DOMAIN_ATTESTATION,
|
||||
)
|
||||
)
|
||||
attestation = get_valid_attestation(state)
|
||||
|
||||
#
|
||||
# Add to state via block transition
|
||||
|
@ -317,7 +243,7 @@ def test_attestation(state, pubkeys, privkeys):
|
|||
return state, [attestation_block, epoch_block], test_state
|
||||
|
||||
|
||||
def test_voluntary_exit(state, pubkeys, privkeys):
|
||||
def test_voluntary_exit(state):
|
||||
pre_state = deepcopy(state)
|
||||
validator_index = get_active_validator_indices(
|
||||
pre_state.validator_registry,
|
||||
|
@ -405,15 +331,15 @@ def test_no_exit_churn_too_long_since_change(state):
|
|||
return pre_state, [block], post_state
|
||||
|
||||
|
||||
def test_transfer(state, pubkeys, privkeys):
|
||||
def test_transfer(state):
|
||||
pre_state = deepcopy(state)
|
||||
current_epoch = get_current_epoch(pre_state)
|
||||
sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1]
|
||||
recipient_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
|
||||
transfer_pubkey = pubkeys[-1]
|
||||
transfer_privkey = privkeys[-1]
|
||||
amount = pre_state.validator_balances[sender_index]
|
||||
pre_transfer_recipient_balance = pre_state.validator_balances[recipient_index]
|
||||
amount = get_balance(pre_state, sender_index)
|
||||
pre_transfer_recipient_balance = get_balance(pre_state, recipient_index)
|
||||
transfer = Transfer(
|
||||
sender=sender_index,
|
||||
recipient=recipient_index,
|
||||
|
@ -448,8 +374,8 @@ def test_transfer(state, pubkeys, privkeys):
|
|||
block.body.transfers.append(transfer)
|
||||
state_transition(post_state, block)
|
||||
|
||||
sender_balance = post_state.validator_balances[sender_index]
|
||||
recipient_balance = post_state.validator_balances[recipient_index]
|
||||
sender_balance = get_balance(post_state, sender_index)
|
||||
recipient_balance = get_balance(post_state, recipient_index)
|
||||
assert sender_balance == 0
|
||||
assert recipient_balance == pre_transfer_recipient_balance + amount
|
||||
|
||||
|
@ -465,7 +391,7 @@ def test_ejection(state):
|
|||
assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# set validator balance to below ejection threshold
|
||||
pre_state.validator_balances[validator_index] = spec.EJECTION_BALANCE - 1
|
||||
set_balance(pre_state, validator_index, spec.EJECTION_BALANCE - 1)
|
||||
|
||||
post_state = deepcopy(pre_state)
|
||||
#
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# from hashlib import sha256
|
||||
from eth_utils import keccak
|
||||
from hashlib import sha256
|
||||
# from eth_utils import keccak
|
||||
|
||||
|
||||
# def hash(x): return sha256(x).digest()
|
||||
def hash(x):
|
||||
return keccak(x)
|
||||
def hash(x): return sha256(x).digest()
|
||||
# def hash(x): return keccak(x)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
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")
|
|
@ -95,7 +95,7 @@ def process_epoch_transition(state: BeaconState) -> None:
|
|||
spec.maybe_reset_eth1_period(state)
|
||||
spec.apply_rewards(state)
|
||||
spec.process_ejections(state)
|
||||
spec.update_registry_and_shuffling_data(state)
|
||||
spec.update_registry(state)
|
||||
spec.process_slashings(state)
|
||||
spec.process_exit_queue(state)
|
||||
spec.finish_epoch_update(state)
|
||||
|
|
Loading…
Reference in New Issue