private async Task RunRefreshPeerLoop() { foreach (RefreshTotalDiffTask refreshTask in _peerRefreshQueue.GetConsumingEnumerable(_refreshLoopCancellation.Token)) { ISyncPeer syncPeer = refreshTask.SyncPeer; if (_logger.IsDebug) { _logger.Debug($"Refreshing info for {syncPeer}."); } CancellationTokenSource initCancelSource = _refreshCancelTokens[syncPeer.Node.Id] = new CancellationTokenSource(); CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(initCancelSource.Token, _refreshLoopCancellation.Token); #pragma warning disable 4014 ExecuteRefreshTask(refreshTask, linkedSource.Token).ContinueWith(t => #pragma warning restore 4014 { _refreshCancelTokens.TryRemove(syncPeer.Node.Id, out _); if (t.IsFaulted) { if (t.Exception != null && t.Exception.InnerExceptions.Any(x => x.InnerException is TimeoutException)) { if (_logger.IsTrace) { _logger.Trace($"Refreshing info for {syncPeer} failed due to timeout: {t.Exception.Message}"); } } else if (_logger.IsDebug) { _logger.Debug($"Refreshing info for {syncPeer} failed {t.Exception}"); } } else if (t.IsCanceled) { if (_logger.IsTrace) { _logger.Trace($"Refresh peer info canceled: {syncPeer.Node:s}"); } } else { UpgradeAllocations("REFRESH"); // cases when we want other nodes to resolve the impasse (check Goerli discussion on 5 out of 9 validators) if (syncPeer.TotalDifficulty == _blockTree.BestSuggestedHeader?.TotalDifficulty && syncPeer.HeadHash != _blockTree.BestSuggestedHeader?.Hash) { Block block = _blockTree.FindBlock(_blockTree.BestSuggestedHeader.Hash, BlockTreeLookupOptions.None); if (block != null) // can be null if fast syncing headers only { syncPeer.SendNewBlock(block); if (_logger.IsDebug) { _logger.Debug($"Sending my best block {block} to {syncPeer}"); } } } } if (_logger.IsDebug) { _logger.Debug($"Refreshed peer info for {syncPeer}."); } initCancelSource.Dispose(); linkedSource.Dispose(); }); } if (_logger.IsInfo) { _logger.Info("Exiting sync peer refresh loop"); } await Task.CompletedTask; }