Add required events
MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Crypto.Randomness;
+using WalletWasabi.Extensions;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
using WalletWasabi.Tor.Socks5.Pool.Circuits;
@@ -55,7 +56,8 @@ public class CoinJoinClient
DoNotRegisterInLastMinuteTimeLimit = doNotRegisterInLastMinuteTimeLimit;
}
- public EventHandler<CoinJoinProgressEventArgs>? CoinJoinClientProgress;
+ public event EventHandler<CoinJoinProgressEventArgs>? CoinJoinClientProgress;
+
private SecureRandom SecureRandom { get; }
public IWasabiHttpClientFactory HttpClientFactory { get; }
private IKeyChain KeyChain { get; }
@@ -75,6 +77,7 @@ public class CoinJoinClient
private async Task<RoundState> WaitForRoundAsync(uint256 excludeRound, CancellationToken token)
{
+ CoinJoinClientProgress.SafeInvoke(this, new WaitingForRound());
return await RoundStatusUpdater
.CreateRoundAwaiter(
roundState =>
@@ -90,7 +93,10 @@ public class CoinJoinClient
private async Task<RoundState> WaitForBlameRoundAsync(uint256 blameRoundId, CancellationToken token)
{
- using CancellationTokenSource waitForBlameRound = new(TimeSpan.FromMinutes(5));
+ var timeout = TimeSpan.FromMinutes(5);
+ CoinJoinClientProgress.SafeInvoke(this, new WaitingForBlameRound(DateTimeOffset.UtcNow + timeout));
+
+ using CancellationTokenSource waitForBlameRound = new(timeout);
using CancellationTokenSource linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitForBlameRound.Token, token);
var roundState = await RoundStatusUpdater
@@ -200,6 +206,8 @@ public class CoinJoinClient
var finalRoundState = await RoundStatusUpdater.CreateRoundAwaiter(s => s.Id == roundId && s.Phase == Phase.Ended, cancellationToken).ConfigureAwait(false);
+ CoinJoinClientProgress.SafeInvoke(this, new RoundEnded(roundState));
+
var wasTxBroadcast = finalRoundState.WasTransactionBroadcast
? $"'{finalRoundState.WasTransactionBroadcast}', Coinjoin TxId: ({unsignedCoinJoin.GetHash()})"
: $"'{finalRoundState.WasTransactionBroadcast}'";
@@ -574,9 +582,16 @@ public class CoinJoinClient
var roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundId, Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);
var roundParameters = roundState.CoinjoinState.Parameters;
var remainingTime = roundParameters.OutputRegistrationTimeout - RoundStatusUpdater.Period;
- var outputRegistrationEndTime = DateTimeOffset.UtcNow + (remainingTime * 0.8); // 80% of the time.
+ var now = DateTimeOffset.UtcNow;
+ var outputRegistrationPhaseEndTime = now + remainingTime;
+
+ // Splitting the ramaining time.
+ // Both operations are done under output registration phase, so we have to do the random timing taking that into account.
+ var outputRegistrationEndTime = now + (remainingTime * 0.8); // 80% of the time.
var readyToSignEndTime = outputRegistrationEndTime + remainingTime * 0.2; // 20% of the time.
+ CoinJoinClientProgress.SafeInvoke(this, new EnteringOutputRegistrationPhase(roundState, outputRegistrationPhaseEndTime));
+
using CancellationTokenSource phaseTimeoutCts = new(remainingTime + ExtraPhaseTimeoutMargin);
using CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, phaseTimeoutCts.Token);
@@ -627,6 +642,8 @@ public class CoinJoinClient
var remainingTime = roundState.CoinjoinState.Parameters.TransactionSigningTimeout - RoundStatusUpdater.Period;
var signingStateEndTime = DateTimeOffset.UtcNow + remainingTime;
+ CoinJoinClientProgress.SafeInvoke(this, new EnteringSigningPhase(roundState, signingStateEndTime));
+
using CancellationTokenSource phaseTimeoutCts = new(remainingTime + ExtraPhaseTimeoutMargin);
using CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, phaseTimeoutCts.Token);
@@ -659,6 +676,8 @@ public class CoinJoinClient
using CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, phaseTimeoutCts.Token);
var combinedToken = combinedCts.Token;
+ CoinJoinClientProgress.SafeInvoke(this, new EnteringInputRegistrationPhase(roundState, roundState.InputRegistrationEnd));
+
// Register coins.
return await CreateRegisterAndConfirmCoinsAsync(smartCoins, roundState, combinedToken).ConfigureAwait(false);
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinProgressEventArgs.cs
WalletWasabi/WabiSabi/Client/CoinJoinProgressEventArgs.cs
+using WalletWasabi.WabiSabi.Models;
+
namespace WalletWasabi.WabiSabi.Client;
public class CoinJoinProgressEventArgs : EventArgs
{
}
+
+public class WaitingForRound : CoinJoinProgressEventArgs
+{
+}
+
+public class WaitingForBlameRound : CoinJoinProgressEventArgs
+{
+ public WaitingForBlameRound(DateTimeOffset dateTimeOffset)
+ {
+ DateTimeOffset = dateTimeOffset;
+ }
+
+ public DateTimeOffset DateTimeOffset { get; }
+}
+
+public class RoundStateChanged : CoinJoinProgressEventArgs
+{
+ public RoundStateChanged(RoundState roundState, DateTimeOffset timeout)
+ {
+ RoundState = roundState;
+ Timeout = timeout;
+ }
+
+ public RoundState RoundState { get; }
+ public DateTimeOffset Timeout { get; }
+}
+
+public class EnteringInputRegistrationPhase : RoundStateChanged
+{
+ public EnteringInputRegistrationPhase(RoundState roundState, DateTimeOffset timeout) : base(roundState, timeout)
+ {
+ }
+}
+
+public class EnteringOutputRegistrationPhase : RoundStateChanged
+{
+ public EnteringOutputRegistrationPhase(RoundState roundState, DateTimeOffset timeout) : base(roundState, timeout)
+ {
+ }
+}
+
+public class EnteringSigningPhase : RoundStateChanged
+{
+ public EnteringSigningPhase(RoundState roundState, DateTimeOffset timeout) : base(roundState, timeout)
+ {
+ }
+}
+
+public class RoundEnded : CoinJoinProgressEventArgs
+{
+ public RoundEnded(RoundState roundState)
+ {
+ RoundState = roundState;
+ }
+
+ public RoundState RoundState { get; }
+}

Add required events