Add debug_standardTraceBlockToFile JSON RPC method (#1392)

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
pull/1413/head
matkt 4 years ago committed by GitHub
parent c1f37f857b
commit 6034b89b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 13
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  3. 7
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java
  4. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  5. 95
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java
  6. 49
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java
  7. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java
  8. 18
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java
  9. 108
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java
  10. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  11. 11
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java
  12. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java
  13. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java
  14. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java
  15. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java
  16. 6
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java
  17. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTest.java
  18. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java
  19. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java
  20. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java
  21. 87
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java
  22. 7
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java
  23. 67
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java
  24. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java
  25. 34
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/StandardJsonTracer.java
  26. 3
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  27. 5
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java

@ -3,7 +3,8 @@
## 1.6.0-RC1
### Additions and Improvements
* Added support for the upcoming YOLOv2 ephemeral testnet and removed the flag for the deprecated YOLOv1 ephemeral testnet.
* Added support for the upcoming YOLOv2 ephemeral testnet and removed the flag for the deprecated YOLOv1 ephemeral testnet.
* Added `debug_standardTraceBlockToFile` JSON-RPC API. This API accepts a block hash and will replay the block. It returns a list of files containing the result of the trace (one file per transaction). [\#1392](https://github.com/hyperledger/besu/pull/1392)
### Bug Fixes

@ -496,7 +496,8 @@ public class RunnerBuilder {
webSocketConfiguration,
metricsConfiguration,
natService,
besuPluginContext.getNamedPlugins());
besuPluginContext.getNamedPlugins(),
dataDir);
jsonRpcHttpService =
Optional.of(
new JsonRpcHttpService(
@ -560,7 +561,8 @@ public class RunnerBuilder {
webSocketConfiguration,
metricsConfiguration,
natService,
besuPluginContext.getNamedPlugins());
besuPluginContext.getNamedPlugins(),
dataDir);
final SubscriptionManager subscriptionManager =
createSubscriptionManager(vertx, transactionPool, blockchainQueries);
@ -726,7 +728,8 @@ public class RunnerBuilder {
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
final NatService natService,
final Map<String, BesuPlugin> namedPlugins) {
final Map<String, BesuPlugin> namedPlugins,
final Path dataDir) {
final Map<String, JsonRpcMethod> methods =
new JsonRpcMethodsFactory()
.methods(
@ -750,7 +753,8 @@ public class RunnerBuilder {
webSocketConfiguration,
metricsConfiguration,
natService,
namedPlugins);
namedPlugins,
dataDir);
methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis));
return methods;
}
@ -856,7 +860,6 @@ public class RunnerBuilder {
privateWebSocketMethodsFactory.methods().forEach(websocketMethodsFactory::addMethods);
}
final WebSocketRequestHandler websocketRequestHandler =
new WebSocketRequestHandler(
vertx,

@ -47,6 +47,7 @@ import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.nat.NatService;
import java.math.BigInteger;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -113,6 +114,9 @@ public class JsonRpcTestMethodsFactory {
apis.add(RpcApis.WEB3);
apis.add(RpcApis.PRIV);
apis.add(RpcApis.DEBUG);
final Path dataDir = mock(Path.class);
return new JsonRpcMethodsFactory()
.methods(
CLIENT_VERSION,
@ -135,6 +139,7 @@ public class JsonRpcTestMethodsFactory {
webSocketConfiguration,
metricsConfiguration,
natService,
new HashMap<>());
new HashMap<>(),
dataDir);
}
}

