@Override
  public void handleRoundChangePayload(final RoundChange message) {
    final ConsensusRoundIdentifier targetRound = message.getRoundIdentifier();

    LOG.debug(
        "Round change from {}: block {}, round {}",
        message.getAuthor(),
        message.getRoundIdentifier().getSequenceNumber(),
        message.getRoundIdentifier().getRoundNumber());

    // Diagnostic logging (only logs anything if the chain has stalled)
    roundChangeManager.storeAndLogRoundChangeSummary(message);

    final MessageAge messageAge =
        determineAgeOfPayload(message.getRoundIdentifier().getRoundNumber());
    if (messageAge == MessageAge.PRIOR_ROUND) {
      LOG.debug("Received RoundChange Payload for a prior round. targetRound={}", targetRound);
      return;
    }

    final Optional<Collection<RoundChange>> result =
        roundChangeManager.appendRoundChangeMessage(message);

    if (!isEarlyRoundChangeEnabled) {
      if (result.isPresent()) {
        LOG.debug(
            "Received sufficient RoundChange messages to change round to targetRound={}",
            targetRound);
        if (messageAge == MessageAge.FUTURE_ROUND) {
          startNewRound(targetRound.getRoundNumber());
        }

        final RoundChangeArtifacts roundChangeMetadata = RoundChangeArtifacts.create(result.get());

        if (finalState.isLocalNodeProposerForRound(targetRound)) {
          if (currentRound.isEmpty()) {
            startNewRound(0);
          }
          currentRound
              .get()
              .startRoundWith(roundChangeMetadata, TimeUnit.MILLISECONDS.toSeconds(clock.millis()));
        }
      }
    } else {

      if (currentRound.isEmpty()) {
        startNewRound(0);
      }
      int currentRoundNumber = currentRound.get().getRoundIdentifier().getRoundNumber();
      // If this node is proposer for the current round, check if quorum is achieved for RC messages
      // aiming this round
      if (targetRound.getRoundNumber() == currentRoundNumber
          && finalState.isLocalNodeProposerForRound(targetRound)
          && result.isPresent()) {

        final RoundChangeArtifacts roundChangeMetadata = RoundChangeArtifacts.create(result.get());

        currentRound
            .get()
            .startRoundWith(roundChangeMetadata, TimeUnit.MILLISECONDS.toSeconds(clock.millis()));
      }

      // check if f+1 RC messages for future rounds are received
      QbftRound qbftRound = currentRound.get();
      Optional<Integer> nextHigherRound =
          roundChangeManager.futureRCQuorumReceived(qbftRound.getRoundIdentifier());
      if (nextHigherRound.isPresent()) {
        LOG.info(
            "Received sufficient RoundChange messages to change round to targetRound={}",
            nextHigherRound.get());
        doRoundChange(nextHigherRound.get());
      }
    }
  }
If the list of validators for the next block to be proposed/imported has changed from the
previous block, log the change. Only log for round 0 (i.e. once per block).

@param qbftRound The current round
consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftBlockHeightManager.java