code: def process_three_phase_msg(self, msg: ThreePhaseMsg, sender: str):
          
          sender = self.generateName(sender, self.instId)
  
          pp_key = ((msg.viewNo, msg.ppSeqNo) if
                    isinstance(msg, PrePrepare) else None)
  
          # the same PrePrepare might come here multiple times
          if (pp_key and (msg, sender) not in self.pre_prepare_tss[pp_key]):
              # TODO more clean solution would be to set timestamps
              # earlier (e.g. in zstack)
              self.pre_prepare_tss[pp_key][msg, sender] = self.get_time_for_3pc_batch()
  
          result, reason = self.validator.validate_3pc_msg(msg)
          if result == DISCARD:
              self.discard(msg, "{} discard message {} from {} "
                                "with the reason: {}".format(self, msg, sender, reason),
                           self.logger.trace)
          elif result == PROCESS:
              self.threePhaseRouter.handleSync((msg, sender))
          else:
              self.logger.debug("{} stashing 3 phase message {} with "
                                "the reason: {}".format(self, msg, reason))
              self.stasher.stash((msg, sender), result)
docs: Process a 3-phase (pre-prepare, prepare and commit) request.
          Dispatch the request only if primary has already been decided, otherwise
          stash it.
  
          :param msg: the Three Phase message, one of PRE-PREPARE, PREPARE,
              COMMIT
          :param sender: name of the node that sent this message