Skip to content

Commit 816c099

Browse files
lu-pintomacfarla
authored andcommitted
implement trace filtering by opcode in debug_* RPCs (besu-eth#9335)
* implement trace filtering by opcode in debug_* RPCs * trace frames just after specified opcodes * Remove trace opcodes empty option check in tracePostExecution Signed-off-by: Luis Pinto <luis.pinto@consensys.net> * changelog rotated Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Luis Pinto <luis.pinto@consensys.net> Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
1 parent 6640903 commit 816c099

File tree

15 files changed

+281
-187
lines changed

15 files changed

+281
-187
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
.settings
2020
.springBeans
2121
.vertx
22+
.profileconfig.json
2223
bin/
2324
local.properties
2425
target/
@@ -32,3 +33,4 @@ site/
3233
jitpack.yml
3334
/ethereum/eth/src/test/resources/tx.csv.gz
3435
**/**/.factorypath
36+
**/**/.jqwik-database

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
### Additions and Improvements
1818
- Update to vertx 4.5.22 [#9375](https://github.com/hyperledger/besu/pull/9375)
19+
- Add `opcodes` optional parameter to RPC methods: `debug_standardTraceBlockToFile`, `debug_standardTraceBadBlockToFile`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, `debug_traceTransaction`, `debug_traceBlock`, `debug_traceCall` for tracing specified opcodes [#9335](https://github.com/hyperledger/besu/pull/9335)
1920

2021
### Bug fixes
2122

ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugStandardTraceBlockToFileIntegrationTest.java

Lines changed: 117 additions & 181 deletions
Large diffs are not rendered by default.

ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugTraceTransactionIntegrationTest.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,18 @@
2929
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
3030
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.CallTracerResult;
3131
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.OpCodeLoggerTracerResult;
32+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.StructLog;
3233
import org.hyperledger.besu.plugin.services.rpc.RpcResponseType;
3334
import org.hyperledger.besu.testutil.BlockTestUtil;
3435

3536
import java.nio.charset.StandardCharsets;
37+
import java.util.Arrays;
38+
import java.util.List;
3639
import java.util.Map;
40+
import java.util.concurrent.atomic.AtomicReference;
3741

42+
import com.fasterxml.jackson.databind.JsonNode;
43+
import com.fasterxml.jackson.databind.ObjectMapper;
3844
import com.google.common.io.Resources;
3945
import org.junit.jupiter.api.BeforeAll;
4046
import org.junit.jupiter.api.BeforeEach;
@@ -101,7 +107,6 @@ public void debugTraceTransactionMissingTest() {
101107

102108
@Test
103109
public void debugTraceTransactionCallTracerSuccessTest() {
104-
105110
final Map<String, String> map = Map.of("tracer", "callTracer");
106111
final Hash trxHash =
107112
Hash.fromHexString("0xcef53f2311d7c80e9086d661e69ac11a5f3d081e28e02a9ba9b66749407ac310");
@@ -133,4 +138,44 @@ public void invalidTracerTypeErrorTest() {
133138
.isThrownBy(() -> method.response(request))
134139
.withMessage("Invalid Tracer Type: invalidTracerType.");
135140
}
141+
142+
@Test
143+
public void debugTraceTransactionSpecificOpcodes() {
144+
final ObjectMapper jsonMapper = new ObjectMapper();
145+
final Hash trxHash =
146+
Hash.fromHexString("0xcef53f2311d7c80e9086d661e69ac11a5f3d081e28e02a9ba9b66749407ac310");
147+
148+
// Get response with defaults first to take expected JSON
149+
JsonRpcRequestContext request =
150+
new JsonRpcRequestContext(
151+
new JsonRpcRequest("2.0", DEBUG_TRACE_TRANSACTION, List.of(trxHash).toArray()));
152+
Object result = ((JsonRpcSuccessResponse) method.response(request)).getResult();
153+
final List<StructLog> defaultLogs = ((OpCodeLoggerTracerResult) result).getStructLogs();
154+
155+
final AtomicReference<String> previousOpName = new AtomicReference<>();
156+
final List<JsonNode> expectedJson =
157+
defaultLogs.stream()
158+
.filter(
159+
log -> {
160+
final boolean keep =
161+
"EQ".equals(log.op())
162+
|| "EQ".equals(previousOpName.get())
163+
|| "DIV".equals(log.op())
164+
|| "DIV".equals(previousOpName.get());
165+
previousOpName.set(log.op());
166+
return keep;
167+
})
168+
.<JsonNode>map(jsonMapper::valueToTree)
169+
.toList();
170+
171+
// Get response that we will assert on
172+
final Map<String, List<String>> map = Map.of("opcodes", Arrays.asList("EQ", "DIV"));
173+
final Object[] params = new Object[] {trxHash, map};
174+
request = new JsonRpcRequestContext(new JsonRpcRequest("2.0", DEBUG_TRACE_TRANSACTION, params));
175+
result = ((JsonRpcSuccessResponse) method.response(request)).getResult();
176+
final List<StructLog> logs = ((OpCodeLoggerTracerResult) result).getStructLogs();
177+
List<JsonNode> json = logs.stream().<JsonNode>map(jsonMapper::valueToTree).toList();
178+
179+
assertThat(json).containsAll(expectedJson);
180+
}
136181
}

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import org.hyperledger.besu.evm.tracing.OpCodeTracerConfigBuilder;
2020
import org.hyperledger.besu.evm.tracing.OpCodeTracerConfigBuilder.OpCodeTracerConfig;
2121

22+
import java.util.Collections;
2223
import java.util.LinkedHashMap;
24+
import java.util.Set;
2325
import javax.annotation.Nullable;
2426

2527
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -77,6 +79,12 @@ default boolean disableStack() {
7779
@SuppressWarnings("NonApiType")
7880
LinkedHashMap<String, Object> tracerConfig();
7981

82+
@JsonProperty("opcodes")
83+
@Value.Default
84+
default Set<String> opcodes() {
85+
return Collections.emptySet();
86+
}
87+
8088
/**
8189
* Convert JSON-RPC parameters to a {@link TraceOptions} object.
8290
*
@@ -88,6 +96,7 @@ default TraceOptions traceOptions() {
8896
.traceStorage(!disableStorage())
8997
.traceMemory(!disableMemory())
9098
.traceStack(!disableStack())
99+
.traceOpcodes(opcodes())
91100
.build();
92101

93102
// Convert string tracer to TracerType enum, handling null case

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.nio.file.Files;
4242
import java.nio.file.Path;
4343
import java.util.ArrayList;
44+
import java.util.Collections;
4445
import java.util.List;
4546
import java.util.Optional;
4647
import java.util.concurrent.TimeUnit;
@@ -96,7 +97,7 @@ public List<String> traceTransactionToFile(
9697
.map(TransactionTraceParams::getTransactionHash)
9798
.map(Hash::fromHexString);
9899
final OpCodeTracerConfig opCodeTracerConfig =
99-
OpCodeTracerConfigBuilder.createFrom(OpCodeTracerConfig.DEFAULT)
100+
OpCodeTracerConfigBuilder.create()
100101
.traceMemory(
101102
transactionTraceParams
102103
.map(TransactionTraceParams::disableMemoryNullable)
@@ -113,6 +114,10 @@ public List<String> traceTransactionToFile(
113114
.map(Boolean.FALSE::equals)
114115
.orElse(false))
115116
.traceReturnData(true)
117+
.traceOpcodes(
118+
transactionTraceParams
119+
.map(TransactionTraceParams::opcodes)
120+
.orElse(Collections.emptySet()))
116121
.eip3155Strict(true)
117122
.build();
118123

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import java.util.ArrayList;
3232
import java.util.List;
33+
import java.util.Locale;
3334
import java.util.Map;
3435
import java.util.Optional;
3536
import java.util.OptionalLong;
@@ -58,6 +59,10 @@ public class DebugOperationTracer implements OperationTracer {
5859
private int pc;
5960
private int depth;
6061

62+
// Flags used for implementing traceOpcodes functionality
63+
private boolean traceOpcode;
64+
private Operation previousOpcode = null;
65+
6166
/**
6267
* Creates the operation tracer.
6368
*
@@ -72,6 +77,10 @@ public DebugOperationTracer(final OpCodeTracerConfig options, final boolean reco
7277

7378
@Override
7479
public void tracePreExecution(final MessageFrame frame) {
80+
final Operation currentOperation = frame.getCurrentOperation();
81+
if (!(traceOpcode = traceOpcode(currentOperation))) {
82+
return;
83+
}
7584
preExecutionStack = captureStack(frame);
7685
gasRemaining = frame.getRemainingGas();
7786
if (lastFrame != null && frame.getDepth() > lastFrame.getDepth())
@@ -81,10 +90,30 @@ public void tracePreExecution(final MessageFrame frame) {
8190
depth = frame.getDepth();
8291
}
8392

93+
private boolean traceOpcode(final Operation currentOpcode) {
94+
if (options.traceOpcodes().isEmpty()) {
95+
return true;
96+
}
97+
final boolean traceCurrentOpcode =
98+
options.traceOpcodes().contains(currentOpcode.getName().toLowerCase(Locale.ROOT));
99+
final boolean tracePreviousOpcode =
100+
previousOpcode != null
101+
&& options.traceOpcodes().contains(previousOpcode.getName().toLowerCase(Locale.ROOT));
102+
103+
if (!traceCurrentOpcode && !tracePreviousOpcode) {
104+
return false;
105+
}
106+
previousOpcode = currentOpcode;
107+
return true;
108+
}
109+
84110
@Override
85111
public void tracePostExecution(final MessageFrame frame, final OperationResult operationResult) {
86112
final Operation currentOperation = frame.getCurrentOperation();
87113
final String opcode = currentOperation.getName();
114+
if (!traceOpcode) {
115+
return;
116+
}
88117
final int opcodeNumber = (opcode != null) ? currentOperation.getOpcode() : Integer.MAX_VALUE;
89118
final WorldUpdater worldUpdater = frame.getWorldUpdater();
90119
final Bytes outputData = frame.getOutputData();

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ void shouldCaptureFrameWhenExceptionalHaltOccurs() {
274274
.traceStack(true)
275275
.build(),
276276
false);
277+
tracer.tracePreExecution(frame);
277278
tracer.tracePostExecution(
278279
frame, new OperationResult(50L, ExceptionalHaltReason.INSUFFICIENT_GAS));
279280

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.io.PrintWriter;
5555
import java.util.ArrayList;
5656
import java.util.Arrays;
57+
import java.util.Collections;
5758
import java.util.Comparator;
5859
import java.util.Deque;
5960
import java.util.LinkedHashMap;
@@ -471,6 +472,7 @@ public void run() {
471472
.traceStack(!hideStack)
472473
.traceReturnData(showReturnData)
473474
.traceStorage(showStorage)
475+
.traceOpcodes(Collections.emptySet())
474476
.eip3155Strict(eip3155strict)
475477
.build())
476478
: OperationTracer.NO_TRACING;

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.io.InputStreamReader;
5050
import java.nio.file.Path;
5151
import java.util.ArrayList;
52+
import java.util.Collections;
5253
import java.util.List;
5354
import java.util.Map;
5455
import java.util.concurrent.TimeUnit;
@@ -220,6 +221,7 @@ private void traceTestSpecs(
220221
.traceStack(!parentCommand.hideStack)
221222
.traceReturnData(parentCommand.showReturnData)
222223
.traceStorage(parentCommand.showStorage)
224+
.traceOpcodes(Collections.emptySet())
223225
.eip3155Strict(parentCommand.eip3155strict)
224226
.build())
225227
: OperationTracer.NO_TRACING;

0 commit comments

Comments
 (0)