예제 #1
0
        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();
            });
        }
예제 #2
0
 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;
 }
예제 #3
0
        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);
        }
예제 #4
0
        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));
        }
예제 #5
0
        public void IndexOrderedBalance(Transaction tx)
        {
            var table    = this.Configuration.GetBalanceTable();
            var entities = OrderedBalanceChange.ExtractScriptBalances(tx).Select(t => t.ToEntity()).AsEnumerable();

            this.Index(entities, table);
        }
예제 #6
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();

            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);
        }
예제 #7
0
 public bool NeedLoading(OrderedBalanceChange change)
 {
     if (change.SpentCoins != null)
     {
         if (change.ColoredTransaction != null || !ColoredBalance)
         {
             return(false);
         }
     }
     return(true);
 }
예제 #8
0
        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));
        }
예제 #9
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);
        }
예제 #10
0
        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);
        }
예제 #11
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
                        });
                    }
                }
            }
        }
예제 #12
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);
        }
예제 #13
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);
 }
예제 #14
0
 public ConfirmedBalanceLocator(OrderedBalanceChange change)
     : this(change.Height, change.BlockId, change.TransactionId)
 {
 }
예제 #15
0
 private string GetKey(OrderedBalanceChange change)
 {
     return(change.Height + "-" + (change.BlockId == null ? new uint256(0) : change.BlockId) + "-" + change.TransactionId + "-" + change.SeenUtc.Ticks);
 }
예제 #16
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);
        }