From bcbb84e63ea6d0876870fb623a2a5151385ec06d Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Wed, 23 Sep 2020 18:18:03 +0200 Subject: [PATCH 01/11] add debug_standardTraceBlockToFile JSON RPC method Signed-off-by: Karim TAAM --- .../besu/ethereum/api/jsonrpc/RpcMethod.java | 1 + .../DebugStandardTraceBlockToFile.java | 86 +++++++++++++++++++ .../parameters/TransactionTraceParams.java | 57 ++++++++---- .../internal/processor/BlockTracer.java | 3 +- .../internal/processor/TransactionTrace.java | 18 ++++ .../internal/processor/TransactionTracer.java | 81 ++++++++++++++++- .../internal/response/JsonRpcError.java | 1 + .../jsonrpc/methods/DebugJsonRpcMethods.java | 5 +- .../DebugStandardTraceBlockToFileTest.java | 82 ++++++++++++++++++ .../methods/DebugTraceTransactionTest.java | 7 +- .../processor/TransactionTracerTest.java | 61 +++++++++++++ .../mainnet/TransactionValidationParams.java | 2 +- .../besu/ethereum/vm}/EVMToolTracer.java | 30 ++++--- .../besu/evmtool/EvmToolCommand.java | 1 + .../besu/evmtool/StateTestSubCommand.java | 1 + 15 files changed, 402 insertions(+), 34 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java rename ethereum/{evmtool/src/main/java/org/hyperledger/besu/evmtool => core/src/main/java/org/hyperledger/besu/ethereum/vm}/EVMToolTracer.java (79%) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index aecbcf6e8e1..6a3c68434bb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -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"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java new file mode 100644 index 00000000000..a65ab19f812 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java @@ -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 transactionTracerSupplier; + private final Supplier blockchainQueries; + + public DebugStandardTraceBlockToFile( + final Supplier 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 = + 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 traceBlock( + final Block block, final Optional transactionTraceParams) { + return transactionTracerSupplier + .get() + .traceTransactionToFile( + block.getHash(), block.getBody().getTransactions(), transactionTraceParams); + } + + protected Object emptyResult() { + final ObjectMapper mapper = new ObjectMapper(); + return mapper.createArrayNode(); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java index 7fb1bb9491d..b704ee82592 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java @@ -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(); + + @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 getTransactionHash() { + return transactionHash() == null + ? Optional.empty() + : Optional.of(Hash.fromHexString(transactionHash())); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java index 1552c69df09..c6b53de1816 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java @@ -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 Optional trace(final Block block, final DebugOperationTracer return blockReplay.block(block, prepareReplayAction(tracer)); } - private TransactionAction prepareReplayAction( + private BlockReplay.TransactionAction 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 diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java index 94c1eb13fc0..8bac5f1e9fb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java @@ -19,18 +19,32 @@ 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 traceFrames; + private final Optional time; public TransactionTrace( final Transaction transaction, final Result result, final List traceFrames) { this.transaction = transaction; this.result = result; this.traceFrames = traceFrames; + this.time = Optional.empty(); + } + + public TransactionTrace( + final Transaction transaction, + final Result result, + final List 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 Result getResult() { public List getTraceFrames() { return traceFrames; } + + public Optional getTime() { + return time; + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index 1f37f912575..d4787468660 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -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 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 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 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 traceTransactionToFile( + final Hash blockHash, + final List transactions, + final Optional transactionTraceParams) { + final List traces = new ArrayList<>(); + try { + final Optional 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 = + 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; + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 00aaafc52ac..8935f861bd1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -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 diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index 4df32e8ffed..c5e36e0828a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -20,6 +20,7 @@ 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; @@ -83,6 +84,8 @@ protected Map create() { 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)); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java new file mode 100644 index 00000000000..417a69b036d --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java @@ -0,0 +1,82 @@ +/* + * 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.Test; + +public class DebugStandardTraceBlockToFileTest { + + 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); + + @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 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())) + .thenReturn(paths); + final JsonRpcSuccessResponse response = + (JsonRpcSuccessResponse) debugStandardTraceBlockToFile.response(request); + final List result = (ArrayList) response.getResult(); + + assertThat(result.size()).isEqualTo(1); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java index 025beebb4bd..801dab36e93 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java @@ -36,6 +36,7 @@ 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 void shouldTraceTheTransactionUsingTheTransactionTracer() { 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 void shouldNotTraceTheTransactionIfNotFound() { 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); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 6d2bcf77159..1571f5aa6dc 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -20,6 +20,7 @@ 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,13 +35,19 @@ 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.EVMToolTracer; 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.Test; import org.junit.runner.RunWith; @@ -198,4 +205,58 @@ public void traceTransactionShouldReturnEmptyResultWhenBlockIsNotAvailable() { assertThat(transactionTrace).isEmpty(); } + + @Test + public void traceTransactionToFileShouldReturnEmptyListWhenNoTransaction() { + + final List transactions = new ArrayList<>(); + + final List transactionTraces = + transactionTracer.traceTransactionToFile( + blockHash, + transactions, + Optional.of(ImmutableTransactionTraceParams.builder().build())); + + assertThat(transactionTraces).isEmpty(); + } + + @Test + public void traceTransactionToFileShouldReturnResultFromProcessTransaction() throws IOException { + + List 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(EVMToolTracer.class), + any(), + any(), + any())) + .thenReturn(result); + + final List transactionTraces = + transactionTracer.traceTransactionToFile( + blockHash, + transactions, + Optional.of(ImmutableTransactionTraceParams.builder().build())); + ; + + assertThat(transactionTraces.size()).isEqualTo(1); + assertThat(Files.readString(Path.of(transactionTraces.get(0)))) + .contains("{\"output\":\"0102\",\"gasUsed\":\"0x0\""); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java index 97293eaf321..43940c27d65 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java @@ -71,7 +71,7 @@ public static TransactionValidationParams blockReplay() { return blockReplayParams; } - static class Builder { + public static class Builder { private boolean allowFutureNonce = false; private boolean checkOnchainPermissions = false; diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EVMToolTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java similarity index 79% rename from ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EVMToolTracer.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java index 4d1f8f7bf5d..1db6525bb6f 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EVMToolTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java @@ -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 org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; -class EVMToolTracer implements OperationTracer { +public class EVMToolTracer 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 EVMToolTracer(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 @@ private static String shortBytes(final Bytes bytes) { 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", + EVMToolTracer.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()); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index 29c95d04db2..dea36398862 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.Code; import org.hyperledger.besu.ethereum.vm.EVM; +import org.hyperledger.besu.ethereum.vm.EVMToolTracer; import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.vm.OperationTracer; diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 4b85c55e321..23686a50ecc 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; +import org.hyperledger.besu.ethereum.vm.EVMToolTracer; import org.hyperledger.besu.ethereum.vm.OperationTracer; import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; From d332694f46ca845b0d8476d3066d809c8398df65 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Wed, 23 Sep 2020 18:36:57 +0200 Subject: [PATCH 02/11] update CHANGELOG.md Signed-off-by: Karim TAAM --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9dfed57079..dbc1930d8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Additions and Improvements * The new version of the [web3js-eea library (v0.10)](https://github.com/PegaSysEng/web3js-eea) supports the onchain privacy group management changes made in Besu v1.5.3. +* 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 * Added `debug_getBadBlocks` JSON-RPC API to analyze and detect consensus flaws. Even if a block is rejected it will be returned by this method [\#1378](https://github.com/hyperledger/besu/pull/1378) From 50255af8e93df0aaac67ddee16548f79ac68265d Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Thu, 24 Sep 2020 14:46:54 +0200 Subject: [PATCH 03/11] fix review issues Signed-off-by: Karim TAAM --- .../org/hyperledger/besu/RunnerBuilder.java | 13 +- .../DebugStandardTraceBlockToFile.java | 13 +- .../parameters/TransactionTraceParams.java | 10 +- .../internal/processor/TransactionTracer.java | 59 +++++--- .../jsonrpc/methods/DebugJsonRpcMethods.java | 8 +- .../methods/JsonRpcMethodsFactory.java | 6 +- .../AbstractJsonRpcHttpServiceTest.java | 3 +- .../JsonRpcHttpServiceHostWhitelistTest.java | 3 +- .../jsonrpc/JsonRpcHttpServiceLoginTest.java | 3 +- .../JsonRpcHttpServiceRpcApisTest.java | 6 +- .../api/jsonrpc/JsonRpcHttpServiceTest.java | 3 +- .../JsonRpcHttpServiceTlsClientAuthTest.java | 3 +- ...RpcHttpServiceTlsMisconfigurationTest.java | 3 +- .../jsonrpc/JsonRpcHttpServiceTlsTest.java | 3 +- .../DebugStandardTraceBlockToFileTest.java | 9 +- .../processor/TransactionTracerTest.java | 14 +- .../besu/ethereum/vm/EVMToolTracer.java | 126 ------------------ .../besu/evmtool/EvmToolCommand.java | 4 +- .../besu/evmtool/StateTestSubCommand.java | 6 +- 19 files changed, 113 insertions(+), 182 deletions(-) delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 6d9c8659411..177ebf3a8dd 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -496,7 +496,8 @@ public Runner build() { webSocketConfiguration, metricsConfiguration, natService, - besuPluginContext.getNamedPlugins()); + besuPluginContext.getNamedPlugins(), + dataDir); jsonRpcHttpService = Optional.of( new JsonRpcHttpService( @@ -560,7 +561,8 @@ public Runner build() { webSocketConfiguration, metricsConfiguration, natService, - besuPluginContext.getNamedPlugins()); + besuPluginContext.getNamedPlugins(), + dataDir); final SubscriptionManager subscriptionManager = createSubscriptionManager(vertx, transactionPool, blockchainQueries); @@ -726,7 +728,8 @@ private Map jsonRpcMethods( final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, final NatService natService, - final Map namedPlugins) { + final Map namedPlugins, + final Path dataDir) { final Map methods = new JsonRpcMethodsFactory() .methods( @@ -750,7 +753,8 @@ private Map jsonRpcMethods( webSocketConfiguration, metricsConfiguration, natService, - namedPlugins); + namedPlugins, + dataDir); methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis)); return methods; } @@ -856,7 +860,6 @@ private WebSocketService createWebsocketService( privateWebSocketMethodsFactory.methods().forEach(websocketMethodsFactory::addMethods); } - final WebSocketRequestHandler websocketRequestHandler = new WebSocketRequestHandler( vertx, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java index a65ab19f812..c1829fa259b 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java @@ -14,6 +14,8 @@ */ 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; @@ -26,6 +28,7 @@ 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; @@ -37,12 +40,15 @@ public class DebugStandardTraceBlockToFile implements JsonRpcMethod { private final Supplier transactionTracerSupplier; private final Supplier blockchainQueries; + private final Path dataDir; public DebugStandardTraceBlockToFile( final Supplier transactionTracerSupplier, - final BlockchainQueries blockchainQueries) { + final BlockchainQueries blockchainQueries, + final Path dataDir) { this.transactionTracerSupplier = transactionTracerSupplier; this.blockchainQueries = Suppliers.ofInstance(blockchainQueries); + this.dataDir = dataDir; } @Override @@ -76,7 +82,10 @@ private List traceBlock( return transactionTracerSupplier .get() .traceTransactionToFile( - block.getHash(), block.getBody().getTransactions(), transactionTraceParams); + block.getHash(), + block.getBody().getTransactions(), + transactionTraceParams, + dataDir.resolve(TRACE_PATH)); } protected Object emptyResult() { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java index b704ee82592..da7014f5bec 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TransactionTraceParams.java @@ -14,10 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; -import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.debug.TraceOptions; -import java.util.Optional; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -34,7 +32,7 @@ public interface TransactionTraceParams { @JsonProperty("txHash") @Nullable - String transactionHash(); + String getTransactionHash(); @JsonProperty(value = "disableStorage") @Value.Default @@ -57,10 +55,4 @@ default boolean disableStack() { default TraceOptions traceOptions() { return new TraceOptions(!disableStorage(), !disableMemory(), !disableStack()); } - - default Optional getTransactionHash() { - return transactionHash() == null - ? Optional.empty() - : Optional.of(Hash.fromHexString(transactionHash())); - } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index d4787468660..a8328101ac1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -19,25 +19,35 @@ 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.EVMToolTracer; +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) { @@ -68,7 +78,7 @@ public Optional traceTransaction( } public Optional traceTransaction( - final Hash blockHash, final Hash transactionHash, final EVMToolTracer tracer) { + final Hash blockHash, final Hash transactionHash, final StandardJsonTracer tracer) { return blockReplay.beforeTransactionInBlock( blockHash, transactionHash, @@ -95,36 +105,53 @@ public Optional traceTransaction( public List traceTransactionToFile( final Hash blockHash, final List transactions, - final Optional transactionTraceParams) { + final Optional transactionTraceParams, + final Path traceDir) { final List traces = new ArrayList<>(); try { final Optional selectedHash = - transactionTraceParams.flatMap(TransactionTraceParams::getTransactionHash); + transactionTraceParams + .map(TransactionTraceParams::getTransactionHash) + .map(Hash::fromHexString); final boolean showMemory = - transactionTraceParams.map(TransactionTraceParams::disableMemory).orElse(true); + 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 tmpFile = - 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)) + 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( - EVMToolTracer.summaryTrace( + StandardJsonTracer.summaryTrace( transaction, trace.getTime().orElseThrow(), trace.getResult()))); - traces.add(tmpFile.getPath()); + traces.add(traceFile.getPath()); } } } } catch (Exception e) { - throw new RuntimeException(String.format("Unable to trace transaction : %s", e.getMessage())); + LOG.error("Unable to create transaction trace : {}", e.getMessage()); + throw new RuntimeException(e.getMessage(), e); } return traces; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index c5e36e0828a..d7e51d78486 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -37,6 +37,7 @@ 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 { @@ -47,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 @@ -86,6 +90,6 @@ protected Map create() { new DebugBatchSendRawTransaction(transactionPool), new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult), new DebugStandardTraceBlockToFile( - () -> new TransactionTracer(blockReplay), blockchainQueries)); + () -> new TransactionTracer(blockReplay), blockchainQueries, dataDir)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java index 45af5338de0..8b0aa3f54f2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java @@ -37,6 +37,7 @@ 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 Map methods( final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, final NatService natService, - final Map namedPlugins) { + final Map namedPlugins, + final Path dataDir) { final Map enabled = new HashMap<>(); if (!rpcApis.isEmpty()) { @@ -85,7 +87,7 @@ public Map methods( namedPlugins, natService), new DebugJsonRpcMethods( - blockchainQueries, protocolSchedule, metricsSystem, transactionPool), + blockchainQueries, protocolSchedule, metricsSystem, transactionPool, dataDir), new EeaJsonRpcMethods( blockchainQueries, protocolSchedule, transactionPool, privacyParameters), new EthJsonRpcMethods( diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java index 6b8e11ee41c..25c8aca3470 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java @@ -168,7 +168,8 @@ protected Map getRpcMethods( mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - new HashMap<>()); + new HashMap<>(), + folder.getRoot().toPath()); } protected void startService() throws Exception { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java index 87e23d0f1fe..d9457541f55 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java @@ -118,7 +118,8 @@ public void initServerAndClient() throws Exception { mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - new HashMap<>())); + new HashMap<>(), + folder.getRoot().toPath())); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java index a0ed2caf690..bdc291fb129 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -144,7 +144,8 @@ public static void initServerAndClient() throws Exception { mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - new HashMap<>())); + new HashMap<>(), + folder.getRoot().toPath())); service = createJsonRpcHttpService(); jwtAuth = service.authenticationService.get().getJwtAuthProvider(); service.start().join(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index 3e405c8f5cf..b2ef0331975 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -211,7 +211,8 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - new HashMap<>())); + new HashMap<>(), + folder.getRoot().toPath())); final JsonRpcHttpService jsonRpcHttpService = new JsonRpcHttpService( vertx, @@ -304,7 +305,8 @@ private JsonRpcHttpService createJsonRpcHttpService( webSocketConfiguration, metricsConfiguration, natService, - new HashMap<>())); + new HashMap<>(), + folder.getRoot().toPath())); final JsonRpcHttpService jsonRpcHttpService = new JsonRpcHttpService( vertx, diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTest.java index 144058cadb3..bfbafa05ad3 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTest.java @@ -146,7 +146,8 @@ public static void initServerAndClient() throws Exception { mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - new HashMap<>())); + new HashMap<>(), + folder.getRoot().toPath())); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java index 2d8595da467..a33ea789094 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java @@ -136,7 +136,8 @@ public void initServer() throws Exception { 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( diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java index 854d363a0ed..3c95e13cf4e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java @@ -124,7 +124,8 @@ public void beforeEach() throws IOException { mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - Collections.emptyMap())); + Collections.emptyMap(), + folder.getRoot().toPath())); } @After diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java index 81d7973f804..040430c79ec 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java @@ -127,7 +127,8 @@ public void initServer() throws Exception { mock(WebSocketConfiguration.class), mock(MetricsConfiguration.class), natService, - Collections.emptyMap())); + Collections.emptyMap(), + folder.getRoot().toPath())); service = createJsonRpcHttpService(createJsonRpcConfig()); service.start().join(); baseUrl = service.url(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java index 417a69b036d..65943939088 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java @@ -34,15 +34,20 @@ 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); + new DebugStandardTraceBlockToFile( + () -> transactionTracer, blockchainQueries, folder.getRoot().toPath()); @Test public void nameShouldBeDebugTraceTransaction() { @@ -71,7 +76,7 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block)); - when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any())) + when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any(), any())) .thenReturn(paths); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugStandardTraceBlockToFile.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 1571f5aa6dc..865cfc0bc6c 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -35,7 +35,7 @@ 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.EVMToolTracer; +import org.hyperledger.besu.ethereum.vm.StandardJsonTracer; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.io.IOException; @@ -49,7 +49,9 @@ 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; @@ -57,6 +59,8 @@ @RunWith(MockitoJUnitRunner.class) public class TransactionTracerTest { + @Rule public TemporaryFolder traceDir = new TemporaryFolder(); + @Mock private ProtocolSchedule protocolSchedule; @Mock private Blockchain blockchain; @@ -215,7 +219,8 @@ public void traceTransactionToFileShouldReturnEmptyListWhenNoTransaction() { transactionTracer.traceTransactionToFile( blockHash, transactions, - Optional.of(ImmutableTransactionTraceParams.builder().build())); + Optional.of(ImmutableTransactionTraceParams.builder().build()), + traceDir.getRoot().toPath()); assertThat(transactionTraces).isEmpty(); } @@ -242,7 +247,7 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr eq(blockHeader), eq(transaction), eq(coinbase), - any(EVMToolTracer.class), + any(StandardJsonTracer.class), any(), any(), any())) @@ -252,7 +257,8 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr transactionTracer.traceTransactionToFile( blockHash, transactions, - Optional.of(ImmutableTransactionTraceParams.builder().build())); + Optional.of(ImmutableTransactionTraceParams.builder().build()), + traceDir.getRoot().toPath()); ; assertThat(transactionTraces.size()).isEqualTo(1); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java deleted file mode 100644 index 1db6525bb6f..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVMToolTracer.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.vm; - -import org.hyperledger.besu.ethereum.core.Gas; -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.operations.ReturnStack; - -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; -import java.util.Optional; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.units.bigints.UInt256; - -public class EVMToolTracer implements OperationTracer { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private final PrintStream out; - private final boolean showMemory; - - public EVMToolTracer(final PrintStream out, final boolean showMemory) { - this.out = out; - this.showMemory = showMemory; - } - - public static String shortNumber(final UInt256 number) { - return number.isZero() ? "0x0" : number.toShortHexString(); - } - - private static String shortBytes(final Bytes bytes) { - 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", - EVMToolTracer.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 = OBJECT_MAPPER.createObjectNode(); - - final Operation currentOp = messageFrame.getCurrentOperation(); - traceLine.put("pc", messageFrame.getPC()); - traceLine.put("op", Bytes.of(currentOp.getOpcode()).toInt()); - traceLine.put("gas", shortNumber(messageFrame.getRemainingGas().asUInt256())); - traceLine.putNull("gasCost"); - traceLine.putNull("memory"); - traceLine.putNull("memSize"); - final ArrayNode stack = traceLine.putArray("stack"); - for (int i = messageFrame.stackSize() - 1; i >= 0; i--) { - stack.add(shortBytes(messageFrame.getStackItem(i))); - } - final ArrayNode returnStack = traceLine.putArray("returnStack"); - final ReturnStack rs = messageFrame.getReturnStack(); - for (int i = rs.size() - 1; i >= 0; i--) { - returnStack.add(rs.get(i)); - } - Bytes returnData = messageFrame.getReturnData(); - traceLine.put("returnData", returnData.size() > 0 ? returnData.toHexString() : null); - traceLine.put("depth", messageFrame.getMessageStackDepth() + 1); - traceLine.put("refund", messageFrame.getGasRefund().toLong()); - - final OperationResult executeResult = executeOperation.execute(); - traceLine.put( - "gasCost", executeResult.getGasCost().map(gas -> shortNumber(gas.asUInt256())).orElse("")); - if (showMemory) { - traceLine.put( - "memory", - messageFrame.readMemory(UInt256.ZERO, messageFrame.memoryWordSize()).toHexString()); - } else { - traceLine.put("memory", "0x"); - } - traceLine.put("memSize", messageFrame.memoryByteSize()); - - final String error = - executeResult - .getHaltReason() - .map(ExceptionalHaltReason::getDescription) - .orElse( - messageFrame - .getRevertReason() - .map(bytes -> new String(bytes.toArrayUnsafe(), StandardCharsets.UTF_8)) - .orElse("")); - traceLine.put("opName", currentOp.getName()); - traceLine.put("error", error); - out.println(traceLine.toString()); - } - - @Override - public void tracePrecompileCall( - final MessageFrame frame, final Gas gasRequirement, final Bytes output) {} - - @Override - public void traceAccountCreationResult( - final MessageFrame frame, final Optional haltReason) {} -} diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index dea36398862..4f4b23740ec 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -35,9 +35,9 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.Code; import org.hyperledger.besu.ethereum.vm.EVM; -import org.hyperledger.besu.ethereum.vm.EVMToolTracer; 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; @@ -220,7 +220,7 @@ public void run() { 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 messageFrameStack = new ArrayDeque<>(); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 23686a50ecc..427dca0e035 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -35,8 +35,8 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; -import org.hyperledger.besu.ethereum.vm.EVMToolTracer; 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; @@ -129,7 +129,7 @@ private void executeStateTest(final Map genera private void traceTestSpecs(final String test, final List 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) { @@ -174,7 +174,7 @@ private void traceTestSpecs(final String test, final List Date: Thu, 24 Sep 2020 14:47:21 +0200 Subject: [PATCH 04/11] fix review issues Signed-off-by: Karim TAAM --- .../besu/ethereum/vm/StandardJsonTracer.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/StandardJsonTracer.java diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/StandardJsonTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/StandardJsonTracer.java new file mode 100644 index 00000000000..38f3fd5f73b --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/StandardJsonTracer.java @@ -0,0 +1,128 @@ +/* + * 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.vm; + +import org.hyperledger.besu.ethereum.core.Gas; +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.operations.ReturnStack; + +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +public class StandardJsonTracer implements OperationTracer { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final PrintStream out; + private final boolean showMemory; + + public StandardJsonTracer(final PrintStream out, final boolean showMemory) { + this.out = out; + this.showMemory = showMemory; + } + + public static String shortNumber(final UInt256 number) { + return number.isZero() ? "0x0" : number.toShortHexString(); + } + + private static String shortBytes(final Bytes bytes) { + 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 = OBJECT_MAPPER.createObjectNode(); + + final Operation currentOp = messageFrame.getCurrentOperation(); + traceLine.put("pc", messageFrame.getPC()); + traceLine.put("op", Bytes.of(currentOp.getOpcode()).toInt()); + traceLine.put("gas", shortNumber(messageFrame.getRemainingGas().asUInt256())); + traceLine.putNull("gasCost"); + traceLine.putNull("memory"); + traceLine.putNull("memSize"); + final ArrayNode stack = traceLine.putArray("stack"); + for (int i = messageFrame.stackSize() - 1; i >= 0; i--) { + stack.add(shortBytes(messageFrame.getStackItem(i))); + } + final ArrayNode returnStack = traceLine.putArray("returnStack"); + final ReturnStack rs = messageFrame.getReturnStack(); + for (int i = rs.size() - 1; i >= 0; i--) { + returnStack.add(rs.get(i)); + } + Bytes returnData = messageFrame.getReturnData(); + traceLine.put("returnData", returnData.size() > 0 ? returnData.toHexString() : null); + traceLine.put("depth", messageFrame.getMessageStackDepth() + 1); + traceLine.put("refund", messageFrame.getGasRefund().toLong()); + + final OperationResult executeResult = executeOperation.execute(); + traceLine.put( + "gasCost", executeResult.getGasCost().map(gas -> shortNumber(gas.asUInt256())).orElse("")); + if (showMemory) { + traceLine.put( + "memory", + messageFrame + .readMemory(UInt256.ZERO, messageFrame.memoryWordSize().multiply(32)) + .toHexString()); + } else { + traceLine.put("memory", "0x"); + } + traceLine.put("memSize", messageFrame.memoryByteSize()); + + final String error = + executeResult + .getHaltReason() + .map(ExceptionalHaltReason::getDescription) + .orElse( + messageFrame + .getRevertReason() + .map(bytes -> new String(bytes.toArrayUnsafe(), StandardCharsets.UTF_8)) + .orElse("")); + traceLine.put("opName", currentOp.getName()); + traceLine.put("error", error); + out.println(traceLine.toString()); + } + + @Override + public void tracePrecompileCall( + final MessageFrame frame, final Gas gasRequirement, final Bytes output) {} + + @Override + public void traceAccountCreationResult( + final MessageFrame frame, final Optional haltReason) {} +} From 9742da8f3986a511b32a1be23f969b8fbb1581b6 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Thu, 24 Sep 2020 16:12:10 +0200 Subject: [PATCH 05/11] fix pipeline Signed-off-by: Karim TAAM --- .../ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java index 6bc30508814..e6f26158e91 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java @@ -47,6 +47,7 @@ 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 Map methods() { 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 Map methods() { webSocketConfiguration, metricsConfiguration, natService, - new HashMap<>()); + new HashMap<>(), + dataDir); } } From b453449e7175c0748c3e93f3296c7900abd070c0 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Thu, 24 Sep 2020 17:24:15 +0200 Subject: [PATCH 06/11] add debug_standardTraceBadBlockToFile Signed-off-by: Karim TAAM --- .../besu/ethereum/api/jsonrpc/RpcMethod.java | 1 + .../DebugStandardTraceBadBlockToFile.java | 78 +++++++++++++++ .../DebugStandardTraceBlockToFile.java | 4 +- .../internal/processor/BlockReplay.java | 14 ++- .../jsonrpc/methods/DebugJsonRpcMethods.java | 8 +- .../DebugStandardTraceBadBlockToFileTest.java | 99 +++++++++++++++++++ .../processor/TransactionTracerTest.java | 1 - .../besu/ethereum/chain/BadBlockManager.java | 10 ++ .../ethereum/MainnetBlockValidatorTest.java | 18 ++++ 9 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 6a3c68434bb..d28cb3d67f6 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -38,6 +38,7 @@ public enum RpcMethod { DEBUG_TRACE_BLOCK_BY_HASH("debug_traceBlockByHash"), DEBUG_TRACE_BLOCK_BY_NUMBER("debug_traceBlockByNumber"), DEBUG_STANDARD_TRACE_BLOCK_TO_FILE("debug_standardTraceBlockToFile"), + DEBUG_STANDARD_TRACE_BAD_BLOCK_TO_FILE("debug_standardTraceBadBlockToFile"), DEBUG_TRACE_TRANSACTION("debug_traceTransaction"), DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"), DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java new file mode 100644 index 00000000000..b049cdd3ba8 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java @@ -0,0 +1,78 @@ +/* + * 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.chain.BadBlockManager; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; + +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Supplier; + +public class DebugStandardTraceBadBlockToFile extends DebugStandardTraceBlockToFile + implements JsonRpcMethod { + + private final ProtocolSchedule protocolSchedule; + + public DebugStandardTraceBadBlockToFile( + final Supplier transactionTracerSupplier, + final BlockchainQueries blockchainQueries, + final ProtocolSchedule protocolSchedule, + final Path dataDir) { + super(transactionTracerSupplier, blockchainQueries, dataDir); + this.protocolSchedule = protocolSchedule; + } + + @Override + public String getName() { + return RpcMethod.DEBUG_STANDARD_TRACE_BAD_BLOCK_TO_FILE.getMethodName(); + } + + @Override + public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { + final Hash blockHash = requestContext.getRequiredParameter(0, Hash.class); + final Optional transactionTraceParams = + requestContext.getOptionalParameter(1, TransactionTraceParams.class); + + final Blockchain blockchain = blockchainQueries.get().getBlockchain(); + final ProtocolSpec protocolSpec = + protocolSchedule.getByBlockNumber(blockchain.getChainHeadHeader().getNumber()); + final BadBlockManager badBlockManager = protocolSpec.getBadBlocksManager(); + + return badBlockManager + .getBadBlocks(blockHash) + .map( + block -> + (JsonRpcResponse) + new JsonRpcSuccessResponse( + requestContext.getRequest().getId(), + traceBlock(block, transactionTraceParams))) + .orElse( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.BLOCK_NOT_FOUND)); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java index c1829fa259b..230b0f28bbe 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java @@ -38,8 +38,8 @@ public class DebugStandardTraceBlockToFile implements JsonRpcMethod { + protected final Supplier blockchainQueries; private final Supplier transactionTracerSupplier; - private final Supplier blockchainQueries; private final Path dataDir; public DebugStandardTraceBlockToFile( @@ -77,7 +77,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { requestContext.getRequest().getId(), JsonRpcError.BLOCK_NOT_FOUND)); } - private List traceBlock( + protected List traceBlock( final Block block, final Optional transactionTraceParams) { return transactionTracerSupplier .get() diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java index cb318d6213b..531c9ce7eb4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java @@ -124,8 +124,12 @@ public Optional afterTransactionInBlock( private Optional performActionWithBlock( final Hash blockHash, final BlockAction action) { - return getBlock(blockHash) - .flatMap(block -> performActionWithBlock(block.getHeader(), block.getBody(), action)); + Optional maybeBlock = getBlock(blockHash); + if (maybeBlock.isEmpty()) { + maybeBlock = getBadBlock(blockHash); + } + return maybeBlock.flatMap( + block -> performActionWithBlock(block.getHeader(), block.getBody(), action)); } private Optional performActionWithBlock( @@ -161,6 +165,12 @@ private Optional getBlock(final Hash blockHash) { return Optional.empty(); } + private Optional getBadBlock(final Hash blockHash) { + final ProtocolSpec protocolSpec = + protocolSchedule.getByBlockNumber(blockchain.getChainHeadHeader().getNumber()); + return protocolSpec.getBadBlocksManager().getBadBlocks(blockHash); + } + @FunctionalInterface private interface BlockAction { Optional perform( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index d7e51d78486..13c5fd6c99a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -20,6 +20,7 @@ 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.DebugStandardTraceBadBlockToFile; 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; @@ -90,6 +91,11 @@ protected Map create() { new DebugBatchSendRawTransaction(transactionPool), new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult), new DebugStandardTraceBlockToFile( - () -> new TransactionTracer(blockReplay), blockchainQueries, dataDir)); + () -> new TransactionTracer(blockReplay), blockchainQueries, dataDir), + new DebugStandardTraceBadBlockToFile( + () -> new TransactionTracer(blockReplay), + blockchainQueries, + protocolSchedule, + dataDir)); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java new file mode 100644 index 00000000000..e6b4c3bd086 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java @@ -0,0 +1,99 @@ +/* + * 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.BadBlockManager; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class DebugStandardTraceBadBlockToFileTest { + + @ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); + + private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); + private final Blockchain blockchain = mock(Blockchain.class); + private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class); + private final TransactionTracer transactionTracer = mock(TransactionTracer.class); + private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); + + private final DebugStandardTraceBadBlockToFile debugStandardTraceBadBlockToFile = + new DebugStandardTraceBadBlockToFile( + () -> transactionTracer, blockchainQueries, protocolSchedule, folder.getRoot().toPath()); + + @Test + public void nameShouldBeDebugTraceTransaction() { + assertThat(debugStandardTraceBadBlockToFile.getName()) + .isEqualTo("debug_standardTraceBadBlockToFile"); + } + + @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_standardTraceBadBlockToFile", params)); + + final List paths = new ArrayList<>(); + paths.add("path-1"); + + final BadBlockManager badBlockManager = new BadBlockManager(); + badBlockManager.addBadBlock(block); + + final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); + when(protocolSpec.getBadBlocksManager()).thenReturn(badBlockManager); + when(blockchainQueries.getBlockchain()).thenReturn(blockchain); + when(blockchain.getChainHeadHeader()).thenReturn(new BlockHeaderTestFixture().buildHeader()); + when(protocolSchedule.getByBlockNumber(blockHeader.getNumber())).thenReturn(protocolSpec); + when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any(), any())) + .thenReturn(paths); + final JsonRpcSuccessResponse response = + (JsonRpcSuccessResponse) debugStandardTraceBadBlockToFile.response(request); + final List result = (ArrayList) response.getResult(); + + assertThat(result.size()).isEqualTo(1); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 865cfc0bc6c..5176223fb4a 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -259,7 +259,6 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr transactions, Optional.of(ImmutableTransactionTraceParams.builder().build()), traceDir.getRoot().toPath()); - ; assertThat(transactionTraces.size()).isEqualTo(1); assertThat(Files.readString(Path.of(transactionTraces.get(0)))) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java index e4c5f85476d..c563adba54f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.core.Hash; import java.util.Collection; +import java.util.Optional; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -46,4 +47,13 @@ public void addBadBlock(final Block badBlock) { public Collection getBadBlocks() { return badBlocks.asMap().values(); } + + /** + * Return an invalid block based on the hash + * + * @return an invalid block + */ + public Optional getBadBlocks(final Hash hash) { + return Optional.ofNullable(badBlocks.getIfPresent(hash)); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java index d07afcb445b..e95c127ec27 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java @@ -237,4 +237,22 @@ public boolean isSuccessful() { HeaderValidationMode.DETACHED_ONLY); assertThat(badBlockManager.getBadBlocks()).isEmpty(); } + + @Test + public void shouldReturnBadBlockBasedOnTheHash() { + when(blockchain.getBlockHeader(any(Hash.class))) + .thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); + when(blockHeaderValidator.validateHeader( + any(BlockHeader.class), + any(BlockHeader.class), + eq(protocolContext), + eq(HeaderValidationMode.DETACHED_ONLY))) + .thenReturn(false); + mainnetBlockValidator.validateAndProcessBlock( + protocolContext, + badBlock, + HeaderValidationMode.DETACHED_ONLY, + HeaderValidationMode.DETACHED_ONLY); + assertThat(badBlockManager.getBadBlocks(badBlock.getHash())).containsSame(badBlock); + } } From b4cffef63b52a3a3fd54e1a35bdca16389d19fe9 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Mon, 28 Sep 2020 18:43:51 +0200 Subject: [PATCH 07/11] update CHANGELOG.md Signed-off-by: Karim TAAM --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392b9a6a37f..2e9eda265d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,23 @@ # Changelog +## 1.6.0-RC1 + +### Additions and Improvements +* 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 + +#### Previously identified known issues + +- [Eth/65 loses peers](KNOWN_ISSUES.md#eth65-loses-peers) +- [Fast sync when running Besu on cloud providers](KNOWN_ISSUES.md#fast-sync-when-running-besu-on-cloud-providers) +- [Privacy users with private transactions created using v1.3.4 or earlier](KNOWN_ISSUES.md#privacy-users-with-private-transactions-created-using-v134-or-earlier) +- [Changes not saved to database correctly causing inconsistent private states](KNOWN_ISSUES.md#Changes-not-saved-to-database-correctly-causing-inconsistent-private-states) + ## 1.5.5 ### Additions and Improvements * The new version of the [web3js-eea library (v0.10)](https://github.com/PegaSysEng/web3js-eea) supports the onchain privacy group management changes made in Besu v1.5.3. -* 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 * Added `debug_getBadBlocks` JSON-RPC API to analyze and detect consensus flaws. Even if a block is rejected it will be returned by this method [\#1378](https://github.com/hyperledger/besu/pull/1378) From 87acf61269a5d998c6606933164693bdba8ae728 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Mon, 28 Sep 2020 19:24:29 +0200 Subject: [PATCH 08/11] update CHANGELOG.md Signed-off-by: Karim TAAM --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5acd6930f44..6a096d323e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Additions and Improvements * 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) -* Added `debug_standardTraceBadBlockToFile` JSON-RPC API. This API is similar to debug_standardTraceBlockToFile, but can be used to obtain info about a block which has been rejected as invalid. +* Added `debug_standardTraceBadBlockToFile` JSON-RPC API. This API is similar to debug_standardTraceBlockToFile`, but can be used to obtain info about a block which has been rejected as invalid. [\#1403](https://github.com/hyperledger/besu/pull/1403) ### Bug Fixes From 99b9b4c7334ef51da5e4b040f8fe9f22ef8c253e Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Mon, 28 Sep 2020 19:28:19 +0200 Subject: [PATCH 09/11] fix pipeline Signed-off-by: Karim TAAM --- .../org/hyperledger/besu/ethereum/chain/BadBlockManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java index c563adba54f..1a097ea9ea0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java @@ -51,6 +51,7 @@ public Collection getBadBlocks() { /** * Return an invalid block based on the hash * + * @param hash of the block * @return an invalid block */ public Optional getBadBlocks(final Hash hash) { From 824e973175864650cb377f91b8a9bba5909d3b3c Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Mon, 28 Sep 2020 19:29:38 +0200 Subject: [PATCH 10/11] update CHANGELOG.md Signed-off-by: Karim TAAM --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a096d323e6..4fdc3d1ce2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Additions and Improvements * 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) -* Added `debug_standardTraceBadBlockToFile` JSON-RPC API. This API is similar to debug_standardTraceBlockToFile`, but can be used to obtain info about a block which has been rejected as invalid. [\#1403](https://github.com/hyperledger/besu/pull/1403) +* Added `debug_standardTraceBadBlockToFile` JSON-RPC API. This API is similar to `debug_standardTraceBlockToFile`, but can be used to obtain info about a block which has been rejected as invalid. [\#1403](https://github.com/hyperledger/besu/pull/1403) ### Bug Fixes From 8b12d25c191b8eed01aa785656358426198452d6 Mon Sep 17 00:00:00 2001 From: Karim TAAM Date: Tue, 29 Sep 2020 10:46:12 +0200 Subject: [PATCH 11/11] fix unitTest Signed-off-by: Karim TAAM --- .../internal/methods/DebugStandardTraceBadBlockToFile.java | 2 +- .../ethereum/api/jsonrpc/internal/processor/BlockReplay.java | 2 +- .../api/jsonrpc/internal/processor/TransactionTracerTest.java | 3 +++ .../org/hyperledger/besu/ethereum/chain/BadBlockManager.java | 2 +- .../hyperledger/besu/ethereum/MainnetBlockValidatorTest.java | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java index b049cdd3ba8..cff97dc99de 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFile.java @@ -64,7 +64,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { final BadBlockManager badBlockManager = protocolSpec.getBadBlocksManager(); return badBlockManager - .getBadBlocks(blockHash) + .getBadBlock(blockHash) .map( block -> (JsonRpcResponse) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java index 531c9ce7eb4..75b321a4165 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java @@ -168,7 +168,7 @@ private Optional getBlock(final Hash blockHash) { private Optional getBadBlock(final Hash blockHash) { final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(blockchain.getChainHeadHeader().getNumber()); - return protocolSpec.getBadBlocksManager().getBadBlocks(blockHash); + return protocolSpec.getBadBlocksManager().getBadBlock(blockHash); } @FunctionalInterface diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 5176223fb4a..224d99db2f1 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ImmutableTransactionTraceParams; +import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.BlockBody; @@ -113,6 +114,8 @@ public void setUp() throws Exception { when(protocolSchedule.getByBlockNumber(12)).thenReturn(protocolSpec); when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor); when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase); + when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); + when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager()); } @Test diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java index 1a097ea9ea0..50f0011a5d2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java @@ -54,7 +54,7 @@ public Collection getBadBlocks() { * @param hash of the block * @return an invalid block */ - public Optional getBadBlocks(final Hash hash) { + public Optional getBadBlock(final Hash hash) { return Optional.ofNullable(badBlocks.getIfPresent(hash)); } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java index e95c127ec27..ca5be707d2b 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java @@ -253,6 +253,6 @@ public void shouldReturnBadBlockBasedOnTheHash() { badBlock, HeaderValidationMode.DETACHED_ONLY, HeaderValidationMode.DETACHED_ONLY); - assertThat(badBlockManager.getBadBlocks(badBlock.getHash())).containsSame(badBlock); + assertThat(badBlockManager.getBadBlock(badBlock.getHash())).containsSame(badBlock); } }