@Test
  public void receiveRoundStateIsNotLostIfASecondProposalMessageIsReceivedForCurrentRound() {
    final Block initialBlock = context.createBlockForProposalFromChainHead(0, 15);
    final Block reproposedBlock = context.createBlockForProposalFromChainHead(1, 15);
    final ConsensusRoundIdentifier nextRoundId = new ConsensusRoundIdentifier(1, 1);

    final PreparedRoundArtifacts preparedRoundArtifacts =
        createValidPreparedRoundArtifacts(context, roundId, initialBlock);

    final List<SignedData<RoundChangePayload>> roundChanges =
        peers.createSignedRoundChangePayload(nextRoundId, preparedRoundArtifacts);

    final RoundSpecificPeers nextRoles = context.roundSpecificPeers(nextRoundId);
    final ValidatorPeer nextProposer = nextRoles.getProposer();

    nextProposer.injectProposalForFutureRound(
        nextRoundId, new RoundChangeCertificate(roundChanges), reproposedBlock);

    peers.verifyMessagesReceived(
        localNodeMessageFactory.createPrepare(nextRoundId, reproposedBlock.getHash()));

    // Inject a prepare, then re-inject the proposal - then ensure only a single prepare is enough
    // to trigger a Commit transmission from the local node
    nextRoles.getNonProposing(0).injectPrepare(nextRoundId, reproposedBlock.getHash());

    nextProposer.injectProposalForFutureRound(
        nextRoundId, new RoundChangeCertificate(roundChanges), reproposedBlock);

    peers.verifyNoMessagesReceived();

    nextRoles.getNonProposing(1).injectPrepare(nextRoundId, reproposedBlock.getHash());

    final Commit expectedCommit =
        new Commit(
            IntegrationTestHelpers.createSignedCommitPayload(
                nextRoundId, reproposedBlock, context.getLocalNodeParams().getNodeKey()));

    peers.verifyMessagesReceived(expectedCommit);
  }
Ensure the Ibft component responds appropriately when a future round Proposal message is
received.
consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/ReceivedFutureProposalTest.java