Try reducing complexity
MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -172,111 +172,31 @@ namespace WalletWasabi.Services
{
if (inputRegistrableRound.AliceClient == null) // If didn't register already, check what can we register.
{
- try
- {
- IEnumerable<(uint256 txid, int index)> registrableCoins = State.GetRegistrableCoins(
- inputRegistrableRound.State.MaximumInputCountPerPeer,
- inputRegistrableRound.State.Denomination,
- inputRegistrableRound.State.FeePerInputs,
- inputRegistrableRound.State.FeePerOutputs);
-
- if (registrableCoins.Count() > 0)
- {
- BitcoinAddress changeAddress = null;
- BitcoinAddress activeAddress = null;
- lock (CustomChangeAddressesLock)
- {
- if (CustomChangeAddresses.Count > 0)
- {
- changeAddress = CustomChangeAddresses.First();
- CustomChangeAddresses.RemoveFirst();
- }
- }
- lock (CustomActiveAddressesLock)
- {
- if (CustomActiveAddresses.Count > 0)
- {
- activeAddress = CustomActiveAddresses.First();
- CustomActiveAddresses.RemoveFirst();
- }
+ await TryRegisterCoinsAsync(inputRegistrableRound);
}
- changeAddress = changeAddress ?? KeyManager.GenerateNewKey("ZeroLink Change", KeyState.Locked, isInternal: true, toFile: false).GetP2wpkhAddress(Network);
- activeAddress = activeAddress ?? KeyManager.GenerateNewKey("ZeroLink Mixed Coin", KeyState.Locked, isInternal: true, toFile: false).GetP2wpkhAddress(Network);
- KeyManager.ToFile();
-
- var blind = CoordinatorPubKey.Blind(activeAddress.ScriptPubKey.ToBytes());
-
- var inputProofs = new List<InputProofModel>();
- foreach ((uint256 txid, int index) coinReference in registrableCoins)
- {
- var coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
- if (coin == null) throw new NotSupportedException("This is impossible.");
- var inputProof = new InputProofModel
+ else // We registered, let's confirm we're online.
{
- Input = coin.GetOutPoint(),
- Proof = coin.Secret.PrivateKey.SignMessage(ByteHelpers.ToHex(blind.BlindedData))
- };
- inputProofs.Add(inputProof);
+ await TryConfirmConnectionAsync(inputRegistrableRound);
}
- AliceClient aliceClient = await AliceClient.CreateNewAsync(changeAddress, blind.BlindedData, inputProofs, CcjHostUri, TorSocks5EndPoint);
-
- byte[] unblindedSignature = CoordinatorPubKey.UnblindSignature(aliceClient.BlindedOutputSignature, blind.BlindingFactor);
-
- if (!CoordinatorPubKey.Verify(unblindedSignature, activeAddress.ScriptPubKey.ToBytes()))
- {
- throw new NotSupportedException("Coordinator did not sign the blinded output properly.");
}
- CcjClientRound roundRegistered = State.GetSingleOrDefaultRound(aliceClient.RoundId);
- if (roundRegistered == null)
- {
- // If our SatoshiClient doesn't yet know about the round because of the dealy create it.
- // Make its state as it'd be the same as our assumed round was, except the roundId and registeredPeerCount, it'll be updated later.
- roundRegistered = new CcjClientRound(CcjRunningRoundState.CloneExcept(inputRegistrableRound.State, aliceClient.RoundId, registeredPeerCount: 1));
- State.AddOrReplaceRound(roundRegistered);
- }
-
- foreach ((uint256 txid, int index) coinReference in registrableCoins)
+ foreach (int ongoingRoundId in State.GetActivelyMixingRounds())
{
- var coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
- if (coin == null) throw new NotSupportedException("This is impossible.");
- roundRegistered.CoinsRegistered.Add(coin);
- State.RemoveCoinFromWaitingList(coin);
+ await TryHandleOngoingRoundAsync(ongoingRoundId);
}
- roundRegistered.ActiveOutputAddress = activeAddress;
- roundRegistered.ChangeOutputAddress = changeAddress;
- roundRegistered.UnblindedSignature = unblindedSignature;
- roundRegistered.AliceClient = aliceClient;
- }
- }
- catch (Exception ex)
- {
- Logger.LogError<CcjClient>(ex);
}
}
- else // We registered, let's confirm we're online.
- {
- try
- {
- string roundHash = await inputRegistrableRound.AliceClient.PostConfirmationAsync();
- if (roundHash != null) // Then the phase went to connection confirmation.
+ catch (TaskCanceledException ex)
{
- inputRegistrableRound.RoundHash = roundHash;
- inputRegistrableRound.State.Phase = CcjRoundPhase.ConnectionConfirmation;
- }
+ Logger.LogTrace<CcjClient>(ex);
}
catch (Exception ex)
{
- if (ex.Message.StartsWith("NotFound", StringComparison.Ordinal)) // Alice timed out.
- {
- State.ClearRoundRegistration(inputRegistrableRound.State.RoundId);
- }
Logger.LogError<CcjClient>(ex);
}
}
- }
- foreach (int ongoingRoundId in State.GetActivelyMixingRounds())
+ private async Task TryHandleOngoingRoundAsync(int ongoingRoundId)
{
try
{
@@ -378,11 +298,106 @@ namespace WalletWasabi.Services
Logger.LogError<CcjClient>(ex);
}
}
+
+ private async Task TryConfirmConnectionAsync(CcjClientRound inputRegistrableRound)
+ {
+ try
+ {
+ string roundHash = await inputRegistrableRound.AliceClient.PostConfirmationAsync();
+ if (roundHash != null) // Then the phase went to connection confirmation.
+ {
+ inputRegistrableRound.RoundHash = roundHash;
+ inputRegistrableRound.State.Phase = CcjRoundPhase.ConnectionConfirmation;
}
}
- catch (TaskCanceledException ex)
+ catch (Exception ex)
{
- Logger.LogTrace<CcjClient>(ex);
+ if (ex.Message.StartsWith("NotFound", StringComparison.Ordinal)) // Alice timed out.
+ {
+ State.ClearRoundRegistration(inputRegistrableRound.State.RoundId);
+ }
+ Logger.LogError<CcjClient>(ex);
+ }
+ }
+
+ private async Task TryRegisterCoinsAsync(CcjClientRound inputRegistrableRound)
+ {
+ try
+ {
+ IEnumerable<(uint256 txid, int index)> registrableCoins = State.GetRegistrableCoins(
+ inputRegistrableRound.State.MaximumInputCountPerPeer,
+ inputRegistrableRound.State.Denomination,
+ inputRegistrableRound.State.FeePerInputs,
+ inputRegistrableRound.State.FeePerOutputs);
+
+ if (registrableCoins.Count() > 0)
+ {
+ BitcoinAddress changeAddress = null;
+ BitcoinAddress activeAddress = null;
+ lock (CustomChangeAddressesLock)
+ {
+ if (CustomChangeAddresses.Count > 0)
+ {
+ changeAddress = CustomChangeAddresses.First();
+ CustomChangeAddresses.RemoveFirst();
+ }
+ }
+ lock (CustomActiveAddressesLock)
+ {
+ if (CustomActiveAddresses.Count > 0)
+ {
+ activeAddress = CustomActiveAddresses.First();
+ CustomActiveAddresses.RemoveFirst();
+ }
+ }
+ changeAddress = changeAddress ?? KeyManager.GenerateNewKey("ZeroLink Change", KeyState.Locked, isInternal: true, toFile: false).GetP2wpkhAddress(Network);
+ activeAddress = activeAddress ?? KeyManager.GenerateNewKey("ZeroLink Mixed Coin", KeyState.Locked, isInternal: true, toFile: false).GetP2wpkhAddress(Network);
+ KeyManager.ToFile();
+
+ var blind = CoordinatorPubKey.Blind(activeAddress.ScriptPubKey.ToBytes());
+
+ var inputProofs = new List<InputProofModel>();
+ foreach ((uint256 txid, int index) coinReference in registrableCoins)
+ {
+ var coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
+ if (coin == null) throw new NotSupportedException("This is impossible.");
+ var inputProof = new InputProofModel
+ {
+ Input = coin.GetOutPoint(),
+ Proof = coin.Secret.PrivateKey.SignMessage(ByteHelpers.ToHex(blind.BlindedData))
+ };
+ inputProofs.Add(inputProof);
+ }
+ AliceClient aliceClient = await AliceClient.CreateNewAsync(changeAddress, blind.BlindedData, inputProofs, CcjHostUri, TorSocks5EndPoint);
+
+ byte[] unblindedSignature = CoordinatorPubKey.UnblindSignature(aliceClient.BlindedOutputSignature, blind.BlindingFactor);
+
+ if (!CoordinatorPubKey.Verify(unblindedSignature, activeAddress.ScriptPubKey.ToBytes()))
+ {
+ throw new NotSupportedException("Coordinator did not sign the blinded output properly.");
+ }
+
+ CcjClientRound roundRegistered = State.GetSingleOrDefaultRound(aliceClient.RoundId);
+ if (roundRegistered == null)
+ {
+ // If our SatoshiClient doesn't yet know about the round because of the dealy create it.
+ // Make its state as it'd be the same as our assumed round was, except the roundId and registeredPeerCount, it'll be updated later.
+ roundRegistered = new CcjClientRound(CcjRunningRoundState.CloneExcept(inputRegistrableRound.State, aliceClient.RoundId, registeredPeerCount: 1));
+ State.AddOrReplaceRound(roundRegistered);
+ }
+
+ foreach ((uint256 txid, int index) coinReference in registrableCoins)
+ {
+ var coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
+ if (coin == null) throw new NotSupportedException("This is impossible.");
+ roundRegistered.CoinsRegistered.Add(coin);
+ State.RemoveCoinFromWaitingList(coin);
+ }
+ roundRegistered.ActiveOutputAddress = activeAddress;
+ roundRegistered.ChangeOutputAddress = changeAddress;
+ roundRegistered.UnblindedSignature = unblindedSignature;
+ roundRegistered.AliceClient = aliceClient;
+ }
}
catch (Exception ex)
{
@@ -585,6 +600,22 @@ namespace WalletWasabi.Services
SmartCoin coinWaitingForMix = State.GetSingleOrDefaultFromWaitingList(coinToDequeue);
if (coinWaitingForMix != null) // If it is not being mixed, we can just remove it.
+ {
+ RemoveCoin(coinWaitingForMix);
+ }
+ }
+
+ if (exceptions.Count == 1)
+ {
+ throw exceptions.Single();
+ }
+ else if (exceptions.Count > 0)
+ {
+ throw new AggregateException(exceptions);
+ }
+ }
+
+ private void RemoveCoin(SmartCoin coinWaitingForMix)
{
State.RemoveCoinFromWaitingList(coinWaitingForMix);
coinWaitingForMix.Locked = false;
@@ -600,17 +631,6 @@ namespace WalletWasabi.Services
}
Logger.LogInfo<CcjClient>($"Coin dequeued: {coinWaitingForMix.Index}:{coinWaitingForMix.TransactionId}.");
}
- }
-
- if (exceptions.Count == 1)
- {
- throw exceptions.Single();
- }
- else if (exceptions.Count > 0)
- {
- throw new AggregateException(exceptions);
- }
- }
public async Task StopAsync()
{

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -90,9 +90,8 @@ namespace WalletWasabi.Services
catch (Exception ex)
{
toRemove.Add(line);
- var rpce = ex as RPCException;
- var logEntry = rpce != null && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY
+ var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY
? $"CoinJoins file contains invalid transaction ID {line}"
: $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\".";

Try reducing complexity