Ejemplo n.º 1
0
    // 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;
    }
Ejemplo n.º 2
0
  // This function is only called by fillBlameMatrix to collect messages sent in
  // phases 1, 2, and 3. and to organize the information appropriately.
  // If the function returns false, this means that packets has an invalid format.
  private static boolean fillBlameMatrixCollectHistory(
      VerificationKey vk,
      VerificationKey from,
      Queue<Packet> packets,
      Matrix matrix,
      // The messages sent in the broadcast phase by the last player to all the other players.
      Map<VerificationKey, Packet> outputVectors,
      // The list of messages history in phase 2.
      Map<VerificationKey, Packet> shuffleMessages,
      // The keys received by everyone in the announcement phase.
      Map<VerificationKey, EncryptionKey> receivedKeys,
      // The keys sent by everyone in the announcement phase.
      Map<VerificationKey, Map<VerificationKey, EncryptionKey>> sentKeys)
      throws FormatException {

    if (packets == null) return false;
    boolean validPacket = true;

    // Collect all packets received in the appropriate place.
    for (Packet packet : packets) {
      switch (packet.phase()) {
        case BroadcastOutput:
          {
            if (outputVectors.containsKey(from)) {

              // We should only ever receive one such message from each player.
              if (outputVectors.containsKey(from) && !outputVectors.get(from).equals(packet)) {

                log.error(
                    "Player "
                        + vk.toString()
                        + " null blames "
                        + from.toString()
                        + ", case A; "
                        + outputVectors.get(from).toString()
                        + " != "
                        + packet.toString());
                matrix.put(vk, Evidence.Liar(from, new Packet[] {outputVectors.get(from), packet}));
              }
            } else {
              outputVectors.put(from, packet);
            }
            break;
          }
        case Announcement:
          {
            Map<VerificationKey, EncryptionKey> map = sentKeys.get(packet.from());
            if (map == null) {
              map = new HashMap<>();
              sentKeys.put(packet.from(), map);
            }

            EncryptionKey key = packet.payload().readEncryptionKey();
            map.put(from, key);
            receivedKeys.put(packet.from(), key);
            break;
          }
        case Shuffling:
          {
            if (shuffleMessages.containsKey(from) && !shuffleMessages.get(from).equals(packet)) {
              matrix.put(vk, Evidence.Liar(from, new Packet[] {shuffleMessages.get(from), packet}));
            } else {
              shuffleMessages.put(from, packet);
            }
            break;
          }
        default:
          {
            // This case should never happen.
            // It's not malicious but it's not allowed either.
            validPacket = false;
          }
      }
    }

    return validPacket;
  }
Ejemplo n.º 3
0
  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;
  }