示例#1
0
        public static void ExtendTree(IBlockTree blockTree, long newChainLength)
        {
            Block previous      = blockTree.RetrieveHeadBlock();
            long  initialLength = previous.Number + 1;

            for (long i = initialLength; i < newChainLength; i++)
            {
                previous = Build.A.Block.WithNumber(i).WithParent(previous).TestObject;
                blockTree.SuggestBlock(previous);
                blockTree.UpdateMainChain(new[] { previous }, true);
            }
        }
示例#2
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);
        }
 public static void AddBlock(IBlockTree blockTree, Block block)
 {
     blockTree.SuggestBlock(block);
     blockTree.UpdateMainChain(new[] { block }, true);
 }
示例#4
0
 public void UpdateMainChain(Block[] blocks, bool wereProcessed)
 {
     _blockTree.UpdateMainChain(blocks, wereProcessed);
 }
示例#5
0
 public void UpdateMainChain(Block[] blocks)
 {
     _blockTree.UpdateMainChain(blocks);
 }
示例#6
0
        public Block Process(Block suggestedBlock, ProcessingOptions options, IBlockTracer blockTracer)
        {
            if (!RunSimpleChecksAheadOfProcessing(suggestedBlock, options))
            {
                return(null);
            }

            UInt256 totalDifficulty = suggestedBlock.TotalDifficulty ?? 0;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Total difficulty of block {suggestedBlock.ToString(Block.Format.Short)} is {totalDifficulty}");
            }

            BlockHeader branchingPoint = null;

            Block[] processedBlocks = null;
            if (_blockTree.Head == null || totalDifficulty > _blockTree.Head.TotalDifficulty || (options & ProcessingOptions.ForceProcessing) != 0)
            {
                List <Block> blocksToBeAddedToMain = new List <Block>();
                Block        toBeProcessed         = suggestedBlock;
                do
                {
                    blocksToBeAddedToMain.Add(toBeProcessed);
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"To be processed (of {suggestedBlock.ToString(Block.Format.Short)}) is {toBeProcessed?.ToString(Block.Format.Short)}");
                    }
                    if (toBeProcessed.IsGenesis)
                    {
                        break;
                    }

                    branchingPoint = _blockTree.FindParentHeader(toBeProcessed.Header, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
                    if (branchingPoint == null)
                    {
                        break; //failure here
                    }

                    bool isFastSyncTransition = _blockTree.Head == _blockTree.Genesis && toBeProcessed.Number > 1;
                    if (!isFastSyncTransition)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Finding parent of {toBeProcessed.ToString(Block.Format.Short)}");
                        }
                        toBeProcessed = _blockTree.FindParent(toBeProcessed.Header, BlockTreeLookupOptions.None);
                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Found parent {toBeProcessed?.ToString(Block.Format.Short)}");
                        }

                        if (toBeProcessed == null)
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Trace($"Treating this as fast sync transition for {suggestedBlock.ToString(Block.Format.Short)}");
                            }
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                } while (!_blockTree.IsMainChain(branchingPoint.Hash));

                if (branchingPoint != null && branchingPoint.Hash != _blockTree.Head?.Hash)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Head block was: {_blockTree.Head?.ToString(BlockHeader.Format.Short)}");
                    }
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Branching from: {branchingPoint.ToString(BlockHeader.Format.Short)}");
                    }
                }
                else
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace(branchingPoint == null ? "Setting as genesis block" : $"Adding on top of {branchingPoint.ToString(BlockHeader.Format.Short)}");
                    }
                }

                Keccak stateRoot = branchingPoint?.StateRoot;
                if (_logger.IsTrace)
                {
                    _logger.Trace($"State root lookup: {stateRoot}");
                }

                List <Block> blocksToProcess = new List <Block>();
                Block[]      blocks;
                if ((options & ProcessingOptions.ForceProcessing) != 0)
                {
                    blocksToBeAddedToMain.Clear();
                    blocks    = new Block[1];
                    blocks[0] = suggestedBlock;
                }
                else
                {
                    foreach (Block block in blocksToBeAddedToMain)
                    {
                        if (block.Hash != null && _blockTree.WasProcessed(block.Number, block.Hash))
                        {
                            if (_logger.IsInfo)
                            {
                                _logger.Info($"Rerunning block after reorg: {block.ToString(Block.Format.FullHashAndNumber)}");
                            }
                        }

                        blocksToProcess.Add(block);
                    }

                    blocks = new Block[blocksToProcess.Count];
                    for (int i = 0; i < blocksToProcess.Count; i++)
                    {
                        blocks[blocks.Length - i - 1] = blocksToProcess[i];
                    }
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"Processing {blocks.Length} blocks from state root {stateRoot}");
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    /* this can happen if the block was loaded as an ancestor and did not go through the recovery queue */
                    _recoveryStep.RecoverData(blocks[i]);
                }

                try
                {
                    processedBlocks = _blockProcessor.Process(stateRoot, blocks, options, blockTracer);
                }
                catch (InvalidBlockException ex)
                {
                    for (int i = 0; i < blocks.Length; i++)
                    {
                        if (blocks[i].Hash == ex.InvalidBlockHash)
                        {
                            _blockTree.DeleteInvalidBlock(blocks[i]);
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"Skipped processing of {suggestedBlock.ToString(Block.Format.FullHashAndNumber)} because of {blocks[i].ToString(Block.Format.FullHashAndNumber)} is invalid");
                            }
                            return(null);
                        }
                    }
                }

                if ((options & ProcessingOptions.ReadOnlyChain) == 0)
                {
                    _blockTree.UpdateMainChain(blocksToBeAddedToMain.ToArray());
                }
            }
            else
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Skipped processing of {suggestedBlock.ToString(Block.Format.FullHashAndNumber)}, Head = {_blockTree.Head?.ToString(BlockHeader.Format.Short)}, total diff = {totalDifficulty}, head total diff = {_blockTree.Head?.TotalDifficulty}");
                }
            }

            Block lastProcessed = null;

            if (processedBlocks != null && processedBlocks.Length > 0)
            {
                lastProcessed = processedBlocks[^ 1];
示例#7
0
        public Block Process(Block suggestedBlock, ProcessingOptions options, IBlockTracer tracer)
        {
            if (!RunSimpleChecksAheadOfProcessing(suggestedBlock, options))
            {
                return(null);
            }

            UInt256 totalDifficulty = suggestedBlock.TotalDifficulty ?? 0;

            if (_logger.IsTrace)
            {
                _logger.Trace($"Total difficulty of block {suggestedBlock.ToString(Block.Format.Short)} is {totalDifficulty}");
            }


            Block[] processedBlocks = null;
            bool    shouldProcess   = suggestedBlock.IsGenesis ||
                                      totalDifficulty > (_blockTree.Head?.TotalDifficulty ?? 0)
                                      // so above is better and more correct but creates an impression of the node staying behind on stats page
                                      // so we are okay to process slightly more
                                      // and below is less correct but potentially reporting well
                                      // || totalDifficulty >= (_blockTree.Head?.TotalDifficulty ?? 0)
                                      || (options & ProcessingOptions.ForceProcessing) == ProcessingOptions.ForceProcessing;

            if (!shouldProcess)
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Skipped processing of {suggestedBlock.ToString(Block.Format.FullHashAndNumber)}, Head = {_blockTree.Head?.Header?.ToString(BlockHeader.Format.Short)}, total diff = {totalDifficulty}, head total diff = {_blockTree.Head?.TotalDifficulty}");
                }
                return(null);
            }

            ProcessingBranch processingBranch = PrepareProcessingBranch(suggestedBlock, options);

            PrepareBlocksToProcess(suggestedBlock, options, processingBranch);

            try
            {
                processedBlocks = _blockProcessor.Process(processingBranch.Root, processingBranch.BlocksToProcess, options, tracer);
            }
            catch (InvalidBlockException ex)
            {
                for (int i = 0; i < processingBranch.BlocksToProcess.Count; i++)
                {
                    if (processingBranch.BlocksToProcess[i].Hash == ex.InvalidBlockHash)
                    {
                        _blockTree.DeleteInvalidBlock(processingBranch.BlocksToProcess[i]);
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"Skipped processing of {suggestedBlock.ToString(Block.Format.FullHashAndNumber)} because of {processingBranch.BlocksToProcess[i].ToString(Block.Format.FullHashAndNumber)} is invalid");
                        }
                        return(null);
                    }
                }
            }

            if ((options & (ProcessingOptions.ReadOnlyChain | ProcessingOptions.DoNotUpdateHead)) == 0)
            {
                _blockTree.UpdateMainChain(processingBranch.Blocks.ToArray(), true);
            }

            Block lastProcessed = null;

            if (processedBlocks != null && processedBlocks.Length > 0)
            {
                lastProcessed = processedBlocks[^ 1];