Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6662 debug trace call many #7441

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ public virtual async Task Execute(CancellationToken cancellationToken)
_api.SyncModeSelector,
_api.BadBlocksStore,
_api.FileSystem,
_api.LogManager);
_api.LogManager,
_api.StateReader);
rpcModuleProvider.RegisterBoundedByCpuCount(debugModuleFactory, _jsonRpcConfig.Timeout);

RegisterTraceRpcModule(rpcModuleProvider);
Expand Down
103 changes: 103 additions & 0 deletions src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,109 @@ public void Debug_traceCall_test()
debugTraceCall.Should().BeEquivalentTo(expected);
}

[Test]
public void Debug_traceCallMany_test()
{
GethTxTraceEntry entry = new();

entry.Storage = new Dictionary<string, string>
{
{"1".PadLeft(64, '0'), "2".PadLeft(64, '0')},
{"3".PadLeft(64, '0'), "4".PadLeft(64, '0')},
};

entry.Memory = new string[]
{
"5".PadLeft(64, '0'),
"6".PadLeft(64, '0')
};
entry.Stack = new string[] { };
entry.Opcode = "STOP";
entry.Gas = 22000;
entry.GasCost = 1;
entry.Depth = 1;

var traces = new List<GethLikeTxTrace>();
traces.Add(new GethLikeTxTrace()
{
ReturnValue = Bytes.FromHexString("a2"),
Entries = new List<GethTxTraceEntry>(),
});
traces[0].Entries.Add(entry);


GethTraceOptions gtOptions = new();

Transaction transaction = Build.A.Transaction.WithTo(TestItem.AddressA).WithHash(TestItem.KeccakA).TestObject;
TransactionForRpc txForRpc = new(transaction);
TransactionForRpcWithTraceTypes transactionForRpcWithTraceTypes = new();
transactionForRpcWithTraceTypes.Transaction = txForRpc;
transactionForRpcWithTraceTypes.TraceTypes = new string[1];

BlockHeader blockHeader = new BlockHeader(
new Hash256("0x0000000000000000000000000000000000000000000000000000000000000000"),
new Hash256("0x0000000000000000000000000000000000000000000000000000000000000000"),
new Address("0xfffffffffffffffffffffffffffffffffffffffe"),
1000000,
123456,
8000000,
1694890200,
new byte[] { 0x54, 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61 },
500000,
100000,
new Hash256("0x0000000000000000000000000000000000000000000000000000000000000000")
);

debugBridge.GetBlockTrace(Arg.Any<BlockParameter>(), Arg.Any<CancellationToken>(), Arg.Any<GethTraceOptions>()).Returns(traces);
debugBridge.HasStateForBlock(Arg.Any<BlockHeader>()).Returns(true);
debugBridge.SearchBlockHeaderForTraceCall(Arg.Any<BlockParameter>()).Returns(new JsonRpc.Modules.SearchResult<BlockHeader>(blockHeader));

DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider);
ResultWrapper<IEnumerable<GethLikeTxTrace>> debug_traceCallMany_output = rpcModule.debug_traceCallMany(new TransactionForRpcWithTraceTypes[] { transactionForRpcWithTraceTypes }, null);


var expected = ResultWrapper<IEnumerable<GethLikeTxTrace>>.Success(
new List<GethLikeTxTrace>()
{
new GethLikeTxTrace()
{
Failed = false,
Entries = new List<GethTxTraceEntry>()
{
new GethTxTraceEntry()
{
Gas = 22000,
GasCost = 1,
Depth = 1,
Memory = new string[]
{
"0000000000000000000000000000000000000000000000000000000000000005",
"0000000000000000000000000000000000000000000000000000000000000006"
},
Opcode = "STOP",
ProgramCounter = 0,
Stack = Array.Empty<string>(),
Storage = new Dictionary<string, string>()
{
{
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000002"
},
{
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000004"
},
}
}
},
Gas = 0,
ReturnValue = new byte[] { 162 }
}
});

debug_traceCallMany_output.Should().BeEquivalentTo(expected);
}

[Test]
public async Task Migrate_receipts()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
using Nethermind.Core.Specs;
using Nethermind.Db;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Int256;
using Nethermind.Serialization.Rlp;
using Nethermind.State;
using Nethermind.State.Proofs;
using Nethermind.Synchronization.ParallelSync;
using Nethermind.Synchronization.Reporting;
Expand All @@ -36,6 +38,7 @@ public class DebugBridge : IDebugBridge
private readonly IBlockStore _badBlockStore;
private readonly IBlockStore _blockStore;
private readonly Dictionary<string, IDb> _dbMappings;
private readonly IStateReader _stateReader;

