async def get_sp_and_ip_sub_slots(self, header_hash: bytes32) -> Optional[Tuple[(Optional[EndOfSubSlotBundle], Optional[EndOfSubSlotBundle])]]:
    block: Optional[FullBlock] = (await self.block_store.get_full_block(header_hash))
    if (block is None):
        return None
    curr_br: BlockRecord = self.block_record(block.header_hash)
    is_overflow = curr_br.overflow
    curr: Optional[FullBlock] = block
    assert (curr is not None)
    while True:
        if curr_br.first_in_sub_slot:
            curr = (await self.block_store.get_full_block(curr_br.header_hash))
            assert (curr is not None)
            break
        if (curr_br.height == 0):
            break
        curr_br = self.block_record(curr_br.prev_hash)
    if (len(curr.finished_sub_slots) == 0):
        return (None, None)
    ip_sub_slot = curr.finished_sub_slots[(- 1)]
    if (not is_overflow):
        return (None, ip_sub_slot)
    if (len(curr.finished_sub_slots) > 1):
        return (curr.finished_sub_slots[(- 2)], ip_sub_slot)
    prev_curr: Optional[FullBlock] = (await self.block_store.get_full_block(curr.prev_header_hash))
    if (prev_curr is None):
        assert (curr.height == 0)
        prev_curr = curr
        prev_curr_br = self.block_record(curr.header_hash)
    else:
        prev_curr_br = self.block_record(curr.prev_header_hash)
    assert (prev_curr_br is not None)
    while (prev_curr_br.height > 0):
        if prev_curr_br.first_in_sub_slot:
            prev_curr = (await self.block_store.get_full_block(prev_curr_br.header_hash))
            assert (prev_curr is not None)
            break
        prev_curr_br = self.block_record(prev_curr_br.prev_hash)
    if (len(prev_curr.finished_sub_slots) == 0):
        return (None, ip_sub_slot)
    return (prev_curr.finished_sub_slots[(- 1)], ip_sub_slot)
async def get_sp_and_ip_sub_slots(self, header_hash: bytes32) -> Optional[Tuple[(Optional[EndOfSubSlotBundle], Optional[EndOfSubSlotBundle])]]:
    block: Optional[FullBlock] = (await self.block_store.get_full_block(header_hash))
    if (block is None):
        return None
    curr_br: BlockRecord = self.block_record(block.header_hash)
    is_overflow = curr_br.overflow
    curr: Optional[FullBlock] = block
    assert (curr is not None)
    while True:
        if curr_br.first_in_sub_slot:
            curr = (await self.block_store.get_full_block(curr_br.header_hash))
            assert (curr is not None)
            break
        if (curr_br.height == 0):
            break
        curr_br = self.block_record(curr_br.prev_hash)
    if (len(curr.finished_sub_slots) == 0):
        return (None, None)
    ip_sub_slot = curr.finished_sub_slots[(- 1)]
    if (not is_overflow):
        return (None, ip_sub_slot)
    if (len(curr.finished_sub_slots) > 1):
        return (curr.finished_sub_slots[(- 2)], ip_sub_slot)
    prev_curr: Optional[FullBlock] = (await self.block_store.get_full_block(curr.prev_header_hash))
    if (prev_curr is None):
        assert (curr.height == 0)
        prev_curr = curr
        prev_curr_br = self.block_record(curr.header_hash)
    else:
        prev_curr_br = self.block_record(curr.prev_header_hash)
    assert (prev_curr_br is not None)
    while (prev_curr_br.height > 0):
        if prev_curr_br.first_in_sub_slot:
            prev_curr = (await self.block_store.get_full_block(prev_curr_br.header_hash))
            assert (prev_curr is not None)
            break
        prev_curr_br = self.block_record(prev_curr_br.prev_hash)
    if (len(prev_curr.finished_sub_slots) == 0):
        return (None, ip_sub_slot)
    return (prev_curr.finished_sub_slots[(- 1)], ip_sub_slot)