public async Task <IActionResult> PostOutputAsync([FromQuery] string roundHash, [FromBody] OutputRequest outputRequest) { if (string.IsNullOrWhiteSpace(roundHash) || outputRequest == null || string.IsNullOrWhiteSpace(outputRequest.OutputScript) || string.IsNullOrWhiteSpace(outputRequest.SignatureHex) || !ModelState.IsValid) { return(BadRequest()); } CcjRound round = Coordinator.TryGetRound(roundHash); if (round == null) { return(NotFound("Round not found.")); } if (round.Status != CcjRoundStatus.Running) { return(Forbid("Round is not running.")); } CcjRoundPhase phase = round.Phase; if (phase != CcjRoundPhase.OutputRegistration) { return(Forbid($"Output registration can only be done from OutputRegistration phase. Current phase: {phase}.")); } var outputScript = new Script(outputRequest.OutputScript); if (RsaKey.PubKey.Verify(ByteHelpers.FromHex(outputRequest.SignatureHex), outputScript.ToBytes())) { using (await OutputLock.LockAsync()) { Bob bob = null; try { bob = new Bob(outputScript); round.AddBob(bob); } catch (Exception ex) { return(BadRequest($"Invalid outputScript is provided. Details: {ex.Message}")); } if (round.CountBobs() == round.AnonymitySet) { await round.ExecuteNextPhaseAsync(CcjRoundPhase.Signing); } } return(NoContent()); } else { return(BadRequest("Invalid signature provided.")); } }
public async Task <IActionResult> PostOutputAsync([FromQuery, Required] long roundId, [FromBody, Required] OutputRequest request) { if (roundId < 0 || request.Level < 0 || !ModelState.IsValid) { return(BadRequest()); } CcjRound round = Coordinator.TryGetRound(roundId); if (round is null) { TryLogLateRequest(roundId, CcjRoundPhase.OutputRegistration); return(NotFound("Round not found.")); } if (round.Status != CcjRoundStatus.Running) { TryLogLateRequest(roundId, CcjRoundPhase.OutputRegistration); return(Gone("Round is not running.")); } CcjRoundPhase phase = round.Phase; if (phase != CcjRoundPhase.OutputRegistration) { TryLogLateRequest(roundId, CcjRoundPhase.OutputRegistration); return(Conflict($"Output registration can only be done from OutputRegistration phase. Current phase: {phase}.")); } if (request.OutputAddress.Network != Network) { // RegTest and TestNet address formats are sometimes the same. if (Network == Network.Main) { return(BadRequest($"Invalid OutputAddress Network.")); } } if (request.OutputAddress == Constants.GetCoordinatorAddress(Network)) { Logger.LogWarning <ChaumianCoinJoinController>($"Bob is registering the coordinator's address. Address: {request.OutputAddress}, Level: {request.Level}, Signature: {request.UnblindedSignature}."); } if (request.Level > round.MixingLevels.GetMaxLevel()) { return(BadRequest($"Invalid mixing Level is provided. Provided: {request.Level}. Maximum: {round.MixingLevels.GetMaxLevel()}.")); } if (round.ContainsRegisteredUnblindedSignature(request.UnblindedSignature)) { return(NoContent()); } MixingLevel mixinglevel = round.MixingLevels.GetLevel(request.Level); Signer signer = mixinglevel.Signer; if (signer.VerifyUnblindedSignature(request.UnblindedSignature, request.OutputAddress.ScriptPubKey.ToBytes())) { using (await OutputLock.LockAsync()) { Bob bob = null; try { bob = new Bob(request.OutputAddress, mixinglevel); round.AddBob(bob); round.AddRegisteredUnblindedSignature(request.UnblindedSignature); } catch (Exception ex) { return(BadRequest($"Invalid outputAddress is provided. Details: {ex.Message}")); } int bobCount = round.CountBobs(); int blindSigCount = round.CountBlindSignatures(); if (bobCount == blindSigCount) // If there'll be more bobs, then round failed. Someone may broke the crypto. { await round.ExecuteNextPhaseAsync(CcjRoundPhase.Signing); } } return(NoContent()); } return(BadRequest("Invalid signature provided.")); }