Skip to content

EIP-8025 -- Fork Choice

Note: This document is a work-in-progress for researchers and implementers.

Table of contents

Introduction

This document extends the fork-choice Store to retain verified ExecutionProofEnvelopes. Stored proofs are not fork-choice inputs.

Note: This specification is built upon Gloas.

Data structures

Modified Store

@dataclass
class Store:
    time: Uint64
    genesis_time: Uint64
    justified_checkpoint: Checkpoint
    finalized_checkpoint: Checkpoint
    unrealized_justified_checkpoint: Checkpoint
    unrealized_finalized_checkpoint: Checkpoint
    proposer_boost_root: Root
    equivocating_indices: Set[ValidatorIndex]
    blocks: Dict[Root, BeaconBlock]
    block_states: Dict[Root, BeaconState]
    block_timeliness: Dict[Root, list[bool]]
    checkpoint_states: Dict[Checkpoint, BeaconState]
    latest_messages: Dict[ValidatorIndex, LatestMessage]
    unrealized_justifications: Dict[Root, Checkpoint]
    payloads: Dict[Root, ExecutionPayloadEnvelope]
    payload_timeliness_vote: Dict[Root, list[Optional[Boolean]]]
    payload_data_availability_vote: Dict[Root, list[Optional[Boolean]]]
    # [New in EIP8025]
    execution_proofs: Dict[Root, Dict[ProofType, ExecutionProofEnvelope]]

Store initialization

Modified get_forkchoice_store

def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -> Store:
    assert anchor_block.state_root == hash_tree_root(anchor_state)
    anchor_root = hash_tree_root(anchor_block)
    anchor_epoch = get_current_epoch(anchor_state)
    justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
    finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
    proposer_boost_root = Root()
    return Store(
        time=Uint64(anchor_state.genesis_time + SLOT_DURATION_MS * anchor_state.slot // 1000),
        genesis_time=anchor_state.genesis_time,
        justified_checkpoint=justified_checkpoint,
        finalized_checkpoint=finalized_checkpoint,
        unrealized_justified_checkpoint=justified_checkpoint,
        unrealized_finalized_checkpoint=finalized_checkpoint,
        proposer_boost_root=proposer_boost_root,
        equivocating_indices=set(),
        blocks={anchor_root: anchor_block.copy()},
        block_states={anchor_root: anchor_state.copy()},
        block_timeliness={anchor_root: [True, True]},
        checkpoint_states={justified_checkpoint: anchor_state.copy()},
        latest_messages={},
        unrealized_justifications={anchor_root: justified_checkpoint},
        payloads={},
        payload_timeliness_vote={anchor_root: [None] * PTC_SIZE},
        payload_data_availability_vote={anchor_root: [None] * PTC_SIZE},
        # [New in EIP8025]
        execution_proofs={},
    )

Handlers

New on_execution_proof

The handler on_execution_proof is called when the node receives a SignedExecutionProofEnvelope for downstream processing. It verifies and stores the proof envelope without changing fork-choice weights, head selection, beacon-chain state, or Gloas payload status.

def on_execution_proof(
    store: Store,
    signed_proof_envelope: SignedExecutionProofEnvelope,
    proof_engine: ProofEngine,
) -> None:
    """
    Verify and store a received execution proof envelope.
    """
    proof_envelope = signed_proof_envelope.message
    beacon_block_root = proof_envelope.beacon_block_root

    # The corresponding beacon block must be known and consensus-valid
    assert beacon_block_root in store.blocks
    state = store.block_states.get(beacon_block_root)
    assert state is not None

    # The corresponding execution payload must be available
    payload_envelope = store.payloads.get(beacon_block_root)
    assert payload_envelope is not None

    # Only one verified proof is stored for each beacon block and proof type
    assert proof_envelope.proof_type not in store.execution_proofs.get(beacon_block_root, {})

    process_execution_proof(state, signed_proof_envelope, payload_envelope, proof_engine)

    # Store only proofs that pass downstream verification
    if beacon_block_root not in store.execution_proofs:
        store.execution_proofs[beacon_block_root] = {}
    store.execution_proofs[beacon_block_root][proof_envelope.proof_type] = proof_envelope