Exemplo n.º 1
0
        public void CanTrack3()
        {
            using (var tester = ServerTester.Create())
            {
                var key    = new BitcoinExtKey(new ExtKey(), tester.Network);
                var pubkey = CreateDerivationStrategy(key.Neuter());
                tester.Client.Track(pubkey);
                tester.Client.Sync(pubkey, null, null, true);                 //Track things do not wait
                var id = tester.RPC.SendToAddress(AddressOf(key, "0/0"), Money.Coins(1.0m));
                id = tester.RPC.SendToAddress(AddressOf(key, "0/1"), Money.Coins(1.1m));
                id = tester.RPC.SendToAddress(AddressOf(key, "0/2"), Money.Coins(1.2m));

                UTXOChanges utxo = null;

                while (utxo == null || utxo.Unconfirmed.UTXOs.Count != 3)
                {
                    utxo = tester.Client.Sync(pubkey, null, null);
                }

                tester.RPC.Generate(1);

                utxo = tester.Client.Sync(pubkey, utxo);
                Assert.True(utxo.HasChanges);
                Assert.Equal(3, utxo.Confirmed.UTXOs.Count);
                utxo = tester.Client.Sync(pubkey, utxo, true);
                Assert.False(utxo.HasChanges);
            }
        }
Exemplo n.º 2
0
        private async Task UpdateInvoice(string invoiceId)
        {
            UTXOChanges changes = null;

            while (true)
            {
                try
                {
                    var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId).ConfigureAwait(false);

                    if (invoice == null)
                    {
                        break;
                    }
                    var stateBefore = invoice.Status;
                    var result      = await UpdateInvoice(changes, invoice).ConfigureAwait(false);

                    changes = result.Changes;
                    if (result.NeedSave)
                    {
                        await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.Status, invoice.ExceptionStatus).ConfigureAwait(false);
                    }

                    var changed = stateBefore != invoice.Status;
                    if (changed)
                    {
                        Logs.PayServer.LogInformation($"Invoice {invoice.Id}: {stateBefore} => {invoice.Status}");
                    }

                    var expirationMonitoring = invoice.MonitoringExpiration.HasValue ? invoice.MonitoringExpiration.Value : invoice.InvoiceTime + TimeSpan.FromMinutes(60);
                    if (invoice.Status == "complete" ||
                        ((invoice.Status == "invalid" || invoice.Status == "expired") && expirationMonitoring < DateTimeOffset.UtcNow))
                    {
                        if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id).ConfigureAwait(false))
                        {
                            Logs.PayServer.LogInformation("Stopped watching invoice " + invoiceId);
                        }
                        break;
                    }

                    if (!changed || _Cts.Token.IsCancellationRequested)
                    {
                        break;
                    }
                }
                catch (OperationCanceledException) when(_Cts.Token.IsCancellationRequested)
                {
                    break;
                }
                catch (Exception ex)
                {
                    Logs.PayServer.LogError(ex, "Unhandled error on watching invoice " + invoiceId);
                    await Task.Delay(10000, _Cts.Token).ConfigureAwait(false);
                }
            }
        }
Exemplo n.º 3
0
        public async Task <UTXOChanges> GetUTXOs(
            string cryptoCode,
            [ModelBinder(BinderType = typeof(DestinationModelBinder))]
            DerivationStrategyBase extPubKey,
            [ModelBinder(BinderType = typeof(BookmarksModelBinding))]
            HashSet <Bookmark> confirmedBookmarks = null,
            [ModelBinder(BinderType = typeof(BookmarksModelBinding))]
            HashSet <Bookmark> unconfirmedBookmarks = null,
            bool longPolling = false)
        {
            unconfirmedBookmarks = unconfirmedBookmarks ?? new HashSet <Bookmark>();
            confirmedBookmarks   = confirmedBookmarks ?? new HashSet <Bookmark>();
            if (extPubKey == null)
            {
                throw new ArgumentNullException(nameof(extPubKey));
            }
            var         network            = GetNetwork(cryptoCode);
            var         chain              = ChainProvider.GetChain(network);
            var         repo               = RepositoryProvider.GetRepository(network);
            var         waitingTransaction = longPolling ? WaitingTransaction(extPubKey) : Task.FromResult(false);
            UTXOChanges changes            = null;

            while (true)
            {
                changes = new UTXOChanges();
                changes.CurrentHeight = chain.Height;
                var transactions = GetAnnotatedTransactions(repo, chain, extPubKey);
                Func <Script[], bool[]> matchScript = (scripts) => scripts.Select(s => transactions.GetKeyPath(s) != null).ToArray();

                var states = UTXOStateResult.CreateStates(matchScript,
                                                          unconfirmedBookmarks,
                                                          transactions.UnconfirmedTransactions.Values.Select(c => c.Record.Transaction),
                                                          confirmedBookmarks,
                                                          transactions.ConfirmedTransactions.Values.Select(c => c.Record.Transaction));

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



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

                if (changes.HasChanges || !(await waitingTransaction))
                {
                    break;
                }
                waitingTransaction = Task.FromResult(false);                 //next time, will not wait
            }
            changes.DerivationStrategy = extPubKey;
            return(changes);
        }
