public void testMessageKeyLimits() throws Exception {
    SessionRecord aliceSessionRecord = new SessionRecord();
    SessionRecord bobSessionRecord = new SessionRecord();

    initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());

    AxolotlStore aliceStore = new TestInMemoryAxolotlStore();
    AxolotlStore bobStore = new TestInMemoryAxolotlStore();

    aliceStore.storeSession(new AxolotlAddress("+14159999999", 1), aliceSessionRecord);
    bobStore.storeSession(new AxolotlAddress("+14158888888", 1), bobSessionRecord);

    SessionCipher aliceCipher =
        new SessionCipher(aliceStore, new AxolotlAddress("+14159999999", 1));
    SessionCipher bobCipher = new SessionCipher(bobStore, new AxolotlAddress("+14158888888", 1));

    List<CiphertextMessage> inflight = new LinkedList<>();

    for (int i = 0; i < 2010; i++) {
      inflight.add(
          aliceCipher.encrypt("you've never been so hungry, you've never been so cold".getBytes()));
    }

    bobCipher.decrypt(new WhisperMessage(inflight.get(1000).serialize()));
    bobCipher.decrypt(new WhisperMessage(inflight.get(inflight.size() - 1).serialize()));

    try {
      bobCipher.decrypt(new WhisperMessage(inflight.get(0).serialize()));
      throw new AssertionError("Should have failed!");
    } catch (DuplicateMessageException dme) {
      // good
    }
  }
  private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord)
      throws DuplicateMessageException, LegacyMessageException, InvalidMessageException,
          NoSuchAlgorithmException, NoSessionException {
    AxolotlStore aliceStore = new TestInMemoryAxolotlStore();
    AxolotlStore bobStore = new TestInMemoryAxolotlStore();

    aliceStore.storeSession(new AxolotlAddress("+14159999999", 1), aliceSessionRecord);
    bobStore.storeSession(new AxolotlAddress("+14158888888", 1), bobSessionRecord);

    SessionCipher aliceCipher =
        new SessionCipher(aliceStore, new AxolotlAddress("+14159999999", 1));
    SessionCipher bobCipher = new SessionCipher(bobStore, new AxolotlAddress("+14158888888", 1));

    byte[] alicePlaintext = "This is a plaintext message.".getBytes();
    CiphertextMessage message = aliceCipher.encrypt(alicePlaintext);
    byte[] bobPlaintext = bobCipher.decrypt(new WhisperMessage(message.serialize()));

    assertTrue(Arrays.equals(alicePlaintext, bobPlaintext));

    byte[] bobReply = "This is a message from Bob.".getBytes();
    CiphertextMessage reply = bobCipher.encrypt(bobReply);
    byte[] receivedReply = aliceCipher.decrypt(new WhisperMessage(reply.serialize()));

    assertTrue(Arrays.equals(bobReply, receivedReply));

    List<CiphertextMessage> aliceCiphertextMessages = new ArrayList<>();
    List<byte[]> alicePlaintextMessages = new ArrayList<>();

    for (int i = 0; i < 50; i++) {
      alicePlaintextMessages.add(("смерть за смерть " + i).getBytes());
      aliceCiphertextMessages.add(aliceCipher.encrypt(("смерть за смерть " + i).getBytes()));
    }

    long seed = System.currentTimeMillis();

    Collections.shuffle(aliceCiphertextMessages, new Random(seed));
    Collections.shuffle(alicePlaintextMessages, new Random(seed));

    for (int i = 0; i < aliceCiphertextMessages.size() / 2; i++) {
      byte[] receivedPlaintext =
          bobCipher.decrypt(new WhisperMessage(aliceCiphertextMessages.get(i).serialize()));
      assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i)));
    }

    List<CiphertextMessage> bobCiphertextMessages = new ArrayList<>();
    List<byte[]> bobPlaintextMessages = new ArrayList<>();

    for (int i = 0; i < 20; i++) {
      bobPlaintextMessages.add(("смерть за смерть " + i).getBytes());
      bobCiphertextMessages.add(bobCipher.encrypt(("смерть за смерть " + i).getBytes()));
    }

    seed = System.currentTimeMillis();

    Collections.shuffle(bobCiphertextMessages, new Random(seed));
    Collections.shuffle(bobPlaintextMessages, new Random(seed));

    for (int i = 0; i < bobCiphertextMessages.size() / 2; i++) {
      byte[] receivedPlaintext =
          aliceCipher.decrypt(new WhisperMessage(bobCiphertextMessages.get(i).serialize()));
      assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i)));
    }

    for (int i = aliceCiphertextMessages.size() / 2; i < aliceCiphertextMessages.size(); i++) {
      byte[] receivedPlaintext =
          bobCipher.decrypt(new WhisperMessage(aliceCiphertextMessages.get(i).serialize()));
      assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i)));
    }

    for (int i = bobCiphertextMessages.size() / 2; i < bobCiphertextMessages.size(); i++) {
      byte[] receivedPlaintext =
          aliceCipher.decrypt(new WhisperMessage(bobCiphertextMessages.get(i).serialize()));
      assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i)));
    }
  }