def test_duplicate_attester_slashing(spec, state): if spec.MAX_ATTESTER_SLASHINGS < 2: return dump_skipping_message( "Skip test if config cannot handle multiple AttesterSlashings per block" ) attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) attester_slashings = [attester_slashing, attester_slashing.copy()] slashed_indices = get_indexed_attestation_participants( spec, attester_slashing.attestation_1) assert not any(state.validators[i].slashed for i in slashed_indices) yield 'pre', state # # Add to state via block transition # block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings = attester_slashings signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) yield 'blocks', [signed_block] yield 'post', None
def test_duplicate_attestation(spec, state): """ Although duplicate attestations can be included on-chain, they should only be rewarded for once. This test addresses this issue found at Interop https://github.com/djrtwo/interop-test-cases/tree/master/tests/prysm_16_duplicate_attestation_rewards """ attestation = get_valid_attestation(spec, state, signed=True) indexed_attestation = spec.get_indexed_attestation(state, attestation) participants = get_indexed_attestation_participants(spec, indexed_attestation) assert len(participants) > 0 single_state = state.copy() dup_state = state.copy() inclusion_slot = state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY add_attestations_to_state(spec, single_state, [attestation], inclusion_slot) add_attestations_to_state(spec, dup_state, [attestation, attestation], inclusion_slot) next_epoch(spec, single_state) next_epoch(spec, dup_state) # Run non-duplicate inclusion rewards for comparison. Do not yield test vectors for _ in run_process_rewards_and_penalties(spec, single_state): pass # Output duplicate inclusion to test vectors yield from run_process_rewards_and_penalties(spec, dup_state) for index in participants: assert state.balances[index] < single_state.balances[index] assert single_state.balances[index] == dup_state.balances[index]
def test_attester_slashing(spec, state): # copy for later balance lookups. pre_state = deepcopy(state) attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) validator_index = get_indexed_attestation_participants(spec, attester_slashing.attestation_1)[0] assert not state.validators[validator_index].slashed yield 'pre', state # # Add to state via block transition # block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings.append(attester_slashing) signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] yield 'post', state slashed_validator = state.validators[validator_index] 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(state, validator_index) < get_balance(pre_state, validator_index) proposer_index = spec.get_beacon_proposer_index(state) # gained whistleblower reward assert ( get_balance(state, proposer_index) > get_balance(pre_state, proposer_index) )
def test_attester_slashing(spec, state): # copy for later balance lookups. pre_state = state.copy() attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) slashed_indices = get_indexed_attestation_participants( spec, attester_slashing.attestation_1) assert not any(state.validators[i].slashed for i in slashed_indices) yield 'pre', state # # Add to state via block transition # block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings.append(attester_slashing) signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] yield 'post', state check_attester_slashing_effect(spec, pre_state, state, slashed_indices)
def test_success_already_exited_recent(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) slashed_indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_1) for index in slashed_indices: spec.initiate_validator_exit(state, index) yield from run_attester_slashing_processing(spec, state, attester_slashing)
def test_success_already_exited_long_ago(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) slashed_indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_1) for index in slashed_indices: spec.initiate_validator_exit(state, index) state.validators[index].withdrawable_epoch = spec.get_current_epoch(state) + 2 yield from run_attester_slashing_processing(spec, state, attester_slashing)
def test_participants_already_slashed(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) # set all indices to slashed validator_indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_1) for index in validator_indices: state.validators[index].slashed = True yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
def test_att1_bad_extra_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_1) 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.attesting_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)
def test_att2_high_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) indices = get_indexed_attestation_participants( spec, attester_slashing.attestation_2) indices.append(spec.ValidatorIndex(len(state.validators))) # off by 1 attester_slashing.attestation_2.attesting_indices = indices yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
def test_duplicate_participants_different_attestation_3(spec, state): """ Same attesters get two different attestations on chain for *different* inclusion delay Earlier attestation (by list order) has incorrect head, later is correct Note: although these are slashable, they can validly be included """ correct_attestation = get_valid_attestation(spec, state, signed=True) incorrect_attestation = correct_attestation.copy() incorrect_attestation.data.beacon_block_root = b'\x42' * 32 sign_attestation(spec, state, incorrect_attestation) indexed_attestation = spec.get_indexed_attestation(state, correct_attestation) participants = get_indexed_attestation_participants( spec, indexed_attestation) assert len(participants) > 0 single_correct_state = state.copy() dup_state = state.copy() inclusion_slot = state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY add_attestations_to_state(spec, single_correct_state, [correct_attestation], inclusion_slot) add_attestations_to_state(spec, dup_state, [incorrect_attestation], inclusion_slot) add_attestations_to_state(spec, dup_state, [correct_attestation], inclusion_slot + 1) next_epoch(spec, single_correct_state) next_epoch(spec, dup_state) # Run non-duplicate inclusion rewards for comparison. Do not yield test vectors for _ in run_process_rewards_and_penalties(spec, single_correct_state): pass # Output duplicate inclusion to test vectors yield from run_process_rewards_and_penalties(spec, dup_state) for index in participants: assert state.balances[index] < single_correct_state.balances[index] # Inclusion delay does not take into account correctness so equal reward assert single_correct_state.balances[index] == dup_state.balances[ index]
def run_attester_slashing_processing(spec, state, attester_slashing, valid=True): """ Run ``process_attester_slashing``, yielding: - pre-state ('pre') - attester_slashing ('attester_slashing') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'attester_slashing', attester_slashing if not valid: expect_assertion_error( lambda: spec.process_attester_slashing(state, attester_slashing)) yield 'post', None return slashed_indices = get_indexed_attestation_participants( spec, attester_slashing.attestation_1) proposer_index = spec.get_beacon_proposer_index(state) pre_proposer_balance = get_balance(state, proposer_index) pre_slashing_balances = { slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices } pre_slashing_effectives = { slashed_index: state.validators[slashed_index].effective_balance for slashed_index in slashed_indices } pre_withdrawalable_epochs = { slashed_index: state.validators[slashed_index].withdrawable_epoch for slashed_index in slashed_indices } total_proposer_rewards = sum( effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT for effective_balance in pre_slashing_effectives.values()) # Process slashing spec.process_attester_slashing(state, attester_slashing) for slashed_index in slashed_indices: pre_withdrawalable_epoch = pre_withdrawalable_epochs[slashed_index] slashed_validator = state.validators[slashed_index] # Check slashing assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH if pre_withdrawalable_epoch < spec.FAR_FUTURE_EPOCH: expected_withdrawable_epoch = max( pre_withdrawalable_epoch, spec.get_current_epoch(state) + spec.EPOCHS_PER_SLASHINGS_VECTOR) assert slashed_validator.withdrawable_epoch == expected_withdrawable_epoch else: assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH assert get_balance( state, slashed_index) < pre_slashing_balances[slashed_index] if proposer_index not in slashed_indices: # gained whistleblower reward assert get_balance( state, proposer_index) == pre_proposer_balance + total_proposer_rewards else: # gained rewards for all slashings, which may include others. And only lost that of themselves. expected_balance = (pre_proposer_balance + total_proposer_rewards - pre_slashing_effectives[proposer_index] // get_min_slashing_penalty_quotient(spec)) assert get_balance(state, proposer_index) == expected_balance yield 'post', state