Skip to content

Commit

Permalink
[PIE-2284] Add trace transaction api (#441)
Browse files Browse the repository at this point in the history
* init trace_transaction api

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* add test cases for trace_transaction api

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* resolve the tests by checking only the presence of the blockHash field and not its value because it is different each time generateTestBlockchain is called

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* clean code

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* to allow the tests to work, we must put back the blocks.bin and no longer generate it during the build. adding the nonce to the blocks.json file should solve this problem

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* clean code

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>

* refactor add additional transaction part

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
  • Loading branch information
matkt committed Mar 9, 2020
1 parent 19b8f9d commit 67a0c79
Show file tree
Hide file tree
Showing 126 changed files with 2,416 additions and 15 deletions.
3 changes: 2 additions & 1 deletion ethereum/api/build.gradle
Expand Up @@ -134,4 +134,5 @@ task generateTestBlockchain() {
}
}
}
test.dependsOn(generateTestBlockchain)
// TODO must be reactivated when we can generate the block.bin file during build
//test.dependsOn(generateTestBlockchain)
Expand Up @@ -114,6 +114,7 @@ public enum RpcMethod {
PERM_REMOVE_NODES_FROM_WHITELIST("perm_removeNodesFromWhitelist"),
RPC_MODULES("rpc_modules"),
TRACE_REPLAY_BLOCK_TRANSACTIONS("trace_replayBlockTransactions"),
TRACE_TRANSACTION("trace_transaction"),
TX_POOL_BESU_STATISTICS("txpool_besuStatistics"),
TX_POOL_BESU_TRANSACTIONS("txpool_besuTransactions"),
WEB3_CLIENT_VERSION("web3_clientVersion"),
Expand Down
@@ -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.jsonrpc.internal.methods;

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.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.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
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.core.Hash;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;

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

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

public class TraceTransaction implements JsonRpcMethod {
private final Supplier<BlockTracer> blockTracerSupplier;

private final BlockchainQueries blockchainQueries;

public TraceTransaction(
final Supplier<BlockTracer> blockTracerSupplier, final BlockchainQueries blockchainQueries) {
this.blockTracerSupplier = blockTracerSupplier;
this.blockchainQueries = blockchainQueries;
}

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

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final Hash transactionHash = requestContext.getRequiredParameter(0, Hash.class);
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), resultByTransactionHash(transactionHash));
}

private Object resultByTransactionHash(final Hash transactionHash) {
return blockchainQueries
.transactionByHash(transactionHash)
.flatMap(TransactionWithMetadata::getBlockNumber)
.flatMap(blockNumber -> blockchainQueries.getBlockchain().getBlockByNumber(blockNumber))
.map((block) -> traceBlock(block, transactionHash))
.orElse(emptyResult());
}

private Object traceBlock(final Block block, final Hash transactionHash) {
if (block == null || block.getBody().getTransactions().isEmpty()) {
return emptyResult();
}
final TransactionTrace transactionTrace =
blockTracerSupplier.get().trace(block, new DebugOperationTracer(TraceOptions.DEFAULT))
.map(BlockTrace::getTransactionTraces).orElse(Collections.emptyList()).stream()
.filter(trxTrace -> trxTrace.getTransaction().getHash().equals(transactionHash))
.findFirst()
.orElseThrow();
return generateTracesFromTransactionTraceAndBlock(transactionTrace, block);
}

private JsonNode generateTracesFromTransactionTraceAndBlock(
final TransactionTrace transactionTrace, final Block block) {
final ObjectMapper mapper = new ObjectMapper();

final ArrayNode resultArrayNode = mapper.createArrayNode();

FlatTraceGenerator.generateFromTransactionTraceAndBlock(transactionTrace, block)
.forEachOrdered(resultArrayNode::addPOJO);

return resultArrayNode;
}

