Add Phase awaiter
MODIFY
WalletWasabi/WabiSabi/Client/AliceClient.cs
WalletWasabi/WabiSabi/Client/AliceClient.cs
@@ -143,7 +143,7 @@ public class AliceClient
await roundStatusUpdater
.CreateRoundAwaiter(
RoundId,
- roundState => roundState.Phase == Phase.ConnectionConfirmation,
+ Phase.ConnectionConfirmation,
cts.Token)
.ConfigureAwait(false);
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -64,10 +64,12 @@ public class CoinJoinClient
public async Task<bool> StartCoinJoinAsync(IEnumerable<SmartCoin> coins, CancellationToken cancellationToken)
{
- var currentRoundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState =>
- roundState.Phase == Phase.InputRegistration &&
- roundState.InputRegistrationEnd - DateTimeOffset.UtcNow > DoNotRegisterInLastMinuteTimeLimit
- , cancellationToken).ConfigureAwait(false);
+ var currentRoundState = await RoundStatusUpdater
+ .CreateRoundAwaiter(
+ Phase.InputRegistration,
+ roundState => roundState.InputRegistrationEnd - DateTimeOffset.UtcNow > DoNotRegisterInLastMinuteTimeLimit,
+ cancellationToken)
+ .ConfigureAwait(false);
// This should be roughly log(#inputs), it could be set slightly
// higher if more inputs are observed but that involves trusting the
@@ -87,7 +89,9 @@ public class CoinJoinClient
using CancellationTokenSource linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitForBlameRound.Token, cancellationToken);
var blameRoundState = await RoundStatusUpdater
- .CreateRoundAwaiter(roundState => roundState.BlameOf == currentRoundState.Id && roundState.Phase == Phase.InputRegistration, linkedTokenSource.Token)
+ .CreateRoundAwaiter(
+ roundState => roundState.BlameOf == currentRoundState.Id && roundState.Phase == Phase.InputRegistration,
+ linkedTokenSource.Token)
.ConfigureAwait(false);
currentRoundState = blameRoundState;
}
@@ -105,7 +109,6 @@ public class CoinJoinClient
var coinCandidates = SelectCoinsForRound(smartCoins, constructionState.Parameters, ConsolidationMode, MinAnonScoreTarget, SecureRandom);
// Register coins.
-
using PersonCircuit personCircuit = HttpClientFactory.NewHttpClientWithPersonCircuit(out Tor.Http.IHttpClient httpClient);
var registeredAliceClients = await CreateRegisterAndConfirmCoinsAsync(httpClient, coinCandidates, roundState, cancellationToken).ConfigureAwait(false);
@@ -126,7 +129,7 @@ public class CoinJoinClient
var availableVsize = registeredAliceClients.SelectMany(x => x.IssuedVsizeCredentials).Sum(x => x.Value);
// Waiting for OutputRegistration phase, all the Alices confirmed their connections, so the list of the inputs will be complete.
- roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, rs => rs.Phase == Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);
+ roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);
// Calculate outputs values
constructionState = roundState.Assert<ConstructionState>();
@@ -150,7 +153,7 @@ public class CoinJoinClient
await scheduler.StartReissuancesAsync(registeredAliceClients, bobClient, cancellationToken).ConfigureAwait(false);
// Output registration.
- roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, rs => rs.Phase == Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);
+ roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);
Logger.LogDebug($"Round ({roundState.Id}): Output registration phase started.");
await scheduler.StartOutputRegistrationsAsync(outputTxOuts, bobClient, cancellationToken).ConfigureAwait(false);
@@ -161,7 +164,7 @@ public class CoinJoinClient
Logger.LogDebug($"Round ({roundState.Id}): Alices({registeredAliceClients.Length}) successfully signalled ready to sign.");
// Signing.
- roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, rs => rs.Phase == Phase.TransactionSigning, cancellationToken).ConfigureAwait(false);
+ roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundState.Id, Phase.TransactionSigning, cancellationToken).ConfigureAwait(false);
Logger.LogDebug($"Round ({roundState.Id}): Transaction signing phase started.");
var signingState = roundState.Assert<SigningState>();

