Add on_attester_slashing() and related test
This commit is contained in:
parent
72d4597131
commit
4fbe162655
|
@ -101,6 +101,7 @@ class Store(object):
|
|||
finalized_checkpoint: Checkpoint
|
||||
best_justified_checkpoint: Checkpoint
|
||||
proposer_boost_root: Root
|
||||
has_equivocated: Set[ValidatorIndex]
|
||||
blocks: Dict[Root, BeaconBlock] = field(default_factory=dict)
|
||||
block_states: Dict[Root, BeaconState] = field(default_factory=dict)
|
||||
checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict)
|
||||
|
@ -129,6 +130,7 @@ def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -
|
|||
finalized_checkpoint=finalized_checkpoint,
|
||||
best_justified_checkpoint=justified_checkpoint,
|
||||
proposer_boost_root=proposer_boost_root,
|
||||
has_equivocated=set(),
|
||||
blocks={anchor_root: copy(anchor_block)},
|
||||
block_states={anchor_root: copy(anchor_state)},
|
||||
checkpoint_states={justified_checkpoint: copy(anchor_state)},
|
||||
|
@ -179,6 +181,7 @@ def get_latest_attesting_balance(store: Store, root: Root) -> Gwei:
|
|||
attestation_score = Gwei(sum(
|
||||
state.validators[i].effective_balance for i in active_indices
|
||||
if (i in store.latest_messages
|
||||
and not i in store.has_equivocated
|
||||
and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
|
||||
))
|
||||
if store.proposer_boost_root == Root():
|
||||
|
@ -358,6 +361,7 @@ def update_latest_messages(store: Store, attesting_indices: Sequence[ValidatorIn
|
|||
target = attestation.data.target
|
||||
beacon_block_root = attestation.data.beacon_block_root
|
||||
for i in attesting_indices:
|
||||
if i not in store.has_equivocated:
|
||||
if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
|
||||
store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=beacon_block_root)
|
||||
```
|
||||
|
@ -459,3 +463,22 @@ def on_attestation(store: Store, attestation: Attestation, is_from_block: bool=F
|
|||
# Update latest messages for attesting indices
|
||||
update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
|
||||
```
|
||||
|
||||
#### `on_attester_slashing`
|
||||
|
||||
```python
|
||||
def on_attester_slashing(store: Store, attester_slashing: AttesterSlashing) -> None:
|
||||
"""
|
||||
Run ``on_attester_slashing`` upon receiving a new ``AttesterSlashing`` from either within a block or directly on the wire.
|
||||
"""
|
||||
attestation_1 = attester_slashing.attestation_1
|
||||
attestation_2 = attester_slashing.attestation_2
|
||||
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
||||
state = store.block_states[store.justified_checkpoint.root]
|
||||
assert is_valid_indexed_attestation(state, attestation_1)
|
||||
assert is_valid_indexed_attestation(state, attestation_2)
|
||||
|
||||
indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
||||
for index in sorted(indices):
|
||||
store.has_equivocated.add(index)
|
||||
```
|
|
@ -24,6 +24,8 @@ from eth2spec.test.helpers.state import (
|
|||
next_epoch,
|
||||
state_transition_and_sign_block,
|
||||
)
|
||||
from tests.core.pyspec.eth2spec.test.helpers.block import apply_empty_block
|
||||
from tests.core.pyspec.eth2spec.test.helpers.fork_choice import run_on_attestation
|
||||
|
||||
|
||||
rng = random.Random(1001)
|
||||
|
@ -338,3 +340,84 @@ def test_proposer_boost_correct_head(spec, state):
|
|||
})
|
||||
|
||||
yield 'steps', test_steps
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_discard_equivocations(spec, state):
|
||||
test_steps = []
|
||||
genesis_state = state.copy()
|
||||
|
||||
# Initialization
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
test_steps.append({
|
||||
'checks': {
|
||||
'head': get_formatted_head_output(spec, store),
|
||||
}
|
||||
})
|
||||
|
||||
# Build block that serves as head before discarding equivocations
|
||||
state_1 = genesis_state.copy()
|
||||
next_slots(spec, state_1, 3)
|
||||
block_1 = build_empty_block_for_next_slot(spec, state_1)
|
||||
signed_block_1 = state_transition_and_sign_block(spec, state_1, block_1)
|
||||
|
||||
# Build equivocating attestations to feed to store
|
||||
state_eqv = state_1.copy()
|
||||
block_eqv = apply_empty_block(spec, state_eqv, state_eqv.slot+1)
|
||||
attestation_eqv = get_valid_attestation(spec, state_eqv, slot=block_eqv.slot, signed=True)
|
||||
|
||||
next_slots(spec, state_1, 1)
|
||||
attestation = get_valid_attestation(spec, state_1, slot=block_eqv.slot, signed=True)
|
||||
assert spec.is_slashable_attestation_data(attestation.data, attestation_eqv.data)
|
||||
|
||||
indexed_attestation = spec.get_indexed_attestation(state_1, attestation)
|
||||
indexed_attestation_eqv = spec.get_indexed_attestation(state_eqv, attestation_eqv)
|
||||
attester_slashing = spec.AttesterSlashing(attestation_1=indexed_attestation, attestation_2=indexed_attestation_eqv)
|
||||
|
||||
# Build block that serves as head after discarding equivocations
|
||||
state_2 = genesis_state.copy()
|
||||
next_slots(spec, state_2, 2)
|
||||
block_2 = build_empty_block_for_next_slot(spec, state_2)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2):
|
||||
block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64))
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
assert spec.hash_tree_root(block_1) < spec.hash_tree_root(block_2)
|
||||
|
||||
# Tick to (block_eqv.slot + 2) slot time
|
||||
time = store.genesis_time + (block_eqv.slot + 2) * spec.config.SECONDS_PER_SLOT
|
||||
on_tick_and_append_step(spec, store, time, test_steps)
|
||||
|
||||
# Process block_2
|
||||
yield from add_block(spec, store, signed_block_2, test_steps)
|
||||
assert store.proposer_boost_root == spec.Root()
|
||||
assert spec.get_head(store) == spec.hash_tree_root(block_2)
|
||||
|
||||
# Process block_1
|
||||
# The head should remain block_2
|
||||
yield from add_block(spec, store, signed_block_1, test_steps)
|
||||
assert store.proposer_boost_root == spec.Root()
|
||||
assert spec.get_head(store) == spec.hash_tree_root(block_2)
|
||||
|
||||
# Process attestation
|
||||
# The head should change to block_1
|
||||
run_on_attestation(spec, store, attestation)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(block_1)
|
||||
|
||||
# Process attester_slashing
|
||||
# The head should revert to block_2
|
||||
spec.on_attester_slashing(store, attester_slashing)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(block_2)
|
||||
|
||||
test_steps.append({
|
||||
'checks': {
|
||||
'head': get_formatted_head_output(spec, store),
|
||||
}
|
||||
})
|
||||
|
||||
yield 'steps', test_steps
|
Loading…
Reference in New Issue