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); } }
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); }
public void UpdateMainChain(Block[] blocks, bool wereProcessed) { _blockTree.UpdateMainChain(blocks, wereProcessed); }
public void UpdateMainChain(Block[] blocks) { _blockTree.UpdateMainChain(blocks); }
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];
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];