public async Task <Transaction> SignTransactionAsync(SignatureRequest[] signatureRequests, Transaction transaction, KeyPath changePath = null, CancellationToken cancellation = default(CancellationToken))
        {
            if (signatureRequests.Length == 0)
            {
                throw new ArgumentException("No signatureRequests is passed", "signatureRequests");
            }
            var segwitCoins = signatureRequests.Where(s => s.InputCoin.GetHashVersion() == HashVersion.Witness).Count();

            if (segwitCoins != signatureRequests.Count() && segwitCoins != 0)
            {
                throw new ArgumentException("Mixing segwit input with non segwit input is not supported", "signatureRequests");
            }

            var segwitMode = segwitCoins != 0;

            Dictionary <OutPoint, SignatureRequest> requests = signatureRequests
                                                               .ToDictionaryUnique(o => o.InputCoin.Outpoint);

            transaction = transaction.Clone();
            Dictionary <OutPoint, IndexedTxIn> inputsByOutpoint = transaction.Inputs.AsIndexedInputs().ToDictionary(i => i.PrevOut);
            Dictionary <OutPoint, ICoin>       coinsByOutpoint  = requests.ToDictionary(o => o.Key, o => o.Value.InputCoin);

            List <Task <TrustedInput> > trustedInputsAsync = new List <Task <TrustedInput> >();

            if (!segwitMode)
            {
                foreach (var sigRequest in signatureRequests)
                {
                    trustedInputsAsync.Add(GetTrustedInputAsync(sigRequest.InputTransaction, (int)sigRequest.InputCoin.Outpoint.N, cancellation));
                }
            }

            var noPubKeyRequests = signatureRequests.Where(r => r.PubKey == null).ToArray();
            List <Task <GetWalletPubKeyResponse> > getPubKeys = new List <Task <GetWalletPubKeyResponse> >();

            foreach (var previousReq in noPubKeyRequests)
            {
                getPubKeys.Add(GetWalletPubKeyAsync(previousReq.KeyPath, cancellation: cancellation));
            }
            await Task.WhenAll(getPubKeys).ConfigureAwait(false);

            await Task.WhenAll(trustedInputsAsync).ConfigureAwait(false);

            for (int i = 0; i < noPubKeyRequests.Length; i++)
            {
                noPubKeyRequests[i].PubKey = getPubKeys[i].Result.UncompressedPublicKey.Compress();
            }

            var            trustedInputs  = trustedInputsAsync.Select(t => t.Result).ToDictionaryUnique(i => i.OutPoint);
            List <byte[]>  apdus          = new List <byte[]>();
            InputStartType inputStartType = segwitMode ? InputStartType.NewSegwit : InputStartType.New;


            bool segwitParsedOnce = false;

            for (int i = 0; i < signatureRequests.Length; i++)
            {
                var sigRequest = signatureRequests[i];
                var input      = inputsByOutpoint[sigRequest.InputCoin.Outpoint];
                apdus.AddRange(UntrustedHashTransactionInputStart(inputStartType, input, trustedInputs, coinsByOutpoint, segwitMode, segwitParsedOnce));
                inputStartType = InputStartType.Continue;
                if (!segwitMode || !segwitParsedOnce)
                {
                    apdus.AddRange(UntrustedHashTransactionInputFinalizeFull(changePath, transaction.Outputs));
                }
                changePath = null;                 //do not resubmit the changepath
                if (segwitMode && !segwitParsedOnce)
                {
                    segwitParsedOnce = true;
                    i--;                     //pass once more
                    continue;
                }
                apdus.Add(UntrustedHashSign(sigRequest.KeyPath, null, transaction.LockTime, SigHash.All));
            }
            var responses = await ExchangeAsync(apdus.ToArray(), cancellation).ConfigureAwait(false);

            foreach (var response in responses)
            {
                if (response.Response.Length > 10)                //Probably a signature
                {
                    response.Response[0] = 0x30;
                }
            }
            var signatures = responses.Where(p => TransactionSignature.IsValid(p.Response)).Select(p => new TransactionSignature(p.Response)).ToArray();

            if (signatureRequests.Length != signatures.Length)
            {
                throw new LedgerWalletException("failed to sign some inputs");
            }
            int sigIndex = 0;

            TransactionBuilder builder = new TransactionBuilder();

            foreach (var sigRequest in signatureRequests)
            {
                var input = inputsByOutpoint[sigRequest.InputCoin.Outpoint];
                if (input == null)
                {
                    continue;
                }
                builder.AddCoins(sigRequest.InputCoin);
                builder.AddKnownSignature(sigRequest.PubKey, signatures[sigIndex]);
                sigIndex++;
            }
            builder.SignTransactionInPlace(transaction);

            sigIndex = 0;
            foreach (var sigRequest in signatureRequests)
            {
                var input = inputsByOutpoint[sigRequest.InputCoin.Outpoint];
                if (input == null)
                {
                    continue;
                }
                sigRequest.Signature = signatures[sigIndex];
                if (!sigRequest.PubKey.Verify(transaction.GetSignatureHash(sigRequest.InputCoin, sigRequest.Signature.SigHash), sigRequest.Signature.Signature))
                {
                    foreach (var sigRequest2 in signatureRequests)
                    {
                        sigRequest2.Signature = null;
                    }
                    return(null);
                }
                sigIndex++;
            }

            return(transaction);
        }
        public byte[][] UntrustedHashTransactionInputStart(
            InputStartType startType,
            IndexedTxIn txIn,
            Dictionary <OutPoint, TrustedInput> trustedInputs,
            Dictionary <OutPoint, ICoin> coins,
            bool segwitMode, bool segwitParsedOnce)
        {
            List <byte[]> apdus = new List <byte[]>();

            trustedInputs = trustedInputs ?? new Dictionary <OutPoint, TrustedInput>();
            // Start building a fake transaction with the passed inputs
            MemoryStream data = new MemoryStream();

            BufferUtils.WriteBuffer(data, txIn.Transaction.Version);

            if (segwitMode && segwitParsedOnce)
            {
                VarintUtils.write(data, 1);
            }
            else
            {
                VarintUtils.write(data, txIn.Transaction.Inputs.Count);
            }

            apdus.Add(CreateAPDU(LedgerWalletConstants.LedgerWallet_CLA, LedgerWalletConstants.LedgerWallet_INS_HASH_INPUT_START, (byte)0x00, (byte)startType, data.ToArray()));
            // Loop for each input
            long currentIndex = 0;

            foreach (var input in txIn.Transaction.Inputs)
            {
                if (segwitMode && segwitParsedOnce && currentIndex != txIn.Index)
                {
                    currentIndex++;
                    continue;
                }
                byte[] script = new byte[0];
                if (currentIndex == txIn.Index || segwitMode && !segwitParsedOnce)
                {
                    script = coins[input.PrevOut].GetScriptCode().ToBytes();
                }

                data = new MemoryStream();
                if (segwitMode)
                {
                    data.WriteByte(0x02);
                    BufferUtils.WriteBuffer(data, input.PrevOut);
                    BufferUtils.WriteBuffer(data, Utils.ToBytes((ulong)coins[input.PrevOut].TxOut.Value.Satoshi, true));
                }
                else
                {
                    var trustedInput = trustedInputs[input.PrevOut];
                    if (trustedInput != null)
                    {
                        data.WriteByte(0x01);
                        var b = trustedInput.ToBytes();
                        // untrusted inputs have constant length
                        data.WriteByte((byte)b.Length);
                        BufferUtils.WriteBuffer(data, b);
                    }
                    else
                    {
                        data.WriteByte(0x00);
                        BufferUtils.WriteBuffer(data, input.PrevOut);
                    }
                }
                VarintUtils.write(data, script.Length);
                apdus.Add(CreateAPDU(LedgerWalletConstants.LedgerWallet_CLA, LedgerWalletConstants.LedgerWallet_INS_HASH_INPUT_START, (byte)0x80, (byte)0x00, data.ToArray()));
                data = new MemoryStream();
                BufferUtils.WriteBuffer(data, script);
                BufferUtils.WriteBuffer(data, input.Sequence);
                apdus.AddRange(CreateAPDUSplit(LedgerWalletConstants.LedgerWallet_CLA, LedgerWalletConstants.LedgerWallet_INS_HASH_INPUT_START, (byte)0x80, (byte)0x00, data.ToArray()));
                currentIndex++;
            }
            return(apdus.ToArray());
        }