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."));
            }
        }
예제 #2
0
        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."));
        }