Use Besu pipeline in trace_block, trace_filter and trace_replayBlockTransactions (#5131)

Use Besu pipeline in trace_block, trace_filter and trace_replayBlockTransactions (#5131)
Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net>
pull/5252/head
ahamlat 2 years ago committed by GitHub
parent 0f973373f3
commit c4034f502e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 2
      ethereum/api/build.gradle
  3. 37
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/BuildArrayNodeCompleterStep.java
  4. 113
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java
  5. 142
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java
  6. 194
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilter.java
  7. 82
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilterSource.java
  8. 86
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFlatTransactionStep.java
  9. 223
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java
  10. 78
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayTransactionStep.java
  11. 47
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TransactionSource.java
  12. 33
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java
  13. 125
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TraceReplayResult.java
  14. 18
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java
  15. 5
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java
  16. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java
  17. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java
  18. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java

@ -28,6 +28,7 @@ This update is a mainnet-compatible Shanghai/Capella upgrade and is recommended
### Bug Fixes
- Persist backward sync status to support resuming across restarts [#5182](https://github.com/hyperledger/besu/pull/5182)
- Re-implement trace_block, trace_filter and trace_replayBlockTransactions RPC endpoints to fix memory issues and improve performance [#5131](https://github.com/hyperledger/besu/pull/5131)
### Download Links
https://hyperledger.jfrog.io/hyperledger/besu-binaries/besu/23.1.2/besu-23.1.2.tar.gz / sha256: 3d3a709a3aab993a0801b412a4719d74e319f942ddc13fb0f30b3c4a54d12538

@ -50,6 +50,8 @@ dependencies {
implementation project(':nat')
implementation project(':plugin-api')
implementation project(':util')
implementation project(':services:pipeline')
implementation project(':services:tasks')
implementation 'com.google.guava:guava'
implementation 'com.graphql-java:graphql-java'

@ -0,0 +1,37 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.util.ArrayNodeWrapper;
import java.util.function.Consumer;
public class BuildArrayNodeCompleterStep implements Consumer<Object> {
private final ArrayNodeWrapper resultArrayNode;
public BuildArrayNodeCompleterStep(final ArrayNodeWrapper resultArrayNode) {
this.resultArrayNode = resultArrayNode;
}
@Override
public void accept(final Object object) {
resultArrayNode.addPOJO(object);
}
public ArrayNodeWrapper getResultArrayNode() {
return this.resultArrayNode;
}
}

@ -0,0 +1,113 @@
/*
* Copyright contributors to Hyperledger Besu..
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import org.hyperledger.besu.datatypes.DataGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
public class ExecuteTransactionStep implements Function<TransactionTrace, TransactionTrace> {
private final TraceBlock.ChainUpdater chainUpdater;
private final DebugOperationTracer tracer;
private final MainnetTransactionProcessor transactionProcessor;
private final Blockchain blockchain;
private final ProtocolSpec protocolSpec;
private final Block block;
public ExecuteTransactionStep(
final TraceBlock.ChainUpdater chainUpdater,
final MainnetTransactionProcessor transactionProcessor,
final Blockchain blockchain,
final DebugOperationTracer tracer,
final ProtocolSpec protocolSpec,
final Block block) {
this.chainUpdater = chainUpdater;
this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain;
this.tracer = tracer;
this.protocolSpec = protocolSpec;
this.block = block;
}
public ExecuteTransactionStep(
final TraceBlock.ChainUpdater chainUpdater,
final MainnetTransactionProcessor transactionProcessor,
final Blockchain blockchain,
final DebugOperationTracer tracer,
final ProtocolSpec protocolSpec) {
this(chainUpdater, transactionProcessor, blockchain, tracer, protocolSpec, null);
}
@Override
public TransactionTrace apply(final TransactionTrace transactionTrace) {
Block block = this.block;
// case where transactionTrace is created only to trace a block reward
if (block == null) {
block =
transactionTrace
.getBlock()
.orElseThrow(
() ->
new RuntimeException(
"Expecting reward block to be in transactionTrace but was empty"));
}
List<TraceFrame> traceFrames = null;
TransactionProcessingResult result = null;
// If it is not a reward Block trace
if (transactionTrace.getTransaction() != null) {
BlockHeader header = block.getHeader();
final Optional<BlockHeader> maybeParentHeader =
blockchain.getBlockHeader(header.getParentHash());
final Wei dataGasPrice =
protocolSpec
.getFeeMarket()
.dataPrice(
maybeParentHeader.flatMap(BlockHeader::getExcessDataGas).orElse(DataGas.ZERO));
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain);
result =
transactionProcessor.processTransaction(
blockchain,
chainUpdater.getNextUpdater(),
header,
transactionTrace.getTransaction(),
header.getCoinbase(),
tracer,
blockHashLookup,
false,
dataGasPrice);
traceFrames = tracer.copyTraceFrames();
tracer.reset();
}
return new TransactionTrace(
transactionTrace.getTransaction(), result, traceFrames, transactionTrace.getBlock());
}
}

@ -14,11 +14,12 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.services.pipeline.PipelineBuilder.createPipelineFrom;
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.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
@ -27,13 +28,25 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.util.ArrayNodeWrapper;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.worldstate.StackedUpdater;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import org.hyperledger.besu.services.pipeline.Pipeline;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.concurrent.ExecutionException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
@ -41,17 +54,11 @@ import org.slf4j.LoggerFactory;
public class TraceBlock extends AbstractBlockParameterMethod {
private static final Logger LOG = LoggerFactory.getLogger(TraceBlock.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
private final Supplier<BlockTracer> blockTracerSupplier;
protected final ProtocolSchedule protocolSchedule;
public TraceBlock(
final Supplier<BlockTracer> blockTracerSupplier,
final ProtocolSchedule protocolSchedule,
final BlockchainQueries queries) {
public TraceBlock(final ProtocolSchedule protocolSchedule, final BlockchainQueries queries) {
super(queries);
this.blockTracerSupplier = blockTracerSupplier;
this.protocolSchedule = protocolSchedule;
}
@ -88,31 +95,75 @@ public class TraceBlock extends AbstractBlockParameterMethod {
if (block == null) {
return emptyResult();
}
final ArrayNodeWrapper resultArrayNode = new ArrayNodeWrapper(MAPPER.createArrayNode());
Tracer.processTracing(
blockchainQueriesSupplier.get(),
Optional.of(block.getHeader()),
mutableWorldState -> {
blockTracerSupplier
.get()
.trace(
mutableWorldState,
block,
new DebugOperationTracer(new TraceOptions(false, false, true)))
.ifPresent(
blockTrace ->
generateTracesFromTransactionTraceAndBlock(
filterParameter,
blockTrace.getTransactionTraces(),
block,
resultArrayNode));
generateRewardsFromBlock(filterParameter, block, resultArrayNode);
return Optional.empty();
});
return resultArrayNode;
final BlockHeader header = block.getHeader();
return Tracer.processTracing(
getBlockchainQueries(),
Optional.of(header),
traceableState -> {
ArrayNodeWrapper resultArrayNode = new ArrayNodeWrapper(MAPPER.createArrayNode());
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header);
final MainnetTransactionProcessor transactionProcessor =
protocolSpec.getTransactionProcessor();
final ChainUpdater chainUpdater = new ChainUpdater(traceableState);
TransactionSource transactionSource = new TransactionSource(block);
final LabelledMetric<Counter> outputCounter =
new PrometheusMetricsSystem(BesuMetricCategory.DEFAULT_METRIC_CATEGORIES, false)
.createLabelledCounter(
BesuMetricCategory.BLOCKCHAIN,
"transactions_traceblock_pipeline_processed_total",
"Number of transactions processed for each block",
"step",
"action");
DebugOperationTracer debugOperationTracer =
new DebugOperationTracer(new TraceOptions(false, false, true));
ExecuteTransactionStep executeTransactionStep =
new ExecuteTransactionStep(
chainUpdater,
transactionProcessor,
getBlockchainQueries().getBlockchain(),
debugOperationTracer,
protocolSpec,
block);
TraceFlatTransactionStep traceFlatTransactionStep =
new TraceFlatTransactionStep(protocolSchedule, block, filterParameter);
BuildArrayNodeCompleterStep buildArrayNodeStep =
new BuildArrayNodeCompleterStep(resultArrayNode);
Pipeline<TransactionTrace> traceBlockPipeline =
createPipelineFrom(
"getTransactions",
transactionSource,
4,
outputCounter,
false,
"trace_block_transactions")
.thenProcess("executeTransaction", executeTransactionStep)
.thenProcessAsyncOrdered("traceFlatTransaction", traceFlatTransactionStep, 4)
.andFinishWith(
"buildArrayNode",
traceStream -> traceStream.forEachOrdered(buildArrayNodeStep));
try {
if (getBlockchainQueries().getEthScheduler().isPresent()) {
getBlockchainQueries()
.getEthScheduler()
.get()
.startPipeline(traceBlockPipeline)
.get();
} else {
EthScheduler ethScheduler = new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem());
ethScheduler.startPipeline(traceBlockPipeline).get();
}
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
resultArrayNode = buildArrayNodeStep.getResultArrayNode();
generateRewardsFromBlock(filterParameter, block, resultArrayNode);
return Optional.of(resultArrayNode);
})
.orElse(emptyResult());
}
protected void generateTracesFromTransactionTraceAndBlock(
@ -135,7 +186,28 @@ public class TraceBlock extends AbstractBlockParameterMethod {
.forEachOrdered(resultArrayNode::addPOJO);
}
private ArrayNodeWrapper emptyResult() {
ArrayNodeWrapper emptyResult() {
return new ArrayNodeWrapper(MAPPER.createArrayNode());
}
public static class ChainUpdater {
private final MutableWorldState worldState;
private WorldUpdater updater;
public ChainUpdater(final MutableWorldState worldState) {
this.worldState = worldState;
}
public WorldUpdater getNextUpdater() {
// if we have no prior updater, it must be the first TX, so use the block's initial state
if (updater == null) {
updater = worldState.updater();
} else if (updater instanceof StackedUpdater) {
((StackedUpdater) updater).markTransactionBoundary();
}
updater = updater.updater();
return updater;
}
}
}

@ -14,29 +14,52 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.services.pipeline.PipelineBuilder.createPipelineFrom;
import org.hyperledger.besu.datatypes.Address;
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.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.RewardTraceGenerator;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.util.ArrayNodeWrapper;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import org.hyperledger.besu.services.pipeline.Pipeline;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.tuweni.bytes.Bytes32;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -48,7 +71,7 @@ public class TraceFilter extends TraceBlock {
final Supplier<BlockTracer> blockTracerSupplier,
final ProtocolSchedule protocolSchedule,
final BlockchainQueries blockchainQueries) {
super(blockTracerSupplier, protocolSchedule, blockchainQueries);
super(protocolSchedule, blockchainQueries);
}
@Override
@ -69,57 +92,134 @@ public class TraceFilter extends TraceBlock {
final ArrayNodeWrapper resultArrayNode =
new ArrayNodeWrapper(
mapper.createArrayNode(), filterParameter.getAfter(), filterParameter.getCount());
if (fromBlock > toBlock)
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), resultArrayNode.getArrayNode());
else
return traceFilterWithPipeline(
requestContext, filterParameter, fromBlock, toBlock, resultArrayNode);
}
private JsonRpcResponse traceFilterWithPipeline(
final JsonRpcRequestContext requestContext,
final FilterParameter filterParameter,
final long fromBlock,
final long toBlock,
final ArrayNodeWrapper resultArrayNode) {
long currentBlockNumber = fromBlock;
while (currentBlockNumber <= toBlock && !resultArrayNode.isFull()) {
Optional<Block> blockByNumber =
blockchainQueriesSupplier.get().getBlockchain().getBlockByNumber(currentBlockNumber);
blockByNumber.ifPresent(
block -> resultArrayNode.addAll(traceBlock(block, Optional.of(filterParameter))));
Optional<Block> block =
blockchainQueriesSupplier.get().getBlockchain().getBlockByNumber(currentBlockNumber);
while ((block.isEmpty() || block.get().getHeader().getParentHash().equals(Bytes32.ZERO))
&& currentBlockNumber < toBlock) {
currentBlockNumber++;
block = blockchainQueriesSupplier.get().getBlockchain().getBlockByNumber(currentBlockNumber);
}
if (block.isEmpty()) {
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), resultArrayNode.getArrayNode());
}
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), resultArrayNode.getArrayNode());
final BlockHeader header = block.get().getHeader();
List<Block> blockList = getBlockList(currentBlockNumber, toBlock, block);
ArrayNodeWrapper result =
Tracer.processTracing(
getBlockchainQueries(),
Optional.of(header),
traceableState -> {
TraceFilterSource traceFilterSource =
new TraceFilterSource(blockList, resultArrayNode);
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header);
final MainnetTransactionProcessor transactionProcessor =
protocolSpec.getTransactionProcessor();
final ChainUpdater chainUpdater = new ChainUpdater(traceableState);
final LabelledMetric<Counter> outputCounter =
new PrometheusMetricsSystem(
BesuMetricCategory.DEFAULT_METRIC_CATEGORIES, false)
.createLabelledCounter(
BesuMetricCategory.BLOCKCHAIN,
"transactions_tracefilter_pipeline_processed_total",
"Number of transactions processed for trace_filter",
"step",
"action");
DebugOperationTracer debugOperationTracer =
new DebugOperationTracer(new TraceOptions(false, false, true));
ExecuteTransactionStep executeTransactionStep =
new ExecuteTransactionStep(
chainUpdater,
transactionProcessor,
getBlockchainQueries().getBlockchain(),
debugOperationTracer,
protocolSpec);
Function<TransactionTrace, CompletableFuture<Stream<FlatTrace>>>
traceFlatTransactionStep =
new TraceFlatTransactionStep(
protocolSchedule, null, Optional.of(filterParameter));
BuildArrayNodeCompleterStep buildArrayNodeStep =
new BuildArrayNodeCompleterStep(resultArrayNode);
Pipeline<TransactionTrace> traceBlockPipeline =
createPipelineFrom(
"getTransactions",
traceFilterSource,
4,
outputCounter,
false,
"trace_block_transactions")
.thenProcess("executeTransaction", executeTransactionStep)
.thenProcessAsyncOrdered(
"traceFlatTransaction", traceFlatTransactionStep, 4)
.andFinishWith(
"buildArrayNode",
traceStream -> traceStream.forEachOrdered(buildArrayNodeStep));
try {
Optional<EthScheduler> ethSchedulerOpt =
getBlockchainQueries().getEthScheduler();
ethSchedulerOpt
.orElse(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()))
.startPipeline(traceBlockPipeline)
.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
return Optional.of(resultArrayNode);
})
.orElse(emptyResult());
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result.getArrayNode());
}
@Override
protected void generateTracesFromTransactionTraceAndBlock(
final Optional<FilterParameter> maybeFilterParameter,
final List<TransactionTrace> transactionTraces,
final Block block,
final ArrayNodeWrapper arrayNode) {
@NotNull
private List<Block> getBlockList(
final long fromBlock, final long toBlock, final Optional<Block> block) {
List<Block> blockList = new ArrayList<>();
Block currentBlock = block.get();
blockList.add(currentBlock);
long index = fromBlock + 1; // We already stored current Block
while (index <= toBlock) {
Optional<Block> blockByNumber =
blockchainQueriesSupplier.get().getBlockchain().getBlockByNumber(index);
blockByNumber.ifPresent(blockList::add);
index++;
}
return blockList;
}
final Iterator<TransactionTrace> iterator = transactionTraces.iterator();
while (!arrayNode.isFull() && iterator.hasNext()) {
maybeFilterParameter.ifPresentOrElse(
filterParameter -> {
final List<Address> fromAddress = filterParameter.getFromAddress();
final List<Address> toAddress = filterParameter.getToAddress();
FlatTraceGenerator.generateFromTransactionTraceAndBlock(
protocolSchedule, iterator.next(), block)
.map(FlatTrace.class::cast)
.filter(
trace ->
fromAddress.isEmpty()
|| Optional.ofNullable(trace.getAction().getFrom())
.map(Address::fromHexString)
.map(fromAddress::contains)
.orElse(false))
.filter(
trace ->
toAddress.isEmpty()
|| Optional.ofNullable(trace.getAction().getTo())
.map(Address::fromHexString)
.map(toAddress::contains)
.orElse(false))
.forEachOrdered(arrayNode::addPOJO);
},
new Runnable() {
@Override
public void run() {
LOG.debug("No filter found. Unable to create traces");
}
});
public Map<Transaction, Block> createTransactionBlockMap(final List<Block> blockList) {
Map<Transaction, Block> transactionBlockMap = new HashMap<>();
for (Block block : blockList) {
List<Transaction> transactionList = block.getBody().getTransactions();
for (Transaction transaction : transactionList) {
transactionBlockMap.put(transaction, block);
}
}
return transactionBlockMap;
}
@Override

@ -0,0 +1,82 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.util.ArrayNodeWrapper;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
public class TraceFilterSource implements Iterator<TransactionTrace> {
private final ArrayNodeWrapper resultArrayNode;
private final Iterator<Block> blockIterator;
private Iterator<TransactionTrace> transactionTraceIterator;
private Block currentBlock;
public TraceFilterSource(final List<Block> blockList, final ArrayNodeWrapper resultArrayNode) {
this.resultArrayNode = resultArrayNode;
this.blockIterator = blockList.iterator();
this.transactionTraceIterator = getNextTransactionIterator();
}
private Iterator<TransactionTrace> getNextTransactionIterator() {
if (!blockIterator.hasNext()) {
return null;
}
currentBlock = blockIterator.next();
List<Transaction> transactions = currentBlock.getBody().getTransactions();
List<TransactionTrace> transactionTraces = new ArrayList<>(transactions.size() + 1);
for (Transaction transaction : transactions) {
transactionTraces.add(new TransactionTrace(transaction, Optional.of(currentBlock)));
}
transactionTraces.add(new TransactionTrace(Optional.of(currentBlock)));
return transactionTraces.iterator();
}
@Override
public boolean hasNext() {
if (resultArrayNode.isFull()) return false;
if (transactionTraceIterator == null) {
return false;
}
if (transactionTraceIterator.hasNext()) {
return true;
}
transactionTraceIterator = getNextTransactionIterator();
return hasNext();
}
@Override
public TransactionTrace next() {
if (transactionTraceIterator == null) {
return null;
}
if (transactionTraceIterator.hasNext()) {
return transactionTraceIterator.next();
}
transactionTraceIterator = getNextTransactionIterator();
return next();
}
}

@ -0,0 +1,86 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.RewardTraceGenerator;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Stream;
public class TraceFlatTransactionStep
implements Function<TransactionTrace, CompletableFuture<Stream<FlatTrace>>> {
private final ProtocolSchedule protocolSchedule;
private final Block block;
private final Optional<FilterParameter> filterParameter;
public TraceFlatTransactionStep(
final ProtocolSchedule protocolSchedule,
final Block block,
final Optional<FilterParameter> filterParameter) {
this.protocolSchedule = protocolSchedule;
this.block = block;
this.filterParameter = filterParameter;
}
@Override
public CompletableFuture<Stream<FlatTrace>> apply(final TransactionTrace transactionTrace) {
Stream<Trace> traceStream = null;
Block block = this.block;
if (block == null) block = transactionTrace.getBlock().get();
if (transactionTrace.getTransaction() == null) {
traceStream = RewardTraceGenerator.generateFromBlock(protocolSchedule, block);
} else {
traceStream =
FlatTraceGenerator.generateFromTransactionTraceAndBlock(
protocolSchedule, transactionTrace, block);
}
if (filterParameter.isPresent()) {
final List<Address> fromAddress = filterParameter.get().getFromAddress();
final List<Address> toAddress = filterParameter.get().getToAddress();
return CompletableFuture.completedFuture(
traceStream
.map(FlatTrace.class::cast)
.filter(
trace ->
fromAddress.isEmpty()
|| Optional.ofNullable(trace.getAction().getFrom())
.map(Address::fromHexString)
.map(fromAddress::contains)
.orElse(false))
.filter(
trace ->
toAddress.isEmpty()
|| Optional.ofNullable(trace.getAction().getTo())
.map(Address::fromHexString)
.map(toAddress::contains)
.orElse(false)));
} else {
return CompletableFuture.completedFuture(traceStream.map(FlatTrace.class::cast));
}
}
}

@ -14,59 +14,52 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter.TraceType.TRACE;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter.TraceType.VM_TRACE;
import static org.hyperledger.besu.services.pipeline.PipelineBuilder.createPipelineFrom;
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.methods.TraceBlock.ChainUpdater;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter.TraceType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TraceFormatter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TraceWriter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.diff.StateDiffGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmTraceGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TraceReplayResult;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.util.ArrayNodeWrapper;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import org.hyperledger.besu.services.pipeline.Pipeline;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Suppliers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TraceReplayBlockTransactions extends AbstractBlockParameterMethod {
private static final Logger LOG = LoggerFactory.getLogger(TraceReplayBlockTransactions.class);
private final Supplier<BlockTracer> blockTracerSupplier;
private final Supplier<StateDiffGenerator> stateDiffGenerator =
Suppliers.memoize(StateDiffGenerator::new);
private final ProtocolSchedule protocolSchedule;
private static final ObjectMapper MAPPER = new ObjectMapper();
public TraceReplayBlockTransactions(
final Supplier<BlockTracer> blockTracerSupplier,
final ProtocolSchedule protocolSchedule,
final BlockchainQueries queries) {
final ProtocolSchedule protocolSchedule, final BlockchainQueries queries) {
super(queries);
this.blockTracerSupplier = blockTracerSupplier;
this.protocolSchedule = protocolSchedule;
}
@ -81,7 +74,7 @@ public class TraceReplayBlockTransactions extends AbstractBlockParameterMethod {
}
@Override
protected Object resultByBlockNumber(
protected ArrayNode resultByBlockNumber(
final JsonRpcRequestContext request, final long blockNumber) {
final TraceTypeParameter traceTypeParameter =
request.getRequiredParameter(1, TraceTypeParameter.class);
@ -104,122 +97,86 @@ public class TraceReplayBlockTransactions extends AbstractBlockParameterMethod {
.orElse(null);
}
private Object traceBlock(final Block block, final TraceTypeParameter traceTypeParameter) {
private ArrayNode traceBlock(final Block block, final TraceTypeParameter traceTypeParameter) {
if (block == null || block.getBody().getTransactions().isEmpty()) {
if (block == null) {
return emptyResult();
}
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
final TraceOptions traceOptions =
new TraceOptions(false, false, traceTypes.contains(VM_TRACE) || traceTypes.contains(TRACE));
final BlockHeader header = block.getHeader();
return Tracer.processTracing(
blockchainQueriesSupplier.get(),
Optional.of(block.getHeader()),
mutableWorldState -> {
return blockTracerSupplier
.get()
.trace(mutableWorldState, block, new DebugOperationTracer(traceOptions))
.map(BlockTrace::getTransactionTraces)
.map((traces) -> generateTracesFromTransactionTrace(traces, block, traceTypes));
getBlockchainQueries(),
Optional.of(header),
traceableState -> {
ArrayNodeWrapper resultArrayNode = new ArrayNodeWrapper(MAPPER.createArrayNode());
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header);
final MainnetTransactionProcessor transactionProcessor =
protocolSpec.getTransactionProcessor();
final ChainUpdater chainUpdater = new ChainUpdater(traceableState);
final TransactionSource transactionSource = new TransactionSource(block);
final LabelledMetric<Counter> outputCounter =
new PrometheusMetricsSystem(BesuMetricCategory.DEFAULT_METRIC_CATEGORIES, false)
.createLabelledCounter(
BesuMetricCategory.BLOCKCHAIN,
"transactions_tracereplayblock_pipeline_processed_total",
"Number of transactions processed for each block",
"step",
"action");
final DebugOperationTracer debugOperationTracer =
new DebugOperationTracer(new TraceOptions(false, false, true));
final ExecuteTransactionStep executeTransactionStep =
new ExecuteTransactionStep(
chainUpdater,
transactionProcessor,
getBlockchainQueries().getBlockchain(),
debugOperationTracer,
protocolSpec,
block);
final Function<TransactionTrace, CompletableFuture<TraceReplayResult>>
traceReplayTransactionStep =
new TraceReplayTransactionStep(protocolSchedule, block, traceTypes);
final BuildArrayNodeCompleterStep buildArrayNodeStep =
new BuildArrayNodeCompleterStep(resultArrayNode);
final Pipeline<TransactionTrace> traceBlockPipeline =
createPipelineFrom(
"getTransactions",
transactionSource,
4,
outputCounter,
false,
"trace_replay_block_transactions")
.thenProcess("executeTransaction", executeTransactionStep)
.thenProcessAsyncOrdered(
"traceReplayTransaction", traceReplayTransactionStep, 4)
.andFinishWith("buildArrayNode", buildArrayNodeStep::accept);
try {
if (getBlockchainQueries().getEthScheduler().isPresent()) {
getBlockchainQueries()
.getEthScheduler()
.get()
.startPipeline(traceBlockPipeline)
.get();
} else {
EthScheduler ethScheduler = new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem());
ethScheduler.startPipeline(traceBlockPipeline).get();
}
} catch (final InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
resultArrayNode = buildArrayNodeStep.getResultArrayNode();
return Optional.of(resultArrayNode.getArrayNode());
})
.orElse(null);
}
private JsonNode generateTracesFromTransactionTrace(
final List<TransactionTrace> transactionTraces,
final Block block,
final Set<TraceTypeParameter.TraceType> traceTypes) {
final ObjectMapper mapper = new ObjectMapper();
final ArrayNode resultArrayNode = mapper.createArrayNode();
final AtomicInteger traceCounter = new AtomicInteger(0);
transactionTraces.forEach(
transactionTrace ->
handleTransactionTrace(
transactionTrace, block, traceTypes, mapper, resultArrayNode, traceCounter));
return resultArrayNode;
}
private void handleTransactionTrace(
final TransactionTrace transactionTrace,
final Block block,
final Set<TraceTypeParameter.TraceType> traceTypes,
final ObjectMapper mapper,
final ArrayNode resultArrayNode,
final AtomicInteger traceCounter) {
final ObjectNode resultNode = mapper.createObjectNode();
TransactionProcessingResult result = transactionTrace.getResult();
resultNode.put("output", result.getOutput().toString());
result.getRevertReason().ifPresent(r -> resultNode.put("revertReason", r.toHexString()));
if (traceTypes.contains(TraceType.STATE_DIFF)) {
generateTracesFromTransactionTrace(
trace -> resultNode.putPOJO("stateDiff", trace),
protocolSchedule,
transactionTrace,
block,
(__, txTrace, currentBlock, ignored) ->
stateDiffGenerator.get().generateStateDiff(txTrace),
traceCounter);
}
setNullNodesIfNotPresent(resultNode, "stateDiff");
if (traceTypes.contains(TraceTypeParameter.TraceType.TRACE)) {
generateTracesFromTransactionTrace(
resultNode.putArray("trace")::addPOJO,
protocolSchedule,
transactionTrace,
block,
FlatTraceGenerator::generateFromTransactionTrace,
traceCounter);
}
setEmptyArrayIfNotPresent(resultNode, "trace");
resultNode.put("transactionHash", transactionTrace.getTransaction().getHash().toHexString());
if (traceTypes.contains(VM_TRACE)) {
generateTracesFromTransactionTrace(
trace -> resultNode.putPOJO("vmTrace", trace),
protocolSchedule,
transactionTrace,
block,
(protocolSchedule, txTrace, currentBlock, ignored) ->
new VmTraceGenerator(transactionTrace).generateTraceStream(),
traceCounter);
}
setNullNodesIfNotPresent(resultNode, "vmTrace");
resultArrayNode.add(resultNode);
}
private void generateTracesFromTransactionTrace(
final TraceWriter writer,
final ProtocolSchedule protocolSchedule,
final TransactionTrace transactionTrace,
final Block block,
final TraceFormatter formatter,
final AtomicInteger traceCounter) {
formatter
.format(protocolSchedule, transactionTrace, block, traceCounter)
.forEachOrdered(writer::write);
}
private void setNullNodesIfNotPresent(final ObjectNode parentNode, final String... keys) {
Arrays.asList(keys)
.forEach(
key ->
Optional.ofNullable(parentNode.get(key))
.ifPresentOrElse(ignored -> {}, () -> parentNode.put(key, (String) null)));
}
private void setEmptyArrayIfNotPresent(final ObjectNode parentNode, final String... keys) {
Arrays.asList(keys)
.forEach(
key ->
Optional.ofNullable(parentNode.get(key))
.ifPresentOrElse(ignored -> {}, () -> parentNode.putArray(key)));
.orElse(emptyResult());
}
private Object emptyResult() {
final ObjectMapper mapper = new ObjectMapper();
return mapper.createArrayNode();
private ArrayNode emptyResult() {
return MAPPER.createArrayNode();
}
}

@ -0,0 +1,78 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.internal.parameters.TraceTypeParameter.TraceType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TraceReplayResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.diff.StateDiffGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.diff.StateDiffTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmTraceGenerator;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public class TraceReplayTransactionStep
implements Function<TransactionTrace, CompletableFuture<TraceReplayResult>> {
private final ProtocolSchedule protocolSchedule;
private final Block block;
private final Set<TraceType> traceTypes;
public TraceReplayTransactionStep(
final ProtocolSchedule protocolSchedule, final Block block, final Set<TraceType> traceTypes) {
this.protocolSchedule = protocolSchedule;
this.block = block;
this.traceTypes = traceTypes;
}
@Override
public CompletableFuture<TraceReplayResult> apply(final TransactionTrace transactionTrace) {
final TraceReplayResult.Builder builder = TraceReplayResult.builder();
transactionTrace
.getResult()
.getRevertReason()
.ifPresent(revertReason -> builder.revertReason(revertReason.toHexString()));
builder.output(transactionTrace.getResult().getOutput().toString());
builder.transactionHash(transactionTrace.getTransaction().getHash().toHexString());
if (traceTypes.contains(TraceType.STATE_DIFF)) {
new StateDiffGenerator()
.generateStateDiff(transactionTrace)
.forEachOrdered(stateDiff -> builder.stateDiff((StateDiffTrace) stateDiff));
}
if (traceTypes.contains(TraceType.TRACE)) {
FlatTraceGenerator.generateFromTransactionTrace(
protocolSchedule, transactionTrace, block, new AtomicInteger())
.forEachOrdered(trace -> builder.addTrace((FlatTrace) trace));
}
if (traceTypes.contains(TraceType.VM_TRACE)) {
new VmTraceGenerator(transactionTrace)
.generateTraceStream()
.forEachOrdered(vmTrace -> builder.vmTrace((VmTrace) vmTrace));
}
return CompletableFuture.completedFuture(builder.build());
}
}

@ -0,0 +1,47 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.internal.processor.TransactionTrace;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
public class TransactionSource implements Iterator<TransactionTrace> {
private final List<Transaction> transactions;
private final AtomicInteger currentIndex = new AtomicInteger(0);
private final Block block;
public TransactionSource(final Block block) {
this.block = block;
this.transactions = block.getBody().getTransactions();
}
@Override
public boolean hasNext() {
return currentIndex.get() < (transactions.size());
}
@Override
public TransactionTrace next() {
return new TransactionTrace(
transactions.get(currentIndex.getAndIncrement()), Optional.of(block));
}
}

@ -14,17 +14,27 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import java.util.List;
import java.util.Optional;
public class TransactionTrace {
private final Transaction transaction;
private final TransactionProcessingResult result;
private final List<TraceFrame> traceFrames;
private final Optional<Block> block;
public TransactionTrace(final Optional<Block> block) {
this.transaction = null;
this.result = null;
this.traceFrames = null;
this.block = block;
}
public TransactionTrace(
final Transaction transaction,
@ -33,6 +43,25 @@ public class TransactionTrace {
this.transaction = transaction;
this.result = result;
this.traceFrames = traceFrames;
this.block = Optional.empty();
}
public TransactionTrace(
final Transaction transaction,
final TransactionProcessingResult result,
final List<TraceFrame> traceFrames,
final Optional<Block> block) {
this.transaction = transaction;
this.result = result;
this.traceFrames = traceFrames;
this.block = block;
}
public TransactionTrace(final Transaction transaction, final Optional<Block> block) {
this.transaction = transaction;
this.result = null;
this.traceFrames = null;
this.block = block;
}
public Transaction getTransaction() {
@ -54,4 +83,8 @@ public class TransactionTrace {
public List<TraceFrame> getTraceFrames() {
return traceFrames;
}
public Optional<Block> getBlock() {
return block;
}
}

@ -0,0 +1,125 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.results;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.diff.StateDiffTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmTrace;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"output", "revertReason", "stateDiff", "trace", "transactionHash", "vmTrace"})
public class TraceReplayResult {
private final String output;
private final String revertReason;
private final StateDiffTrace stateDiff;
private final List<FlatTrace> traces;
private final VmTrace vmTrace;
private final String transactionHash;
public TraceReplayResult(
final String output,
final String revertReason,
final StateDiffTrace stateDiff,
final List<FlatTrace> traces,
final String transactionHash,
final VmTrace vmTrace) {
this.output = output;
this.revertReason = revertReason;
this.stateDiff = stateDiff;
this.traces = traces;
this.transactionHash = transactionHash;
this.vmTrace = vmTrace;
}
@JsonGetter(value = "output")
public String getOutput() {
return output;
}
@JsonInclude(Include.NON_NULL)
@JsonGetter(value = "revertReason")
public String getRevertReason() {
return revertReason;
}
@JsonGetter(value = "stateDiff")
public StateDiffTrace getStateDiff() {
return stateDiff;
}
@JsonGetter(value = "trace")
public List<FlatTrace> getTraces() {
return traces;
}
@JsonGetter(value = "vmTrace")
public VmTrace getVmTrace() {
return vmTrace;
}
@JsonGetter(value = "transactionHash")
public String getTransactionHash() {
return transactionHash;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String output;
private String revertReason;
private StateDiffTrace stateDiff;
private final List<FlatTrace> traces = new ArrayList<>();
private VmTrace vmTrace;
private String transactionHash;
public TraceReplayResult build() {
return new TraceReplayResult(
output, revertReason, stateDiff, traces, transactionHash, vmTrace);
}
public void output(final String output) {
this.output = output;
}
public void revertReason(final String revertReason) {
this.revertReason = revertReason;
}
public void stateDiff(final StateDiffTrace stateDiff) {
this.stateDiff = stateDiff;
}
public void transactionHash(final String transactionHash) {
this.transactionHash = transactionHash;
}
public void addTrace(final FlatTrace trace) {
traces.add(trace);
}
public void vmTrace(final VmTrace vmTrace) {
this.vmTrace = vmTrace;
}
}
}

@ -530,19 +530,11 @@ public class FlatTraceGenerator {
private static boolean hasRevertInSubCall(
final TransactionTrace transactionTrace, final TraceFrame callFrame) {
for (int i = 0; i < transactionTrace.getTraceFrames().size(); i++) {
if (i + 1 < transactionTrace.getTraceFrames().size()) {
final TraceFrame next = transactionTrace.getTraceFrames().get(i + 1);
if (next.getDepth() == callFrame.getDepth()) {
if (next.getOpcode().equals("REVERT")) {
return true;
} else if (next.getOpcode().equals("RETURN")) {
return false;
}
}
}
}
return false;
return transactionTrace.getTraceFrames().stream()
.filter(traceFrame -> !traceFrame.equals(callFrame))
.takeWhile(traceFrame -> !traceFrame.getOpcode().equals("RETURN"))
.filter(traceFrame -> traceFrame.getOpcode().equals("REVERT"))
.anyMatch(traceFrame -> traceFrame.getDepth() == callFrame.getDepth());
}
private static String calculateCallingAddress(final FlatTrace.Context lastContext) {

@ -58,13 +58,12 @@ public class TraceJsonRpcMethods extends ApiGroupJsonRpcMethods {
final BlockReplay blockReplay =
new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain());
return mapOf(
new TraceReplayBlockTransactions(
() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceReplayBlockTransactions(protocolSchedule, blockchainQueries),
new TraceFilter(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceGet(() -> new BlockTracer(blockReplay), blockchainQueries, protocolSchedule),
new TraceTransaction(
() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceBlock(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceBlock(protocolSchedule, blockchainQueries),
new TraceCall(
blockchainQueries,
protocolSchedule,

@ -67,6 +67,7 @@ public class BlockchainQueries {
private final Blockchain blockchain;
private final Optional<Path> cachePath;
private final Optional<TransactionLogBloomCacher> transactionLogBloomCacher;
private final Optional<EthScheduler> ethScheduler;
private final ApiConfiguration apiConfig;
public BlockchainQueries(final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
@ -102,6 +103,7 @@ public class BlockchainQueries {
this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive;
this.cachePath = cachePath;
this.ethScheduler = scheduler;
this.transactionLogBloomCacher =
(cachePath.isPresent() && scheduler.isPresent())
? Optional.of(
@ -1024,4 +1026,8 @@ public class BlockchainQueries {
return logIndexOffset;
}
public Optional<EthScheduler> getEthScheduler() {
return ethScheduler;
}
}

@ -47,6 +47,7 @@ public class TraceFrame {
private final Optional<Bytes32[]> stack;
private final Optional<Bytes[]> memory;
private final Optional<Map<UInt256, UInt256>> storage;
private final WorldUpdater worldUpdater;
private final Optional<Bytes> revertReason;
private final Optional<Map<Address, Wei>> maybeRefunds;
@ -151,7 +152,7 @@ public class TraceFrame {
}
public Bytes getInputData() {
return inputData;
return this.inputData;
}
public Bytes getOutputData() {

@ -58,7 +58,9 @@ public class DebugOperationTracer implements OperationTracer {
public void tracePreExecution(final MessageFrame frame) {
preExecutionStack = captureStack(frame);
gasRemaining = frame.getRemainingGas();
inputData = frame.getInputData().copy();
if (lastFrame != null && frame.getMessageStackDepth() > lastFrame.getDepth())
inputData = frame.getInputData().copy();
else inputData = frame.getInputData();
pc = frame.getPC();
}

Loading…
Cancel
Save