예제 #1
0
 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;
     }
 }
예제 #2
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];
예제 #3
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];
예제 #4
0
        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);
                }
            }
        }