Beispiel #1
0
        private ValueTask DownloadFailHandler <T>(Task <T> downloadTask, string entities)
        {
            if (downloadTask.IsFaulted)
            {
                _sinceLastTimeout = 0;
                if (downloadTask.Exception?.InnerException is TimeoutException ||
                    (downloadTask.Exception?.InnerExceptions.Any(x => x is TimeoutException) ?? false) ||
                    (downloadTask.Exception?.InnerExceptions.Any(x => x.InnerException is TimeoutException) ?? false))
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Error($"Failed to retrieve {entities} when synchronizing (Timeout)", downloadTask.Exception);
                    }
                    _syncBatchSize.Shrink();
                }
                else
                {
                    Exception          exception          = downloadTask.Exception;
                    AggregateException aggregateException = exception as AggregateException;
                    if (aggregateException != null)
                    {
                        exception = aggregateException.InnerExceptions[0];
                    }

                    if (_logger.IsInfo)
                    {
                        _logger.Error($"Failed to retrieve {entities} when synchronizing.", exception);
                    }
                }

                throw new EthSynchronizationException($"{entities} task faulted", downloadTask.Exception);
            }

            return(default);
Beispiel #2
0
        public async Task <long> DownloadBlocks(PeerInfo bestPeer, int newBlocksToSkip, CancellationToken cancellation, bool shouldProcess = true)
        {
            if (bestPeer == null)
            {
                string message = $"Not expecting best peer to be null inside the {nameof(BlockDownloader)}";
                _logger.Error(message);
                throw new ArgumentNullException(message);
            }

            int blocksSynced        = 0;
            int ancestorLookupLevel = 0;

            long currentNumber = Math.Max(0, Math.Min(_blockTree.BestKnownNumber, bestPeer.HeadNumber - 1));

            while (bestPeer.TotalDifficulty > (_blockTree.BestSuggestedHeader?.TotalDifficulty ?? 0) && currentNumber <= bestPeer.HeadNumber)
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Continue full sync with {bestPeer} (our best {_blockTree.BestKnownNumber})");
                }
                if (ancestorLookupLevel > MaxReorganizationLength)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Could not find common ancestor with {bestPeer}");
                    }
                    throw new EthSynchronizationException("Peer with inconsistent chain in sync");
                }

                long blocksLeft      = bestPeer.HeadNumber - currentNumber - newBlocksToSkip;
                int  blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, _syncBatchSize.Current);
                if (blocksToRequest <= 1)
                {
                    break;
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"Full sync request {currentNumber}+{blocksToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {blocksToRequest} more.");
                }
                var headers = await RequestHeaders(bestPeer, cancellation, currentNumber, blocksToRequest);

                List <Keccak> hashes = new List <Keccak>();
                Dictionary <Keccak, BlockHeader> headersByHash = new Dictionary <Keccak, BlockHeader>();
                for (int i = 1; i < headers.Length; i++)
                {
                    if (headers[i] == null)
                    {
                        break;
                    }

                    hashes.Add(headers[i].Hash);
                    headersByHash[headers[i].Hash] = headers[i];
                }

                Task <BlockBody[]> bodiesTask = bestPeer.SyncPeer.GetBlocks(hashes.ToArray(), cancellation);
                await bodiesTask.ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        _sinceLastTimeout = 0;
                        if (t.Exception?.InnerException is TimeoutException ||
                            (t.Exception?.InnerExceptions.Any(x => x is TimeoutException) ?? false) ||
                            (t.Exception?.InnerExceptions.Any(x => x.InnerException is TimeoutException) ?? false))
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Error("Failed to retrieve bodies when synchronizing (Timeout)", bodiesTask.Exception);
                            }
                            _syncBatchSize.Shrink();
                        }
                        else
                        {
                            if (_logger.IsError)
                            {
                                _logger.Error("Failed to retrieve bodies when synchronizing", bodiesTask.Exception);
                            }
                        }

                        throw new EthSynchronizationException("Bodies task faulted.", bodiesTask.Exception);
                    }
                });

                if (bodiesTask.IsCanceled)
                {
                    return(blocksSynced);
                }

                BlockBody[] bodies = bodiesTask.Result;
                Block[]     blocks = new Block[bodies.Length];
                for (int i = 0; i < bodies.Length; i++)
                {
                    BlockBody body = bodies[i];
                    if (body == null)
                    {
                        // TODO: this is how it used to be... I do not want to touch it without extensive testing
                        throw new EthSynchronizationException($"{bestPeer} sent an empty body for {blocks[i].ToString(Block.Format.Short)}.");
                    }

                    blocks[i] = new Block(null, body);
                }

                _sinceLastTimeout++;
                if (_sinceLastTimeout > 2)
                {
                    _syncBatchSize.Expand();
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    blocks[i].Header = headersByHash[hashes[i]];
                }

                if (blocks.Length > 0)
                {
                    bool parentIsKnown = _blockTree.IsKnownBlock(blocks[0].Number - 1, blocks[0].ParentHash);
                    if (!parentIsKnown)
                    {
                        ancestorLookupLevel += _syncBatchSize.Current;
                        currentNumber        = currentNumber >= _syncBatchSize.Current ? (currentNumber - _syncBatchSize.Current) : 0L;
                        continue;
                    }
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (cancellation.IsCancellationRequested)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace("Peer sync cancelled");
                        }
                        break;
                    }

                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Received {blocks[i]} from {bestPeer}");
                    }

                    // can move this to block tree now?
                    if (!_blockValidator.ValidateSuggestedBlock(blocks[i]))
                    {
                        throw new EthSynchronizationException($"{bestPeer} sent an invalid block {blocks[i].ToString(Block.Format.Short)}.");
                    }

                    if (HandleAddResult(blocks[i].Header, i == 0, _blockTree.SuggestBlock(blocks[i], shouldProcess)))
                    {
                        blocksSynced++;
                    }

                    currentNumber = currentNumber + 1;
                }

                if (blocksSynced > 0)
                {
                    _syncStats.Update(_blockTree.BestSuggestedHeader?.Number ?? 0, bestPeer.HeadNumber, 1);
                }
            }

            return(blocksSynced);
        }
