mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-02-18 14:14:23 -05:00
Merge pull request #1206 from ethereum/cov-hunt
Test coverage improvements
This commit is contained in:
@@ -4,13 +4,15 @@ from eth2spec.utils.bls import bls_sign
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
|
||||
|
||||
def get_valid_transfer(spec, state, slot=None, sender_index=None, amount=None, fee=None, signed=False):
|
||||
def get_valid_transfer(spec, state, slot=None, sender_index=None,
|
||||
recipient_index=None, amount=None, fee=None, signed=False):
|
||||
if slot is None:
|
||||
slot = state.slot
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
if sender_index is None:
|
||||
sender_index = spec.get_active_validator_indices(state, current_epoch)[-1]
|
||||
recipient_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
if recipient_index is None:
|
||||
recipient_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
transfer_pubkey = pubkeys[-1]
|
||||
transfer_privkey = privkeys[-1]
|
||||
|
||||
|
||||
@@ -84,6 +84,29 @@ def test_success_since_max_epochs_per_crosslink(spec, state):
|
||||
yield from run_attestation_processing(spec, state, attestation)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_wrong_end_epoch_with_max_epochs_per_crosslink(spec, state):
|
||||
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
data = attestation.data
|
||||
# test logic sanity check: make sure the attestation only includes MAX_EPOCHS_PER_CROSSLINK epochs
|
||||
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
|
||||
# Now change it to be different
|
||||
data.crosslink.end_epoch += 1
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@always_bls
|
||||
@spec_state_test
|
||||
@@ -147,6 +170,47 @@ def test_wrong_shard(spec, state):
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_invalid_shard(spec, state):
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
# off by one (with respect to valid range) on purpose
|
||||
attestation.data.crosslink.shard = spec.SHARD_COUNT
|
||||
|
||||
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):
|
||||
assert spec.MIN_ATTESTATION_INCLUSION_DELAY < spec.SLOTS_PER_EPOCH * 2
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=True)
|
||||
|
||||
state.slot = spec.SLOTS_PER_EPOCH * 2 # target epoch will be too old to handle
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_future_target_epoch(spec, state):
|
||||
assert spec.MIN_ATTESTATION_INCLUSION_DELAY < spec.SLOTS_PER_EPOCH * 2
|
||||
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.target.epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_new_source_epoch(spec, state):
|
||||
|
||||
@@ -200,12 +200,106 @@ def test_participants_already_slashed(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_custody_bit_0_and_1(spec, state):
|
||||
def test_custody_bit_0_and_1_intersect(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
||||
attester_slashing.attestation_1.custody_bit_1_indices = (
|
||||
attester_slashing.attestation_1.custody_bit_0_indices
|
||||
attester_slashing.attestation_1.custody_bit_1_indices.append(
|
||||
attester_slashing.attestation_1.custody_bit_0_indices[0]
|
||||
)
|
||||
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@always_bls
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_att1_bad_extra_index(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_1.custody_bit_0_indices
|
||||
options = list(set(range(len(state.validators))) - set(indices))
|
||||
indices.append(options[len(options) // 2]) # add random index, not previously in attestation.
|
||||
attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices)
|
||||
# Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not),
|
||||
# see if the bad extra index is spotted, and slashing is aborted.
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@always_bls
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_att1_bad_replaced_index(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_1.custody_bit_0_indices
|
||||
options = list(set(range(len(state.validators))) - set(indices))
|
||||
indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation.
|
||||
attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices)
|
||||
# Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not),
|
||||
# see if the bad replaced index is spotted, and slashing is aborted.
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@always_bls
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_att2_bad_extra_index(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_2.custody_bit_0_indices
|
||||
options = list(set(range(len(state.validators))) - set(indices))
|
||||
indices.append(options[len(options) // 2]) # add random index, not previously in attestation.
|
||||
attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices)
|
||||
# Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not),
|
||||
# see if the bad extra index is spotted, and slashing is aborted.
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@always_bls
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_att2_bad_replaced_index(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_2.custody_bit_0_indices
|
||||
options = list(set(range(len(state.validators))) - set(indices))
|
||||
indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation.
|
||||
attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices)
|
||||
# Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not),
|
||||
# see if the bad replaced index is spotted, and slashing is aborted.
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_1_bit0(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
||||
indices = attester_slashing.attestation_1.custody_bit_0_indices
|
||||
assert len(indices) >= 3
|
||||
indices[1], indices[2] = indices[2], indices[1] # unsort second and third index
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_2_bit0(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
indices = attester_slashing.attestation_2.custody_bit_0_indices
|
||||
assert len(indices) >= 3
|
||||
indices[1], indices[2] = indices[2], indices[1] # unsort second and third index
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_2)
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
# note: unsorted indices for custody bit 0 are to be introduced in phase 1 testing.
|
||||
|
||||
@@ -49,20 +49,50 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
|
||||
assert len(state.balances) == pre_validator_count + 1
|
||||
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
|
||||
|
||||
effective = min(spec.MAX_EFFECTIVE_BALANCE,
|
||||
pre_balance + deposit.data.amount)
|
||||
effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
assert state.validators[validator_index].effective_balance == effective
|
||||
|
||||
assert state.eth1_deposit_index == state.eth1_data.deposit_count
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_new_deposit(spec, state):
|
||||
def test_new_deposit_under_max(spec, state):
|
||||
# fresh deposit = next validator index = validator appended to registry
|
||||
validator_index = len(state.validators)
|
||||
# effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement.
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_new_deposit_max(spec, state):
|
||||
# fresh deposit = next validator index = validator appended to registry
|
||||
validator_index = len(state.validators)
|
||||
# effective balance will be exactly the same as balance.
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_new_deposit_over_max(spec, state):
|
||||
# fresh deposit = next validator index = validator appended to registry
|
||||
validator_index = len(state.validators)
|
||||
# just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE + 1
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@always_bls
|
||||
@spec_state_test
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
from eth2spec.test.helpers.block import apply_empty_block
|
||||
from eth2spec.test.helpers.transfers import get_valid_transfer
|
||||
from eth2spec.test.helpers.transfers import get_valid_transfer, sign_transfer
|
||||
|
||||
|
||||
def run_transfer_processing(spec, state, transfer, valid=True):
|
||||
@@ -13,11 +13,6 @@ def run_transfer_processing(spec, state, transfer, valid=True):
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
pre_transfer_sender_balance = state.balances[transfer.sender]
|
||||
pre_transfer_recipient_balance = state.balances[transfer.recipient]
|
||||
pre_transfer_proposer_balance = state.balances[proposer_index]
|
||||
|
||||
yield 'pre', state
|
||||
yield 'transfer', transfer
|
||||
|
||||
@@ -26,6 +21,11 @@ def run_transfer_processing(spec, state, transfer, valid=True):
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
pre_transfer_sender_balance = state.balances[transfer.sender]
|
||||
pre_transfer_recipient_balance = state.balances[transfer.recipient]
|
||||
pre_transfer_proposer_balance = state.balances[proposer_index]
|
||||
|
||||
spec.process_transfer(state, transfer)
|
||||
yield 'post', state
|
||||
|
||||
@@ -107,20 +107,48 @@ def test_active_but_transfer_past_effective_balance(spec, state):
|
||||
def test_incorrect_slot(spec, state):
|
||||
transfer = get_valid_transfer(spec, state, slot=state.slot + 1, signed=True)
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_fee_result_dust(spec, state):
|
||||
def test_transfer_clean(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=spec.MIN_DEPOSIT_AMOUNT, fee=0, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_transfer_clean_split_to_fee(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=spec.MIN_DEPOSIT_AMOUNT // 2, fee=spec.MIN_DEPOSIT_AMOUNT // 2, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_fee(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=0, fee=1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
@@ -142,11 +170,11 @@ def test_insufficient_balance_for_fee_result_full(spec, state):
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_amount_result_dust(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
@@ -287,7 +315,7 @@ def test_no_dust_sender(spec, state):
|
||||
)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
@@ -301,7 +329,29 @@ def test_no_dust_recipient(spec, state):
|
||||
state.balances[transfer.recipient] = 0
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_non_existent_sender(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0)
|
||||
transfer.sender = len(state.validators)
|
||||
sign_transfer(spec, state, transfer, 42) # mostly valid signature, but sender won't exist, use bogus key.
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_non_existent_recipient(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
recipient_index=len(state.validators), amount=1, fee=0, signed=True)
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
@@ -313,6 +363,6 @@ def test_invalid_pubkey(spec, state):
|
||||
state.validators[transfer.sender].withdrawal_credentials = spec.ZERO_HASH
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
process_calls = [
|
||||
'process_justification_and_finalization',
|
||||
'process_crosslinks',
|
||||
'process_rewards_and_penalties',
|
||||
'process_registry_updates',
|
||||
'process_reveal_deadlines',
|
||||
'process_challenge_deadlines',
|
||||
'process_slashings',
|
||||
'process_final_updates',
|
||||
'after_process_final_updates',
|
||||
]
|
||||
|
||||
|
||||
def run_epoch_processing_to(spec, state, process_name: str):
|
||||
"""
|
||||
Processes to the next epoch transition, up to, but not including, the sub-transition named ``process_name``
|
||||
"""
|
||||
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH)
|
||||
|
||||
# transition state to slot before epoch state transition
|
||||
spec.process_slots(state, slot - 1)
|
||||
|
||||
# start transitioning, do one slot update before the epoch itself.
|
||||
spec.process_slot(state)
|
||||
|
||||
# process components of epoch transition before final-updates
|
||||
for name in process_calls:
|
||||
if name == process_name:
|
||||
break
|
||||
# only run when present. Later phases introduce more to the epoch-processing.
|
||||
if hasattr(spec, name):
|
||||
getattr(spec, name)(state)
|
||||
|
||||
|
||||
def run_epoch_processing_with(spec, state, process_name: str):
|
||||
"""
|
||||
Processes to the next epoch transition, up to and including the sub-transition named ``process_name``
|
||||
- pre-state ('pre'), state before calling ``process_name``
|
||||
- post-state ('post'), state after calling ``process_name``
|
||||
"""
|
||||
run_epoch_processing_to(spec, state, process_name)
|
||||
yield 'pre', state
|
||||
getattr(spec, process_name)(state)
|
||||
yield 'post', state
|
||||
@@ -3,42 +3,20 @@ from copy import deepcopy
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_slot,
|
||||
state_transition_and_sign_block,
|
||||
next_slot
|
||||
)
|
||||
from eth2spec.test.helpers.block import apply_empty_block, sign_block
|
||||
from eth2spec.test.helpers.block import apply_empty_block
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
add_attestation_to_state,
|
||||
build_empty_block_for_next_slot,
|
||||
fill_aggregate_attestation,
|
||||
get_valid_attestation,
|
||||
sign_attestation,
|
||||
)
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
|
||||
|
||||
def run_process_crosslinks(spec, state, valid=True):
|
||||
"""
|
||||
Run ``process_crosslinks``, yielding:
|
||||
- pre-state ('pre')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
# transition state to slot before state transition
|
||||
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot = slot
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# cache state before epoch transition
|
||||
spec.process_slot(state)
|
||||
|
||||
# process components of epoch transition before processing crosslinks
|
||||
spec.process_justification_and_finalization(state)
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_crosslinks(state)
|
||||
yield 'post', state
|
||||
def run_process_crosslinks(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_crosslinks')
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
|
||||
run_epoch_processing_with, run_epoch_processing_to
|
||||
)
|
||||
|
||||
|
||||
def run_process_final_updates(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_final_updates')
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_vote_no_reset(spec, state):
|
||||
assert spec.SLOTS_PER_ETH1_VOTING_PERIOD > spec.SLOTS_PER_EPOCH
|
||||
# skip ahead to the end of the epoch
|
||||
state.slot = spec.SLOTS_PER_EPOCH - 1
|
||||
for i in range(state.slot + 1): # add a vote for each skipped slot.
|
||||
state.eth1_data_votes.append(
|
||||
spec.Eth1Data(deposit_root=b'\xaa' * 32,
|
||||
deposit_count=state.eth1_deposit_index,
|
||||
block_hash=b'\xbb' * 32))
|
||||
|
||||
yield from run_process_final_updates(spec, state)
|
||||
|
||||
assert len(state.eth1_data_votes) == spec.SLOTS_PER_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_vote_reset(spec, state):
|
||||
# skip ahead to the end of the voting period
|
||||
state.slot = spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1
|
||||
for i in range(state.slot + 1): # add a vote for each skipped slot.
|
||||
state.eth1_data_votes.append(
|
||||
spec.Eth1Data(deposit_root=b'\xaa' * 32,
|
||||
deposit_count=state.eth1_deposit_index,
|
||||
block_hash=b'\xbb' * 32))
|
||||
|
||||
yield from run_process_final_updates(spec, state)
|
||||
|
||||
assert len(state.eth1_data_votes) == 0
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_effective_balance_hysteresis(spec, state):
|
||||
# Prepare state up to the final-updates.
|
||||
# Then overwrite the balances, we only want to focus to be on the hysteresis based changes.
|
||||
run_epoch_processing_to(spec, state, 'process_final_updates')
|
||||
# Set some edge cases for balances
|
||||
max = spec.MAX_EFFECTIVE_BALANCE
|
||||
min = spec.EJECTION_BALANCE
|
||||
inc = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
half_inc = inc // 2
|
||||
cases = [
|
||||
(max, max, max, "as-is"),
|
||||
(max, max - 1, max - inc, "round down, step lower"),
|
||||
(max, max + 1, max, "round down"),
|
||||
(max, max - inc, max - inc, "exactly 1 step lower"),
|
||||
(max, max - inc - 1, max - (2 * inc), "just 1 over 1 step lower"),
|
||||
(max, max - inc + 1, max - inc, "close to 1 step lower"),
|
||||
(min, min + (half_inc * 3), min, "bigger balance, but not high enough"),
|
||||
(min, min + (half_inc * 3) + 1, min + inc, "bigger balance, high enough, but small step"),
|
||||
(min, min + (half_inc * 4) - 1, min + inc, "bigger balance, high enough, close to double step"),
|
||||
(min, min + (half_inc * 4), min + (2 * inc), "exact two step balance increment"),
|
||||
(min, min + (half_inc * 4) + 1, min + (2 * inc), "over two steps, round down"),
|
||||
]
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
for i, (pre_eff, bal, _, _) in enumerate(cases):
|
||||
assert spec.is_active_validator(state.validators[i], current_epoch)
|
||||
state.validators[i].effective_balance = pre_eff
|
||||
state.balances[i] = bal
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_final_updates(state)
|
||||
yield 'post', state
|
||||
|
||||
for i, (_, _, post_eff, name) in enumerate(cases):
|
||||
assert state.validators[i].effective_balance == post_eff, name
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_historical_root_accumulator(spec, state):
|
||||
# skip ahead to near the end of the historical roots period (excl block before epoch processing)
|
||||
state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1
|
||||
history_len = len(state.historical_roots)
|
||||
|
||||
yield from run_process_final_updates(spec, state)
|
||||
|
||||
assert len(state.historical_roots) == history_len + 1
|
||||
@@ -0,0 +1,280 @@
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
|
||||
run_epoch_processing_with
|
||||
)
|
||||
|
||||
|
||||
def run_process_just_and_fin(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_justification_and_finalization')
|
||||
|
||||
|
||||
def get_shards_for_slot(spec, state, slot):
|
||||
epoch = spec.slot_to_epoch(slot)
|
||||
epoch_start_shard = spec.get_epoch_start_shard(state, epoch)
|
||||
committees_per_slot = spec.get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH
|
||||
shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT
|
||||
return [shard + i for i in range(committees_per_slot)]
|
||||
|
||||
|
||||
def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False):
|
||||
# we must be at the end of the epoch
|
||||
assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0
|
||||
|
||||
previous_epoch = spec.get_previous_epoch(state)
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
|
||||
if current_epoch == epoch:
|
||||
attestations = state.current_epoch_attestations
|
||||
elif previous_epoch == epoch:
|
||||
attestations = state.previous_epoch_attestations
|
||||
else:
|
||||
raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
|
||||
|
||||
total_balance = spec.get_total_active_balance(state)
|
||||
remaining_balance = total_balance * 2 // 3
|
||||
|
||||
epoch_start_slot = spec.get_epoch_start_slot(epoch)
|
||||
for slot in range(epoch_start_slot, epoch_start_slot + spec.SLOTS_PER_EPOCH):
|
||||
for shard in get_shards_for_slot(spec, state, slot):
|
||||
# Check if we already have had sufficient balance. (and undone if we don't want it).
|
||||
# If so, do not create more attestations. (we do not have empty pending attestations normally anyway)
|
||||
if remaining_balance < 0:
|
||||
return
|
||||
|
||||
committee = spec.get_crosslink_committee(state, spec.slot_to_epoch(slot), shard)
|
||||
# Create a bitfield filled with the given count per attestation,
|
||||
# exactly on the right-most part of the committee field.
|
||||
|
||||
aggregation_bits = [0] * len(committee)
|
||||
for v in range(len(committee) * 2 // 3 + 1):
|
||||
if remaining_balance > 0:
|
||||
remaining_balance -= state.validators[v].effective_balance
|
||||
aggregation_bits[v] = 1
|
||||
else:
|
||||
break
|
||||
|
||||
# remove just one attester to make the marginal support insufficient
|
||||
if not sufficient_support:
|
||||
aggregation_bits[aggregation_bits.index(1)] = 0
|
||||
|
||||
attestations.append(spec.PendingAttestation(
|
||||
aggregation_bits=aggregation_bits,
|
||||
data=spec.AttestationData(
|
||||
beacon_block_root=b'\xff' * 32, # irrelevant to testing
|
||||
source=source,
|
||||
target=target,
|
||||
crosslink=spec.Crosslink(shard=shard)
|
||||
),
|
||||
inclusion_delay=1,
|
||||
))
|
||||
|
||||
|
||||
def get_checkpoints(spec, epoch):
|
||||
c1 = None if epoch < 1 else spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32)
|
||||
c2 = None if epoch < 2 else spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32)
|
||||
c3 = None if epoch < 3 else spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32)
|
||||
c4 = None if epoch < 4 else spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32)
|
||||
c5 = None if epoch < 5 else spec.Checkpoint(epoch=epoch - 5, root=b'\xee' * 32)
|
||||
return c1, c2, c3, c4, c5
|
||||
|
||||
|
||||
def put_checkpoints_in_block_roots(spec, state, checkpoints):
|
||||
for c in checkpoints:
|
||||
state.block_roots[spec.get_epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root
|
||||
|
||||
|
||||
def finalize_on_234(spec, state, epoch, sufficient_support):
|
||||
assert epoch > 4
|
||||
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
|
||||
|
||||
# 43210 -- epochs ago
|
||||
# 3210x -- justification bitfield indices
|
||||
# 11*0. -- justification bitfield contents, . = this epoch, * is being justified now
|
||||
# checkpoints for the epochs ago:
|
||||
c1, c2, c3, c4, _ = get_checkpoints(spec, epoch)
|
||||
put_checkpoints_in_block_roots(spec, state, [c1, c2, c3, c4])
|
||||
|
||||
old_finalized = state.finalized_checkpoint
|
||||
state.previous_justified_checkpoint = c4
|
||||
state.current_justified_checkpoint = c3
|
||||
state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]()
|
||||
state.justification_bits[1:3] = [1, 1] # mock 3rd and 4th latest epochs as justified (indices are pre-shift)
|
||||
# mock the 2nd latest epoch as justifiable, with 4th as source
|
||||
add_mock_attestations(spec, state,
|
||||
epoch=epoch - 2,
|
||||
source=c4,
|
||||
target=c2,
|
||||
sufficient_support=sufficient_support)
|
||||
|
||||
# process!
|
||||
yield from run_process_just_and_fin(spec, state)
|
||||
|
||||
assert state.previous_justified_checkpoint == c3 # changed to old current
|
||||
if sufficient_support:
|
||||
assert state.current_justified_checkpoint == c2 # changed to 2nd latest
|
||||
assert state.finalized_checkpoint == c4 # finalized old previous justified epoch
|
||||
else:
|
||||
assert state.current_justified_checkpoint == c3 # still old current
|
||||
assert state.finalized_checkpoint == old_finalized # no new finalized
|
||||
|
||||
|
||||
def finalize_on_23(spec, state, epoch, sufficient_support):
|
||||
assert epoch > 3
|
||||
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
|
||||
|
||||
# 43210 -- epochs ago
|
||||
# 210xx -- justification bitfield indices (pre shift)
|
||||
# 3210x -- justification bitfield indices (post shift)
|
||||
# 01*0. -- justification bitfield contents, . = this epoch, * is being justified now
|
||||
# checkpoints for the epochs ago:
|
||||
c1, c2, c3, _, _ = get_checkpoints(spec, epoch)
|
||||
put_checkpoints_in_block_roots(spec, state, [c1, c2, c3])
|
||||
|
||||
old_finalized = state.finalized_checkpoint
|
||||
state.previous_justified_checkpoint = c3
|
||||
state.current_justified_checkpoint = c3
|
||||
state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]()
|
||||
state.justification_bits[1] = 1 # mock 3rd latest epoch as justified (index is pre-shift)
|
||||
# mock the 2nd latest epoch as justifiable, with 3rd as source
|
||||
add_mock_attestations(spec, state,
|
||||
epoch=epoch - 2,
|
||||
source=c3,
|
||||
target=c2,
|
||||
sufficient_support=sufficient_support)
|
||||
|
||||
# process!
|
||||
yield from run_process_just_and_fin(spec, state)
|
||||
|
||||
assert state.previous_justified_checkpoint == c3 # changed to old current
|
||||
if sufficient_support:
|
||||
assert state.current_justified_checkpoint == c2 # changed to 2nd latest
|
||||
assert state.finalized_checkpoint == c3 # finalized old previous justified epoch
|
||||
else:
|
||||
assert state.current_justified_checkpoint == c3 # still old current
|
||||
assert state.finalized_checkpoint == old_finalized # no new finalized
|
||||
|
||||
|
||||
def finalize_on_123(spec, state, epoch, sufficient_support):
|
||||
assert epoch > 5
|
||||
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
|
||||
|
||||
# 43210 -- epochs ago
|
||||
# 210xx -- justification bitfield indices (pre shift)
|
||||
# 3210x -- justification bitfield indices (post shift)
|
||||
# 011*. -- justification bitfield contents, . = this epoch, * is being justified now
|
||||
# checkpoints for the epochs ago:
|
||||
c1, c2, c3, c4, c5 = get_checkpoints(spec, epoch)
|
||||
put_checkpoints_in_block_roots(spec, state, [c1, c2, c3, c4, c5])
|
||||
|
||||
old_finalized = state.finalized_checkpoint
|
||||
state.previous_justified_checkpoint = c5
|
||||
state.current_justified_checkpoint = c3
|
||||
state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]()
|
||||
state.justification_bits[1] = 1 # mock 3rd latest epochs as justified (index is pre-shift)
|
||||
# mock the 2nd latest epoch as justifiable, with 5th as source
|
||||
add_mock_attestations(spec, state,
|
||||
epoch=epoch - 2,
|
||||
source=c5,
|
||||
target=c2,
|
||||
sufficient_support=sufficient_support)
|
||||
# mock the 1st latest epoch as justifiable, with 3rd as source
|
||||
add_mock_attestations(spec, state,
|
||||
epoch=epoch - 1,
|
||||
source=c3,
|
||||
target=c1,
|
||||
sufficient_support=sufficient_support)
|
||||
|
||||
# process!
|
||||
yield from run_process_just_and_fin(spec, state)
|
||||
|
||||
assert state.previous_justified_checkpoint == c3 # changed to old current
|
||||
if sufficient_support:
|
||||
assert state.current_justified_checkpoint == c1 # changed to 1st latest
|
||||
assert state.finalized_checkpoint == c3 # finalized old current
|
||||
else:
|
||||
assert state.current_justified_checkpoint == c3 # still old current
|
||||
assert state.finalized_checkpoint == old_finalized # no new finalized
|
||||
|
||||
|
||||
def finalize_on_12(spec, state, epoch, sufficient_support):
|
||||
assert epoch > 2
|
||||
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
|
||||
|
||||
# 43210 -- epochs ago
|
||||
# 210xx -- justification bitfield indices (pre shift)
|
||||
# 3210x -- justification bitfield indices (post shift)
|
||||
# 001*. -- justification bitfield contents, . = this epoch, * is being justified now
|
||||
# checkpoints for the epochs ago:
|
||||
c1, c2, _, _, _ = get_checkpoints(spec, epoch)
|
||||
put_checkpoints_in_block_roots(spec, state, [c1, c2])
|
||||
|
||||
old_finalized = state.finalized_checkpoint
|
||||
state.previous_justified_checkpoint = c2
|
||||
state.current_justified_checkpoint = c2
|
||||
state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]()
|
||||
state.justification_bits[0] = 1 # mock 2nd latest epoch as justified (this is pre-shift)
|
||||
# mock the 1st latest epoch as justifiable, with 2nd as source
|
||||
add_mock_attestations(spec, state,
|
||||
epoch=epoch - 1,
|
||||
source=c2,
|
||||
target=c1,
|
||||
sufficient_support=sufficient_support)
|
||||
|
||||
# process!
|
||||
yield from run_process_just_and_fin(spec, state)
|
||||
|
||||
assert state.previous_justified_checkpoint == c2 # changed to old current
|
||||
if sufficient_support:
|
||||
assert state.current_justified_checkpoint == c1 # changed to 1st latest
|
||||
assert state.finalized_checkpoint == c2 # finalized previous justified epoch
|
||||
else:
|
||||
assert state.current_justified_checkpoint == c2 # still old current
|
||||
assert state.finalized_checkpoint == old_finalized # no new finalized
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_234_ok_support(spec, state):
|
||||
yield from finalize_on_234(spec, state, 5, True)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_234_poor_support(spec, state):
|
||||
yield from finalize_on_234(spec, state, 5, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_23_ok_support(spec, state):
|
||||
yield from finalize_on_23(spec, state, 4, True)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_23_poor_support(spec, state):
|
||||
yield from finalize_on_23(spec, state, 4, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_123_ok_support(spec, state):
|
||||
yield from finalize_on_123(spec, state, 6, True)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_123_poor_support(spec, state):
|
||||
yield from finalize_on_123(spec, state, 6, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_12_ok_support(spec, state):
|
||||
yield from finalize_on_12(spec, state, 3, True)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_12_poor_support(spec, state):
|
||||
yield from finalize_on_12(spec, state, 3, False)
|
||||
@@ -1,46 +1,25 @@
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||
from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
|
||||
|
||||
def run_process_registry_updates(spec, state, valid=True):
|
||||
"""
|
||||
Run ``process_crosslinks``, yielding:
|
||||
- pre-state ('pre')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
# transition state to slot before state transition
|
||||
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot = slot
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
def run_process_registry_updates(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_registry_updates')
|
||||
|
||||
# cache state before epoch transition
|
||||
spec.process_slot(state)
|
||||
|
||||
# process components of epoch transition before registry update
|
||||
spec.process_justification_and_finalization(state)
|
||||
spec.process_crosslinks(state)
|
||||
spec.process_rewards_and_penalties(state)
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_registry_updates(state)
|
||||
yield 'post', state
|
||||
def mock_deposit(spec, state, index):
|
||||
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
state.validators[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation(spec, state):
|
||||
index = 0
|
||||
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
# Mock a new deposit
|
||||
state.validators[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
state.validators[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
mock_deposit(spec, state, index)
|
||||
|
||||
for _ in range(spec.ACTIVATION_EXIT_DELAY + 1):
|
||||
next_epoch(spec, state)
|
||||
@@ -49,10 +28,39 @@ def test_activation(spec, state):
|
||||
|
||||
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 spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_sorting(spec, state):
|
||||
mock_activations = 10
|
||||
|
||||
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
|
||||
|
||||
# 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_churn_limit(state)
|
||||
assert mock_activations > churn_limit
|
||||
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# the first got in as second
|
||||
assert state.validators[0].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
# the prioritized got in as first
|
||||
assert state.validators[mock_activations - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
# the second last is at the end of the queue, and did not make the churn,
|
||||
# hence is not assigned an activation_epoch yet.
|
||||
assert state.validators[mock_activations - 2].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
# the one at churn_limit - 1 did not make it, it was out-prioritized
|
||||
assert state.validators[churn_limit - 1].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
# but the the one in front of the above did
|
||||
assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
|
||||
run_epoch_processing_with, run_epoch_processing_to
|
||||
)
|
||||
|
||||
|
||||
def run_process_slashings(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_slashings')
|
||||
|
||||
|
||||
def slash_validators(spec, state, indices, out_epochs):
|
||||
total_slashed_balance = 0
|
||||
for i, out_epoch in zip(indices, out_epochs):
|
||||
v = state.validators[i]
|
||||
v.slashed = True
|
||||
spec.initiate_validator_exit(state, i)
|
||||
v.withdrawable_epoch = out_epoch
|
||||
total_slashed_balance += v.effective_balance
|
||||
|
||||
state.slashings[
|
||||
spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHINGS_VECTOR
|
||||
] = total_slashed_balance
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_max_penalties(spec, state):
|
||||
slashed_count = (len(state.validators) // 3) + 1
|
||||
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2)
|
||||
|
||||
slashed_indices = list(range(slashed_count))
|
||||
slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count)
|
||||
|
||||
total_balance = spec.get_total_active_balance(state)
|
||||
total_penalties = sum(state.slashings)
|
||||
|
||||
assert total_balance // 3 <= total_penalties
|
||||
|
||||
yield from run_process_slashings(spec, state)
|
||||
|
||||
for i in slashed_indices:
|
||||
assert state.balances[i] == 0
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_small_penalty(spec, state):
|
||||
# Just the bare minimum for this one validator
|
||||
state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE
|
||||
# All the other validators get the maximum.
|
||||
for i in range(1, len(state.validators)):
|
||||
state.validators[i].effective_balance = state.balances[i] = spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2)
|
||||
|
||||
slash_validators(spec, state, [0], [out_epoch])
|
||||
|
||||
total_balance = spec.get_total_active_balance(state)
|
||||
total_penalties = sum(state.slashings)
|
||||
|
||||
assert total_balance // 3 > total_penalties
|
||||
|
||||
run_epoch_processing_to(spec, state, 'process_slashings')
|
||||
pre_slash_balances = list(state.balances)
|
||||
yield 'pre', state
|
||||
spec.process_slashings(state)
|
||||
yield 'post', state
|
||||
|
||||
assert state.balances[0] == pre_slash_balances[0] - (state.validators[0].effective_balance
|
||||
* 3 * total_penalties // total_balance)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_scaled_penalties(spec, state):
|
||||
# skip to next epoch
|
||||
state.slot = spec.SLOTS_PER_EPOCH
|
||||
|
||||
# Also mock some previous slashings, so that we test to have the delta in the penalties computation.
|
||||
base = spec.EJECTION_BALANCE
|
||||
incr = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
# Just add some random slashings. non-zero slashings are at least the minimal effective balance.
|
||||
state.slashings[0] = base + (incr * 12)
|
||||
state.slashings[4] = base + (incr * 3)
|
||||
state.slashings[5] = base + (incr * 6)
|
||||
state.slashings[spec.EPOCHS_PER_SLASHINGS_VECTOR - 1] = base + (incr * 7)
|
||||
|
||||
slashed_count = len(state.validators) // 4
|
||||
|
||||
assert slashed_count > 10
|
||||
|
||||
# make the balances non-uniform.
|
||||
# Otherwise it would just be a simple 3/4 balance slashing. Test the per-validator scaled penalties.
|
||||
diff = spec.MAX_EFFECTIVE_BALANCE - base
|
||||
increments = diff // incr
|
||||
for i in range(10):
|
||||
state.validators[i].effective_balance = base + (incr * (i % increments))
|
||||
assert state.validators[i].effective_balance <= spec.MAX_EFFECTIVE_BALANCE
|
||||
# add/remove some, see if balances different than the effective balances are picked up
|
||||
state.balances[i] = state.validators[i].effective_balance + i - 5
|
||||
|
||||
total_balance = spec.get_total_active_balance(state)
|
||||
|
||||
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2)
|
||||
|
||||
slashed_indices = list(range(slashed_count))
|
||||
|
||||
# Process up to the sub-transition, then Hi-jack and get the balances.
|
||||
# We just want to test the slashings.
|
||||
# But we are not interested in the other balance changes during the same epoch transition.
|
||||
run_epoch_processing_to(spec, state, 'process_slashings')
|
||||
pre_slash_balances = list(state.balances)
|
||||
|
||||
slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count)
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_slashings(state)
|
||||
yield 'post', state
|
||||
|
||||
total_penalties = sum(state.slashings)
|
||||
|
||||
for i in slashed_indices:
|
||||
v = state.validators[i]
|
||||
penalty = v.effective_balance * total_penalties * 3 // total_balance
|
||||
assert state.balances[i] == pre_slash_balances[i] - penalty
|
||||
@@ -4,15 +4,46 @@ from eth2spec.utils.ssz.ssz_impl import signing_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.transfers import get_valid_transfer
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
|
||||
from eth2spec.test.helpers.keys import privkeys, pubkeys
|
||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
|
||||
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
|
||||
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_prev_slot_block_transition(spec, state):
|
||||
# Go to clean slot
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
# Make a block for it
|
||||
block = build_empty_block(spec, state, slot=state.slot, signed=True)
|
||||
# Transition to next slot, above block will not be invalid on top of new state.
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
yield 'pre', state
|
||||
expect_assertion_error(lambda: state_transition_and_sign_block(spec, state, block))
|
||||
yield 'blocks', [block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_same_slot_block_transition(spec, state):
|
||||
# Same slot on top of pre-state, but move out of slot 0 first.
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
block = build_empty_block(spec, state, slot=state.slot, signed=True)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'post', state
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@@ -35,6 +66,22 @@ def test_empty_block_transition(spec, state):
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_invalid_state_root(spec, state):
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.state_root = b"\xaa" * 32
|
||||
sign_block(spec, state, block)
|
||||
|
||||
expect_assertion_error(
|
||||
lambda: spec.state_transition(state, block, validate_state_root=True))
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_skipped_slots(spec, state):
|
||||
@@ -76,26 +123,29 @@ def test_empty_epoch_transition(spec, state):
|
||||
assert spec.get_block_root_at_slot(state, slot) == block.parent_root
|
||||
|
||||
|
||||
# @with_all_phases
|
||||
# @spec_state_test
|
||||
# def test_empty_epoch_transition_not_finalizing(spec, state):
|
||||
# # copy for later balance lookups.
|
||||
# pre_state = deepcopy(state)
|
||||
# yield 'pre', state
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_empty_epoch_transition_not_finalizing(spec, state):
|
||||
# Don't run for non-minimal configs, it takes very long, and the effect
|
||||
# of calling finalization/justification is just the same as with the minimal configuration.
|
||||
if spec.SLOTS_PER_EPOCH > 8:
|
||||
return
|
||||
|
||||
# block = build_empty_block_for_next_slot(spec, state)
|
||||
# block.slot += spec.SLOTS_PER_EPOCH * 5
|
||||
# sign_block(spec, state, block, proposer_index=0)
|
||||
# copy for later balance lookups.
|
||||
pre_balances = list(state.balances)
|
||||
yield 'pre', state
|
||||
|
||||
# state_transition_and_sign_block(spec, state, block)
|
||||
spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH * 5))
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# yield 'blocks', [block]
|
||||
# yield 'post', state
|
||||
yield 'blocks', [block]
|
||||
yield 'post', state
|
||||
|
||||
# assert state.slot == block.slot
|
||||
# assert state.finalized_epoch < spec.get_current_epoch(state) - 4
|
||||
# for index in range(len(state.validators)):
|
||||
# assert get_balance(state, index) < get_balance(pre_state, index)
|
||||
assert state.slot == block.slot
|
||||
assert state.finalized_checkpoint.epoch < spec.get_current_epoch(state) - 4
|
||||
for index in range(len(state.validators)):
|
||||
assert state.balances[index] < pre_balances[index]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@@ -172,7 +222,27 @@ def test_attester_slashing(spec, state):
|
||||
)
|
||||
|
||||
|
||||
# TODO update functions below to be like above, i.e. with @spec_state_test and yielding data to put into the test vector
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_expected_deposit_in_block(spec, state):
|
||||
# Make the state expect a deposit, then don't provide it.
|
||||
state.eth1_data.deposit_count += 1
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
sign_block(spec, state, block)
|
||||
bad = False
|
||||
try:
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
bad = True
|
||||
except AssertionError:
|
||||
pass
|
||||
if bad:
|
||||
raise AssertionError("expected deposit was not enforced")
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@@ -376,6 +446,7 @@ def test_historical_batch(spec, state):
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
@@ -386,29 +457,78 @@ def test_historical_batch(spec, state):
|
||||
assert len(state.historical_roots) == pre_historical_roots_len + 1
|
||||
|
||||
|
||||
# @with_all_phases
|
||||
# @spec_state_test
|
||||
# def test_eth1_data_votes(spec, state):
|
||||
# yield 'pre', state
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_data_votes_consensus(spec, state):
|
||||
# Don't run when it will take very, very long to simulate. Minimal configuration suffices.
|
||||
if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16:
|
||||
return
|
||||
|
||||
# expected_votes = 0
|
||||
# assert len(state.eth1_data_votes) == expected_votes
|
||||
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
|
||||
sign_block(spec, state, offset_block)
|
||||
state_transition_and_sign_block(spec, state, offset_block)
|
||||
yield 'pre', state
|
||||
|
||||
# blocks = []
|
||||
# for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1):
|
||||
# block = build_empty_block_for_next_slot(spec, state)
|
||||
# state_transition_and_sign_block(spec, state, block)
|
||||
# expected_votes += 1
|
||||
# assert len(state.eth1_data_votes) == expected_votes
|
||||
# blocks.append(block)
|
||||
a = b'\xaa' * 32
|
||||
b = b'\xbb' * 32
|
||||
c = b'\xcc' * 32
|
||||
|
||||
# block = build_empty_block_for_next_slot(spec, state)
|
||||
# blocks.append(block)
|
||||
blocks = []
|
||||
|
||||
# state_transition_and_sign_block(spec, state, block)
|
||||
for i in range(0, spec.SLOTS_PER_ETH1_VOTING_PERIOD):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# wait for over 50% for A, then start voting B
|
||||
block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
|
||||
# yield 'blocks', [block]
|
||||
# yield 'post', state
|
||||
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
|
||||
assert state.eth1_data.block_hash == a
|
||||
|
||||
# assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0
|
||||
# assert len(state.eth1_data_votes) == 1
|
||||
# transition to next eth1 voting period
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.eth1_data.block_hash = c
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
||||
assert state.eth1_data.block_hash == a
|
||||
assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0
|
||||
assert len(state.eth1_data_votes) == 1
|
||||
assert state.eth1_data_votes[0].block_hash == c
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_data_votes_no_consensus(spec, state):
|
||||
# Don't run when it will take very, very long to simulate. Minimal configuration suffices.
|
||||
if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16:
|
||||
return
|
||||
|
||||
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
|
||||
sign_block(spec, state, offset_block)
|
||||
state_transition_and_sign_block(spec, state, offset_block)
|
||||
yield 'pre', state
|
||||
|
||||
a = b'\xaa' * 32
|
||||
b = b'\xbb' * 32
|
||||
|
||||
blocks = []
|
||||
|
||||
for i in range(0, spec.SLOTS_PER_ETH1_VOTING_PERIOD):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# wait for precisely 50% for A, then start voting B for other 50%
|
||||
block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
|
||||
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
|
||||
assert state.eth1_data.block_hash == b'\x00' * 32
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
||||
Reference in New Issue
Block a user