Exemplo n.º 1
0
        public async Task <IActionResult> PostSignaturesAsync([FromQuery] string uniqueId, [FromQuery] long roundId, [FromBody] IDictionary <int, string> signatures)
        {
            if (roundId <= 0 ||
                signatures == null ||
                signatures.Count() <= 0 ||
                signatures.Any(x => x.Key < 0 || string.IsNullOrWhiteSpace(x.Value)) ||
                !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 (alice == null)
            {
                return(NotFound("Alice not found."));
            }

            // Check if Alice provided signature to all her inputs.
            if (signatures.Count != alice.Inputs.Count())
            {
                return(BadRequest("Alice did not provide enough witnesses."));
            }

            if (round.Status != CcjRoundStatus.Running)
            {
                return(Gone("Round is not running."));
            }

            CcjRoundPhase phase = round.Phase;

            switch (phase)
            {
            case CcjRoundPhase.Signing:
            {
                using (await SigningLock.LockAsync())
                {
                    foreach (var signaturePair in signatures)
                    {
                        int       index   = signaturePair.Key;
                        WitScript witness = null;
                        try
                        {
                            witness = new WitScript(signaturePair.Value);
                        }
                        catch (Exception ex)
                        {
                            return(BadRequest($"Malformed witness is provided. Details: {ex.Message}"));
                        }
                        int maxIndex = round.UnsignedCoinJoin.Inputs.Count - 1;
                        if (maxIndex < index)
                        {
                            return(BadRequest($"Index out of range. Maximum value: {maxIndex}. Provided value: {index}"));
                        }

                        // Check duplicates.
                        if (!string.IsNullOrWhiteSpace(round.SignedCoinJoin.Inputs[index].WitScript?.ToString()))                                        // Not sure why WitScript?.ToString() is needed, there was something wrong in previous HiddenWallet version if I didn't do this.
                        {
                            return(BadRequest($"Input is already signed."));
                        }

                        // Verify witness.
                        var cjCopy = new Transaction(round.UnsignedCoinJoin.ToHex());
                        cjCopy.Inputs[index].WitScript = witness;
                        TxOut output = alice.Inputs.Single(x => x.OutPoint == cjCopy.Inputs[index].PrevOut).Output;
                        if (!Script.VerifyScript(output.ScriptPubKey, cjCopy, index, output.Value, ScriptVerify.Standard, SigHash.All))
                        {
                            return(BadRequest($"Invalid witness is provided."));
                        }

                        // Finally add it to our CJ.
                        round.SignedCoinJoin.Inputs[index].WitScript = witness;
                    }

                    alice.State = AliceState.SignedCoinJoin;

                    await round.BroadcastCoinJoinIfFullySignedAsync();
                }

                return(NoContent());
            }

            default:
            {
                return(Conflict($"CoinJoin can only be requested from Signing phase. Current phase: {phase}."));
            }
            }
        }