Skip to content

Commit

Permalink
Adding withLogs parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
asoto-iov committed Nov 26, 2024
1 parent 685e0ab commit e3037c0
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 64 deletions.
26 changes: 4 additions & 22 deletions rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package co.rsk.rpc.modules.debug;

import co.rsk.rpc.modules.debug.trace.TracerType;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -36,8 +35,7 @@ public class TraceOptions {
private final Set<String> disabledFields;
private final Set<String> unsupportedOptions;
private boolean onlyTopCall;
private boolean diffMode;
private TracerType tracerType;
private boolean withLog;

public TraceOptions() {
this.disabledFields = new HashSet<>();
Expand All @@ -55,14 +53,11 @@ public TraceOptions(Map<String, String> traceOptions) {
public final void addOption(String key, String value) {
switch (key) {
case "onlyTopCall" -> onlyTopCall = Boolean.parseBoolean(value);
case "diffMode" -> diffMode = Boolean.parseBoolean(value);
case "withLog" -> withLog = Boolean.parseBoolean(value);
default -> addDisableOption(key, value);
}
}




private void addDisableOption(String key, String value) {
DisableOption disableOption = DisableOption.getDisableOption(key);
if (disableOption != null) {
Expand All @@ -74,21 +69,12 @@ private void addDisableOption(String key, String value) {
}
}


public void setOnlyTopCall(boolean onlyTopCall) {
this.onlyTopCall = onlyTopCall;
}

public void setDiffMode(boolean diffMode) {
this.diffMode = diffMode;
}

public boolean isOnlyTopCall() {
return onlyTopCall;
}

public boolean isDiffMode() {
return diffMode;
public boolean isWithLog() {
return withLog;
}

public Set<String> getDisabledFields() {
Expand All @@ -99,10 +85,6 @@ public Set<String> getUnsupportedOptions() {
return Collections.unmodifiableSet(unsupportedOptions);
}

public TracerType getTracerType() {
return tracerType;
}

public static class Deserializer extends StdDeserializer<TraceOptions> {
@Serial
private static final long serialVersionUID = 4222943114560623356L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@
import org.bouncycastle.util.encoders.Hex;
import org.ethereum.db.TransactionInfo;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.LogInfo;
import org.ethereum.vm.program.ProgramResult;
import org.ethereum.vm.program.invoke.InvokeData;
import org.ethereum.vm.trace.SummarizedProgramTrace;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class CallTraceTransformer {

private CallTraceTransformer() {
}

public static TransactionTrace toTrace(SummarizedProgramTrace trace, TransactionInfo txInfo, DataWord codeAddress, boolean onlyTopCall) {
public static TransactionTrace toTrace(SummarizedProgramTrace trace, TransactionInfo txInfo, DataWord codeAddress, boolean onlyTopCall, boolean withLog) {
boolean isContractCreation = txInfo.getReceipt().getTransaction().isContractCreation();
CallType callType = isContractCreation ? CallType.NONE : CallType.CALL;

Expand All @@ -49,6 +52,11 @@ public static TransactionTrace toTrace(SummarizedProgramTrace trace, Transaction
if (trace.getReverted()) {
programResult.setRevert();
}

if (withLog) {
programResult.addLogInfos(txInfo.getReceipt().getLogInfoList());
}

InvokeData invoke = trace.getInvokeData();

CreationData creationData = null;
Expand All @@ -64,41 +72,48 @@ public static TransactionTrace toTrace(SummarizedProgramTrace trace, Transaction
traceType = TraceType.CREATE;
}

TxTraceResult traceOutput = toTrace(traceType, callType, invoke, codeAddress, programResult, creationData);
if(!onlyTopCall) {
TxTraceResult traceOutput = toTrace(traceType, callType, invoke, codeAddress, programResult, creationData, withLog);
if (!onlyTopCall) {
for (ProgramSubtrace subtrace : trace.getSubtraces()) {
traceOutput.addCall(toTrace(subtrace));
traceOutput.addCall(toTrace(subtrace, withLog));
}
}

return new TransactionTrace(txInfo.getReceipt().getTransaction().getHash().toHexString(), traceOutput);
}

private static TxTraceResult toTrace(ProgramSubtrace programSubtrace) {
private static TxTraceResult toTrace(ProgramSubtrace programSubtrace, boolean withLog) {
InvokeData invokeData = programSubtrace.getInvokeData();
TxTraceResult subTrace = toTrace(programSubtrace.getTraceType(), programSubtrace.getCallType(), invokeData, programSubtrace.getCodeAddress(), programSubtrace.getProgramResult(), programSubtrace.getCreationData());
TxTraceResult subTrace = toTrace(programSubtrace.getTraceType(), programSubtrace.getCallType(), invokeData, programSubtrace.getCodeAddress(), programSubtrace.getProgramResult(), programSubtrace.getCreationData(), withLog);
for (ProgramSubtrace call : programSubtrace.getSubtraces()) {
subTrace.addCall(toTrace(call));
subTrace.addCall(toTrace(call, withLog));
}
return subTrace;
}

private static TxTraceResult toTrace(TraceType traceType, CallType callType, InvokeData invoke, DataWord codeAddress, ProgramResult programResult, CreationData creationData) {
private static TxTraceResult toTrace(TraceType traceType, CallType callType, InvokeData invoke, DataWord codeAddress, ProgramResult programResult, CreationData creationData, boolean withLog) {
String type = traceType == TraceType.CREATE ? "CREATE" : callType.name();
String from;
String to = null;
String gas = null;
//TODO input data is missing in TransferInvoke
String input = null;
String value = null;
String output = null;
String gasUsed = null;
String revertReason = null;
String error = null;


if (callType == CallType.DELEGATECALL) {
from = new RskAddress(invoke.getOwnerAddress().getLast20Bytes()).toJsonString();
} else {
from = new RskAddress(invoke.getCallerAddress().getLast20Bytes()).toJsonString();
}
String to = null;
String gas = null;

List<LogInfoResult> logInfoResultList = null;

DataWord callValue = invoke.getCallValue();
//TODO input data is missing in TransferInvoke
String input = null;
String value = null;
String output = null;


if (traceType == TraceType.CREATE) {
Expand All @@ -125,24 +140,17 @@ private static TxTraceResult toTrace(TraceType traceType, CallType callType, Inv
}

gas = HexUtils.toQuantityJsonHex(invoke.getGas());

}

String gasUsed = null;

String revertReason = null;


String error = null;

if (programResult != null) {
gasUsed = HexUtils.toQuantityJsonHex(programResult.getGasUsed());

if (programResult.isRevert()) {
Pair<String, byte[]> programRevert = EthModule.decodeProgramRevert(programResult);
revertReason = programRevert.getLeft();
output = HexUtils.toJsonHex(programRevert.getRight());

error = "execution reverted";
} else if (traceType != TraceType.CREATE) {
output = HexUtils.toJsonHex(programResult.getHReturn());
Expand All @@ -152,6 +160,18 @@ private static TxTraceResult toTrace(TraceType traceType, CallType callType, Inv
}
}

if(withLog) {
logInfoResultList = new ArrayList<>();
List<LogInfo> logInfoList = programResult.getLogInfoList();

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

Variable
programResult
may be null at this access as suggested by
this
null guard.
if(logInfoList != null) {
for (int i = 0; i < programResult.getLogInfoList().size(); i++) {
LogInfo logInfo = programResult.getLogInfoList().get(i);
LogInfoResult logInfoResult = fromLogInfo(logInfo, i);
logInfoResultList.add(logInfoResult);
}
}
}

return TxTraceResult.builder()
.type(type)
.from(from)
Expand All @@ -163,8 +183,20 @@ private static TxTraceResult toTrace(TraceType traceType, CallType callType, Inv
.output(output)
.revertReason(revertReason)
.error(error)
.logs(logInfoResultList)
.build();

}

private static LogInfoResult fromLogInfo(LogInfo logInfo, int index) {
String address = HexUtils.toJsonHex(logInfo.getAddress());
List<String> topics = logInfo.getTopics().stream().map(DataWord::getData).map(HexUtils::toJsonHex).toList();
String data = HexUtils.toJsonHex(logInfo.getData());
return LogInfoResult.builder()
.index(index)
.address(address)
.topics(topics)
.data(data)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
import java.util.List;

public class CallTracer implements DebugTracer {

public static final String UNSUPPORTED_OPERATION = "Operation not supported by this tracer.";
private static final Logger logger = LoggerFactory.getLogger("callTracer");
private static final ObjectMapper OBJECT_MAPPER = Serializers.createMapper(true);

Expand Down Expand Up @@ -93,7 +91,7 @@ public JsonNode traceTransaction(@Nonnull String transactionHash, @Nonnull Trace
return null;
}

TransactionTrace trace = CallTraceTransformer.toTrace(programTrace, txInfo, null, traceOptions.isOnlyTopCall());
TransactionTrace trace = CallTraceTransformer.toTrace(programTrace, txInfo, null, traceOptions.isOnlyTopCall(), traceOptions.isWithLog());
return OBJECT_MAPPER.valueToTree(trace.getResult());
}

Expand Down Expand Up @@ -128,15 +126,12 @@ public TracerType getTracerType() {
return TracerType.CALL_TRACER;
}

private JsonNode traceBlock(Block block, TraceOptions traceOptions) {
if (traceOptions != null) {
logger.warn("Trace Options not supported yet");
}
List<TransactionTrace> result = buildBlockTraces(block, traceOptions.isOnlyTopCall());
private JsonNode traceBlock(Block block, @Nonnull TraceOptions traceOptions) {
List<TransactionTrace> result = buildBlockTraces(block, traceOptions.isOnlyTopCall(), traceOptions.isWithLog());
return OBJECT_MAPPER.valueToTree(result);
}

private List<TransactionTrace> buildBlockTraces(Block block, boolean onlyTopCall) {
private List<TransactionTrace> buildBlockTraces(Block block, boolean onlyTopCall, boolean withLog) {
List<TransactionTrace> blockTraces = new ArrayList<>();

if (block != null && block.getNumber() != 0) {
Expand All @@ -161,7 +156,7 @@ private List<TransactionTrace> buildBlockTraces(Block block, boolean onlyTopCall
return Collections.emptyList();
}

TransactionTrace trace = CallTraceTransformer.toTrace(programTrace, txInfo, null, onlyTopCall);
TransactionTrace trace = CallTraceTransformer.toTrace(programTrace, txInfo, null, onlyTopCall, withLog);

blockTraces.add(trace);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package co.rsk.rpc.modules.debug.trace.call;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

public record LogInfoResult(@JsonProperty int index, @JsonProperty String address, @JsonProperty List<String> topics,
@JsonProperty String data) {

public static Builder builder() {
return new Builder();
}

public static class Builder {
private int index;
private String address;
private List<String> topics;
private String data;

public Builder index(int index) {
this.index = index;
return this;
}

public Builder address(String address) {
this.address = address;
return this;
}

public Builder topics(List<String> topics) {
this.topics = topics;
return this;
}

public Builder data(String data) {
this.data = data;
return this;
}

public LogInfoResult build() {
return new LogInfoResult(index, address, topics, data);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ public class TxTraceResult {
private final String error;
private final String revertReason;
private final List<TxTraceResult> calls;
private final List<String> logs;
private final List<LogInfoResult> logs;

public TxTraceResult(String type, String from, String to, String value, String gas, String gasUsed, String input, String output, String error, String revertReason, List<TxTraceResult> calls, List<String> logs) {
public TxTraceResult(String type, String from, String to, String value, String gas, String gasUsed, String input, String output, String error, String revertReason, List<TxTraceResult> calls, List<LogInfoResult> logs) {
this.type = type;
this.from = from;
this.to = to;
Expand Down Expand Up @@ -110,9 +110,8 @@ public List<TxTraceResult> getCalls() {
return calls.isEmpty() ? null : calls;
}

//TODO
@JsonGetter("logs")
public List<String> getLogs() {
public List<LogInfoResult> getLogs() {
return logs.isEmpty() ? null : logs;
}

Expand All @@ -125,11 +124,6 @@ public void addCall(TxTraceResult call) {
calls.add(call);
}

public void addAllCall(List<TxTraceResult> calls) {
this.calls.addAll(calls);
}


//Builder class
public static class Builder {
private String type;
Expand All @@ -143,7 +137,7 @@ public static class Builder {
private String error;
private String revertReason;
private List<TxTraceResult> calls;
private List<String> logs;
private List<LogInfoResult> logs;

public Builder type(String type) {
this.type = type;
Expand Down Expand Up @@ -200,7 +194,7 @@ public Builder calls(List<TxTraceResult> calls) {
return this;
}

public Builder logs(List<String> logs) {
public Builder logs(List<LogInfoResult> logs) {
this.logs = logs;
return this;
}
Expand Down

0 comments on commit e3037c0

Please sign in to comment.