diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md deleted file mode 100644 index 177c9c18c..000000000 --- a/specs/phase1/shard-fork-choice.md +++ /dev/null @@ -1,178 +0,0 @@ -# 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) - - [`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 - -#### `get_forkchoice_shard_store` - -```python -def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> ShardStore: - return ShardStore( - shard=shard, - signed_blocks={ - anchor_state.shard_states[shard].latest_block_root: SignedShardBlock( - message=ShardBlock(slot=compute_previous_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: Shard, root: Root) -> Gwei: - shard_store = store.shard_stores[shard] - 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 shard_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 get_shard_ancestor( - store, - shard, - shard_store.latest_messages[i].root, - shard_store.signed_blocks[root].message.slot, - ) == root - ) - )) -``` - -#### `get_shard_head` - -```python -def get_shard_head(store: Store, shard: Shard) -> Root: - # Execute the LMD-GHOST fork choice - """ - Execute the LMD-GHOST fork choice. - """ - shard_store = store.shard_stores[shard] - beacon_head_root = get_head(store) - shard_head_state = store.block_states[beacon_head_root].shard_states[shard] - shard_head_root = shard_head_state.latest_block_root - shard_blocks = { - root: signed_shard_block.message for root, signed_shard_block in shard_store.signed_blocks.items() - if signed_shard_block.message.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, root), root) - ) -``` - -#### `get_shard_ancestor` - -```python -def get_shard_ancestor(store: Store, shard: Shard, root: Root, slot: Slot) -> Root: - shard_store = store.shard_stores[shard] - block = shard_store.signed_blocks[root].message - if block.slot > slot: - return get_shard_ancestor(store, shard, 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: Shard) -> Sequence[SignedShardBlock]: - """ - Return the canonical shard block branch that has not yet been crosslinked. - """ - shard_store = store.shard_stores[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) - root = shard_head_root - signed_shard_blocks = [] - while root != latest_shard_block_root: - signed_shard_block = shard_store.signed_blocks[root] - signed_shard_blocks.append(signed_shard_block) - root = signed_shard_block.message.shard_parent_root - - signed_shard_blocks.reverse() - return signed_shard_blocks -``` - -### Handlers - -#### `on_shard_block` - -```python -def on_shard_block(store: Store, signed_shard_block: SignedShardBlock) -> None: - shard_block = signed_shard_block.message - shard = shard_block.shard - shard_store = store.shard_stores[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 - shard_state = shard_parent_state.copy() - shard_state_transition(shard_state, signed_shard_block, beacon_parent_state, validate_result=True) - - # Add new block to the store - # Note: storing `SignedShardBlock` format for computing `ShardTransition.proposer_signature_aggregate` - shard_store.signed_blocks[hash_tree_root(shard_block)] = signed_shard_block - - # Add new state for this block to the store - shard_store.block_states[hash_tree_root(shard_block)] = shard_state -``` diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md deleted file mode 100644 index 35d421cdd..000000000 --- a/specs/phase1/shard-transition.md +++ /dev/null @@ -1,145 +0,0 @@ -# Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs - -**Notice**: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - - -- [Introduction](#introduction) -- [Helper functions](#helper-functions) - - [Shard block verification functions](#shard-block-verification-functions) - - [`verify_shard_block_message`](#verify_shard_block_message) - - [`verify_shard_block_signature`](#verify_shard_block_signature) -- [Shard state transition function](#shard-state-transition-function) -- [Fraud proofs](#fraud-proofs) - - [Verifying the proof](#verifying-the-proof) - - - -## Introduction - -This document describes the shard transition function and fraud proofs as part of Phase 1 of Ethereum 2.0. - -## Helper functions - -### Shard block verification functions - -#### `verify_shard_block_message` - -```python -def verify_shard_block_message(beacon_parent_state: BeaconState, - shard_parent_state: ShardState, - block: ShardBlock) -> bool: - # Check `shard_parent_root` field - assert block.shard_parent_root == shard_parent_state.latest_block_root - # Check `beacon_parent_root` field - beacon_parent_block_header = beacon_parent_state.latest_block_header.copy() - if beacon_parent_block_header.state_root == Root(): - beacon_parent_block_header.state_root = hash_tree_root(beacon_parent_state) - beacon_parent_root = hash_tree_root(beacon_parent_block_header) - assert block.beacon_parent_root == beacon_parent_root - # Check `slot` field - shard = block.shard - next_slot = Slot(block.slot + 1) - offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_parent_state, shard), next_slot) - assert block.slot in offset_slots - # Check `proposer_index` field - assert block.proposer_index == get_shard_proposer_index(beacon_parent_state, block.slot, shard) - # Check `body` field - assert 0 < len(block.body) <= MAX_SHARD_BLOCK_SIZE - return True -``` - -#### `verify_shard_block_signature` - -```python -def verify_shard_block_signature(beacon_parent_state: BeaconState, - signed_block: SignedShardBlock) -> bool: - proposer = beacon_parent_state.validators[signed_block.message.proposer_index] - domain = get_domain(beacon_parent_state, DOMAIN_SHARD_PROPOSAL, compute_epoch_at_slot(signed_block.message.slot)) - signing_root = compute_signing_root(signed_block.message, domain) - return bls.Verify(proposer.pubkey, signing_root, signed_block.signature) -``` - -## Shard state transition function - -The post-state corresponding to a pre-state `shard_state` and a signed block `signed_block` is defined as `shard_state_transition(shard_state, signed_block, beacon_parent_state)`, where `beacon_parent_state` is the parent beacon state of the `signed_block`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. State transitions that cause a `uint64` overflow or underflow are also considered invalid. - -```python -def shard_state_transition(shard_state: ShardState, - signed_block: SignedShardBlock, - beacon_parent_state: BeaconState, - validate_result: bool = True) -> None: - assert verify_shard_block_message(beacon_parent_state, shard_state, signed_block.message) - - if validate_result: - assert verify_shard_block_signature(beacon_parent_state, signed_block) - - process_shard_block(shard_state, signed_block.message) -``` - -```python -def process_shard_block(shard_state: ShardState, - block: ShardBlock) -> None: - """ - Update ``shard_state`` with shard ``block``. - """ - shard_state.slot = block.slot - prev_gasprice = shard_state.gasprice - shard_block_length = len(block.body) - shard_state.gasprice = compute_updated_gasprice(prev_gasprice, uint64(shard_block_length)) - if shard_block_length != 0: - shard_state.latest_block_root = hash_tree_root(block) -``` - -## Fraud proofs - -### Verifying the proof - -TODO. The intent is to have a single universal fraud proof type, which contains the following parts: - -1. An on-time attestation `attestation` on some shard `shard` signing a `transition: ShardTransition` -2. An index `offset_index` of a particular position to focus on -3. The `transition: ShardTransition` itself -4. The full body of the shard block `shard_block` -5. A Merkle proof to the `shard_states` in the parent block the attestation is referencing -6. The `subkey` to generate the custody bit - -Call the following function to verify the proof: - -```python -def is_valid_fraud_proof(beacon_state: BeaconState, - attestation: Attestation, - offset_index: uint64, - transition: ShardTransition, - block: ShardBlock, - subkey: BLSPubkey, - beacon_parent_block: BeaconBlock) -> bool: - # 1. Check if `custody_bits[offset_index][j] != generate_custody_bit(subkey, block_contents)` for any `j`. - custody_bits = attestation.custody_bits_blocks - for j in range(len(custody_bits[offset_index])): - if custody_bits[offset_index][j] != generate_custody_bit(subkey, block): - return True - - # 2. Check if the shard state transition result is wrong between - # `transition.shard_states[offset_index - 1]` to `transition.shard_states[offset_index]`. - if offset_index == 0: - shard_states = beacon_parent_block.body.shard_transitions[attestation.data.shard].shard_states - shard_state = shard_states[len(shard_states) - 1] - else: - shard_state = transition.shard_states[offset_index - 1] # Not doing the actual state updates here. - - process_shard_block(shard_state, block) - if shard_state != transition.shard_states[offset_index]: - return True - - return False -``` - -```python -def generate_custody_bit(subkey: BLSPubkey, block: ShardBlock) -> bool: - # TODO - ... -```