// Check for players with insufficient funds.
    private void blameInsufficientFunds()
        throws CoinNetworkException, TimeoutException, Matrix, IOException, InterruptedException,
            FormatException, AddressFormatException {

      List<VerificationKey> offenders = new LinkedList<>();

      // Check that each participant has the required amounts.
      for (VerificationKey player : players.values()) {
        if (!coin.sufficientFunds(player.address(), amount + fee)) {
          // Enter the blame phase.
          offenders.add(player);
        }
      }

      // If they do, return.
      if (offenders.isEmpty()) return;

      // If not, enter blame phase and find offending transactions.
      phase.set(Phase.Blame);
      Message blameMessage = messages.make();
      for (VerificationKey offender : offenders) {
        blameMessage = blameMessage.attach(Blame.InsufficientFunds(offender));
      }

      // Broadcast offending transactions.
      mailbox.broadcast(blameMessage, phase.get());

      // Get all subsequent blame messages.
      throw fillBlameMatrix();
    }
    // In the shuffle phase, we have to receive a set of strings from the previous player and
    // decrypt them all.
    final Message decryptAll(Message message, DecryptionKey key, int expected)
        throws IOException, InterruptedException, FormatException {

      Message decrypted = messages.make();

      int count = 0;
      Set<String> addrs = new HashSet<>(); // Used to check that all addresses are different.

      while (!message.isEmpty()) {
        String encrypted = message.readString();
        message = message.rest();

        addrs.add(encrypted);
        count++;
        decrypted = decrypted.attach(key.decrypt(encrypted));
      }

      if (addrs.size() != count || count != expected) {
        phase.set(Phase.Blame);
        mailbox.broadcast(
            messages.make().attach(Blame.ShuffleFailure(players.get(N))), phase.get());

        return null;
      }

      return decrypted;
    }
    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();
    }
    // Some misbehavior that has occurred during the shuffle phase and we want to
    // find out what happened!
    private void blameShuffleMisbehavior()
        throws TimeoutException, Matrix, InterruptedException, FormatException, IOException {

      // Skip to phase 4 and do an equivocation check.
      phase.set(Phase.EquivocationCheck);
      equivocationCheck(encryptionKeys, newAddresses, true);
    }
    // In the broadcast phase, we have to either receive all the
    // new addresses or send them all out. This is set off in its own function so that the
    // malicious machine can override it.
    Deque<Address> readAndBroadcastNewAddresses(Message shuffled)
        throws IOException, InterruptedException, TimeoutException, BlameException,
            FormatException {

      Deque<Address> newAddresses;
      if (me == N) {
        // The last player adds his own new address in without
        // encrypting anything and shuffles the result.
        newAddresses = readNewAddresses(shuffled);
        mailbox.broadcast(shuffled, phase.get());
      } else {
        // All other players just receive their addresses from the last one.
        newAddresses = readNewAddresses(mailbox.receiveFrom(players.get(N), phase.get()));
      }

      return newAddresses;
    }
