private async Task DownloadNextBlocksAsync(Node node, CancellationToken ctsToken, int maxBlocksToDownload = 1) { var heights = new List <Height>(); using (await _asyncLock.LockAsync()) { if (_blocksToDownload.Count == 0) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }); return; } if (_blocksToDownload.Count < maxBlocksToDownload * 2) { maxBlocksToDownload = 1; } for (int i = 0; i < maxBlocksToDownload; i++) { // todo check if we have that much block var height = _blocksToDownload.Min(); _blocksToDownload.TryRemove(height); heights.Add(height); } } try { var headers = new HashSet <ChainedBlock>(); foreach (var height in heights) { var neededHeader = await _walletJob.TryGetHeaderAsync(height); headers.Add(neededHeader); } var delayMinutes = heights.Count; using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(delayMinutes))) { var timeoutToken = cancellationTokenSource.Token; var downloadCtsToken = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, ctsToken).Token; HashSet <Block> blocks = null; try { blocks = new HashSet <Block>(await Task.Run(() => node.GetBlocks(headers.ToArray(), downloadCtsToken))); } catch (Exception) { if (timeoutToken.IsCancellationRequested) { node.DisconnectAsync($"Block download time > {delayMinutes}min"); } else { node.DisconnectAsync("Block download failed"); } blocks = null; } if (blocks == null) { foreach (var height in heights) { _blocksToDownload.Add(height); } } else { int i = 0; foreach (var block in blocks) { _downloadedBlocks.AddOrReplace(heights[i], block); i++; } } } } catch (Exception) { using (_asyncLock.Lock()) { foreach (var height in heights) { _blocksToDownload.Add(height); } } } }