private Object emptyResult() {
final ObjectMapper mapper = new ObjectMapper();
return mapper.createArrayNode();
}
}
Expand Up @@ -24,6 +24,7 @@
@JsonInclude(NON_NULL)
public class Action {

private final String creationMethod;
private final String callType;
private final String from;
private final String gas;
Expand All @@ -36,6 +37,7 @@ public class Action {
private final String refundAddress;

private Action(
final String creationMethod,
final String callType,
final String from,
final String gas,
Expand All @@ -46,6 +48,7 @@ private Action(
final String address,
final String balance,
final String refundAddress) {
this.creationMethod = creationMethod;
this.callType = callType;
this.from = from;
this.gas = gas;
Expand All @@ -62,6 +65,10 @@ public static Builder builder() {
return new Builder();
}

public String getCreationMethod() {
return creationMethod;
}

public String getCallType() {
return callType;
}
Expand Down Expand Up @@ -103,6 +110,7 @@ public String getRefundAddress() {
}

public static final class Builder {
private String creationMethod;
private String callType;
private String from;
private String gas;
Expand All @@ -118,6 +126,7 @@ private Builder() {}

public static Builder of(final Action action) {
final Builder builder = new Builder();
builder.creationMethod = action.creationMethod;
builder.callType = action.callType;
builder.from = action.from;
builder.gas = action.gas;
Expand All @@ -138,6 +147,11 @@ public static Builder from(final TransactionTrace trace) {
.value(Quantity.create(trace.getTransaction().getValue()));
}

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

public Builder callType(final String callType) {
this.callType = callType;
return this;
Expand Down Expand Up @@ -206,7 +220,17 @@ public String getGas() {

public Action build() {
return new Action(
callType, from, gas, input, to, init, value, address, balance, refundAddress);
creationMethod,
callType,
from,
gas,
input,
to,
init,
value,
address,
balance,
refundAddress);
}
}
}
Expand Up @@ -25,10 +25,27 @@
import java.util.concurrent.atomic.AtomicReference;

import com.fasterxml.jackson.annotation.JsonInclude;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonPropertyOrder({
"action",
"blockHash",
"blockNumber",
"result",
"error",
"subtraces",
"traceAddress",
"transactionHash",
"transactionPosition",
"type"
})
public class FlatTrace implements Trace {
private final Action action;
private final Result result;
private final Long blockNumber;
private final String blockHash;
private final Integer transactionPosition;
private final String transactionHash;
private final Optional<String> error;
private final int subtraces;
private final List<Integer> traceAddress;
Expand All @@ -40,13 +57,21 @@ private FlatTrace(
final int subtraces,
final List<Integer> traceAddress,
final String type,
final Long blockNumber,
final String blockHash,
final Integer transactionPosition,
final String transactionHash,
final Optional<String> error) {
this(
actionBuilder != null ? actionBuilder.build() : null,
resultBuilder != null ? resultBuilder.build() : null,
subtraces,
traceAddress,
type,
blockNumber,
blockHash,
transactionPosition,
transactionHash,
error);
}

Expand All @@ -56,12 +81,20 @@ private FlatTrace(
final int subtraces,
final List<Integer> traceAddress,
final String type,
final Long blockNumber,
final String blockHash,
final Integer transactionPosition,
final String transactionHash,
final Optional<String> error) {
this.action = action;
this.result = result;
this.subtraces = subtraces;
this.traceAddress = traceAddress;
this.type = type;
this.blockNumber = blockNumber;
this.blockHash = blockHash;
this.transactionPosition = transactionPosition;
this.transactionHash = transactionHash;
this.error = error;
}

Expand All @@ -75,6 +108,26 @@ public Action getAction() {
return action;
}

@JsonInclude(NON_NULL)
public Long getBlockNumber() {
return blockNumber;
}

@JsonInclude(NON_NULL)
public String getBlockHash() {
return blockHash;
}

@JsonInclude(NON_NULL)
public String getTransactionHash() {
return transactionHash;
}

@JsonInclude(NON_NULL)
public Integer getTransactionPosition() {
return transactionPosition;
}

@JsonInclude(NON_NULL)
public String getError() {
return error.orElse(null);
Expand Down Expand Up @@ -159,6 +212,10 @@ public static final class Builder {
private int subtraces;
private List<Integer> traceAddress = new ArrayList<>();
private String type = "call";
private Long blockNumber;
private String blockHash;
private String transactionHash;
private Integer transactionPosition;
private Optional<String> error = Optional.empty();

private Builder() {}
Expand Down Expand Up @@ -191,6 +248,26 @@ public String getType() {
return type;
}

public Builder blockNumber(final Long blockNumber) {
this.blockNumber = blockNumber;
return this;
}

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

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

public Builder transactionPosition(final Integer transactionPosition) {
this.transactionPosition = transactionPosition;
return this;
}

public Builder error(final Optional<String> error) {
this.error = error;
return this;
Expand All @@ -201,14 +278,24 @@ void incSubTraces() {
}

public FlatTrace build() {
return new FlatTrace(actionBuilder, resultBuilder, subtraces, traceAddress, type, error);
return new FlatTrace(
actionBuilder,
resultBuilder,
subtraces,
traceAddress,
type,
blockNumber,
blockHash,
transactionPosition,
transactionHash,
error);
}

Result.Builder getResultBuilder() {
public Result.Builder getResultBuilder() {
return resultBuilder;
}

Action.Builder getActionBuilder() {
public Action.Builder getActionBuilder() {
return actionBuilder;
}
}
Expand Down

0 comments on commit 67a0c79

Please sign in to comment.