Make sure Alice gets banned in connection confirmation, too.
MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1788,25 +1788,11 @@ namespace WalletWasabi.Tests
await Task.Delay(3000);
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
- if (response.StatusCode == HttpStatusCode.BadRequest) // Very rarely it fails, let's try to catch it.
- {
- Logger.LogWarning(await response.Content.ReadAsStringAsync());
- }
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var inputsResp = await response.Content.ReadAsJsonAsync<InputsResponse>();
- Assert.NotNull(inputsResp.BlindedOutputSignature);
- Assert.NotEqual(Guid.Empty, inputsResp.UniqueId);
- }
-
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
- {
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(1, status.RegisteredPeerCount);
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ string message = await response.Content.ReadAsStringAsync();
+ Assert.StartsWith("\"Input is banned from participation for", message);
}
- await Task.Delay(1000);
using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

MODIFY
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
@@ -340,13 +340,23 @@ namespace WalletWasabi.ChaumianCoinJoin
case CcjRoundPhase.ConnectionConfirmation:
{
// Only fail if less than two one alices are registered.
- // Don't ban anyone, it's ok if they lost connection.
- RemoveAlicesBy(AliceState.InputsRegistered);
- var alicesToBan = await RemoveAlicesIfInputsSpentAsync(); // So ban only those who confirmed participation, yet spent their inputs.
- if (alicesToBan.Count() != 0)
+ // What if an attacker registers all the time many alices, then drops out. He'll achieve only 2 alices to participate?
+ // If he registers many alices at InputRegistration
+ // AND never confirms in connection confirmation
+ // THEN connection confirmation will go with 2 alices in every round
+ // Therefore Alices those didn't confirm, nor requested dsconnection should be banned:
+ IEnumerable<Alice> alicesToBan1 = GetAlicesBy(AliceState.InputsRegistered);
+ IEnumerable<Alice> alicesToBan2 = await RemoveAlicesIfInputsSpentAsync(); // So ban only those who confirmed participation, yet spent their inputs.
+
+ IEnumerable<OutPoint> inputsToBan = alicesToBan1.SelectMany(x => x.Inputs).Select(y => y.OutPoint).Concat(alicesToBan2.SelectMany(x => x.Inputs).Select(y => y.OutPoint).ToArray()).Distinct();
+
+ if (inputsToBan.Count() != 0)
{
- await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.Now, alicesToBan.SelectMany(x => x.Inputs).Select(y => y.OutPoint).ToArray());
+ await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.Now, inputsToBan.ToArray());
}
+
+ RemoveAlicesBy(alicesToBan1.Select(x => x.UniqueId).Concat(alicesToBan2.Select(y => y.UniqueId)).Distinct().ToArray());
+
int aliceCountAfterConnectionConfirmationTimeout = CountAlices();
if (aliceCountAfterConnectionConfirmationTimeout < 2)
{
@@ -598,7 +608,7 @@ namespace WalletWasabi.ChaumianCoinJoin
}
}
- public void RemoveAlicesBy(Guid id)
+ public void RemoveAlicesBy(params Guid[] ids)
{
using (RoundSyncronizerLock.Lock())
{
@@ -606,9 +616,12 @@ namespace WalletWasabi.ChaumianCoinJoin
{
throw new InvalidOperationException("Removing Alice is only allowed in InputRegistration and ConnectionConfirmation phases.");
}
+ foreach (var id in ids)
+ {
Alices.RemoveAll(x => x.UniqueId == id);
}
}
+ }
public void RemoveAliceIfContains(OutPoint input)
{

Make sure Alice gets banned in connection confirmation, too.