@ -37,6 +37,7 @@ public enum RpcMethod {
DEBUG_TRACE_BLOCK("debug_traceBlock"),
DEBUG_TRACE_BLOCK_BY_HASH("debug_traceBlockByHash"),
DEBUG_TRACE_BLOCK_BY_NUMBER("debug_traceBlockByNumber"),
DEBUG_STANDARD_TRACE_BLOCK_TO_FILE("debug_standardTraceBlockToFile"),
DEBUG_TRACE_TRANSACTION("debug_traceTransaction"),
DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"),
DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"),

@ -0,0 +1,95 @@
/*
* 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 static org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer.TRACE_PATH;
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.nio.file.Path;
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;
private final Path dataDir;
public DebugStandardTraceBlockToFile(
final Supplier<TransactionTracer> transactionTracerSupplier,
final BlockchainQueries blockchainQueries,
final Path dataDir) {
this.transactionTracerSupplier = transactionTracerSupplier;
this.blockchainQueries = Suppliers.ofInstance(blockchainQueries);
this.dataDir = dataDir;
}
@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,
dataDir.resolve(TRACE_PATH));
}
protected Object emptyResult() {
final ObjectMapper mapper = new ObjectMapper();
return mapper.createArrayNode();
}
}

@ -16,28 +16,43 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import com.fasterxml.jackson.annotation.JsonCreator;
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 getTransactionHash();
@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;
}
public TraceOptions traceOptions() {
return new TraceOptions(!disableStorage, !disableMemory, !disableStack);
default TraceOptions traceOptions() {
return new TraceOptions(!disableStorage(), !disableMemory(), !disableStack());
}
}

@ -14,7 +14,6 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockReplay.TransactionAction;
import org.hyperledger.besu.ethereum.core.AbstractWorldUpdater;
import org.hyperledger.besu.ethereum.core.AbstractWorldUpdater.StackedUpdater;
import org.hyperledger.besu.ethereum.core.Block;
@ -47,7 +46,7 @@ public class BlockTracer {
return blockReplay.block(block, prepareReplayAction(tracer));
}
private TransactionAction<TransactionTrace> prepareReplayAction(
private BlockReplay.TransactionAction<TransactionTrace> prepareReplayAction(
final DebugOperationTracer tracer) {
return (transaction, header, blockchain, mutableWorldState, transactionProcessor) -> {
// if we have no prior updater, it must be the first TX, so use the block's initial state

@ -19,18 +19,32 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result;
import java.util.List;
import java.util.Optional;
public class TransactionTrace {
private final Transaction transaction;
private final Result result;
private final List<TraceFrame> traceFrames;
private final Optional<Long> time;
public TransactionTrace(
final Transaction transaction, final Result result, final List<TraceFrame> traceFrames) {
this.transaction = transaction;
this.result = result;
this.traceFrames = traceFrames;
this.time = Optional.empty();
}
public TransactionTrace(
final Transaction transaction,
final Result result,
final List<TraceFrame> traceFrames,
final Long time) {
this.transaction = transaction;
this.result = result;
this.traceFrames = traceFrames;
this.time = Optional.of(time);
}
public Transaction getTransaction() {
@ -52,4 +66,8 @@ public class TransactionTrace {
public List<TraceFrame> getTraceFrames() {
return traceFrames;
}
public Optional<Long> getTime() {
return time;
}
}

@ -14,16 +14,40 @@
*/
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.debug.TraceOptions;
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.StandardJsonTracer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Stopwatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** Used to produce debug traces of transactions */
public class TransactionTracer {
private static final Logger LOG = LogManager.getLogger();
public static final String TRACE_PATH = "traces";
private final BlockReplay blockReplay;
public TransactionTracer(final BlockReplay blockReplay) {
@ -36,6 +60,7 @@ public class TransactionTracer {
blockHash,
transactionHash,
(transaction, header, blockchain, mutableWorldState, transactionProcessor) -> {
final Stopwatch timer = Stopwatch.createStarted();
final Result result =
transactionProcessor.processTransaction(
blockchain,
@ -46,7 +71,88 @@ public class TransactionTracer {
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 StandardJsonTracer 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 Path traceDir) {
final List<String> traces = new ArrayList<>();
try {
final Optional<Hash> selectedHash =
transactionTraceParams
.map(TransactionTraceParams::getTransactionHash)
.map(Hash::fromHexString);
final boolean showMemory =
transactionTraceParams
.map(TransactionTraceParams::traceOptions)
.map(TraceOptions::isMemoryEnabled)
.orElse(true);
if (!Files.isDirectory(traceDir) && !traceDir.toFile().mkdirs()) {
throw new IOException(
String.format("Trace directory '%s' does not exist and could not be made.", traceDir));
}
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 traceFile =
traceDir
.resolve(
String.format(
"block_%.10s-%d-%.10s-%s",
blockHash.toHexString(),
i,
transaction.getHash().toHexString(),
System.currentTimeMillis()))
.toFile();
try (PrintStream out = new PrintStream(new FileOutputStream(traceFile))) {
traceTransaction(
blockHash, transaction.getHash(), new StandardJsonTracer(out, showMemory))
.ifPresent(
trace ->
out.println(
StandardJsonTracer.summaryTrace(
transaction, trace.getTime().orElseThrow(), trace.getResult())));
traces.add(traceFile.getPath());
}
}
}
} catch (Exception e) {
LOG.error("Unable to create transaction trace : {}", e.getMessage());
throw new RuntimeException(e.getMessage(), e);
}
return traces;
}
}

@ -75,6 +75,7 @@ public enum JsonRpcError {
WORLD_STATE_UNAVAILABLE(-32000, "World state unavailable"),
// Debug failures
BLOCK_NOT_FOUND(-32000, "Block not found"),
PARENT_BLOCK_NOT_FOUND(-32000, "Parent block not found"),
// Permissioning/Account allowlist errors

@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugAccountRa
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugBatchSendRawTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetBadBlocks;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugMetrics;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStandardTraceBlockToFile;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStorageRangeAt;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugTraceBlock;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugTraceBlockByHash;
@ -36,6 +37,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import java.nio.file.Path;
import java.util.Map;
public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods {
@ -46,16 +48,19 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods {
private final ProtocolSchedule protocolSchedule;
private final ObservableMetricsSystem metricsSystem;
private final TransactionPool transactionPool;
private final Path dataDir;
DebugJsonRpcMethods(
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
final ObservableMetricsSystem metricsSystem,
final TransactionPool transactionPool) {
final TransactionPool transactionPool,
final Path dataDir) {
this.blockchainQueries = blockchainQueries;
this.protocolSchedule = protocolSchedule;
this.metricsSystem = metricsSystem;
this.transactionPool = transactionPool;
this.dataDir = dataDir;
}
@Override
@ -83,6 +88,8 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods {
new DebugTraceBlockByNumber(() -> new BlockTracer(blockReplay), blockchainQueries),
new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay)),
new DebugBatchSendRawTransaction(transactionPool),
new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult));
new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult),
new DebugStandardTraceBlockToFile(
() -> new TransactionTracer(blockReplay), blockchainQueries, dataDir));
}
}

