// 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; }
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; }