Esempio n. 1
0
        public void IndexOrderedBalance(Transaction tx)
        {
            var table    = Configuration.GetBalanceTable();
            var entities = OrderedBalanceChange.ExtractScriptBalances(tx).Select(t => t.ToEntity()).AsEnumerable();

            Index(entities, table);
        }
Esempio n. 2
0
 public void IndexOrderedBalances(ChainBase chain)
 {
     IndexBalances(chain, "balances", (txid, tx, blockid, header, height) =>
     {
         return(OrderedBalanceChange.ExtractScriptBalances(txid, tx, blockid, header, height));
     });
 }
        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);
        }
Esempio n. 4
0
 private bool ShouldReplace(OrderedBalanceChange annotatedTransaction, OrderedBalanceChange conflicted)
 {
     if (annotatedTransaction.BlockId == null)
     {
         if (conflicted.BlockId == null)
         {
             return(annotatedTransaction.SeenUtc > conflicted.SeenUtc); // The is a replaced tx, we want the youngest
         }
         else
         {
             return(false);
         }
     }
     else
     {
         if (conflicted.BlockId == null)
         {
             return(true);
         }
         else
         {
             return(annotatedTransaction.Height < conflicted.Height); // The most buried block win (should never happen though)
         }
     }
 }
Esempio n. 5
0
 public ColoredBalanceChangeEntry(OrderedBalanceChange balanceChange, ColoredTransaction coloredTransaction)
 {
     _Colored = coloredTransaction;
     for (var i = 0; i < balanceChange.SpentIndices.Count; i++)
     {
         var spentIndex = balanceChange.SpentIndices[i];
         var entry      = coloredTransaction.Inputs.FirstOrDefault(o => o.Index == (uint)spentIndex);
         if (entry != null)
         {
             AddSpentCoin(entry.Asset, balanceChange.SpentCoins[(int)i]);
         }
         else
         {
             AddSpentCoin(null, balanceChange.SpentCoins[(int)i]);
         }
     }
     foreach (var coin in balanceChange.ReceivedCoins)
     {
         var entry = coloredTransaction.GetColoredEntry(coin.Outpoint.N);
         if (entry != null)
         {
             AddReceivedCoin(entry.Asset, coin, !coloredTransaction.Issuances.Contains(entry));
         }
         else
         {
             AddReceivedCoin(null, coin, false);
         }
     }
 }
Esempio n. 6
0
        public Task IndexOrderedBalanceAsync(Transaction tx)
        {
            var table    = Configuration.GetBalanceTable();
            var entities = OrderedBalanceChange.ExtractScriptBalances(tx).Select(t => t.ToEntity()).AsEnumerable();

            return(IndexAsync(entities, table));
        }
 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;
 }
Esempio n. 8
0
        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);

            if (change.SpentCoins == null)
            {
                var success = await change.EnsureSpentCoinsLoadedAsync(parentIds, parents.Select(t => t == null ? null : t.Transaction).ToArray()).ConfigureAwait(false);

                if (!success)
                {
                    return(false);
                }
            }
            if (ColoredBalance && change.ColoredTransaction == null)
            {
                var success = await change.EnsureColoredTransactionLoadedAsync(new IndexerColoredTransactionRepository(Configuration)).ConfigureAwait(false);

                if (!success)
                {
                    return(false);
                }
            }
            var entity = change.ToEntity();
            await Configuration.GetBalanceTable().ExecuteAsync(TableOperation.Merge(entity)).ConfigureAwait(false);

            return(true);
        }
Esempio n. 9
0
        public void CleanUnconfirmedChanges(Script scriptPubKey, TimeSpan olderThan)
        {
            var table = Configuration.GetBalanceTable();
            List <DynamicTableEntity> unconfirmed = new List <DynamicTableEntity>();

            foreach (var c in table.ExecuteQueryAsync(new BalanceQuery().CreateTableQuery(new BalanceId(scriptPubKey))).GetAwaiter().GetResult())
            {
                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();
            });
        }
Esempio n. 10
0
        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(ConsensusFactory);

            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);
        }
Esempio n. 11
0
        public void IndexOrderedBalance(Transaction tx)
        {
            var table = Configuration.GetBalanceTable();

            foreach (var group in OrderedBalanceChange.ExtractScriptBalances(tx).GroupBy(c => c.BalanceId, c => c.ToEntity()))
            {
                Index(group, table);
            }
        }
Esempio n. 12
0
        public async Task <bool> EnsurePreviousLoadedAsync(OrderedBalanceChange change)
        {
            if (!NeedLoading(change))
            {
                return(true);
            }
            var transactions =
                await GetTransactionsAsync(false, ColoredBalance, change.SpentOutpoints.Select(s => s.Hash).ToArray()).ConfigureAwait(false);

            CoinCollection result = new CoinCollection();

            for (int i = 0; i < transactions.Length; i++)
            {
                var outpoint = change.SpentOutpoints[i];
                if (outpoint.IsNull)
                {
                    continue;
                }
                var prev = transactions[i];
                if (prev == null)
                {
                    return(false);
                }
                if (ColoredBalance && prev.ColoredTransaction == null)
                {
                    return(false);
                }
                result.Add(new Coin(outpoint, prev.Transaction.Outputs[change.SpentOutpoints[i].N]));
            }
            change.SpentCoins = result;


            if (ColoredBalance && change.ColoredBalanceChangeEntry == null)
            {
                var thisTransaction = await GetTransactionAsync(false, ColoredBalance, change.TransactionId).ConfigureAwait(false);

                if (thisTransaction.ColoredTransaction == null)
                {
                    return(false);
                }
                change.ColoredBalanceChangeEntry = new ColoredBalanceChangeEntry(change, thisTransaction.ColoredTransaction);
            }

            var entity     = change.ToEntity();
            var spentCoins = Helper.GetEntityProperty(entity, "b");
            var coloredTx  = ColoredBalance ? entity.Properties["g"].BinaryValue : null;

            entity.Properties.Clear();
            if (coloredTx != null)
            {
                entity.Properties.Add("g", new EntityProperty(coloredTx));
            }
            Helper.SetEntityProperty(entity, "b", spentCoins);
            Configuration.GetBalanceTable().Execute(TableOperation.Merge(entity));
            change.AddRedeemInfo();
            return(true);
        }
