class RoundChangeCertificateValidator {

  public boolean validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot(
      final ConsensusRoundIdentifier expectedRound, final RoundChangeCertificate roundChangeCert) {

    final Collection<SignedData<RoundChangePayload>> roundChangeMsgs =
        roundChangeCert.getRoundChangePayloads();

    if (hasDuplicateAuthors(roundChangeMsgs)) {
      return false;
    }

    if (roundChangeMsgs.size() < quorum) {
      LOG.info("Invalid RoundChangeCertificate, insufficient RoundChange messages.");
      return false;
    }

    if (!roundChangeCert.getRoundChangePayloads().stream()
        .allMatch(p -> p.getPayload().getRoundIdentifier().equals(expectedRound))) {
      LOG.info(
          "Invalid RoundChangeCertificate, not all embedded RoundChange messages have a "
              + "matching target round.");
      return false;
    }

    final RoundChangePayloadValidator roundChangeValidator =
        new RoundChangePayloadValidator(
            messageValidatorFactory,
            validators,
            IbftHelpers.prepareMessageCountForQuorum(quorum),
            chainHeight);

    if (!roundChangeCert.getRoundChangePayloads().stream()
        .allMatch(roundChangeValidator::validateRoundChange)) {
      LOG.info("Invalid RoundChangeCertificate, embedded RoundChange message failed validation.");
      return false;
    }

    return true;
  }

  public  RoundChangeCertificateValidator(
      final Collection<Address> validators,
      final MessageValidatorForHeightFactory messageValidatorFactory,
      final long chainHeight);

}