Beispiel #3
0
        public async Task <long> DownloadBlocks(PeerInfo bestPeer, int newBlocksToSkip, CancellationToken cancellation, DownloadOptions options = DownloadOptions.DownloadAndProcess)
        {
            var downloadReceipts = options == DownloadOptions.DownloadWithReceipts;
            var shouldProcess    = options == DownloadOptions.DownloadAndProcess;

            if (bestPeer == null)
            {
                string message = $"Not expecting best peer to be null inside the {nameof(BlockDownloader)}";
                _logger.Error(message);
                throw new ArgumentNullException(message);
            }

            int blocksSynced        = 0;
            int ancestorLookupLevel = 0;

            long currentNumber = Math.Max(0, Math.Min(_blockTree.BestKnownNumber, bestPeer.HeadNumber - 1));

            while (bestPeer.TotalDifficulty > (_blockTree.BestSuggestedHeader?.TotalDifficulty ?? 0) && currentNumber <= bestPeer.HeadNumber)
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Continue full sync with {bestPeer} (our best {_blockTree.BestKnownNumber})");
                }
                if (ancestorLookupLevel > MaxReorganizationLength)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Could not find common ancestor with {bestPeer}");
                    }
                    throw new EthSynchronizationException("Peer with inconsistent chain in sync");
                }

                long blocksLeft      = bestPeer.HeadNumber - currentNumber - newBlocksToSkip;
                int  blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, _syncBatchSize.Current);
                if (blocksToRequest <= 1)
                {
                    break;
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"Full sync request {currentNumber}+{blocksToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {blocksToRequest} more.");
                }
                var headers = await RequestHeaders(bestPeer, cancellation, currentNumber, blocksToRequest);

                Block[] blocks = new Block[headers.Length - 1];

                List <Keccak>         blockHashes  = new List <Keccak>();
                Dictionary <int, int> indexMapping = new Dictionary <int, int>();
                int currentBodyIndex = 0;
                for (int i = 1; i < headers.Length; i++)
                {
                    if (headers[i] == null)
                    {
                        break;
                    }

                    if (headers[i].HasBody)
                    {
                        blocks[i - 1] = new Block(headers[i], (BlockBody)null);
                        indexMapping.Add(currentBodyIndex, i - 1);
                        currentBodyIndex++;
                        blockHashes.Add(headers[i].Hash);
                    }
                    else
                    {
                        blocks[i - 1] = new Block(headers[i], BlockBody.Empty);
                    }
                }

                Task <BlockBody[]> bodiesTask = blockHashes.Count == 0
                    ? Task.FromResult(Array.Empty <BlockBody>())
                    : bestPeer.SyncPeer.GetBlocks(blockHashes, cancellation);

                Task <TxReceipt[][]> receiptsTasks = !downloadReceipts || blockHashes.Count == 0
                    ? Task.FromResult(Array.Empty <TxReceipt[]>())
                    : bestPeer.SyncPeer.GetReceipts(blockHashes, cancellation);

                ValueTask DownloadFailHandler <T>(Task <T> t, string entities)
                {
                    if (t.IsFaulted)
                    {
                        _sinceLastTimeout = 0;
                        if (t.Exception?.InnerException is TimeoutException ||
                            (t.Exception?.InnerExceptions.Any(x => x is TimeoutException) ?? false) ||
                            (t.Exception?.InnerExceptions.Any(x => x.InnerException is TimeoutException) ?? false))
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Error($"Failed to retrieve {entities} when synchronizing (Timeout)", bodiesTask.Exception);
                            }
                            _syncBatchSize.Shrink();
                        }
                        else
                        {
                            if (_logger.IsError)
                            {
                                _logger.Error($"Failed to retrieve {entities} when synchronizing.", bodiesTask.Exception);
                            }
                        }

                        throw new EthSynchronizationException($"{entities} task faulted", t.Exception);
                    }

                    return(default);