Prevent DoS attacks
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
@@ -83,33 +83,6 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
await arena.StopAsync(CancellationToken.None);
}
- [Fact]
- public async Task InputRegistrationFullVsizeAsync()
- {
- WabiSabiConfig cfg = new();
- var round = WabiSabiFactory.CreateRound(cfg);
- using Key key = new();
- var coin = WabiSabiFactory.CreateCoin(key);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round);
-
- // TODO add configuration parameter
- round.InitialInputVsizeAllocation = (int)round.MaxVsizeAllocationPerAlice;
-
- // Add an alice
- await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
- round.Alices.Add(WabiSabiFactory.CreateAlice());
-
- Assert.Equal(0, round.RemainingInputVsizeAllocation);
-
- // try to add an additional input
- var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
- var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
- Assert.Equal(WabiSabiProtocolErrorCode.VsizeQuotaExceeded, ex.ErrorCode);
-
- await arena.StopAsync(CancellationToken.None);
- }
-
[Fact]
public async Task InputRegistrationTimedoutAsync()
{
@@ -400,7 +373,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
- Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
+ Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs
@@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client;
using Xunit;
@@ -53,38 +54,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var preAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key));
round.Alices.Add(preAlice);
- var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9;
- var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
- var resp = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None).ConfigureAwait(false);
- AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp);
-
- await arena.StopAsync(CancellationToken.None);
- }
-
- [Fact]
- public async Task SuccessWithAliceUpdateCrossRoundAsync()
- {
- WabiSabiConfig cfg = new();
- var round = WabiSabiFactory.CreateRound(cfg);
- var anotherRound = WabiSabiFactory.CreateRound(cfg);
-
- using Key key = new();
- var coin = WabiSabiFactory.CreateCoin(key);
- using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round, anotherRound);
-
- // Make sure an Alice have already been registered with the same input.
- var preAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key));
-
- var initialRemaining = anotherRound.RemainingInputVsizeAllocation;
- anotherRound.Alices.Add(preAlice);
-
- var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9;
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
- var resp = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None).ConfigureAwait(false);
-
- AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp);
- Assert.Empty(anotherRound.Alices);
- Assert.Equal(initialRemaining, anotherRound.RemainingInputVsizeAllocation);
+ var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None).ConfigureAwait(false));
+ Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -249,6 +249,12 @@ 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.SelectMany(r => r.Value.Alices.Select(a => a.Coin));
+
+ if (registeredCoins.Any(x => x.Outpoint == coin.Outpoint))
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.AliceAlreadyRegistered);
+ }
return InputRegistrationHandler.RegisterInput(
Config,
request.RoundId,

Prevent DoS attacks