def test_finality_rule_2(spec, state): # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) next_epoch_via_block(spec, state) yield 'pre', state blocks = [] for epoch in range(3): if epoch == 0: prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, False) check_finality(spec, state, prev_state, True, False, False) elif epoch == 1: prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, False, False) check_finality(spec, state, prev_state, False, True, False) elif epoch == 2: prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, False, True) # finalized by rule 2 check_finality(spec, state, prev_state, True, False, True) assert state.finalized_checkpoint == prev_state.previous_justified_checkpoint blocks += new_blocks yield 'blocks', blocks yield 'post', state
def test_finality_rule_3(spec, state): """ Test scenario described here https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892 """ # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) next_epoch_via_block(spec, state) yield 'pre', state blocks = [] prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, False) blocks += new_blocks check_finality(spec, state, prev_state, True, False, False) # In epoch N, JE is set to N, prev JE is set to N-1 prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, False) blocks += new_blocks check_finality(spec, state, prev_state, True, True, True) # In epoch N+1, JE is N, prev JE is N-1, and not enough messages get in to do anything prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, False, False) blocks += new_blocks check_finality(spec, state, prev_state, False, True, False) # In epoch N+2, JE is N, prev JE is N, and enough messages from the previous epoch get in to justify N+1. # N+1 now becomes the JE. Not enough messages from epoch N+2 itself get in to justify N+2 prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, False, True) blocks += new_blocks # rule 2 check_finality(spec, state, prev_state, True, False, True) # In epoch N+3, LJE is N+1, prev LJE is N, and enough messages get in to justify epochs N+2 and N+3. prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, True) blocks += new_blocks # rule 3 check_finality(spec, state, prev_state, True, True, True) assert state.finalized_checkpoint == prev_state.current_justified_checkpoint yield 'blocks', blocks yield 'post', state
def apply_next_epoch_with_attestations(spec, state, store): _, new_signed_blocks, post_state = next_epoch_with_attestations(spec, state, True, False) for signed_block in new_signed_blocks: block = signed_block.message block_root = hash_tree_root(block) store.blocks[block_root] = block store.block_states[block_root] = post_state last_signed_block = signed_block spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) return post_state, store, last_signed_block
def test_finality_no_updates_at_genesis(spec, state): assert spec.get_current_epoch(state) == spec.GENESIS_EPOCH yield 'pre', state blocks = [] for epoch in range(2): prev_state, new_blocks, state = next_epoch_with_attestations(spec, state, True, False) blocks += new_blocks # justification/finalization skipped at GENESIS_EPOCH if epoch == 0: check_finality(spec, state, prev_state, False, False, False) # justification/finalization skipped at GENESIS_EPOCH + 1 elif epoch == 1: check_finality(spec, state, prev_state, False, False, False) yield 'blocks', blocks yield 'post', state
def test_finality_rule_4(spec, state): # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) next_epoch_via_block(spec, state) yield 'pre', state blocks = [] for epoch in range(2): prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, False) blocks += new_blocks if epoch == 0: check_finality(spec, state, prev_state, True, False, False) elif epoch == 1: # rule 4 of finality check_finality(spec, state, prev_state, True, True, True) assert state.finalized_checkpoint == prev_state.current_justified_checkpoint yield 'blocks', blocks yield 'post', state
def apply_next_epoch_with_attestations(spec, state, store, fill_cur_epoch, fill_prev_epoch, participation_fn=None, test_steps=None): if test_steps is None: test_steps = [] _, new_signed_blocks, post_state = next_epoch_with_attestations( spec, state, fill_cur_epoch, fill_prev_epoch, participation_fn=participation_fn) for signed_block in new_signed_blocks: block = signed_block.message yield from tick_and_add_block(spec, store, signed_block, test_steps) block_root = block.hash_tree_root() assert store.blocks[block_root] == block last_signed_block = signed_block assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() return post_state, store, last_signed_block
def test_filtered_block_tree(spec, state): test_steps = [] # 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), }}) # transition state past initial couple of epochs next_epoch(spec, state) next_epoch(spec, state) # fill in attestations for entire epoch, justifying the recent epoch prev_state, signed_blocks, state = next_epoch_with_attestations( spec, state, True, False) assert state.current_justified_checkpoint.epoch > prev_state.current_justified_checkpoint.epoch # tick time forward and add blocks and attestations to store current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) for signed_block in signed_blocks: yield from add_block(spec, store, signed_block, test_steps) assert store.justified_checkpoint == state.current_justified_checkpoint # the last block in the branch should be the head expected_head_root = spec.hash_tree_root(signed_blocks[-1].message) assert spec.get_head(store) == expected_head_root test_steps.append({ 'checks': { 'head': get_formatted_head_output(spec, store), 'justified_checkpoint_root': encode_hex(store.justified_checkpoint.root), } }) # # create branch containing the justified block but not containing enough on # chain votes to justify that block # # build a chain without attestations off of previous justified block non_viable_state = store.block_states[ store.justified_checkpoint.root].copy() # ensure that next wave of votes are for future epoch next_epoch(spec, non_viable_state) next_epoch(spec, non_viable_state) next_epoch(spec, non_viable_state) assert spec.get_current_epoch( non_viable_state) > store.justified_checkpoint.epoch # create rogue block that will be attested to in this non-viable branch rogue_block = build_empty_block_for_next_slot(spec, non_viable_state) signed_rogue_block = state_transition_and_sign_block( spec, non_viable_state, rogue_block) # create an epoch's worth of attestations for the rogue block next_epoch(spec, non_viable_state) attestations = [] for i in range(spec.SLOTS_PER_EPOCH): slot = rogue_block.slot + i for index in range( spec.get_committee_count_per_slot( non_viable_state, spec.compute_epoch_at_slot(slot))): attestation = get_valid_attestation(spec, non_viable_state, slot, index, signed=True) attestations.append(attestation) # tick time forward to be able to include up to the latest attestation current_time = (attestations[-1].data.slot + 1) * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) # include rogue block and associated attestations in the store yield from add_block(spec, store, signed_rogue_block, test_steps) for attestation in attestations: yield from tick_and_run_on_attestation(spec, store, attestation, test_steps) # ensure that get_head still returns the head from the previous branch assert spec.get_head(store) == expected_head_root test_steps.append( {'checks': { 'head': get_formatted_head_output(spec, store) }}) yield 'steps', test_steps
def test_new_finalized_slot_is_justified_checkpoint_ancestor(spec, state): """ J: Justified F: Finalized state: epoch [0] <- [1] <- [2] <- [3] <- [4] <- [5] F J another_state (forked from state at epoch 3): └──── [4] <- [5] F J """ test_steps = [] # Initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time # Process state next_epoch(spec, state) state, store, _ = yield from apply_next_epoch_with_attestations( spec, state, store, False, True, test_steps=test_steps) state, store, _ = yield from apply_next_epoch_with_attestations( spec, state, store, True, False, test_steps=test_steps) next_epoch(spec, state) for _ in range(2): state, store, _ = yield from apply_next_epoch_with_attestations( spec, state, store, False, True, test_steps=test_steps) assert state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 2 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 4 assert store.justified_checkpoint == state.current_justified_checkpoint # Create another chain # Forking from epoch 3 all_blocks = [] slot = spec.compute_start_slot_at_epoch(3) block_root = spec.get_block_root_at_slot(state, slot) another_state = store.block_states[block_root].copy() for _ in range(2): _, signed_blocks, another_state = next_epoch_with_attestations( spec, another_state, True, True) all_blocks += signed_blocks assert another_state.finalized_checkpoint.epoch == 3 assert another_state.current_justified_checkpoint.epoch == 4 pre_store_justified_checkpoint_root = store.justified_checkpoint.root for block in all_blocks: yield from tick_and_add_block(spec, store, block, test_steps) finalized_slot = spec.compute_start_slot_at_epoch( store.finalized_checkpoint.epoch) ancestor_at_finalized_slot = spec.get_ancestor( store, pre_store_justified_checkpoint_root, finalized_slot) assert ancestor_at_finalized_slot == store.finalized_checkpoint.root assert store.finalized_checkpoint == another_state.finalized_checkpoint assert store.justified_checkpoint == another_state.current_justified_checkpoint yield 'steps', test_steps
def test_new_finalized_slot_is_not_justified_checkpoint_ancestor(spec, state): """ J: Justified F: Finalized state (forked from genesis): epoch [0] <- [1] <- [2] <- [3] <- [4] <- [5] F J another_state (forked from epoch 0): └──── [1] <- [2] <- [3] <- [4] <- [5] F J """ test_steps = [] # Initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time # ----- Process state # Goal: make `store.finalized_checkpoint.epoch == 0` and `store.justified_checkpoint.epoch == 3` # Skip epoch 0 next_epoch(spec, state) # Forking another_state another_state = state.copy() # Fill epoch 1 with previous epoch attestations state, store, _ = yield from apply_next_epoch_with_attestations( spec, state, store, False, True, test_steps=test_steps) # Skip epoch 2 next_epoch(spec, state) # Fill epoch 3 & 4 with previous epoch attestations for _ in range(2): state, store, _ = yield from apply_next_epoch_with_attestations( spec, state, store, False, True, test_steps=test_steps) assert state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 0 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert store.justified_checkpoint == state.current_justified_checkpoint # Create another chain # Goal: make `another_state.finalized_checkpoint.epoch == 2` and `another_state.justified_checkpoint.epoch == 3` all_blocks = [] # Fill epoch 1 & 2 with previous + current epoch attestations for _ in range(3): _, signed_blocks, another_state = next_epoch_with_attestations( spec, another_state, True, True) all_blocks += signed_blocks assert another_state.finalized_checkpoint.epoch == 2 assert another_state.current_justified_checkpoint.epoch == 3 assert state.finalized_checkpoint != another_state.finalized_checkpoint assert state.current_justified_checkpoint != another_state.current_justified_checkpoint pre_store_justified_checkpoint_root = store.justified_checkpoint.root # Apply blocks of `another_state` to `store` for block in all_blocks: # NOTE: Do not call `on_tick` here yield from add_block(spec, store, block, test_steps) finalized_slot = spec.compute_start_slot_at_epoch( store.finalized_checkpoint.epoch) ancestor_at_finalized_slot = spec.get_ancestor( store, pre_store_justified_checkpoint_root, finalized_slot) assert ancestor_at_finalized_slot != store.finalized_checkpoint.root assert store.finalized_checkpoint == another_state.finalized_checkpoint assert store.justified_checkpoint == another_state.current_justified_checkpoint yield 'steps', test_steps
def test_new_justified_is_later_than_store_justified(spec, state): """ J: Justified F: Finalized fork_1_state (forked from genesis): epoch [0] <- [1] <- [2] <- [3] <- [4] F J fork_2_state (forked from fork_1_state's epoch 2): epoch └──── [3] <- [4] <- [5] <- [6] F J fork_3_state (forked from genesis): [0] <- [1] <- [2] <- [3] <- [4] <- [5] F J """ # The 1st fork, from genesis fork_1_state = state.copy() # The 3rd fork, from genesis fork_3_state = state.copy() test_steps = [] # Initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time # ----- Process fork_1_state # Skip epoch 0 next_epoch(spec, fork_1_state) # Fill epoch 1 with previous epoch attestations fork_1_state, store, _ = yield from apply_next_epoch_with_attestations( spec, fork_1_state, store, False, True, test_steps=test_steps) # Fork `fork_2_state` at the start of epoch 2 fork_2_state = fork_1_state.copy() assert spec.get_current_epoch(fork_2_state) == 2 # Skip epoch 2 next_epoch(spec, fork_1_state) # # Fill epoch 3 & 4 with previous epoch attestations for _ in range(2): fork_1_state, store, _ = yield from apply_next_epoch_with_attestations( spec, fork_1_state, store, False, True, test_steps=test_steps) assert fork_1_state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 0 assert fork_1_state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert store.justified_checkpoint == fork_1_state.current_justified_checkpoint # ------ fork_2_state: Create a chain to set store.best_justified_checkpoint # NOTE: The goal is to make `store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch` all_blocks = [] # Proposed an empty block at epoch 2, 1st slot block = build_empty_block_for_next_slot(spec, fork_2_state) signed_block = state_transition_and_sign_block(spec, fork_2_state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Skip to epoch 4 for _ in range(2): next_epoch(spec, fork_2_state) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Propose a block at epoch 4, 5th slot # Propose a block at epoch 5, 5th slot for _ in range(2): next_epoch(spec, fork_2_state) next_slots(spec, fork_2_state, 4) signed_block = state_transition_with_full_attestations_block( spec, fork_2_state, True, True) yield from tick_and_add_block(spec, store, signed_block, test_steps) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Propose a block at epoch 6, SAFE_SLOTS_TO_UPDATE_JUSTIFIED + 2 slot next_epoch(spec, fork_2_state) next_slots(spec, fork_2_state, spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED + 2) signed_block = state_transition_with_full_attestations_block( spec, fork_2_state, True, True) assert fork_2_state.finalized_checkpoint.epoch == 0 assert fork_2_state.current_justified_checkpoint.epoch == 5 # Check SAFE_SLOTS_TO_UPDATE_JUSTIFIED time = store.genesis_time + fork_2_state.slot * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) assert spec.compute_slots_since_epoch_start( spec.get_current_slot(store)) >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED # Run on_block yield from add_block(spec, store, signed_block, test_steps) assert store.finalized_checkpoint.epoch == 0 assert store.justified_checkpoint.epoch == 3 assert store.best_justified_checkpoint.epoch == 5 # ------ fork_3_state: Create another chain to test the # "Update justified if new justified is later than store justified" case all_blocks = [] for _ in range(3): next_epoch(spec, fork_3_state) # epoch 3 _, signed_blocks, fork_3_state = next_epoch_with_attestations( spec, fork_3_state, True, True) all_blocks += signed_blocks assert fork_3_state.finalized_checkpoint.epoch == 0 # epoch 4, attest the first 5 blocks _, blocks, fork_3_state = next_slots_with_attestations( spec, fork_3_state, 5, True, True) all_blocks += blocks.copy() assert fork_3_state.finalized_checkpoint.epoch == 0 # Propose a block at epoch 5, 5th slot next_epoch(spec, fork_3_state) next_slots(spec, fork_3_state, 4) signed_block = state_transition_with_full_block(spec, fork_3_state, True, True) all_blocks.append(signed_block.copy()) assert fork_3_state.finalized_checkpoint.epoch == 0 # Propose a block at epoch 6, 5th slot next_epoch(spec, fork_3_state) next_slots(spec, fork_3_state, 4) signed_block = state_transition_with_full_block(spec, fork_3_state, True, True) all_blocks.append(signed_block.copy()) assert fork_3_state.finalized_checkpoint.epoch == 3 assert fork_3_state.current_justified_checkpoint.epoch == 4 # Apply blocks of `fork_3_state` to `store` for block in all_blocks: if store.time < spec.compute_time_at_slot(fork_2_state, block.message.slot): time = store.genesis_time + block.message.slot * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, block, test_steps) assert store.finalized_checkpoint == fork_3_state.finalized_checkpoint assert store.justified_checkpoint == fork_3_state.current_justified_checkpoint assert store.justified_checkpoint != store.best_justified_checkpoint assert store.best_justified_checkpoint == fork_2_state.current_justified_checkpoint yield 'steps', test_steps
def test_process_light_client_update_finality_updated(spec, state): pre_snapshot = spec.LightClientSnapshot( header=spec.BeaconBlockHeader(), current_sync_committee=state.current_sync_committee, next_sync_committee=state.next_sync_committee, ) store = spec.LightClientStore(snapshot=pre_snapshot, valid_updates=[]) # Change finality blocks = [] next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2) for epoch in range(3): prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, True) blocks += new_blocks # Ensure that finality checkpoint has changed assert state.finalized_checkpoint.epoch == 3 # Ensure that it's same period snapshot_period = spec.compute_epoch_at_slot( pre_snapshot.header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD update_period = spec.compute_epoch_at_slot( state.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD assert snapshot_period == update_period # Updated sync_committee and finality next_sync_committee_branch = [ spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX)) ] finalized_block_header = blocks[spec.SLOTS_PER_EPOCH - 1].message assert finalized_block_header.slot == spec.compute_start_slot_at_epoch( state.finalized_checkpoint.epoch) assert finalized_block_header.hash_tree_root( ) == state.finalized_checkpoint.root finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) # Build block header block = build_empty_block(spec, state) block_header = spec.BeaconBlockHeader( slot=block.slot, proposer_index=block.proposer_index, parent_root=block.parent_root, state_root=state.hash_tree_root(), body_root=block.body.hash_tree_root(), ) # Sync committee signing the finalized_block_header committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state)) sync_committee_bits = [True] * len(committee) sync_committee_signature = compute_aggregate_sync_committee_signature( spec, state, block_header.slot, committee, block_root=spec.Root(block_header.hash_tree_root()), ) update = spec.LightClientUpdate( header=finalized_block_header, next_sync_committee=state.next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finality_header=block_header, # block_header is the signed header finality_branch=finality_branch, sync_committee_bits=sync_committee_bits, sync_committee_signature=sync_committee_signature, fork_version=state.fork.current_version, ) spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root) # snapshot has been updated assert len(store.valid_updates) == 0 assert store.snapshot.header == update.header
def test_process_light_client_update_finality_updated(spec, state): store = initialize_light_client_store(spec, state) # Change finality blocks = [] next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2) for epoch in range(3): prev_state, new_blocks, state = next_epoch_with_attestations( spec, state, True, True) blocks += new_blocks # Ensure that finality checkpoint has changed assert state.finalized_checkpoint.epoch == 3 # Ensure that it's same period snapshot_period = spec.compute_sync_committee_period( spec.compute_epoch_at_slot(store.optimistic_header.slot)) update_period = spec.compute_sync_committee_period( spec.compute_epoch_at_slot(state.slot)) assert snapshot_period == update_period # Updated sync_committee and finality next_sync_committee_branch = [ spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX)) ] finalized_block_header = blocks[spec.SLOTS_PER_EPOCH - 1].message assert finalized_block_header.slot == spec.compute_start_slot_at_epoch( state.finalized_checkpoint.epoch) assert finalized_block_header.hash_tree_root( ) == state.finalized_checkpoint.root finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) # Build block header block = build_empty_block(spec, state) block_header = spec.BeaconBlockHeader( slot=block.slot, proposer_index=block.proposer_index, parent_root=block.parent_root, state_root=state.hash_tree_root(), body_root=block.body.hash_tree_root(), ) # Sync committee signing the finalized_block_header sync_aggregate = get_sync_aggregate(spec, state, block_header, block_root=spec.Root( block_header.hash_tree_root())) update = spec.LightClientUpdate( attested_header=block_header, next_sync_committee=state.next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finalized_header=finalized_block_header, finality_branch=finality_branch, sync_aggregate=sync_aggregate, fork_version=state.fork.current_version, ) spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root) assert store.current_max_active_participants > 0 assert store.optimistic_header == update.attested_header assert store.finalized_header == update.finalized_header assert store.best_valid_update is None