mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 22:07:59 -05:00
Tracing multiple levels of calls now works properly (#352)
The iterative look back mechanism flat trace was using was missing many of the nesting subtlties, so a stack based calculation is used. A new test case with two levels of calls is also introduced. Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -54,4 +56,8 @@ public class TracingUtils {
|
||||
}
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
public static String weiAsHex(final Wei balance) {
|
||||
return balance.isZero() ? "0x0" : balance.toShortHexString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.diff;
|
||||
|
||||
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.TracingUtils;
|
||||
import org.hyperledger.besu.ethereum.core.AbstractWorldUpdater.UpdateTrackingAccount;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater;
|
||||
import org.hyperledger.besu.ethereum.debug.TraceFrame;
|
||||
|
||||
@@ -114,8 +114,7 @@ public class StateDiffGenerator {
|
||||
}
|
||||
|
||||
private static String balanceAsHex(final Account account) {
|
||||
final Wei balance = account.getBalance();
|
||||
return balance.isZero() ? "0x0" : balance.toShortHexString();
|
||||
return TracingUtils.weiAsHex(account.getBalance());
|
||||
}
|
||||
|
||||
private static String codeAsHex(final Account account) {
|
||||
|
||||
@@ -18,11 +18,11 @@ import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.debug.TraceFrame;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -74,7 +74,6 @@ public class Action {
|
||||
final Transaction transaction,
|
||||
final String lastContractAddress,
|
||||
final Address contractCallAddress,
|
||||
final TraceFrame traceFrame,
|
||||
final Gas gasRemaining,
|
||||
final Optional<Bytes> inputBytes,
|
||||
final String callType) {
|
||||
@@ -92,7 +91,7 @@ public class Action {
|
||||
return builder()
|
||||
.address(lastContractAddress)
|
||||
.refundAddress(contractCallAddress.toString())
|
||||
.balance(balance.toShortHexString());
|
||||
.balance(TracingUtils.weiAsHex(balance));
|
||||
}
|
||||
|
||||
public String getCallType() {
|
||||
@@ -176,11 +175,19 @@ public class Action {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCallType() {
|
||||
return callType;
|
||||
}
|
||||
|
||||
public Builder from(final String from) {
|
||||
this.from = from;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public Builder gas(final String gas) {
|
||||
this.gas = gas;
|
||||
return this;
|
||||
@@ -196,6 +203,10 @@ public class Action {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public Builder init(final String init) {
|
||||
this.init = init;
|
||||
return this;
|
||||
@@ -216,7 +227,7 @@ public class Action {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder refundAddress(final String refundAddress) {
|
||||
Builder refundAddress(final String refundAddress) {
|
||||
this.refundAddress = refundAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -115,37 +115,31 @@ public class FlatTrace implements Trace {
|
||||
public static class Context {
|
||||
|
||||
private final Builder builder;
|
||||
private boolean returned;
|
||||
private boolean isSubtrace = false;
|
||||
private long gasUsed = 0;
|
||||
|
||||
public Context(final Builder builder) {
|
||||
this(builder, false);
|
||||
}
|
||||
|
||||
Context(final Builder builder, final boolean returned) {
|
||||
Context(final Builder builder) {
|
||||
this.builder = builder;
|
||||
this.returned = returned;
|
||||
}
|
||||
|
||||
public Builder getBuilder() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public boolean isReturned() {
|
||||
return returned;
|
||||
void incGasUsed(final long gas) {
|
||||
setGasUsed(gasUsed + gas);
|
||||
}
|
||||
|
||||
boolean isSubtrace() {
|
||||
return isSubtrace;
|
||||
void decGasUsed(final long gas) {
|
||||
setGasUsed(gasUsed - gas);
|
||||
}
|
||||
|
||||
Context subTrace() {
|
||||
this.isSubtrace = true;
|
||||
return this;
|
||||
public long getGasUsed() {
|
||||
return gasUsed;
|
||||
}
|
||||
|
||||
void markAsReturned() {
|
||||
this.returned = true;
|
||||
public void setGasUsed(final long gasUsed) {
|
||||
this.gasUsed = gasUsed;
|
||||
builder.getResultBuilder().gasUsed("0x" + Long.toHexString(gasUsed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +164,8 @@ public class FlatTrace implements Trace {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder subtraces(final int subtraces) {
|
||||
this.subtraces = subtraces;
|
||||
return this;
|
||||
public int getSubtraces() {
|
||||
return subtraces;
|
||||
}
|
||||
|
||||
public Builder traceAddress(final List<Integer> traceAddress) {
|
||||
@@ -190,13 +183,8 @@ public class FlatTrace implements Trace {
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder incSubTraces() {
|
||||
return incSubTraces(1);
|
||||
}
|
||||
|
||||
Builder incSubTraces(final int n) {
|
||||
this.subtraces += n;
|
||||
return this;
|
||||
void incSubTraces() {
|
||||
this.subtraces++;
|
||||
}
|
||||
|
||||
public FlatTrace build() {
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace.Context;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.debug.TraceFrame;
|
||||
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
|
||||
@@ -31,11 +32,11 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
@@ -52,90 +53,77 @@ public class FlatTraceGenerator {
|
||||
public static Stream<Trace> generateFromTransactionTrace(
|
||||
final TransactionTrace transactionTrace, final AtomicInteger traceCounter) {
|
||||
final FlatTrace.Builder firstFlatTraceBuilder = FlatTrace.freshBuilder(transactionTrace);
|
||||
final String lastContractAddress =
|
||||
transactionTrace.getTransaction().getTo().orElse(Address.ZERO).toHexString();
|
||||
final Transaction tx = transactionTrace.getTransaction();
|
||||
final String lastContractAddress = tx.getTo().orElse(Address.ZERO).toHexString();
|
||||
|
||||
final Optional<String> smartContractCode =
|
||||
transactionTrace.getTransaction().getInit().isPresent()
|
||||
? Optional.of(transactionTrace.getResult().getOutput().toString())
|
||||
: Optional.empty();
|
||||
tx.getInit().map(__ -> transactionTrace.getResult().getOutput().toString());
|
||||
final Optional<String> smartContractAddress =
|
||||
smartContractCode.isPresent()
|
||||
? Optional.of(
|
||||
Address.contractAddress(
|
||||
transactionTrace.getTransaction().getSender(),
|
||||
transactionTrace.getTransaction().getNonce())
|
||||
.toHexString())
|
||||
: Optional.empty();
|
||||
smartContractCode.map(
|
||||
__ -> Address.contractAddress(tx.getSender(), tx.getNonce()).toHexString());
|
||||
|
||||
// set code field in result node
|
||||
smartContractCode.ifPresent(firstFlatTraceBuilder.getResultBuilder()::code);
|
||||
|
||||
// set init field if transaction is a smart contract deployment
|
||||
transactionTrace
|
||||
.getTransaction()
|
||||
.getInit()
|
||||
.map(Bytes::toHexString)
|
||||
.ifPresent(firstFlatTraceBuilder.getActionBuilder()::init);
|
||||
tx.getInit().map(Bytes::toHexString).ifPresent(firstFlatTraceBuilder.getActionBuilder()::init);
|
||||
|
||||
// set to, input and callType fields if not a smart contract
|
||||
transactionTrace
|
||||
.getTransaction()
|
||||
.getTo()
|
||||
.ifPresent(
|
||||
to ->
|
||||
firstFlatTraceBuilder
|
||||
.getActionBuilder()
|
||||
.to(to.toString())
|
||||
.callType("call")
|
||||
.input(
|
||||
transactionTrace
|
||||
.getTransaction()
|
||||
.getData()
|
||||
.map(Bytes::toHexString)
|
||||
.orElse(
|
||||
transactionTrace
|
||||
.getTransaction()
|
||||
.getInit()
|
||||
.map(Bytes::toHexString)
|
||||
.orElse(Bytes.EMPTY.toHexString()))));
|
||||
// declare a queue of transactionTrace contexts
|
||||
if (tx.getTo().isPresent()) {
|
||||
final Bytes payload = tx.getPayload();
|
||||
firstFlatTraceBuilder
|
||||
.getActionBuilder()
|
||||
.to(tx.getTo().map(Bytes::toHexString).orElse(null))
|
||||
.callType("call")
|
||||
.input(payload == null ? "0x" : payload.toHexString());
|
||||
} else {
|
||||
firstFlatTraceBuilder
|
||||
.type("create")
|
||||
.getResultBuilder()
|
||||
.address(smartContractAddress.orElse(null));
|
||||
}
|
||||
|
||||
final List<FlatTrace.Builder> flatTraces = new ArrayList<>();
|
||||
|
||||
// stack of previous contexts
|
||||
final Deque<FlatTrace.Context> tracesContexts = new ArrayDeque<>();
|
||||
|
||||
// add the first transactionTrace context to the queue of transactionTrace contexts
|
||||
final Context traceContext = new Context(firstFlatTraceBuilder);
|
||||
tracesContexts.addLast(traceContext);
|
||||
FlatTrace.Context currentContext = new FlatTrace.Context(firstFlatTraceBuilder);
|
||||
tracesContexts.addLast(currentContext);
|
||||
flatTraces.add(currentContext.getBuilder());
|
||||
// declare the first transactionTrace context as the previous transactionTrace context
|
||||
final List<Integer> addressVector = new ArrayList<>();
|
||||
final AtomicLong cumulativeGasCost = new AtomicLong(0);
|
||||
long cumulativeGasCost = 0;
|
||||
|
||||
int traceFrameIndex = 0;
|
||||
final List<TraceFrame> traceFrames = transactionTrace.getTraceFrames();
|
||||
final int lastTraceFrameIndex = traceFrames.size() - 1;
|
||||
for (final TraceFrame traceFrame : traceFrames) {
|
||||
cumulativeGasCost.addAndGet(traceFrame.getGasCost().orElse(Gas.ZERO).toLong());
|
||||
cumulativeGasCost += traceFrame.getGasCost().orElse(Gas.ZERO).toLong();
|
||||
final String opcodeString = traceFrame.getOpcode();
|
||||
if ("CALL".equals(opcodeString)
|
||||
|| "CALLCODE".equals(opcodeString)
|
||||
|| "DELEGATECALL".equals(opcodeString)
|
||||
|| "STATICCALL".equals(opcodeString)) {
|
||||
final Optional<TraceFrame> nextTraceFrame =
|
||||
(traceFrameIndex < lastTraceFrameIndex)
|
||||
? Optional.of(traceFrames.get(traceFrameIndex + 1))
|
||||
: Optional.empty();
|
||||
handleCall(
|
||||
transactionTrace,
|
||||
traceFrame,
|
||||
nextTraceFrame,
|
||||
lastContractAddress,
|
||||
cumulativeGasCost,
|
||||
addressVector,
|
||||
tracesContexts,
|
||||
traceFrameIndex,
|
||||
opcodeString.toLowerCase(Locale.US));
|
||||
currentContext =
|
||||
handleCall(
|
||||
transactionTrace,
|
||||
traceFrame,
|
||||
lastContractAddress,
|
||||
smartContractAddress,
|
||||
flatTraces,
|
||||
cumulativeGasCost,
|
||||
tracesContexts,
|
||||
traceFrameIndex,
|
||||
traceFrames,
|
||||
opcodeString.toLowerCase(Locale.US));
|
||||
} else if ("RETURN".equals(opcodeString) || "STOP".equals(opcodeString)) {
|
||||
handleReturn(transactionTrace, traceFrame, smartContractAddress, tracesContexts);
|
||||
currentContext.incGasUsed(cumulativeGasCost);
|
||||
currentContext = handleReturn(transactionTrace, traceFrame, tracesContexts, currentContext);
|
||||
} else if ("SELFDESTRUCT".equals(opcodeString)) {
|
||||
handleSelfDestruct(
|
||||
traceFrame, lastContractAddress, cumulativeGasCost, addressVector, tracesContexts);
|
||||
currentContext.incGasUsed(cumulativeGasCost);
|
||||
currentContext = handleSelfDestruct(traceFrame, flatTraces, tracesContexts);
|
||||
} else if (!traceFrame.getExceptionalHaltReasons().isEmpty()) {
|
||||
traceContext
|
||||
currentContext
|
||||
.getBuilder()
|
||||
.error(
|
||||
traceFrame.getExceptionalHaltReasons().stream()
|
||||
@@ -144,150 +132,110 @@ public class FlatTraceGenerator {
|
||||
}
|
||||
traceFrameIndex++;
|
||||
}
|
||||
final List<Trace> flatTraces = new ArrayList<>();
|
||||
tracesContexts.forEach(context -> flatTraces.add(context.getBuilder().build()));
|
||||
traceCounter.incrementAndGet();
|
||||
return flatTraces.stream();
|
||||
return flatTraces.stream().map(FlatTrace.Builder::build);
|
||||
}
|
||||
|
||||
private static void handleCall(
|
||||
private static FlatTrace.Context handleCall(
|
||||
final TransactionTrace transactionTrace,
|
||||
final TraceFrame traceFrame,
|
||||
final Optional<TraceFrame> nextTraceFrame,
|
||||
final String lastContractAddress,
|
||||
final AtomicLong cumulativeGasCost,
|
||||
final List<Integer> addressVector,
|
||||
final Optional<String> smartContractAddress,
|
||||
final List<FlatTrace.Builder> flatTraces,
|
||||
final long cumulativeGasCost,
|
||||
final Deque<FlatTrace.Context> tracesContexts,
|
||||
final int traceFrameIndex,
|
||||
final String callType) {
|
||||
final List<TraceFrame> traceFrames,
|
||||
final String opcodeString) {
|
||||
final FlatTrace.Context currentContext;
|
||||
final TraceFrame nextTraceFrame = traceFrames.get(traceFrameIndex + 1);
|
||||
final Bytes32[] stack = traceFrame.getStack().orElseThrow();
|
||||
final Address contractCallAddress = toAddress(stack[stack.length - 2]);
|
||||
if (addressVector.size() <= traceFrame.getDepth()) {
|
||||
addressVector.add(0);
|
||||
} else {
|
||||
addressVector.set(traceFrame.getDepth(), addressVector.get(traceFrame.getDepth()) + 1);
|
||||
}
|
||||
final List<Integer> traceAddress = Lists.newCopyOnWriteArrayList(addressVector);
|
||||
|
||||
final FlatTrace.Builder subTraceBuilder =
|
||||
FlatTrace.builder().traceAddress(traceAddress).resultBuilder(Result.builder());
|
||||
// get the next trace frame to set the gas field (gas remaining) of the sub trace
|
||||
final TraceFrame traceFrameAfterCall =
|
||||
transactionTrace.getTraceFrames().get(traceFrameIndex + 1);
|
||||
FlatTrace.builder()
|
||||
.traceAddress(calculateTraceAddress(tracesContexts))
|
||||
.resultBuilder(Result.builder());
|
||||
final Action.Builder subTraceActionBuilder =
|
||||
Action.createCallAction(
|
||||
transactionTrace.getTransaction(),
|
||||
lastContractAddress,
|
||||
smartContractAddress.orElse(lastContractAddress),
|
||||
contractCallAddress,
|
||||
traceFrame,
|
||||
traceFrameAfterCall.getGasRemaining(),
|
||||
nextTraceFrame.map(TraceFrame::getInputData),
|
||||
callType);
|
||||
nextTraceFrame.getGasRemaining(),
|
||||
Optional.ofNullable(nextTraceFrame.getInputData()),
|
||||
opcodeString.toLowerCase(Locale.US));
|
||||
|
||||
final long gasCost = cumulativeGasCost.longValue();
|
||||
// retrieve the previous transactionTrace context
|
||||
Optional.ofNullable(tracesContexts.peekLast())
|
||||
.ifPresent(
|
||||
previousContext -> {
|
||||
// increment sub traces counter of previous transactionTrace
|
||||
previousContext.getBuilder().incSubTraces();
|
||||
// set gas cost of previous transactionTrace
|
||||
previousContext
|
||||
.getBuilder()
|
||||
.getResultBuilder()
|
||||
.gasUsed(Gas.of(gasCost).toHexString());
|
||||
});
|
||||
tracesContexts.addLast(
|
||||
new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder)).subTrace());
|
||||
cumulativeGasCost.set(0);
|
||||
currentContext = new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder));
|
||||
currentContext.decGasUsed(cumulativeGasCost);
|
||||
tracesContexts.addLast(currentContext);
|
||||
flatTraces.add(currentContext.getBuilder());
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
private static void handleReturn(
|
||||
private static FlatTrace.Context handleReturn(
|
||||
final TransactionTrace transactionTrace,
|
||||
final TraceFrame traceFrame,
|
||||
final Optional<String> smartContractAddress,
|
||||
final Deque<FlatTrace.Context> tracesContexts) {
|
||||
final Deque<FlatTrace.Context> polledContexts = new ArrayDeque<>();
|
||||
FlatTrace.Context ctx;
|
||||
// find last non returned transactionTrace
|
||||
while ((ctx = tracesContexts.pollLast()) != null) {
|
||||
polledContexts.addFirst(ctx);
|
||||
// continue until finding a non returned context
|
||||
if (ctx.isReturned()) {
|
||||
continue;
|
||||
}
|
||||
final FlatTrace.Builder flatTraceBuilder = ctx.getBuilder();
|
||||
final Gas gasRemainingAtStartOfTrace =
|
||||
Gas.fromHexString(flatTraceBuilder.getActionBuilder().getGas());
|
||||
final Gas gasUsed = gasRemainingAtStartOfTrace.minus(traceFrame.getGasRemaining());
|
||||
final Result.Builder resultBuilder = flatTraceBuilder.getResultBuilder();
|
||||
final Gas finalGasUsed;
|
||||
if (ctx.isSubtrace()) {
|
||||
finalGasUsed = gasUsed;
|
||||
} else {
|
||||
finalGasUsed = computeGasUsed(transactionTrace, gasUsed);
|
||||
}
|
||||
|
||||
// set gas used for the trace
|
||||
resultBuilder.gasUsed(finalGasUsed.toHexString());
|
||||
// set address and type to create if smart contract deployment
|
||||
smartContractAddress.ifPresentOrElse(
|
||||
address -> {
|
||||
resultBuilder.address(address);
|
||||
flatTraceBuilder.type("create");
|
||||
},
|
||||
// set output otherwise
|
||||
() -> resultBuilder.output(traceFrame.getOutputData().toHexString()));
|
||||
ctx.markAsReturned();
|
||||
break;
|
||||
final Deque<FlatTrace.Context> tracesContexts,
|
||||
final FlatTrace.Context currentContext) {
|
||||
if (tracesContexts.size() == 1) {
|
||||
currentContext.setGasUsed(computeGasUsed(transactionTrace, currentContext.getGasUsed()));
|
||||
}
|
||||
// reinsert polled contexts add the end of the queue
|
||||
polledContexts.forEach(tracesContexts::addLast);
|
||||
if (currentContext.getBuilder().getResultBuilder().getCode() == null) {
|
||||
currentContext
|
||||
.getBuilder()
|
||||
.getResultBuilder()
|
||||
.output(traceFrame.getOutputData().toHexString());
|
||||
}
|
||||
tracesContexts.removeLast();
|
||||
final FlatTrace.Context nextContext = tracesContexts.peekLast();
|
||||
if (nextContext != null) {
|
||||
nextContext.getBuilder().incSubTraces();
|
||||
}
|
||||
return nextContext;
|
||||
}
|
||||
|
||||
private static void handleSelfDestruct(
|
||||
private static FlatTrace.Context handleSelfDestruct(
|
||||
final TraceFrame traceFrame,
|
||||
final String lastContractAddress,
|
||||
final AtomicLong cumulativeGasCost,
|
||||
final List<Integer> addressVector,
|
||||
final Deque<FlatTrace.Context> tracesContexts) {
|
||||
final List<FlatTrace.Builder> flatTraces,
|
||||
final Deque<Context> tracesContexts) {
|
||||
final Bytes32[] stack = traceFrame.getStack().orElseThrow();
|
||||
final Address refundAddress = toAddress(stack[0]);
|
||||
if (addressVector.size() <= traceFrame.getDepth()) {
|
||||
addressVector.add(0);
|
||||
}
|
||||
final FlatTrace.Builder subTraceBuilder =
|
||||
FlatTrace.builder().type("suicide").traceAddress(addressVector);
|
||||
FlatTrace.builder()
|
||||
.type("suicide")
|
||||
.traceAddress(calculateSelfDescructAddress(tracesContexts));
|
||||
|
||||
final AtomicReference<Wei> weiBalance = Atomics.newReference(Wei.ZERO);
|
||||
traceFrame
|
||||
.getMaybeRefunds()
|
||||
.ifPresent(refunds -> weiBalance.set(refunds.getOrDefault(refundAddress, Wei.ZERO)));
|
||||
|
||||
final Action.Builder callingAction = tracesContexts.peekLast().getBuilder().getActionBuilder();
|
||||
final String actionAddress =
|
||||
callingAction.getCallType().equals("call")
|
||||
? callingAction.getTo()
|
||||
: callingAction.getFrom();
|
||||
final Action.Builder subTraceActionBuilder =
|
||||
Action.createSelfDestructAction(lastContractAddress, refundAddress, weiBalance.get());
|
||||
Action.createSelfDestructAction(actionAddress, refundAddress, weiBalance.get());
|
||||
|
||||
final long gasCost = cumulativeGasCost.longValue();
|
||||
// retrieve the previous transactionTrace context
|
||||
if (!tracesContexts.isEmpty()) {
|
||||
final FlatTrace.Context previousContext = tracesContexts.peekLast();
|
||||
// increment sub traces counter of previous transactionTrace
|
||||
previousContext.getBuilder().incSubTraces();
|
||||
// set gas cost of previous transactionTrace
|
||||
previousContext.getBuilder().getResultBuilder().gasUsed(Gas.of(gasCost).toHexString());
|
||||
flatTraces.add(
|
||||
new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder)).getBuilder());
|
||||
final FlatTrace.Context lastContext = tracesContexts.removeLast();
|
||||
lastContext.getBuilder().incSubTraces();
|
||||
final FlatTrace.Context currentContext = tracesContexts.peekLast();
|
||||
if (currentContext != null) {
|
||||
currentContext.getBuilder().incSubTraces();
|
||||
}
|
||||
tracesContexts.addLast(
|
||||
new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder)));
|
||||
cumulativeGasCost.set(0);
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
private static Gas computeGasUsed(
|
||||
final TransactionTrace transactionTrace, final Gas fallbackValue) {
|
||||
private static long computeGasUsed(
|
||||
final TransactionTrace transactionTrace, final long fallbackValue) {
|
||||
final long firstFrameGasRemaining =
|
||||
transactionTrace.getTraceFrames().get(0).getGasRemaining().toLong();
|
||||
final long gasRemainingAfterTransactionWasProcessed =
|
||||
transactionTrace.getResult().getGasRemaining();
|
||||
if (firstFrameGasRemaining > gasRemainingAfterTransactionWasProcessed) {
|
||||
return Gas.of(firstFrameGasRemaining - gasRemainingAfterTransactionWasProcessed);
|
||||
return firstFrameGasRemaining - gasRemainingAfterTransactionWasProcessed;
|
||||
} else {
|
||||
return fallbackValue;
|
||||
}
|
||||
@@ -297,4 +245,18 @@ public class FlatTraceGenerator {
|
||||
return Address.wrap(
|
||||
Bytes.of(Arrays.copyOfRange(value.toArray(), Bytes32.SIZE - Address.SIZE, Bytes32.SIZE)));
|
||||
}
|
||||
|
||||
private static List<Integer> calculateTraceAddress(final Deque<FlatTrace.Context> contexts) {
|
||||
return contexts.stream()
|
||||
.map(context -> context.getBuilder().getSubtraces())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<Integer> calculateSelfDescructAddress(
|
||||
final Deque<FlatTrace.Context> contexts) {
|
||||
return Streams.concat(
|
||||
contexts.stream()
|
||||
.map(context -> context.getBuilder().getSubtraces())) // , Stream.of(0))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,10 @@ public class Result {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Builder address(final String address) {
|
||||
this.address = address;
|
||||
return this;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc;
|
||||
|
||||
import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -159,7 +160,15 @@ public abstract class AbstractJsonRpcHttpBySpecTest extends AbstractJsonRpcHttpS
|
||||
final String expectedResult = expectedResponse.get("result").toString();
|
||||
final String actualResult = responseBody.get("result").toString();
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
assertThat(mapper.readTree(actualResult)).isEqualTo(mapper.readTree(expectedResult));
|
||||
mapper.configure(INDENT_OUTPUT, true);
|
||||
assertThat(
|
||||
mapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(mapper.readTree(actualResult)))
|
||||
.isEqualTo(
|
||||
mapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(mapper.readTree(expectedResult)));
|
||||
}
|
||||
|
||||
// Check error
|
||||
|
||||
@@ -256,6 +256,72 @@
|
||||
"to": "0x00F0000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"number": "0x12",
|
||||
"transactions": [
|
||||
{
|
||||
"comment": "Memory Read.",
|
||||
"secretKey": "0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"to": "0x0100000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"comment": "Revert.",
|
||||
"secretKey": "0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"to": "0x0110000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"number": "0x13",
|
||||
"transactions": [
|
||||
{
|
||||
"comment": "Deploy contract that will self-destruct when called.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x6004600C60003960046000F3600035FF"
|
||||
},
|
||||
{
|
||||
"comment": "call the prior contract then call log contract 00a0..00.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x60006000600060006000738f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f5AF1600060006000600060007300A00000000000000000000000000000000000005AF1"
|
||||
},
|
||||
{
|
||||
"comment": "Deploy contract that will self-destruct when called.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x6004600C60003960046000F3600035FF"
|
||||
},
|
||||
{
|
||||
"comment": "call the prior contract then callcode log contract 00a0..00.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x60006000600060006000732c2b9c9a4a25e24b174f26114e8926a9f2128fe45AF2600060006000600060007300A00000000000000000000000000000000000005AF2"
|
||||
},
|
||||
{
|
||||
"comment": "Deploy contract that will self-destruct when called.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x6004600C60003960046000F3600035FF"
|
||||
},
|
||||
{
|
||||
"comment": "call the prior contract then delegatecall log contract 00a0..00.",
|
||||
"secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"gasLimit": "0xFFFFF2",
|
||||
"gasPrice": "0xEF",
|
||||
"data": "0x600060006000600073fb88de099e13c3ed21f80a7a1e49f8caecf10df65AF460006000600060007300A00000000000000000000000000000000000005AF4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "trace_replayBlockTransactions",
|
||||
"params": [
|
||||
"0x13",
|
||||
[
|
||||
"trace"
|
||||
]
|
||||
],
|
||||
"id": 415
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"result": [
|
||||
{
|
||||
"output": "0x600035ff",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff300e",
|
||||
"init": "0x6004600c60003960046000f3600035ff",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f",
|
||||
"code": "0x600035ff",
|
||||
"gasUsed": "0x338"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0x1309b6d2187aa8b0dfe78fcf0a96d4a3e861bfbc381959d253ede57624a37f9b",
|
||||
"vmTrace": null
|
||||
},
|
||||
{
|
||||
"output": "0x",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff2e26",
|
||||
"init": "0x60006000600060006000738f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f5af1600060006000600060007300a00000000000000000000000000000000000005af1",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0x9fbda871d559710256a2502a2517b794b482db40",
|
||||
"code": "0x",
|
||||
"gasUsed": "0x1c39"
|
||||
},
|
||||
"subtraces": 2,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "call",
|
||||
"from": "0x9fbda871d559710256a2502a2517b794b482db40",
|
||||
"gas": "0xfb2ea9",
|
||||
"input": "0x",
|
||||
"to": "0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x138e",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 1,
|
||||
"traceAddress": [
|
||||
0
|
||||
],
|
||||
"type": "call"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"address": "0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f",
|
||||
"balance": "0x0",
|
||||
"refundAddress": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"result": null,
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type": "suicide"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "call",
|
||||
"from": "0x9fbda871d559710256a2502a2517b794b482db40",
|
||||
"gas": "0xfb18a5",
|
||||
"input": "0x",
|
||||
"to": "0x00a0000000000000000000000000000000000000",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x30b",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
1
|
||||
],
|
||||
"type": "call"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0x6b9b967cfbeedeb7f0f4956b8103075ddfcea26c01d6d5dc3f9e2ed2ec9c42c0",
|
||||
"vmTrace": null
|
||||
},
|
||||
{
|
||||
"output": "0x600035ff",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff300e",
|
||||
"init": "0x6004600c60003960046000f3600035ff",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4",
|
||||
"code": "0x600035ff",
|
||||
"gasUsed": "0x338"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0xf12fd37f0836bd51f21bc15aa6bf5bea4b62fbbd39e1ee06725f91ac13ebc904",
|
||||
"vmTrace": null
|
||||
},
|
||||
{
|
||||
"output": "0x",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff2e26",
|
||||
"init": "0x60006000600060006000732c2b9c9a4a25e24b174f26114e8926a9f2128fe45af2600060006000600060007300a00000000000000000000000000000000000005af2",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0x30753e4a8aad7f8597332e813735def5dd395028",
|
||||
"code": "0x",
|
||||
"gasUsed": "0x1c39"
|
||||
},
|
||||
"subtraces": 2,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "callcode",
|
||||
"from": "0x30753e4a8aad7f8597332e813735def5dd395028",
|
||||
"gas": "0xfb2ea9",
|
||||
"input": "0x",
|
||||
"to": "0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x138e",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 1,
|
||||
"traceAddress": [
|
||||
0
|
||||
],
|
||||
"type": "call"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"address": "0x30753e4a8aad7f8597332e813735def5dd395028",
|
||||
"balance": "0x0",
|
||||
"refundAddress": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"result": null,
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type": "suicide"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "callcode",
|
||||
"from": "0x30753e4a8aad7f8597332e813735def5dd395028",
|
||||
"gas": "0xfb18a5",
|
||||
"input": "0x",
|
||||
"to": "0x00a0000000000000000000000000000000000000",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x30b",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
1
|
||||
],
|
||||
"type": "call"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0x4c253746668dca6ac3f7b9bc18248b558a95b5fc881d140872c2dff984d344a7",
|
||||
"vmTrace": null
|
||||
},
|
||||
{
|
||||
"output": "0x600035ff",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff300e",
|
||||
"init": "0x6004600c60003960046000f3600035ff",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6",
|
||||
"code": "0x600035ff",
|
||||
"gasUsed": "0x338"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0x821ca63d171c5a3c60d32a738803092a52562056db3727a175f659cf49aae283",
|
||||
"vmTrace": null
|
||||
},
|
||||
{
|
||||
"output": "0x",
|
||||
"stateDiff": null,
|
||||
"trace": [
|
||||
{
|
||||
"action": {
|
||||
"from": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"gas": "0xff2e4e",
|
||||
"init": "0x600060006000600073fb88de099e13c3ed21f80a7a1e49f8caecf10df65af460006000600060007300a00000000000000000000000000000000000005af4",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"address": "0xaa588d3737b611bafd7bd713445b314bd453a5c8",
|
||||
"code": "0x",
|
||||
"gasUsed": "0x1c33"
|
||||
},
|
||||
"subtraces": 2,
|
||||
"traceAddress": [],
|
||||
"type": "create"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "delegatecall",
|
||||
"from": "0xaa588d3737b611bafd7bd713445b314bd453a5c8",
|
||||
"gas": "0xfb2ed3",
|
||||
"input": "0x",
|
||||
"to": "0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x138e",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 1,
|
||||
"traceAddress": [
|
||||
0
|
||||
],
|
||||
"type": "call"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"address": "0xaa588d3737b611bafd7bd713445b314bd453a5c8",
|
||||
"balance": "0x0",
|
||||
"refundAddress": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"result": null,
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type": "suicide"
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"callType": "delegatecall",
|
||||
"from": "0xaa588d3737b611bafd7bd713445b314bd453a5c8",
|
||||
"gas": "0xfb18d2",
|
||||
"input": "0x",
|
||||
"to": "0x00a0000000000000000000000000000000000000",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"gasUsed": "0x30b",
|
||||
"output": "0x"
|
||||
},
|
||||
"subtraces": 0,
|
||||
"traceAddress": [
|
||||
1
|
||||
],
|
||||
"type": "call"
|
||||
}
|
||||
],
|
||||
"transactionHash": "0xb795475e8f1820b683b60eb1a366bdc23e29f9cdf9639f7768c6644d20e3bbd1",
|
||||
"vmTrace": null
|
||||
}
|
||||
],
|
||||
"id": 415
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
Reference in New Issue
Block a user