public DebugBridge(
IConfigProvider configProvider,
Expand All @@ -46,7 +49,8 @@ public DebugBridge(
IReceiptsMigration receiptsMigration,
ISpecProvider specProvider,
ISyncModeSelector syncModeSelector,
IBlockStore badBlockStore)
IBlockStore badBlockStore,
IStateReader stateReader)
{
_configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider));
_tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
Expand All @@ -56,12 +60,14 @@ public DebugBridge(
_specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider));
_syncModeSelector = syncModeSelector ?? throw new ArgumentNullException(nameof(syncModeSelector));
_badBlockStore = badBlockStore;
_stateReader = stateReader ?? throw new ArgumentNullException(nameof(stateReader));
dbProvider = dbProvider ?? throw new ArgumentNullException(nameof(dbProvider));
IDb blockInfosDb = dbProvider.BlockInfosDb ?? throw new ArgumentNullException(nameof(dbProvider.BlockInfosDb));
IDb blocksDb = dbProvider.BlocksDb ?? throw new ArgumentNullException(nameof(dbProvider.BlocksDb));
IDb headersDb = dbProvider.HeadersDb ?? throw new ArgumentNullException(nameof(dbProvider.HeadersDb));
IDb codeDb = dbProvider.CodeDb ?? throw new ArgumentNullException(nameof(dbProvider.CodeDb));
IDb metadataDb = dbProvider.MetadataDb ?? throw new ArgumentNullException(nameof(dbProvider.MetadataDb));


_dbMappings = new Dictionary<string, IDb>(StringComparer.InvariantCultureIgnoreCase)
{
Expand Down Expand Up @@ -212,4 +218,38 @@ public IEnumerable<string> TraceBadBlockToFile(
CancellationToken cancellationToken,
GethTraceOptions? gethTraceOptions = null) =>
_tracer.TraceBadBlockToFile(blockHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken);

public SearchResult<BlockHeader> SearchBlockHeaderForTraceCall(BlockParameter blockParameter)
{
SearchResult<BlockHeader> headerSearch = _blockTree.SearchForHeader(blockParameter);
if (headerSearch.IsError)
{
return headerSearch;
}

BlockHeader header = headerSearch.Object;
if (header!.IsGenesis)
{
UInt256 baseFee = header.BaseFeePerGas;
header = new BlockHeader(
header.Hash!,
Keccak.OfAnEmptySequenceRlp,
Address.Zero,
header.Difficulty,
header.Number + 1,
header.GasLimit,
header.Timestamp + 1,
header.ExtraData,
header.BlobGasUsed,
header.ExcessBlobGas);

header.TotalDifficulty = 2 * header.Difficulty;
header.BaseFeePerGas = baseFee;
}

return new SearchResult<BlockHeader>(header);
}

public bool HasStateForBlock(BlockHeader header) => _stateReader.HasStateForBlock(header);

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO.Abstractions;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Find;
using Nethermind.Blockchain.Receipts;
using Nethermind.Config;
using Nethermind.Consensus.Processing;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class DebugModuleFactory : ModuleFactoryBase<IDebugRpcModule>
private readonly IBlockStore _badBlockStore;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
private readonly IStateReader _stateReader;

public DebugModuleFactory(
IWorldStateManager worldStateManager,
Expand All @@ -57,7 +59,8 @@ public DebugModuleFactory(
ISyncModeSelector syncModeSelector,
IBlockStore badBlockStore,
IFileSystem fileSystem,
ILogManager logManager)
ILogManager logManager,
IStateReader stateReader)
{
_worldStateManager = worldStateManager;
_dbProvider = dbProvider.AsReadOnly(false);
Expand All @@ -75,6 +78,7 @@ public DebugModuleFactory(
_badBlockStore = badBlockStore;
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
_logger = logManager.GetClassLogger();
_stateReader = stateReader ?? throw new ArgumentNullException(nameof(stateReader));
}

public override IDebugRpcModule Create()
Expand Down Expand Up @@ -120,7 +124,8 @@ public override IDebugRpcModule Create()
_receiptsMigration,
_specProvider,
_syncModeSelector,
_badBlockStore);
_badBlockStore,
_stateReader);

return new DebugRpcModule(_logManager, debugBridge, _jsonRpcConfig, _specProvider);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@
using System.Collections.Generic;
using Nethermind.JsonRpc.Modules.Eth;
using Nethermind.Core.Specs;

using Nethermind.Facade.Eth;
using Nethermind.Evm.Tracing.ParityStyle;
using Nethermind.Int256;
using Newtonsoft.Json;
using Nethermind.State;

using Nethermind.Facade.Eth.RpcTransaction;


namespace Nethermind.JsonRpc.Modules.DebugModule;

