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);
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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);
        }
示例#4
0
 public void ReportInvalid(PeerInfo peerInfo)
 {
     if (peerInfo != null)
     {
         _stats.ReportSyncEvent(peerInfo.SyncPeer.Node, NodeStatsEventType.SyncFailed);
         peerInfo.SyncPeer.Disconnect(DisconnectReason.BreachOfProtocol, "SYNC BREACH");
     }
 }
示例#5
0
        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");
            }
        }
示例#6
0
        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();
            }
        }
示例#7
0
 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()
     };
 }
示例#9
0
        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);
        }
示例#10
0
        public SyncPeerAllocation(PeerInfo initialPeer, string description)
        {
            lock (_allocationLock)
            {
                if (!initialPeer.IsAllocated)
                {
                    initialPeer.IsAllocated = true;
                    Current = initialPeer;
                }
            }

            Description = description;
        }
示例#11
0
        public void ReportNoSyncProgress(PeerInfo peerInfo)
        {
            if (peerInfo == null)
            {
                return;
            }

            if (_logger.IsDebug)
            {
                _logger.Debug($"No sync progress reported with {peerInfo}");
            }
            peerInfo.SleepingSince = DateTime.UtcNow;
        }
示例#12
0
        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;
        }
示例#13
0
 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");
         }
     }
 }
示例#14
0
        public void FinishSync()
        {
            PeerInfo current = Current;

            if (current == null)
            {
                return;
            }

            lock (_allocationLock)
            {
                current.IsAllocated = false;
                Current             = null;
            }
        }
示例#15
0
        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();
                }
            }
        }
示例#16
0
        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);
        }
示例#17
0
        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}])");
                }
            }
        }
示例#18
0
        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}])");
                }
            }
        }
示例#19
0
        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);
        }
示例#20
0
        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);
        }
示例#21
0
        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");
            }
        }
示例#22
0
        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);
        }
示例#23
0
        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
            });
        }
示例#24
0
        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);
        }
示例#25
0
        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}");
            }
        }
示例#26
0
        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);
        }
示例#27
0
        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);
                }
            }
        }
示例#28
0
        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");
                    }
                }
            }
        }
示例#29
0
        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);
        }
示例#30
0
        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);
        }