Exemplo n.º 4
0
        private async Task <UTXOChanges> GetUTXOChanges(DerivationStrategyBase strategy, CancellationToken cancellation)
        {
            var thisCompletionSource = new TaskCompletionSource <UTXOChanges>();
            var completionSource     = _FetchingUTXOs.GetOrAdd(strategy.ToString(), (s) => thisCompletionSource);

            if (thisCompletionSource != completionSource)
            {
                return(await completionSource.Task);
            }
            try
            {
                var utxos = await _MemoryCache.GetOrCreateAsync("CACHEDCOINS_" + strategy.ToString(), async entry =>
                {
                    var now            = DateTimeOffset.UtcNow;
                    UTXOChanges result = null;
                    try
                    {
                        result = await _Client.GetUTXOsAsync(strategy, cancellation).ConfigureAwait(false);
                    }
                    catch
                    {
                        Logs.PayServer.LogError($"{Network.CryptoCode}: Call to NBXplorer GetUTXOsAsync timed out, this should never happen, please report this issue to NBXplorer developers");
                        throw;
                    }
                    var spentTime = DateTimeOffset.UtcNow - now;
                    if (spentTime.TotalSeconds > 30)
                    {
                        Logs.PayServer.LogWarning($"{Network.CryptoCode}: NBXplorer took {(int)spentTime.TotalSeconds} seconds to reply, there is something wrong, please report this issue to NBXplorer developers");
                    }
                    entry.AbsoluteExpiration = DateTimeOffset.UtcNow + CacheSpan;
                    return(result);
                });

                _FetchingUTXOs.TryRemove(strategy.ToString(), out var unused);
                completionSource.TrySetResult(utxos);
            }
            catch (Exception ex)
            {
                completionSource.TrySetException(ex);
            }
            finally
            {
                _FetchingUTXOs.TryRemove(strategy.ToString(), out var unused);
            }
            return(await completionSource.Task);
        }
Exemplo n.º 5
0
        public async Task <UTXOChanges> SyncAsync(DerivationStrategyBase extKey, uint256 confHash, uint256 unconfHash, bool noWait = false, CancellationToken cancellation = default(CancellationToken))
        {
            Dictionary <string, string> parameters = new Dictionary <string, string>();

            if (confHash != null)
            {
                parameters.Add("confHash", confHash.ToString());
            }
            if (unconfHash != null)
            {
                parameters.Add("unconfHash", unconfHash.ToString());
            }
            parameters.Add("noWait", noWait.ToString());

            var query = String.Join("&", parameters.Select(p => p.Key + "=" + p.Value).ToArray());
            var bytes = await SendAsync <byte[]>(HttpMethod.Get, null, "v1/sync/{0}?" + query, new object[] { extKey.ToString() }, cancellation).ConfigureAwait(false);

            UTXOChanges changes = new UTXOChanges();

            changes.FromBytes(bytes);
            return(changes);
        }
Exemplo n.º 6
0
 public UTXOChanges GetUTXOs(DerivationStrategyBase extKey, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default)
 {
     return(GetUTXOsAsync(extKey, previousChange, longPolling, cancellation).GetAwaiter().GetResult());
 }
