Create AliceClient with input registration
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs
@@ -37,10 +37,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
// Register Alices.
var esk = km.GetSecrets("", smartCoin.ScriptPubKey).Single();
- var aliceClient = new AliceClient(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network));
using CancellationTokenSource cancellationTokenSource = new();
- var task = aliceClient.RegisterAndConfirmInputAsync(roundStateUpdater, cancellationTokenSource.Token);
+ var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network), roundStateUpdater, cancellationTokenSource.Token);
while (round.Alices.Count == 0)
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
@@ -8,6 +8,7 @@ using WalletWasabi.Helpers;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Models;
@@ -191,21 +192,19 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
var round = Assert.Single(arena.Rounds);
round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead;
- // Register Alices.
- var aliceClient1 = new AliceClient(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network));
- var aliceClient2 = new AliceClient(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network));
-
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
await roundStateUpdater.StartAsync(CancellationToken.None);
- var task1 = aliceClient1.RegisterAndConfirmInputAsync(roundStateUpdater, CancellationToken.None);
- var task2 = aliceClient2.RegisterAndConfirmInputAsync(roundStateUpdater, CancellationToken.None);
+ var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
+ var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
while (Phase.ConnectionConfirmation != round.Phase)
{
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
}
await Task.WhenAll(task1, task2);
+ var aliceClient1 = task1.Result;
+ var aliceClient2 = task2.Result;
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
Assert.Equal(Phase.OutputRegistration, round.Phase);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
@@ -224,14 +224,11 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
// Register Alices.
- var aliceClient1 = new AliceClient(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network));
- var aliceClient2 = new AliceClient(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network));
-
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
await roundStateUpdater.StartAsync(CancellationToken.None);
- var task1 = aliceClient1.RegisterAndConfirmInputAsync(roundStateUpdater, CancellationToken.None);
- var task2 = aliceClient2.RegisterAndConfirmInputAsync(roundStateUpdater, CancellationToken.None);
+ var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
+ var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
while (Phase.OutputRegistration != round.Phase)
{
@@ -240,6 +237,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
await Task.WhenAll(task1, task2);
+ var aliceClient1 = task1.Result;
+ var aliceClient2 = task2.Result;
+
// Register outputs.
var bobClient = new BobClient(round.Id, arenaClient);
using var destKey1 = new Key();

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs
@@ -49,12 +49,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
var bitcoinSecret = km.GetSecrets("", coin1.ScriptPubKey).Single().PrivateKey.GetBitcoinSecret(Network.Main);
- var aliceClient = new AliceClient(RoundState.FromRound(round), aliceArenaClient, coin1, bitcoinSecret);
-
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), wabiSabiApi);
await roundStateUpdater.StartAsync(CancellationToken.None);
- var task = aliceClient.RegisterAndConfirmInputAsync(roundStateUpdater, CancellationToken.None);
+ var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), aliceArenaClient, coin1, bitcoinSecret, roundStateUpdater, CancellationToken.None);
do
{
@@ -62,7 +60,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
}
while (round.Phase != Phase.ConnectionConfirmation);
- await task;
+ var aliceClient = await task;
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1));
Assert.Equal(Phase.OutputRegistration, round.Phase);

