Merge pull request #1477 from ethereum/forkchoice-future-blocks

Forkchoice mods
This commit is contained in:
Danny Ryan 2019-11-12 16:40:32 -07:00 committed by GitHub
commit 1aad31df59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 10 deletions

View File

@ -238,6 +238,12 @@ def on_block(store: Store, block: BeaconBlock) -> None:
```python
def on_attestation(store: Store, attestation: Attestation) -> None:
"""
Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
An ``attestation`` that is asserted as invalid may be valid at a later time,
consider scheduling it for later processing in such case.
"""
target = attestation.data.target
# Attestations must be from the current or previous epoch
@ -248,10 +254,17 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
# Cannot calculate the current shuffling if have not seen the target
assert target.root in store.blocks
# Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
assert target.root in store.blocks
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives
base_state = store.block_states[target.root].copy()
assert store.time >= base_state.genesis_time + compute_start_slot_at_epoch(target.epoch) * SECONDS_PER_SLOT
# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
assert attestation.data.beacon_block_root in store.blocks
# 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
# Store target checkpoint state if not yet seen
if target not in store.checkpoint_states:
process_slots(base_state, compute_start_slot_at_epoch(target.epoch))

View File

@ -1,8 +1,7 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.state import state_transition_and_sign_block
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block
def run_on_attestation(spec, state, store, attestation, valid=True):
@ -89,18 +88,48 @@ def test_on_attestation_past_epoch(spec, state):
@spec_state_test
def test_on_attestation_target_not_in_store(spec, state):
store = spec.get_genesis_store(state)
time = 100
time = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
spec.on_tick(store, time)
# move to next epoch to make block new target
state.slot += spec.SLOTS_PER_EPOCH
# move to immediately before next epoch to make block new target
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
target_block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, target_block)
# do not add block to store
# do not add target block to store
attestation = get_valid_attestation(spec, state, slot=target_block.slot)
assert attestation.data.target.root == target_block.signing_root()
run_on_attestation(spec, state, store, attestation, False)
@with_all_phases
@spec_state_test
def test_on_attestation_beacon_block_not_in_store(spec, state):
store = spec.get_genesis_store(state)
time = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
spec.on_tick(store, time)
# move to immediately before next epoch to make block new target
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
target_block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, target_block)
# store target in store
spec.on_block(store, target_block)
head_block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, head_block)
# do not add head block to store
attestation = get_valid_attestation(spec, state, slot=head_block.slot)
assert attestation.data.target.root == target_block.signing_root()
assert attestation.data.beacon_block_root == head_block.signing_root()
attestation = get_valid_attestation(spec, state, slot=block.slot)
run_on_attestation(spec, state, store, attestation, False)
@ -124,6 +153,26 @@ def test_on_attestation_future_epoch(spec, state):
run_on_attestation(spec, state, store, attestation, False)
@with_all_phases
@spec_state_test
def test_on_attestation_future_block(spec, state):
store = spec.get_genesis_store(state)
time = spec.SECONDS_PER_SLOT * 5
spec.on_tick(store, time)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
spec.on_block(store, block)
# attestation for slot immediately prior to the block being attested to
attestation = get_valid_attestation(spec, state, slot=block.slot - 1, signed=False)
attestation.data.beacon_block_root = block.signing_root()
sign_attestation(spec, state, attestation)
run_on_attestation(spec, state, store, attestation, False)
@with_all_phases
@spec_state_test
def test_on_attestation_same_slot(spec, state):

View File

@ -14,6 +14,16 @@ def next_slot(spec, state):
spec.process_slots(state, state.slot + 1)
def transition_to(spec, state, slot):
"""
Transition to ``slot``.
"""
assert state.slot <= slot
for _ in range(slot - state.slot):
next_slot(spec, state)
assert state.slot == slot
def next_epoch(spec, state):
"""
Transition to the start slot of the next epoch