Esempio n. 1
0
        /// <summary>
        /// Returns a dictionary of all the transactions being watched (both under addresses
        /// and standalone).
        /// </summary>
        public ConcurrentDictionary <uint256, WatchTransactionData> GetWatchedTransactions()
        {
            var txDict = new ConcurrentDictionary <uint256, WatchTransactionData>();

            foreach (WatchedAddress address in this.WatchedAddresses.Values)
            {
                foreach (WatchTransactionData transaction in address.Transactions.Values)
                {
                    transaction.Address = address.Address;
                    txDict.TryAdd(transaction.Id, transaction);
                }
            }

            foreach (WatchTransactionData transaction in this.WatchedTransactions.Values)
            {
                // It is conceivable that a transaction could be both watched
                // in isolation and watched as a transaction under one or
                // more watched addresses.
                if (!txDict.TryAdd(transaction.Id, transaction))
                {
                    // Check to see if there is better information in
                    // the watched transaction than the watched address.
                    // If there is, use the watched transaction info instead.

                    WatchTransactionData existingTx = txDict[transaction.Id];

                    if (existingTx.MerkleProof == null)
                    {
                        existingTx.MerkleProof = transaction.MerkleProof;
                    }

                    if (existingTx.BlockHash == null)
                    {
                        existingTx.BlockHash = transaction.BlockHash;
                    }

                    // At this stage the transaction info in txDict should
                    // include the best available information from both
                    // sources. There is therefore no need to explicitly
                    // update txDict.
                }
            }

            return(txDict);
        }
        /// <inheritdoc />
        public void StoreTransaction(WatchTransactionData transactionData)
        {
            if (this.Wallet.WatchedTransactions.ContainsKey(transactionData.Id.ToString()))
            {
                this.logger.LogDebug($"already watching transaction: {transactionData.Id}. coin: {this.coinType}");
                return;
            }

            this.logger.LogDebug($"added transaction: {transactionData.Id} to the watch list. coin: {this.coinType}");
            this.Wallet.WatchedTransactions.TryAdd(transactionData.Id.ToString(), new WatchTransactionData
            {
                BlockHash   = transactionData.BlockHash,
                Hex         = transactionData.Hex,
                Id          = transactionData.Id,
                MerkleProof = transactionData.MerkleProof
            });

            this.SaveWatchOnlyWallet();
        }
        /// <inheritdoc />
        public void ProcessTransaction(Transaction transaction, Block block = null)
        {
            uint256 transactionHash = transaction.GetHash();

            this.logger.LogDebug($"watch only wallet received transaction - hash: {transactionHash}, coin: {this.coinType}");

            // Check the transaction inputs to see if a watched address is affected.
            foreach (TxIn input in transaction.Inputs)
            {
                // See if the previous transaction is in the watch-only wallet.
                this.txLookup.TryGetValue(input.PrevOut.Hash, out WatchTransactionData prevTransactionData);

                // If it is null, it can't be related to one of the watched addresses (or it is the very first watched transaction)
                if (prevTransactionData == null)
                {
                    continue;
                }

                var prevTransaction = this.network.CreateTransaction(prevTransactionData.Hex);

                // Check if the previous transaction's outputs contain one of our addresses.
                foreach (TxOut prevOutput in prevTransaction.Outputs)
                {
                    this.Wallet.WatchedAddresses.TryGetValue(prevOutput.ScriptPubKey.ToString(), out WatchedAddress addressInWallet);

                    if (addressInWallet != null)
                    {
                        // Retrieve a transaction, if present.
                        addressInWallet.Transactions.TryGetValue(transactionHash.ToString(), out WatchTransactionData existingTransaction);

                        if (existingTransaction == null)
                        {
                            var newTransaction = new WatchTransactionData
                            {
                                Id        = transactionHash,
                                Hex       = transaction.ToHex(),
                                BlockHash = block?.GetHash()
                            };

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

                            addressInWallet.Transactions.TryAdd(transactionHash.ToString(), newTransaction);

                            // Update the lookup cache with the new transaction information.
                            // Since the WO record is new it probably isn't in the lookup cache.
                            this.txLookup.TryAdd(newTransaction.Id, newTransaction);
                        }
                        else
                        {
                            // If there was a transaction already present in the WO wallet,
                            // it is most likely that it has now been confirmed in a block.
                            // Therefore, update the transaction record with the hash of the
                            // block containing the transaction.
                            if (existingTransaction.BlockHash == null)
                            {
                                existingTransaction.BlockHash = block?.GetHash();
                            }

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

                            // Update the lookup cache with the new transaction information.
                            // Since the WO record was not new it probably is already in the lookup cache.
                            // Therefore, unconditionally update it.
                            this.txLookup.AddOrUpdate(existingTransaction.Id, existingTransaction, (key, oldValue) => existingTransaction);
                        }

                        this.SaveWatchOnlyWallet();
                    }
                }
            }

            // Check the transaction outputs for transactions we might be interested in.
            foreach (TxOut utxo in transaction.Outputs)
            {
                // Check if the outputs contain one of our addresses.
                this.Wallet.WatchedAddresses.TryGetValue(utxo.ScriptPubKey.ToString(), out WatchedAddress addressInWallet);

                if (addressInWallet != null)
                {
                    // Retrieve a transaction, if present.
                    addressInWallet.Transactions.TryGetValue(transactionHash.ToString(), out WatchTransactionData existingTransaction);

                    if (existingTransaction == null)
                    {
                        var newTransaction = new WatchTransactionData
                        {
                            Id        = transactionHash,
                            Hex       = transaction.ToHex(),
                            BlockHash = block?.GetHash()
                        };

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

                        addressInWallet.Transactions.TryAdd(transactionHash.ToString(), newTransaction);

                        // Update the lookup cache with the new transaction information.
                        // Since the WO record is new it probably isn't in the lookup cache.
                        this.txLookup.TryAdd(newTransaction.Id, newTransaction);
                    }
                    else
                    {
                        // If there was a transaction already present in the WO wallet,
                        // it is most likely that it has now been confirmed in a block.
                        // Therefore, update the transaction record with the hash of the
                        // block containing the transaction.
                        if (existingTransaction.BlockHash == null)
                        {
                            existingTransaction.BlockHash = block?.GetHash();
                        }

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

                        // Update the lookup cache with the new transaction information.
                        // Since the WO record was not new it probably is already in the lookup cache.
                        // Therefore, unconditionally update it.
                        this.txLookup.AddOrUpdate(existingTransaction.Id, existingTransaction, (key, oldValue) => existingTransaction);
                    }

                    this.SaveWatchOnlyWallet();
                }
            }

            this.Wallet.WatchedTransactions.TryGetValue(transactionHash.ToString(), out WatchTransactionData existingWatchedTransaction);

            if (existingWatchedTransaction != null && block != null)
            {
                // The transaction was previously stored, in an unconfirmed state.
                // So now update the block hash and Merkle proof since it has
                // appeared in a block.
                existingWatchedTransaction.BlockHash   = block.GetHash();
                existingWatchedTransaction.MerkleProof = new MerkleBlock(block, new[] { transaction.GetHash() }).PartialMerkleTree;

                // Update the lookup cache with the new transaction information.
                this.txLookup.AddOrUpdate(existingWatchedTransaction.Id, existingWatchedTransaction, (key, oldValue) => existingWatchedTransaction);

                this.SaveWatchOnlyWallet();
            }
        }