[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. 34
      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 return blockTracer
.trace(block, new DebugOperationTracer(traceOptions)) .trace(block, new DebugOperationTracer(traceOptions))
.map(BlockTrace::getTransactionTraces) .map(BlockTrace::getTransactionTraces)
.map((traces) -> formatTraces(traces, traceTypeParameter)) .map((traces) -> generateTracesFromTransactionTrace(traces, traceTypeParameter))
.orElse(null); .orElse(null);
} }
private JsonNode formatTraces( private JsonNode generateTracesFromTransactionTrace(
final List<TransactionTrace> traces, final TraceTypeParameter traceTypeParameter) { final List<TransactionTrace> transactionTraces, final TraceTypeParameter traceTypeParameter) {
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes(); final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
final ArrayNode resultArrayNode = mapper.createArrayNode(); final ArrayNode resultArrayNode = mapper.createArrayNode();
final ObjectNode resultNode = mapper.createObjectNode();
final AtomicInteger traceCounter = new AtomicInteger(0); final AtomicInteger traceCounter = new AtomicInteger(0);
traces.stream() transactionTraces.forEach(
.findFirst() transactionTrace ->
.ifPresent( handleTransactionTrace(
transactionTrace -> { transactionTrace, traceTypes, mapper, resultArrayNode, traceCounter));
resultNode.put( return resultArrayNode;
"transactionHash", transactionTrace.getTransaction().getHash().toHexString()); }
resultNode.put("output", transactionTrace.getResult().getOutput().toString());
});
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)) { if (traceTypes.contains(TraceTypeParameter.TraceType.TRACE)) {
formatTraces( generateTracesFromTransactionTrace(
resultNode.putArray("trace")::addPOJO, resultNode.putArray("trace")::addPOJO,
traces, transactionTrace,
FlatTraceGenerator::generateFromTransactionTrace, FlatTraceGenerator::generateFromTransactionTrace,
traceCounter); traceCounter);
} }
if (traceTypes.contains(TraceTypeParameter.TraceType.VM_TRACE)) { if (traceTypes.contains(TraceTypeParameter.TraceType.VM_TRACE)) {
formatTraces( generateTracesFromTransactionTrace(
trace -> resultNode.putPOJO("vmTrace", trace), trace -> resultNode.putPOJO("vmTrace", trace),
traces, transactionTrace,
(transactionTrace, ignored) -> (__, ignored) -> new VmTraceGenerator(transactionTrace).generateTraceStream(),
new VmTraceGenerator(transactionTrace).generateTraceStream(),
traceCounter); traceCounter);
} }
setEmptyArrayIfNotPresent(resultNode, "trace");
setNullNodesIfNotPresent(resultNode, "vmTrace", "stateDiff");
resultArrayNode.add(resultNode); resultArrayNode.add(resultNode);
return resultArrayNode;
} }
private void formatTraces( private void generateTracesFromTransactionTrace(
final TraceWriter writer, final TraceWriter writer,
final List<TransactionTrace> traces, final TransactionTrace transactionTrace,
final TraceFormatter formatter, final TraceFormatter formatter,
final AtomicInteger traceCounter) { final AtomicInteger traceCounter) {
traces.forEach( formatter.format(transactionTrace, traceCounter).forEachOrdered(writer::write);
(transactionTrace) ->
formatter.format(transactionTrace, traceCounter).forEachOrdered(writer::write));
} }
private void setNullNodesIfNotPresent(final ObjectNode parentNode, final String... keys) { 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.api.jsonrpc.internal.processor.BlockReplay.TransactionAction;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Hash; 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.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.List;
import java.util.Optional; import java.util.Optional;
/** Used to produce debug traces of blocks */ /** Used to produce debug traces of blocks */
@ -53,7 +55,9 @@ public class BlockTracer {
tracer, tracer,
new BlockHashLookup(header, blockchain), new BlockHashLookup(header, blockchain),
false); 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", "gasPrice": "0xEF",
"to": "0x0010000000000000000000000000000000000000", "to": "0x0010000000000000000000000000000000000000",
"data": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002" "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", "jsonrpc": "2.0",
"result": [ "result": [
{ {
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"output": "0x", "output": "0x",
"stateDiff": null,
"trace": [ "trace": [
{ {
"action": { "action": {
@ -31,13 +31,37 @@
"output": "0x" "output": "0x"
}, },
"subtraces": 0, "subtraces": 0,
"traceAddress": [ "traceAddress": [],
],
"type": "call" "type": "call"
} }
], ],
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7", "vmTrace": null,
"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"
}
],
"vmTrace": null,
"stateDiff": null
} }
], ],
"id": 415 "id": 415

@ -16,7 +16,8 @@
{ {
"output": "0x", "output": "0x",
"stateDiff": null, "stateDiff": null,
"trace": [], "trace": [
],
"transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7", "transactionHash": "0x4de634fe767d1f6d0512ca0c9c0a054d3a2596f7cdd7c1eea5f93046a740b3c7",
"vmTrace": { "vmTrace": {
"code": "0x6020356000355560603560403555", "code": "0x6020356000355560603560403555",
@ -77,7 +78,8 @@
"cost": 20000, "cost": 20000,
"ex": { "ex": {
"mem": null, "mem": null,
"push": [], "push": [
],
"store": { "store": {
"key": "0x1", "key": "0x1",
"val": "0x1" "val": "0x1"
@ -143,7 +145,8 @@
"cost": 20000, "cost": 20000,
"ex": { "ex": {
"mem": null, "mem": null,
"push": [], "push": [
],
"store": { "store": {
"key": "0x2", "key": "0x2",
"val": "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 "id": 415

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

Loading…
Cancel
Save