Finish input registration tests
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -122,6 +122,13 @@ namespace WalletWasabi.Backend.Controllers
{
alicesToRemove.UnionWith(tr.Select(x => x.UniqueId)); // Input is already registered by this alice, remove it later if all the checks are completed fine.
}
+ if (Coordinator.AnyRunningRoundContainsInput(inputProof.Input, out List<Alice> tnr))
+ {
+ if(tr.Union(tnr).Count() > tr.Count())
+ {
+ return BadRequest("Input is already registered in another round.");
+ }
+ }
// ToDo: Refuse banned UTXO here!

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1605,6 +1605,7 @@ namespace WalletWasabi.Tests
}
roundConfig.Denomination = new Money(0.008m, MoneyUnit.BTC); // one satoshi less than our output
+ roundConfig.ConnectionConfirmationTimeout = 2;
coordinator.UpdateRoundConfig(roundConfig);
coordinator.FailAllRoundsInInputRegistration();
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
@@ -1665,6 +1666,10 @@ namespace WalletWasabi.Tests
request.Inputs.First().Proof = proof;
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);
@@ -1738,6 +1743,51 @@ namespace WalletWasabi.Tests
Assert.Equal(CcjRoundPhase.ConnectionConfirmation, status.CurrentPhase);
Assert.Equal(0, status.RegisteredPeerCount);
}
+
+ roundConfig.ConnectionConfirmationTimeout = 1; // One second.
+ coordinator.UpdateRoundConfig(roundConfig);
+ coordinator.FailAllRoundsInInputRegistration();
+
+ 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.ConnectionConfirmation, status.CurrentPhase);
+ Assert.Equal(0, status.RegisteredPeerCount);
+ }
+
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ string message = await response.Content.ReadAsStringAsync();
+ Assert.Equal("\"Input is already registered in another round.\"", message);
+ }
+
+ await Task.Delay(2000);
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ 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);
+ }
+
+ await Task.Delay(1000);
+ 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(0, status.RegisteredPeerCount);
+ }
}
}

MODIFY
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
@@ -435,14 +435,7 @@ namespace WalletWasabi.ChaumianCoinJoin
}
}
- if (alices.Count > 0)
- {
- return true;
- }
- else
- {
- return false;
- }
+ return alices.Count > 0;
}
public int CountBobs(bool syncronized = true)

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -144,6 +144,25 @@ namespace WalletWasabi.Services
}
}
+ public bool AnyRunningRoundContainsInput(OutPoint input, out List<Alice> alices)
+ {
+ using (RoundsListLock.Lock())
+ {
+ alices = new List<Alice>();
+ foreach(var round in Rounds.Where(x=>x.Status == CcjRoundStatus.Running))
+ {
+ if(round.ContainsInput(input, out List<Alice> roundAlices))
+ {
+ foreach(var alice in roundAlices)
+ {
+ alices.Add(alice);
+ }
+ }
+ }
+ return alices.Count > 0;
+ }
+ }
+
#region IDisposable Support
private volatile bool _disposedValue = false; // To detect redundant calls

Finish input registration tests