Make sure states response responds according to roundcount
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -36,27 +36,33 @@ namespace WalletWasabi.Backend.Controllers
/// <summary>
/// Satoshi gets various status information.
/// </summary>
- /// <returns>CurrentPhase, Denomination, RegisteredPeerCount, RequiredPeerCount, MaximumInputCountPerPeer, FeePerInputs, FeePerOutputs, CoordinatorFeePercent, Version</returns>
- /// <response code="200">CurrentPhase, Denomination, RegisteredPeerCount, RequiredPeerCount, MaximumInputCountPerPeer, FeePerInputs, FeePerOutputs, CoordinatorFeePercent, Version</response>
- [HttpGet("status")]
+ /// <returns>List of CcjRunningRoundStatus (Phase, Denomination, RegisteredPeerCount, RequiredPeerCount, MaximumInputCountPerPeer, FeePerInputs, FeePerOutputs, CoordinatorFeePercent)</returns>
+ /// <response code="200">List of CcjRunningRoundStatus (Phase, Denomination, RegisteredPeerCount, RequiredPeerCount, MaximumInputCountPerPeer, FeePerInputs, FeePerOutputs, CoordinatorFeePercent)</response>
+ [HttpGet("states")]
[ProducesResponseType(200)]
- public IActionResult GetStatus()
+ public IActionResult GetStates()
{
- CcjRound currentRound = Coordinator.GetCurrentRound();
- CcjRound currentInputRegistrationRound = Coordinator.GetCurrentInputRegisterableRound();
- var response = new CcjStatusResponse
+ var response = new List<CcjRunningRoundState>();
+
+ foreach(CcjRound round in Coordinator.GetRunningRounds())
+ {
+ var state = new CcjRunningRoundState
{
- CurrentPhase = currentRound.Phase,
- Denomination = currentInputRegistrationRound.Denomination,
- RegisteredPeerCount = currentInputRegistrationRound.CountAlices(syncronized: false),
- RequiredPeerCount = currentInputRegistrationRound.AnonymitySet,
+ Phase = round.Phase,
+ Denomination = round.Denomination,
+ RegisteredPeerCount = round.CountAlices(syncronized: false),
+ RequiredPeerCount = round.AnonymitySet,
MaximumInputCountPerPeer = 7, // Constant for now. If we want to do something with it later, we'll put it to the config file.
- RegistrationTimeout = (int)currentInputRegistrationRound.AliceRegistrationTimeout.TotalSeconds,
- FeePerInputs = currentInputRegistrationRound.FeePerInputs,
- FeePerOutputs = currentInputRegistrationRound.FeePerOutputs,
- CoordinatorFeePercent = currentInputRegistrationRound.CoordinatorFeePercent,
- Version = 1
+ RegistrationTimeout = (int)round.AliceRegistrationTimeout.TotalSeconds,
+ FeePerInputs = round.FeePerInputs,
+ FeePerOutputs = round.FeePerOutputs,
+ CoordinatorFeePercent = round.CoordinatorFeePercent,
+ RoundId = round.RoundId
};
+
+ response.Add(state);
+ }
+
return Ok(response);
}
@@ -237,9 +243,9 @@ namespace WalletWasabi.Backend.Controllers
byte[] signature = RsaKey.SignBlindedData(blindedData);
// Check if phase changed since.
- if (round.Status != CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration)
+ if (round.Status != ChaumianCoinJoin.CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration)
{
- return StatusCode(StatusCodes.Status503ServiceUnavailable, "The state of the round changed while handling the request. Try again.");
+ return base.StatusCode(StatusCodes.Status503ServiceUnavailable, "The state of the round changed while handling the request. Try again.");
}
// Progress round if needed.

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1474,24 +1474,28 @@ namespace WalletWasabi.Tests
using (var client = new TorHttpClient(new Uri(RegTestFixture.BackendEndPoint)))
{
- // Basic status tests.
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ // Basic states tests.
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+ Assert.Equal(2, states.Count());
+ foreach (CcjRunningRoundState roundState in states)
+ {
// Never changes.
- Assert.Equal(1, status.Version);
- Assert.Equal(new Money(0.00000544m, MoneyUnit.BTC), status.FeePerInputs);
- Assert.Equal(new Money(0.00000264m, MoneyUnit.BTC), status.FeePerOutputs);
- Assert.Equal(7, status.MaximumInputCountPerPeer);
+ Assert.True(0 < roundState.RoundId);
+ Assert.Equal(new Money(0.00000544m, MoneyUnit.BTC), roundState.FeePerInputs);
+ Assert.Equal(new Money(0.00000264m, MoneyUnit.BTC), roundState.FeePerOutputs);
+ Assert.Equal(7, roundState.MaximumInputCountPerPeer);
// Changes per rounds.
- Assert.Equal(denomination, status.Denomination);
- Assert.Equal(coordinatorFeePercent, status.CoordinatorFeePercent);
- Assert.Equal(anonymitySet, status.RequiredPeerCount);
- Assert.Equal(connectionConfirmationTimeout, status.RegistrationTimeout);
+ Assert.Equal(denomination, roundState.Denomination);
+ Assert.Equal(coordinatorFeePercent, roundState.CoordinatorFeePercent);
+ Assert.Equal(anonymitySet, roundState.RequiredPeerCount);
+ Assert.Equal(connectionConfirmationTimeout, roundState.RegistrationTimeout);
// Changes per phases.
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(0, status.RegisteredPeerCount);
+ Assert.Equal(CcjRoundPhase.InputRegistration, roundState.Phase);
+ Assert.Equal(0, roundState.RegisteredPeerCount);
+ }
}
// Inputs request tests
@@ -1630,6 +1634,7 @@ namespace WalletWasabi.Tests
roundConfig.ConnectionConfirmationTimeout = 2;
coordinator.UpdateRoundConfig(roundConfig);
coordinator.FailAllRoundsInInputRegistration();
+ long roundId = 0;
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -1637,6 +1642,7 @@ namespace WalletWasabi.Tests
Assert.NotNull(inputsResp.BlindedOutputSignature);
Assert.NotEqual(Guid.Empty, inputsResp.UniqueId);
Assert.True(inputsResp.RoundId > 0);
+ roundId = inputsResp.RoundId;
string queryString = $"/api/v1/btc/chaumiancoinjoin/confirmation?uniqueId={inputsResp.UniqueId}&roundId={inputsResp.RoundId}";
using (var response2 = await client.SendAsync(HttpMethod.Post, queryString))
@@ -1645,12 +1651,14 @@ namespace WalletWasabi.Tests
}
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(1, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.InputRegistration, roundState.Phase);
+ Assert.Equal(1, roundState.RegisteredPeerCount);
}
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
@@ -1660,12 +1668,14 @@ namespace WalletWasabi.Tests
Assert.Equal("\"Blinded output has already been registered.\"", message);
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(1, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.InputRegistration, roundState.Phase);
+ Assert.Equal(1, roundState.RegisteredPeerCount);
}
request.BlindedOutputHashHex = "foo";
@@ -1678,12 +1688,14 @@ namespace WalletWasabi.Tests
Assert.Equal("\"Invalid blinded output hex.\"", message);
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(1, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.InputRegistration, roundState.Phase);
+ Assert.Equal(1, roundState.RegisteredPeerCount);
}
var blindingKey = Global.Coordinator.RsaKey;
@@ -1705,12 +1717,14 @@ namespace WalletWasabi.Tests
Assert.True(inputsResp.RoundId > 0);
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(1, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.InputRegistration, roundState.Phase);
+ Assert.Equal(1, roundState.RegisteredPeerCount);
}
request.BlindedOutputHashHex = new Transaction().ToHex();
@@ -1764,26 +1778,34 @@ namespace WalletWasabi.Tests
Assert.NotNull(inputsResp.BlindedOutputSignature);
Assert.NotEqual(Guid.Empty, inputsResp.UniqueId);
Assert.True(inputsResp.RoundId > 0);
+ roundId = inputsResp.RoundId;
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.ConnectionConfirmation, status.CurrentPhase);
- Assert.Equal(0, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.ConnectionConfirmation, roundState.Phase);
+ Assert.Equal(2, roundState.RegisteredPeerCount);
+
+ var roundState2 = states.First(x => x.Phase == CcjRoundPhase.InputRegistration);
+ Assert.Equal(0, roundState2.RegisteredPeerCount);
}
roundConfig.ConnectionConfirmationTimeout = 1; // One second.
coordinator.UpdateRoundConfig(roundConfig);
coordinator.FailAllRoundsInInputRegistration();
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.ConnectionConfirmation, status.CurrentPhase);
- Assert.Equal(0, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ var roundState = states.Single(x => x.RoundId == roundId);
+ Assert.Equal(CcjRoundPhase.ConnectionConfirmation, roundState.Phase);
+ Assert.Equal(2, roundState.RegisteredPeerCount);
}
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
@@ -1802,12 +1824,15 @@ namespace WalletWasabi.Tests
Assert.StartsWith("\"Input is banned from participation for", message);
}
- using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/status/"))
+ using (var response = await client.SendAsync(HttpMethod.Get, "/api/v1/btc/chaumiancoinjoin/states/"))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var status = await response.Content.ReadAsJsonAsync<CcjStatusResponse>();
- Assert.Equal(CcjRoundPhase.InputRegistration, status.CurrentPhase);
- Assert.Equal(0, status.RegisteredPeerCount);
+ var states = await response.Content.ReadAsJsonAsync<IEnumerable<CcjRunningRoundState>>();
+
+ foreach (var roundState in states.Where(x => x.Phase == CcjRoundPhase.InputRegistration))
+ {
+ Assert.Equal(0, roundState.RegisteredPeerCount);
+ }
}
}
}

