void blameBroadcastShuffleMessages() throws TimeoutException, Matrix, IOException, InterruptedException, FormatException { phase.set(Phase.Blame); log.warn("Player " + me + " enters blame phase and sends broadcast messages."); // Collect all packets from phase 2 and 3. Queue<Packet> evidence = mailbox.getPacketsByPhase(Phase.Shuffling); evidence.addAll(mailbox.getPacketsByPhase(Phase.BroadcastOutput)); // Send them all with the decryption key. mailbox.broadcast( messages.make().attach(Blame.ShuffleAndEquivocationFailure(dk, evidence)), phase.get()); throw fillBlameMatrix(); }
// Players run an equivocation check when they must confirm that they all have // the same information. void equivocationCheck( Map<VerificationKey, EncryptionKey> encryptonKeys, Queue<Address> newAddresses, boolean errorCase // There is an equivocation check that occurs ) throws InterruptedException, TimeoutException, Matrix, IOException, FormatException { Message equivocationCheck = equivocationCheckHash(players, encryptonKeys, newAddresses); mailbox.broadcast(equivocationCheck, phase.get()); System.out.println("Player " + me + " equivocation message " + equivocationCheck); // Wait for a similar message from everyone else and check that the result is the name. Map<VerificationKey, Message> hashes = null; hashes = mailbox.receiveFromMultipleBlameless(playerSet(1, players.size()), phase.get()); hashes.put(vk, equivocationCheck); if (areEqual(hashes.values())) { // We may have got this far as part of a normal part of the protocol or as a part // of an error case. If this is a normal part of the protocol, a blame message // having been received indicates that another player has a problem with the // output vector received from the last player in phase 3. if (errorCase || mailbox.blame(Reason.ShuffleFailure) || mailbox.blame(Reason.MissingOutput)) { blameBroadcastShuffleMessages(); } return; } log.warn("Player " + me + " equivocation check fails."); // If the hashes are not equal, enter the blame phase. // Collect all packets from phase 1 and 3. phase.set(Phase.Blame); Queue<Packet> evidence = mailbox.getPacketsByPhase(Phase.Announcement); evidence.addAll(mailbox.getPacketsByPhase(Phase.BroadcastOutput)); mailbox.broadcast(messages.make().attach(Blame.EquivocationFailure(evidence)), phase.get()); throw fillBlameMatrix(); }
// 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; }