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);
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); }
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);