public void AddPeer(ISyncPeer syncPeer) { if (_logger.IsDebug) { _logger.Debug($"Adding sync peer {syncPeer.Node:c}"); } if (!_isStarted) { if (_logger.IsDebug) { _logger.Debug($"Sync peer pool not started yet - adding peer is blocked: {syncPeer.Node:s}"); } return; } if (_peers.ContainsKey(syncPeer.Node.Id)) { if (_logger.IsDebug) { _logger.Debug($"Sync peer {syncPeer.Node:c} already in peers collection."); } return; } var peerInfo = new PeerInfo(syncPeer); _peers.TryAdd(syncPeer.Node.Id, peerInfo); Metrics.SyncPeers = _peers.Count; if (_logger.IsDebug) { _logger.Debug($"Adding {syncPeer.Node:c} to refresh queue"); } _peerRefreshQueue.Add(peerInfo); }
private void AllocationOnReplaced(object sender, AllocationChangeEventArgs e) { if (e.Previous == null) { if (_logger.IsDebug) { _logger.Debug($"Allocating {e.Current} on {_blocksSyncAllocation}."); } } else { if (_logger.IsDebug) { _logger.Debug($"Replacing {e.Previous} with {e.Current} on {_blocksSyncAllocation}."); } } if (e.Previous != null) { _cancelDueToBetterPeer = true; _peerSyncCancellation?.Cancel(); } PeerInfo newPeer = e.Current; BlockHeader bestSuggested = _blockTree.BestSuggestedHeader; if (newPeer.TotalDifficulty > bestSuggested.TotalDifficulty) { RequestSynchronization(SyncTriggerType.PeerChange); } }
public void ReplaceCurrent(PeerInfo betterPeer) { if (betterPeer == null) { throw new ArgumentNullException(nameof(betterPeer)); } AllocationChangeEventArgs args; lock (_allocationLock) { PeerInfo current = Current; if (current != null && !CanBeReplaced) { return; } if (betterPeer.IsAllocated) { return; } betterPeer.IsAllocated = true; if (current != null) { current.IsAllocated = false; } args = new AllocationChangeEventArgs(current, betterPeer); Current = betterPeer; } Replaced?.Invoke(this, args); }
public void ReportInvalid(PeerInfo peerInfo) { if (peerInfo != null) { _stats.ReportSyncEvent(peerInfo.SyncPeer.Node, NodeStatsEventType.SyncFailed); peerInfo.SyncPeer.Disconnect(DisconnectReason.BreachOfProtocol, "SYNC BREACH"); } }
private void DropUselessPeers() { if (DateTime.UtcNow - _lastUselessDrop < TimeSpan.FromSeconds(30)) { // give some time to monitoring nodes return; } _lastUselessDrop = DateTime.UtcNow; if ((decimal)PeerCount / PeerMaxCount > 0.5m) { long ourNumber = _blockTree.BestSuggested?.Number ?? 0L; UInt256 ourDifficulty = _blockTree.BestSuggested?.TotalDifficulty ?? UInt256.Zero; foreach (PeerInfo peerInfo in AllPeers) { if (peerInfo.HeadNumber > ourNumber) { // as long as we are behind we can use the stuck peers continue; } if (peerInfo.HeadNumber == 0) { peerInfo.SyncPeer.Disconnect(DisconnectReason.UselessPeer, "PEER REVIEW / HEAD 0"); } else if (peerInfo.HeadNumber == 1920000) // mainnet, stuck Geth nodes { peerInfo.SyncPeer.Disconnect(DisconnectReason.UselessPeer, "PEER REVIEW / 1920000"); } else if (peerInfo.HeadNumber == 7280022) // mainnet, stuck Geth nodes { peerInfo.SyncPeer.Disconnect(DisconnectReason.UselessPeer, "PEER REVIEW / 7280022"); } else if (peerInfo.HeadNumber > ourNumber + 1024L && peerInfo.TotalDifficulty < ourDifficulty) { // probably classic nodes tht remain connected after we went pass the DAO // worth to find a better way to discard them at the right time peerInfo.SyncPeer.Disconnect(DisconnectReason.UselessPeer, "STRAY PEER"); } } } if (PeerCount == PeerMaxCount) { int worstLatency = 0; PeerInfo worstPeer = null; foreach (PeerInfo peerInfo in AllPeers) { long latency = _stats.GetOrAdd(peerInfo.SyncPeer.Node).GetAverageLatency(NodeLatencyStatType.BlockHeaders) ?? 100000; if (latency > worstLatency) { worstPeer = peerInfo; } } worstPeer?.SyncPeer.Disconnect(DisconnectReason.TooManyPeers, "PEER REVIEW / LATENCY"); } }
public void RemovePeer(ISyncPeer syncPeer) { if (_logger.IsDebug) { _logger.Debug($"Removing sync peer {syncPeer.Node:c}"); } if (!_isStarted) { if (_logger.IsDebug) { _logger.Debug($"Sync peer pool not started yet - removing {syncPeer.Node:c} is blocked."); } return; } PublicKey id = syncPeer.Node.Id; if (id == null) { if (_logger.IsDebug) { _logger.Debug("Peer ID was null when removing peer"); } return; } if (!_peers.TryRemove(id, out _)) { // possible if sync failed - we remove peer and eventually initiate disconnect, which calls remove peer again return; } Metrics.SyncPeers = _peers.Count; foreach ((SyncPeerAllocation allocation, _) in _replaceableAllocations) { if (allocation.Current?.SyncPeer.Node.Id == id) { PeerInfo peerInfo = allocation.Current; if (_logger.IsTrace) { _logger.Trace($"Requesting peer cancel with {syncPeer.Node:c} on {allocation}"); } allocation.Cancel(); peerInfo.MarkDisconnected(); } } if (_refreshCancelTokens.TryGetValue(id, out CancellationTokenSource initCancelTokenSource)) { initCancelTokenSource?.Cancel(); } }
public void ReportInvalid(PeerInfo peerInfo, string details) { /* since the allocations can have the peers dynamically changed * it may be hard for the external classes to ensure that the peerInfo is not null at the time when they report * so we decide to check for null here and not consider the scenario to be exceptional */ if (peerInfo != null) { _stats.ReportSyncEvent(peerInfo.SyncPeer.Node, NodeStatsEventType.SyncFailed); peerInfo.SyncPeer.Disconnect(DisconnectReason.BreachOfProtocol, details); } }
public static int MaxReceiptsPerRequest(this PeerInfo peer) { return peer.PeerClientType switch { PeerClientType.BeSu => BeSuSyncLimits.MaxReceiptFetch, PeerClientType.Geth => GethSyncLimits.MaxReceiptFetch, PeerClientType.Nethermind => NethermindSyncLimits.MaxReceiptFetch, PeerClientType.Parity => ParitySyncLimits.MaxReceiptFetch, PeerClientType.Unknown => 128, _ => throw new ArgumentOutOfRangeException() }; }
public void ReportNoSyncProgress(PeerInfo peerInfo) { if (peerInfo == null) { return; } if (_logger.IsDebug) { _logger.Debug($"No sync progress reported with {peerInfo}"); } _sleepingPeers.TryAdd(peerInfo, DateTime.UtcNow); }
public SyncPeerAllocation(PeerInfo initialPeer, string description) { lock (_allocationLock) { if (!initialPeer.IsAllocated) { initialPeer.IsAllocated = true; Current = initialPeer; } } Description = description; }
public void ReportNoSyncProgress(PeerInfo peerInfo) { if (peerInfo == null) { return; } if (_logger.IsDebug) { _logger.Debug($"No sync progress reported with {peerInfo}"); } peerInfo.SleepingSince = DateTime.UtcNow; }
public void ReportNoSyncProgress(PeerInfo peerInfo, bool isSevere = true) { if (peerInfo == null) { return; } if (_logger.IsDebug) { _logger.Debug($"No sync progress reported with {peerInfo}"); } peerInfo.SleepingSince = DateTime.UtcNow; peerInfo.IsSleepingDeeply = isSevere; }
private void ValidateBatchConsistency(PeerInfo bestPeer, BlockHeader[] headers) { // Parity 1.11 non canonical blocks when testing on 27/06 for (int i = 0; i < headers.Length; i++) { if (i != 0 && headers[i] != null && headers[i]?.ParentHash != headers[i - 1]?.Hash) { if (_logger.IsTrace) { _logger.Trace($"Inconsistent block list from peer {bestPeer}"); } throw new EthSynchronizationException("Peer sent an inconsistent block list"); } } }
public void FinishSync() { PeerInfo current = Current; if (current == null) { return; } lock (_allocationLock) { current.IsAllocated = false; Current = null; } }
private void BlockTreeOnNewHeadBlock(object sender, BlockEventArgs e) { foreach ((SyncPeerAllocation allocation, _) in _allocations) { PeerInfo currentPeer = allocation.Current; if (currentPeer == null) { continue; } if (currentPeer.TotalDifficulty < (e.Block.TotalDifficulty ?? 0)) { allocation.Cancel(); } } }
public void ReportNoSyncProgress(SyncPeerAllocation allocation) { PeerInfo peer = allocation?.Current; if (peer == null) { return; } // this is generally with the strange Parity nodes behaviour if (_logger.IsDebug) { _logger.Debug($"No sync progress reported with {allocation.Current}"); } _sleepingPeers.TryAdd(peer, DateTime.UtcNow); }
private void ReplaceIfWorthReplacing(SyncPeerAllocation allocation, PeerInfo peerInfo) { if (!allocation.CanBeReplaced) { return; } if (peerInfo == null) { return; } if (allocation.Current == null) { allocation.ReplaceCurrent(peerInfo); return; } if (peerInfo == allocation.Current) { if (_logger.IsTrace) { _logger.Trace($"{allocation} is already syncing with best peer {peerInfo}"); } return; } var currentSpeed = _stats.GetOrAdd(allocation.Current?.SyncPeer.Node)?.GetAverageTransferSpeed() ?? 0; var newSpeed = _stats.GetOrAdd(peerInfo.SyncPeer.Node)?.GetAverageTransferSpeed() ?? 0; if (newSpeed / (decimal)Math.Max(1L, currentSpeed) > 1m + _minDiffPercentageForSpeedSwitch && newSpeed > currentSpeed + _minDiffForSpeedSwitch) { if (_logger.IsInfo) { _logger.Info($"Sync peer substitution{Environment.NewLine} OUT: {allocation.Current}[{currentSpeed}]{Environment.NewLine} IN : {peerInfo}[{newSpeed}]"); } allocation.ReplaceCurrent(peerInfo); } else { if (_logger.IsTrace) { _logger.Trace($"Staying with current peer {allocation.Current}[{currentSpeed}] (ignoring {peerInfo}[{newSpeed}])"); } } }
private void ReplaceIfWorthReplacing(SyncPeerAllocation allocation, PeerInfo peerInfo) { if (!allocation.CanBeReplaced) { return; } if (peerInfo == null) { return; } if (allocation.Current == null) { allocation.ReplaceCurrent(peerInfo); return; } if (peerInfo == allocation.Current) { if (_logger.IsTrace) { _logger.Trace($"{allocation} is already syncing with best peer {peerInfo}"); } return; } var currentLatency = _stats.GetOrAdd(allocation.Current?.SyncPeer.Node)?.GetAverageLatency(NodeLatencyStatType.BlockHeaders) ?? 100000; var newLatency = _stats.GetOrAdd(peerInfo.SyncPeer.Node)?.GetAverageLatency(NodeLatencyStatType.BlockHeaders) ?? 100001; if (newLatency / (decimal)Math.Max(1L, currentLatency) < 1m - _syncConfig.MinDiffPercentageForLatencySwitch / 100m && newLatency < currentLatency - _syncConfig.MinDiffForLatencySwitch) { if (_logger.IsInfo) { _logger.Info($"Sync peer substitution{Environment.NewLine} OUT: {allocation.Current}[{currentLatency}]{Environment.NewLine} IN : {peerInfo}[{newLatency}]"); } allocation.ReplaceCurrent(peerInfo); } else { if (_logger.IsTrace) { _logger.Trace($"Staying with current peer {allocation.Current}[{currentLatency}] (ignoring {peerInfo}[{newLatency}])"); } } }
public SyncPeerAllocation Borrow(BorrowOptions borrowOptions, string description) { SyncPeerAllocation allocation = new SyncPeerAllocation(description); if ((borrowOptions & BorrowOptions.DoNotReplace) == BorrowOptions.DoNotReplace) { allocation.CanBeReplaced = false; } PeerInfo bestPeer = SelectBestPeerForAllocation(allocation, "BORROW"); if (bestPeer != null) { allocation.ReplaceCurrent(bestPeer); } _allocations.TryAdd(allocation, null); return(allocation); }
public void Cancel() { PeerInfo current = Current; if (current == null) { return; } lock (_allocationLock) { current.IsAllocated = false; Current = null; } AllocationChangeEventArgs args = new AllocationChangeEventArgs(current, null); Cancelled?.Invoke(this, args); }
public void ReportWeakPeer(SyncPeerAllocation allocation) { PeerInfo weakPeer = allocation.Current; if (weakPeer == null) { /* it may have just got disconnected and in such case the allocation would be nullified * in such case there is no need to talk about whether the peer is good or bad */ return; } if (weakPeer.IncreaseWeakness() > MaxPeerWeakness) { /* fast Geth nodes send invalid nodes quite often :/ * so we let them deliver fast and only disconnect them when they really misbehave */ allocation.Current.SyncPeer.Disconnect(DisconnectReason.UselessPeer, "peer is too weak"); } }
public PeerInfo Select(PeerInfo currentPeer, IEnumerable <PeerInfo> peers, INodeStatsManager nodeStatsManager, IBlockTree blockTree) { long nullSpeed = _priority ? -1 : long.MaxValue; long currentSpeed = currentPeer == null ? nullSpeed : nodeStatsManager.GetOrAdd(currentPeer.SyncPeer.Node).GetAverageTransferSpeed() ?? nullSpeed; (PeerInfo Info, long TransferSpeed)bestPeer = (currentPeer, currentSpeed); foreach (PeerInfo info in peers) { (this as IPeerSelectionStrategy).CheckAsyncState(info); long averageTransferSpeed = nodeStatsManager.GetOrAdd(info.SyncPeer.Node).GetAverageTransferSpeed() ?? 0; if (_priority ? averageTransferSpeed > bestPeer.TransferSpeed : averageTransferSpeed < bestPeer.TransferSpeed) { bestPeer = (info, averageTransferSpeed); } } return(bestPeer.Info); }
public void AddPeer(ISyncPeer syncPeer) { if (_logger.IsDebug) { _logger.Debug($"Adding sync peer {syncPeer.Node:c}"); } if (!_isStarted) { if (_logger.IsDebug) { _logger.Debug($"Sync peer pool not started yet - adding peer is blocked: {syncPeer.Node:s}"); } return; } if (_peers.ContainsKey(syncPeer.Node.Id)) { if (_logger.IsDebug) { _logger.Debug($"Sync peer {syncPeer.Node:c} already in peers collection."); } return; } PeerInfo peerInfo = new PeerInfo(syncPeer); _peers.TryAdd(syncPeer.Node.Id, peerInfo); Metrics.SyncPeers = _peers.Count; if (_logger.IsDebug) { _logger.Debug($"Adding {syncPeer.Node:c} to refresh queue"); } if (NetworkDiagTracer.IsEnabled) { NetworkDiagTracer.ReportInterestingEvent(peerInfo.SyncPeer.Node.Host, "adding node to refresh queue"); } _peerRefreshQueue.Add(new RefreshTotalDiffTask { PeerInfo = peerInfo }); }
public SyncPeerAllocation Borrow(BorrowOptions borrowOptions, string description, long?minNumber = null) { SyncPeerAllocation allocation = new SyncPeerAllocation(description); allocation.MinBlocksAhead = minNumber - _blockTree.BestSuggestedHeader?.Number; if ((borrowOptions & BorrowOptions.DoNotReplace) == BorrowOptions.DoNotReplace) { allocation.CanBeReplaced = false; } PeerInfo bestPeer = SelectBestPeerForAllocation(allocation, "BORROW", (borrowOptions & BorrowOptions.LowPriority) == BorrowOptions.LowPriority); if (bestPeer != null) { allocation.ReplaceCurrent(bestPeer); } _allocations.TryAdd(allocation, null); return(allocation); }
public void Free(SyncPeerAllocation syncPeerAllocation) { if (_logger.IsTrace) { _logger.Trace($"Returning {syncPeerAllocation}"); } PeerInfo peerInfo = syncPeerAllocation.Current; if (peerInfo != null && !syncPeerAllocation.CanBeReplaced) { _peerBadness.TryRemove(peerInfo, out _); } _allocations.TryRemove(syncPeerAllocation, out _); syncPeerAllocation.Cancel(); if (_allocations.Count > 1024 * 16) { _logger.Warn($"Peer allocations leakage - {_allocations.Count}"); } }
private async Task <BlockHeader[]> RequestHeaders(PeerInfo bestPeer, CancellationToken cancellation, long currentNumber, int headersToRequest) { var headersRequest = bestPeer.SyncPeer.GetBlockHeaders(currentNumber, headersToRequest, 0, cancellation); await headersRequest.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)) { _syncBatchSize.Shrink(); if (_logger.IsTrace) { _logger.Error("Failed to retrieve headers when synchronizing (Timeout)", t.Exception); } } else { if (_logger.IsError) { _logger.Error("Failed to retrieve headers when synchronizing", t.Exception); } } throw new EthSynchronizationException("Headers task faulted.", t.Exception); } }); cancellation.ThrowIfCancellationRequested(); var headers = headersRequest.Result; ValidateSeals(cancellation, headers); ValidateBatchConsistency(bestPeer, headers); return(headers); }
public BlockDownloadContext(ISpecProvider specProvider, PeerInfo syncPeer, BlockHeader[] headers, bool downloadReceipts, IReceiptsRecovery receiptsRecovery) { _indexMapping = new Dictionary <int, int>(); _downloadReceipts = downloadReceipts; _receiptsRecovery = receiptsRecovery; _specProvider = specProvider; _syncPeer = syncPeer; Blocks = new Block[headers.Length - 1]; NonEmptyBlockHashes = new List <Keccak>(); if (_downloadReceipts) { ReceiptsForBlocks = new TxReceipt[Blocks.Length][]; // do that only if downloading receipts } 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++; NonEmptyBlockHashes.Add(headers[i].Hash); } else { Blocks[i - 1] = new Block(headers[i], BlockBody.Empty); } } }
private void UpdateAllocations(string reason) { foreach ((SyncPeerAllocation allocation, _) in _allocations) { if (!allocation.CanBeReplaced) { continue; } PeerInfo bestPeer = SelectBestPeerForAllocation(allocation, reason); if (bestPeer != allocation.Current) { ReplaceIfWorthReplacing(allocation, bestPeer); } else { if (_logger.IsTrace) { _logger.Trace($"No better peer to sync with when updating allocations"); } } } }
public async Task <long> DownloadHeaders(PeerInfo bestPeer, int newBlocksToSkip, CancellationToken cancellation) { if (bestPeer == null) { string message = $"Not expecting best peer to be null inside the {nameof(BlockDownloader)}"; _logger.Error(message); throw new ArgumentNullException(message); } int headersSynced = 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.IsTrace) { _logger.Trace($"Continue headers sync with {bestPeer} (our best {_blockTree.BestKnownNumber})"); } long blocksLeft = bestPeer.HeadNumber - currentNumber - newBlocksToSkip; int headersToRequest = (int)Math.Min(blocksLeft + 1, _syncBatchSize.Current); if (headersToRequest <= 1) { break; } if (_logger.IsTrace) { _logger.Trace($"Headers request {currentNumber}+{headersToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {headersToRequest} more."); } BlockHeader[] headers = await RequestHeaders(bestPeer, cancellation, currentNumber, headersToRequest); BlockHeader startingPoint = headers[0] == null ? null : _blockTree.FindHeader(headers[0].Hash, BlockTreeLookupOptions.TotalDifficultyNotNeeded); if (startingPoint == null) { ancestorLookupLevel++; if (ancestorLookupLevel >= _ancestorJumps.Length) { if (_logger.IsWarn) { _logger.Warn($"Could not find common ancestor with {bestPeer}"); } throw new EthSynchronizationException("Peer with inconsistent chain in sync"); } int ancestorJump = _ancestorJumps[ancestorLookupLevel] - _ancestorJumps[ancestorLookupLevel - 1]; currentNumber = currentNumber >= ancestorJump ? (currentNumber - ancestorJump) : 0L; continue; } ancestorLookupLevel = 0; _sinceLastTimeout++; if (_sinceLastTimeout >= 2) { // if peers are not timing out then we can try to be slightly more eager _syncBatchSize.Expand(); } for (int i = 1; i < headers.Length; i++) { if (cancellation.IsCancellationRequested) { break; } BlockHeader currentHeader = headers[i]; if (currentHeader == null) { if (headersSynced > 0) { break; } return(0); } if (_logger.IsTrace) { _logger.Trace($"Received {currentHeader} from {bestPeer:s}"); } bool isValid = i > 1 ? _blockValidator.ValidateHeader(currentHeader, headers[i - 1], false) : _blockValidator.ValidateHeader(currentHeader, false); if (!isValid) { throw new EthSynchronizationException($"{bestPeer} sent a block {currentHeader.ToString(BlockHeader.Format.Short)} with an invalid header"); } if (HandleAddResult(bestPeer, currentHeader, i == 0, _blockTree.Insert(currentHeader))) { headersSynced++; } currentNumber = currentNumber + 1; } if (headersSynced > 0) { _syncReport.FullSyncBlocksDownloaded.Update(_blockTree.BestSuggestedHeader?.Number ?? 0); _syncReport.FullSyncBlocksKnown = bestPeer.HeadNumber; } else { break; } } return(headersSynced); }
public async Task <long> DownloadBlocks(PeerInfo bestPeer, int numberOfLatestBlocksToBeIgnored, CancellationToken cancellation, BlockDownloaderOptions options = BlockDownloaderOptions.Process) { IReceiptsRecovery receiptsRecovery = new ReceiptsRecovery(); if (bestPeer == null) { string message = $"Not expecting best peer to be null inside the {nameof(BlockDownloader)}"; if (_logger.IsError) { _logger.Error(message); } throw new ArgumentNullException(message); } bool downloadReceipts = (options & BlockDownloaderOptions.DownloadReceipts) == BlockDownloaderOptions.DownloadReceipts; bool shouldProcess = (options & BlockDownloaderOptions.Process) == BlockDownloaderOptions.Process; bool shouldMoveToMain = (options & BlockDownloaderOptions.MoveToMain) == BlockDownloaderOptions.MoveToMain; int blocksSynced = 0; int ancestorLookupLevel = 0; long currentNumber = Math.Max(0, Math.Min(_blockTree.BestKnownNumber, bestPeer.HeadNumber - 1)); // pivot number - 6 for uncle validation // long currentNumber = Math.Max(Math.Max(0, pivotNumber - 6), 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})"); } long blocksLeft = bestPeer.HeadNumber - currentNumber - numberOfLatestBlocksToBeIgnored; int headersToRequest = (int)Math.Min(blocksLeft + 1, _syncBatchSize.Current); if (headersToRequest <= 1) { break; } headersToRequest = Math.Min(headersToRequest, bestPeer.MaxHeadersPerRequest()); if (_logger.IsTrace) { _logger.Trace($"Full sync request {currentNumber}+{headersToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {headersToRequest} more."); } if (cancellation.IsCancellationRequested) { return(blocksSynced); // check before every heavy operation } BlockHeader[] headers = await RequestHeaders(bestPeer, cancellation, currentNumber, headersToRequest); BlockDownloadContext context = new BlockDownloadContext(_specProvider, bestPeer, headers, downloadReceipts, receiptsRecovery); if (cancellation.IsCancellationRequested) { return(blocksSynced); // check before every heavy operation } await RequestBodies(bestPeer, cancellation, context); if (downloadReceipts) { if (cancellation.IsCancellationRequested) { return(blocksSynced); // check before every heavy operation } await RequestReceipts(bestPeer, cancellation, context); } _sinceLastTimeout++; if (_sinceLastTimeout > 2) { _syncBatchSize.Expand(); } Block[] blocks = context.Blocks; Block blockZero = blocks[0]; if (context.FullBlocksCount > 0) { bool parentIsKnown = _blockTree.IsKnownBlock(blockZero.Number - 1, blockZero.ParentHash); if (!parentIsKnown) { ancestorLookupLevel++; if (ancestorLookupLevel >= _ancestorJumps.Length) { if (_logger.IsWarn) { _logger.Warn($"Could not find common ancestor with {bestPeer}"); } throw new EthSynchronizationException("Peer with inconsistent chain in sync"); } int ancestorJump = _ancestorJumps[ancestorLookupLevel] - _ancestorJumps[ancestorLookupLevel - 1]; currentNumber = currentNumber >= ancestorJump ? (currentNumber - ancestorJump) : 0L; continue; } } ancestorLookupLevel = 0; for (int blockIndex = 0; blockIndex < context.FullBlocksCount; blockIndex++) { if (cancellation.IsCancellationRequested) { if (_logger.IsTrace) { _logger.Trace("Peer sync cancelled"); } break; } Block currentBlock = blocks[blockIndex]; if (_logger.IsTrace) { _logger.Trace($"Received {currentBlock} from {bestPeer}"); } // can move this to block tree now? if (!_blockValidator.ValidateSuggestedBlock(currentBlock)) { throw new EthSynchronizationException($"{bestPeer} sent an invalid block {currentBlock.ToString(Block.Format.Short)}."); } if (HandleAddResult(bestPeer, currentBlock.Header, blockIndex == 0, _blockTree.SuggestBlock(currentBlock, shouldProcess))) { if (downloadReceipts) { for (int receiptIndex = 0; receiptIndex < (context.ReceiptsForBlocks[blockIndex]?.Length ?? 0); receiptIndex++) { _receiptStorage.Add(context.ReceiptsForBlocks[blockIndex][receiptIndex], true); } } blocksSynced++; } if (shouldMoveToMain) { _blockTree.UpdateMainChain(new[] { currentBlock }, false); } currentNumber += 1; } if (blocksSynced > 0) { _syncReport.FullSyncBlocksDownloaded.Update(_blockTree.BestSuggestedHeader?.Number ?? 0); _syncReport.FullSyncBlocksKnown = bestPeer.HeadNumber; } else { break; } } return(blocksSynced); }