use of org.hyperledger.besu.ethereum.debug.TraceFrame in project besu by hyperledger.
the class DebugOperationTracer method tracePrecompileCall.
@Override
public void tracePrecompileCall(final MessageFrame frame, final Gas gasRequirement, final Bytes output) {
if (traceFrames.isEmpty()) {
final TraceFrame traceFrame = new TraceFrame(frame.getPC(), Optional.empty(), frame.getRemainingGas(), Optional.empty(), frame.getGasRefund(), frame.getMessageStackDepth(), Optional.empty(), frame.getRecipientAddress(), frame.getValue(), frame.getInputData().copy(), frame.getOutputData(), Optional.empty(), Optional.empty(), Optional.empty(), frame.getWorldUpdater(), Optional.empty(), Optional.ofNullable(frame.getRefunds()), Optional.ofNullable(frame.getCode()), frame.getMaxStackSize(), Optional.empty(), true, Optional.empty(), Optional.empty());
traceFrames.add(traceFrame);
}
traceFrames.get(traceFrames.size() - 1).setPrecompiledGasCost(Optional.of(gasRequirement));
}
use of org.hyperledger.besu.ethereum.debug.TraceFrame in project besu by hyperledger.
the class DebugTraceBlockByHashTest method shouldReturnCorrectResponse.
@Test
public void shouldReturnCorrectResponse() {
final Object[] params = new Object[] { blockHash };
final JsonRpcRequestContext request = new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_traceBlockByHash", params));
final TraceFrame traceFrame = new TraceFrame(12, Optional.of("NONE"), 45L, OptionalLong.of(56L), 0L, 2, Optional.empty(), null, Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), Optional.empty(), Optional.empty(), null, Optional.empty(), Optional.empty(), Optional.empty(), 0, Optional.empty(), false, Optional.empty(), Optional.empty());
final TransactionProcessingResult transaction1Result = mock(TransactionProcessingResult.class);
final TransactionProcessingResult transaction2Result = mock(TransactionProcessingResult.class);
final TransactionTrace transaction1Trace = mock(TransactionTrace.class);
final TransactionTrace transaction2Trace = mock(TransactionTrace.class);
BlockTrace blockTrace = new BlockTrace(Arrays.asList(transaction1Trace, transaction2Trace));
when(transaction1Trace.getTraceFrames()).thenReturn(Arrays.asList(traceFrame));
when(transaction2Trace.getTraceFrames()).thenReturn(Arrays.asList(traceFrame));
when(transaction1Trace.getResult()).thenReturn(transaction1Result);
when(transaction2Trace.getResult()).thenReturn(transaction2Result);
when(transaction1Result.getOutput()).thenReturn(Bytes.fromHexString("1234"));
when(transaction2Result.getOutput()).thenReturn(Bytes.fromHexString("1234"));
when(blockTracer.trace(eq(blockHash), any())).thenReturn(Optional.of(blockTrace));
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceBlockByHash.response(request);
final Collection<?> result = (Collection<?>) response.getResult();
assertThat(result).hasSize(2);
}
use of org.hyperledger.besu.ethereum.debug.TraceFrame in project besu by hyperledger.
the class DebugTraceBlockByNumberTest method shouldReturnCorrectResponse.
@Test
public void shouldReturnCorrectResponse() {
final long blockNumber = 1L;
final Object[] params = new Object[] { Long.toHexString(blockNumber) };
final JsonRpcRequestContext request = new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_traceBlockByNumber", params));
final TraceFrame traceFrame = new TraceFrame(12, Optional.of("NONE"), 45L, OptionalLong.of(56L), 0L, 2, Optional.empty(), null, Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), Optional.empty(), Optional.empty(), null, Optional.empty(), Optional.empty(), Optional.empty(), 0, Optional.empty(), false, Optional.empty(), Optional.empty());
final TransactionProcessingResult transaction1Result = mock(TransactionProcessingResult.class);
final TransactionProcessingResult transaction2Result = mock(TransactionProcessingResult.class);
final TransactionTrace transaction1Trace = mock(TransactionTrace.class);
final TransactionTrace transaction2Trace = mock(TransactionTrace.class);
final BlockTrace blockTrace = new BlockTrace(asList(transaction1Trace, transaction2Trace));
when(transaction1Trace.getTraceFrames()).thenReturn(singletonList(traceFrame));
when(transaction2Trace.getTraceFrames()).thenReturn(singletonList(traceFrame));
when(transaction1Trace.getResult()).thenReturn(transaction1Result);
when(transaction2Trace.getResult()).thenReturn(transaction2Result);
when(transaction1Result.getOutput()).thenReturn(Bytes.fromHexString("1234"));
when(transaction2Result.getOutput()).thenReturn(Bytes.fromHexString("1234"));
when(blockchain.getBlockHashByNumber(blockNumber)).thenReturn(Optional.of(blockHash));
when(blockTracer.trace(eq(blockHash), any())).thenReturn(Optional.of(blockTrace));
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceBlockByNumber.response(request);
final Collection<DebugTraceTransactionResult> result = getResult(response);
assertThat(result).usingFieldByFieldElementComparator().isEqualTo(DebugTraceTransactionResult.of(blockTrace.getTransactionTraces()));
}
use of org.hyperledger.besu.ethereum.debug.TraceFrame in project besu by hyperledger.
the class StateDiffGenerator method generateStateDiff.
public Stream<Trace> generateStateDiff(final TransactionTrace transactionTrace) {
final List<TraceFrame> traceFrames = transactionTrace.getTraceFrames();
if (traceFrames.size() < 1) {
return Stream.empty();
}
// This corresponds to the world state after the TX executed
// It is two deep because of the way we addressed Spurious Dragon.
final WorldUpdater transactionUpdater = traceFrames.get(0).getWorldUpdater().parentUpdater().get().parentUpdater().get();
// This corresponds to the world state prior to the TX execution,
// Either the initial block state or the state of the prior TX
final WorldUpdater previousUpdater = transactionUpdater.parentUpdater().get();
final StateDiffTrace stateDiffResult = new StateDiffTrace();
for (final Account updatedAccount : transactionUpdater.getTouchedAccounts()) {
final Address accountAddress = updatedAccount.getAddress();
final Account rootAccount = previousUpdater.get(accountAddress);
// calculate storage diff
final Map<String, DiffNode> storageDiff = new TreeMap<>();
for (final Map.Entry<UInt256, UInt256> entry : ((UpdateTrackingAccount<?>) updatedAccount).getUpdatedStorage().entrySet()) {
// FIXME cast
final UInt256 newValue = entry.getValue();
if (rootAccount == null) {
if (!UInt256.ZERO.equals(newValue)) {
storageDiff.put(entry.getKey().toHexString(), new DiffNode(null, newValue.toHexString()));
}
} else {
final UInt256 originalValue = rootAccount.getStorageValue(entry.getKey());
if (!originalValue.equals(newValue)) {
storageDiff.put(entry.getKey().toHexString(), new DiffNode(originalValue.toHexString(), newValue.toHexString()));
}
}
}
// populate the diff object
final AccountDiff accountDiff = new AccountDiff(createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::balanceAsHex), createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::codeAsHex), createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::nonceAsHex), storageDiff);
if (accountDiff.hasDifference()) {
stateDiffResult.put(accountAddress.toHexString(), accountDiff);
}
}
// Add deleted accounts
for (final Address accountAddress : transactionUpdater.getDeletedAccountAddresses()) {
final Account deletedAccount = previousUpdater.get(accountAddress);
if (deletedAccount == null) {
continue;
}
final AccountDiff accountDiff = new AccountDiff(createDiffNode(deletedAccount, null, StateDiffGenerator::balanceAsHex), createDiffNode(deletedAccount, null, StateDiffGenerator::codeAsHex), createDiffNode(deletedAccount, null, StateDiffGenerator::nonceAsHex), Collections.emptyMap());
stateDiffResult.put(accountAddress.toHexString(), accountDiff);
}
return Stream.of(stateDiffResult);
}
use of org.hyperledger.besu.ethereum.debug.TraceFrame in project besu by hyperledger.
the class FlatTraceGenerator method generateFromTransactionTrace.
/**
* Generates a stream of {@link Trace} from the passed {@link TransactionTrace} data.
*
* @param protocolSchedule the current {@link ProtocolSchedule} to use
* @param transactionTrace the {@link TransactionTrace} to use
* @param block the {@link Block} to use
* @param traceCounter the current trace counter value
* @param consumer to use to add additional contextual information to the trace
* @return a stream of generated traces {@link Trace}
*/
public static Stream<Trace> generateFromTransactionTrace(final ProtocolSchedule protocolSchedule, final TransactionTrace transactionTrace, final Block block, final AtomicInteger traceCounter, final Consumer<FlatTrace.Builder> consumer) {
final FlatTrace.Builder firstFlatTraceBuilder = FlatTrace.freshBuilder(transactionTrace);
final Transaction tx = transactionTrace.getTransaction();
final Optional<String> smartContractCode = tx.getInit().map(__ -> transactionTrace.getResult().getOutput().toString());
final Optional<String> smartContractAddress = smartContractCode.map(__ -> Address.contractAddress(tx.getSender(), tx.getNonce()).toHexString());
final Optional<Bytes> revertReason = transactionTrace.getResult().getRevertReason();
// set code field in result node
smartContractCode.ifPresent(firstFlatTraceBuilder.getResultBuilder()::code);
revertReason.ifPresent(r -> firstFlatTraceBuilder.revertReason(r.toHexString()));
// set init field if transaction is a smart contract deployment
tx.getInit().map(Bytes::toHexString).ifPresent(firstFlatTraceBuilder.getActionBuilder()::init);
// set to, input and callType fields if not a smart contract
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());
if (!transactionTrace.getTraceFrames().isEmpty() && hasRevertInSubCall(transactionTrace, transactionTrace.getTraceFrames().get(0))) {
firstFlatTraceBuilder.error(Optional.of("Reverted"));
}
} else {
firstFlatTraceBuilder.type("create").getResultBuilder().address(smartContractAddress.orElse(null));
}
if (!transactionTrace.getTraceFrames().isEmpty()) {
final OptionalLong precompiledGasCost = transactionTrace.getTraceFrames().get(0).getPrecompiledGasCost();
if (precompiledGasCost.isPresent()) {
firstFlatTraceBuilder.getResultBuilder().gasUsed("0x" + Long.toHexString(precompiledGasCost.getAsLong()));
}
}
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
FlatTrace.Context currentContext = new FlatTrace.Context(firstFlatTraceBuilder);
tracesContexts.addLast(currentContext);
flatTraces.add(currentContext.getBuilder());
// declare the first transactionTrace context as the previous transactionTrace context
long cumulativeGasCost = 0;
final Iterator<TraceFrame> iter = transactionTrace.getTraceFrames().iterator();
Optional<TraceFrame> nextTraceFrame = iter.hasNext() ? Optional.of(iter.next()) : Optional.empty();
while (nextTraceFrame.isPresent()) {
final TraceFrame traceFrame = nextTraceFrame.get();
nextTraceFrame = iter.hasNext() ? Optional.of(iter.next()) : Optional.empty();
cumulativeGasCost += traceFrame.getGasCost().orElse(0L) + traceFrame.getPrecompiledGasCost().orElse(0L);
final String opcodeString = traceFrame.getOpcode();
if ("CALL".equals(opcodeString) || "CALLCODE".equals(opcodeString) || "DELEGATECALL".equals(opcodeString) || "STATICCALL".equals(opcodeString)) {
currentContext = handleCall(transactionTrace, traceFrame, nextTraceFrame, flatTraces, cumulativeGasCost, tracesContexts, opcodeString.toLowerCase(Locale.US));
} else if ("CALLDATALOAD".equals(opcodeString)) {
currentContext = handleCallDataLoad(currentContext, traceFrame);
} else if ("RETURN".equals(opcodeString) || "STOP".equals(opcodeString)) {
currentContext = handleReturn(protocolSchedule, transactionTrace, block, traceFrame, tracesContexts, currentContext);
} else if ("SELFDESTRUCT".equals(opcodeString)) {
if (traceFrame.getExceptionalHaltReason().isPresent()) {
currentContext = handleCall(transactionTrace, traceFrame, nextTraceFrame, flatTraces, cumulativeGasCost, tracesContexts, opcodeString.toLowerCase(Locale.US));
} else {
currentContext = handleSelfDestruct(traceFrame, tracesContexts, currentContext, flatTraces);
}
} else if (("CREATE".equals(opcodeString) || "CREATE2".equals(opcodeString)) && (traceFrame.getExceptionalHaltReason().isEmpty() || traceFrame.getDepth() == 0)) {
currentContext = handleCreateOperation(traceFrame, nextTraceFrame, flatTraces, cumulativeGasCost, tracesContexts, smartContractAddress);
} else if ("REVERT".equals(opcodeString)) {
currentContext = handleRevert(tracesContexts, currentContext);
}
if (traceFrame.getExceptionalHaltReason().isPresent()) {
currentContext = handleHalt(flatTraces, tracesContexts, currentContext, traceFrame);
}
if (currentContext == null) {
break;
}
}
return flatTraces.stream().peek(consumer).map(FlatTrace.Builder::build);
}
Aggregations