// When we know we'll receive a bunch of blame messages, we have to go through them all
    // to figure out what's going on.
    final Matrix fillBlameMatrix() throws IOException, InterruptedException, FormatException {
      Matrix matrix = new Matrix();

      Map<VerificationKey, Queue<Packet>> blameMessages = mailbox.receiveAllBlame();

      // Get all hashes received in phase 4 to check that they were reported correctly.
      Map<VerificationKey, Message> hashes = new HashMap<>();
      {
        Queue<Packet> hashMessages = mailbox.getPacketsByPhase(Phase.EquivocationCheck);

        for (Packet packet : hashMessages) {
          hashes.put(packet.from(), packet.payload());
        }

        // Include my own hash.
        hashes.put(vk, equivocationCheckHash(players, encryptionKeys, newAddresses));
      }

      // The messages sent in the broadcast phase by the last player to all the other players.
      Map<VerificationKey, Packet> outputVectors = new HashMap<>();

      // The encryption keys sent from every player to every other.
      Map<VerificationKey, Map<VerificationKey, EncryptionKey>> sentKeys = new HashMap<>();

      // The list of messages history in phase 2.
      Map<VerificationKey, Packet> shuffleMessages = new HashMap<>();

      // The set of decryption keys from each player.
      Map<VerificationKey, DecryptionKey> decryptionKeys = new HashMap<>();

      // Determine who is being blamed and by whom.
      for (Map.Entry<VerificationKey, Queue<Packet>> entry : blameMessages.entrySet()) {
        VerificationKey from = entry.getKey();
        Queue<Packet> responses = entry.getValue();
        for (Packet packet : responses) {
          Message message = packet.payload();

          if (message.isEmpty()) {
            log.error("Empty blame message received from " + from);
            matrix.put(vk, Evidence.InvalidFormat(from, packet));
          }

          while (!message.isEmpty()) {
            Blame blame = message.readBlame();
            message = message.rest();

            switch (blame.reason) {
              case InsufficientFunds:
                {
                  // Is the evidence included sufficient?
                  // TODO check whether this is a conflicting transaction.
                  matrix.put(from, Evidence.InsufficientFunds(blame.accused));
                  break;
                }

                // If there is an equivocation failure, all players must send a blame
                // message containing the messages they received in phases 1 and 4.
              case EquivocationFailure:
                {
                  // These are the keys received by
                  // this player in the announcement phase.
                  Map<VerificationKey, EncryptionKey> receivedKeys = new HashMap<>();
                  if (!fillBlameMatrixCollectHistory(
                      vk,
                      from,
                      blame.packets,
                      matrix,
                      outputVectors,
                      shuffleMessages,
                      receivedKeys,
                      sentKeys)) {

                    matrix.put(from, Evidence.Liar(from, new Packet[] {packet}));
                  }

                  Queue<Address> addresses = new LinkedList<>();

                  // The last player will not have
                  // received a separate set of addresses for
                  // us to check, so we insert our own.
                  if (from.equals(players.get(N))) {
                    addresses = newAddresses;
                  } else {

                    Message output = outputVectors.get(from).payload();
                    while (!output.isEmpty()) {
                      addresses.add(output.readAddress());
                      output = output.rest();
                    }
                  }

                  // If the sender is not player one, we add the keys he sent us,
                  // as he would not have received any set of keys from himself.
                  if (!from.equals(players.get(1))) {
                    receivedKeys.put(from, encryptionKeys.get(from));
                  }

                  // Check if this player correctly reported
                  // the hash previously sent to us.
                  Message newHash = equivocationCheckHash(players, receivedKeys, addresses);

                  if (newHash == null || !hashes.get(from).equals(newHash)) {
                    matrix.put(vk, Evidence.Liar(from, new Packet[] {packet}));
                  }

                  break;
                }
              case ShuffleAndEquivocationFailure:
                {
                  if (!decryptionKeys.containsKey(from)) {
                    // We should not receive two keys from the same player so this
                    // block should always execute in a perfect world.
                    decryptionKeys.put(from, blame.privateKey);
                  }

                  // Check that the decryption key is valid. (The decryption key
                  // can be null for player 1, who doesn't make one.)
                  if (!packet.from().equals(players.get(1))) {
                    if (blame.privateKey == null) {
                      matrix.put(vk, Evidence.InvalidFormat(from, packet));
                    } else if (!blame.privateKey.EncryptionKey().equals(encryptionKeys.get(from))) {

                      // We have received a private key that does not match
                      // the public key we have for this player. Include the
                      // original packet containing the encryption key and the
                      // packet with the mismatched key.
                      Packet newKeyPacket = null;
                      Queue<Packet> newKeyPackets = mailbox.getPacketsByPhase(Phase.Announcement);
                      for (Packet p : newKeyPackets) {
                        if (p.from().equals(packet.from())) {
                          newKeyPacket = p;
                        }
                      }
                      matrix.put(vk, Evidence.Liar(from, new Packet[] {newKeyPacket, packet}));
                    }
                  }

                  if (!fillBlameMatrixCollectHistory(
                      vk,
                      from,
                      blame.packets,
                      matrix,
                      outputVectors,
                      shuffleMessages,
                      new HashMap<VerificationKey, EncryptionKey>(),
                      sentKeys)) {

                    matrix.put(vk, Evidence.Liar(from, new Packet[] {packet}));
                  }

                  break;
                }
              case DoubleSpend:
                {
                  // TODO does this actually spend the funds?
                  matrix.put(from, Evidence.DoubleSpend(blame.accused, blame.t));
                  break;
                }
              case InvalidSignature:
                {
                  if (blame.invalid == null || blame.accused == null) {
                    matrix.put(vk, Evidence.Liar(from, new Packet[] {packet}));
                    break;
                  }

                  // TODO do we agree that the signature is invalid?
                  matrix.put(from, Evidence.InvalidSignature(blame.accused, blame.invalid));

                  break;
                }
                // These should already have been handled.
              case MissingOutput:
                {
                  break;
                }
              case ShuffleFailure:
                {
                  break;
                }
              default:
                matrix.put(vk, Evidence.Placeholder(from, Reason.InvalidFormat));
            }
          }
        }
      }

      // Check that we have all the required announcement messages.
      if (sentKeys.size() > 0) {
        for (int i = 2; i < players.size(); i++) {
          if (i == me) {
            continue;
          }

          VerificationKey from = players.get(i);

          Map<VerificationKey, EncryptionKey> sent = sentKeys.get(from);

          if (sent == null) {
            // This should not really happen.
            continue;
          }

          // Add in the key I received from this player.
          sent.put(vk, encryptionKeys.get(from));

          EncryptionKey key = null;
          for (int j = 1; j <= players.size(); j++) {
            if (i == j) {
              continue;
            }

            VerificationKey to = players.get(j);

            EncryptionKey next = sent.get(to);

            if (next == null) {
              // blame player to. He should have sent us this.
              matrix.put(vk, Evidence.Placeholder(to, Reason.Liar));
              continue;
            }

            if (key != null && !key.equals(next)) {
              matrix.put(vk, Evidence.EquivocationFailureAnnouncement(from, sent));
              break;
            }

            key = next;
          }
        }
      }

      boolean outputEquivocate = false;
      if (outputVectors.size() > 0) {
        // Add our own vector to this.
        if (me != N) {
          outputVectors.put(vk, mailbox.getPacketsByPhase(Phase.BroadcastOutput).peek());
        }

        // We should have one output vector for every player except the last and ourselves.
        Set<VerificationKey> leftover = playerSet(1, N - 1);
        leftover.removeAll(outputVectors.keySet());
        if (leftover.size() > 0) {
          for (VerificationKey key : leftover) {
            // matrix.put(vk, Evidence.Placeholder(key, Reason.Liar));
          }
        }

        List<Message> outputMessages = new LinkedList<>();
        for (Packet packet : outputVectors.values()) {
          outputMessages.add(packet.payload());
        }

        // If they are not all equal, blame the last player for equivocating.
        if (!areEqual(outputMessages)) {
          matrix.put(vk, Evidence.EquivocationFailureBroadcast(players.get(N), outputVectors));
          outputEquivocate = true;
        }
      }

      if (decryptionKeys.size() > 0) {

        // We should have one decryption key for every player except the first.
        Set<VerificationKey> leftover = playerSet(2, players.size());
        leftover.removeAll(decryptionKeys.keySet());
        if (leftover.size() > 0) {
          log.warn("leftover");
          // TODO blame someone.
        } else {
          Evidence shuffleEvidence =
              checkShuffleMisbehavior(
                  players,
                  decryptionKeys,
                  shuffleMessages,
                  outputEquivocate ? null : outputVectors);
          if (shuffleEvidence != null) {
            matrix.put(vk, shuffleEvidence);
          }
        }
      }

      return matrix;
    }