[Tracing API] Handling of multiple transactions in a block. (#304)

* Handle multiple transactions in the same block.
* Fix a bug in `BlockTracer`. The bug was causing multiple `TransactionTrace` to share the same list of `TraceFrame`.
A new method has been added in `DebugOperationTracer` to reset the list of frames for successive transactions in a block.

Signed-off-by: Abdelhamid Bakhta <abdelhamid.bakhta@consensys.net>
pull/313/head
Abdelhamid Bakhta 5 years ago committed by GitHub
parent 24c0a72e67
commit 7793a15f3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java
  2. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java
  3. BIN
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/blocks.bin
  4. 8
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/src/blocks.json
  5. 32
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/flat/trace_replayBlockTransactions_0x4.json
  6. 155
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_vmTrace_replayBlockTransactions_0x4.json
  7. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java

@ -101,57 +101,57 @@ public class TraceReplayBlockTransactions extends AbstractBlockParameterMethod {
return blockTracer
.trace(block, new DebugOperationTracer(traceOptions))
.map(BlockTrace::getTransactionTraces)
.map((traces) -> formatTraces(traces, traceTypeParameter))
.map((traces) -> generateTracesFromTransactionTrace(traces, traceTypeParameter))
.orElse(null);
}
private JsonNode formatTraces(
final List<TransactionTrace> traces, final TraceTypeParameter traceTypeParameter) {
private JsonNode generateTracesFromTransactionTrace(
final List<TransactionTrace> transactionTraces, final TraceTypeParameter traceTypeParameter) {
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
final ObjectMapper mapper = new ObjectMapper();
final ArrayNode resultArrayNode = mapper.createArrayNode();
final ObjectNode resultNode = mapper.createObjectNode();
final AtomicInteger traceCounter = new AtomicInteger(0);
traces.stream()
.findFirst()
.ifPresent(
transactionTrace -> {
resultNode.put(
"transactionHash", transactionTrace.getTransaction().getHash().toHexString());
resultNode.put("output", transactionTrace.getResult().getOutput().toString());
});
transactionTraces.forEach(
transactionTrace ->
handleTransactionTrace(
transactionTrace, traceTypes, mapper, resultArrayNode, traceCounter));
return resultArrayNode;
}
private void handleTransactionTrace(
final TransactionTrace transactionTrace,
final Set<TraceTypeParameter.TraceType> traceTypes,
final ObjectMapper mapper,
final ArrayNode resultArrayNode,
final AtomicInteger traceCounter) {
final ObjectNode resultNode = mapper.createObjectNode();
resultNode.put("transactionHash", transactionTrace.getTransaction().getHash().toHexString());
resultNode.put("output", transactionTrace.getResult().getOutput().toString());
setEmptyArrayIfNotPresent(resultNode, "trace");
setNullNodesIfNotPresent(resultNode, "vmTrace", "stateDiff");
if (traceTypes.contains(TraceTypeParameter.TraceType.TRACE)) {
formatTraces(
generateTracesFromTransactionTrace(
resultNode.putArray("trace")::addPOJO,
traces,
transactionTrace,
FlatTraceGenerator::generateFromTransactionTrace,
traceCounter);
}
if (traceTypes.contains(TraceTypeParameter.TraceType.VM_TRACE)) {
formatTraces(
generateTracesFromTransactionTrace(
trace -> resultNode.putPOJO("vmTrace", trace),
traces,
(transactionTrace, ignored) ->
new VmTraceGenerator(transactionTrace).generateTraceStream(),
transactionTrace,
(__, ignored) -> new VmTraceGenerator(transactionTrace).generateTraceStream(),
traceCounter);
}
setEmptyArrayIfNotPresent(resultNode, "trace");
setNullNodesIfNotPresent(resultNode, "vmTrace", "stateDiff");
resultArrayNode.add(resultNode);
return resultArrayNode;
}
private void formatTraces(
private void generateTracesFromTransactionTrace(
final TraceWriter writer,
final List<TransactionTrace> traces,
final TransactionTrace transactionTrace,
final TraceFormatter formatter,
final AtomicInteger traceCounter) {
traces.forEach(
(transactionTrace) ->
formatter.format(transactionTrace, traceCounter).forEachOrdered(writer::write));
formatter.format(transactionTrace, traceCounter).forEachOrdered(writer::write);
}
private void setNullNodesIfNotPresent(final ObjectNode parentNode, final String... keys) {

@ -17,10 +17,12 @@ 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.Block;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.List;
import java.util.Optional;
/** Used to produce debug traces of blocks */
@ -53,7 +55,9 @@ public class BlockTracer {
tracer,
new BlockHashLookup(header, blockchain),
false);
return new TransactionTrace(transaction, result, tracer.getTraceFrames());
final List<TraceFrame> traceFrames = tracer.copyTraceFrames();
tracer.reset();
return new TransactionTrace(transaction, result, traceFrames);
};
}
}

@ -40,6 +40,14 @@
"gasPrice": "0xEF",
"to": "0x0010000000000000000000000000000000000000",
"data": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"
},
{
"comment": "Set contract storage (key,value)'s: (1,3),(2,4)",
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"gasLimit": "0xFFFFF2",
"gasPrice": "0xEF",
"to": "0x0010000000000000000000000000000000000000",
"data": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004"
}
]
},

