Beispiel #1
0
        private void SyncBlock(Block block, ISyncPeer syncPeer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"{block}");
            }

            // we do not trust total difficulty from peers
            // Parity sends invalid data here and it is equally expensive to validate and to set from null
            block.Header.TotalDifficulty = null;

            bool isKnownParent = _blockTree.IsKnownBlock(block.Number - 1, block.ParentHash);

            if (isKnownParent)
            {
                if (!_blockValidator.ValidateSuggestedBlock(block))
                {
                    string message = $"Peer {syncPeer?.Node:c} sent an invalid block";
                    if (_logger.IsDebug)
                    {
                        _logger.Debug(message);
                    }
                    lock (_recentlySuggested)
                    {
                        _recentlySuggested.Delete(block.Hash);
                    }

                    throw new EthSyncException(message);
                }

                AddBlockResult result = _blockTree.SuggestBlock(block, true);
                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                }
            }

            // TODO: now it should be done by sync peer pool?
            // // do not change to if..else
            // // there are some rare cases when it did not work...
            // // do not remember why
            // if (result == AddBlockResult.UnknownParent)
            // {
            //     _synchronizer.RequestSynchronization(SyncTriggerType.NewBlock);
            // }
        }
Beispiel #2
0
        private void SyncBlock(Block block, ISyncPeer syncPeer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"{block}");
            }

            // we do not trust total difficulty from peers
            // Parity sends invalid data here and it is equally expensive to validate and to set from null
            block.Header.TotalDifficulty = null;

            bool isKnownParent = _blockTree.IsKnownBlock(block.Number - 1, block.ParentHash);

            if (isKnownParent)
            {
                if (!_blockValidator.ValidateSuggestedBlock(block))
                {
                    string message = $"Peer {syncPeer?.Node:c} sent an invalid block";
                    if (_logger.IsDebug)
                    {
                        _logger.Debug(message);
                    }
                    lock (_recentlySuggested)
                    {
                        _recentlySuggested.Delete(block.Hash);
                    }

                    throw new EthSyncException(message);
                }

                AddBlockResult result = _blockTree.SuggestBlock(block);
                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                }
            }
        }
Beispiel #3
0
        public bool IsBeaconSyncHeadersFinished()
        {
            BlockHeader?lowestInsertedBeaconHeader = _blockTree.LowestInsertedBeaconHeader;
            bool        chainMerged =
                ((lowestInsertedBeaconHeader?.Number ?? 0) - 1) <= (_blockTree.BestSuggestedHeader?.Number ?? long.MaxValue) &&
                lowestInsertedBeaconHeader != null &&
                _blockTree.IsKnownBlock(lowestInsertedBeaconHeader.Number - 1, lowestInsertedBeaconHeader.ParentHash !);
            bool finished = lowestInsertedBeaconHeader == null ||
                            lowestInsertedBeaconHeader.Number <= _syncConfig.PivotNumberParsed + 1 ||
                            chainMerged;

            if (_logger.IsTrace)
            {
                _logger.Trace(
                    $"IsBeaconSyncHeadersFinished: {finished}," +
                    $" BeaconPivotExists: {_beaconPivot.BeaconPivotExists()}," +
                    $" LowestInsertedBeaconHeaderNumber: {_blockTree.LowestInsertedBeaconHeader?.Number}," +
                    $" BestSuggestedHeader: {_blockTree.BestSuggestedHeader?.Number}," +
                    $" ChainMerged: {chainMerged}," +
                    $" BeaconPivot: {_beaconPivot.PivotNumber}," +
                    $" BeaconPivotDestinationNumber: {_beaconPivot.PivotDestinationNumber}");
            }
            return(finished);
        }
Beispiel #4
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);
        }
Beispiel #5
0
 public bool IsKnownBlock(long number, Keccak blockHash)
 {
     return(_blockTree.IsKnownBlock(number, blockHash));
 }
Beispiel #6
0
 public void Does_not_request_peer_refresh_on_known_hints()
 {
     _blockTree.IsKnownBlock(1, TestItem.KeccakA).ReturnsForAnyArgs(true);
     _syncServer.HintBlock(TestItem.KeccakA, 1, _nodeWhoSentTheBlock);
     _peerPool.DidNotReceiveWithAnyArgs().RefreshTotalDifficulty(null, null);
 }
Beispiel #7
0
 public bool IsKnownBlock(long number, Keccak blockHash) => _wrapped.IsKnownBlock(number, blockHash);
 public bool IsKnownBlock(long number, Keccak blockHash)
 {
     return(_wrapped.IsKnownBlock(number, blockHash));
 }