@ -37,6 +37,7 @@ import org.hyperledger.besu.nat.NatService;
import org.hyperledger.besu.plugin.BesuPlugin;
import java.math.BigInteger;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@ -67,7 +68,8 @@ public class JsonRpcMethodsFactory {
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
final NatService natService,
final Map<String, BesuPlugin> namedPlugins) {
final Map<String, BesuPlugin> namedPlugins,
final Path dataDir) {
final Map<String, JsonRpcMethod> enabled = new HashMap<>();
if (!rpcApis.isEmpty()) {
@ -85,7 +87,7 @@ public class JsonRpcMethodsFactory {
namedPlugins,
natService),
new DebugJsonRpcMethods(
blockchainQueries, protocolSchedule, metricsSystem, transactionPool),
blockchainQueries, protocolSchedule, metricsSystem, transactionPool, dataDir),
new EeaJsonRpcMethods(
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
new EthJsonRpcMethods(

@ -168,7 +168,8 @@ public abstract class AbstractJsonRpcHttpServiceTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
new HashMap<>());
new HashMap<>(),
folder.getRoot().toPath());
}
protected void startService() throws Exception {

@ -118,7 +118,8 @@ public class JsonRpcHttpServiceHostWhitelistTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
new HashMap<>()));
new HashMap<>(),
folder.getRoot().toPath()));
service = createJsonRpcHttpService();
service.start().join();

