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(_)))); }
/// 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()); }
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); }
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); }
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); }
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); } } }
public async Task <IEnumerable <Balance> > ListAsync(BalanceQuery query) { return(await _balanceRepository.ListAsync(query)); }