text: import logging
  from typing import List, Union
  
  from goji.consensus.block_record import BlockRecord
  from goji.consensus.blockchain_interface import BlockchainInterface
  from goji.consensus.constants import ConsensusConstants
  from goji.types.blockchain_format.sized_bytes import bytes32
  from goji.types.full_block import FullBlock
  from goji.types.header_block import HeaderBlock
  from goji.types.unfinished_block import UnfinishedBlock
  from goji.types.unfinished_header_block import UnfinishedHeaderBlock
  from goji.util.ints import uint64
  
  log = logging.getLogger(__name__)
  
  
  def final_eos_is_already_included(
      header_block: Union[UnfinishedHeaderBlock, UnfinishedBlock, HeaderBlock, FullBlock],
      blocks: BlockchainInterface,
      sub_slot_iters: uint64,
  ) -> bool:
      """
      Args:
          header_block: An overflow block, with potentially missing information about the new sub slot
          blocks: all blocks that have been included before header_block
          sub_slot_iters: sub_slot_iters at the header_block
  
      Returns: True iff the missing sub slot was already included in a previous block. Returns False if the sub
      slot was not included yet, and therefore it is the responsibility of this block to include it
  
      """
      if len(header_block.finished_sub_slots) > 0:
          # We already have an included empty sub slot, which means the prev block is 2 sub slots behind.
          return False
      curr: BlockRecord = blocks.block_record(header_block.prev_header_hash)
  
      # We also check if curr is close to header_block, which means it's in the same sub slot
      seen_overflow_block = curr.overflow and (header_block.total_iters - curr.total_iters < sub_slot_iters // 2)
      while not curr.first_in_sub_slot and not curr.height == 0:
          if curr.overflow and header_block.total_iters - curr.total_iters < sub_slot_iters // 2:
              seen_overflow_block = True
          curr = blocks.block_record(curr.prev_hash)
  
      if curr.first_in_sub_slot and seen_overflow_block:
          # We have seen another overflow block in this slot (same as header_block), therefore there are no
          # missing sub slots
          return True
  
      # We have not seen any overflow blocks, therefore header_block will have to include the missing sub slot in
      # the future
      return False
  
  
  def get_block_challenge(
      constants: ConsensusConstants,
      header_block: Union[UnfinishedHeaderBlock, UnfinishedBlock, HeaderBlock, FullBlock],
      blocks: BlockchainInterface,
      genesis_block: bool,
      overflow: bool,
      skip_overflow_last_ss_validation: bool,
  ):
      if len(header_block.finished_sub_slots) > 0:
          if overflow:
              # New sub-slot with overflow block
              if skip_overflow_last_ss_validation:
                  # In this case, we are missing the final sub-slot bundle (it's not finished yet), however
                  # There is a whole empty slot before this block is infused
                  challenge: bytes32 = header_block.finished_sub_slots[-1].challenge_chain.get_hash()
              else:
                  challenge = header_block.finished_sub_slots[
                      -1
                  ].challenge_chain.challenge_chain_end_of_slot_vdf.challenge
          else:
              # No overflow, new slot with a new challenge
              challenge = header_block.finished_sub_slots[-1].challenge_chain.get_hash()
      else:
          if genesis_block:
              challenge = constants.GENESIS_CHALLENGE
          else:
              if overflow:
                  if skip_overflow_last_ss_validation:
                      # Overflow infusion without the new slot, so get the last challenge
                      challenges_to_look_for = 1
                  else:
                      # Overflow infusion, so get the second to last challenge. skip_overflow_last_ss_validation is False,
                      # Which means no sub slots are omitted
                      challenges_to_look_for = 2
              else:
                  challenges_to_look_for = 1
              reversed_challenge_hashes: List[bytes32] = []
              curr: BlockRecord = blocks.block_record(header_block.prev_header_hash)
              while len(reversed_challenge_hashes) < challenges_to_look_for:
                  if curr.first_in_sub_slot:
                      assert curr.finished_challenge_slot_hashes is not None
                      reversed_challenge_hashes += reversed(curr.finished_challenge_slot_hashes)
                  if curr.height == 0:
                      assert curr.finished_challenge_slot_hashes is not None
                      assert len(curr.finished_challenge_slot_hashes) > 0
                      break
                  curr = blocks.block_record(curr.prev_hash)
              challenge = reversed_challenge_hashes[challenges_to_look_for - 1]
      return challenge
  