github.com/prysmaticlabs/prysm@v1.4.4/tools/specs-checker/data/specs/phase0/fork-choice.md (about)

     1  ```python
     2  def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -> Store:
     3      assert anchor_block.state_root == hash_tree_root(anchor_state)
     4      anchor_root = hash_tree_root(anchor_block)
     5      anchor_epoch = get_current_epoch(anchor_state)
     6      justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
     7      finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
     8      return Store(
     9          time=uint64(anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot),
    10          genesis_time=anchor_state.genesis_time,
    11          justified_checkpoint=justified_checkpoint,
    12          finalized_checkpoint=finalized_checkpoint,
    13          best_justified_checkpoint=justified_checkpoint,
    14          blocks={anchor_root: copy(anchor_block)},
    15          block_states={anchor_root: copy(anchor_state)},
    16          checkpoint_states={justified_checkpoint: copy(anchor_state)},
    17      )
    18  ```
    19  ```python
    20  def get_slots_since_genesis(store: Store) -> int:
    21      return (store.time - store.genesis_time) // SECONDS_PER_SLOT
    22  ```
    23  ```python
    24  def get_current_slot(store: Store) -> Slot:
    25      return Slot(GENESIS_SLOT + get_slots_since_genesis(store))
    26  ```
    27  ```python
    28  def compute_slots_since_epoch_start(slot: Slot) -> int:
    29      return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot))
    30  ```
    31  ```python
    32  def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
    33      block = store.blocks[root]
    34      if block.slot > slot:
    35          return get_ancestor(store, block.parent_root, slot)
    36      elif block.slot == slot:
    37          return root
    38      else:
    39          # root is older than queried slot, thus a skip slot. Return most recent root prior to slot
    40          return root
    41  ```
    42  ```python
    43  def get_latest_attesting_balance(store: Store, root: Root) -> Gwei:
    44      state = store.checkpoint_states[store.justified_checkpoint]
    45      active_indices = get_active_validator_indices(state, get_current_epoch(state))
    46      return Gwei(sum(
    47          state.validators[i].effective_balance for i in active_indices
    48          if (i in store.latest_messages
    49              and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
    50      ))
    51  ```
    52  ```python
    53  def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconBlock]) -> bool:
    54      block = store.blocks[block_root]
    55      children = [
    56          root for root in store.blocks.keys()
    57          if store.blocks[root].parent_root == block_root
    58      ]
    59  
    60      # If any children branches contain expected finalized/justified checkpoints,
    61      # add to filtered block-tree and signal viability to parent.
    62      if any(children):
    63          filter_block_tree_result = [filter_block_tree(store, child, blocks) for child in children]
    64          if any(filter_block_tree_result):
    65              blocks[block_root] = block
    66              return True
    67          return False
    68  
    69      # If leaf block, check finalized/justified checkpoints as matching latest.
    70      head_state = store.block_states[block_root]
    71  
    72      correct_justified = (
    73          store.justified_checkpoint.epoch == GENESIS_EPOCH
    74          or head_state.current_justified_checkpoint == store.justified_checkpoint
    75      )
    76      correct_finalized = (
    77          store.finalized_checkpoint.epoch == GENESIS_EPOCH
    78          or head_state.finalized_checkpoint == store.finalized_checkpoint
    79      )
    80      # If expected finalized/justified, add to viable block-tree and signal viability to parent.
    81      if correct_justified and correct_finalized:
    82          blocks[block_root] = block
    83          return True
    84  
    85      # Otherwise, branch not viable
    86      return False
    87  ```
    88  ```python
    89  def get_filtered_block_tree(store: Store) -> Dict[Root, BeaconBlock]:
    90      """
    91      Retrieve a filtered block tree from ``store``, only returning branches
    92      whose leaf state's justified/finalized info agrees with that in ``store``.
    93      """
    94      base = store.justified_checkpoint.root
    95      blocks: Dict[Root, BeaconBlock] = {}
    96      filter_block_tree(store, base, blocks)
    97      return blocks
    98  ```
    99  ```python
   100  def get_head(store: Store) -> Root:
   101      # Get filtered block tree that only includes viable branches
   102      blocks = get_filtered_block_tree(store)
   103      # Execute the LMD-GHOST fork choice
   104      head = store.justified_checkpoint.root
   105      while True:
   106          children = [
   107              root for root in blocks.keys()
   108              if blocks[root].parent_root == head
   109          ]
   110          if len(children) == 0:
   111              return head
   112          # Sort by latest attesting balance with ties broken lexicographically
   113          head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root))
   114  ```
   115  ```python
   116  def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool:
   117      """
   118      To address the bouncing attack, only update conflicting justified
   119      checkpoints in the fork choice if in the early slots of the epoch.
   120      Otherwise, delay incorporation of new justified checkpoint until next epoch boundary.
   121  
   122      See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion.
   123      """
   124      if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED:
   125          return True
   126  
   127      justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
   128      if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root:
   129          return False
   130  
   131      return True
   132  ```
   133  ```python
   134  def validate_on_attestation(store: Store, attestation: Attestation) -> None:
   135      target = attestation.data.target
   136  
   137      # Attestations must be from the current or previous epoch
   138      current_epoch = compute_epoch_at_slot(get_current_slot(store))
   139      # Use GENESIS_EPOCH for previous when genesis to avoid underflow
   140      previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
   141      # If attestation target is from a future epoch, delay consideration until the epoch arrives
   142      assert target.epoch in [current_epoch, previous_epoch]
   143      assert target.epoch == compute_epoch_at_slot(attestation.data.slot)
   144  
   145      # Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
   146      assert target.root in store.blocks
   147  
   148      # Attestations must be for a known block. If block is unknown, delay consideration until the block is found
   149      assert attestation.data.beacon_block_root in store.blocks
   150      # Attestations must not be for blocks in the future. If not, the attestation should not be considered
   151      assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
   152  
   153      # LMD vote must be consistent with FFG vote target
   154      target_slot = compute_start_slot_at_epoch(target.epoch)
   155      assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot)
   156  
   157      # Attestations can only affect the fork choice of subsequent slots.
   158      # Delay consideration in the fork choice until their slot is in the past.
   159      assert get_current_slot(store) >= attestation.data.slot + 1
   160  ```
   161  ```python
   162  def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None:
   163      # Store target checkpoint state if not yet seen
   164      if target not in store.checkpoint_states:
   165          base_state = copy(store.block_states[target.root])
   166          if base_state.slot < compute_start_slot_at_epoch(target.epoch):
   167              process_slots(base_state, compute_start_slot_at_epoch(target.epoch))
   168          store.checkpoint_states[target] = base_state
   169  ```
   170  ```python
   171  def update_latest_messages(store: Store, attesting_indices: Sequence[ValidatorIndex], attestation: Attestation) -> None:
   172      target = attestation.data.target
   173      beacon_block_root = attestation.data.beacon_block_root
   174      for i in attesting_indices:
   175          if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
   176              store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=beacon_block_root)
   177  ```
   178  ```python
   179  def on_tick(store: Store, time: uint64) -> None:
   180      previous_slot = get_current_slot(store)
   181  
   182      # update store time
   183      store.time = time
   184  
   185      current_slot = get_current_slot(store)
   186      # Not a new epoch, return
   187      if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0):
   188          return
   189      # Update store.justified_checkpoint if a better checkpoint is known
   190      if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
   191          store.justified_checkpoint = store.best_justified_checkpoint
   192  ```
   193  ```python
   194  def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
   195      block = signed_block.message
   196      # Parent block must be known
   197      assert block.parent_root in store.block_states
   198      # Make a copy of the state to avoid mutability issues
   199      pre_state = copy(store.block_states[block.parent_root])
   200      # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
   201      assert get_current_slot(store) >= block.slot
   202  
   203      # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
   204      finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
   205      assert block.slot > finalized_slot
   206      # Check block is a descendant of the finalized block at the checkpoint finalized slot
   207      assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
   208  
   209      # Check the block is valid and compute the post-state
   210      state = pre_state.copy()
   211      state_transition(state, signed_block, True)
   212      # Add new block to the store
   213      store.blocks[hash_tree_root(block)] = block
   214      # Add new state for this block to the store
   215      store.block_states[hash_tree_root(block)] = state
   216  
   217      # Update justified checkpoint
   218      if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
   219          if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch:
   220              store.best_justified_checkpoint = state.current_justified_checkpoint
   221          if should_update_justified_checkpoint(store, state.current_justified_checkpoint):
   222              store.justified_checkpoint = state.current_justified_checkpoint
   223  
   224      # Update finalized checkpoint
   225      if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
   226          store.finalized_checkpoint = state.finalized_checkpoint
   227  
   228          # Potentially update justified if different from store
   229          if store.justified_checkpoint != state.current_justified_checkpoint:
   230              # Update justified if new justified is later than store justified
   231              if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
   232                  store.justified_checkpoint = state.current_justified_checkpoint
   233                  return
   234  
   235              # Update justified if store justified is not in chain with finalized checkpoint
   236              finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
   237              ancestor_at_finalized_slot = get_ancestor(store, store.justified_checkpoint.root, finalized_slot)
   238              if ancestor_at_finalized_slot != store.finalized_checkpoint.root:
   239                  store.justified_checkpoint = state.current_justified_checkpoint
   240  ```
   241  ```python
   242  def on_attestation(store: Store, attestation: Attestation) -> None:
   243      """
   244      Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
   245  
   246      An ``attestation`` that is asserted as invalid may be valid at a later time,
   247      consider scheduling it for later processing in such case.
   248      """
   249      validate_on_attestation(store, attestation)
   250      store_target_checkpoint_state(store, attestation.data.target)
   251  
   252      # Get state at the `target` to fully validate attestation
   253      target_state = store.checkpoint_states[attestation.data.target]
   254      indexed_attestation = get_indexed_attestation(target_state, attestation)
   255      assert is_valid_indexed_attestation(target_state, indexed_attestation)
   256  
   257      # Update latest messages for attesting indices
   258      update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
   259  ```