Add ArenaBuilder.
ADD
WalletWasabi.Tests/Helpers/ArenaBuilder.cs
+using Moq;
+using NBitcoin;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using WalletWasabi.BitcoinCore.Rpc;
+using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Backend.Banning;
+using WalletWasabi.WabiSabi.Backend.Rounds;
+
+namespace WalletWasabi.Tests.Helpers
+{
+ /// <summary>
+ /// Builder class for <see cref="Arena"/>.
+ /// </summary>
+ public class ArenaBuilder
+ {
+ public TimeSpan? Period { get; set; }
+ public Network? Network { get; set; }
+ public WabiSabiConfig? Config { get; set; }
+ public IRPCClient? Rpc { get; set; }
+ public Prison? Prison { get; set; }
+
+ /// <param name="rounds">Rounds to initialize <see cref="Arena"/> with.</param>
+ public Arena Create(params Round[] rounds)
+ {
+ TimeSpan period = Period ?? TimeSpan.FromHours(1);
+ Prison prison = Prison ?? new();
+ WabiSabiConfig config = Config ?? new();
+ IRPCClient rpc = Rpc ?? WabiSabiFactory.CreatePreconfiguredRpcClient().Object;
+ Network network = Network ?? Network.Main;
+
+ Arena arena = new(period, network, config, rpc, prison);
+
+ foreach (var round in rounds)
+ {
+ arena.Rounds.Add(round);
+ }
+
+ return arena;
+ }
+
+ public Task<Arena> CreateAndStartAsync(params Round[] rounds)
+ => CreateAndStartAsync(rounds, CancellationToken.None);
+
+ public async Task<Arena> CreateAndStartAsync(Round[] rounds, CancellationToken cancellationToken = default)
+ {
+ Arena? toDispose = null;
+
+ try
+ {
+ toDispose = Create(rounds);
+ Arena arena = toDispose;
+ await arena.StartAsync(cancellationToken).ConfigureAwait(false);
+ toDispose = null;
+ return arena;
+ }
+ finally
+ {
+ toDispose?.Dispose();
+ }
+ }
+
+ public static ArenaBuilder From(WabiSabiConfig cfg) => new() { Config = cfg };
+
+ public static ArenaBuilder From(WabiSabiConfig cfg, Prison prison) => new() { Config = cfg, Prison = prison };
+
+ public static ArenaBuilder From(WabiSabiConfig cfg, IMock<IRPCClient> mockRpc, Prison prison) => new() { Config = cfg, Rpc = mockRpc.Object, Prison = prison };
+ }
+}

MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -52,7 +52,7 @@ namespace WalletWasabi.Tests.Helpers
cfg,
Network.Main,
new InsecureRandom(),
- new(100m)));
+ new FeeRate(100m)));
round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead;
return round;
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs
@@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Backend.Banning;
using WalletWasabi.WabiSabi.Backend.Rounds;
using Xunit;
@@ -54,12 +55,15 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
round.Alices.Add(a3);
round.Alices.Add(a4);
round.SetPhase(Phase.ConnectionConfirmation);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
Assert.Equal(Phase.ConnectionConfirmation, round.Phase);
- Assert.Equal(0, arena.Prison.CountInmates().noted);
- Assert.Equal(0, arena.Prison.CountInmates().banned);
+ Assert.Equal(0, prison.CountInmates().noted);
+ Assert.Equal(0, prison.CountInmates().banned);
await arena.StopAsync(CancellationToken.None);
}
@@ -87,13 +91,16 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
round.Alices.Add(a3);
round.Alices.Add(a4);
round.SetPhase(Phase.ConnectionConfirmation);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
Assert.Equal(Phase.OutputRegistration, round.Phase);
Assert.Equal(2, round.Alices.Count);
- Assert.Equal(2, arena.Prison.CountInmates().noted);
- Assert.Equal(0, arena.Prison.CountInmates().banned);
+ Assert.Equal(2, prison.CountInmates().noted);
+ Assert.Equal(0, prison.CountInmates().banned);
await arena.StopAsync(CancellationToken.None);
}
@@ -121,12 +128,15 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
round.Alices.Add(a3);
round.Alices.Add(a4);
round.SetPhase(Phase.ConnectionConfirmation);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
Assert.DoesNotContain(round, arena.ActiveRounds);
- Assert.Equal(3, arena.Prison.CountInmates().noted);
- Assert.Equal(0, arena.Prison.CountInmates().banned);
+ Assert.Equal(3, prison.CountInmates().noted);
+ Assert.Equal(0, prison.CountInmates().banned);
await arena.StopAsync(CancellationToken.None);
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
@@ -3,13 +3,13 @@ using NBitcoin;
using NBitcoin.RPC;
using System;
using System.Linq;
-using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Backend.Banning;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Models;
@@ -65,7 +65,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny<Transaction>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc);
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, mockRpc, prison).Create();
+ await arena.StartAsync(CancellationToken.None);
+
var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);
await aliceClient1.ReadyToSignAsync(CancellationToken.None);
@@ -82,7 +85,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
Assert.DoesNotContain(round, arena.ActiveRounds);
Assert.Equal(Phase.Ended, round.Phase);
Assert.False(round.WasTransactionBroadcast);
- Assert.Empty(arena.Prison.GetInmates());
+ Assert.Empty(prison.GetInmates());
await arena.StopAsync(CancellationToken.None);
}
@@ -102,7 +105,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny<Transaction>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc);
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, mockRpc, prison).Create();
+ await arena.StartAsync(CancellationToken.None);
+
var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);
await aliceClient1.ReadyToSignAsync(CancellationToken.None);
@@ -123,7 +129,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
// There should be no inmate, because we aren't punishing spenders with banning
// as there's no reason to ban already spent UTXOs,
// the cost of spending the UTXO is the punishment instead.
- Assert.Empty(arena.Prison.GetInmates());
+ Assert.Empty(prison.GetInmates());
await arena.StopAsync(CancellationToken.None);
}
@@ -143,7 +149,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny<Transaction>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc);
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, mockRpc, prison).Create();
+ await arena.StartAsync(CancellationToken.None);
+
var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);
await aliceClient1.ReadyToSignAsync(CancellationToken.None);
@@ -159,7 +168,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
Assert.Equal(Phase.Ended, round.Phase);
Assert.False(round.WasTransactionBroadcast);
Assert.Empty(arena.Rounds.Where(x => x.IsBlameRound));
- Assert.Contains(aliceClient2.SmartCoin.OutPoint, arena.Prison.GetInmates().Select(x => x.Utxo));
+ Assert.Contains(aliceClient2.SmartCoin.OutPoint, prison.GetInmates().Select(x => x.Utxo));
await arena.StopAsync(CancellationToken.None);
}
@@ -180,7 +189,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny<Transaction>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc);
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, mockRpc, prison).Create();
+ await arena.StartAsync(CancellationToken.None);
+
var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);
// Make sure not all alices signed.
@@ -198,7 +210,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
Assert.DoesNotContain(round, arena.ActiveRounds);
Assert.Single(arena.Rounds.Where(x => x.IsBlameRound));
var badOutpoint = alice3.Coin.Outpoint;
- Assert.Contains(badOutpoint, arena.Prison.GetInmates().Select(x => x.Utxo));
+ Assert.Contains(badOutpoint, prison.GetInmates().Select(x => x.Utxo));
var blameRound = arena.Rounds.Single(x => x.IsBlameRound);
Assert.True(blameRound.IsBlameRound);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
@@ -142,8 +142,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
- arena.Prison.Punish(outpoint, Punishment.Banned, uint256.One);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
+
+ prison.Punish(outpoint, Punishment.Banned, uint256.One);
var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);
@@ -165,8 +169,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
- arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
+
+ prison.Punish(outpoint, Punishment.Noted, uint256.One);
var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);
@@ -188,8 +196,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new() { AllowNotedInputRegistration = false };
var round = WabiSabiFactory.CreateRound(cfg);
var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
- arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One);
+
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, prison).Create(round);
+ await arena.StartAsync(CancellationToken.None);
+
+ prison.Punish(outpoint, Punishment.Noted, uint256.One);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs
@@ -69,9 +69,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg);
var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(alice.Coin);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc, round, blameRound);
- arena.Prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero);
- await using ArenaRequestHandler handler = new(cfg, arena.Prison, arena, mockRpc.Object);
+ Prison prison = new();
+ using Arena arena = ArenaBuilder.From(cfg, mockRpc, prison).Create(round, blameRound);
+ await arena.StartAsync(CancellationToken.None);
+
+ prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero);
+ await using ArenaRequestHandler handler = new(cfg, prison, arena, mockRpc.Object);
var req = WabiSabiFactory.CreateInputRegistrationRequest(key: key, round: blameRound, prevout: bannedCoin);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req, CancellationToken.None));

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -15,7 +15,6 @@ using WalletWasabi.WabiSabi.Backend.Banning;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.PostRequests;
using WalletWasabi.WabiSabi.Crypto.CredentialRequesting;
-using WalletWasabi.WabiSabi.Crypto;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
@@ -34,10 +33,10 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public HashSet<Round> Rounds { get; } = new();
private AsyncLock AsyncLock { get; } = new();
- public Network Network { get; }
- public WabiSabiConfig Config { get; }
+ private Network Network { get; }
+ private WabiSabiConfig Config { get; }
public IRPCClient Rpc { get; }
- public Prison Prison { get; }
+ private Prison Prison { get; }
public SecureRandom Random { get; }
public IEnumerable<Round> ActiveRounds => Rounds.Where(x => x.Phase != Phase.Ended);

Add ArenaBuilder.