private async Task RegisterOutputAsync(CcjClientRound ongoingRound) { IEnumerable <TxoRef> registeredInputs = ongoingRound.Registration.CoinsRegistered.Select(x => x.GetTxoRef()); var shuffledOutputs = ongoingRound.Registration.ActiveOutputs.ToList(); shuffledOutputs.Shuffle(); foreach (var activeOutput in shuffledOutputs) { using (var bobClient = new BobClient(CcjHostUriAction, TorSocks5EndPoint)) { if (!await bobClient.PostOutputAsync(ongoingRound.RoundId, activeOutput)) { Logger.LogWarning <AliceClient>($"Round ({ongoingRound.State.RoundId}) Bobs did not have enough time to post outputs before timeout. If you see this message, contact nopara73, so he can optimize the phase timeout periods to the worst Internet/Tor connections, which may be yours.)"); break; } // Unblind our exposed links. foreach (TxoRef input in registeredInputs) { if (ExposedLinks.ContainsKey(input)) // Should never not contain, but oh well, let's not disrupt the round for this. { var found = ExposedLinks[input].FirstOrDefault(x => x.Key.GetP2wpkhAddress(Network) == activeOutput.Address); if (found != default) { found.IsBlinded = false; } else { // Should never happen, but oh well we can autocorrect it so why not. ExposedLinks[input] = ExposedLinks[input].Append(new HdPubKeyBlindedPair(KeyManager.GetKeyForScriptPubKey(activeOutput.Address.ScriptPubKey), false)); } } } } } ongoingRound.Registration.SetPhaseCompleted(CcjRoundPhase.OutputRegistration); Logger.LogInfo <AliceClient>($"Round ({ongoingRound.State.RoundId}) Bob Posted outputs: {ongoingRound.Registration.ActiveOutputs.Count()}."); }
private (HdPubKey change, IEnumerable <HdPubKey> active) GetOutputsToRegister(Money baseDenomination, int mixingLevelCount, IEnumerable <TxoRef> coinsToRegister) { // Figure out how many mixing level we need to register active outputs. Money inputSum = Money.Zero; foreach (TxoRef coinReference in coinsToRegister) { SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference); inputSum += coin.Amount; } int maximumMixingLevelCount = 1; var denominations = new List <Money> { baseDenomination }; for (int i = 1; i < mixingLevelCount; i++) { Money denom = denominations.Last() * 2; denominations.Add(denom); if (inputSum > denom) { maximumMixingLevelCount = i + 1; } } string changeLabel = "ZeroLink Change"; string activeLabel = "ZeroLink Mixed Coin"; var keysToSurelyRegister = ExposedLinks.Where(x => coinsToRegister.Contains(x.Key)).SelectMany(x => x.Value).Select(x => x.Key).ToArray(); var keysTryNotToRegister = ExposedLinks.SelectMany(x => x.Value).Select(x => x.Key).Except(keysToSurelyRegister).ToArray(); // Get all locked internal keys we have and assert we have enough. KeyManager.AssertLockedInternalKeysIndexed(howMany: maximumMixingLevelCount + 1); IEnumerable <HdPubKey> allLockedInternalKeys = KeyManager.GetKeys(x => x.IsInternal && x.KeyState == KeyState.Locked && !keysTryNotToRegister.Contains(x)); // If any of our inputs have exposed address relationship then prefer that. allLockedInternalKeys = keysToSurelyRegister.Concat(allLockedInternalKeys).Distinct(); // Prefer not to bloat the wallet: if (allLockedInternalKeys.Count() <= maximumMixingLevelCount) { allLockedInternalKeys = allLockedInternalKeys.Concat(keysTryNotToRegister).Distinct(); } var newKeys = new List <HdPubKey>(); for (int i = allLockedInternalKeys.Count(); i <= maximumMixingLevelCount + 1; i++) { HdPubKey k = KeyManager.GenerateNewKey("", KeyState.Locked, isInternal: true, toFile: false); newKeys.Add(k); } allLockedInternalKeys = allLockedInternalKeys.Concat(newKeys); // Select the change and active keys to register and label them accordingly. HdPubKey change = allLockedInternalKeys.First(); change.SetLabel(changeLabel); var actives = new List <HdPubKey>(); foreach (HdPubKey active in allLockedInternalKeys.Skip(1).Take(maximumMixingLevelCount)) { actives.Add(active); active.SetLabel(activeLabel); } // Remember which links we are exposing. var outLinks = new List <HdPubKeyBlindedPair> { new HdPubKeyBlindedPair(change, isBlinded: false) }; foreach (var active in actives) { outLinks.Add(new HdPubKeyBlindedPair(active, isBlinded: true)); } foreach (TxoRef coin in coinsToRegister) { if (!ExposedLinks.TryAdd(coin, outLinks)) { var newOutLinks = new List <HdPubKeyBlindedPair>(); foreach (HdPubKeyBlindedPair link in ExposedLinks[coin]) { newOutLinks.Add(link); } foreach (HdPubKeyBlindedPair link in outLinks) { HdPubKeyBlindedPair found = newOutLinks.FirstOrDefault(x => x == link); if (found == default) { newOutLinks.Add(link); } else // If already in it then update the blinded value if it's getting exposed just now. (eg. the change) { if (found.IsBlinded) { found.IsBlinded = link.IsBlinded; } } } ExposedLinks[coin] = newOutLinks; } } // Save our modifications in the keymanager before we give back the selected keys. KeyManager.ToFile(); return(change, actives); }