mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-01-19 15:11:05 +00:00
Merge branch 'v09x' into toc_ci
This commit is contained in:
commit
49a8264f5d
@ -82,7 +82,7 @@ def get_eth1_data(distance: uint64) -> Bytes32:
|
||||
return hash(distance)
|
||||
|
||||
|
||||
def hash(x: bytes) -> Bytes32:
|
||||
def hash(x: bytes) -> Bytes32: # type: ignore
|
||||
if x not in hash_cache:
|
||||
hash_cache[x] = Bytes32(_hash(x))
|
||||
return hash_cache[x]
|
||||
|
@ -593,6 +593,34 @@ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
return validator.activation_epoch <= epoch < validator.exit_epoch
|
||||
```
|
||||
|
||||
#### `is_eligible_for_activation_queue`
|
||||
|
||||
```python
|
||||
def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
||||
"""
|
||||
Check if ``validator`` is eligible to be placed into the activation queue.
|
||||
"""
|
||||
return (
|
||||
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
||||
and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||
)
|
||||
```
|
||||
|
||||
#### `is_eligible_for_activation`
|
||||
|
||||
```python
|
||||
def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
|
||||
"""
|
||||
Check if ``validator`` is eligible for activation.
|
||||
"""
|
||||
return (
|
||||
# Placement in queue is finalized
|
||||
validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
|
||||
# Has not yet been activated
|
||||
and validator.activation_epoch == FAR_FUTURE_EPOCH
|
||||
)
|
||||
```
|
||||
|
||||
#### `is_slashable_validator`
|
||||
|
||||
```python
|
||||
@ -630,8 +658,8 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe
|
||||
# Verify max number of indices
|
||||
if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
|
||||
return False
|
||||
# Verify indices are sorted
|
||||
if not indices == sorted(indices):
|
||||
# Verify indices are sorted and unique
|
||||
if not indices == sorted(set(indices)):
|
||||
return False
|
||||
# Verify aggregate signature
|
||||
if not bls_verify(
|
||||
@ -1304,26 +1332,22 @@ def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||
def process_registry_updates(state: BeaconState) -> None:
|
||||
# Process activation eligibility and ejections
|
||||
for index, validator in enumerate(state.validators):
|
||||
if (
|
||||
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
||||
and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||
):
|
||||
validator.activation_eligibility_epoch = get_current_epoch(state)
|
||||
if is_eligible_for_activation_queue(validator):
|
||||
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
|
||||
|
||||
if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
|
||||
initiate_validator_exit(state, ValidatorIndex(index))
|
||||
|
||||
# Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
|
||||
# Queue validators eligible for activation and not yet dequeued for activation
|
||||
activation_queue = sorted([
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH
|
||||
and validator.activation_epoch >= compute_activation_exit_epoch(state.finalized_checkpoint.epoch)
|
||||
], key=lambda index: state.validators[index].activation_eligibility_epoch)
|
||||
# Dequeued validators for activation up to churn limit (without resetting activation epoch)
|
||||
if is_eligible_for_activation(state, validator)
|
||||
# Order by the sequence of activation_eligibility_epoch setting and then index
|
||||
], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
|
||||
# Dequeued validators for activation up to churn limit
|
||||
for index in activation_queue[:get_validator_churn_limit(state)]:
|
||||
validator = state.validators[index]
|
||||
if validator.activation_epoch == FAR_FUTURE_EPOCH:
|
||||
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
||||
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
||||
```
|
||||
|
||||
#### Slashings
|
||||
@ -1484,6 +1508,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
data = attestation.data
|
||||
assert data.index < get_committee_count_at_slot(state, data.slot)
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
||||
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
|
||||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
|
@ -104,11 +104,18 @@ def get_genesis_store(genesis_state: BeaconState) -> Store:
|
||||
)
|
||||
```
|
||||
|
||||
#### `get_slots_since_genesis`
|
||||
|
||||
```python
|
||||
def get_slots_since_genesis(store: Store) -> int:
|
||||
return (store.time - store.genesis_time) // SECONDS_PER_SLOT
|
||||
```
|
||||
|
||||
#### `get_current_slot`
|
||||
|
||||
```python
|
||||
def get_current_slot(store: Store) -> Slot:
|
||||
return Slot((store.time - store.genesis_time) // SECONDS_PER_SLOT)
|
||||
return Slot(GENESIS_SLOT + get_slots_since_genesis(store))
|
||||
```
|
||||
|
||||
#### `compute_slots_since_epoch_start`
|
||||
@ -272,7 +279,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
assert block.parent_root in store.block_states
|
||||
pre_state = store.block_states[block.parent_root].copy()
|
||||
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT
|
||||
assert get_current_slot(store) >= block.slot
|
||||
# Add new block to the store
|
||||
store.blocks[hash_tree_root(block)] = block
|
||||
# Check block is a descendant of the finalized block
|
||||
@ -289,7 +296,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
|
||||
# Update justified checkpoint
|
||||
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
store.best_justified_checkpoint = state.current_justified_checkpoint
|
||||
if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch:
|
||||
store.best_justified_checkpoint = state.current_justified_checkpoint
|
||||
if should_update_justified_checkpoint(store, state.current_justified_checkpoint):
|
||||
store.justified_checkpoint = state.current_justified_checkpoint
|
||||
|
||||
@ -315,12 +323,13 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
|
||||
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
|
||||
assert target.epoch in [current_epoch, previous_epoch]
|
||||
assert target.epoch == compute_epoch_at_slot(attestation.data.slot)
|
||||
|
||||
# Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
|
||||
assert target.root in store.blocks
|
||||
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives
|
||||
base_state = store.block_states[target.root].copy()
|
||||
assert store.time >= base_state.genesis_time + compute_start_slot_at_epoch(target.epoch) * SECONDS_PER_SLOT
|
||||
assert get_current_slot(store) >= compute_start_slot_at_epoch(target.epoch)
|
||||
|
||||
# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
|
||||
assert attestation.data.beacon_block_root in store.blocks
|
||||
@ -335,7 +344,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
|
||||
# Attestations can only affect the fork choice of subsequent slots.
|
||||
# Delay consideration in the fork choice until their slot is in the past.
|
||||
assert store.time >= (attestation.data.slot + 1) * SECONDS_PER_SLOT
|
||||
assert get_current_slot(store) >= attestation.data.slot + 1
|
||||
|
||||
# Get state at the `target` to validate attestation and calculate the committees
|
||||
indexed_attestation = get_indexed_attestation(target_state, attestation)
|
||||
|
@ -84,6 +84,29 @@ def test_on_attestation_past_epoch(spec, state):
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_mismatched_target_and_slot(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
attestation.data.target.epoch += 1
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
assert attestation.data.target.epoch == spec.GENESIS_EPOCH + 1
|
||||
assert spec.compute_epoch_at_slot(attestation.data.slot) == spec.GENESIS_EPOCH
|
||||
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
|
||||
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_target_not_in_store(spec, state):
|
||||
|
@ -168,7 +168,7 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_block_outside_safe_slots_and_old_block(spec, state):
|
||||
def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
@ -187,20 +187,30 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
|
||||
just_block.slot = spec.compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
|
||||
store.blocks[just_block.hash_tree_root()] = just_block
|
||||
|
||||
# Mock the justified checkpoint
|
||||
just_state = store.block_states[last_block_root]
|
||||
new_justified = spec.Checkpoint(
|
||||
epoch=just_state.current_justified_checkpoint.epoch + 1,
|
||||
root=just_block.hash_tree_root(),
|
||||
)
|
||||
just_state.current_justified_checkpoint = new_justified
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, just_state)
|
||||
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
|
||||
# Step time past safe slots
|
||||
spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT)
|
||||
assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
assert store.justified_checkpoint != new_justified
|
||||
assert store.best_justified_checkpoint == new_justified
|
||||
previously_justified = store.justified_checkpoint
|
||||
|
||||
# Add a series of new blocks with "better" justifications
|
||||
best_justified_checkpoint = spec.Checkpoint(epoch=0)
|
||||
for i in range(3, 0, -1):
|
||||
just_state = store.block_states[last_block_root]
|
||||
new_justified = spec.Checkpoint(
|
||||
epoch=previously_justified.epoch + i,
|
||||
root=just_block.hash_tree_root(),
|
||||
)
|
||||
if new_justified.epoch > best_justified_checkpoint.epoch:
|
||||
best_justified_checkpoint = new_justified
|
||||
|
||||
just_state.current_justified_checkpoint = new_justified
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, just_state)
|
||||
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
assert store.justified_checkpoint == previously_justified
|
||||
# ensure the best from the series was stored
|
||||
assert store.best_justified_checkpoint == best_justified_checkpoint
|
||||
|
@ -177,6 +177,20 @@ def test_invalid_index(spec, state):
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_mismatched_target_and_slot(spec, state):
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
attestation.data.slot = attestation.data.slot - spec.SLOTS_PER_EPOCH
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_old_target_epoch(spec, state):
|
||||
|
@ -252,6 +252,76 @@ def test_att2_bad_replaced_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_normal_signed(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_1.attesting_indices
|
||||
indices.pop(1) # remove an index, make room for the additional duplicate index.
|
||||
attester_slashing.attestation_1.attesting_indices = sorted(indices)
|
||||
|
||||
# sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||
|
||||
indices.append(indices[0]) # add one of the indices a second time
|
||||
attester_slashing.attestation_1.attesting_indices = sorted(indices)
|
||||
|
||||
# it will just appear normal, unless the double index is spotted
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_normal_signed(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
indices = attester_slashing.attestation_2.attesting_indices
|
||||
indices.pop(2) # remove an index, make room for the additional duplicate index.
|
||||
attester_slashing.attestation_2.attesting_indices = sorted(indices)
|
||||
|
||||
# sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_2)
|
||||
|
||||
indices.append(indices[1]) # add one of the indices a second time
|
||||
attester_slashing.attestation_2.attesting_indices = sorted(indices)
|
||||
|
||||
# it will just appear normal, unless the double index is spotted
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_double_signed(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_1.attesting_indices
|
||||
indices.pop(1) # remove an index, make room for the additional duplicate index.
|
||||
indices.append(indices[2]) # add one of the indices a second time
|
||||
attester_slashing.attestation_1.attesting_indices = sorted(indices)
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1) # will have one attester signing it double
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_double_signed(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
indices = attester_slashing.attestation_2.attesting_indices
|
||||
indices.pop(1) # remove an index, make room for the additional duplicate index.
|
||||
indices.append(indices[2]) # add one of the indices a second time
|
||||
attester_slashing.attestation_2.attesting_indices = sorted(indices)
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_2) # will have one attester signing it double
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_1(spec, state):
|
||||
|
@ -17,24 +17,80 @@ def mock_deposit(spec, state, index):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation(spec, state):
|
||||
def test_add_to_activation_queue(spec, state):
|
||||
# move past first two irregular epochs wrt finality
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
index = 0
|
||||
mock_deposit(spec, state, index)
|
||||
|
||||
for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
|
||||
next_epoch(spec, state)
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# validator moved into queue
|
||||
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_to_activated_if_finalized(spec, state):
|
||||
# move past first two irregular epochs wrt finality
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
index = 0
|
||||
mock_deposit(spec, state, index)
|
||||
|
||||
# mock validator as having been in queue since latest finalized
|
||||
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
|
||||
state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
|
||||
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# validator activated for future epoch
|
||||
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert state.validators[index].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
assert spec.is_active_validator(
|
||||
state.validators[index],
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_no_activation_no_finality(spec, state):
|
||||
# move past first two irregular epochs wrt finality
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
index = 0
|
||||
mock_deposit(spec, state, index)
|
||||
|
||||
# mock validator as having been in queue only after latest finalized
|
||||
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
|
||||
state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch + 1
|
||||
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# validator not activated
|
||||
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_sorting(spec, state):
|
||||
mock_activations = 10
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
|
||||
# try to activate more than the per-epoch churn linmit
|
||||
mock_activations = churn_limit * 2
|
||||
|
||||
epoch = spec.get_current_epoch(state)
|
||||
for i in range(mock_activations):
|
||||
@ -44,9 +100,9 @@ def test_activation_queue_sorting(spec, state):
|
||||
# give the last priority over the others
|
||||
state.validators[mock_activations - 1].activation_eligibility_epoch = epoch
|
||||
|
||||
# make sure we are hitting the churn
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
assert mock_activations > churn_limit
|
||||
# move state forward and finalize to allow for activations
|
||||
state.slot += spec.SLOTS_PER_EPOCH * 3
|
||||
state.finalized_checkpoint.epoch = epoch + 1
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
@ -63,6 +119,38 @@ def test_activation_queue_sorting(spec, state):
|
||||
assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_efficiency(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
mock_activations = churn_limit * 2
|
||||
|
||||
epoch = spec.get_current_epoch(state)
|
||||
for i in range(mock_activations):
|
||||
mock_deposit(spec, state, i)
|
||||
state.validators[i].activation_eligibility_epoch = epoch + 1
|
||||
|
||||
# move state forward and finalize to allow for activations
|
||||
state.slot += spec.SLOTS_PER_EPOCH * 3
|
||||
state.finalized_checkpoint.epoch = epoch + 1
|
||||
|
||||
# Run first registry update. Do not yield test vectors
|
||||
for _ in run_process_registry_updates(spec, state):
|
||||
pass
|
||||
|
||||
# Half should churn in first run of registry update
|
||||
for i in range(mock_activations):
|
||||
if i < mock_activations // 2:
|
||||
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
|
||||
else:
|
||||
assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# Second half should churn in second run of registry update
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
for i in range(mock_activations):
|
||||
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_ejection(spec, state):
|
||||
@ -73,13 +161,87 @@ def test_ejection(spec, state):
|
||||
# Mock an ejection
|
||||
state.validators[index].effective_balance = spec.EJECTION_BALANCE
|
||||
|
||||
for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
|
||||
next_epoch(spec, state)
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
assert state.validators[index].exit_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(
|
||||
state.validators[index],
|
||||
spec.get_current_epoch(state),
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_ejection_past_churn_limit(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
|
||||
# try to eject more than per-epoch churn limit
|
||||
mock_ejections = churn_limit * 3
|
||||
|
||||
for i in range(mock_ejections):
|
||||
state.validators[i].effective_balance = spec.EJECTION_BALANCE
|
||||
|
||||
expected_ejection_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
for i in range(mock_ejections):
|
||||
# first third ejected in normal speed
|
||||
if i < mock_ejections // 3:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch
|
||||
# second thirdgets delayed by 1 epoch
|
||||
elif mock_ejections // 3 <= i < mock_ejections * 2 // 3:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch + 1
|
||||
# second thirdgets delayed by 2 epochs
|
||||
else:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch + 2
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_activation_and_ejection(spec, state):
|
||||
# move past first two irregular epochs wrt finality
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
# ready for entrance into activation queue
|
||||
activation_queue_index = 0
|
||||
mock_deposit(spec, state, activation_queue_index)
|
||||
|
||||
# ready for activation
|
||||
activation_index = 1
|
||||
mock_deposit(spec, state, activation_index)
|
||||
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
|
||||
state.validators[activation_index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
|
||||
|
||||
# ready for ejection
|
||||
ejection_index = 2
|
||||
state.validators[ejection_index].effective_balance = spec.EJECTION_BALANCE
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# validator moved into activation queue
|
||||
validator = state.validators[activation_queue_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
|
||||
# validator activated for future epoch
|
||||
validator = state.validators[activation_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
|
||||
# validator ejected for future epoch
|
||||
validator = state.validators[ejection_index]
|
||||
assert validator.exit_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
|
@ -3,7 +3,7 @@ from copy import deepcopy
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.bls import bls_sign
|
||||
|
||||
from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \
|
||||
transition_unsigned_block
|
||||
from eth2spec.test.helpers.keys import privkeys, pubkeys
|
||||
@ -253,6 +253,58 @@ def test_attester_slashing(spec, state):
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_proposer_after_inactive_index(spec, state):
|
||||
# disable some low validator index to check after for
|
||||
inactive_index = 10
|
||||
state.validators[inactive_index].exit_epoch = spec.get_current_epoch(state)
|
||||
|
||||
# skip forward, get brand new proposers
|
||||
state.slot = spec.SLOTS_PER_EPOCH * 2
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
while True:
|
||||
next_slot(spec, state)
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
if proposer_index > inactive_index:
|
||||
# found a proposer that has a higher index than a disabled validator
|
||||
yield 'pre', state
|
||||
# test if the proposer can be recognized correctly after the inactive validator
|
||||
signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
break
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_high_proposer_index(spec, state):
|
||||
# disable a good amount of validators to make the active count lower, for a faster test
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
for i in range(len(state.validators) // 3):
|
||||
state.validators[i].exit_epoch = current_epoch
|
||||
|
||||
# skip forward, get brand new proposers
|
||||
state.slot = spec.SLOTS_PER_EPOCH * 2
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
active_count = len(spec.get_active_validator_indices(state, current_epoch))
|
||||
while True:
|
||||
next_slot(spec, state)
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
if proposer_index >= active_count:
|
||||
# found a proposer that has a higher index than the active validator count
|
||||
yield 'pre', state
|
||||
# test if the proposer can be recognized correctly, even while it has a high index.
|
||||
signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
break
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_expected_deposit_in_block(spec, state):
|
||||
|
@ -2,6 +2,6 @@
|
||||
pytest>=4.4
|
||||
../config_helpers
|
||||
flake8==3.7.7
|
||||
mypy==0.701
|
||||
mypy==0.750
|
||||
pytest-cov
|
||||
pytest-xdist
|
||||
|
Loading…
x
Reference in New Issue
Block a user