Merge pull request #1742 from ethereum/ffg_lmd_vote_consistency
Added FFG-LMD vote consistency checks
This commit is contained in:
commit
7625728fb8
|
@ -162,7 +162,7 @@ def get_latest_attesting_balance(store: Store, root: Root) -> Gwei:
|
||||||
active_indices = get_active_validator_indices(state, get_current_epoch(state))
|
active_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||||
return Gwei(sum(
|
return Gwei(sum(
|
||||||
state.validators[i].effective_balance for i in active_indices
|
state.validators[i].effective_balance for i in active_indices
|
||||||
if (i in store.latest_messages
|
if (i in store.latest_messages
|
||||||
and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
|
and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
|
||||||
))
|
))
|
||||||
```
|
```
|
||||||
|
@ -285,6 +285,10 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None:
|
||||||
# Attestations must not be for blocks in the future. If not, the attestation should not be considered
|
# Attestations must not be for blocks in the future. If not, the attestation should not be considered
|
||||||
assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
|
assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
|
||||||
|
|
||||||
|
# FFG and LMD vote must be consistent with each other
|
||||||
|
target_slot = compute_start_slot_at_epoch(target.epoch)
|
||||||
|
assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot)
|
||||||
|
|
||||||
# Attestations can only affect the fork choice of subsequent slots.
|
# Attestations can only affect the fork choice of subsequent slots.
|
||||||
# Delay consideration in the fork choice until their slot is in the past.
|
# Delay consideration in the fork choice until their slot is in the past.
|
||||||
assert get_current_slot(store) >= attestation.data.slot + 1
|
assert get_current_slot(store) >= attestation.data.slot + 1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from eth2spec.test.context import PHASE0, with_all_phases, spec_state_test
|
from eth2spec.test.context import PHASE0, with_all_phases, spec_state_test
|
||||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||||
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
|
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
|
||||||
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch
|
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot
|
||||||
|
|
||||||
|
|
||||||
def run_on_attestation(spec, state, store, attestation, valid=True):
|
def run_on_attestation(spec, state, store, attestation, valid=True):
|
||||||
|
@ -116,6 +116,44 @@ def test_on_attestation_mismatched_target_and_slot(spec, state):
|
||||||
run_on_attestation(spec, state, store, attestation, False)
|
run_on_attestation(spec, state, store, attestation, False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_on_attestation_inconsistent_target_and_head(spec, state):
|
||||||
|
store = spec.get_forkchoice_store(state)
|
||||||
|
spec.on_tick(store, store.time + 2 * spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||||
|
|
||||||
|
# Create chain 1 as empty chain between genesis and start of 1st epoch
|
||||||
|
target_state_1 = state.copy()
|
||||||
|
next_epoch(spec, target_state_1)
|
||||||
|
|
||||||
|
# Create chain 2 with different block in chain from chain 1 from chain 1 from chain 1 from chain 1
|
||||||
|
target_state_2 = state.copy()
|
||||||
|
diff_block = build_empty_block_for_next_slot(spec, target_state_2)
|
||||||
|
signed_diff_block = state_transition_and_sign_block(spec, target_state_2, diff_block)
|
||||||
|
spec.on_block(store, signed_diff_block)
|
||||||
|
next_epoch(spec, target_state_2)
|
||||||
|
next_slot(spec, target_state_2)
|
||||||
|
|
||||||
|
# Create and store block new head block on target state 1
|
||||||
|
head_block = build_empty_block_for_next_slot(spec, target_state_1)
|
||||||
|
signed_head_block = state_transition_and_sign_block(spec, target_state_1, head_block)
|
||||||
|
spec.on_block(store, signed_head_block)
|
||||||
|
|
||||||
|
# Attest to head of chain 1
|
||||||
|
attestation = get_valid_attestation(spec, target_state_1, slot=head_block.slot, signed=False)
|
||||||
|
epoch = spec.compute_epoch_at_slot(attestation.data.slot)
|
||||||
|
|
||||||
|
# Set attestation target to be from chain 2
|
||||||
|
attestation.data.target = spec.Checkpoint(epoch=epoch, root=spec.get_block_root(target_state_2, epoch))
|
||||||
|
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 + 1
|
||||||
|
assert spec.get_block_root(target_state_1, epoch) != attestation.data.target.root
|
||||||
|
|
||||||
|
run_on_attestation(spec, state, store, attestation, False)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_on_attestation_target_not_in_store(spec, state):
|
def test_on_attestation_target_not_in_store(spec, state):
|
||||||
|
|
Loading…
Reference in New Issue