Пример #1
0
        public AnnotatedTransactionCollection(IEnumerable <AnnotatedTransaction> transactions) : base(transactions)
        {
            foreach (var tx in transactions)
            {
                var h = tx.Record.Transaction.GetHash();
                _TxById.Add(h, tx);
                foreach (var keyPathInfo in tx.Record.TransactionMatch.Inputs.Concat(tx.Record.TransactionMatch.Outputs))
                {
                    if (keyPathInfo.KeyPath != null)
                    {
                        _KeyPaths.TryAdd(keyPathInfo.ScriptPubKey, keyPathInfo.KeyPath);
                    }
                }
            }


            UTXOState state = new UTXOState();

            foreach (var confirmed in transactions
                     .Where(tx => tx.Type == AnnotatedTransactionType.Confirmed)
                     .TopologicalSort())
            {
                if (state.Apply(confirmed.Record.Transaction) == ApplyTransactionResult.Conflict ||
                    !ConfirmedTransactions.TryAdd(confirmed.Record.Transaction.GetHash(), confirmed))
                {
                    Logs.Explorer.LogError("A conflict among confirmed transaction happened, this should be impossible");
                    throw new InvalidOperationException("The impossible happened");
                }
            }

            foreach (var unconfirmed in transactions
                     .Where(tx => tx.Type == AnnotatedTransactionType.Unconfirmed || tx.Type == AnnotatedTransactionType.Orphan)
                     .OrderByDescending(t => t.Record.Inserted)                                                            // OrderByDescending so that the last received is least likely to be conflicted
                     .TopologicalSort())
            {
                var hash = unconfirmed.Record.Transaction.GetHash();
                if (ConfirmedTransactions.ContainsKey(hash))
                {
                    DuplicatedTransactions.Add(unconfirmed);
                }
                else
                {
                    if (state.Apply(unconfirmed.Record.Transaction) == ApplyTransactionResult.Conflict)
                    {
                        ReplacedTransactions.TryAdd(hash, unconfirmed);
                    }
                    else
                    {
                        if (!UnconfirmedTransactions.TryAdd(hash, unconfirmed))
                        {
                            throw new InvalidOperationException("The impossible happened (!UnconfirmedTransactions.TryAdd(hash, unconfirmed))");
                        }
                    }
                }
            }
        }
        public AnnotatedTransactionCollection(IEnumerable <AnnotatedTransaction> transactions, Models.TrackedSource trackedSource) : base(transactions)
        {
            foreach (var tx in transactions)
            {
                foreach (var keyPathInfo in tx.Record.KnownKeyPathMapping)
                {
                    _KeyPaths.TryAdd(keyPathInfo.Key, keyPathInfo.Value);
                }
            }

            UTXOState state = new UTXOState();

            foreach (var confirmed in transactions
                     .Where(tx => tx.Type == AnnotatedTransactionType.Confirmed).ToList()
                     .TopologicalSort())
            {
                if (state.Apply(confirmed.Record) == ApplyTransactionResult.Conflict)
                {
                    Logs.Explorer.LogError("A conflict among confirmed transaction happened, this should be impossible");
                    throw new InvalidOperationException("The impossible happened");
                }
                ConfirmedTransactions.Add(confirmed);
                _TxById.Add(confirmed.Record.TransactionHash, confirmed);
            }

            foreach (var unconfirmed in transactions
                     .Where(tx => tx.Type == AnnotatedTransactionType.Unconfirmed || tx.Type == AnnotatedTransactionType.Orphan)
                     .OrderByDescending(t => t.Record.Inserted)                    // OrderByDescending so that the last received is least likely to be conflicted
                     .ToList()
                     .TopologicalSort())
            {
                if (_TxById.ContainsKey(unconfirmed.Record.TransactionHash))
                {
                    _TxById.Add(unconfirmed.Record.TransactionHash, unconfirmed);
                    DuplicatedTransactions.Add(unconfirmed);
                }
                else
                {
                    _TxById.Add(unconfirmed.Record.TransactionHash, unconfirmed);
                    if (state.Apply(unconfirmed.Record) == ApplyTransactionResult.Conflict)
                    {
                        ReplacedTransactions.Add(unconfirmed);
                    }
                    else
                    {
                        UnconfirmedTransactions.Add(unconfirmed);
                    }
                }
            }

            TrackedSource = trackedSource;
        }