MODIFY
WalletWasabi/WabiSabi/Client/AliceClient.cs
WalletWasabi/WabiSabi/Client/AliceClient.cs
@@ -15,20 +15,28 @@ namespace WalletWasabi.WabiSabi.Client
{
public class AliceClient
{
- public AliceClient(RoundState roundState, ArenaClient arenaClient, SmartCoin coin, BitcoinSecret bitcoinSecret)
- {
+ private AliceClient(
+ Guid aliceId,
+ RoundState roundState,
+ ArenaClient arenaClient,
+ SmartCoin coin,
+ BitcoinSecret bitcoinSecret,
+ IEnumerable<Credential> issuedAmountCredentials,
+ IEnumerable<Credential> issuedVsizeCredentials)
+ {
+ AliceId = aliceId;
RoundId = roundState.Id;
ArenaClient = arenaClient;
SmartCoin = coin;
FeeRate = roundState.FeeRate;
BitcoinSecret = bitcoinSecret;
- IssuedAmountCredentials = Array.Empty<Credential>();
- IssuedVsizeCredentials = Array.Empty<Credential>();
+ IssuedAmountCredentials = issuedAmountCredentials;
+ IssuedVsizeCredentials = issuedVsizeCredentials;
MaxVsizeAllocationPerAlice = roundState.MaxVsizeAllocationPerAlice;
ConfirmationTimeout = roundState.ConnectionConfirmationTimeout / 2;
}
- public Guid? AliceId { get; private set; }
+ public Guid AliceId { get; }
public uint256 RoundId { get; }
private ArenaClient ArenaClient { get; }
public SmartCoin SmartCoin { get; }
@@ -39,31 +47,43 @@ namespace WalletWasabi.WabiSabi.Client
private long MaxVsizeAllocationPerAlice { get; }
private TimeSpan ConfirmationTimeout { get; }
- public async Task RegisterAndConfirmInputAsync(RoundStateUpdater roundStatusUpdater, CancellationToken cancellationToken)
+ public static async Task<AliceClient> CreateRegisterAndConfirmInputAsync(
+ RoundState roundState,
+ ArenaClient arenaClient,
+ SmartCoin coin,
+ BitcoinSecret bitcoinSecret,
+ RoundStateUpdater roundStatusUpdater,
+ CancellationToken cancellationToken)
{
+ AliceClient? aliceClient = null;
try
{
- await RegisterInputAsync(cancellationToken).ConfigureAwait(false);
- await ConfirmConnectionAsync(roundStatusUpdater, cancellationToken).ConfigureAwait(false);
+ aliceClient = await RegisterInputAsync(roundState, arenaClient, coin, bitcoinSecret, cancellationToken).ConfigureAwait(false);
+ await aliceClient.ConfirmConnectionAsync(roundStatusUpdater, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
- await TryToUnregisterAlicesAsync(CancellationToken.None).ConfigureAwait(false);
+ if (aliceClient is { })
+ {
+ await aliceClient.TryToUnregisterAlicesAsync(CancellationToken.None).ConfigureAwait(false);
+ }
+
throw;
}
+
+ return aliceClient;
}
- private async Task RegisterInputAsync(CancellationToken cancellationToken)
+ private static async Task<AliceClient> RegisterInputAsync(RoundState roundState, ArenaClient arenaClient, SmartCoin coin, BitcoinSecret bitcoinSecret, CancellationToken cancellationToken)
{
+ AliceClient? aliceClient;
try
{
- var response = await ArenaClient.RegisterInputAsync(RoundId, SmartCoin.Coin.Outpoint, BitcoinSecret.PrivateKey, cancellationToken).ConfigureAwait(false);
- AliceId = response.Value;
- SmartCoin.CoinJoinInProgress = true;
+ coin.CoinJoinInProgress = true;
+ var response = await arenaClient.RegisterInputAsync(roundState.Id, coin.Coin.Outpoint, bitcoinSecret.PrivateKey, cancellationToken).ConfigureAwait(false);
+ aliceClient = new(response.Value, roundState, arenaClient, coin, bitcoinSecret, response.IssuedAmountCredentials, response.IssuedVsizeCredentials);
- IssuedAmountCredentials = response.IssuedAmountCredentials;
- IssuedVsizeCredentials = response.IssuedVsizeCredentials;
- Logger.LogInfo($"Round ({RoundId}), Alice ({AliceId}): Registered {SmartCoin.OutPoint}.");
+ Logger.LogInfo($"Round ({roundState.Id}), Alice ({aliceClient.AliceId}): Registered {coin.OutPoint}.");
}
catch (System.Net.Http.HttpRequestException ex)
{
@@ -72,28 +92,30 @@ namespace WalletWasabi.WabiSabi.Client
switch (wpe.ErrorCode)
{
case WabiSabiProtocolErrorCode.InputSpent:
- SmartCoin.SpentAccordingToBackend = true;
- Logger.LogInfo($"{SmartCoin.Coin.Outpoint} is spent according to the backend. The wallet is not fully synchronized or corrupted.");
+ coin.SpentAccordingToBackend = true;
+ Logger.LogInfo($"{coin.Coin.Outpoint} is spent according to the backend. The wallet is not fully synchronized or corrupted.");
break;
case WabiSabiProtocolErrorCode.InputBanned:
- SmartCoin.BannedUntilUtc = DateTimeOffset.UtcNow.AddDays(1);
- SmartCoin.SetIsBanned();
- Logger.LogInfo($"{SmartCoin.Coin.Outpoint} is banned.");
+ coin.BannedUntilUtc = DateTimeOffset.UtcNow.AddDays(1);
+ coin.SetIsBanned();
+ Logger.LogInfo($"{coin.Coin.Outpoint} is banned.");
break;
case WabiSabiProtocolErrorCode.InputNotWhitelisted:
- SmartCoin.SpentAccordingToBackend = false;
- Logger.LogWarning($"{SmartCoin.Coin.Outpoint} cannot be registered in the blame round.");
+ coin.SpentAccordingToBackend = false;
+ Logger.LogWarning($"{coin.Coin.Outpoint} cannot be registered in the blame round.");
break;
case WabiSabiProtocolErrorCode.AliceAlreadyRegistered:
- Logger.LogInfo($"{SmartCoin.Coin.Outpoint} was already registered.");
+ Logger.LogInfo($"{coin.Coin.Outpoint} was already registered.");
break;
}
}
throw;
}
+
+ return aliceClient;
}
private async Task ConfirmConnectionAsync(RoundStateUpdater roundStatusUpdater, CancellationToken cancellationToken)
@@ -137,7 +159,7 @@ namespace WalletWasabi.WabiSabi.Client
var response = await ArenaClient
.ConfirmConnectionAsync(
RoundId,
- (Guid)AliceId,
+ AliceId,
amountsToRequest,
vsizesToRequest,
IssuedAmountCredentials,
@@ -152,7 +174,7 @@ namespace WalletWasabi.WabiSabi.Client
return isConfirmed;
}
- private async Task TryToUnregisterAlicesAsync(CancellationToken cancellationToken)
+ public async Task TryToUnregisterAlicesAsync(CancellationToken cancellationToken)
{
try
{
@@ -170,6 +192,7 @@ namespace WalletWasabi.WabiSabi.Client
SmartCoin.CoinJoinInProgress = false;
Logger.LogInfo($"{SmartCoin.Coin.Outpoint} the round was not found. Nothing to unregister.");
break;
+
case WabiSabiProtocolErrorCode.WrongPhase:
Logger.LogInfo($"{SmartCoin.Coin.Outpoint} could not be unregistered at this phase (too late).");
break;
@@ -183,7 +206,7 @@ namespace WalletWasabi.WabiSabi.Client
public async Task RemoveInputAsync(CancellationToken cancellationToken)
{
- await ArenaClient.RemoveInputAsync(RoundId, (Guid)AliceId, cancellationToken).ConfigureAwait(false);
+ await ArenaClient.RemoveInputAsync(RoundId, AliceId, cancellationToken).ConfigureAwait(false);
Logger.LogInfo($"Round ({RoundId}), Alice ({AliceId}): Inputs removed.");
}
@@ -196,7 +219,7 @@ namespace WalletWasabi.WabiSabi.Client
public async Task ReadyToSignAsync(CancellationToken cancellationToken)
{
- await ArenaClient.ReadyToSignAsync(RoundId, (Guid)AliceId, cancellationToken).ConfigureAwait(false);
+ await ArenaClient.ReadyToSignAsync(RoundId, AliceId, cancellationToken).ConfigureAwait(false);
Logger.LogInfo($"Round ({RoundId}), Alice ({AliceId}): Ready to sign.");
}
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -79,10 +79,9 @@ namespace WalletWasabi.WabiSabi.Client
var constructionState = roundState.Assert<ConstructionState>();
var coinCandidates = SelectCoinsForRound(smartCoins, constructionState.Parameters);
- var aliceClientsToRegister = coinCandidates.Select(x => CreateAliceClient(x, roundState)).ToImmutableArray();
// Register coins.
- var registeredAliceClients = await RegisterAndConfirmCoinsAsync(aliceClientsToRegister, cancellationToken).ConfigureAwait(false);
+ var registeredAliceClients = await CreateRegisterAndConfirmCoinsAsync(coinCandidates, roundState, cancellationToken).ConfigureAwait(false);
if (!registeredAliceClients.Any())
{
throw new InvalidOperationException($"Round ({roundState.Id}): There is no available alices to participate with.");
@@ -130,7 +129,11 @@ namespace WalletWasabi.WabiSabi.Client
return finalRoundState.WasTransactionBroadcast;
}
- private AliceClient CreateAliceClient(SmartCoin coin, RoundState roundState)
+ private async Task<ImmutableArray<AliceClient>> CreateRegisterAndConfirmCoinsAsync(IEnumerable<SmartCoin> smartCoins, RoundState roundState, CancellationToken cancellationToken)
+ {
+ async Task<AliceClient?> RegisterInputTask(SmartCoin coin)
+ {
+ try
{
var aliceArenaClient = new ArenaClient(
roundState.CreateAmountCredentialClient(SecureRandom),
@@ -143,18 +146,8 @@ namespace WalletWasabi.WabiSabi.Client
{
throw new InvalidOperationException("The key cannot generate the utxo scriptpubkey. This could happen if the wallet password is not the correct one.");
}
- return new AliceClient(roundState, aliceArenaClient, coin, secret);
- }
- private async Task<ImmutableArray<AliceClient>> RegisterAndConfirmCoinsAsync(
- IEnumerable<AliceClient> aliceClients, CancellationToken cancellationToken)
- {
- async Task<AliceClient?> RegisterInputTask(AliceClient aliceClient)
- {
- try
- {
- await aliceClient.RegisterAndConfirmInputAsync(RoundStatusUpdater, cancellationToken).ConfigureAwait(false);
- return aliceClient;
+ return await AliceClient.CreateRegisterAndConfirmInputAsync(roundState, aliceArenaClient, coin, secret, RoundStatusUpdater, cancellationToken).ConfigureAwait(false);
}
catch (System.Net.Http.HttpRequestException ex)
{
@@ -162,7 +155,7 @@ namespace WalletWasabi.WabiSabi.Client
}
}
- var registerRequests = aliceClients.Select(RegisterInputTask).ToImmutableArray();
+ var registerRequests = smartCoins.Select(RegisterInputTask).ToImmutableArray();
await Task.WhenAll(registerRequests).ConfigureAwait(false);
return registerRequests

Create AliceClient with input registration