diff --git a/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs b/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs index 1e89583e906..3e240bef779 100644 --- a/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs +++ b/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Consensus.Processing; @@ -27,6 +28,7 @@ namespace Nethermind.Consensus.Tracing; public class GethStyleTracer : IGethStyleTracer { + private readonly IBlockStore _badBlockStore; private readonly IBlockTree _blockTree; private readonly ISpecProvider _specProvider; private readonly ChangeableTransactionProcessorAdapter _transactionProcessorAdapter; @@ -39,6 +41,7 @@ public GethStyleTracer(IBlockchainProcessor processor, IWorldState worldState, IReceiptStorage receiptStorage, IBlockTree blockTree, + IBlockStore badBlockStore, ISpecProvider specProvider, ChangeableTransactionProcessorAdapter transactionProcessorAdapter, IFileSystem fileSystem) @@ -47,6 +50,7 @@ public GethStyleTracer(IBlockchainProcessor processor, _worldState = worldState; _receiptStorage = receiptStorage ?? throw new ArgumentNullException(nameof(receiptStorage)); _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); + _badBlockStore = badBlockStore ?? throw new ArgumentNullException(nameof(badBlockStore)); _specProvider = specProvider; _transactionProcessorAdapter = transactionProcessorAdapter; _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); @@ -55,7 +59,7 @@ public GethStyleTracer(IBlockchainProcessor processor, public GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions options, CancellationToken cancellationToken) { Block block = _blockTree.FindBlock(blockHash, BlockTreeLookupOptions.None); - if (block is null) throw new InvalidOperationException("Only historical blocks"); + if (block is null) throw new InvalidOperationException($"No historical block found for {blockHash}"); if (txIndex > block.Transactions.Length - 1) throw new InvalidOperationException($"Block {blockHash} has only {block.Transactions.Length} transactions and the requested tx index was {txIndex}"); @@ -106,7 +110,7 @@ public GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions op public GethLikeTxTrace? Trace(long blockNumber, int txIndex, GethTraceOptions options, CancellationToken cancellationToken) { Block block = _blockTree.FindBlock(blockNumber, BlockTreeLookupOptions.RequireCanonical); - if (block is null) throw new InvalidOperationException("Only historical blocks"); + if (block is null) throw new InvalidOperationException($"No historical block found for {blockNumber}"); if (txIndex > block.Transactions.Length - 1) throw new InvalidOperationException($"Block {blockNumber} has only {block.Transactions.Length} transactions and the requested tx index was {txIndex}"); @@ -116,7 +120,7 @@ public GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions op public GethLikeTxTrace? Trace(long blockNumber, Transaction tx, GethTraceOptions options, CancellationToken cancellationToken) { Block block = _blockTree.FindBlock(blockNumber, BlockTreeLookupOptions.RequireCanonical); - if (block is null) throw new InvalidOperationException("Only historical blocks"); + if (block is null) throw new InvalidOperationException($"No historical block found for {blockNumber}"); if (tx.Hash is null) throw new InvalidOperationException("Cannot trace transactions without tx hash set."); block = block.WithReplacedBodyCloned(BlockBody.WithOneTransactionOnly(tx)); @@ -150,7 +154,7 @@ public IEnumerable TraceBlockToFile(Hash256 blockHash, GethTraceOptions ArgumentNullException.ThrowIfNull(blockHash); ArgumentNullException.ThrowIfNull(options); - var block = _blockTree.FindBlock(blockHash) ?? throw new InvalidOperationException("Only historical blocks"); + var block = _blockTree.FindBlock(blockHash) ?? throw new InvalidOperationException($"No historical block found for {blockHash}"); if (!block.IsGenesis) { @@ -167,6 +171,24 @@ public IEnumerable TraceBlockToFile(Hash256 blockHash, GethTraceOptions return tracer.FileNames; } + public IEnumerable TraceBadBlockToFile(Hash256 blockHash, GethTraceOptions options, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(blockHash); + ArgumentNullException.ThrowIfNull(options); + + var block = _badBlockStore + .GetAll() + .Where(b => b.Hash == blockHash) + .FirstOrDefault() + ?? throw new InvalidOperationException($"No historical block found for {blockHash}"); + + var tracer = new GethLikeBlockFileTracer(block, options, _fileSystem); + + _processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken)); + + return tracer.FileNames; + } + private GethLikeTxTrace? Trace(Block block, Hash256? txHash, CancellationToken cancellationToken, GethTraceOptions options) { ArgumentNullException.ThrowIfNull(txHash); diff --git a/src/Nethermind/Nethermind.Consensus/Tracing/IGethStyleTracer.cs b/src/Nethermind/Nethermind.Consensus/Tracing/IGethStyleTracer.cs index 159ec97e81c..4651add653e 100644 --- a/src/Nethermind/Nethermind.Consensus/Tracing/IGethStyleTracer.cs +++ b/src/Nethermind/Nethermind.Consensus/Tracing/IGethStyleTracer.cs @@ -22,4 +22,5 @@ public interface IGethStyleTracer IReadOnlyCollection TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken); IReadOnlyCollection TraceBlock(Rlp blockRlp, GethTraceOptions options, CancellationToken cancellationToken); IEnumerable TraceBlockToFile(Hash256 blockHash, GethTraceOptions options, CancellationToken cancellationToken); + IEnumerable TraceBadBlockToFile(Hash256 blockHash, GethTraceOptions options, CancellationToken cancellationToken); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index e66d93d334f..00faf0cd169 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -467,6 +467,25 @@ static IEnumerable GetFileNames(Hash256 hash) => actual.Should().BeEquivalentTo(expected); } + [Test] + public void StandardTraceBadBlockToFile() + { + var blockHash = Keccak.EmptyTreeHash; + + static IEnumerable GetFileNames(Hash256 hash) => + new[] { $"block_{hash.ToShortString()}-0", $"block_{hash.ToShortString()}-1" }; + + debugBridge + .TraceBadBlockToFile(Arg.Is(blockHash), Arg.Any(), Arg.Any()) + .Returns(c => GetFileNames(c.ArgAt(0))); + + var rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); + var actual = rpcModule.debug_standardTraceBadBlockToFile(blockHash); + var expected = ResultWrapper>.Success(GetFileNames(blockHash)); + + actual.Should().BeEquivalentTo(expected); + } + [Test] public void TraceBlockByHash_Success() { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs index 137939bcf6a..dd7d34ebc82 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs @@ -206,4 +206,10 @@ public IEnumerable TraceBlockToFile( CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => _tracer.TraceBlockToFile(blockHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); + + public IEnumerable TraceBadBlockToFile( + Hash256 blockHash, + CancellationToken cancellationToken, + GethTraceOptions? gethTraceOptions = null) => + _tracer.TraceBadBlockToFile(blockHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs index 3716de73a7d..a394e5a36ea 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs @@ -106,6 +106,7 @@ public override IDebugRpcModule Create() scope.WorldState, _receiptStorage, _blockTree, + _badBlockStore, _specProvider, transactionProcessorAdapter, _fileSystem); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs index 00f5927af22..9c0b2919d32 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs @@ -361,6 +361,18 @@ public ResultWrapper> debug_standardTraceBlockToFile(Hash256 return ResultWrapper>.Success(files); } + public ResultWrapper> debug_standardTraceBadBlockToFile(Hash256 blockHash, GethTraceOptions options = null) + { + using var cancellationTokenSource = new CancellationTokenSource(_traceTimeout); + var cancellationToken = cancellationTokenSource.Token; + + var files = _debugBridge.TraceBadBlockToFile(blockHash, cancellationToken, options); + + if (_logger.IsTrace) _logger.Trace($"{nameof(debug_standardTraceBadBlockToFile)} request {blockHash}, result: {files}"); + + return ResultWrapper>.Success(files); + } + public ResultWrapper> debug_getBadBlocks() { IEnumerable badBlocks = _debugBridge.GetBadBlocks().Select(block => new BadBlock(block, true, _specProvider, _blockDecoder)); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs index 73cc66f74b0..9b8db38d2cf 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs @@ -35,6 +35,7 @@ public interface IDebugBridge void InsertReceipts(BlockParameter blockParameter, TxReceipt[] receipts); SyncReportSymmary GetCurrentSyncStage(); IEnumerable TraceBlockToFile(Hash256 blockHash, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null); + IEnumerable TraceBadBlockToFile(Hash256 blockHash, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null); public IEnumerable GetBadBlocks(); TxReceipt[]? GetReceiptsForBlock(BlockParameter param); Transaction? GetTransactionFromHash(Hash256 hash); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs index 6615e53f408..dc0a8bd9014 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs @@ -109,6 +109,10 @@ public interface IDebugRpcModule : IRpcModule IsImplemented = true, IsSharable = false)] ResultWrapper> debug_standardTraceBlockToFile(Hash256 blockHash, GethTraceOptions options = null); + [JsonRpcMethod(Description = "This method is similar to the `debug_standardTraceBlockToFile` method, but can be used to obtain information about a block that has been rejected as invalid.", + IsImplemented = true, IsSharable = false)] + ResultWrapper> debug_standardTraceBadBlockToFile(Hash256 blockHash, GethTraceOptions options = null); + [JsonRpcMethod(Description = "Return list of invalid blocks.")] ResultWrapper> debug_getBadBlocks(); }