Пример #1
0
        public IEnumerable <T> Query(ChainBase chain, BalanceQuery query = null)
        {
            if (query == null)
            {
                query = new BalanceQuery();
            }
            var tableQuery = query.CreateTableQuery(Escape(Scope), "");

            return(ExecuteBalanceQuery(Table, tableQuery, query.PageSizes)
                   .Where(_ => chain.Contains(((ConfirmedBalanceLocator)UnEscapeLocator(_.RowKey)).BlockHash))
                   .Select(_ => Serializer.ToObject <T>(ParseData(_))));
        }
Пример #2
0
        /// Function:   ListAsync
        ///
        /// List player balances asynchronously.
        ///
        /// Parameters:
        /// balanceQuery -  The balance query.
        ///
        /// Returns:    An asynchronous result that yields the list.
        public async Task <IEnumerable <Balance> > ListAsync(BalanceQuery balanceQuery)
        {
            // AsNoTracking tells EF Core it doesn't need to track changes on listed entities.
            // Disabling entity tracking makes the code a little faster.
            var queryable = _context.Balances.Where(p => p.PlayerId == balanceQuery.PlayerId).AsNoTracking();

            if (balanceQuery.Asset.HasValue && balanceQuery.Asset > 0)
            {
                queryable = queryable.Where(p => p.Asset == balanceQuery.Asset);
            }

            return(await queryable.ToListAsync());
        }