@ -144,7 +144,8 @@ public class JsonRpcHttpServiceLoginTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
new HashMap<>()));
new HashMap<>(),
folder.getRoot().toPath()));
service = createJsonRpcHttpService();
jwtAuth = service.authenticationService.get().getJwtAuthProvider();
service.start().join();

@ -211,7 +211,8 @@ public class JsonRpcHttpServiceRpcApisTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
new HashMap<>()));
new HashMap<>(),
folder.getRoot().toPath()));
final JsonRpcHttpService jsonRpcHttpService =
new JsonRpcHttpService(
vertx,
@ -304,7 +305,8 @@ public class JsonRpcHttpServiceRpcApisTest {
webSocketConfiguration,
metricsConfiguration,
natService,
new HashMap<>()));
new HashMap<>(),
folder.getRoot().toPath()));
final JsonRpcHttpService jsonRpcHttpService =
new JsonRpcHttpService(
vertx,

@ -146,7 +146,8 @@ public class JsonRpcHttpServiceTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
new HashMap<>()));
new HashMap<>(),
folder.getRoot().toPath()));
service = createJsonRpcHttpService();
service.start().join();

@ -136,7 +136,8 @@ public class JsonRpcHttpServiceTlsClientAuthTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
Collections.emptyMap()));
Collections.emptyMap(),
folder.getRoot().toPath()));
System.setProperty("javax.net.ssl.trustStore", CLIENT_AS_CA_CERT.getKeyStoreFile().toString());
System.setProperty(

@ -124,7 +124,8 @@ public class JsonRpcHttpServiceTlsMisconfigurationTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
Collections.emptyMap()));
Collections.emptyMap(),
folder.getRoot().toPath()));
}
@After

@ -127,7 +127,8 @@ public class JsonRpcHttpServiceTlsTest {
mock(WebSocketConfiguration.class),
mock(MetricsConfiguration.class),
natService,
Collections.emptyMap()));
Collections.emptyMap(),
folder.getRoot().toPath()));
service = createJsonRpcHttpService(createJsonRpcConfig());
service.start().join();
baseUrl = service.url();

@ -0,0 +1,87 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class DebugStandardTraceBlockToFileTest {
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder();
private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private final Blockchain blockchain = mock(Blockchain.class);
private final TransactionTracer transactionTracer = mock(TransactionTracer.class);
private final DebugStandardTraceBlockToFile debugStandardTraceBlockToFile =
new DebugStandardTraceBlockToFile(
() -> transactionTracer, blockchainQueries, folder.getRoot().toPath());
@Test
public void nameShouldBeDebugTraceTransaction() {
assertThat(debugStandardTraceBlockToFile.getName()).isEqualTo("debug_standardTraceBlockToFile");
}
@SuppressWarnings("rawtypes")
@Test
public void shouldTraceTheTransactionUsingTheTransactionTracer() {
final BlockDataGenerator blockGenerator = new BlockDataGenerator();
final Block genesis = blockGenerator.genesisBlock();
final Block block =
blockGenerator.block(
new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash()));
final Object[] params = new Object[] {block.getHash(), new HashMap<>()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "debug_standardTraceBlockToFile", params));
final List<String> paths = new ArrayList<>();
paths.add("path-1");
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block));
when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any(), any()))
.thenReturn(paths);
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) debugStandardTraceBlockToFile.response(request);
final List result = (ArrayList) response.getResult();
assertThat(result.size()).isEqualTo(1);
}
}

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.Collections;
import java.util.HashMap;
@ -121,7 +122,8 @@ public class DebugTraceTransactionTest {
when(blockchain.headBlockNumber()).thenReturn(12L);
when(blockchain.transactionByHash(transactionHash))
.thenReturn(Optional.of(transactionWithMetadata));
when(transactionTracer.traceTransaction(eq(blockHash), eq(transactionHash), any()))
when(transactionTracer.traceTransaction(
eq(blockHash), eq(transactionHash), any(DebugOperationTracer.class)))
.thenReturn(Optional.of(transactionTrace));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) debugTraceTransaction.response(request);
@ -184,7 +186,8 @@ public class DebugTraceTransactionTest {
when(blockHeader.getNumber()).thenReturn(12L);
when(blockchain.headBlockNumber()).thenReturn(12L);
when(blockchain.transactionByHash(transactionHash)).thenReturn(Optional.empty());
when(transactionTracer.traceTransaction(eq(blockHash), eq(transactionHash), any()))
when(transactionTracer.traceTransaction(
eq(blockHash), eq(transactionHash), any(DebugOperationTracer.class)))
.thenReturn(Optional.of(transactionTrace));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) debugTraceTransaction.response(request);

