Do not freeze all the coins just the registered ones.
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
@@ -164,7 +164,7 @@ public class WabiSabiHttpApiIntegrationTests : IClassFixture<WabiSabiApiApplicat
consolidationMode: true);
// Run the coinjoin client task.
- Assert.True(await coinJoinClient.StartCoinJoinAsync(coins, cts.Token));
+ Assert.True((await coinJoinClient.StartCoinJoinAsync(coins, cts.Token)).SuccessfulBroadcast);
var broadcastedTx = await transactionCompleted.Task; // wait for the transaction to be broadcasted.
Assert.NotNull(broadcastedTx);
@@ -309,7 +309,7 @@ public class WabiSabiHttpApiIntegrationTests : IClassFixture<WabiSabiApiApplicat
await Assert.ThrowsAsync<AggregateException>(async () => await Task.WhenAll(new Task[] { badCoinsTask, coinJoinTask }));
Assert.True(badCoinsTask.IsFaulted);
- Assert.True(coinJoinTask.Result);
+ Assert.True(coinJoinTask.Result.SuccessfulBroadcast);
var broadcastedTx = await transactionCompleted.Task; // wait for the transaction to be broadcasted.
Assert.NotNull(broadcastedTx);

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -106,7 +106,7 @@ public class CoinJoinClient
return roundState;
}
- public async Task<bool> StartCoinJoinAsync(IEnumerable<SmartCoin> coinCandidates, CancellationToken cancellationToken)
+ public async Task<CoinJoinResult> StartCoinJoinAsync(IEnumerable<SmartCoin> coinCandidates, CancellationToken cancellationToken)
{
var tryLimit = 6;
@@ -120,21 +120,22 @@ public class CoinJoinClient
for (var tries = 0; tries < tryLimit; tries++)
{
- if (await StartRoundAsync(coins, currentRoundState, cancellationToken).ConfigureAwait(false))
+ CoinJoinResult result = await StartRoundAsync(coins, currentRoundState, cancellationToken).ConfigureAwait(false);
+ if (!result.GoForBlameRound)
{
- return true;
+ return result;
}
currentRoundState = await WaitForBlameRoundAsync(currentRoundState.Id, cancellationToken).ConfigureAwait(false);
}
- return false;
+ throw new InvalidOperationException($"Blame rounds were not successful.");
}
/// <summary>Attempt to participate in a specified round.</summary>
/// <param name="roundState">Defines the round parameter and state information to use.</param>
/// <returns><c>True</c>: client should end the current CoinJoin progress. <c>False</c>: continue with the blame rounds.</returns>
- public async Task<bool> StartRoundAsync(IEnumerable<SmartCoin> smartCoins, RoundState roundState, CancellationToken cancellationToken)
+ public async Task<CoinJoinResult> StartRoundAsync(IEnumerable<SmartCoin> smartCoins, RoundState roundState, CancellationToken cancellationToken)
{
var constructionState = roundState.Assert<ConstructionState>();
@@ -148,13 +149,20 @@ public class CoinJoinClient
catch (UnexpectedRoundPhaseException ex)
{
Logger.LogInfo($"Round ({roundState.Id}): Registration phase ended by the coordinator: '{ex.Message}'.");
- return true;
+ return new CoinJoinResult(
+ GoForBlameRound: false,
+ SuccessfulBroadcast: false,
+ RegisteredCoins: Enumerable.Empty<SmartCoin>());
}
if (!registeredAliceClients.Any())
{
Logger.LogInfo($"Round ({roundState.Id}): There is no available alices to participate with.");
- return true;
+
+ return new CoinJoinResult(
+ GoForBlameRound: false,
+ SuccessfulBroadcast: false,
+ RegisteredCoins: Enumerable.Empty<SmartCoin>());
}
try
@@ -231,7 +239,10 @@ public class CoinJoinClient
LogCoinJoinSummary(registeredAliceClients, outputTxOuts, unsignedCoinJoin, roundState);
- return finalRoundState.WasTransactionBroadcast;
+ return new CoinJoinResult(
+ GoForBlameRound: !finalRoundState.WasTransactionBroadcast,
+ SuccessfulBroadcast: finalRoundState.WasTransactionBroadcast,
+ RegisteredCoins: registeredAliceClients.Select(a => a.SmartCoin));
}
finally
{

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinManager.cs
WalletWasabi/WabiSabi/Client/CoinJoinManager.cs
@@ -160,10 +160,10 @@ public class CoinJoinManager : BackgroundService
try
{
- var success = await finishedCoinJoin.CoinJoinTask.ConfigureAwait(false);
- if (success)
+ var result = await finishedCoinJoin.CoinJoinTask.ConfigureAwait(false);
+ if (result.SuccessfulBroadcast)
{
- CoinRefrigerator.Freeze(finishedCoinJoin.CoinCandidates);
+ CoinRefrigerator.Freeze(result.RegisteredCoins);
MarkDestinationsUsed(finishedCoinJoin);
Logger.LogInfo($"{logPrefix} finished!");
}
@@ -231,7 +231,7 @@ public class CoinJoinManager : BackgroundService
finishedCoinJoin.Wallet,
finishedCoinJoin.CoinJoinTask.Status switch
{
- TaskStatus.RanToCompletion when finishedCoinJoin.CoinJoinTask.Result => CompletionStatus.Success,
+ TaskStatus.RanToCompletion when finishedCoinJoin.CoinJoinTask.Result.SuccessfulBroadcast => CompletionStatus.Success,
TaskStatus.Canceled => CompletionStatus.Canceled,
TaskStatus.Faulted => CompletionStatus.Failed,
_ => CompletionStatus.Unknown,

ADD
WalletWasabi/WabiSabi/Client/CoinJoinResult.cs
+using System.Collections.Generic;
+using WalletWasabi.Blockchain.TransactionOutputs;
+
+namespace WalletWasabi.WabiSabi.Client;
+
+public record CoinJoinResult(
+ bool GoForBlameRound,
+ bool SuccessfulBroadcast,
+ IEnumerable<SmartCoin> RegisteredCoins)
+{
+}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinTracker.cs
WalletWasabi/WabiSabi/Client/CoinJoinTracker.cs
@@ -29,7 +29,7 @@ public class CoinJoinTracker : IDisposable
private CancellationTokenSource CancellationTokenSource { get; }
public Wallet Wallet { get; }
- public Task<bool> CoinJoinTask { get; }
+ public Task<CoinJoinResult> CoinJoinTask { get; }
public IEnumerable<SmartCoin> CoinCandidates { get; }
public IEnumerable<IDestination> Destinations => CoinJoinClient.Destinations;
public bool IsCompleted => CoinJoinTask.IsCompleted;

Do not freeze all the coins just the registered ones.