code: def process_vchd_msg(self, msg: ViewChangeDone, sender: str) -> bool:
          
          logger.info("{}'s primary selector started processing of ViewChangeDone msg from {} : {}".
                      format(self.name, sender, msg))
          view_no = msg.viewNo
          if self.view_no != view_no:
              self.provider.discard(msg, '{} got Primary from {} for view no {} '
                                         'whereas current view no is {}'.
                                    format(self, sender, view_no, self.view_no),
                                    logMethod=logger.info)
              return False
  
          new_primary_name = msg.name
          if new_primary_name == self.previous_master_primary:
              self.provider.discard(msg, '{} got Primary from {} for {} who was primary of '
                                         'master in previous view too'.
                                    format(self, sender, new_primary_name),
                                    logMethod=logger.info)
              return False
  
          # Since a node can send ViewChangeDone more than one time
          self._on_verified_view_change_done_msg(msg, sender)
          # TODO why do we check that after the message tracking
          if self.provider.has_primary():
              self.provider.discard(msg, "it already decided primary which is {}".
                                    format(self.provider.current_primary_name()), logger.info)
              return False
  
          self._start_selection()
docs: Processes ViewChangeDone messages. Once n-f messages have been
          received, decides on a primary for specific replica.
  
          :param msg: ViewChangeDone message
          :param sender: the name of the node from which this message was sent