Esempio n. 13
0
        public void IndexWalletBalances(ChainBase chain)
        {
            Configuration.GetWalletBalanceTable().CreateIfNotExists();
            Configuration.GetWalletRulesTable().CreateIfNotExists();
            var walletRules = Configuration.CreateIndexerClient().GetAllWalletRules();

            IndexBalances(chain, "wallets", (txid, tx, blockid, header, height) =>
            {
                return(OrderedBalanceChange.ExtractWalletBalances(txid, tx, blockid, header, height, walletRules));
            });
        }
Esempio n. 14
0
 public bool NeedLoading(OrderedBalanceChange change)
 {
     if (change.SpentCoins != null)
     {
         if (change.ColoredTransaction != null || !ColoredBalance)
         {
             return(false);
         }
     }
     return(true);
 }
Esempio n. 15
0
 private bool NeedLoading(OrderedBalanceChange change)
 {
     if (change.SpentCoins != null)
     {
         if (change.ColoredBalanceChangeEntry != null || !ColoredBalance)
         {
             change.AddRedeemInfo();
             return(false);
         }
     }
     return(true);
 }
Esempio n. 16
0
        public Task IndexWalletOrderedBalanceAsync(int height, Block block, WalletRuleEntryCollection walletRules)
        {
            var table   = 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(ConsensusFactory))
                .AsEnumerable();

            return(IndexAsync(entities, table));
        }
Esempio n. 17
0
        public void IndexOrderedBalance(int height, Block block)
        {
            var table   = 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(ConsensusFactory))
                .AsEnumerable();

            Index(entities, table);
        }
Esempio n. 18
0
        public void IndexWalletOrderedBalance(int height, Block block, WalletRuleEntryCollection walletRules)
        {
            var table   = Configuration.GetBalanceTable();
            var blockId = block.GetHash();

            foreach (var transaction in block.Transactions)
            {
                var txId    = transaction.GetHash();
                var changes = OrderedBalanceChange.ExtractWalletBalances(txId, transaction, blockId, block.Header, height, walletRules).Select(c => c.ToEntity());
                foreach (var group in changes.GroupBy(c => c.PartitionKey))
                {
                    Index(group, table);
                }
            }
        }
Esempio n. 19
0
        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);
        }
Esempio n. 20
0
        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
                        });
                    }
                }
            }
        }
Esempio n. 21
0
 private bool Prepare(OrderedBalanceChange change)
 {
     change.UpdateToScriptCoins();
     if (change.SpentCoins == null && ExcludeIncompleteBalanceChange)
     {
         return(false);
     }
     if (ColoredBalance)
     {
         if (change.ColoredTransaction == null)
         {
             return(false);
         }
         change.UpdateToColoredCoins();
     }
     return(true);
 }
Esempio n. 22
0
        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);
        }
Esempio n. 23
0
        public void IndexOrderedBalance(int height, Block block)
        {
            var table   = Configuration.GetBalanceTable();
            var blockId = block.GetHash();

            foreach (var group in
                     block
                     .Transactions
                     .SelectMany(t => OrderedBalanceChange.ExtractScriptBalances(t.GetHash(), t, blockId, block.Header, height))
                     .Select(_ => _.ToEntity())
                     .GroupBy(c => c.PartitionKey)
                     )
            {
                foreach (var batch in group.Partition(100))
                {
                    Index(batch, table);
                }
            }
        }
Esempio n. 24
0
 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);
 }
 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)
 {
 }
Esempio n. 27
0
 public ConfirmedBalanceLocator(OrderedBalanceChange change)
     : this(change.Height, change.BlockId, change.TransactionId)
 {
 }
        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;
        }
        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
                        });
                    }
            }
        }
 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;
 }
Esempio n. 31
0
        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);
        }
Esempio n. 32
0
 private string GetKey(OrderedBalanceChange change)
 {
     return(change.Height + "-" + (change.BlockId == null ? new uint256(0) : change.BlockId) + "-" + change.TransactionId + "-" + change.SeenUtc.Ticks);
 }
 public bool NeedLoading(OrderedBalanceChange change)
 {
     if(change.SpentCoins != null)
     {
         if(change.ColoredTransaction != null || !ColoredBalance)
         {
             return false;
         }
     }
     return true;
 }
        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.Execute(TableOperation.Delete(c));
            });
        }
 private string GetKey(OrderedBalanceChange change)
 {
     return($"{change.Height}-{(change.BlockId == null ? new uint256(0) : change.BlockId)}-{change.TransactionId}-{change.SeenUtc.Ticks}");
 }
        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 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;
 }
 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;
        }