public void CleanUnconfirmedChanges(Script scriptPubKey, TimeSpan olderThan) { var table = Configuration.GetBalanceTable(); List <DynamicTableEntity> unconfirmed = new List <DynamicTableEntity>(); foreach (var c in table.ExecuteQuery(new BalanceQuery().CreateTableQuery(new BalanceId(scriptPubKey)))) { var change = new OrderedBalanceChange(c); if (change.BlockId != null) { break; } if (DateTime.UtcNow - change.SeenUtc < olderThan) { continue; } unconfirmed.Add(c); } Parallel.ForEach(unconfirmed, c => { var t = Configuration.GetBalanceTable(); c.ETag = "*"; t.ExecuteAsync(TableOperation.Delete(c)).GetAwaiter().GetResult(); }); }
internal OrderedBalanceChange(string walletId, OrderedBalanceChange source) : this(source.TransactionId, walletId, source.ScriptPubKey, source.BlockId, null, source.Height) { SeenUtc = source.SeenUtc; IsCoinbase = source.IsCoinbase; HasOpReturn = source.HasOpReturn; }
public static IEnumerable <OrderedBalanceChange> ExtractWalletBalances( uint256 txId, Transaction tx, uint256 blockId, BlockHeader blockHeader, int height, WalletRuleEntryCollection walletCollection) { Dictionary <string, OrderedBalanceChange> entitiesByWallet = new Dictionary <string, OrderedBalanceChange>(); var scriptBalances = ExtractScriptBalances(txId, tx, blockId, blockHeader, height); foreach (var scriptBalance in scriptBalances) { foreach (var walletRuleEntry in walletCollection.GetRulesFor(scriptBalance.ScriptPubKey)) { OrderedBalanceChange walletEntity = null; if (!entitiesByWallet.TryGetValue(walletRuleEntry.WalletId, out walletEntity)) { walletEntity = new OrderedBalanceChange(walletRuleEntry.WalletId, scriptBalance); entitiesByWallet.Add(walletRuleEntry.WalletId, walletEntity); } walletEntity.Merge(scriptBalance, walletRuleEntry.Rule); } } foreach (var b in entitiesByWallet.Values) { b.UpdateToScriptCoins(); } return(entitiesByWallet.Values); }
public Task IndexOrderedBalanceAsync(Transaction tx) { var table = this.Configuration.GetBalanceTable(); var entities = OrderedBalanceChange.ExtractScriptBalances(tx).Select(t => t.ToEntity()).AsEnumerable(); return(this.IndexAsync(entities, table)); }
public void IndexOrderedBalance(Transaction tx) { var table = this.Configuration.GetBalanceTable(); var entities = OrderedBalanceChange.ExtractScriptBalances(tx).Select(t => t.ToEntity()).AsEnumerable(); this.Index(entities, table); }
public async Task <bool> EnsurePreviousLoadedAsync(OrderedBalanceChange change) { if (!NeedLoading(change)) { return(true); } var parentIds = change.SpentOutpoints.Select(s => s.Hash).ToArray(); var parents = await GetTransactionsAsync(false, ColoredBalance, parentIds).ConfigureAwait(false); var cache = new NoSqlTransactionRepository(); foreach (var parent in parents.Where(p => p != null)) { cache.Put(parent.TransactionId, parent.Transaction); } if (change.SpentCoins == null) { var success = await change.EnsureSpentCoinsLoadedAsync(cache).ConfigureAwait(false); if (!success) { return(false); } } if (ColoredBalance && change.ColoredTransaction == null) { var indexerRepo = new IndexerColoredTransactionRepository(Configuration); indexerRepo.Transactions = new CompositeTransactionRepository(new[] { new ReadOnlyTransactionRepository(cache), indexerRepo.Transactions }); var success = await change.EnsureColoredTransactionLoadedAsync(indexerRepo).ConfigureAwait(false); if (!success) { return(false); } } var entity = change.ToEntity(); if (!change.IsEmpty) { await Configuration.GetBalanceTable().ExecuteAsync(TableOperation.Merge(entity)).ConfigureAwait(false); } else { try { await Configuration.GetTransactionTable().ExecuteAsync(TableOperation.Delete(entity)).ConfigureAwait(false); } catch (StorageException ex) { if (ex.RequestInformation == null || ex.RequestInformation.HttpStatusCode != 404) { throw; } } } return(true); }
public bool NeedLoading(OrderedBalanceChange change) { if (change.SpentCoins != null) { if (change.ColoredTransaction != null || !ColoredBalance) { return(false); } } return(true); }
public Task IndexWalletOrderedBalanceAsync(int height, Block block, WalletRuleEntryCollection walletRules) { var table = this.Configuration.GetBalanceTable(); var blockId = block == null ? null : block.GetHash(); var entities = block .Transactions .SelectMany(t => OrderedBalanceChange.ExtractWalletBalances(null, t, blockId, block.Header, height, walletRules)) .Select(t => t.ToEntity()) .AsEnumerable(); return(this.IndexAsync(entities, table)); }
private static bool IsMinConf(OrderedBalanceChange e, int minConfirmation, ChainBase chain) { if (e.BlockId == null) { return(minConfirmation == 0); } var b = chain.GetBlock(e.BlockId); if (b == null) { return(false); } return((chain.Height - b.Height) + 1 >= minConfirmation); }
public void IndexOrderedBalance(int height, Block block) { var table = this.Configuration.GetBalanceTable(); var blockId = block == null ? null : block.GetHash(); var header = block == null ? null : block.Header; var entities = block .Transactions .SelectMany(t => OrderedBalanceChange.ExtractScriptBalances(t.GetHash(), t, blockId, header, height)) .Select(_ => _.ToEntity()) .AsEnumerable(); this.Index(entities, table); }
internal void Merge(OrderedBalanceChange other, WalletRule walletRule) { if (other.ReceivedCoins.Count != 0) { ReceivedCoins.AddRange(other.ReceivedCoins); ReceivedCoins = new CoinCollection(ReceivedCoins.Distinct <ICoin, OutPoint>(c => c.Outpoint)); if (walletRule != null) { foreach (var c in other.ReceivedCoins) { this.MatchedRules.Add(new MatchedRule() { Index = c.Outpoint.N, Rule = walletRule, MatchType = MatchLocation.Output }); } } } if (other.SpentIndices.Count != 0) { SpentIndices.AddRange(other.SpentIndices); SpentIndices = SpentIndices.Distinct().ToList(); SpentOutpoints.AddRange(other.SpentOutpoints); SpentOutpoints = SpentOutpoints.Distinct().ToList(); //Remove cached value, no longer correct UpdateToUncoloredCoins(); SpentCoins = null; if (walletRule != null) { foreach (var c in other.SpentIndices) { this.MatchedRules.Add(new MatchedRule() { Index = c, Rule = walletRule, MatchType = MatchLocation.Input }); } } } }
private bool MergeIntoWalletCore(string walletId, BalanceId balanceId, WalletRule rule, CancellationToken cancel) { var indexer = Configuration.CreateIndexer(); var query = new BalanceQuery() { From = new UnconfirmedBalanceLocator().Floor(), RawOrdering = true }; var sourcesByKey = GetOrderedBalanceCore(balanceId, query, cancel) .ToDictionary(i => GetKey(i)); if (sourcesByKey.Count == 0) { return(false); } var destByKey = GetOrderedBalance(walletId, query, cancel) .ToDictionary(i => GetKey(i)); List <OrderedBalanceChange> entities = new List <OrderedBalanceChange>(); foreach (var kv in sourcesByKey) { var source = kv.Value; var existing = destByKey.TryGet(kv.Key); if (existing == null) { existing = new OrderedBalanceChange(walletId, source); } existing.Merge(kv.Value, rule); entities.Add(existing); if (entities.Count == 100) { indexer.Index(entities); } } if (entities.Count != 0) { indexer.Index(entities); } return(true); }
private bool Prepare(OrderedBalanceChange change) { change.UpdateToScriptCoins(); if (change.SpentCoins == null || change.ReceivedCoins == null) { return(false); } if (change.IsEmpty) { return(false); } if (ColoredBalance) { if (change.ColoredTransaction == null) { return(false); } change.UpdateToColoredCoins(); } return(true); }
public ConfirmedBalanceLocator(OrderedBalanceChange change) : this(change.Height, change.BlockId, change.TransactionId) { }
private string GetKey(OrderedBalanceChange change) { return(change.Height + "-" + (change.BlockId == null ? new uint256(0) : change.BlockId) + "-" + change.TransactionId + "-" + change.SeenUtc.Ticks); }
public static IEnumerable <OrderedBalanceChange> ExtractScriptBalances(uint256 txId, Transaction transaction, uint256 blockId, BlockHeader blockHeader, int height) { if (transaction == null) { throw new ArgumentNullException("transaction"); } if (txId == null) { txId = transaction.GetHash(); } if (blockId == null && blockHeader != null) { blockId = blockHeader.GetHash(); } Dictionary <Script, OrderedBalanceChange> changeByScriptPubKey = new Dictionary <Script, OrderedBalanceChange>(); uint i = 0; foreach (var input in transaction.Inputs) { if (transaction.IsCoinBase) { i++; break; } TxDestination signer = null; if (input.ScriptSig.Length != 0) { signer = input.ScriptSig.GetSigner(); } else { signer = GetSigner(input.WitScript); } if (signer != null) { OrderedBalanceChange entry = null; if (!changeByScriptPubKey.TryGetValue(signer.ScriptPubKey, out entry)) { entry = new OrderedBalanceChange(txId, signer.ScriptPubKey, blockId, blockHeader, height); changeByScriptPubKey.Add(signer.ScriptPubKey, entry); } entry.SpentOutpoints.Add(input.PrevOut); entry.SpentIndices.Add(i); } i++; } i = 0; bool hasOpReturn = false; foreach (var output in transaction.Outputs) { var bytes = output.ScriptPubKey.ToBytes(true); if (bytes.Length != 0 && bytes[0] == (byte)OpcodeType.OP_RETURN) { hasOpReturn = true; i++; continue; } OrderedBalanceChange entry = null; if (!changeByScriptPubKey.TryGetValue(output.ScriptPubKey, out entry)) { entry = new OrderedBalanceChange(txId, output.ScriptPubKey, blockId, blockHeader, height); changeByScriptPubKey.Add(output.ScriptPubKey, entry); } entry.ReceivedCoins.Add(new Coin() { Outpoint = new OutPoint(txId, i), TxOut = output }); i++; } foreach (var entity in changeByScriptPubKey) { entity.Value.HasOpReturn = hasOpReturn; entity.Value.IsCoinbase = transaction.IsCoinBase; } return(changeByScriptPubKey.Values); }