Exemplo n.º 7
0
        private async Task <(bool NeedSave, UTXOChanges Changes)> UpdateInvoice(UTXOChanges changes, InvoiceEntity invoice)
        {
            bool needSave = false;
            //Fetch unknown payments
            var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);

            changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false);

            var         utxos         = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
            List <Coin> receivedCoins = new List <Coin>();

            foreach (var received in utxos)
            {
                if (invoice.AvailableAddressHashes.Contains(received.Output.ScriptPubKey.Hash.ToString()))
                {
                    receivedCoins.Add(new Coin(received.Outpoint, received.Output));
                }
            }

            var  alreadyAccounted = new HashSet <OutPoint>(invoice.Payments.Select(p => p.Outpoint));
            bool dirtyAddress     = false;

            foreach (var coin in receivedCoins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
            {
                var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false);

                invoice.Payments.Add(payment);
                dirtyAddress = true;
            }
            //////

            if (invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
            {
                needSave = true;
                await _InvoiceRepository.UnaffectAddress(invoice.Id);

                invoice.Status = "expired";
                if (invoice.FullNotifications)
                {
                    _NotificationManager.Notify(invoice);
                }
            }

            if (invoice.Status == "new" || invoice.Status == "expired")
            {
                var totalPaid = (await GetPaymentsWithTransaction(invoice)).Select(p => p.Payment.Output.Value).Sum();
                if (totalPaid >= invoice.GetTotalCryptoDue())
                {
                    if (invoice.Status == "new")
                    {
                        invoice.Status = "paid";
                        if (invoice.FullNotifications)
                        {
                            _NotificationManager.Notify(invoice);
                        }
                        invoice.ExceptionStatus = null;
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        needSave = true;
                    }
                    else if (invoice.Status == "expired")
                    {
                        invoice.ExceptionStatus = "paidLate";
                        needSave = true;
                    }
                }

                if (totalPaid > invoice.GetTotalCryptoDue() && invoice.ExceptionStatus != "paidOver")
                {
                    invoice.ExceptionStatus = "paidOver";
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    needSave = true;
                }

                if (totalPaid < invoice.GetTotalCryptoDue() && invoice.Payments.Count != 0 && invoice.ExceptionStatus != "paidPartial")
                {
                    Logs.PayServer.LogInformation("Paid to " + invoice.DepositAddress);
                    invoice.ExceptionStatus = "paidPartial";
                    needSave = true;
                    if (dirtyAddress)
                    {
                        var address = await _Wallet.ReserveAddressAsync(_DerivationFactory.Parse(invoice.DerivationStrategy));

                        Logs.PayServer.LogInformation("Generate new " + address);
                        await _InvoiceRepository.NewAddress(invoice.Id, address);
                    }
                }
            }

            if (invoice.Status == "paid")
            {
                var transactions = await GetPaymentsWithTransaction(invoice);

                var chainConfirmedTransactions = transactions.Where(t => t.Confirmations >= 1);
                if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
                {
                    transactions = transactions.Where(t => !t.Transaction.RBF);
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.MediumSpeed)
                {
                    transactions = transactions.Where(t => t.Confirmations >= 1);
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.LowSpeed)
                {
                    transactions = transactions.Where(t => t.Confirmations >= 6);
                }

                var chainTotalConfirmed = chainConfirmedTransactions.Select(t => t.Payment.Output.Value).Sum();

                if (// Is after the monitoring deadline
                    (invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
                    &&
                    // And not enough amount confirmed
                    (chainTotalConfirmed < invoice.GetTotalCryptoDue()))
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    invoice.Status = "invalid";
                    needSave       = true;
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                }
                else
                {
                    var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
                    if (totalConfirmed >= invoice.GetTotalCryptoDue())
                    {
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        invoice.Status = "confirmed";
                        _NotificationManager.Notify(invoice);
                        needSave = true;
                    }
                }
            }

            if (invoice.Status == "confirmed")
            {
                var transactions = await GetPaymentsWithTransaction(invoice);

                transactions = transactions.Where(t => t.Confirmations >= 6);
                var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
                if (totalConfirmed >= invoice.GetTotalCryptoDue())
                {
                    invoice.Status = "complete";
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                    needSave = true;
                }
            }
            return(needSave, changes);
        }
Exemplo n.º 8
0
 public Money GetBalance(UTXOChanges utxoChanges)
 {
     return(new Money(utxoChanges.GetUnspentUTXOs().Select(c => c.Value).Sum(money => money.GetValue(null)), MoneyUnit.BTC));
 }
Exemplo n.º 9
0
 public Task <UTXOChanges> GetUTXOsAsync(TrackedSource trackedSource, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default)
 {
     return(GetUTXOsAsync(trackedSource, previousChange?.Confirmed?.Bookmark, previousChange?.Unconfirmed?.Bookmark, longPolling, cancellation));
 }
