Task <T> Request <T>(string key, Func <Task <T> > wrapped)
        {
            Task <T> task = null;

            using (@lock.LockRead())
            {
                task = _Tasks.TryGet(key) as Task <T>;
            }
            if (task != null)
            {
                return(task);
            }
            using (@lock.LockWrite())
            {
                task = _Tasks.TryGet(key) as Task <T>;
                if (task != null)
                {
                    return(task);
                }
                task = wrapped();
                _Tasks.Add(key, task);
            }
            task.ContinueWith((_) =>
            {
                using (@lock.LockWrite())
                {
                    _Tasks.Remove(key);
                }
            });
            return(task);
        }
        public override Task SaveChangesAsync(IEnumerable <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash)
        {
            Guard.NotNull(oldBlockHash, nameof(oldBlockHash));
            Guard.NotNull(nextBlockHash, nameof(nextBlockHash));
            Guard.NotNull(unspentOutputs, nameof(unspentOutputs));

            using (lockobj.LockWrite())
            {
                if (blockHash != null && oldBlockHash != blockHash)
                {
                    return(Task.FromException(new InvalidOperationException("Invalid oldBlockHash")));
                }
                blockHash = nextBlockHash;
                foreach (var unspent in unspentOutputs)
                {
                    UnspentOutputs existing;
                    if (unspents.TryGetValue(unspent.TransactionId, out existing))
                    {
                        existing.Spend(unspent);
                    }
                    else
                    {
                        existing = unspent.Clone();
                        unspents.Add(unspent.TransactionId, existing);
                    }
                    if (existing.IsPrunable)
                    {
                        unspents.Remove(unspent.TransactionId);
                    }
                }
            }
            return(Task.FromResult(true));
        }
Beispiel #3
0
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            ColoredTransaction result = null;
            bool found;

            using (@lock.LockRead())
            {
                found = _ColoredTransactions.TryGetValue(txId, out result);
            }
            if (!found)
            {
                result = await _Inner.GetAsync(txId).ConfigureAwait(false);

                using (@lock.LockWrite())
                {
                    _ColoredTransactions.AddOrReplace(txId, result);
                }
            }
            return(result);
        }
        public override async Task <uint256> Rewind()
        {
            if (innerBlockHash == null)
            {
                innerBlockHash = await inner.GetBlockHashAsync().ConfigureAwait(false);
            }

            Task <uint256> rewindinginner = null;

            using (this.lockobj.LockWrite())
            {
                WaitOngoingTasks();
                if (blockHash == innerBlockHash)
                {
                    this.unspents.Clear();
                }
                if (this.unspents.Count != 0)
                {
                    //More intelligent version can restore without throwing away the cache. (as the rewind data is in the cache)
                    this.unspents.Clear();
                    blockHash = innerBlockHash;
                    return(blockHash);
                }
                else
                {
                    rewindinginner = inner.Rewind();
                    this.rewinding = rewindinginner;
                }
            }
            var h = await rewindinginner.ConfigureAwait(false);

            using (lockobj.LockWrite())
            {
                innerBlockHash = h;
                blockHash      = h;
            }
            return(h);
        }
