-
Notifications
You must be signed in to change notification settings - Fork 892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add debug_standardTraceBlockToFile JSON RPC method #1392
Changes from 3 commits
bcbb84e
d332694
bdba868
50255af
38046da
c45752e
9742da8
76cbd63
2309566
b4cffef
799674f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* 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.parameters.TransactionTraceParams; | ||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer; | ||
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.query.BlockchainQueries; | ||
import org.hyperledger.besu.ethereum.core.Block; | ||
import org.hyperledger.besu.ethereum.core.Hash; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.function.Supplier; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.google.common.base.Suppliers; | ||
|
||
public class DebugStandardTraceBlockToFile implements JsonRpcMethod { | ||
|
||
private final Supplier<TransactionTracer> transactionTracerSupplier; | ||
private final Supplier<BlockchainQueries> blockchainQueries; | ||
|
||
public DebugStandardTraceBlockToFile( | ||
final Supplier<TransactionTracer> transactionTracerSupplier, | ||
final BlockchainQueries blockchainQueries) { | ||
this.transactionTracerSupplier = transactionTracerSupplier; | ||
this.blockchainQueries = Suppliers.ofInstance(blockchainQueries); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return RpcMethod.DEBUG_STANDARD_TRACE_BLOCK_TO_FILE.getMethodName(); | ||
} | ||
|
||
@Override | ||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { | ||
final Hash blockHash = requestContext.getRequiredParameter(0, Hash.class); | ||
final Optional<TransactionTraceParams> transactionTraceParams = | ||
requestContext.getOptionalParameter(1, TransactionTraceParams.class); | ||
|
||
return blockchainQueries | ||
.get() | ||
.getBlockchain() | ||
.getBlockByHash(blockHash) | ||
.map( | ||
block -> | ||
(JsonRpcResponse) | ||
new JsonRpcSuccessResponse( | ||
requestContext.getRequest().getId(), | ||
traceBlock(block, transactionTraceParams))) | ||
.orElse( | ||
new JsonRpcErrorResponse( | ||
requestContext.getRequest().getId(), JsonRpcError.BLOCK_NOT_FOUND)); | ||
} | ||
|
||
private List<String> traceBlock( | ||
final Block block, final Optional<TransactionTraceParams> transactionTraceParams) { | ||
return transactionTracerSupplier | ||
.get() | ||
.traceTransactionToFile( | ||
block.getHash(), block.getBody().getTransactions(), transactionTraceParams); | ||
} | ||
|
||
protected Object emptyResult() { | ||
final ObjectMapper mapper = new ObjectMapper(); | ||
return mapper.createArrayNode(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,30 +14,53 @@ | |
*/ | ||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; | ||
|
||
import org.hyperledger.besu.ethereum.core.Hash; | ||
import org.hyperledger.besu.ethereum.debug.TraceOptions; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import java.util.Optional; | ||
import javax.annotation.Nullable; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||
import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||
import org.immutables.value.Value; | ||
|
||
@Value.Immutable | ||
@JsonSerialize(as = ImmutableTransactionTraceParams.class) | ||
@JsonDeserialize(as = ImmutableTransactionTraceParams.class) | ||
@JsonIgnoreProperties(ignoreUnknown = true) | ||
public class TransactionTraceParams { | ||
|
||
private final boolean disableStorage; | ||
private final boolean disableMemory; | ||
private final boolean disableStack; | ||
|
||
@JsonCreator() | ||
public TransactionTraceParams( | ||
@JsonProperty("disableStorage") final boolean disableStorage, | ||
@JsonProperty("disableMemory") final boolean disableMemory, | ||
@JsonProperty("disableStack") final boolean disableStack) { | ||
this.disableStorage = disableStorage; | ||
this.disableMemory = disableMemory; | ||
this.disableStack = disableStack; | ||
public interface TransactionTraceParams { | ||
|
||
@JsonProperty("txHash") | ||
@Nullable | ||
String transactionHash(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use standard getter/setters There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
@JsonProperty(value = "disableStorage") | ||
@Value.Default | ||
default boolean disableStorage() { | ||
return false; | ||
} | ||
|
||
@JsonProperty(value = "disableMemory") | ||
@Value.Default | ||
default boolean disableMemory() { | ||
return false; | ||
} | ||
|
||
@JsonProperty(value = "disableStack") | ||
@Value.Default | ||
default boolean disableStack() { | ||
return false; | ||
} | ||
|
||
default TraceOptions traceOptions() { | ||
return new TraceOptions(!disableStorage(), !disableMemory(), !disableStack()); | ||
} | ||
|
||
public TraceOptions traceOptions() { | ||
return new TraceOptions(!disableStorage, !disableMemory, !disableStack); | ||
default Optional<Hash> getTransactionHash() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will need to be renamed, or dropped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
return transactionHash() == null | ||
? Optional.empty() | ||
: Optional.of(Hash.fromHexString(transactionHash())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,12 +14,26 @@ | |
*/ | ||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor; | ||
|
||
import static java.util.function.Predicate.isEqual; | ||
|
||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams; | ||
import org.hyperledger.besu.ethereum.core.Hash; | ||
import org.hyperledger.besu.ethereum.core.Transaction; | ||
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result; | ||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; | ||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; | ||
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; | ||
import org.hyperledger.besu.ethereum.vm.EVMToolTracer; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.PrintStream; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.google.common.base.Stopwatch; | ||
|
||
/** Used to produce debug traces of transactions */ | ||
public class TransactionTracer { | ||
|
@@ -36,6 +50,7 @@ public Optional<TransactionTrace> traceTransaction( | |
blockHash, | ||
transactionHash, | ||
(transaction, header, blockchain, mutableWorldState, transactionProcessor) -> { | ||
final Stopwatch timer = Stopwatch.createStarted(); | ||
final Result result = | ||
transactionProcessor.processTransaction( | ||
blockchain, | ||
|
@@ -46,7 +61,71 @@ public Optional<TransactionTrace> traceTransaction( | |
tracer, | ||
new BlockHashLookup(header, blockchain), | ||
false); | ||
return new TransactionTrace(transaction, result, tracer.getTraceFrames()); | ||
timer.stop(); | ||
return new TransactionTrace( | ||
transaction, result, tracer.getTraceFrames(), timer.elapsed(TimeUnit.NANOSECONDS)); | ||
}); | ||
} | ||
|
||
public Optional<TransactionTrace> traceTransaction( | ||
final Hash blockHash, final Hash transactionHash, final EVMToolTracer tracer) { | ||
return blockReplay.beforeTransactionInBlock( | ||
blockHash, | ||
transactionHash, | ||
(transaction, header, blockchain, mutableWorldState, transactionProcessor) -> { | ||
final Stopwatch timer = Stopwatch.createStarted(); | ||
final Result result = | ||
transactionProcessor.processTransaction( | ||
blockchain, | ||
mutableWorldState.updater(), | ||
header, | ||
transaction, | ||
header.getCoinbase(), | ||
tracer, | ||
new BlockHashLookup(header, blockchain), | ||
false, | ||
new TransactionValidationParams.Builder().allowFutureNonce(true).build()); | ||
|
||
timer.stop(); | ||
return new TransactionTrace( | ||
transaction, result, new ArrayList<>(), timer.elapsed(TimeUnit.NANOSECONDS)); | ||
}); | ||
} | ||
|
||
public List<String> traceTransactionToFile( | ||
final Hash blockHash, | ||
final List<Transaction> transactions, | ||
final Optional<TransactionTraceParams> transactionTraceParams) { | ||
final List<String> traces = new ArrayList<>(); | ||
try { | ||
final Optional<Hash> selectedHash = | ||
transactionTraceParams.flatMap(TransactionTraceParams::getTransactionHash); | ||
final boolean showMemory = | ||
transactionTraceParams.map(TransactionTraceParams::disableMemory).orElse(true); | ||
for (int i = 0; i < transactions.size(); i++) { | ||
final Transaction transaction = transactions.get(i); | ||
if (selectedHash.isEmpty() | ||
|| selectedHash.filter(isEqual(transaction.getHash())).isPresent()) { | ||
final File tmpFile = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want these going to tmp? perhaps our data root under traces? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a strong opinion on it. I did this to be consistent with the other clients. But I just changed to put it in the data/traces now |
||
File.createTempFile( | ||
String.format( | ||
"block_%.10s-%d-%.10s-", | ||
blockHash.toHexString(), i, transaction.getHash().toHexString()), | ||
""); | ||
try (PrintStream out = new PrintStream(new FileOutputStream(tmpFile))) { | ||
traceTransaction(blockHash, transaction.getHash(), new EVMToolTracer(out, showMemory)) | ||
.ifPresent( | ||
trace -> | ||
out.println( | ||
EVMToolTracer.summaryTrace( | ||
transaction, trace.getTime().orElseThrow(), trace.getResult()))); | ||
traces.add(tmpFile.getPath()); | ||
} | ||
} | ||
} | ||
} catch (Exception e) { | ||
throw new RuntimeException(String.format("Unable to trace transaction : %s", e.getMessage())); | ||
} | ||
return traces; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved to the 1.6.0-RC1 section once merged.