RENAME
WalletWasabi/Backend/Models/Responses/CcjStatusResponse.cs
WalletWasabi/Backend/Models/Responses/CcjRunningRoundState.cs
@@ -9,10 +9,10 @@ using WalletWasabi.JsonConverters;
namespace WalletWasabi.Backend.Models.Responses
{
- public class CcjStatusResponse
+ public class CcjRunningRoundState
{
[JsonConverter(typeof(StringEnumConverter))]
- public CcjRoundPhase CurrentPhase { get; set; }
+ public CcjRoundPhase Phase { get; set; }
[JsonConverter(typeof(MoneyBtcJsonConverter))]
public Money Denomination { get; set; }
@@ -33,6 +33,6 @@ namespace WalletWasabi.Backend.Models.Responses
public decimal CoordinatorFeePercent { get; set; }
- public int Version { get; set; }
+ public long RoundId { get; set; }
}
}

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -264,6 +264,14 @@ namespace WalletWasabi.Services
}
}
+ public IEnumerable<CcjRound> GetRunningRounds()
+ {
+ using (RoundsListLock.Lock())
+ {
+ return Rounds.Where(x => x.Status == CcjRoundStatus.Running).ToArray();
+ }
+ }
+
public CcjRound GetCurrentInputRegisterableRound()
{
using (RoundsListLock.Lock())

Make sure states response responds according to roundcount