private void Processing() { try { while (_processingBuffer.TryDequeue(out var blockToProcess) && _error == null) { var processedBlock = _processor.Process(blockToProcess); _writingBuffer.Add(processedBlock.Id, processedBlock); } } catch (Exception e) { _error = e; } }
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];
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];
private void Process(Block suggestedBlock, bool forMining) { if (suggestedBlock.Number != 0 && _blockTree.FindParent(suggestedBlock) == null) { throw new InvalidOperationException("Got an orphaned block for porcessing."); } if (suggestedBlock.Header.TotalDifficulty == null) { throw new InvalidOperationException("block without total difficulty calculated was suggested for processing"); } if (!forMining && suggestedBlock.Hash == null) { throw new InvalidOperationException("block hash should be known at this stage if the block is not mining"); } foreach (BlockHeader ommerHeader in suggestedBlock.Ommers) { if (ommerHeader.Hash == null) { throw new InvalidOperationException("ommer's hash is null when processing block"); } } BigInteger totalDifficulty = suggestedBlock.TotalDifficulty ?? 0; BigInteger totalTransactions = suggestedBlock.TotalTransactions ?? 0; if (_logger.IsDebugEnabled) { _logger.Debug($"Total difficulty of block {suggestedBlock.ToString(Block.Format.Short)} is {totalDifficulty}"); _logger.Debug($"Total transactions of block {suggestedBlock.ToString(Block.Format.Short)} is {totalTransactions}"); } if (totalDifficulty > (_blockTree.Head?.TotalDifficulty ?? 0)) { List <Block> blocksToBeAddedToMain = new List <Block>(); Block toBeProcessed = suggestedBlock; do { blocksToBeAddedToMain.Add(toBeProcessed); toBeProcessed = toBeProcessed.Number == 0 ? null : _blockTree.FindParent(toBeProcessed); // TODO: need to remove the hardcoded head block store at keccak zero as it would be referenced by the genesis... if (toBeProcessed == null) { break; } } while (!_blockTree.IsMainChain(toBeProcessed.Hash)); BlockHeader branchingPoint = toBeProcessed?.Header; if (branchingPoint != null && branchingPoint.Hash != _blockTree.Head?.Hash) { if (_logger.IsDebugEnabled) { _logger.Debug($"Head block was: {_blockTree.Head?.ToString(BlockHeader.Format.Short)}"); _logger.Debug($"Branching from: {branchingPoint.ToString(BlockHeader.Format.Short)}"); } } else { if (_logger.IsDebugEnabled) { _logger.Debug(branchingPoint == null ? "Setting as genesis block" : $"Adding on top of {branchingPoint.ToString(BlockHeader.Format.Short)}"); } } Keccak stateRoot = branchingPoint?.StateRoot; if (_logger.IsTraceEnabled) { _logger.Trace($"State root lookup: {stateRoot}"); } List <Block> unprocessedBlocksToBeAddedToMain = new List <Block>(); foreach (Block block in blocksToBeAddedToMain) { if (!forMining && _blockTree.WasProcessed(block.Hash)) { stateRoot = block.Header.StateRoot; if (_logger.IsTraceEnabled) { _logger.Trace($"State root lookup: {stateRoot}"); } break; } unprocessedBlocksToBeAddedToMain.Add(block); } Block[] blocks = new Block[unprocessedBlocksToBeAddedToMain.Count]; for (int i = 0; i < unprocessedBlocksToBeAddedToMain.Count; i++) { blocks[blocks.Length - i - 1] = unprocessedBlocksToBeAddedToMain[i]; } if (_logger.IsDebugEnabled) { _logger.Debug($"Processing {blocks.Length} blocks from state root {stateRoot}"); } //TODO: process blocks one by one here, refactor this, test for (int i = 0; i < blocks.Length; i++) { if (blocks[i].Transactions.Length > 0 && blocks[i].Transactions[0].SenderAddress == null) { _signer.RecoverAddresses(blocks[i]); } } Block[] processedBlocks = _blockProcessor.Process(stateRoot, blocks, forMining); // TODO: lots of unnecessary loading and decoding here, review after adding support for loading headers only List <BlockHeader> blocksToBeRemovedFromMain = new List <BlockHeader>(); if (_blockTree.Head?.Hash != branchingPoint?.Hash && _blockTree.Head != null) { blocksToBeRemovedFromMain.Add(_blockTree.Head); BlockHeader teBeRemovedFromMain = _blockTree.FindHeader(_blockTree.Head.ParentHash); while (teBeRemovedFromMain != null && teBeRemovedFromMain.Hash != branchingPoint?.Hash) { blocksToBeRemovedFromMain.Add(teBeRemovedFromMain); teBeRemovedFromMain = _blockTree.FindHeader(teBeRemovedFromMain.ParentHash); } } if (!forMining) { foreach (Block processedBlock in processedBlocks) { if (_logger.IsDebugEnabled) { _logger.Debug($"Marking {processedBlock.ToString(Block.Format.Short)} as processed"); } // TODO: review storage and retrieval of receipts since we removed them from the block class _blockTree.MarkAsProcessed(processedBlock.Hash); } Block newHeadBlock = processedBlocks[processedBlocks.Length - 1]; newHeadBlock.Header.TotalDifficulty = suggestedBlock.TotalDifficulty; // TODO: cleanup total difficulty if (_logger.IsDebugEnabled) { _logger.Debug($"Setting head block to {newHeadBlock.ToString(Block.Format.Short)}"); } foreach (BlockHeader blockHeader in blocksToBeRemovedFromMain) { if (_logger.IsDebugEnabled) { _logger.Debug($"Moving {blockHeader.ToString(BlockHeader.Format.Short)} to branch"); } _blockTree.MoveToBranch(blockHeader.Hash); // TODO: only for miners //foreach (Transaction transaction in block.Transactions) //{ // _transactionStore.AddPending(transaction); //} if (_logger.IsDebugEnabled) { _logger.Debug($"Block {blockHeader.ToString(BlockHeader.Format.Short)} moved to branch"); } } foreach (Block block in blocksToBeAddedToMain) { if (_logger.IsDebugEnabled) { _logger.Debug($"Moving {block.ToString(Block.Format.Short)} to main"); } _blockTree.MoveToMain(block); // TODO: only for miners foreach (Transaction transaction in block.Transactions) { _transactionStore.RemovePending(transaction); } if (_logger.IsDebugEnabled) { _logger.Debug($"Block {block.ToString(Block.Format.Short)} added to main chain"); } } if (_logger.IsDebugEnabled) { _logger.Debug($"Updating total difficulty of the main chain to {totalDifficulty}"); } if (_logger.IsDebugEnabled) { _logger.Debug($"Updating total transactions of the main chain to {totalTransactions}"); } } else { Block blockToBeMined = processedBlocks[processedBlocks.Length - 1]; _miningCancellation = new CancellationTokenSource(); CancellationTokenSource anyCancellation = CancellationTokenSource.CreateLinkedTokenSource(_miningCancellation.Token, _loopCancellationSource.Token); _sealEngine.MineAsync(blockToBeMined, anyCancellation.Token).ContinueWith(t => { anyCancellation.Dispose(); if (_logger.IsInfoEnabled) { _logger.Info($"Mined a block {t.Result.ToString(Block.Format.Short)} with parent {t.Result.Header.ParentHash}"); } Block minedBlock = t.Result; if (minedBlock.Hash == null) { throw new InvalidOperationException("Mined a block with null hash"); } _blockTree.SuggestBlock(minedBlock); }, _miningCancellation.Token); } } }