private static Evidence checkShuffleMisbehavior( Map<Integer, VerificationKey> players, Map<VerificationKey, DecryptionKey> decryptionKeys, Map<VerificationKey, Packet> shuffleMessages, Map<VerificationKey, Packet> broadcastMessages) throws FormatException { if (players == null || decryptionKeys == null || shuffleMessages == null || broadcastMessages == null) throw new NullPointerException(); SortedSet<String> outputs = new TreeSet<>(); // Go through the steps of shuffling messages. for (int i = 1; i < players.size(); i++) { Packet packet = shuffleMessages.get(players.get(i + 1)); if (packet == null) { // TODO Blame a player for lying. return null; } Message message = packet.payload(); // Grab the correct number of addresses and decrypt them. SortedSet<String> decrypted = new TreeSet<>(); for (int j = 0; j < i; j++) { if (message.isEmpty()) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(i), decryptionKeys, shuffleMessages, broadcastMessages); } String address = message.readString(); message = message.rest(); for (int k = i + 1; k <= players.size(); k++) { address = decryptionKeys.get(players.get(k)).decrypt(address); } // There shouldn't be duplicates. if (decrypted.contains(address)) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(i), decryptionKeys, shuffleMessages, broadcastMessages); } decrypted.add(address); } // Does this contain all the previous addresses? if (!decrypted.containsAll(outputs)) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(i), decryptionKeys, shuffleMessages, broadcastMessages); } decrypted.removeAll(outputs); // There should be one new address. if (decrypted.size() != 1) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(i), decryptionKeys, shuffleMessages, broadcastMessages); } outputs.add(decrypted.first()); } // Now check the last set of messages from player N. // All broadcast messages should have the same content and we should // have already checked for this. Therefore we just look for the first // one that is available. // (The message for player 1 should always be available, so theoretically // we don't need to loop through everybody, but who knows what might have happened.) Packet packet = null; for (int j = 1; j <= players.size(); j++) { packet = broadcastMessages.get(players.get(j)); if (packet != null) { break; } } if (packet == null) { // TODO blame someone. return null; } Message message = packet.payload(); // Grab the correct number of addresses and decrypt them. SortedSet<String> addresses = new TreeSet<>(); for (int j = 0; j < players.size(); j++) { if (message.isEmpty()) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages); } Address address = message.readAddress(); // There shouldn't be duplicates. if (addresses.contains(address.toString())) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages); } addresses.add(address.toString()); } // Does this contain all the previous addresses? if (!addresses.containsAll(outputs)) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages); } addresses.removeAll(outputs); // There should be one new address. if (addresses.size() != 1) { return Evidence.ShuffleMisbehaviorDropAddress( players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages); } return null; }