Beispiel #9
0
        public async Task <long> DownloadBlocks(PeerInfo bestPeer, int newBlocksToSkip, CancellationToken cancellation, bool shouldProcess = true)
        {
            if (bestPeer == null)
            {
                string message = $"Not expecting best peer to be null inside the {nameof(BlockDownloader)}";
                _logger.Error(message);
                throw new ArgumentNullException(message);
            }

            int blocksSynced        = 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.IsDebug)
                {
                    _logger.Debug($"Continue full sync with {bestPeer} (our best {_blockTree.BestKnownNumber})");
                }
                if (ancestorLookupLevel > MaxReorganizationLength)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Could not find common ancestor with {bestPeer}");
                    }
                    throw new EthSynchronizationException("Peer with inconsistent chain in sync");
                }

                long blocksLeft      = bestPeer.HeadNumber - currentNumber - newBlocksToSkip;
                int  blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, _syncBatchSize.Current);
                if (blocksToRequest <= 1)
                {
                    break;
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"Full sync request {currentNumber}+{blocksToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {blocksToRequest} more.");
                }
                var headers = await RequestHeaders(bestPeer, cancellation, currentNumber, blocksToRequest);

                List <Keccak> hashes = new List <Keccak>();
                Dictionary <Keccak, BlockHeader> headersByHash = new Dictionary <Keccak, BlockHeader>();
                for (int i = 1; i < headers.Length; i++)
                {
                    if (headers[i] == null)
                    {
                        break;
                    }

                    hashes.Add(headers[i].Hash);
                    headersByHash[headers[i].Hash] = headers[i];
                }

                Task <BlockBody[]> bodiesTask = bestPeer.SyncPeer.GetBlocks(hashes.ToArray(), cancellation);
                await bodiesTask.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))
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Error("Failed to retrieve bodies when synchronizing (Timeout)", bodiesTask.Exception);
                            }
                            _syncBatchSize.Shrink();
                        }
                        else
                        {
                            if (_logger.IsError)
                            {
                                _logger.Error("Failed to retrieve bodies when synchronizing", bodiesTask.Exception);
                            }
                        }

                        throw new EthSynchronizationException("Bodies task faulted.", bodiesTask.Exception);
                    }
                });

                if (bodiesTask.IsCanceled)
                {
                    return(blocksSynced);
                }

                BlockBody[] bodies = bodiesTask.Result;
                Block[]     blocks = new Block[bodies.Length];
                for (int i = 0; i < bodies.Length; i++)
                {
                    BlockBody body = bodies[i];
                    if (body == null)
                    {
                        // TODO: this is how it used to be... I do not want to touch it without extensive testing
                        throw new EthSynchronizationException($"{bestPeer} sent an empty body for {blocks[i].ToString(Block.Format.Short)}.");
                    }

                    blocks[i] = new Block(null, body);
                }

                _sinceLastTimeout++;
                if (_sinceLastTimeout > 2)
                {
                    _syncBatchSize.Expand();
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    blocks[i].Header = headersByHash[hashes[i]];
                }

                if (blocks.Length > 0)
                {
                    bool parentIsKnown = _blockTree.IsKnownBlock(blocks[0].Number - 1, blocks[0].ParentHash);
                    if (!parentIsKnown)
                    {
                        ancestorLookupLevel += _syncBatchSize.Current;
                        currentNumber        = currentNumber >= _syncBatchSize.Current ? (currentNumber - _syncBatchSize.Current) : 0L;
                        continue;
                    }
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (cancellation.IsCancellationRequested)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace("Peer sync cancelled");
                        }
                        break;
                    }

                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Received {blocks[i]} from {bestPeer}");
                    }

                    // can move this to block tree now?
                    if (!_blockValidator.ValidateSuggestedBlock(blocks[i]))
                    {
                        throw new EthSynchronizationException($"{bestPeer} sent an invalid block {blocks[i].ToString(Block.Format.Short)}.");
                    }

                    if (HandleAddResult(blocks[i].Header, i == 0, _blockTree.SuggestBlock(blocks[i], shouldProcess)))
                    {
                        blocksSynced++;
                    }

                    currentNumber = currentNumber + 1;
                }

                if (blocksSynced > 0)
                {
                    _syncStats.Update(_blockTree.BestSuggestedHeader?.Number ?? 0, bestPeer.HeadNumber, 1);
                }
            }

            return(blocksSynced);
        }
