remove Failed phase
Rename `TransactionBroadcasting` phase to `Ended` and use it for both
terminal states, with a new boolean `IsTransactionBroadcasted` to
distinguish between the successful and failed states in the `Ended`
phase.
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs
@@ -124,7 +124,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.DoesNotContain(round, arena.Rounds);
+ Assert.DoesNotContain(round, arena.ActiveRounds);
Assert.Equal(3, arena.Prison.CountInmates().noted);
Assert.Equal(0, arena.Prison.CountInmates().banned);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs
@@ -128,8 +128,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
round.Alices.Add(WabiSabiFactory.CreateAlice());
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.InputRegistration, round.Phase);
- Assert.DoesNotContain(round, arena.Rounds);
+ Assert.Equal(Phase.Ended, round.Phase);
+ Assert.DoesNotContain(round, arena.ActiveRounds);
await arena.StopAsync(CancellationToken.None);
}
@@ -157,8 +157,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, blameRound);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.InputRegistration, blameRound.Phase);
- Assert.DoesNotContain(blameRound, arena.Rounds);
+ Assert.Equal(Phase.Ended, blameRound.Phase);
+ Assert.DoesNotContain(blameRound, arena.ActiveRounds);
await arena.StopAsync(CancellationToken.None);
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
@@ -45,7 +45,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.TransactionBroadcasting, round.Phase);
+ Assert.Equal(Phase.Ended, round.Phase);
await arena.StopAsync(CancellationToken.None);
}
@@ -81,7 +81,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.Failed, round.Phase);
+ Assert.Equal(Phase.Ended, round.Phase);
+ Assert.False(round.IsTransactionBroadcasted);
Assert.Empty(arena.Prison.GetInmates());
await arena.StopAsync(CancellationToken.None);
@@ -119,7 +120,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.Failed, round.Phase);
+ Assert.Equal(Phase.Ended, round.Phase);
+ Assert.False(round.IsTransactionBroadcasted);
// There should be no inmate, because we aren't punishing spenders with banning
// as there's no reason to ban already spent UTXOs,
@@ -160,7 +162,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
var signedCoinJoin = round.Assert<SigningState>().CreateTransaction();
await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(Phase.Failed, round.Phase);
+ Assert.Equal(Phase.Ended, round.Phase);
+ Assert.False(round.IsTransactionBroadcasted);
Assert.Empty(arena.Rounds.Where(x => x.IsBlameRound));
Assert.Contains(aliceClient2.Coin.Outpoint, arena.Prison.GetInmates().Select(x => x.Utxo));

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Models/RoundStateUpdaterTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Models/RoundStateUpdaterTests.cs
@@ -42,7 +42,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Models
var round1IRTask = roundStatusUpdater.CreateRoundAwaiter(roundState1.Id, rs => rs.Phase == Phase.InputRegistration, cancellationToken);
var round1ORTask = roundStatusUpdater.CreateRoundAwaiter(roundState1.Id, rs => rs.Phase == Phase.OutputRegistration, cancellationToken);
var round1TSTask = roundStatusUpdater.CreateRoundAwaiter(roundState1.Id, rs => rs.Phase == Phase.TransactionSigning, round1TSCts.Token);
- var round1TBTask = roundStatusUpdater.CreateRoundAwaiter(roundState1.Id, rs => rs.Phase == Phase.TransactionBroadcasting, cancellationToken);
+ var round1TBTask = roundStatusUpdater.CreateRoundAwaiter(roundState1.Id, rs => rs.Phase == Phase.Ended, cancellationToken);
// Start
await roundStatusUpdater.StartAsync(cancellationTokenSource.Token);
@@ -57,7 +57,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Models
// we can subscribe to events.
await roundStatusUpdater.TriggerAndWaitRoundAsync(TestTimeOut);
var round2IRTask = roundStatusUpdater.CreateRoundAwaiter(roundState2.Id, rs => rs.Phase == Phase.InputRegistration, cancellationToken);
- var round2TBTask = roundStatusUpdater.CreateRoundAwaiter(roundState2.Id, rs => rs.Phase == Phase.TransactionBroadcasting, cancellationToken);
+ var round2TBTask = roundStatusUpdater.CreateRoundAwaiter(roundState2.Id, rs => rs.Phase == Phase.Ended, cancellationToken);
// Force the RoundStatusUpdater to run again just to make it trigger the events.
await roundStatusUpdater.TriggerAndWaitRoundAsync(TestTimeOut);

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -37,6 +37,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public Prison Prison { get; }
public SecureRandom Random { get; }
+ public IEnumerable<Round> ActiveRounds => Rounds.Where(x => x.Phase != Phase.Ended);
+
protected override async Task ActionAsync(CancellationToken cancel)
{
using (await AsyncLock.LockAsync(cancel).ConfigureAwait(false))
@@ -69,7 +71,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
if (round.InputCount < Config.MinInputCountByRound)
{
- round.SetPhase(Phase.Failed);
+ round.SetPhase(Phase.Ended);
round.LogInfo($"Not enough inputs ({round.InputCount}) in {nameof(Phase.InputRegistration)} phase.");
}
else
@@ -99,7 +101,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
if (round.InputCount < Config.MinInputCountByRound)
{
- round.SetPhase(Phase.Failed);
+ round.SetPhase(Phase.Ended);
round.LogInfo($"Not enough inputs ({round.InputCount}) in {nameof(Phase.ConnectionConfirmation)} phase.");
}
else
@@ -173,7 +175,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
// Broadcasting.
await Rpc.SendRawTransactionAsync(coinjoin).ConfigureAwait(false);
- round.SetPhase(Phase.TransactionBroadcasting); // TODO remove from Rounds after timeout?
+ round.IsTransactionBroadcasted = true;
+ round.SetPhase(Phase.Ended);
round.LogInfo($"Successfully broadcast the CoinJoin: {coinjoin.GetHash()}.");
}
@@ -208,7 +211,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
}
round.Alices.RemoveAll(x => alicesWhoDidntSign.Contains(x));
- round.SetPhase(Phase.Failed);
+ round.SetPhase(Phase.Ended);
if (round.InputCount >= Config.MinInputCountByRound)
{
@@ -240,8 +243,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
foreach (var expiredRound in Rounds.Where(
x =>
- ( x.Phase == Phase.TransactionBroadcasting || x.Phase == Phase.Failed )
- && x.TransactionBroadcastingStart + Config.RoundExpiryTimeout < DateTimeOffset.UtcNow).ToArray())
+ x.Phase == Phase.Ended
+ && x.End + Config.RoundExpiryTimeout < DateTimeOffset.UtcNow).ToArray())
{
Rounds.Remove(expiredRound);
}
@@ -264,7 +267,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
var coin = await InputRegistrationHandler.OutpointToCoinAsync(request, Prison, Rpc, Config).ConfigureAwait(false);
using (await AsyncLock.LockAsync().ConfigureAwait(false))
{
- var registeredCoins = Rounds.Where(x => x.Phase != Phase.Failed).SelectMany(r => r.Alices.Select(a => a.Coin));
+ var registeredCoins = Rounds.Where(x => !(x.Phase == Phase.Ended && !x.IsTransactionBroadcasted)).SelectMany(r => r.Alices.Select(a => a.Coin));
if (registeredCoins.Any(x => x.Outpoint == coin.Outpoint))
{

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Phase.cs
WalletWasabi/WabiSabi/Backend/Rounds/Phase.cs
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace WalletWasabi.WabiSabi.Backend.Rounds
{
public enum Phase
@@ -12,7 +6,6 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
ConnectionConfirmation,
OutputRegistration,
TransactionSigning,
- TransactionBroadcasting,
- Failed
+ Ended
}
}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
@@ -61,7 +61,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public DateTimeOffset ConnectionConfirmationStart { get; private set; }
public DateTimeOffset OutputRegistrationStart { get; private set; }
public DateTimeOffset TransactionSigningStart { get; private set; }
- public DateTimeOffset TransactionBroadcastingStart { get; private set; }
+ public DateTimeOffset End { get; private set; }
+ public bool IsTransactionBroadcasted { get; set; }
public int InitialInputVsizeAllocation { get; internal set; }
public int RemainingInputVsizeAllocation => InitialInputVsizeAllocation - InputCount * MaxVsizeAllocationPerAlice;
@@ -96,9 +97,9 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
TransactionSigningStart = DateTimeOffset.UtcNow;
}
- else if (phase == Phase.TransactionBroadcasting || phase == Phase.Failed)
+ else if (phase == Phase.Ended)
{
- TransactionBroadcastingStart = DateTimeOffset.UtcNow; // TODO rename End
+ End = DateTimeOffset.UtcNow;
}
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -117,9 +117,9 @@ namespace WalletWasabi.WabiSabi.Client
// Send signature.
await SignTransactionAsync(aliceClients, unsignedCoinJoin, cancellationToken).ConfigureAwait(false);
- var finalRoundState = await RoundStatusUpdater.CreateRoundAwaiter(s => s.Id == roundState.Id && ( s.Phase == Phase.Failed || s.Phase == Phase.TransactionBroadcasting ) || s.BlameOf == roundState.Id, cancellationToken).ConfigureAwait(false);
+ var finalRoundState = await RoundStatusUpdater.CreateRoundAwaiter(s => s.Id == roundState.Id && s.Phase == Phase.Ended, cancellationToken).ConfigureAwait(false);
- return finalRoundState.Id == roundState.Id && finalRoundState.Phase == Phase.TransactionBroadcasting;
+ return finalRoundState.IsTransactionBroadcasted;
}
private List<AliceClient> CreateAliceClients(RoundState roundState)

MODIFY
WalletWasabi/WabiSabi/Models/RoundState.cs
WalletWasabi/WabiSabi/Models/RoundState.cs
@@ -15,6 +15,7 @@ namespace WalletWasabi.WabiSabi.Models
CredentialIssuerParameters VsizeCredentialIssuerParameters,
FeeRate FeeRate,
Phase Phase,
+ bool IsTransactionBroadcasted,
TimeSpan ConnectionConfirmationTimeout,
long MaxRegistrableAmount,
long MaxRegistrableVsize,
@@ -29,6 +30,7 @@ namespace WalletWasabi.WabiSabi.Models
round.VsizeCredentialIssuerParameters,
round.FeeRate,
round.Phase,
+ round.IsTransactionBroadcasted,
round.ConnectionConfirmationTimeout,
round.MaxRegistrableAmount,
round.MaxRegistrableVsize,

remove Failed phase

Rename `TransactionBroadcasting` phase to `Ended` and use it for both
terminal states, with a new boolean `IsTransactionBroadcasted` to
distinguish between the successful and failed states in the `Ended`
phase.