public Keccak GetBlockhash(BlockHeader currentBlock, UInt256 number) { UInt256 current = currentBlock.Number; if (number >= current || number < current - UInt256.Min(current, (UInt256)_maxDepth)) { return(null); } BlockHeader header = _chain.FindHeader(currentBlock.ParentHash); for (var i = 0; i < _maxDepth; i++) { if (number == header.Number) { return(header.Hash); } header = _chain.FindHeader(header.ParentHash); if (_chain.IsMainChain(header.Hash)) { header = _chain.FindHeader(number); } } return(null); }
private GethLikeTxTrace[] TraceBlock(Block?block, GethTraceOptions options, CancellationToken cancellationToken, Keccak?txHash = null) { if (block == null) { throw new InvalidOperationException("Only canonical, historical blocks supported"); } if (!block.IsGenesis) { BlockHeader?parent = _blockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None); if (parent?.Hash is null) { throw new InvalidOperationException("Cannot trace blocks with invalid parents"); } if (!_blockTree.IsMainChain(parent.Hash)) { throw new InvalidOperationException("Cannot trace orphaned blocks"); } } GethLikeBlockTracer listener = txHash == null ? new GethLikeBlockTracer(options) : new GethLikeBlockTracer(txHash, options); _processor.Process(block, ProcessingOptions.Trace, listener.WithCancellation(cancellationToken)); return(listener.BuildResult().ToArray()); }
public override async Task <BlockVisitOutcome> VisitBlock(Block block, CancellationToken cancellationToken) { BlockVisitOutcome outcome = await base.VisitBlock(block, cancellationToken); if (_blockTree.IsMainChain(block.Header)) { _receiptStorage.EnsureCanonical(block); } return(outcome); }
private GethLikeTxTrace[] TraceBlock(Block block) { if (block == null) { throw new InvalidOperationException("Only canonical, historical blocks supported"); } if (block.Number != 0) { Block parent = _blockTree.FindParent(block); if (!_blockTree.IsMainChain(parent.Hash)) { throw new InvalidOperationException("Cannot trace orphaned blocks"); } } GethLikeBlockTracer listener = new GethLikeBlockTracer(); _processor.Process(block, ProcessingOptions.ForceProcessing | ProcessingOptions.WithRollback | ProcessingOptions.ReadOnlyChain | ProcessingOptions.NoValidation, listener); return(listener.BuildResult().ToArray()); }
public async Task Happy_path(long headNumber, int options) { BlockDownloader.DownloadOptions downloadOptions = (BlockDownloader.DownloadOptions)options; bool withReceipts = downloadOptions == BlockDownloader.DownloadOptions.DownloadWithReceipts; InMemoryReceiptStorage inMemoryReceiptStorage = new InMemoryReceiptStorage(); BlockDownloader blockDownloader = new BlockDownloader(_blockTree, TestBlockValidator.AlwaysValid, TestSealValidator.AlwaysValid, NullSyncReport.Instance, inMemoryReceiptStorage, RopstenSpecProvider.Instance, LimboLogs.Instance); ISyncPeer syncPeer = Substitute.For <ISyncPeer>(); Task <BlockHeader[]> buildHeadersResponse = null; syncPeer.GetBlockHeaders(Arg.Any <long>(), Arg.Any <int>(), Arg.Any <int>(), Arg.Any <CancellationToken>()) .Returns(ci => buildHeadersResponse = _responseBuilder.BuildHeaderResponse(ci.ArgAt <long>(0), ci.ArgAt <int>(1), Response.AllCorrect)); Response blockResponseOptions = Response.AllCorrect; if (withReceipts) { blockResponseOptions |= Response.WithTransactions; } Task <BlockBody[]> buildBlocksResponse = null; syncPeer.GetBlocks(Arg.Any <IList <Keccak> >(), Arg.Any <CancellationToken>()) .Returns(ci => buildBlocksResponse = _responseBuilder.BuildBlocksResponse(ci.ArgAt <IList <Keccak> >(0), blockResponseOptions)); syncPeer.GetReceipts(Arg.Any <IList <Keccak> >(), Arg.Any <CancellationToken>()) .Returns(ci => _responseBuilder.BuildReceiptsResponse(buildHeadersResponse.Result, buildBlocksResponse.Result)); PeerInfo peerInfo = new PeerInfo(syncPeer); peerInfo.TotalDifficulty = UInt256.MaxValue; peerInfo.HeadNumber = headNumber; await blockDownloader.DownloadHeaders(peerInfo, SyncModeSelector.FullSyncThreshold, CancellationToken.None); Assert.AreEqual(Math.Max(0, headNumber - SyncModeSelector.FullSyncThreshold), _blockTree.BestSuggestedHeader.Number, "headers"); peerInfo.HeadNumber *= 2; await blockDownloader.DownloadBlocks(peerInfo, 0, CancellationToken.None, downloadOptions); _blockTree.BestSuggestedHeader.Number.Should().Be(Math.Max(0, headNumber * 2)); _blockTree.IsMainChain(_blockTree.BestSuggestedHeader.Hash).Should().Be(downloadOptions != BlockDownloader.DownloadOptions.DownloadAndProcess); inMemoryReceiptStorage.Count.Should().Be(withReceipts ? buildBlocksResponse.Result.Sum(b => b.Transactions?.Length ?? 0) : 0); }
private GethLikeTxTrace[] TraceBlock(Block block, GethTraceOptions options, Keccak txHash = null) { if (block == null) { throw new InvalidOperationException("Only canonical, historical blocks supported"); } if (block.Number != 0) { BlockHeader parent = _blockTree.FindParentHeader(block.Header, BlockTreeLookupOptions.None); if (!_blockTree.IsMainChain(parent.Hash)) { throw new InvalidOperationException("Cannot trace orphaned blocks"); } } GethLikeBlockTracer listener = txHash == null ? new GethLikeBlockTracer(options) : new GethLikeBlockTracer(txHash, options); _processor.Process(block, ProcessingOptions.Trace, listener); return(listener.BuildResult().ToArray()); }
public FullTransaction MapFullTransaction(Kafka.Models.FullTransaction fullTransaction) { var index = fullTransaction.Index; var transaction = fullTransaction.Transaction; var receipt = fullTransaction.Receipt; var removed = !_blockTree.IsMainChain(receipt.BlockHash); return(new FullTransaction { minedAt = (long)_blockTree.FindBlock(receipt.BlockHash, BlockTreeLookupOptions.None).Timestamp, blockNumber = (long)receipt.BlockNumber, receipt = new Receipt { transactionIndex = index, blockNumber = (long)receipt.BlockNumber, toAddr = receipt.Recipient?.ToString() ?? string.Empty, blockHash = receipt.BlockHash.ToString(), fromAddr = receipt.Sender?.ToString() ?? string.Empty, logsBloom = receipt.Bloom.ToString(), gasUsed = receipt.GasUsed, contractAddress = receipt.ContractAddress?.ToString() ?? string.Empty, transactionHash = receipt.TxHash.ToString(), cumulativeGasUsed = receipt.GasUsedTotal, status = receipt.StatusCode, logs = receipt.Logs?.Select((l, i) => new Log { logIndex = i, blockNumber = (long)receipt.BlockNumber, transactionIndex = receipt.Index, blockHash = receipt.BlockHash.ToString(), data = l.Data.ToString(), transactionHash = receipt.TxHash.ToString(), address = l.LoggersAddress.ToString(), logTopics = l.Topics?.Select(t => t.ToString()).ToList() ?? new List <string>(), removed = removed }).ToList() ?? new List <Log>() }, tx = MapTransaction(index, receipt.BlockNumber, receipt.BlockHash, transaction) }); }
public bool IsMainChain(BlockHeader blockHeader) => _blockTree.IsMainChain(blockHeader);
public bool IsMainChain(BlockHeader blockHeader) { return(_blockTree.IsMainChain(blockHeader)); }
public bool IsMainChain(BlockHeader blockHeader) => _wrapped.IsMainChain(blockHeader);
public bool IsMainChain(Keccak blockHash) { return(_blockTree.IsMainChain(blockHash)); }
public bool IsMainChain(Keccak blockHash) { return(_wrapped.IsMainChain(blockHash)); }
public static bool IsOnMainChainBehindHead(this IBlockTree blockTree, Block block) => block.Number < (blockTree.Head?.Number ?? 0) && blockTree.IsMainChain(block.Header);
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 bool IsInconsistent(Keccak blockHash) => blockHash != Keccak.Zero && !_blockTree.IsMainChain(blockHash);
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); } } }