@ -14,8 +14,8 @@
"jsonrpc": "2.0",
"result": [
{
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"output": "0x",
"stateDiff": null,
"trace": [
{
"action": {
@ -31,13 +31,37 @@
"output": "0x"
},
"subtraces": 0,
"traceAddress": [
"traceAddress": [],
"type": "call"
}
],
"vmTrace": null,
"stateDiff": null
},
{
"transactionHash": "0xf882ec206292910527fd7095e59a1ca027b873296f1eba3886aa1addc4ff0ab9",
"output": "0x",
"trace": [
{
"action": {
"callType": "call",
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
"gas": "0xffaaea",
"input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004",
"to": "0x0010000000000000000000000000000000000000",
"value": "0x0"
},
"result": {
"gasUsed": "0x9c58",
"output": "0x"
},
"subtraces": 0,
"traceAddress": [],
"type": "call"
}
],
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"vmTrace": null
"vmTrace": null,
"stateDiff": null
}
],
"id": 415

@ -16,7 +16,8 @@
{
"output": "0x",
"stateDiff": null,
"trace": [],
"trace": [
],
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"vmTrace": {
"code": "0x6020356000355560603560403555",
@ -77,7 +78,8 @@
"cost": 20000,
"ex": {
"mem": null,
"push": [],
"push": [
],
"store": {
"key": "0x1",
"val": "0x1"
@ -143,7 +145,8 @@
"cost": 20000,
"ex": {
"mem": null,
"push": [],
"push": [
],
"store": {
"key": "0x2",
"val": "0x2"
@ -155,6 +158,152 @@
}
]
}
},
{
"output": "0x",
"stateDiff": null,
"trace": [
],
"transactionHash": "0xf882ec206292910527fd7095e59a1ca027b873296f1eba3886aa1addc4ff0ab9",
"vmTrace": {
"code": "0x6020356000355560603560403555",
"ops": [
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x20"
],
"store": null,
"used": 16755431
},
"pc": 0,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x3"
],
"store": null,
"used": 16755428
},
"pc": 2,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x0"
],
"store": null,
"used": 16755425
},
"pc": 3,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x1"
],
"store": null,
"used": 16755422
},
"pc": 5,
"sub": null
},
{
"cost": 20000,
"ex": {
"mem": null,
"push": [
],
"store": {
"key": "0x1",
"val": "0x3"
},
"used": 16735422
},
"pc": 6,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x60"
],
"store": null,
"used": 16735419
},
"pc": 7,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x4"
],
"store": null,
"used": 16735416
},
"pc": 9,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x40"
],
"store": null,
"used": 16735413
},
"pc": 10,
"sub": null
},
{
"cost": 3,
"ex": {
"mem": null,
"push": [
"0x2"
],
"store": null,
"used": 16735410
},
"pc": 12,
"sub": null
},
{
"cost": 20000,
"ex": {
"mem": null,
"push": [
],
"store": {
"key": "0x2",
"val": "0x4"
},
"used": 16715410
},
"pc": 13,
"sub": null
}
]
}
}
],
"id": 415

@ -35,7 +35,7 @@ import org.apache.tuweni.units.bigints.UInt256;
public class DebugOperationTracer implements OperationTracer {
private final TraceOptions options;
private final List<TraceFrame> traceFrames = new ArrayList<>();
private List<TraceFrame> traceFrames = new ArrayList<>();
private TraceFrame lastFrame;
public DebugOperationTracer(final TraceOptions options) {
@ -140,4 +140,13 @@ public class DebugOperationTracer implements OperationTracer {
public List<TraceFrame> getTraceFrames() {
return traceFrames;
}
public void reset() {
traceFrames = new ArrayList<>();
lastFrame = null;
}
public List<TraceFrame> copyTraceFrames() {
return new ArrayList<>(traceFrames);
}
}

Loading…
Cancel
Save