Ejemplo n.º 1
0
 public async Task StopAsync()
 {
     await(BlockchainProcessor?.StopAsync() ?? Task.CompletedTask);
     await(BlockProducer?.StopAsync() ?? Task.CompletedTask);
     await(PeerPool?.StopAsync() ?? Task.CompletedTask);
     await(Synchronizer?.StopAsync() ?? Task.CompletedTask);
     Logger?.Flush();
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Downloads blocks from <paramref name="peers"/> in parallel,
        /// using the given <paramref name="blockFetcher"/> function.
        /// </summary>
        /// <param name="peers">A list of peers to download blocks.</param>
        /// <param name="blockFetcher">A function to take demands and a peer, and then
        /// download corresponding blocks.</param>
        /// <param name="singleSessionTimeout">A maximum time to wait each single call of
        /// <paramref name="blockFetcher"/>.  If a call is timed out unsatisfied demands
        /// are automatically retried to fetch from other peers.</param>
        /// <param name="cancellationToken">A cancellation token to observe while waiting
        /// for the task to complete.</param>
        /// <returns>An async enumerable that yields pairs of a fetched block and its source
        /// peer.  It terminates when all demands are satisfied.</returns>
        public async IAsyncEnumerable <Tuple <Block <TAction>, TPeer> > Complete(
            IReadOnlyList <TPeer> peers,
            BlockFetcher blockFetcher,
            TimeSpan singleSessionTimeout,
            [EnumeratorCancellation] CancellationToken cancellationToken = default
            )
        {
            if (!peers.Any())
            {
                throw new ArgumentException("The list of peers must not be empty.", nameof(peers));
            }

            var pool       = new PeerPool(peers);
            var queue      = new AsyncProducerConsumerQueue <Tuple <Block <TAction>, TPeer> >();
            var completion =
                new ConcurrentDictionary <HashDigest <SHA256>, bool>(_satisfiedBlocks);

            await foreach (var hashes in EnumerateChunks(cancellationToken))
            {
                cancellationToken.ThrowIfCancellationRequested();
                IList <HashDigest <SHA256> > hashDigests =
                    hashes is IList <HashDigest <SHA256> > l ? l : hashes.ToList();

                foreach (HashDigest <SHA256> hash in hashDigests)
                {
                    completion.TryAdd(hash, false);
                }

                cancellationToken.ThrowIfCancellationRequested();
                await pool.SpawnAsync(
                    async (peer, ct) =>
                {
                    ct.ThrowIfCancellationRequested();
                    var demands = new HashSet <HashDigest <SHA256> >(hashDigests);
                    try
                    {
                        _logger.Debug(
                            "Request blocks {BlockHashes} to {Peer}...",
                            hashDigests,
                            peer
                            );
                        var timeout = new CancellationTokenSource(singleSessionTimeout);
                        CancellationToken timeoutToken = timeout.Token;
                        timeoutToken.Register(() =>
                                              _logger.Debug("Timed out to wait a response from {Peer}.", peer)
                                              );
                        ct.Register(() => timeout.Cancel());

                        try
                        {
                            ConfiguredCancelableAsyncEnumerable <Block <TAction> > blocks =
                                blockFetcher(peer, hashDigests, timeoutToken)
                                .WithCancellation(timeoutToken);
                            await foreach (Block <TAction> block in blocks)
                            {
                                _logger.Debug(
                                    "Downloaded a block #{BlockIndex} {BlockHash} " +
                                    "from {Peer}.",
                                    block.Index,
                                    block.Hash,
                                    peer
                                    );

                                if (Satisfy(block))
                                {
                                    await queue.EnqueueAsync(
                                        Tuple.Create(block, peer),
                                        cancellationToken
                                        );
                                }

                                demands.Remove(block.Hash);
                            }
                        }
                        catch (OperationCanceledException e)
                        {
                            if (ct.IsCancellationRequested)
                            {
                                _logger.Error(
                                    e,
                                    "A blockFetcher job (peer: {Peer}) is cancelled.",
                                    peer
                                    );
                                throw;
                            }

                            _logger.Debug(
                                e,
                                "Timed out to wait a response from {Peer}.",
                                peer
                                );
                        }
                    }
                    finally
                    {
                        if (demands.Any())
                        {
                            _logger.Verbose(
                                "Fetched blocks from {Peer}, but there are still " +
                                "unsatisfied demands ({UnsatisfiedDemandsNumber}) so " +
                                "enqueue them again: {UnsatisfiedDemands}.",
                                peer,
                                demands.Count,
                                demands
                                );
                            Demand(demands, retry: true);
                        }
                        else
                        {
                            _logger.Verbose("Fetched blocks from {Peer}.", peer);
                        }
                    }
                },
                    cancellationToken : cancellationToken
                    );
            }

            while (!completion.All(kv => kv.Value))
            {
                Tuple <Block <TAction>, TPeer> pair;
                try
                {
                    pair = await queue.DequeueAsync(cancellationToken);
                }
                catch (InvalidOperationException)
                {
                    break;
                }

                yield return(pair);

                _logger.Verbose(
                    "Completed a block {BlockIndex} {BlockHash} from {Peer}.",
                    pair.Item1.Index,
                    pair.Item1.Hash,
                    pair.Item2
                    );
                completion[pair.Item1.Hash] = true;
            }

            _logger.Verbose("Completed all blocks ({Number}).", completion.Count);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Downloads blocks from <paramref name="peers"/> in parallel,
        /// using the given <paramref name="blockFetcher"/> function.
        /// </summary>
        /// <param name="peers">A list of peers to download blocks.</param>
        /// <param name="blockFetcher">A function to take demands and a peer, and then
        /// download corresponding blocks.</param>
        /// <param name="singleSessionTimeout">A maximum time to wait each single call of
        /// <paramref name="blockFetcher"/>.  If a call is timed out unsatisfied demands
        /// are automatically retried to fetch from other peers.</param>
        /// <param name="cancellationToken">A cancellation token to observe while waiting
        /// for the task to complete.</param>
        /// <returns>An async enumerable that yields pairs of a fetched block and its source
        /// peer.  It terminates when all demands are satisfied.</returns>
        public async IAsyncEnumerable <Tuple <Block <TAction>, TPeer> > Complete(
            IReadOnlyList <TPeer> peers,
            BlockFetcher blockFetcher,
            TimeSpan singleSessionTimeout,
            [EnumeratorCancellation] CancellationToken cancellationToken = default
            )
        {
            if (!peers.Any())
            {
                throw new ArgumentException("The list of peers must not be empty.", nameof(peers));
            }

            var pool  = new PeerPool(peers);
            var queue = new AsyncProducerConsumerQueue <Tuple <Block <TAction>, TPeer> >();

            Task producer = Task.Run(async() =>
            {
                try
                {
                    await foreach (var hashes in EnumerateChunks(cancellationToken))
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        IList <HashDigest <SHA256> > hashDigests =
                            hashes is IList <HashDigest <SHA256> > l ? l : hashes.ToList();

                        cancellationToken.ThrowIfCancellationRequested();
                        await pool.SpawnAsync(
                            CreateEnqueuing(
                                hashDigests,
                                blockFetcher,
                                singleSessionTimeout,
                                cancellationToken,
                                queue
                                ),
                            cancellationToken: cancellationToken
                            );
                    }

                    await pool.WaitAll(cancellationToken);
                }
                finally
                {
                    queue.CompleteAdding();
                }
            });

            while (await queue.OutputAvailableAsync(cancellationToken))
            {
                Tuple <Block <TAction>, TPeer> pair;
                try
                {
                    pair = await queue.DequeueAsync(cancellationToken);
                }
                catch (InvalidOperationException)
                {
                    break;
                }

                yield return(pair);

                _logger.Verbose(
                    "Completed a block {BlockIndex} {BlockHash} from {Peer}.",
                    pair.Item1.Index,
                    pair.Item1.Hash,
                    pair.Item2
                    );
            }

            await producer;
        }