/// <summary>
        /// Checks if a transaction has valid UTXOs that are spent by it.
        /// </summary>
        /// <param name="transaction">The transaction to check.</param>
        /// <param name="coins">Returns the coins found if this parameter supplies an empty coin list.</param>
        /// <returns><c>True</c> if UTXO's are valid and <c>false</c> otherwise.</returns>
        private bool TransactionHasValidUTXOs(Transaction transaction, List <Coin> coins = null)
        {
            lock (this.lockObject)
            {
                // All the input UTXO's should be present in spending details of the multi-sig address.
                foreach (TxIn input in transaction.Inputs)
                {
                    TransactionData transactionData = this.Wallet.MultiSigAddress.Transactions
                                                      .Where(t => t.Id == input.PrevOut.Hash && t.Index == input.PrevOut.N && t.SpendingDetails?.TransactionId == transaction.GetHash())
                                                      .SingleOrDefault();

                    if (transactionData == null)
                    {
                        return(false);
                    }

                    coins?.Add(new Coin(transactionData.Id, (uint)transactionData.Index, transactionData.Amount, transactionData.ScriptPubKey));
                }

                return(true);
            }
        }
        /// <summary>
        /// Compares transaction data to determine the order of inclusion in the transaction.
        /// </summary>
        /// <param name="x">First transaction data.</param>
        /// <param name="y">Second transaction data.</param>
        /// <returns>Returns <c>0</c> if the outputs are the same and <c>-1<c> or <c>1</c> depending on whether the first or second output takes precedence.</returns>
        public static int CompareTransactionData(TransactionData x, TransactionData y)
        {
            // The oldest UTXO (determined by block height) is selected first.
            if ((x.BlockHeight ?? int.MaxValue) != (y.BlockHeight ?? int.MaxValue))
            {
                return(((x.BlockHeight ?? int.MaxValue) < (y.BlockHeight ?? int.MaxValue)) ? -1 : 1);
            }

            // If a block has more than one UTXO, then they are selected in order of transaction id.
            if (x.Id != y.Id)
            {
                return((x.Id < y.Id) ? -1 : 1);
            }

            // If multiple UTXOs appear within a transaction then they are selected in ascending index order.
            if (x.Index != y.Index)
            {
                return((x.Index < y.Index) ? -1 : 1);
            }

            return(0);
        }
        /// <summary>
        /// Mark an output as spent, the credit of the output will not be used to calculate the balance.
        /// The output will remain in the wallet for history (and reorg).
        /// </summary>
        /// <param name="transaction">The transaction from which details are added.</param>
        /// <param name="paidToOutputs">A list of payments made out</param>
        /// <param name="spendingTransactionId">The id of the transaction containing the output being spent, if this is a spending transaction.</param>
        /// <param name="spendingTransactionIndex">The index of the output in the transaction being referenced, if this is a spending transaction.</param>
        /// <param name="blockHeight">Height of the block.</param>
        /// <param name="blockHash">Hash of the block.</param>
        /// <param name="block">The block containing the transaction to add.</param>
        private void AddSpendingTransactionToWallet(Transaction transaction, IEnumerable <TxOut> paidToOutputs,
                                                    uint256 spendingTransactionId, int?spendingTransactionIndex, int?blockHeight = null, uint256 blockHash = null, Block block = null)
        {
            Guard.NotNull(transaction, nameof(transaction));
            Guard.NotNull(paidToOutputs, nameof(paidToOutputs));
            Guard.Assert(blockHash == (blockHash ?? block?.GetHash()));

            // Get the transaction being spent.
            TransactionData spentTransaction = this.Wallet.MultiSigAddress.Transactions.SingleOrDefault(t => (t.Id == spendingTransactionId) && (t.Index == spendingTransactionIndex));

            if (spentTransaction == null)
            {
                // Strange, why would it be null?
                this.logger.LogTrace("(-)[TX_NULL]");
                return;
            }

            // If the details of this spending transaction are seen for the first time.
            if (spentTransaction.SpendingDetails == null)
            {
                this.logger.LogTrace("Spending UTXO '{0}-{1}' is new. BlockHeight={2}", spendingTransactionId, spendingTransactionIndex, blockHeight);

                List <PaymentDetails> payments = new List <PaymentDetails>();
                foreach (TxOut paidToOutput in paidToOutputs)
                {
                    // Figure out how to retrieve the destination address.
                    string         destinationAddress = string.Empty;
                    ScriptTemplate scriptTemplate     = paidToOutput.ScriptPubKey.FindTemplate(this.network);
                    switch (scriptTemplate.Type)
                    {
                    // Pay to PubKey can be found in outputs of staking transactions.
                    case TxOutType.TX_PUBKEY:
                        PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(paidToOutput.ScriptPubKey);
                        destinationAddress = pubKey.GetAddress(this.network).ToString();
                        break;

                    // Pay to PubKey hash is the regular, most common type of output.
                    case TxOutType.TX_PUBKEYHASH:
                        destinationAddress = paidToOutput.ScriptPubKey.GetDestinationAddress(this.network).ToString();
                        break;

                    case TxOutType.TX_NONSTANDARD:
                    case TxOutType.TX_SCRIPTHASH:
                        destinationAddress = paidToOutput.ScriptPubKey.GetDestinationAddress(this.network).ToString();
                        break;

                    case TxOutType.TX_MULTISIG:
                    case TxOutType.TX_NULL_DATA:
                    case TxOutType.TX_SEGWIT:
                        break;
                    }

                    payments.Add(new PaymentDetails
                    {
                        DestinationScriptPubKey = paidToOutput.ScriptPubKey,
                        DestinationAddress      = destinationAddress,
                        Amount = paidToOutput.Value
                    });
                }

                SpendingDetails spendingDetails = new SpendingDetails
                {
                    TransactionId = transaction.GetHash(),
                    Payments      = payments,
                    CreationTime  = DateTimeOffset.FromUnixTimeSeconds(block?.Header.Time ?? transaction.Time),
                    BlockHeight   = blockHeight,
                    BlockHash     = blockHash,
                    Hex           = transaction.ToHex(),
                    IsCoinStake   = transaction.IsCoinStake == false ? (bool?)null : true
                };

                spentTransaction.SpendingDetails = spendingDetails;
                spentTransaction.MerkleProof     = null;
            }
            else // If this spending transaction is being confirmed in a block.
            {
                this.logger.LogTrace("Spending transaction ID '{0}' is being confirmed, updating. BlockHeight={1}", spendingTransactionId, blockHeight);

                // Update the block height.
                if (spentTransaction.SpendingDetails.BlockHeight == null && blockHeight != null)
                {
                    spentTransaction.SpendingDetails.BlockHeight = blockHeight;
                    spentTransaction.SpendingDetails.BlockHash   = blockHash;
                }

                // Update the block time to be that of the block in which the transaction is confirmed.
                if (block != null)
                {
                    spentTransaction.SpendingDetails.CreationTime = DateTimeOffset.FromUnixTimeSeconds(block.Header.Time);
                }
            }
        }
        /// <summary>
        /// Adds a transaction that credits the wallet with new coins.
        /// This method is can be called many times for the same transaction (idempotent).
        /// </summary>
        /// <param name="transaction">The transaction from which details are added.</param>
        /// <param name="utxo">The unspent output to add to the wallet.</param>
        /// <param name="blockHeight">Height of the block.</param>
        /// <param name="blockHash">Hash of the block.</param>
        /// <param name="block">The block containing the transaction to add.</param>
        private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int?blockHeight = null, uint256 blockHash = null, Block block = null)
        {
            Guard.NotNull(transaction, nameof(transaction));
            Guard.NotNull(utxo, nameof(utxo));
            Guard.Assert(blockHash == (blockHash ?? block?.GetHash()));

            uint256 transactionHash = transaction.GetHash();

            // Get the collection of transactions to add to.
            Script script = utxo.ScriptPubKey;

            // Check if a similar UTXO exists or not (same transaction ID and same index).
            // New UTXOs are added, existing ones are updated.
            int             index            = transaction.Outputs.IndexOf(utxo);
            Money           amount           = utxo.Value;
            TransactionData foundTransaction = this.Wallet.MultiSigAddress.Transactions.FirstOrDefault(t => (t.Id == transactionHash) && (t.Index == index));

            if (foundTransaction == null)
            {
                this.logger.LogTrace("UTXO '{0}-{1}' not found, creating. BlockHeight={2}, BlockHash={3}", transactionHash, index, blockHeight, blockHash);

                TransactionData newTransaction = new TransactionData
                {
                    Amount       = amount,
                    BlockHeight  = blockHeight,
                    BlockHash    = blockHash,
                    Id           = transactionHash,
                    CreationTime = DateTimeOffset.FromUnixTimeSeconds(block?.Header.Time ?? transaction.Time),
                    Index        = index,
                    ScriptPubKey = script,
                    Hex          = transaction.ToHex()
                };

                // Add the Merkle proof to the (non-spending) transaction.
                if (block != null)
                {
                    newTransaction.MerkleProof = new MerkleBlock(block, new[] { transactionHash }).PartialMerkleTree;
                }

                this.Wallet.MultiSigAddress.Transactions.Add(newTransaction);
                this.AddInputKeysLookupLock(newTransaction);
            }
            else
            {
                this.logger.LogTrace("Transaction ID '{0}-{1}' found, updating BlockHeight={2}, BlockHash={3}.", transactionHash, index, blockHeight, blockHash);

                // Update the block height and block hash.
                if ((foundTransaction.BlockHeight == null) && (blockHeight != null))
                {
                    foundTransaction.BlockHeight = blockHeight;
                    foundTransaction.BlockHash   = blockHash;
                }

                // Update the block time.
                if (block != null)
                {
                    foundTransaction.CreationTime = DateTimeOffset.FromUnixTimeSeconds(block.Header.Time);
                }

                // Add the Merkle proof now that the transaction is confirmed in a block.
                if ((block != null) && (foundTransaction.MerkleProof == null))
                {
                    foundTransaction.MerkleProof = new MerkleBlock(block, new[] { transactionHash }).PartialMerkleTree;
                }
            }

            this.TransactionFoundInternal(script);
        }