Skip to content

Commit

Permalink
Merge pull request #873 from ethereum/validator-indices-bug
Browse files Browse the repository at this point in the history
minor bug `process_attester_slashings`
  • Loading branch information
djrtwo authored Apr 3, 2019
2 parents 06ba5fe + 014138b commit 75f0af4
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 2 deletions.
9 changes: 7 additions & 2 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,9 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices

# ensure no duplicate indices across custody bits
assert len(set(custody_bit_0_indices).intersection(set(custody_bit_1_indices))) == 0

if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
return False

Expand Down Expand Up @@ -2290,10 +2293,12 @@ def process_attester_slashing(state: BeaconState,

assert verify_indexed_attestation(state, attestation1)
assert verify_indexed_attestation(state, attestation2)
attesting_indices_1 = attestation1.custody_bit_0_indices + attestation1.custody_bit_1_indices
attesting_indices_2 = attestation2.custody_bit_0_indices + attestation2.custody_bit_1_indices
slashable_indices = [
index for index in attestation1.validator_indices
index for index in attesting_indices_1
if (
index in attestation2.validator_indices and
index in attesting_indices_2 and
is_slashable_validator(state.validator_registry[index], get_current_epoch(state))
)
]
Expand Down
115 changes: 115 additions & 0 deletions tests/phase0/block_processing/test_process_attester_slashing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from copy import deepcopy
import pytest

import build.phase0.spec as spec
from build.phase0.spec import (
get_balance,
get_beacon_proposer_index,
process_attester_slashing,
)
from tests.phase0.helpers import (
get_valid_attester_slashing,
)

# mark entire file as 'attester_slashing'
pytestmark = pytest.mark.attester_slashings


def run_attester_slashing_processing(state, attester_slashing, valid=True):
"""
Run ``process_attester_slashing`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)

if not valid:
with pytest.raises(AssertionError):
process_attester_slashing(post_state, attester_slashing)
return state, None

process_attester_slashing(post_state, attester_slashing)

slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
slashed_validator = post_state.validator_registry[slashed_index]
assert not slashed_validator.initiated_exit
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert (
get_balance(post_state, slashed_index) <
get_balance(state, slashed_index)
)
proposer_index = get_beacon_proposer_index(state, state.slot)
# gained whistleblower reward
assert (
get_balance(post_state, proposer_index) >
get_balance(state, proposer_index)
)

return state, post_state


def test_success_double(state):
attester_slashing = get_valid_attester_slashing(state)

pre_state, post_state = run_attester_slashing_processing(state, attester_slashing)

return pre_state, attester_slashing, post_state


def test_success_surround(state):
attester_slashing = get_valid_attester_slashing(state)

# set attestion1 to surround attestation 2
attester_slashing.attestation_1.data.source_epoch = attester_slashing.attestation_2.data.source_epoch - 1
attester_slashing.attestation_1.data.slot = attester_slashing.attestation_2.data.slot + spec.SLOTS_PER_EPOCH

pre_state, post_state = run_attester_slashing_processing(state, attester_slashing)

return pre_state, attester_slashing, post_state


def test_same_data(state):
attester_slashing = get_valid_attester_slashing(state)

attester_slashing.attestation_1.data = attester_slashing.attestation_2.data

pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)

return pre_state, attester_slashing, post_state


def test_no_double_or_surround(state):
attester_slashing = get_valid_attester_slashing(state)

attester_slashing.attestation_1.data.slot += spec.SLOTS_PER_EPOCH

pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)

return pre_state, attester_slashing, post_state


def test_participants_already_slashed(state):
attester_slashing = get_valid_attester_slashing(state)

# set all indices to slashed
attestation_1 = attester_slashing.attestation_1
validator_indices = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices
for index in validator_indices:
state.validator_registry[index].slashed = True

pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)

return pre_state, attester_slashing, post_state


def test_custody_bit_0_and_1(state):
attester_slashing = get_valid_attester_slashing(state)

attester_slashing.attestation_1.custody_bit_1_indices = (
attester_slashing.attestation_1.custody_bit_0_indices
)
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)

return pre_state, attester_slashing, post_state
13 changes: 13 additions & 0 deletions tests/phase0/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
Attestation,
AttestationData,
AttestationDataAndCustodyBit,
AttesterSlashing,
BeaconBlockHeader,
Deposit,
DepositData,
Eth1Data,
ProposerSlashing,
VoluntaryExit,
# functions
convert_to_indexed,
get_active_validator_indices,
get_attestation_participants,
get_block_root,
Expand Down Expand Up @@ -244,6 +246,17 @@ def get_valid_proposer_slashing(state):
)


def get_valid_attester_slashing(state):
attestation_1 = get_valid_attestation(state)
attestation_2 = deepcopy(attestation_1)
attestation_2.data.target_root = b'\x01'*32

return AttesterSlashing(
attestation_1=convert_to_indexed(state, attestation_1),
attestation_2=convert_to_indexed(state, attestation_2),
)


def get_valid_attestation(state, slot=None):
if slot is None:
slot = state.slot
Expand Down
35 changes: 35 additions & 0 deletions tests/phase0/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# functions
get_active_validator_indices,
get_balance,
get_beacon_proposer_index,
get_block_root,
get_current_epoch,
get_domain,
Expand All @@ -40,6 +41,7 @@
build_empty_block_for_next_slot,
force_registry_change_at_next_epoch,
get_valid_attestation,
get_valid_attester_slashing,
get_valid_proposer_slashing,
privkeys,
pubkeys,
Expand Down Expand Up @@ -140,6 +142,39 @@ def test_proposer_slashing(state):
return state, [block], test_state


def test_attester_slashing(state):
test_state = deepcopy(state)
attester_slashing = get_valid_attester_slashing(state)
validator_index = attester_slashing.attestation_1.custody_bit_0_indices[0]

#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(test_state)
block.body.attester_slashings.append(attester_slashing)
state_transition(test_state, block)

assert not state.validator_registry[validator_index].initiated_exit
assert not state.validator_registry[validator_index].slashed

slashed_validator = test_state.validator_registry[validator_index]
assert not slashed_validator.initiated_exit
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)

proposer_index = get_beacon_proposer_index(test_state, test_state.slot)
# gained whistleblower reward
assert (
get_balance(test_state, proposer_index) >
get_balance(state, proposer_index)
)

return state, [block], test_state


def test_deposit_in_block(state):
pre_state = deepcopy(state)
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
Expand Down

0 comments on commit 75f0af4

Please sign in to comment.