private async Task DownloadNextBlocks(Node node, CancellationToken ctsToken, int maxBlocksToDownload = 1) { var heights = new List <Height>(); try { await _sem.WaitAsync(ctsToken).ConfigureAwait(false); if (BlocksToDownload.Count == 0) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); 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); } } finally { _sem.Release(); } try { var headers = new HashSet <ChainedBlock>(); foreach (var height in heights) { WalletJob.TryGetHeader(height, out ChainedBlock neededHeader); headers.Add(neededHeader); } var delayMinutes = heights.Count; var timeoutToken = new CancellationTokenSource(TimeSpan.FromMinutes(delayMinutes)).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)).ConfigureAwait(false)); } catch { 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 { try { await _sem.WaitAsync(ctsToken).ConfigureAwait(false); foreach (var height in heights) { BlocksToDownload.Add(height); } } finally { _sem.Release(); } } }