Beispiel #10
0
        public void AddNewBlock(Block block, Node nodeWhoSentTheBlock)
        {
            if (block.Number < _pivotNumber)
            {
                return;
            }

            if (block.TotalDifficulty == null)
            {
                throw new InvalidOperationException("Cannot add a block with unknown total difficulty");
            }

            _pool.TryFind(nodeWhoSentTheBlock.Id, out PeerInfo peerInfo);
            if (peerInfo == null)
            {
                string errorMessage = $"Received a new block from an unknown peer {nodeWhoSentTheBlock:c} {nodeWhoSentTheBlock.Id} {_pool.PeerCount}";
                if (_logger.IsDebug)
                {
                    _logger.Debug(errorMessage);
                }
                return;
            }

            if ((block.TotalDifficulty ?? 0) > peerInfo.TotalDifficulty)
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"ADD NEW BLOCK Updating header of {peerInfo} from {peerInfo.HeadNumber} {peerInfo.TotalDifficulty} to {block.Number} {block.TotalDifficulty}");
                }
                peerInfo.HeadNumber      = block.Number;
                peerInfo.HeadHash        = block.Hash;
                peerInfo.TotalDifficulty = block.TotalDifficulty ?? peerInfo.TotalDifficulty;
            }

            if ((block.TotalDifficulty ?? 0) < _blockTree.BestSuggestedHeader.TotalDifficulty)
            {
                return;
            }

            lock (_recentlySuggested)
            {
                if (_recentlySuggested.Get(block.Hash) != null)
                {
                    return;
                }
                _recentlySuggested.Set(block.Hash, _dummyValue);
            }

            if (block.Number > _blockTree.BestKnownNumber + 8)
            {
                // ignore blocks when syncing in a simple non-locking way
                _synchronizer.RequestSynchronization(SyncTriggerType.NewDistantBlock);
                return;
            }

            if (block.Number < (_blockTree.Head?.Number ?? 0) - 512)
            {
                return;
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Adding new block {block.ToString(Block.Format.Short)}) from {nodeWhoSentTheBlock:c}");
            }

            _sealValidator.HintValidationRange(_sealValidatorUserGuid, block.Number - 128, block.Number + 1024);
            if (!_sealValidator.ValidateSeal(block.Header, true))
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Peer {peerInfo.SyncPeer?.Node:c} sent a block with an invalid seal");
                }
                throw new EthSynchronizationException("Peer sent a block with an invalid seal");
            }

            if (block.Number <= _blockTree.BestKnownNumber + 1)
            {
                if (_logger.IsInfo)
                {
                    string authorString = block.Author == null ? null : "sealed by " + (KnownAddresses.GoerliValidators.ContainsKey(block.Author) ? KnownAddresses.GoerliValidators[block.Author] : block.Author?.ToString());
                    if (authorString == null)
                    {
                        authorString = block.Beneficiary == null ? string.Empty : "mined by " + (KnownAddresses.KnownMiners.ContainsKey(block.Beneficiary) ? KnownAddresses.KnownMiners[block.Beneficiary] : block.Beneficiary?.ToString());
                    }

                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Discovered a new block {string.Empty.PadLeft(9 - block.Number.ToString().Length, ' ')}{block.ToString(Block.Format.HashNumberAndTx)} {authorString}, sent by {nodeWhoSentTheBlock:s}");
                    }
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block}");
                }

                if (_synchronizer.SyncMode == SyncMode.Full)
                {
                    AddBlockResult result        = AddBlockResult.UnknownParent;
                    bool           isKnownParent = _blockTree.IsKnownBlock(block.Number - 1, block.ParentHash);
                    if (isKnownParent)
                    {
                        if (!_blockValidator.ValidateSuggestedBlock(block))
                        {
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"Peer {peerInfo.SyncPeer?.Node:c} sent an invalid block");
                            }
                            throw new EthSynchronizationException("Peer sent an invalid block");
                        }

                        result = _blockTree.SuggestBlock(block, true);
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                        }
                    }

                    if (result == AddBlockResult.UnknownParent)
                    {
                        _synchronizer.RequestSynchronization(SyncTriggerType.Reorganization);
                    }
                }
            }
            else
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Received a block {block.Hash} ({block.Number}) from {nodeWhoSentTheBlock} - need to resync");
                }
                _synchronizer.RequestSynchronization(SyncTriggerType.NewNearBlock);
            }
        }