Пример #3
0
        BalanceModel Balance(BalanceId balanceId,
                             BalanceLocator continuation,
                             BlockFeature until,
                             BlockFeature from,
                             bool includeImmature,
                             bool unspentOnly,
                             bool colored,
                             int?unconfExpiration)
        {
            var expiration = GetExpiration(unconfExpiration);
            CancellationTokenSource cancel = new CancellationTokenSource();

            cancel.CancelAfter(30000);

            BalanceQuery query = new BalanceQuery();

            query.RawOrdering = true;
            query.From        = null;

            if (from != null)
            {
                query.From         = ToBalanceLocator(from);
                query.FromIncluded = true;
            }

            if (continuation != null)
            {
                query = new BalanceQuery
                {
                    From         = continuation,
                    FromIncluded = false
                };
                query.RawOrdering = true;
            }

            if (query.From == null)
            {
                query.From = new UnconfirmedBalanceLocator(DateTimeOffset.UtcNow - expiration);
            }

            if (until != null)
            {
                query.To           = ToBalanceLocator(until);
                query.FromIncluded = true;
            }

            if (query.To.IsGreaterThan(query.From))
            {
                throw InvalidParameters("Invalid argument : from < until");
            }

            var client = Configuration.Indexer.CreateIndexerClient();

            client.ColoredBalance = colored;
            var balance =
                client
                .GetOrderedBalance(balanceId, query)
                .TakeWhile(_ => !cancel.IsCancellationRequested)
                .WhereNotExpired(expiration)
                .Where(o => includeImmature || IsMature(o, Chain.Tip))
                .AsBalanceSheet(Chain);

            var balanceChanges = balance.All;

            if (until != null && balance.Confirmed.Count != 0)            //Strip unconfirmed that can appear after the last until
            {
                List <OrderedBalanceChange> oldUnconfirmed = new List <OrderedBalanceChange>();
                var older = balanceChanges.Last();
                for (int i = 0; i < balanceChanges.Count; i++)
                {
                    var last = balanceChanges[i];
                    if (last.BlockId == null)
                    {
                        if (last.SeenUtc < older.SeenUtc)
                        {
                            oldUnconfirmed.Add(last);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                foreach (var unconf in oldUnconfirmed)
                {
                    balanceChanges.Remove(unconf);
                }
            }

            var conflicts = RemoveConflicts(balance);

            if (unspentOnly)
            {
                HashSet <OutPoint> spents = new HashSet <OutPoint>();
                foreach (var change in balanceChanges.SelectMany(b => b.SpentCoins))
                {
                    spents.Add(change.Outpoint);
                }
                foreach (var change in balanceChanges)
                {
                    change.SpentCoins.Clear();
                    change.ReceivedCoins.RemoveAll(c => spents.Contains(c.Outpoint));
                }
            }

            var result = new BalanceModel(balanceChanges, Chain);

            result.ConflictedOperations = result.GetBalanceOperations(conflicts, Chain);
            if (cancel.IsCancellationRequested)
            {
                if (balanceChanges.Count > 0)
                {
                    var lastop = balanceChanges[balanceChanges.Count - 1];
                    result.Continuation = lastop.CreateBalanceLocator();
                }
            }
            return(result);
        }
Пример #4
0
        public BalanceSummary BalanceSummary(
            BalanceId balanceId,
            BlockFeature at,
            bool debug,
            bool colored,
            int?unconfExpiration)
        {
            var expiration = GetExpiration(unconfExpiration);
            var repo       = Configuration.CreateWalletRepository();
            CancellationTokenSource cancel = new CancellationTokenSource();

            cancel.CancelAfter(30000);
            var checkpoint = Configuration.Indexer.CreateIndexer()
                             .GetCheckpoint(balanceId.Type == BalanceType.Address ? IndexerCheckpoints.Balances : IndexerCheckpoints.Wallets);

            var atBlock = AtBlock(at);

            var query = new BalanceQuery();

            query.RawOrdering = true;
            query.From        = null;

            if (at != null)
            {
                query.From = ToBalanceLocator(atBlock);
            }
            if (query.From == null)
            {
                query.From = new UnconfirmedBalanceLocator(DateTimeOffset.UtcNow - expiration);
            }

            query.PageSizes = new[] { 1, 10, 100 };

            var cacheTable    = repo.GetBalanceSummaryCacheTable(balanceId, colored);
            var cachedSummary = cacheTable.Query(Chain, query).FirstOrDefault(c => (((ConfirmedBalanceLocator)c.Locator).BlockHash == atBlock.HashBlock && at != null) ||
                                                                              c.Immature.TransactionCount == 0 ||
                                                                              ((c.Immature.TransactionCount != 0) && !IsMature(c.OlderImmature, atBlock)));

            var cachedLocator = cachedSummary == null ? null : (ConfirmedBalanceLocator)cachedSummary.Locator;

            if (cachedSummary != null && at != null && cachedLocator.Height == atBlock.Height)
            {
                cachedSummary.CacheHit = CacheHit.FullCache;
                cachedSummary.PrepareForSend(at, debug);
                return(cachedSummary);
            }

            cachedSummary = cachedSummary ?? new BalanceSummary()
            {
                Confirmed     = new BalanceSummaryDetails(),
                UnConfirmed   = new BalanceSummaryDetails(),
                OlderImmature = int.MaxValue
            };

            int stopAtHeight = cachedSummary.Locator == null ? -1 : cachedLocator.Height;
            int lookback     = (int)(expiration.Ticks / this.Network.Consensus.PowTargetSpacing.Ticks);

            if (at == null)
            {
                stopAtHeight = stopAtHeight - lookback;
            }

            var client = Configuration.Indexer.CreateIndexerClient();

            client.ColoredBalance = colored;

            var diff =
                client
                .GetOrderedBalance(balanceId, query)
                .WhereNotExpired(expiration)
                .TakeWhile(_ => !cancel.IsCancellationRequested)
                //Some confirmation of the fetched unconfirmed may hide behind stopAtHeigh
                .TakeWhile(_ => _.BlockId == null || _.Height > stopAtHeight - lookback)
                .AsBalanceSheet(Chain);

            if (cancel.Token.IsCancellationRequested)
            {
                throw new HttpResponseException(new HttpResponseMessage()
                {
                    StatusCode   = HttpStatusCode.InternalServerError,
                    ReasonPhrase = "The server can't fetch the balance summary because the balance is too big. Please, load it in several step with ?at={blockFeature} parameter. Once fully loaded after all the step, the summary will return in constant time."
                });
            }
            RemoveBehind(diff, stopAtHeight);
            RemoveConflicts(diff);

            var unconfs = diff.Unconfirmed;
            var confs   = cachedLocator == null ?
                          diff.Confirmed :
                          diff.Confirmed.Where(c => c.Height > cachedLocator.Height).ToList();

            var immature = confs.Where(c => !IsMature(c, atBlock)).ToList();


            var summary = new BalanceSummary()
            {
                Confirmed   = BalanceSummaryDetails.CreateFrom(confs, Network, colored),
                Immature    = BalanceSummaryDetails.CreateFrom(immature, Network, colored),
                UnConfirmed = BalanceSummaryDetails.CreateFrom(unconfs, Network, colored),
            };

            summary.Confirmed += cachedSummary.Confirmed;
            summary.Immature  += cachedSummary.Immature;
            summary.Locator    = new ConfirmedBalanceLocator(atBlock.Height, atBlock.HashBlock);
            summary.CacheHit   = cachedSummary.Locator == null ? CacheHit.NoCache : CacheHit.PartialCache;

            var newCachedLocator = (ConfirmedBalanceLocator)summary.Locator;

            if (
                cachedSummary.Locator == null ||
                newCachedLocator.BlockHash != cachedLocator.BlockHash)
            {
                var olderImmature    = immature.Select(_ => _.Height).Concat(new[] { int.MaxValue }).Min();
                var newCachedSummary = new Models.BalanceSummary()
                {
                    Confirmed     = summary.Confirmed,
                    Immature      = summary.Immature,
                    Locator       = summary.Locator,
                    OlderImmature = Math.Min(cachedSummary.OlderImmature, olderImmature)
                };
                var checkpointBlock = Chain.GetBlock(checkpoint.BlockLocator.Blocks[0]);
                if (checkpointBlock != null && checkpointBlock.Height >= atBlock.Height)
                {
                    cacheTable.Create(newCachedLocator, newCachedSummary);
                }
            }

            summary.PrepareForSend(at, debug);
            return(summary);
        }
Пример #5
0
        BalanceModel Balance(BalanceId balanceId,
                             BalanceLocator continuation,
                             BlockFeature until,
                             BlockFeature from,
                             bool includeImmature,
                             bool unspentOnly,
                             bool colored)
        {
            CancellationTokenSource cancel = new CancellationTokenSource();

            cancel.CancelAfter(30000);

            BalanceQuery query = new BalanceQuery();

            query.From = null;

            if (from != null)
            {
                query.From         = ToBalanceLocator(from);
                query.FromIncluded = true;
            }

            if (continuation != null)
            {
                query = new BalanceQuery
                {
                    From         = continuation,
                    FromIncluded = false
                };
            }

            if (query.From == null)
            {
                query.From = new UnconfirmedBalanceLocator(DateTimeOffset.UtcNow - TimeSpan.FromHours(24.0));
            }

            if (until != null)
            {
                query.To           = ToBalanceLocator(until);
                query.FromIncluded = true;
            }

            if (query.To.IsGreaterThan(query.From))
            {
                throw InvalidParameters("Invalid agurment : from < until");
            }

            var client = Configuration.Indexer.CreateIndexerClient();

            client.ColoredBalance = colored;
            var balance =
                client
                .GetOrderedBalance(balanceId, query)
                .TakeWhile(_ => !cancel.IsCancellationRequested)
                .WhereNotExpired()
                .Where(o => includeImmature || IsMature(o, Chain.Tip))
                .AsBalanceSheet(Chain);

            var balanceChanges = balance.All;

            if (until != null && balance.Confirmed.Count != 0) //Strip unconfirmed that can appear after the last until
            {
                for (int i = balanceChanges.Count - 1; i >= 0; i--)
                {
                    var last = balanceChanges[i];
                    if (last.BlockId == null)
                    {
                        balanceChanges.RemoveAt(i);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            if (unspentOnly)
            {
                var changeByTxId   = balanceChanges.ToDictionary(_ => _.TransactionId);
                var spentOutpoints = changeByTxId.Values.SelectMany(b => b.SpentCoins.Select(c => c.Outpoint)).ToDictionary(_ => _);
                foreach (var change in changeByTxId.Values.ToArray())
                {
                    change.SpentCoins.Clear();
                    change.ReceivedCoins.RemoveAll(c => spentOutpoints.ContainsKey(c.Outpoint));
                }
            }

            var result = new BalanceModel(balanceChanges, Chain);

            if (cancel.IsCancellationRequested)
            {
                if (balanceChanges.Count > 0)
                {
                    var lastop = balanceChanges[balanceChanges.Count - 1];
                    result.Continuation = lastop.CreateBalanceLocator();
                }
            }
            return(result);
        }
Пример #6
0
        public void CanQueryBalanceRange()
        {
            using(var tester = CreateTester())
            {
                Key bob = new BitcoinSecret("L4JinGSmHxKJJrjbeFx3zxf9Vr3VD6jmq5wXpDm6ywUewcWoXEAy").PrivateKey;
                var chainBuilder = tester.CreateChainBuilder();
                chainBuilder.NoRandom = true;

                Dictionary<string, Transaction> txs = new Dictionary<string, Transaction>();
                txs.Add("tx1", chainBuilder.EmitMoney(bob, "1.0"));
                chainBuilder.SubmitBlock();

                txs.Add("tx21", chainBuilder.EmitMoney(bob, "2.1"));
                txs.Add("tx22", chainBuilder.EmitMoney(bob, "2.2"));
                chainBuilder.SubmitBlock();

                txs.Add("tx31", chainBuilder.EmitMoney(bob, "3.1"));
                txs.Add("tx32", chainBuilder.EmitMoney(bob, "3.2"));
                txs.Add("tx33", chainBuilder.EmitMoney(bob, "3.3"));
                chainBuilder.SubmitBlock();

                txs.Add("tx41", chainBuilder.EmitMoney(bob, "4.2"));
                txs.Add("tx42", chainBuilder.EmitMoney(bob, "4.3"));
                txs.Add("tx43", chainBuilder.EmitMoney(bob, "4.1"));
                chainBuilder.SubmitBlock();

                txs.Add("utx51", chainBuilder.EmitMoney(bob, "5.1", isCoinbase: false, indexBalance: true));
                Thread.Sleep(1000);
                txs.Add("utx52", chainBuilder.EmitMoney(bob, "5.2", isCoinbase: false, indexBalance: true));
                Thread.Sleep(1000);
                txs.Add("utx53", chainBuilder.EmitMoney(bob, "5.3", isCoinbase: false, indexBalance: true));

                chainBuilder.SyncIndexer();


                var tests = new String[][]
                {
                     new string[]{"2in", "4in", "tx43,tx42,tx41,tx33,tx32,tx31,tx22,tx21"},
                     new string[]{"4in", "2in", "tx43,tx42,tx41,tx33,tx32,tx31,tx22,tx21"}, //Does not care about order
                     new string[]{"2ex", "4in", "tx43,tx42,tx41,tx33,tx32,tx31"},
                     new string[]{"2in", "4ex", "tx33,tx32,tx31,tx22,tx21"},
                     new string[]{"2ex", "4ex", "tx33,tx32,tx31"},
                     new string[]{"{utx51}in", "{utx52}in", "utx52,utx51"},
                     new string[]{"{utx51}in", "{utx53}ex", "utx52,utx51"},
                     new string[]{"{utx51}ex", "{utx53}in", "utx53,utx52"},
                     new string[]{"{utx52}ex", "3in", "utx53,tx43,tx42,tx41,tx33,tx32,tx31"}
                };

                var all = tester.Client.GetOrderedBalance(bob).ToArray();
                Assert.Equal(all.Length, txs.Count);

                foreach(var test in tests)
                {
                    var data = test;
                    BalanceQuery query = new BalanceQuery();
                    query.From = Parse(data[0], all, txs);
                    query.FromIncluded = ParseIncl(data[0]);
                    query.To = Parse(data[1], all, txs);
                    query.ToIncluded = ParseIncl(data[1]);

                    var result = tester.Client.GetOrderedBalance(bob, query).ToArray();
                    var expected = data[2].Split(',').ToArray();

                    var expectedResult = String.Join(",", expected);
                    var actualResult = String.Join(",", result.Select(o => GetName(txs, o)));
                    Assert.Equal(expectedResult, actualResult);
                }
            }
        }
Пример #7
0
 public async Task <IEnumerable <Balance> > ListAsync(BalanceQuery query)
 {
     return(await _balanceRepository.ListAsync(query));
 }