Exemplo n.º 10
0
        public async Task <Money> GetBalance(DerivationStrategyBase derivationStrategy, CancellationToken cancellation = default(CancellationToken))
        {
            UTXOChanges changes = await GetUTXOChanges(derivationStrategy, cancellation);

            return(changes.GetUnspentUTXOs().Select(c => c.Value).Sum());
        }
Exemplo n.º 11
0
 void UseUtxoCoins(IEnumerable <WalletAddr> addrs, List <CoinCandidate> candidates, UTXOChanges utxos, int minConfs = 0)
 {
     if (minConfs <= 0)
     {
         foreach (var utxo in utxos.Unconfirmed.UTXOs)
         {
             var addrStr = utxo.ScriptPubKey.GetDestinationAddress(GetNetwork()).ToString();
             var addr    = addrs.Where(a => a.Address == addrStr).FirstOrDefault();
             if (addr != null)
             {
                 candidates.Add(new CoinCandidate(addr, utxo.AsCoin(), utxo));
             }
         }
     }
     foreach (var utxo in utxos.Confirmed.UTXOs)
     {
         if (minConfs > 0 && utxo.Confirmations < minConfs)
         {
             continue;
         }
         // check is not already spent but as yet unconfirmed
         if (utxos.Unconfirmed.SpentOutpoints.Any(so => so.Hash == utxo.Outpoint.Hash))
         {
             continue;
         }
         var addrStr = utxo.ScriptPubKey.GetDestinationAddress(GetNetwork()).ToString();
         var addr    = addrs.Where(a => a.Address == addrStr).FirstOrDefault();
         if (addr != null)
         {
             candidates.Add(new CoinCandidate(addr, utxo.AsCoin(), utxo));
         }
     }
 }
Exemplo n.º 12
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"));
        }
Exemplo n.º 13
0
        private async Task UpdateInvoice(string invoiceId)
        {
            UTXOChanges changes = null;

            while (true)
            {
                try
                {
                    var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId, true).ConfigureAwait(false);

                    if (invoice == null)
                    {
                        break;
                    }
                    var stateBefore     = invoice.Status;
                    var postSaveActions = new List <Action>();
                    var result          = await UpdateInvoice(changes, invoice, postSaveActions).ConfigureAwait(false);

                    changes = result.Changes;
                    if (result.NeedSave)
                    {
                        await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.Status, invoice.ExceptionStatus).ConfigureAwait(false);

                        _EventAggregator.Publish(new InvoiceDataChangedEvent()
                        {
                            InvoiceId = invoice.Id
                        });
                    }

                    var changed = stateBefore != invoice.Status;

                    foreach (var saveAction in postSaveActions)
                    {
                        saveAction();
                    }

                    if (invoice.Status == "complete" ||
                        ((invoice.Status == "invalid" || invoice.Status == "expired") && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
                    {
                        if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id).ConfigureAwait(false))
                        {
                            Logs.PayServer.LogInformation("Stopped watching invoice " + invoiceId);
                        }
                        break;
                    }

                    if (!changed || _Cts.Token.IsCancellationRequested)
                    {
                        break;
                    }
                }
                catch (OperationCanceledException) when(_Cts.Token.IsCancellationRequested)
                {
                    break;
                }
                catch (Exception ex)
                {
                    Logs.PayServer.LogError(ex, "Unhandled error on watching invoice " + invoiceId);
                    await Task.Delay(10000, _Cts.Token).ConfigureAwait(false);
                }
            }
        }
Exemplo n.º 14
0
 public Money GetBalance(UTXOChanges utxoChanges)
 {
     return(utxoChanges.GetUnspentUTXOs().Select(c => c.Value).Sum());
 }
