Пример #1
0
        public static void ExtendTree(IBlockTree blockTree, int newChainLength)
        {
            Block previous      = blockTree.RetrieveHeadBlock();
            int   initialLength = (int)previous.Number + 1;

            for (int i = initialLength; i < newChainLength; i++)
            {
                previous = Build.A.Block.WithNumber(i).WithParent(previous).TestObject;
                blockTree.SuggestBlock(previous);
                blockTree.MarkAsProcessed(previous.Hash);
                blockTree.MoveToMain(previous.Hash);
            }
        }
Пример #2
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);
                }
            }
        }