5.5 KiB
5.5 KiB
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
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
Helpers
ShardStore
@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
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)},
block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]},
)
get_shard_latest_attesting_balance
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
and store.latest_messages[i].shard == shard_store.shard
and get_shard_ancestor(
store, shard_store, store.latest_messages[i].root, shard_store.blocks[root].slot
) == root
)
))
get_shard_head
def get_shard_head(store: Store, shard_store: ShardStore) -> Root:
# Execute the LMD-GHOST fork choice
shard_blocks = shard_store.blocks
head_beacon_root = get_head(store)
head_shard_state = store.block_states[head_beacon_root].shard_states[shard_store.shard]
shard_head_root = head_shard_state.latest_block_root
while True:
# Find the valid child block roots
children = [
root for root in shard_store.blocks.keys()
if (
shard_blocks[root].shard_parent_root == shard_head_root
and shard_blocks[root].slot > head_shard_state.slot
)
]
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
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 earliest root prior to slot
return root
Handlers
on_shard_block
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
pre_shard_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_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
)
# Add new block to the store
shard_store.blocks[hash_tree_root(shard_block)] = shard_block
# Check the block is valid and compute the post-state
verify_shard_block_message(beacon_state, pre_shard_state, shard_block, shard_block.slot, shard)
verify_shard_block_signature(beacon_state, signed_shard_block)
post_state = get_post_shard_state(beacon_state, pre_shard_state, shard_block)
# Add new state for this block to the store
shard_store.block_states[hash_tree_root(shard_block)] = post_state