Exemplo n.º 15
0
        private async Task <(bool NeedSave, UTXOChanges Changes)> UpdateInvoice(UTXOChanges changes, InvoiceEntity invoice)
        {
            bool needSave = false;

            if (invoice.Status != "invalid" && invoice.ExpirationTime < DateTimeOffset.UtcNow && (invoice.Status == "new" || invoice.Status == "paidPartial"))
            {
                needSave       = true;
                invoice.Status = "invalid";
            }

            if (invoice.Status == "invalid" || invoice.Status == "new" || invoice.Status == "paidPartial")
            {
                var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);
                changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false);

                var utxos      = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
                var invoiceIds = utxos.Select(u => _Wallet.GetInvoiceId(u.Output.ScriptPubKey)).ToArray();
                utxos =
                    utxos
                    .Where((u, i) => invoiceIds[i].GetAwaiter().GetResult() == invoice.Id)
                    .ToArray();

                List <Coin> receivedCoins = new List <Coin>();
                foreach (var received in utxos)
                {
                    if (received.Output.ScriptPubKey == invoice.DepositAddress.ScriptPubKey)
                    {
                        receivedCoins.Add(new Coin(received.Outpoint, received.Output));
                    }
                }

                var alreadyAccounted = new HashSet <OutPoint>(invoice.Payments.Select(p => p.Outpoint));
                foreach (var coin in receivedCoins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
                {
                    var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false);

                    invoice.Payments.Add(payment);
                    if (invoice.Status == "new")
                    {
                        invoice.Status = "paidPartial";
                        needSave       = true;
                    }
                }
            }

            if (invoice.Status == "paidPartial")
            {
                var totalPaid = invoice.Payments.Select(p => p.Output.Value).Sum();
                if (totalPaid == invoice.GetTotalCryptoDue())
                {
                    invoice.Status = "paid";
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                    invoice.ExceptionStatus = null;
                    needSave = true;
                }

                if (totalPaid > invoice.GetTotalCryptoDue())
                {
                    invoice.Status          = "paidOver";
                    invoice.ExceptionStatus = "paidOver";
                    needSave = true;
                }

                if (totalPaid < invoice.GetTotalCryptoDue() && invoice.ExceptionStatus == null)
                {
                    invoice.ExceptionStatus = "paidPartial";
                    needSave = true;
                }
            }

            if (invoice.Status == "paid" || invoice.Status == "paidOver")
            {
                var getTransactions = invoice.Payments.Select(o => o.Outpoint.Hash).Select(o => _ExplorerClient.GetTransactionAsync(o, _Cts.Token)).ToArray();
                await Task.WhenAll(getTransactions).ConfigureAwait(false);

                var transactions = getTransactions.Select(c => c.GetAwaiter().GetResult()).ToArray();

                bool confirmed = false;
                var  minConf   = transactions.Select(t => t.Confirmations).Min();
                if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
                {
                    if (minConf > 0)
                    {
                        confirmed = true;
                    }
                    else
                    {
                        confirmed = !transactions.Any(t => t.Transaction.RBF);
                    }
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.MediumSpeed)
                {
                    confirmed = minConf >= 1;
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.LowSpeed)
                {
                    confirmed = minConf >= 6;
                }

                if (confirmed)
                {
                    invoice.Status = "confirmed";
                    _NotificationManager.Notify(invoice);
                    needSave = true;
                }
            }

            if (invoice.Status == "confirmed")
            {
                var getTransactions = invoice.Payments.Select(o => o.Outpoint.Hash).Select(o => _ExplorerClient.GetTransactionAsync(o, _Cts.Token)).ToArray();
                await Task.WhenAll(getTransactions).ConfigureAwait(false);

                var transactions = getTransactions.Select(c => c.GetAwaiter().GetResult()).ToArray();
                var minConf      = transactions.Select(t => t.Confirmations).Min();
                if (minConf >= 6)
                {
                    invoice.Status = "complete";
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                    needSave = true;
                }
            }

            return(needSave, changes);
        }
Exemplo n.º 16
0
 public UTXOChanges GetUTXOs(TrackedSource trackedSource, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default)
 {
     return(GetUTXOsAsync(trackedSource, previousChange, longPolling, cancellation).GetAwaiter().GetResult());
 }
Exemplo n.º 17
0
 public Task <UTXOChanges> GetUTXOsAsync(DerivationStrategyBase extKey, UTXOChanges previousChange, bool longPolling = true, CancellationToken cancellation = default)
 {
     return(GetUTXOsAsync(extKey, previousChange?.Confirmed?.Bookmark, previousChange?.Unconfirmed?.Bookmark, longPolling, cancellation));
 }
Exemplo n.º 18
0
 public UTXOChanges Sync(DerivationStrategyBase extKey, UTXOChanges previousChange, bool noWait = false, CancellationToken cancellation = default(CancellationToken))
 {
     return(SyncAsync(extKey, previousChange, noWait, cancellation).GetAwaiter().GetResult());
 }
Exemplo n.º 19
0
 public Task <UTXOChanges> SyncAsync(DerivationStrategyBase extKey, UTXOChanges previousChange, bool noWait = false, CancellationToken cancellation = default(CancellationToken))
 {
     return(SyncAsync(extKey, previousChange?.Confirmed?.Hash, previousChange?.Unconfirmed?.Hash, noWait, cancellation));
 }
Exemplo n.º 20
0
        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"));
        }