Exemple #1
0
        public async Task <FileContentResult> Sync(
            [ModelBinder(BinderType = typeof(DestinationModelBinder))]
            DerivationStrategyBase extPubKey,
            [ModelBinder(BinderType = typeof(UInt256ModelBinding))]
            uint256 confHash = null,
            [ModelBinder(BinderType = typeof(UInt256ModelBinding))]
            uint256 unconfHash = null,
            bool noWait        = false)
        {
            if (extPubKey == null)
            {
                throw new ArgumentNullException(nameof(extPubKey));
            }

            var         waitingTransaction = noWait ? Task.FromResult(false) : WaitingTransaction(extPubKey);
            UTXOChanges changes            = null;
            var         getKeyPaths        = GetKeyPaths(extPubKey);
            var         matchScript        = MatchKeyPaths(getKeyPaths);

            while (true)
            {
                changes = new UTXOChanges();
                changes.CurrentHeight = Chain.Height;
                var transactions = GetAnnotatedTransactions(extPubKey);

                var unconf = transactions.Where(tx => tx.Height == MempoolHeight);
                var conf   = transactions.Where(tx => tx.Height != MempoolHeight);
                conf   = conf.TopologicalSort(DependsOn(conf.ToList())).ToList();
                unconf = unconf.OrderByDescending(t => t.Record.Inserted)
                         .TopologicalSort(DependsOn(unconf.ToList())).ToList();


                var states = UTXOStateResult.CreateStates(matchScript, unconfHash, unconf.Select(c => c.Record.Transaction), confHash, conf.Select(c => c.Record.Transaction));

                var conflicted = states.Unconfirmed.Actual.Conflicts
                                 .SelectMany(c => c.Value)
                                 .SelectMany(txid => transactions.GetByTxId(txid))
                                 .Where(a => a.Height == MempoolHeight)
                                 .Select(a => a.Record)
                                 .Distinct()
                                 .ToList();

                if (conflicted.Count != 0)
                {
                    Logs.Explorer.LogInformation($"Clean {conflicted.Count} conflicted transactions");
                    if (Logs.Explorer.IsEnabled(LogLevel.Debug))
                    {
                        foreach (var conflict in conflicted)
                        {
                            Logs.Explorer.LogDebug($"Transaction {conflict.Transaction.GetHash()} is conflicted");
                        }
                    }
                    Repository.CleanTransactions(extPubKey, conflicted);
                }

                changes.Confirmed   = SetUTXOChange(states.Confirmed);
                changes.Unconfirmed = SetUTXOChange(states.Unconfirmed, states.Confirmed.Actual);



                FillUTXOsInformation(changes.Confirmed.UTXOs, getKeyPaths, transactions, changes.CurrentHeight);
                FillUTXOsInformation(changes.Unconfirmed.UTXOs, getKeyPaths, transactions, changes.CurrentHeight);

                if (changes.HasChanges || !(await waitingTransaction))
                {
                    break;
                }
                waitingTransaction = Task.FromResult(false);                 //next time, will not wait
            }

            return(new FileContentResult(changes.ToBytes(), "application/octet-stream"));
        }
        public async Task <FileContentResult> Sync(
            [ModelBinder(BinderType = typeof(DestinationModelBinder))]
            BitcoinExtPubKey extPubKey,
            [ModelBinder(BinderType = typeof(UInt256ModelBinding))]
            uint256 lastBlockHash = null,
            [ModelBinder(BinderType = typeof(UInt256ModelBinding))]
            uint256 unconfirmedHash = null,
            bool noWait             = false)
        {
            lastBlockHash = lastBlockHash ?? uint256.Zero;
            var actualLastBlockHash = uint256.Zero;

            var waitingTransaction = noWait ? Task.FromResult(false) : WaitingTransaction(extPubKey);

            Runtime.Repository.MarkAsUsed(new KeyInformation(extPubKey));
            UTXOChanges changes                 = null;
            UTXOChanges previousChanges         = null;
            List <TrackedTransaction> cleanList = null;
            var getKeyPath = GetKeyPaths(extPubKey);

            while (true)
            {
                cleanList = new List <TrackedTransaction>();
                HashSet <uint256> conflictedUnconf = new HashSet <uint256>();
                changes = new UTXOChanges();
                List <AnnotatedTransaction> transactions = GetAnnotatedTransactions(extPubKey);

                var unconf = transactions.Where(tx => tx.Height == MempoolHeight);
                var conf   = transactions.Where(tx => tx.Height != MempoolHeight);

                conf   = conf.TopologicalSort(DependsOn(conf.ToList())).ToList();
                unconf = unconf.TopologicalSort(DependsOn(unconf.ToList())).ToList();

                foreach (var item in conf.Concat(unconf))
                {
                    var record = item.Record;
                    if (record.BlockHash == null)
                    {
                        if (                        //A parent conflicted with the current utxo
                            record.Transaction.Inputs.Any(i => conflictedUnconf.Contains(i.PrevOut.Hash))
                            ||
                            //Conflict with the confirmed utxo
                            changes.Confirmed.HasConflict(record.Transaction))
                        {
                            cleanList.Add(record);
                            conflictedUnconf.Add(record.Transaction.GetHash());
                            continue;
                        }
                        if (changes.Unconfirmed.HasConflict(record.Transaction))
                        {
                            Logs.Explorer.LogInformation($"Conflicts in the mempool. {record.Transaction.GetHash()} ignored");
                            continue;
                        }
                        changes.Unconfirmed.LoadChanges(record.Transaction, getKeyPath);
                    }
                    else
                    {
                        if (changes.Confirmed.HasConflict(record.Transaction))
                        {
                            Logs.Explorer.LogError("A conflict among confirmed transaction happened, this should be impossible");
                            throw new InvalidOperationException("The impossible happened");
                        }
                        changes.Unconfirmed.LoadChanges(record.Transaction, getKeyPath);
                        changes.Confirmed.LoadChanges(record.Transaction, getKeyPath);
                        changes.Confirmed.Hash = record.BlockHash;
                        actualLastBlockHash    = record.BlockHash;
                        if (record.BlockHash == lastBlockHash)
                        {
                            previousChanges = changes.Clone();
                        }
                    }
                }

                changes.Unconfirmed      = changes.Unconfirmed.Diff(changes.Confirmed);
                changes.Unconfirmed.Hash = changes.Unconfirmed.GetHash();
                if (changes.Unconfirmed.Hash == unconfirmedHash)
                {
                    changes.Unconfirmed.Clear();
                }
                else
                {
                    changes.Unconfirmed.Reset = true;
                }


                if (actualLastBlockHash == lastBlockHash)
                {
                    changes.Confirmed.Clear();
                }
                else if (previousChanges != null)
                {
                    changes.Confirmed.Reset = false;
                    changes.Confirmed       = changes.Confirmed.Diff(previousChanges.Confirmed);
                }
                else
                {
                    changes.Confirmed.Reset = true;
                    changes.Confirmed.SpentOutpoints.Clear();
                }

                if (changes.HasChanges || !(await waitingTransaction))
                {
                    break;
                }
                waitingTransaction = Task.FromResult(false);                 //next time, will not wait
            }

            Runtime.Repository.CleanTransactions(extPubKey.ExtPubKey, cleanList);

            return(new FileContentResult(changes.ToBytes(), "application/octet-stream"));
        }