private boolean validateProposalAndRoundChangeAreConsistent(final Proposal proposal) {
    final ConsensusRoundIdentifier proposalRoundIdentifier = proposal.getRoundIdentifier();

    if (proposalRoundIdentifier.getRoundNumber() == 0) {
      if (!validateRoundZeroProposalHasNoRoundChangesOrPrepares(proposal)) {
        return false;
      }

      return validateBlockCoinbaseMatchesMsgAuthor(proposal);
    } else {

      if (!validateRoundChanges(proposal, proposal.getRoundChanges())) {
        LOG.info("{}: failed to validate piggy-backed round change payloads", ERROR_PREFIX);
        return false;
      }

      // The RoundChangePayloadValidator ensures the PreparedRound is less than targetRound
      // therefore, no need to validate that here.
      final Optional<SignedData<RoundChangePayload>> roundChangeWithLatestPreparedRound =
          getRoundChangeWithLatestPreparedRound(proposal.getRoundChanges());

      if (roundChangeWithLatestPreparedRound.isPresent()) {
        final PreparedRoundMetadata metadata =
            roundChangeWithLatestPreparedRound.get().getPayload().getPreparedRoundMetadata().get();

        LOG.debug(
            "Prepared Metadata blockhash : {}, proposal blockhash: {}, prepared round in message: {}, proposal round in message: {}",
            metadata.getPreparedBlockHash(),
            proposal.getBlock().getHash(),
            metadata.getPreparedRound(),
            proposal.getRoundIdentifier().getRoundNumber());

        // The Hash in the roundchange/proposals is NOT the same as the value in the
        // prepares/roundchanges
        // as said payloads reference the block with an OLD round number in it - therefore, need
        // to create a block with the old round in it, then re-calc expected hash
        // Need to check that if we substitute the LatestPrepareCert round number into the supplied
        // block that we get the SAME hash as PreparedCert.
        final QbftBlock currentBlockWithOldRound =
            blockInterface.replaceRoundInBlock(proposal.getBlock(), metadata.getPreparedRound());

        final Hash expectedPriorBlockHash = currentBlockWithOldRound.getHash();

        if (!metadata.getPreparedBlockHash().equals(expectedPriorBlockHash)) {
          LOG.info(
              "{}: Latest Prepared Metadata blockhash does not align with proposed block. Expected: {}, Actual: {}",
              ERROR_PREFIX,
              expectedPriorBlockHash,
              metadata.getPreparedBlockHash());
          return false;
        }

        // validate the prepares
        if (!validatePrepares(
            metadata, proposal.getRoundIdentifier().getSequenceNumber(), proposal.getPrepares())) {
          LOG.info("{}: Piggy-backed prepares failed validation", ERROR_PREFIX);
          return false;
        }
      } else {
        // no one prepared, so prepares should be empty
        if (!proposal.getPrepares().isEmpty()) {
          LOG.info("{}: No PreparedMetadata exists, so prepare list must be empty", ERROR_PREFIX);
          return false;
        }

        return validateBlockCoinbaseMatchesMsgAuthor(proposal);
      }

      return true;
    }
  }
Validate.

@param msg the Proposal msg
@return the boolean
consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/ProposalValidator.java