@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ImmutableTransactionTraceParams;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockBody;
@ -34,15 +35,23 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.vm.StandardJsonTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -50,6 +59,8 @@ import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TransactionTracerTest {
@Rule public TemporaryFolder traceDir = new TemporaryFolder();
@Mock private ProtocolSchedule protocolSchedule;
@Mock private Blockchain blockchain;
@ -198,4 +209,60 @@ public class TransactionTracerTest {
assertThat(transactionTrace).isEmpty();
}
@Test
public void traceTransactionToFileShouldReturnEmptyListWhenNoTransaction() {
final List<Transaction> transactions = new ArrayList<>();
final List<String> transactionTraces =
transactionTracer.traceTransactionToFile(
blockHash,
transactions,
Optional.of(ImmutableTransactionTraceParams.builder().build()),
traceDir.getRoot().toPath());
assertThat(transactionTraces).isEmpty();
}
@Test
public void traceTransactionToFileShouldReturnResultFromProcessTransaction() throws IOException {
List<Transaction> transactions = Collections.singletonList(transaction);
final Result result = mock(Result.class);
when(result.getOutput()).thenReturn(Bytes.of(0x01, 0x02));
when(blockchain.getBlockHeader(blockHash)).thenReturn(Optional.of(blockHeader));
when(blockchain.getBlockHeader(previousBlockHash)).thenReturn(Optional.of(previousBlockHeader));
when(blockBody.getTransactions()).thenReturn(Collections.singletonList(transaction));
when(blockchain.getBlockBody(blockHash)).thenReturn(Optional.of(blockBody));
final WorldUpdater updater = mutableWorldState.updater();
final Address coinbase = blockHeader.getCoinbase();
when(transactionProcessor.processTransaction(
eq(blockchain),
eq(updater),
eq(blockHeader),
eq(transaction),
eq(coinbase),
any(StandardJsonTracer.class),
any(),
any(),
any()))
.thenReturn(result);
final List<String> transactionTraces =
transactionTracer.traceTransactionToFile(
blockHash,
transactions,
Optional.of(ImmutableTransactionTraceParams.builder().build()),
traceDir.getRoot().toPath());
;
assertThat(transactionTraces.size()).isEqualTo(1);
assertThat(Files.readString(Path.of(transactionTraces.get(0))))
.contains("{\"output\":\"0102\",\"gasUsed\":\"0x0\"");
}
}