Beispiel #5
0
        public void Listen(ConcurrentChain chain = null)
        {
            ListenerTrace.Info($"Connecting to node {_Configuration.Indexer.Node}");
            var ip = Utils.ParseIpEndpoint(_Configuration.Indexer.Node, Configuration.Indexer.Network.DefaultPort);

            ListenerTrace.Info($"Connecting to node ip {ip.ToString()}");
            var node = Node.Connect(Configuration.Indexer.Network, ip);

            ListenerTrace.Info($"Connected, trying handshake...");
            node.VersionHandshake();
            ListenerTrace.Info($"Hanshaked");
            node.Disconnect();

            _Chain   = new ConcurrentChain(_Configuration.Indexer.Network);
            _Indexer = Configuration.Indexer.CreateIndexer();
            if (chain == null)
            {
                chain = new ConcurrentChain(_Configuration.Indexer.Network);
            }
            _Chain = chain;
            ListenerTrace.Info("Fetching headers from " + _Chain.Tip.Height + " (from azure)");
            var client = Configuration.Indexer.CreateIndexerClient();

            client.SynchronizeChain(chain);
            ListenerTrace.Info("Headers fetched tip " + _Chain.Tip.Height);

            _Disposables.Add(_IndexerScheduler = new CustomThreadPoolTaskScheduler(50, 100, "Indexing Threads"));
            _Indexer.TaskScheduler             = _IndexerScheduler;

            _Group = new NodesGroup(Configuration.Indexer.Network);
            _Disposables.Add(_Group);
            _Group.AllowSameGroup        = true;
            _Group.MaximumNodeConnection = 2;
            AddressManager addrman = new AddressManager();

            addrman.Add(new NetworkAddress(ip), IPAddress.Parse("127.0.0.1"));

            _Group.NodeConnectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addrman)
            {
                Mode = AddressManagerBehaviorMode.None
            });
            _Group.NodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(_Chain)
            {
                SkipPoWCheck = true
            });
            _Group.NodeConnectionParameters.TemplateBehaviors.Add(new Behavior(this));



            ListenerTrace.Info("Fetching wallet rules...");
            _Wallets = _Configuration.Indexer.CreateIndexerClient().GetAllWalletRules();
            ListenerTrace.Info("Wallet rules fetched");

            ListenerTrace.Info("Fetching wallet subscriptions...");
            _Subscriptions = new SubscriptionCollection(_Configuration.GetSubscriptionsTable().Read());
            ListenerTrace.Info("Subscriptions fetched");
            _Group.Connect();

            ListenerTrace.Info("Fetching transactions to broadcast...");

            _Disposables.Add(
                Configuration
                .Topics
                .BroadcastedTransactions
                .CreateConsumer("listener", true)
                .EnsureSubscriptionExists()
                .OnMessage((tx, ctl) =>
            {
                uint256 hash = null;
                var repo     = Configuration.Indexer.CreateIndexerClient();
                var rejects  = Configuration.GetRejectTable();
                try
                {
                    hash          = tx.Transaction.GetHash();
                    var indexedTx = repo.GetTransaction(hash);
                    ListenerTrace.Info("Broadcasting " + hash);
                    var reject = rejects.ReadOne(hash.ToString());
                    if (reject != null)
                    {
                        ListenerTrace.Info("Abort broadcasting of rejected");
                        return;
                    }

                    if (_Broadcasting.Count > 1000)
                    {
                        _Broadcasting.Clear();
                    }

                    _Broadcasting.TryAdd(hash, tx.Transaction);
                    if (indexedTx == null || !indexedTx.BlockIds.Any(id => Chain.Contains(id)))
                    {
                        var unused = SendMessageAsync(tx.Transaction);
                    }
                    var reschedule = new[]
                    {
                        TimeSpan.FromMinutes(5),
                        TimeSpan.FromMinutes(10),
                        TimeSpan.FromHours(1),
                        TimeSpan.FromHours(6),
                        TimeSpan.FromHours(24),
                    };
                    if (tx.Tried <= reschedule.Length - 1)
                    {
                        ctl.RescheduleIn(reschedule[tx.Tried]);
                        tx.Tried++;
                    }
                }
                catch (Exception ex)
                {
                    if (!_Disposed)
                    {
                        LastException = ex;
                        ListenerTrace.Error("Error for new broadcasted transaction " + hash, ex);
                        throw;
                    }
                }
            }));
            ListenerTrace.Info("Transactions to broadcast fetched");

            _Disposables.Add(_Configuration
                             .Topics
                             .SubscriptionChanges
                             .EnsureSubscriptionExists()
                             .AddUnhandledExceptionHandler(ExceptionOnMessagePump)
                             .OnMessage(c =>
            {
                using (_SubscriptionSlimLock.LockWrite())
                {
                    if (c.Added)
                    {
                        _Subscriptions.Add(c.Subscription);
                    }
                    else
                    {
                        _Subscriptions.Remove(c.Subscription.Id);
                    }
                }
            }));

            _Disposables.Add(_Configuration
                             .Topics
                             .SendNotifications
                             .AddUnhandledExceptionHandler(ExceptionOnMessagePump)
                             .OnMessageAsync((n, act) =>
            {
                return(SendAsync(n, act).ContinueWith(t =>
                {
                    if (!_Disposed)
                    {
                        if (t.Exception != null)
                        {
                            LastException = t.Exception;
                        }
                    }
                }));
            }, new OnMessageOptions()
            {
                MaxConcurrentCalls = 1000,
                AutoComplete       = true,
                AutoRenewTimeout   = TimeSpan.Zero
            }));

            _Disposables.Add(Configuration
                             .Topics
                             .AddedAddresses
                             .CreateConsumer("updater", true)
                             .EnsureSubscriptionExists()
                             .AddUnhandledExceptionHandler(ExceptionOnMessagePump)
                             .OnMessage(evt =>
            {
                if (evt == null)
                {
                    return;
                }
                ListenerTrace.Info("New wallet rule");
                using (_WalletsSlimLock.LockWrite())
                {
                    foreach (var address in evt)
                    {
                        _Wallets.Add(address.CreateWalletRuleEntry());
                    }
                }
            }));
        }
        public override async Task <FetchCoinsResponse> FetchCoinsAsync(uint256[] txIds)
        {
            Guard.NotNull(txIds, nameof(txIds));

            FetchCoinsResponse result         = null;
            uint256            innerBlockHash = null;

            UnspentOutputs[] outputs     = new UnspentOutputs[txIds.Length];
            List <int>       miss        = new List <int>();
            List <uint256>   missedTxIds = new List <uint256>();

            using (_Lock.LockRead())
            {
                WaitOngoingTasks();
                for (int i = 0; i < txIds.Length; i++)
                {
                    CacheItem cache;
                    if (!_Unspents.TryGetValue(txIds[i], out cache))
                    {
                        miss.Add(i);
                        missedTxIds.Add(txIds[i]);
                    }
                    else
                    {
                        outputs[i] = cache.UnspentOutputs == null ? null :
                                     cache.UnspentOutputs.IsPrunable ? null :
                                     cache.UnspentOutputs.Clone();
                    }
                }
                PerformanceCounter.AddMissCount(miss.Count);
                PerformanceCounter.AddHitCount(txIds.Length - miss.Count);
            }
            var fetchedCoins = await Inner.FetchCoinsAsync(missedTxIds.ToArray()).ConfigureAwait(false);

            using (_Lock.LockWrite())
            {
                _Flushing.Wait();
                innerBlockHash = fetchedCoins.BlockHash;
                if (_BlockHash == null)
                {
                    Debug.Assert(_Unspents.Count == 0);
                    _InnerBlockHash = innerBlockHash;
                    _BlockHash      = innerBlockHash;
                }
                for (int i = 0; i < miss.Count; i++)
                {
                    var index   = miss[i];
                    var unspent = fetchedCoins.UnspentOutputs[i];
                    outputs[index] = unspent;
                    CacheItem cache = new CacheItem();
                    cache.ExistInInner    = unspent != null;
                    cache.IsDirty         = false;
                    cache.UnspentOutputs  = unspent;
                    cache.OriginalOutputs = unspent?._Outputs.ToArray();
                    _Unspents.TryAdd(txIds[index], cache);
                }
                result = new FetchCoinsResponse(outputs, _BlockHash);
            }

            if (CacheEntryCount > MaxItems)
            {
                Evict();
                if (CacheEntryCount > MaxItems)
                {
                    await FlushAsync().ConfigureAwait(false);

                    Evict();
                }
            }

            return(result);
        }