Skip to content

Commit

Permalink
trace_get (hyperledger#3477)
Browse files Browse the repository at this point in the history
* Initial commit for trace_get implementation. No tests.
* test files for trace_get

Signed-off-by: Mark Terry <mark.terry@consensys.net>
Signed-off-by: Frank Li <b439988l@gmail.com>
Signed-off-by: Sally MacFarlane <sally.macfarlane@consensys.net>

Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
Co-authored-by: Frank Li <b439988l@gmail.com>
  • Loading branch information
3 people authored Mar 4, 2022
1 parent 35f1642 commit 365a7f9
Show file tree
Hide file tree
Showing 95 changed files with 3,230 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public enum RpcMethod {
TRACE_REPLAY_BLOCK_TRANSACTIONS("trace_replayBlockTransactions"),
TRACE_BLOCK("trace_block"),
TRACE_CALL("trace_call"),
TRACE_GET("trace_get"),
TRACE_TRANSACTION("trace_transaction"),
TRACE_FILTER("trace_filter"),
TX_POOL_BESU_STATISTICS("txpool_besuStatistics"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.MixInIgnoreRevertReason;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;

import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.ObjectMapper;

public class TraceGet extends AbstractTraceByHash implements JsonRpcMethod {
private static final ObjectMapper MAPPER_IGNORE_REVERT_REASON = new ObjectMapper();

public TraceGet(
final Supplier<BlockTracer> blockTracerSupplier,
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule) {
super(blockTracerSupplier, blockchainQueries, protocolSchedule);

// The trace_get specification does not output the revert reason, so we have to remove it
MAPPER_IGNORE_REVERT_REASON.addMixIn(FlatTrace.class, MixInIgnoreRevertReason.class);
}

@Override
public String getName() {
return RpcMethod.TRACE_GET.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
if (requestContext.getRequest().getParamLength() != 2) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}

final Hash transactionHash = requestContext.getRequiredParameter(0, Hash.class);
final List<?> traceNumbersAsStrings = requestContext.getRequiredParameter(1, List.class);
final List<Integer> traceNumbers =
traceNumbersAsStrings.stream()
.map(t -> Integer.parseInt(((String) t).substring(2), 16))
.collect(Collectors.toList());

return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(),
MAPPER_IGNORE_REVERT_REASON.valueToTree(
resultByTransactionHash(transactionHash)
.filter(trace -> trace.getTraceAddress().equals(traceNumbers))
.findFirst()
.orElse(null)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceBlock;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceCall;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceFilter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceGet;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceReplayBlockTransactions;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockReplay;
Expand Down Expand Up @@ -47,7 +48,6 @@ public class TraceJsonRpcMethods extends ApiGroupJsonRpcMethods {

@Override
protected String getApiGroup() {
// Disable TRACE functionality while under development
return RpcApis.TRACE.name();
}

Expand All @@ -62,6 +62,7 @@ protected Map<String, JsonRpcMethod> create() {
new TraceReplayBlockTransactions(
() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceFilter(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceGet(() -> new BlockTracer(blockReplay), blockchainQueries, protocolSchedule),
new TraceTransaction(
() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceBlock(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.util;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;

import java.util.Collections;
import java.util.function.Supplier;
import java.util.stream.Stream;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;

public class TraceUtil {

private TraceUtil() {
throw new IllegalStateException("Utility class");
}

public static Stream<FlatTrace> resultByTransactionHash(
final Hash transactionHash,
final BlockchainQueries blockchainQueries,
final Supplier<BlockTracer> blockTracerSupplier,
final ProtocolSchedule protocolSchedule) {
return blockchainQueries
.transactionByHash(transactionHash)
.flatMap(TransactionWithMetadata::getBlockNumber)
.flatMap(blockNumber -> blockchainQueries.getBlockchain().getBlockByNumber(blockNumber))
.map(block -> getTraceBlock(block, transactionHash, blockTracerSupplier, protocolSchedule))
.orElse(Stream.empty());
}

private static Stream<FlatTrace> getTraceBlock(
final Block block,
final Hash transactionHash,
final Supplier<BlockTracer> blockTracerSupplier,
final ProtocolSchedule protocolSchedule) {
if (block == null || block.getBody().getTransactions().isEmpty()) {
return Stream.empty();
}
final TransactionTrace transactionTrace =
getTransactionTrace(block, transactionHash, blockTracerSupplier);
return getTraceStream(protocolSchedule, transactionTrace, block);
}

private static TransactionTrace getTransactionTrace(
final Block block,
final Hash transactionHash,
final Supplier<BlockTracer> blockTracerSupplier) {
return blockTracerSupplier
.get()
.trace(block, new DebugOperationTracer(new TraceOptions(false, false, true)))
.map(BlockTrace::getTransactionTraces)
.orElse(Collections.emptyList())
.stream()
.filter(trxTrace -> trxTrace.getTransaction().getHash().equals(transactionHash))
.findFirst()
.orElseThrow();
}

private static Stream<FlatTrace> getTraceStream(
final ProtocolSchedule protocolSchedule,
final TransactionTrace transactionTrace,
final Block block) {
return FlatTraceGenerator.generateFromTransactionTraceAndBlock(
protocolSchedule, transactionTrace, block)
.map(FlatTrace.class::cast);
}

public static JsonNode arrayNodeFromTraceStream(final Stream<FlatTrace> traceStream) {
final ObjectMapper mapper = new ObjectMapper();
final ArrayNode resultArrayNode = mapper.createArrayNode();
traceStream.forEachOrdered(resultArrayNode::addPOJO);
return resultArrayNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static Object[][] specs() {
return AbstractJsonRpcHttpBySpecTest.findSpecFiles(
new String[] {
"trace/specs/trace-block",
"trace/specs/trace-get",
"trace/specs/trace-transaction",
"trace/specs/replay-trace-transaction/flat",
"trace/specs/replay-trace-transaction/vm-trace",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static Object[][] specs() {
return AbstractJsonRpcHttpBySpecTest.findSpecFiles(
new String[] {
"trace/specs/trace-block",
"trace/specs/trace-get",
"trace/specs/trace-transaction",
"trace/specs/replay-trace-transaction/flat",
"trace/specs/replay-trace-transaction/vm-trace",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"comment" : "Simple value transfer",
"request" : {
"jsonrpc" : "2.0",
"method" : "trace_get",
"params" : [ "0x28fa8042c7b5835f4f91fc20937f3e70dcf3585c1afe31202bb6075185f9abfe", [ ] ],
"id" : 197
},
"response" : {
"jsonrpc" : "2.0",
"result" : {
"action" : {
"callType" : "call",
"from" : "0x627306090abab3a6e1400e9345bc60c78a8bef57",
"gas" : "0xffadea",
"input" : "0x",
"to" : "0x0000000000000000000000000000000000000999",
"value" : "0x1"
},
"blockHash" : "0xf07e45fae684d31ce732c3026e57c810d4f236261aa39b20017137c348ffac4b",
"blockNumber" : 2,
"result" : {
"gasUsed" : "0x0",
"output" : "0x"
},
"subtraces" : 0,
"traceAddress" : [ ],
"transactionHash" : "0x28fa8042c7b5835f4f91fc20937f3e70dcf3585c1afe31202bb6075185f9abfe",
"transactionPosition" : 0,
"type" : "call"
},
"id" : 197
},
"statusCode" : 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"comment" : "Deploy contract that will self-destruct when called.",
"request" : {
"jsonrpc" : "2.0",
"method" : "trace_get",
"params" : [ "0x8d5477f0aae852c3e9487b0f8e7b9ecf9ccdf23d7934d4b4b7eff40c271031e5", [ ] ],
"id" : 200
},
"response" : {
"jsonrpc" : "2.0",
"result" : {
"action" : {
"creationMethod": "create",
"from" : "0x627306090abab3a6e1400e9345bc60c78a8bef57",
"gas" : "0xff300e",
"init" : "0x6004600c60003960046000f3600035ff",
"value" : "0x0"
},
"blockHash" : "0x47a62e941d567d1d7411b32ff38bdef817357d226a0204c285e8db27b3808554",
"blockNumber" : 3,
"result" : {
"address" : "0xf12b5dd4ead5f743c6baa640b0216200e89b60da",
"code" : "0x600035ff",
"gasUsed" : "0x338"
},
"subtraces" : 0,
"traceAddress" : [ ],
"transactionHash" : "0x8d5477f0aae852c3e9487b0f8e7b9ecf9ccdf23d7934d4b4b7eff40c271031e5",
"transactionPosition" : 0,
"type" : "create"
},
"id" : 200
},
"statusCode" : 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"comment" : "Set contract storage (key,value)'s: (1,1),(2,2)",
"request" : {
"jsonrpc" : "2.0",
"method" : "trace_get",
"params" : [ "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7", [ ] ],
"id" : 203
},
"response" : {
"jsonrpc" : "2.0",
"result" : {
"action" : {
"callType" : "call",
"from" : "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
"gas" : "0xffabba",
"input" : "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002",
"to" : "0x0010000000000000000000000000000000000000",
"value" : "0x0"
},
"blockHash" : "0xa1221b6473a02f05fd7235f3b336c9a061c04e74afc0034e8d6207148149d2be",
"blockNumber" : 4,
"result" : {
"gasUsed" : "0x9c58",
"output" : "0x"
},
"subtraces" : 0,
"traceAddress" : [ ],
"transactionHash" : "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"transactionPosition" : 0,
"type" : "call"
},
"id" : 203
},
"statusCode" : 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"comment" : "Set contract storage (key,value)'s: (1,3),(2,4)",
"request" : {
"jsonrpc" : "2.0",
"method" : "trace_get",
"params" : [ "0xf882ec206292910527fd7095e59a1ca027b873296f1eba3886aa1addc4ff0ab9", [ ] ],
"id" : 206
},
"response" : {
"jsonrpc" : "2.0",
"result" : {
"action" : {
"callType" : "call",
"from" : "0x627306090abab3a6e1400e9345bc60c78a8bef57",
"gas" : "0xffabba",
"input" : "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004",
"to" : "0x0010000000000000000000000000000000000000",
"value" : "0x0"
},
"blockHash" : "0xa1221b6473a02f05fd7235f3b336c9a061c04e74afc0034e8d6207148149d2be",
"blockNumber" : 4,
"result" : {
"gasUsed" : "0x2728",
"output" : "0x"
},
"subtraces" : 0,
"traceAddress" : [ ],
"transactionHash" : "0xf882ec206292910527fd7095e59a1ca027b873296f1eba3886aa1addc4ff0ab9",
"transactionPosition" : 1,
"type" : "call"
},
"id" : 206
},
"statusCode" : 200
}
Loading

0 comments on commit 365a7f9

Please sign in to comment.