public class DebugRpcModule : IDebugRpcModule
Expand Down Expand Up @@ -85,6 +93,45 @@ public ResultWrapper<GethLikeTxTrace> debug_traceCall(TransactionForRpc call, Bl
if (_logger.IsTrace) _logger.Trace($"{nameof(debug_traceTransaction)} request {tx.Hash}, result: trace");
return ResultWrapper<GethLikeTxTrace>.Success(transactionTrace);
}
public ResultWrapper<IEnumerable<GethLikeTxTrace>> debug_traceCallMany(TransactionForRpcWithTraceTypes[] calls, BlockParameter? blockParameter = null)
{
blockParameter ??= BlockParameter.Latest;
using CancellationTokenSource cancellationTokenSource = new(_traceTimeout);
CancellationToken cancellationToken = cancellationTokenSource.Token;

SearchResult<BlockHeader> headerSearch = _debugBridge.SearchBlockHeaderForTraceCall(blockParameter);
if (headerSearch.IsError)
{
return ResultWrapper<IEnumerable<GethLikeTxTrace>>.Fail(headerSearch);
}

if (!_debugBridge.HasStateForBlock(headerSearch.Object))
{
return ResultWrapper<IEnumerable<GethLikeTxTrace>>.Fail($"No state available for block {headerSearch.Object.ToString(BlockHeader.Format.FullHashAndNumber)}", ErrorCodes.ResourceUnavailable);
}

Dictionary<Hash256, ParityTraceTypes> traceTypeByTransaction = new(calls.Length);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParityTraceType's have no sense in Geth style tracing (debug module)

Transaction[] txs = new Transaction[calls.Length];
for (int i = 0; i < calls.Length; i++)
{
calls[i].Transaction.EnsureDefaults(_jsonRpcConfig.GasCap);
Transaction tx = calls[i].Transaction.ToTransaction();
tx.Hash = new Hash256(new UInt256((ulong)i).ToBigEndian());
txs[i] = tx;
}
Comment on lines +102 to +121
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All logic should live in GethStyleTracer

Copy link
Contributor Author

@ssonthal ssonthal Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I'm sorry but I didn't understand what you meant here. Did you mean using GethStyleTracer instead of GethLikeTrace as object?


Block block = new(headerSearch.Object!, txs, Enumerable.Empty<BlockHeader>());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is constructed but never used?


IReadOnlyCollection<GethLikeTxTrace> traces = _debugBridge.GetBlockTrace(blockParameter, cancellationToken);

if (traces is null)
{
return ResultWrapper<IEnumerable<GethLikeTxTrace>>.Fail($"Failed to trace block transactions for input txns: {JsonConvert.SerializeObject(calls)}", ErrorCodes.ResourceNotFound);
}

if (_logger.IsTrace) _logger.Trace($"{nameof(debug_traceCallMany)} with input transactions: {calls} returned the result: {traces}");
return ResultWrapper<IEnumerable<GethLikeTxTrace>>.Success(traces);
}

public ResultWrapper<GethLikeTxTrace> debug_traceTransactionByBlockhashAndIndex(Hash256 blockhash, int index, GethTraceOptions options = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ public interface IDebugBridge
public IEnumerable<Block> GetBadBlocks();
TxReceipt[]? GetReceiptsForBlock(BlockParameter param);
Transaction? GetTransactionFromHash(Hash256 hash);
public SearchResult<BlockHeader> SearchBlockHeaderForTraceCall(BlockParameter blockParameter);
public bool HasStateForBlock(BlockHeader header);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public interface IDebugRpcModule : IRpcModule
[JsonRpcMethod(Description = "This method lets you run an eth_call within the context of the given block execution using the final state of parent block as the base. The block can be specified either by hash or by number. It takes the same input object as a eth_call. It returns the same output as debug_traceTransaction.", IsImplemented = true, IsSharable = true)]
ResultWrapper<GethLikeTxTrace> debug_traceCall(TransactionForRpc call, BlockParameter? blockParameter = null, GethTraceOptions? options = null);

[JsonRpcMethod(Description = "This method lets you run trace_callMany for a list of transactions.It doesn't charge fees. It returns a list of Trace objects.", IsImplemented = true, IsSharable = true)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the description. @rubo ?

ResultWrapper<IEnumerable<GethLikeTxTrace>> debug_traceCallMany(TransactionForRpcWithTraceTypes[] calls, BlockParameter? blockParameter = null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the signature should be different: https://www.chainnodes.org/docs/ethereum/debug_traceCallMany
For overrides, please wait for this PR: #7362


[JsonRpcMethod(Description = "", IsSharable = true)]
ResultWrapper<GethLikeTxTrace> debug_traceTransactionByBlockAndIndex(BlockParameter blockParameter, int txIndex, GethTraceOptions options = null);

Expand Down