# Ethereum 2.0 Phase 1 -- Beacon Chain + Shard Chain Fork Choice **Notice**: This document is a work-in-progress for researchers and implementers. ## Table of contents - [Introduction](#introduction) - [Fork choice](#fork-choice) - [Helpers](#helpers) - [`ShardStore`](#shardstore) - [`get_forkchoice_shard_store`](#get_forkchoice_shard_store) - [`get_shard_latest_attesting_balance`](#get_shard_latest_attesting_balance) - [`get_shard_head`](#get_shard_head) - [`get_shard_ancestor`](#get_shard_ancestor) - [`get_pending_shard_blocks`](#get_pending_shard_blocks) - [Handlers](#handlers) - [`on_shard_block`](#on_shard_block) ## Introduction This document is the shard chain fork choice spec for part of Ethereum 2.0 Phase 1. It assumes the [beacon chain fork choice spec](./fork-choice.md). ## Fork choice ### Helpers #### `ShardStore` ```python @dataclass class ShardStore: shard: Shard blocks: Dict[Root, ShardBlock] = field(default_factory=dict) block_states: Dict[Root, ShardState] = field(default_factory=dict) ``` #### `get_forkchoice_shard_store` ```python def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> ShardStore: return ShardStore( shard=shard, blocks={anchor_state.shard_states[shard].latest_block_root: ShardBlock(slot=anchor_state.slot, shard=shard)}, block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]}, ) ``` #### `get_shard_latest_attesting_balance` ```python def get_shard_latest_attesting_balance(store: Store, shard_store: ShardStore, root: Root) -> Gwei: state = store.checkpoint_states[store.justified_checkpoint] active_indices = get_active_validator_indices(state, get_current_epoch(state)) return Gwei(sum( state.validators[i].effective_balance for i in active_indices if ( i in store.latest_messages # TODO: check the latest message logic: currently, validator's previous vote of another shard # would be ignored once their newer vote is accepted. Check if it makes sense. and store.latest_messages[i].shard == shard_store.shard and get_shard_ancestor( store, shard_store, store.latest_messages[i].shard_root, shard_store.blocks[root].slot ) == root ) )) ``` #### `get_shard_head` ```python def get_shard_head(store: Store, shard_store: ShardStore) -> Root: # Execute the LMD-GHOST fork choice beacon_head_root = get_head(store) shard_head_state = store.block_states[beacon_head_root].shard_states[shard_store.shard] shard_head_root = shard_head_state.latest_block_root shard_blocks = { root: shard_block for root, shard_block in shard_store.blocks.items() if shard_block.slot > shard_head_state.slot } while True: # Find the valid child block roots children = [ root for root, shard_block in shard_blocks.items() if shard_block.shard_parent_root == shard_head_root ] if len(children) == 0: return shard_head_root # Sort by latest attesting balance with ties broken lexicographically shard_head_root = max( children, key=lambda root: (get_shard_latest_attesting_balance(store, shard_store, root), root) ) ``` #### `get_shard_ancestor` ```python def get_shard_ancestor(store: Store, shard_store: ShardStore, root: Root, slot: Slot) -> Root: block = shard_store.blocks[root] if block.slot > slot: return get_shard_ancestor(store, shard_store, block.shard_parent_root, slot) elif block.slot == slot: return root else: # root is older than queried slot, thus a skip slot. Return most recent root prior to slot return root ``` #### `get_pending_shard_blocks` ```python def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[ShardBlock]: """ Return the canonical shard block branch that has not yet been crosslinked. """ shard = shard_store.shard beacon_head_root = get_head(store) beacon_head_state = store.block_states[beacon_head_root] latest_shard_block_root = beacon_head_state.shard_states[shard].latest_block_root shard_head_root = get_shard_head(store, shard_store) root = shard_head_root shard_blocks = [] while root != latest_shard_block_root: shard_block = shard_store.blocks[root] shard_blocks.append(shard_block) root = shard_block.shard_parent_root shard_blocks.reverse() return shard_blocks ``` ### Handlers #### `on_shard_block` ```python def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: SignedShardBlock) -> None: shard_block = signed_shard_block.message shard = shard_store.shard # Check shard # TODO: check it in networking spec assert shard_block.shard == shard # Check shard parent exists assert shard_block.shard_parent_root in shard_store.block_states shard_parent_state = shard_store.block_states[shard_block.shard_parent_root] # Check beacon parent exists assert shard_block.beacon_parent_root in store.block_states beacon_parent_state = store.block_states[shard_block.beacon_parent_root] # Check that block is later than the finalized shard state slot (optimization to reduce calls to get_ancestor) finalized_beacon_state = store.block_states[store.finalized_checkpoint.root] finalized_shard_state = finalized_beacon_state.shard_states[shard] assert shard_block.slot > finalized_shard_state.slot # Check block is a descendant of the finalized block at the checkpoint finalized slot finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) assert ( get_ancestor(store, shard_block.beacon_parent_root, finalized_slot) == store.finalized_checkpoint.root ) # Check the block is valid and compute the post-state assert verify_shard_block_message(beacon_parent_state, shard_parent_state, shard_block) assert verify_shard_block_signature(beacon_parent_state, signed_shard_block) post_state = get_post_shard_state(shard_parent_state, shard_block) # Add new block to the store shard_store.blocks[hash_tree_root(shard_block)] = shard_block # Add new state for this block to the store shard_store.block_states[hash_tree_root(shard_block)] = post_state ```