Пример #3
0
 public bool HasChanges()
 {
     return(ConfirmedTransactions.HasChanges() || UnconfirmedTransactions.HasChanges() || ReplacedTransactions.HasChanges());
 }
Пример #4
0
        public BalanceSheet(IEnumerable <OrderedBalanceChange> changes, ChainBase chain)
        {
            if (chain == null)
            {
                throw new ArgumentNullException("chain");
            }
            _Chain = chain;

            var transactionsById = new Dictionary <uint256, OrderedBalanceChange>();
            List <OrderedBalanceChange> ignoredTransactions = new List <OrderedBalanceChange>();

            foreach (var trackedTx in changes)
            {
                int?txHeight = null;

                if (trackedTx.BlockId != null && chain.TryGetHeight(trackedTx.BlockId, out var height))
                {
                    txHeight = height;
                }
                if (trackedTx.BlockId != null && txHeight is null)
                {
                    _Prunable.Add(trackedTx);
                    continue;
                }

                if (transactionsById.TryGetValue(trackedTx.TransactionId, out var conflicted))
                {
                    if (ShouldReplace(trackedTx, conflicted))
                    {
                        ignoredTransactions.Add(conflicted);
                        transactionsById.Remove(trackedTx.TransactionId);
                        transactionsById.Add(trackedTx.TransactionId, trackedTx);
                    }
                    else
                    {
                        ignoredTransactions.Add(trackedTx);
                    }
                }
                else
                {
                    transactionsById.Add(trackedTx.TransactionId, trackedTx);
                }
            }

            // Let's resolve the double spents
            Dictionary <OutPoint, uint256> spentBy = new Dictionary <OutPoint, uint256>();
            HashSet <uint256> incoherent           = new HashSet <uint256>();

            foreach (var annotatedTransaction in transactionsById.Values.Where(r => r.BlockId != null))
            {
                foreach (var spent in annotatedTransaction.SpentOutpoints)
                {
                    // No way to have double spent in confirmed transactions
                    spentBy.Add(spent, annotatedTransaction.TransactionId);
                    if (transactionsById.TryGetValue(spent.Hash, out var spentTx) && spentTx.BlockId == null)
                    {
                        // It happens that we can get a conf depending on an unconf tx (because of queries on Azure not returning it)
                        // in such case, if we see that the unconf has a conf'd parent transaction, just do like if it did not exist
                        incoherent.Add(spent.Hash);
                    }
                }
            }

            foreach (var incoherentHash in incoherent)
            {
                transactionsById.Remove(incoherentHash);
            }

removeConflicts:
            HashSet <uint256> conflicts = new HashSet <uint256>();

            foreach (var annotatedTransaction in transactionsById.Values.Where(r => r.BlockId == null))
            {
                foreach (var spent in annotatedTransaction.SpentOutpoints)
                {
                    if (spentBy.TryGetValue(spent, out var conflictHash) &&
                        transactionsById.TryGetValue(conflictHash, out var conflicted))
                    {
                        if (conflicted == annotatedTransaction)
                        {
                            goto nextTransaction;
                        }
                        if (conflicts.Contains(conflictHash))
                        {
                            spentBy.Remove(spent);
                            spentBy.Add(spent, annotatedTransaction.TransactionId);
                        }
                        else if (ShouldReplace(annotatedTransaction, conflicted))
                        {
                            conflicts.Add(conflictHash);
                            spentBy.Remove(spent);
                            spentBy.Add(spent, annotatedTransaction.TransactionId);

                            if (conflicted.BlockId == null)
                            {
                                ReplacedTransactions.Add(conflicted);
                            }
                            else
                            {
                                ignoredTransactions.Add(conflicted);
                            }
                        }
                        else
                        {
                            conflicts.Add(annotatedTransaction.TransactionId);
                            if (conflicted.BlockId == null)
                            {
                                ReplacedTransactions.Add(annotatedTransaction);
                            }
                            else
                            {
                                ignoredTransactions.Add(annotatedTransaction);
                            }
                        }
                    }
                    else
                    {
                        spentBy.Add(spent, annotatedTransaction.TransactionId);
                    }
                }
                nextTransaction :;
            }

            foreach (var e in conflicts)
            {
                transactionsById.Remove(e);
            }
            if (conflicts.Count != 0)
            {
                goto removeConflicts;
            }

            // Topological sort
            var sortedTrackedTransactions = transactionsById.Values.TopologicalSort();

            // Remove all ignored transaction from the database
            foreach (var ignored in ignoredTransactions)
            {
                _Prunable.Add(ignored);
            }

            _All = sortedTrackedTransactions;
            _All.Reverse();
            _Confirmed   = sortedTrackedTransactions.Where(s => s.BlockId != null).ToList();
            _Unconfirmed = sortedTrackedTransactions.Where(s => s.BlockId == null).ToList();
        }
        public AnnotatedTransactionCollection(ICollection <TrackedTransaction> transactions, Models.TrackedSource trackedSource, SlimChain headerChain, Network network) : base(transactions.Count)
        {
            _TxById = new Dictionary <uint256, AnnotatedTransaction>(transactions.Count);
            foreach (var tx in transactions)
            {
                foreach (var keyPathInfo in tx.KnownKeyPathMapping)
                {
                    _KeyPaths.TryAdd(keyPathInfo.Key, keyPathInfo.Value);
                }
            }

            // Let's remove the dups and let's get the current height of the transactions
            foreach (var trackedTx in transactions)
            {
                int? txHeight = null;
                bool isMature = true;

                if (trackedTx.BlockHash != null && headerChain.TryGetHeight(trackedTx.BlockHash, out var height))
                {
                    txHeight = height;
                    isMature = trackedTx.IsCoinBase ? headerChain.Height - height >= network.Consensus.CoinbaseMaturity : true;
                }

                AnnotatedTransaction annotatedTransaction = new AnnotatedTransaction(txHeight, trackedTx, isMature);
                if (_TxById.TryGetValue(trackedTx.TransactionHash, out var conflicted))
                {
                    if (ShouldReplace(annotatedTransaction, conflicted))
                    {
                        CleanupTransactions.Add(conflicted);
                        _TxById.Remove(trackedTx.TransactionHash);
                        _TxById.Add(trackedTx.TransactionHash, annotatedTransaction);
                    }
                    else
                    {
                        CleanupTransactions.Add(annotatedTransaction);
                    }
                }
                else
                {
                    _TxById.Add(trackedTx.TransactionHash, annotatedTransaction);
                }
            }

            // Let's resolve the double spents
            Dictionary <OutPoint, uint256> spentBy = new Dictionary <OutPoint, uint256>(transactions.SelectMany(t => t.SpentOutpoints).Count());

            foreach (var annotatedTransaction in _TxById.Values.Where(r => r.Height is int))
            {
                foreach (var spent in annotatedTransaction.Record.SpentOutpoints)
                {
                    // No way to have double spent in confirmed transactions
                    spentBy.Add(spent, annotatedTransaction.Record.TransactionHash);
                }
            }

removeConflicts:
            HashSet <uint256> toRemove = new HashSet <uint256>();

            foreach (var annotatedTransaction in _TxById.Values.Where(r => r.Height is null))
            {
                foreach (var spent in annotatedTransaction.Record.SpentOutpoints)
                {
                    if (spentBy.TryGetValue(spent, out var conflictHash) &&
                        _TxById.TryGetValue(conflictHash, out var conflicted))
                    {
                        if (conflicted == annotatedTransaction)
                        {
                            goto nextTransaction;
                        }
                        if (toRemove.Contains(conflictHash))
                        {
                            spentBy.Remove(spent);
                            spentBy.Add(spent, annotatedTransaction.Record.TransactionHash);
                        }
                        else if (ShouldReplace(annotatedTransaction, conflicted))
                        {
                            toRemove.Add(conflictHash);
                            spentBy.Remove(spent);
                            spentBy.Add(spent, annotatedTransaction.Record.TransactionHash);

                            if (conflicted.Height is null && annotatedTransaction.Height is null)
                            {
                                ReplacedTransactions.Add(conflicted);
                            }
                            else
                            {
                                CleanupTransactions.Add(conflicted);
                            }
                        }
                        else
                        {
                            toRemove.Add(annotatedTransaction.Record.TransactionHash);
                            if (conflicted.Height is null && annotatedTransaction.Height is null)
                            {
                                ReplacedTransactions.Add(annotatedTransaction);
                            }