public async Task <IActionResult> PostConfirmationAsync([FromQuery] string uniqueId, [FromQuery] long roundId) { if (roundId <= 0 || !ModelState.IsValid) { return(BadRequest()); } Guid uniqueIdGuid = CheckUniqueId(uniqueId, out IActionResult returnFailureResponse); if (returnFailureResponse != null) { return(returnFailureResponse); } CcjRound round = Coordinator.TryGetRound(roundId); if (round == null) { return(NotFound("Round not found.")); } Alice alice = round.TryGetAliceBy(uniqueIdGuid); if (round == null) { return(NotFound("Alice not found.")); } if (round.Status != CcjRoundStatus.Running) { return(Forbid("Round is not running.")); } CcjRoundPhase phase = round.Phase; switch (phase) { case CcjRoundPhase.InputRegistration: { round.StartAliceTimeout(uniqueIdGuid); return(NoContent()); } case CcjRoundPhase.ConnectionConfirmation: { alice.State = AliceState.ConnectionConfirmed; // Progress round if needed. if (round.AllAlices(AliceState.ConnectionConfirmed)) { IEnumerable <Alice> alicesToBan = await round.RemoveAlicesIfInputsSpentAsync(); // So ban only those who confirmed participation, yet spent their inputs. if (alicesToBan.Count() > 0) { await Coordinator.UtxoReferee.BanUtxosAsync(1, DateTimeOffset.Now, alicesToBan.SelectMany(x => x.Inputs).Select(y => y.OutPoint).ToArray()); } int aliceCountAfterConnectionConfirmationTimeout = round.CountAlices(); if (aliceCountAfterConnectionConfirmationTimeout < 2) { round.Fail(); } else { round.UpdateAnonymitySet(aliceCountAfterConnectionConfirmationTimeout); // Progress to the next phase, which will be OutputRegistration await round.ExecuteNextPhaseAsync(CcjRoundPhase.OutputRegistration); } } return(Ok(round.RoundHash)); // Participation can be confirmed multiple times, whatever. } default: { return(Forbid($"Participation can be only confirmed from InputRegistration or ConnectionConfirmation phase. Current phase: {phase}.")); } } }