示例#6
0
 final void execute(PhaseId startPhaseId) throws IOException {
   PhaseId currentPhaseId = startPhaseId;
   while (currentPhaseId != null) {
     IPhase currentPhase = findPhase(currentPhaseId);
     CurrentPhase.set(currentPhaseId);
     phaseListenerManager.notifyBeforePhase(currentPhaseId, lifecycle);
     PhaseId nextPhaseId = currentPhase.execute(getDisplay());
     phaseListenerManager.notifyAfterPhase(currentPhaseId, lifecycle);
     currentPhaseId = nextPhaseId;
   }
 }
    void checkDoubleSpending(Transaction t)
        throws InterruptedException, IOException, FormatException, TimeoutException, Matrix,
            CoinNetworkException, AddressFormatException {

      // Check for double spending.
      Message doubleSpend = messages.make();
      for (VerificationKey key : players.values()) {
        Transaction o = coin.getConflictingTransaction(t, key.address(), amount);
        if (o != null) {
          doubleSpend = doubleSpend.attach(Blame.DoubleSpend(key, o));
        }
      }

      if (!doubleSpend.isEmpty()) {
        phase.set(Phase.Blame);

        mailbox.broadcast(doubleSpend, 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();
    }
    // Everyone except player 1 creates a new keypair and sends it around to everyone else.
    DecryptionKey broadcastNewKey(Map<VerificationKey, Address> changeAddresses)
        throws TimeoutException, InterruptedException, IOException, FormatException {
      DecryptionKey dk = null;
      dk = crypto.makeDecryptionKey();

      // Broadcast the public key and store it in the set with everyone else's.
      encryptionKeys.put(vk, dk.EncryptionKey());
      changeAddresses.put(vk, change);
      Message message = messages.make().attach(dk.EncryptionKey());
      if (change != null) {
        message = message.attach(change);
      }
      mailbox.broadcast(message, phase.get());
      return dk;
    }
示例#10
0
    Transaction protocolDefinition()
        throws TimeoutException, Matrix, InterruptedException, FormatException, IOException,
            CoinNetworkException, ExecutionException, AddressFormatException {

      if (amount <= 0) {
        throw new IllegalArgumentException();
      }

      // Phase 1: Announcement
      // In the announcement phase, participants distribute temporary encryption keys.
      phase.set(Phase.Announcement);
      log.info("Player " + me + " begins CoinShuffle protocol " + " with " + N + " players.");
      System.out.println(
          "Player " + me + " begins CoinShuffle protocol " + " with " + N + " players.");

      // Check for sufficient funds.
      // There was a problem with the wording of the original paper which would have meant
      // that player 1's funds never would have been checked, but it's necessary to check
      // everybody.
      blameInsufficientFunds();
      System.out.println("Player " + me + " finds sufficient funds");

      // This will contain the change addresses.
      Map<VerificationKey, Address> changeAddresses = new HashMap<>();

      // Everyone creates a new keypair and sends it around to everyone else.
      // Note that the key for player 1 is not actually used; however, player 1
      // needs to send an announcement message at this point too because he might
      // have a change address. Therefore he just follows the same procedure as
      // everyone else.
      dk = broadcastNewKey(changeAddresses);
      System.out.println("Player " + me + " has broadcasted the new encryption key.");

      // Now we wait to receive similar key from everyone else.
      Map<VerificationKey, Message> announcement;
      try {
        announcement = mailbox.receiveFromMultiple(playerSet(1, N), phase.get());
      } catch (BlameException e) {
        // might receive blame messages about insufficient funds.
        phase.set(Phase.Blame);
        throw fillBlameMatrix();
      }
      System.out.println("Player " + me + " is about to read announcements.");

      readAnnouncements(announcement, encryptionKeys, changeAddresses);

      // Phase 2: Shuffle
      // In the shuffle phase, players go in order and reorder the addresses they have been
      // given by the previous player. They insert their own address in a random location.
      // Everyone has the incentive to insert their own address at a random location, which
      // is sufficient to ensure that the result appears random to everybody.
      phase.set(Phase.Shuffling);
      System.out.println("Player " + me + " reaches phase 2: " + encryptionKeys);

      try {
        // Player one begins the cycle and encrypts its new address with everyone's
        // public encryption key, in order.
        // Each subsequent player reorders the cycle and removes one layer of encryption.
        Message shuffled;
        if (me != 1) {
          shuffled = decryptAll(mailbox.receiveFrom(players.get(me - 1), phase.get()), dk, me - 1);

          if (shuffled == null) {
            blameShuffleMisbehavior();
          }
        } else {
          shuffled = messages.make();
        }

        // Make an encrypted address to the mix, and then shuffle everything ourselves.
        shuffled = shufflePhase(shuffled, addrNew);

        // Pass it along to the next player.
        if (me != N) {
          mailbox.send(shuffled, phase.get(), players.get(me + 1));
        }

        // Phase 3: broadcast outputs.
        // In this phase, the last player just broadcasts the transaction to everyone else.
        phase.set(Phase.BroadcastOutput);
        System.out.println("Player " + me + " reaches phase 3 ");

        newAddresses = readAndBroadcastNewAddresses(shuffled);

      } catch (BlameException e) {
        switch (e.packet.payload().readBlame().reason) {
          case MissingOutput:
            {
              // This was sent by a player in phase 3, which means that the new addresses
              // were sent out by the last player, which means that we need to receive
              // the new addresses before proceeding.
              if (newAddresses == null) {
                newAddresses =
                    readNewAddresses(mailbox.receiveFromBlameless(players.get(N), phase.get()));
              }
              // Continue on to next case.
            }
          case ShuffleFailure:
            {
              blameShuffleMisbehavior();
            }
          default:
            {
              throw fillBlameMatrix();
            }
        }
      }

      // Everyone else receives the broadcast and checks that their message was included.
      if (!newAddresses.contains(addrNew)) {
        phase.set(Phase.Blame);
        mailbox.broadcast(messages.make().attach(Blame.MissingOutput(players.get(N))), phase.get());

        blameShuffleMisbehavior();
      }

      // Phase 4: equivocation check.
      // In this phase, participants check whether any player has history different
      // encryption keys to different players.
      phase.set(Phase.EquivocationCheck);
      System.out.println("Player " + me + " reaches phase 4: ");

      equivocationCheck(encryptionKeys, newAddresses, false);

      // Phase 5: verification and submission.
      // Everyone creates a Bitcoin transaction and signs it, then broadcasts the signature.
      // If all signatures check out, then the transaction is history into the net.
      phase.set(Phase.VerificationAndSubmission);
      System.out.println("Player " + me + " reaches phase 5. ");

      List<VerificationKey> inputs = new LinkedList<>();
      for (int i = 1; i <= N; i++) {
        inputs.add(players.get(i));
      }

      // Generate the join transaction.
      Transaction t = coin.shuffleTransaction(amount, fee, inputs, newAddresses, changeAddresses);

      checkDoubleSpending(t);
      if (t == null) throw new RuntimeException("Transaction in null. This should not happen.");

      // Generate the input script using our signing key.
      Message inputScript = messages.make().attach(t.sign(sk));

      System.out.println("Player " + me + " broadcasts signature ");

      mailbox.broadcast(inputScript, phase.get());

      // Send signature messages around and receive them from other players.
      // During this time we could also get notices of invalid signatures
      // or double spends, so we have to watch out for that.
      Map<VerificationKey, Message> signatureMessages = null;
      boolean invalidClaim = false;
      try {
        signatureMessages = mailbox.receiveFromMultiple(playerSet(1, N), phase.get());
        signatureMessages.put(vk, inputScript);
        System.out.println("Player " + me + " receives signatures ");
      } catch (BlameException e) {
        switch (e.packet.payload().readBlame().reason) {
          case InvalidSignature:
            {
              // Continue receiving messages and ignore any further blame messages.
              signatureMessages =
                  mailbox.receiveFromMultipleBlameless(playerSet(1, N), phase.get());

              invalidClaim = true;
              break;
            }
          case DoubleSpend:
          default:
            {
              phase.set(Phase.Blame);
              // Could receive notice of double spending here.
              throw fillBlameMatrix();
            }
        }
      }

      // Verify the signatures.
      Map<VerificationKey, Bytestring> invalid = new HashMap<>();
      for (Map.Entry<VerificationKey, Message> sig : signatureMessages.entrySet()) {
        VerificationKey key = sig.getKey();
        Bytestring signature = sig.getValue().readSignature();
        signatures.put(key, signature);

        if (!t.addInputScript(signature)) {
          invalid.put(key, signature);
        }
      }

      if (invalid.size() > 0 || invalidClaim) {
        phase.set(Phase.Blame);
        Message blameMessage = messages.make();

        for (Map.Entry<VerificationKey, Bytestring> bad : invalid.entrySet()) {
          VerificationKey key = bad.getKey();
          Bytestring signature = bad.getValue();

          blameMessage = blameMessage.attach(Blame.InvalidSignature(key, signature));
        }
        mailbox.broadcast(blameMessage, phase.get());
        throw fillBlameMatrix();
      }

      // Send the transaction into the net.
      t.send();

      // The protocol has completed successfully.
      phase.set(Phase.Completed);

      return t;
    }