fix corner case to properly handle skipped slots in get_ancestor

This commit is contained in:
Danny Ryan 2020-01-20 17:40:36 -07:00
parent 2e3fcc16f1
commit f1697d03e7
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
2 changed files with 48 additions and 14 deletions

View File

@ -136,7 +136,8 @@ def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
elif block.slot == slot: elif block.slot == slot:
return root return root
else: else:
return Bytes32() # root is older than queried slot: no results. # root is older than queried slot, thus a skip slot. Return earliest root prior to slot
return root
``` ```
#### `get_latest_attesting_balance` #### `get_latest_attesting_balance`
@ -239,13 +240,8 @@ def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: C
if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED:
return True return True
new_justified_block = store.blocks[new_justified_checkpoint.root] justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
if new_justified_block.slot <= compute_start_slot_at_epoch(store.justified_checkpoint.epoch): if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root:
return False
if not (
get_ancestor(store, new_justified_checkpoint.root, store.blocks[store.justified_checkpoint.root].slot)
== store.justified_checkpoint.root
):
return False return False
return True return True
@ -284,12 +280,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
# Add new block to the store # Add new block to the store
store.blocks[hash_tree_root(block)] = block store.blocks[hash_tree_root(block)] = block
# Check block is a descendant of the finalized block # Check block is a descendant of the finalized block
assert ( finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
get_ancestor(store, hash_tree_root(block), store.blocks[store.finalized_checkpoint.root].slot) == assert get_ancestor(store, hash_tree_root(block), finalized_slot) == store.finalized_checkpoint.root
store.finalized_checkpoint.root
)
# Check that block is later than the finalized epoch slot
assert block.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
# Check the block is valid and compute the post-state # Check the block is valid and compute the post-state
state = state_transition(pre_state, signed_block, True) state = state_transition(pre_state, signed_block, True)
# Add new state for this block to the store # Add new state for this block to the store

View File

@ -135,6 +135,48 @@ def test_on_block_before_finalized(spec, state):
run_on_block(spec, store, signed_block, False) run_on_block(spec, store, signed_block, False)
@with_all_phases
@spec_state_test
def test_on_block_finalized_skip_slots(spec, state):
# Initialization
store = spec.get_genesis_store(state)
time = 100
spec.on_tick(store, time)
store.finalized_checkpoint = spec.Checkpoint(
epoch=store.finalized_checkpoint.epoch + 2,
root=store.finalized_checkpoint.root
)
# Build block that includes the skipped slots up to finality in chain
block = build_empty_block(spec, state, spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + 2)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
run_on_block(spec, store, signed_block)
@with_all_phases
@spec_state_test
def test_on_block_finalized_skip_slots_not_in_skip_chain(spec, state):
# Initialization
store = spec.get_genesis_store(state)
store.finalized_checkpoint = spec.Checkpoint(
epoch=store.finalized_checkpoint.epoch + 2,
root=store.finalized_checkpoint.root
)
# First transition through the epoch to ensure no skipped slots
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
# Now build a block at later slot than finalized epoch
# Includes finalized block in chain, but not at appropriate skip slot
block = build_empty_block(spec, state, spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + 2)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
run_on_block(spec, store, signed_block, False)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state): def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):