Exemplo n.º 1
0
        public static bool TryParse(string str, out TrackedSource trackedSource, NBXplorerNetwork network)
        {
            if (str == null)
            {
                throw new ArgumentNullException(nameof(str));
            }
            if (network == null)
            {
                throw new ArgumentNullException(nameof(network));
            }
            trackedSource = null;
            var strSpan = str.AsSpan();

            if (strSpan.StartsWith("DERIVATIONSCHEME:".AsSpan(), StringComparison.Ordinal))
            {
                if (!DerivationSchemeTrackedSource.TryParse(strSpan, out var derivationSchemeTrackedSource, network))
                {
                    return(false);
                }
                trackedSource = derivationSchemeTrackedSource;
            }
            else if (strSpan.StartsWith("ADDRESS:".AsSpan(), StringComparison.Ordinal))
            {
                if (!AddressTrackedSource.TryParse(strSpan, out var addressTrackedSource, network.NBitcoinNetwork))
                {
                    return(false);
                }
                trackedSource = addressTrackedSource;
            }
            else
            {
                return(false);
            }
            return(true);
        }
Exemplo n.º 2
0
        public override bool Equals(object obj)
        {
            TrackedSource item = obj as TrackedSource;

            if (item == null)
            {
                return(false);
            }
            return(ToString().Equals(item.ToString()));
        }
        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;
        }
Exemplo n.º 4
0
        public override string ToString()
        {
            var conf = (BlockId == null ? "unconfirmed" : "confirmed");

            string strategy = TrackedSource.ToPrettyString();
            var    txId     = TransactionData.TransactionHash.ToString();

            txId = txId.Substring(0, 6) + "..." + txId.Substring(txId.Length - 6);

            string keyPathSuffix = string.Empty;
            var    keyPaths      = Outputs.Select(v => v.KeyPath?.ToString()).Where(k => k != null).ToArray();

            if (keyPaths.Length != 0)
            {
                keyPathSuffix = $" ({String.Join(", ", keyPaths)})";
            }
            return($"{CryptoCode}: {strategy} matching {conf} transaction {txId}{keyPathSuffix}");
        }
Exemplo n.º 5
0
 public static bool TryParse(ReadOnlySpan <char> strSpan, out TrackedSource addressTrackedSource, Network network)
 {
     if (strSpan == null)
     {
         throw new ArgumentNullException(nameof(strSpan));
     }
     if (network == null)
     {
         throw new ArgumentNullException(nameof(network));
     }
     addressTrackedSource = null;
     if (!strSpan.StartsWith("ADDRESS:".AsSpan(), StringComparison.Ordinal))
     {
         return(false);
     }
     try
     {
         addressTrackedSource = new AddressTrackedSource(BitcoinAddress.Create(strSpan.Slice("ADDRESS:".Length).ToString(), network));
         return(true);
     }
     catch { return(false); }
 }
Exemplo n.º 6
0
        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);
                }
            }

            var unconfs = _TxById.Where(r => r.Value.Height is null)
                          .ToDictionary(kv => kv.Key, kv => kv.Value);
            var replaced = new Dictionary <uint256, AnnotatedTransaction>();

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

            foreach (var annotatedTransaction in unconfs.Values)
            {
                foreach (var spent in annotatedTransaction.Record.SpentOutpoints)
                {
                    // All children of a replaced transaction should be replaced
                    if (replaced.TryGetValue(spent.Hash, out var parent) && parent.ReplacedBy is uint256)
                    {
                        annotatedTransaction.Replaceable = false;
                        annotatedTransaction.ReplacedBy  = parent.ReplacedBy;
                        replaced.TryAdd(annotatedTransaction.Record.TransactionHash, annotatedTransaction);
                        toRemove.Add(annotatedTransaction.Record.TransactionHash);
                        goto nextTransaction;
                    }

                    // If there is a conflict, let's see who get replaced
                    if (spentBy.TryGetValue(spent, out var conflictHash) &&
                        _TxById.TryGetValue(conflictHash, out var conflicted))
                    {
                        // Conflict with one-self... not a conflict.
                        if (conflicted == annotatedTransaction)
                        {
                            goto nextTransaction;
                        }
                        // We know the conflict is already removed, so this transaction replace it
                        if (toRemove.Contains(conflictHash))
                        {
                            spentBy.Remove(spent);
                            spentBy.Add(spent, annotatedTransaction.Record.TransactionHash);
                        }
                        else
                        {
                            AnnotatedTransaction toKeep = null, toReplace = null;
                            var shouldReplace = ShouldReplace(annotatedTransaction, conflicted);
                            if (shouldReplace)
                            {
                                toReplace = conflicted;
                                toKeep    = annotatedTransaction;
                            }
                            else
                            {
                                toReplace = annotatedTransaction;
                                toKeep    = conflicted;
                            }
                            toRemove.Add(toReplace.Record.TransactionHash);

                            if (toKeep == annotatedTransaction)
                            {
                                spentBy.Remove(spent);
                                spentBy.Add(spent, annotatedTransaction.Record.TransactionHash);
                            }

                            if (toKeep.Height is null && toReplace.Height is null)
                            {
                                toReplace.ReplacedBy  = toKeep.Record.TransactionHash;
                                toReplace.Replaceable = false;
                                toKeep.Replacing      = toReplace.Record.TransactionHash;
                                replaced.TryAdd(toReplace.Record.TransactionHash, toReplace);
                            }
                            else
                            {
                                CleanupTransactions.Add(toReplace);
                            }
                        }
                    }
        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);
                            }