Skip to content

Commit

Permalink
Merge pull request #2839 from rsksmart/add_rpc_debug_tracer
Browse files Browse the repository at this point in the history
Adding callTracer into debug_tracer module
  • Loading branch information
Vovchyk authored Dec 5, 2024
2 parents 7171fcc + 6ff2c50 commit 218ca4b
Show file tree
Hide file tree
Showing 29 changed files with 1,778 additions and 189 deletions.
21 changes: 13 additions & 8 deletions rskj-core/src/main/java/co/rsk/RskContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
import co.rsk.rpc.*;
import co.rsk.rpc.modules.debug.DebugModule;
import co.rsk.rpc.modules.debug.DebugModuleImpl;
import co.rsk.rpc.modules.debug.trace.RskTracer;
import co.rsk.rpc.modules.debug.trace.TraceProvider;
import co.rsk.rpc.modules.debug.trace.call.CallTracer;
import co.rsk.rpc.modules.eth.*;
import co.rsk.rpc.modules.eth.subscribe.BlockHeaderNotificationEmitter;
import co.rsk.rpc.modules.eth.subscribe.LogsNotificationEmitter;
Expand Down Expand Up @@ -104,6 +107,7 @@
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.signature.Secp256k1;
import org.ethereum.datasource.*;
import org.ethereum.db.BlockStore;
import org.ethereum.db.IndexedBlockStore;
import org.ethereum.db.ReceiptStore;
import org.ethereum.db.ReceiptStoreImplV2;
Expand Down Expand Up @@ -812,15 +816,16 @@ public synchronized ConfigCapabilities getConfigCapabilities() {
public synchronized DebugModule getDebugModule() {
checkIfNotClosed();

Web3InformationRetriever web3i = getWeb3InformationRetriever();
BlockStore bs = getBlockStore();
BlockExecutor be = getBlockExecutor();
RskTracer rskTracer = new RskTracer(bs, getReceiptStore(),
be, web3i);

CallTracer callTracer = new CallTracer(bs, be, web3i, getReceiptStore(), getBlockchain());
TraceProvider traceProvider = new TraceProvider(Arrays.asList(callTracer, rskTracer));
if (debugModule == null) {
debugModule = new DebugModuleImpl(
getBlockStore(),
getReceiptStore(),
getNodeMessageHandler(),
getBlockExecutor(),
getTxQuotaChecker(),
getWeb3InformationRetriever()
);
debugModule = new DebugModuleImpl(traceProvider, getNodeMessageHandler(), getTxQuotaChecker());
}

return debugModule;
Expand Down
22 changes: 11 additions & 11 deletions rskj-core/src/main/java/co/rsk/rpc/Web3DebugModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
import co.rsk.net.handler.quota.TxQuota;
import co.rsk.rpc.modules.debug.DebugModule;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.Map;
import org.ethereum.rpc.parameters.DebugTracerParam;

@java.lang.SuppressWarnings("squid:S100")
public interface Web3DebugModule {
Expand All @@ -32,27 +31,28 @@ default String debug_wireProtocolQueueSize() {
}

default JsonNode debug_traceTransaction(String transactionHash) throws Exception {
return debug_traceTransaction(transactionHash, null);
return getDebugModule().traceTransaction(transactionHash);
}

default JsonNode debug_traceTransaction(String transactionHash, Map<String, String> traceOptions) throws Exception {
return getDebugModule().traceTransaction(transactionHash, traceOptions);
default JsonNode debug_traceTransaction(String transactionHash, DebugTracerParam traceParams) throws Exception {
return getDebugModule().traceTransaction(transactionHash, traceParams.getTraceOptions(), traceParams.getTracerType());
}

default JsonNode debug_traceBlockByHash(String blockHash) throws Exception {
return debug_traceBlockByHash(blockHash, null);
return getDebugModule().traceBlockByHash(blockHash);
}

default JsonNode debug_traceBlockByHash(String blockHash, Map<String, String> traceOptions) throws Exception {
return getDebugModule().traceBlockByHash(blockHash, traceOptions);
default JsonNode debug_traceBlockByHash(String blockHash, DebugTracerParam debugTracerParam) throws Exception {
return getDebugModule().traceBlockByHash(blockHash, debugTracerParam.getTraceOptions(), debugTracerParam.getTracerType());
}

default JsonNode debug_traceBlockByNumber(String bnOrId) throws Exception {
return debug_traceBlockByNumber(bnOrId, null);

return debug_traceBlockByNumber(bnOrId, new DebugTracerParam());
}

default JsonNode debug_traceBlockByNumber(String bnOrId, Map<String, String> traceOptions) throws Exception {
return getDebugModule().traceBlockByNumber(bnOrId, traceOptions);
default JsonNode debug_traceBlockByNumber(String bnOrId, DebugTracerParam debugTracerParam) throws Exception {
return getDebugModule().traceBlockByNumber(bnOrId, debugTracerParam.getTraceOptions(), debugTracerParam.getTracerType());
}

default TxQuota debug_accountTransactionQuota(String address) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@
package co.rsk.rpc.modules.debug;

import co.rsk.net.handler.quota.TxQuota;
import co.rsk.rpc.modules.debug.trace.TracerType;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.Map;

public interface DebugModule {

String wireProtocolQueueSize();

JsonNode traceTransaction(String transactionHash, Map<String, String> traceOptions) throws Exception;
JsonNode traceTransaction(String transactionHash) throws Exception;

JsonNode traceTransaction(String transactionHash, TraceOptions traceOptions, TracerType tracerType) throws Exception;

JsonNode traceBlockByHash(String blockHash, Map<String, String> traceOptions) throws Exception;
JsonNode traceBlockByHash(String blockHash, TraceOptions traceOptions, TracerType tracerType) throws Exception;
JsonNode traceBlockByHash(String blockHash) throws Exception;

JsonNode traceBlockByNumber(String bnOrId, Map<String, String> traceOptions) throws Exception;
JsonNode traceBlockByNumber(String bnOrId, TraceOptions traceOptions, TracerType tracerType) throws Exception;

TxQuota accountTransactionQuota(String address);
}
152 changes: 56 additions & 96 deletions rskj-core/src/main/java/co/rsk/rpc/modules/debug/DebugModuleImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,145 +19,105 @@
package co.rsk.rpc.modules.debug;

import co.rsk.core.RskAddress;
import co.rsk.core.bc.BlockExecutor;
import co.rsk.crypto.Keccak256;
import co.rsk.net.MessageHandler;
import co.rsk.net.handler.quota.TxQuota;
import co.rsk.net.handler.quota.TxQuotaChecker;
import co.rsk.rpc.Web3InformationRetriever;
import co.rsk.rpc.modules.debug.trace.DebugTracer;
import co.rsk.rpc.modules.debug.trace.TraceProvider;
import co.rsk.rpc.modules.debug.trace.TracerType;
import co.rsk.util.HexUtils;
import co.rsk.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.db.BlockStore;
import org.ethereum.db.ReceiptStore;
import org.ethereum.db.TransactionInfo;
import org.ethereum.vm.trace.ProgramTraceProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DebugModuleImpl implements DebugModule {
//this could be configurable
public static final TracerType DEFAULT_TRACER_TYPE = TracerType.RSK_TRACER;
private static final Logger logger = LoggerFactory.getLogger("web3");

private final BlockStore blockStore;
private final ReceiptStore receiptStore;

private final TraceProvider traceProvider;
private final MessageHandler messageHandler;
private final BlockExecutor blockExecutor;

private final TxQuotaChecker txQuotaChecker;
private final Web3InformationRetriever web3InformationRetriever;

public DebugModuleImpl(
BlockStore blockStore,
ReceiptStore receiptStore,
MessageHandler messageHandler,
BlockExecutor blockExecutor,
TxQuotaChecker txQuotaChecker,
Web3InformationRetriever web3InformationRetriever) {
this.blockStore = blockStore;
this.receiptStore = receiptStore;

public DebugModuleImpl(TraceProvider traceProvider, MessageHandler messageHandler, TxQuotaChecker txQuotaChecker) {
this.traceProvider = traceProvider;
this.messageHandler = messageHandler;
this.blockExecutor = blockExecutor;
this.txQuotaChecker = txQuotaChecker;
this.web3InformationRetriever = web3InformationRetriever;
}


@Override
public String wireProtocolQueueSize() {
long n = messageHandler.getMessageQueueSize();
return HexUtils.toQuantityJsonHex(n);
}

@Override
public JsonNode traceTransaction(String transactionHash, Map<String, String> traceOptions) {
logger.trace("debug_traceTransaction for txHash: {}", StringUtils.trim(transactionHash));

TraceOptions options = toTraceOptions(traceOptions);

byte[] hash = HexUtils.stringHexToByteArray(transactionHash);
TransactionInfo txInfo = receiptStore.getInMainChain(hash, blockStore).orElse(null);

if (txInfo == null) {
logger.trace("No transaction info for txHash: {}", StringUtils.trim(transactionHash));
return null;
public TxQuota accountTransactionQuota(String address) {
if (logger.isTraceEnabled()) {
logger.trace("debug_accountTransactionQuota({})", StringUtils.trim(address));
}

Block block = blockStore.getBlockByHash(txInfo.getBlockHash());
Block parent = blockStore.getBlockByHash(block.getParentHash().getBytes());
Transaction tx = block.getTransactionsList().get(txInfo.getIndex());
txInfo.setTransaction(tx);

ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(options);
blockExecutor.traceBlock(programTraceProcessor, 0, block, parent.getHeader(), false, false);

return programTraceProcessor.getProgramTraceAsJsonNode(tx.getHash());
RskAddress rskAddress = new RskAddress(address);
return txQuotaChecker.getTxQuota(rskAddress);
}

@Override
public JsonNode traceBlockByHash(String blockHash, Map<String, String> traceOptions) {
logger.trace("debug_traceBlockByHash for blockHash: {}", StringUtils.trim(blockHash));
public JsonNode traceTransaction(String transactionHash) throws Exception {
return traceTransaction(transactionHash, new TraceOptions(), null);
}

TraceOptions options = toTraceOptions(traceOptions);
@Override
public JsonNode traceTransaction(String transactionHash, TraceOptions traceOptions, TracerType tracerType) throws Exception {
TracerType type = getTracerTypeOrDefault(tracerType);

byte[] bHash = HexUtils.stringHexToByteArray(blockHash);
Block block = blockStore.getBlockByHash(bHash);
if (block == null) {
logger.trace("No block is found for blockHash: {}", StringUtils.trim(blockHash));
return null;
if (traceOptions == null) {
traceOptions = new TraceOptions();
}

return traceBlock(block, options);
DebugTracer tracer = traceProvider.getTracer(type);
if (logger.isTraceEnabled()) {
logger.trace("debug_traceTransaction for txHash: {}", StringUtils.trim(transactionHash));
}
return tracer.traceTransaction(transactionHash, traceOptions);
}

@Override
public JsonNode traceBlockByNumber(String bnOrId, Map<String, String> traceOptions) {
logger.trace("debug_traceBlockByNumber for bnOrId: {}", StringUtils.trim(bnOrId));
public JsonNode traceBlockByHash(String blockHash, TraceOptions traceOptions, TracerType tracerType) {
TracerType type = getTracerTypeOrDefault(tracerType);

TraceOptions options = toTraceOptions(traceOptions);

Block block = web3InformationRetriever.getBlock(bnOrId).orElse(null);
if (block == null) {
logger.trace("No block is found for bnOrId: {}", StringUtils.trim(bnOrId));
return null;
if (traceOptions == null) {
traceOptions = new TraceOptions();
}

return traceBlock(block, options);
if (logger.isTraceEnabled()) {
logger.trace("debug_traceBlockByHash for blockHash: {}", StringUtils.trim(blockHash));
}
DebugTracer tracer = traceProvider.getTracer(type);
return tracer.traceBlockByHash(blockHash, traceOptions);
}

private JsonNode traceBlock(Block block, TraceOptions options) {
Block parent = blockStore.getBlockByHash(block.getParentHash().getBytes());

ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(options);
blockExecutor.traceBlock(programTraceProcessor, 0, block, parent.getHeader(), false, false);

List<Keccak256> txHashes = block.getTransactionsList().stream()
.map(Transaction::getHash)
.collect(Collectors.toList());

return programTraceProcessor.getProgramTracesAsJsonNode(txHashes);
@Override
public JsonNode traceBlockByHash(String blockHash) throws Exception {
return traceBlockByHash(blockHash, new TraceOptions(), null);
}

private TraceOptions toTraceOptions(Map<String, String> traceOptions) {
TraceOptions options = new TraceOptions(traceOptions);

if (!options.getUnsupportedOptions().isEmpty()) {
// TODO: implement the logic that takes into account the remaining trace options.
logger.warn("Received {} unsupported trace options", options.getUnsupportedOptions().size());
@Override
public JsonNode traceBlockByNumber(String bnOrId, TraceOptions traceOptions, TracerType tracerType) throws Exception {
TracerType type = getTracerTypeOrDefault(tracerType);
if (traceOptions == null) {
traceOptions = new TraceOptions();
}

return options;
if (logger.isTraceEnabled()) {
logger.trace("debug_traceBlockByNumber for bnOrId: {}", StringUtils.trim(bnOrId));
}
DebugTracer tracer = traceProvider.getTracer(type);
return tracer.traceBlockByNumber(bnOrId, traceOptions);
}

@Override
public TxQuota accountTransactionQuota(String address) {
logger.trace("debug_accountTransactionQuota({})", StringUtils.trim(address));
RskAddress rskAddress = new RskAddress(address);
return this.txQuotaChecker.getTxQuota(rskAddress);
private TracerType getTracerTypeOrDefault(TracerType tracerType) {
//TODO review about this default tracer logic
if (tracerType == null) {
return DEFAULT_TRACER_TYPE;
}
return tracerType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,13 @@ public enum DisableOption {
this.option = option;
this.value = value;
}

public static DisableOption getDisableOption(String option) {
for (DisableOption disableOption : DisableOption.values()) {
if (disableOption.option.equalsIgnoreCase(option)) {
return disableOption;
}
}
return null;
}
}
Loading

0 comments on commit 218ca4b

Please sign in to comment.