MODIFY
WalletWasabi/WabiSabi/Client/RoundStateAwaiter.cs
WalletWasabi/WabiSabi/Client/RoundStateAwaiter.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using WalletWasabi.CoinJoin.Common.Models;
+using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Client;
@@ -10,19 +12,27 @@ namespace WalletWasabi.WabiSabi.Client;
public record RoundStateAwaiter
{
public RoundStateAwaiter(
- Predicate<RoundState> predicate,
+ Predicate<RoundState>? predicate,
uint256? roundId,
+ Phase? phase,
CancellationToken cancellationToken)
{
+ if (predicate is null && phase is null)
+ {
+ throw new ArgumentNullException(nameof(predicate));
+ }
+
TaskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
Predicate = predicate;
RoundId = roundId;
+ Phase = phase;
cancellationToken.Register(() => Cancel());
}
private TaskCompletionSource<RoundState> TaskCompletionSource { get; }
- private Predicate<RoundState> Predicate { get; }
+ private Predicate<RoundState>? Predicate { get; }
private uint256? RoundId { get; }
+ private Phase? Phase { get; }
public Task<RoundState> Task => TaskCompletionSource.Task;
@@ -39,15 +49,40 @@ public record RoundStateAwaiter
return true;
}
- var relevantRoundStates = RoundId is null ? allRoundStates.Values.ToArray() : new[] { allRoundStates[RoundId] };
+ foreach (var roundState in allRoundStates.Values.ToArray())
+ {
+ if (RoundId is { })
+ {
+ if (roundState.Id != RoundId)
+ {
+ continue;
+ }
+ }
- foreach (var roundState in relevantRoundStates)
+ if (Phase is { })
{
- if (Predicate(roundState))
+ if (roundState.Phase > Phase)
{
- TaskCompletionSource.SetResult(roundState);
+ TaskCompletionSource.TrySetException(new InvalidOperationException($"Round {RoundId} unexpected phase change. Waiting for '{Phase}' but the round is in '{roundState.Phase}'."));
return true;
}
+
+ if (roundState.Phase != Phase)
+ {
+ continue;
+ }
+ }
+
+ if (Predicate is { })
+ {
+ if (!Predicate(roundState))
+ {
+ continue;
+ }
+ }
+
+ TaskCompletionSource.SetResult(roundState);
+ return true;
}
return false;

MODIFY
WalletWasabi/WabiSabi/Client/RoundStateUpdater.cs
WalletWasabi/WabiSabi/Client/RoundStateUpdater.cs
@@ -4,7 +4,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Bases;
+using WalletWasabi.CoinJoin.Common.Models;
using WalletWasabi.WabiSabi.Backend.PostRequests;
+using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Client;
@@ -40,13 +42,13 @@ public class RoundStateUpdater : PeriodicRunner
}
}
- public Task<RoundState> CreateRoundAwaiter(uint256? roundId, Predicate<RoundState> predicate, CancellationToken cancellationToken)
+ private Task<RoundState> CreateRoundAwaiter(uint256? roundId, Phase? phase, Predicate<RoundState>? predicate, CancellationToken cancellationToken)
{
RoundStateAwaiter? roundStateAwaiter = null;
lock (AwaitersLock)
{
- roundStateAwaiter = new RoundStateAwaiter(predicate, roundId, cancellationToken);
+ roundStateAwaiter = new RoundStateAwaiter(predicate, roundId, phase, cancellationToken);
Awaiters.Add(roundStateAwaiter);
}
@@ -63,7 +65,22 @@ public class RoundStateUpdater : PeriodicRunner
public Task<RoundState> CreateRoundAwaiter(Predicate<RoundState> predicate, CancellationToken cancellationToken)
{
- return CreateRoundAwaiter(null, predicate, cancellationToken);
+ return CreateRoundAwaiter(null, null, predicate, cancellationToken);
+ }
+
+ public Task<RoundState> CreateRoundAwaiter(uint256 roundId, Phase phase, CancellationToken cancellationToken)
+ {
+ return CreateRoundAwaiter(roundId, phase, null, cancellationToken);
+ }
+
+ public Task<RoundState> CreateRoundAwaiter(Phase phase, CancellationToken cancellationToken)
+ {
+ return CreateRoundAwaiter(null, phase, null, cancellationToken);
+ }
+
+ public Task<RoundState> CreateRoundAwaiter(Phase phase, Predicate<RoundState> predicate, CancellationToken cancellationToken)
+ {
+ return CreateRoundAwaiter(null, phase, predicate, cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)

Add Phase awaiter