@ -71,7 +71,7 @@ public class TransactionValidationParams {
return blockReplayParams;
}
static class Builder {
public static class Builder {
private boolean allowFutureNonce = false;
private boolean checkOnchainPermissions = false;

@ -14,14 +14,12 @@
*
*/
package org.hyperledger.besu.evmtool;
package org.hyperledger.besu.ethereum.vm;
import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Operation;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.Operation.OperationResult;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.vm.operations.ReturnStack;
import java.io.PrintStream;
@ -34,19 +32,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
class EVMToolTracer implements OperationTracer {
public class StandardJsonTracer implements OperationTracer {
private final ObjectMapper objectMapper = new ObjectMapper();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final PrintStream out;
private final boolean showMemory;
EVMToolTracer(final PrintStream out, final boolean showMemory) {
public StandardJsonTracer(final PrintStream out, final boolean showMemory) {
this.out = out;
this.showMemory = showMemory;
}
static String shortNumber(final UInt256 number) {
public static String shortNumber(final UInt256 number) {
return number.isZero() ? "0x0" : number.toShortHexString();
}
@ -54,10 +52,22 @@ class EVMToolTracer implements OperationTracer {
return bytes.isZero() ? "0x0" : bytes.toShortHexString();
}
public static String summaryTrace(
final Transaction transaction, final long timer, final TransactionProcessor.Result result) {
final ObjectNode summaryLine = OBJECT_MAPPER.createObjectNode();
summaryLine.put("output", result.getOutput().toUnprefixedHexString());
summaryLine.put(
"gasUsed",
StandardJsonTracer.shortNumber(
UInt256.valueOf(transaction.getGasLimit() - result.getGasRemaining())));
summaryLine.put("time", timer);
return summaryLine.toString();
}
@Override
public void traceExecution(
final MessageFrame messageFrame, final ExecuteOperation executeOperation) {
final ObjectNode traceLine = objectMapper.createObjectNode();
final ObjectNode traceLine = OBJECT_MAPPER.createObjectNode();
final Operation currentOp = messageFrame.getCurrentOperation();
traceLine.put("pc", messageFrame.getPC());
@ -86,7 +96,9 @@ class EVMToolTracer implements OperationTracer {
if (showMemory) {
traceLine.put(
"memory",
messageFrame.readMemory(UInt256.ZERO, messageFrame.memoryWordSize()).toHexString());
messageFrame
.readMemory(UInt256.ZERO, messageFrame.memoryWordSize().multiply(32))
.toHexString());
} else {
traceLine.put("memory", "0x");
}

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.vm.Code;
import org.hyperledger.besu.ethereum.vm.EVM;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.vm.StandardJsonTracer;
import java.io.File;
import java.io.IOException;
@ -222,7 +223,7 @@ public class EvmToolCommand implements Runnable {
final OperationTracer tracer = // You should have picked Mercy.
lastLoop && showJsonResults
? new EVMToolTracer(System.out, !noMemory)
? new StandardJsonTracer(System.out, !noMemory)
: OperationTracer.NO_TRACING;
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedul
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.vm.StandardJsonTracer;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import java.io.File;
@ -128,7 +129,7 @@ public class StateTestSubCommand implements Runnable {
private void traceTestSpecs(final String test, final List<GeneralStateTestCaseEipSpec> specs) {
final OperationTracer tracer = // You should have picked Mercy.
parentCommand.showJsonResults
? new EVMToolTracer(System.out, !parentCommand.noMemory)
? new StandardJsonTracer(System.out, !parentCommand.noMemory)
: OperationTracer.NO_TRACING;
for (final GeneralStateTestCaseEipSpec spec : specs) {
@ -173,7 +174,7 @@ public class StateTestSubCommand implements Runnable {
summaryLine.put("output", result.getOutput().toUnprefixedHexString());
summaryLine.put(
"gasUsed",
EVMToolTracer.shortNumber(
StandardJsonTracer.shortNumber(
UInt256.valueOf(transaction.getGasLimit() - result.getGasRemaining())));
summaryLine.put("time", timer.elapsed(TimeUnit.NANOSECONDS));

Loading…
Cancel
Save