code: def _can_process_pre_prepare(self, pre_prepare: PrePrepare, sender: str) -> Optional[int]:
          
          # TODO: Check whether it is rejecting PRE-PREPARE from previous view
  
          # PRE-PREPARE should not be sent from non primary
          if not self.isMsgFromPrimary(pre_prepare, sender):
              return PP_CHECK_NOT_FROM_PRIMARY
  
          # Already has a PRE-PREPARE with same 3 phase key
          if (pre_prepare.viewNo, pre_prepare.ppSeqNo) in self.prePrepares:
              return PP_CHECK_DUPLICATE
  
          if not self.is_pre_prepare_time_acceptable(pre_prepare, sender):
              return PP_CHECK_WRONG_TIME
  
          if compare_3PC_keys((pre_prepare.viewNo, pre_prepare.ppSeqNo),
                              self.__last_pp_3pc) > 0:
              return PP_CHECK_OLD  # ignore old pre-prepare
  
          if self.nonFinalisedReqs(pre_prepare.reqIdr):
              return PP_CHECK_REQUEST_NOT_FINALIZED
  
          if not self.__is_next_pre_prepare(pre_prepare.viewNo,
                                            pre_prepare.ppSeqNo):
              return PP_CHECK_NOT_NEXT
  
          if f.POOL_STATE_ROOT_HASH.nm in pre_prepare and \
                  pre_prepare.poolStateRootHash != self.stateRootHash(POOL_LEDGER_ID):
              return PP_CHECK_INCORRECT_POOL_STATE_ROOT
  
          # BLS multi-sig:
          status = self._bls_bft_replica.validate_pre_prepare(pre_prepare,
                                                              sender)
          if status is not None:
              return status
          return None
docs: Decide whether this replica is eligible to process a PRE-PREPARE.
  
          :param pre_prepare: a PRE-PREPARE msg to process
          :param sender: the name of the node that sent the PRE-PREPARE msg