-
Notifications
You must be signed in to change notification settings - Fork 127
Add debug tracers needed for modularized workflow #11477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
7f6b200
a1a8f1c
ddd1086
c03a405
fbc394d
d82f723
8f25f5f
5dd3038
4f89b5b
5760ce5
50e2a5e
de03967
7ad4ec8
4a9f6f8
40d3b4c
78fd0a9
89fc1ec
e7ee181
add64cd
f07e1c5
8cb1803
4742d1b
89e3b14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package org.hiero.mirror.web3.evm.contracts.execution.traceability; | ||
|
||
import com.hedera.hapi.streams.ContractActionType; | ||
import com.hedera.hapi.streams.ContractActions; | ||
import com.hedera.node.app.service.contract.impl.exec.ActionSidecarContentTracer; | ||
import com.hedera.services.utils.EntityIdUtils; | ||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
import jakarta.inject.Named; | ||
import java.util.Optional; | ||
import lombok.CustomLog; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.hiero.mirror.common.domain.entity.Entity; | ||
import org.hiero.mirror.web3.evm.properties.TraceProperties; | ||
import org.hiero.mirror.web3.state.CommonEntityAccessor; | ||
import org.hyperledger.besu.evm.frame.MessageFrame; | ||
import org.hyperledger.besu.evm.operation.Operation; | ||
|
||
@Named | ||
@CustomLog | ||
public class MirrorOperationActionTracer implements ActionSidecarContentTracer { | ||
|
||
private final TraceProperties traceProperties; | ||
private final CommonEntityAccessor commonEntityAccessor; | ||
|
||
public MirrorOperationActionTracer( | ||
@NonNull final TraceProperties traceProperties, @NonNull final CommonEntityAccessor commonEntityAccessor) { | ||
this.traceProperties = traceProperties; | ||
this.commonEntityAccessor = commonEntityAccessor; | ||
} | ||
steven-sheehy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Override | ||
public void tracePostExecution( | ||
Check notice on line 34 in web3/src/main/java/org/hiero/mirror/web3/evm/contracts/execution/traceability/MirrorOperationActionTracer.java
|
||
@NonNull final MessageFrame frame, @NonNull final Operation.OperationResult operationResult) { | ||
if (!traceProperties.isEnabled()) { | ||
return; | ||
} | ||
|
||
if (traceProperties.stateFilterCheck(frame.getState())) { | ||
return; | ||
} | ||
|
||
final var recipientAddress = frame.getRecipientAddress(); | ||
final var recipientNum = recipientAddress != null | ||
? commonEntityAccessor.get( | ||
com.hedera.pbj.runtime.io.buffer.Bytes.wrap(recipientAddress.toArray()), Optional.empty()) | ||
: Optional.empty(); | ||
|
||
if (recipientNum.isPresent() | ||
&& traceProperties.contractFilterCheck( | ||
EntityIdUtils.asHexedEvmAddress(((Entity) recipientNum.get()).getId()))) { | ||
return; | ||
} | ||
|
||
log.info( | ||
"type={} operation={}, callDepth={}, contract={}, sender={}, recipient={}, remainingGas={}, revertReason={}, input={}, output={}, return={}", | ||
Check notice on line 57 in web3/src/main/java/org/hiero/mirror/web3/evm/contracts/execution/traceability/MirrorOperationActionTracer.java
|
||
frame.getType(), | ||
frame.getCurrentOperation() != null | ||
? frame.getCurrentOperation().getName() | ||
: StringUtils.EMPTY, | ||
frame.getDepth(), | ||
frame.getContractAddress().toShortHexString(), | ||
frame.getSenderAddress().toShortHexString(), | ||
frame.getRecipientAddress().toShortHexString(), | ||
frame.getRemainingGas(), | ||
frame.getRevertReason() | ||
.orElse(org.apache.tuweni.bytes.Bytes.EMPTY) | ||
.toHexString(), | ||
frame.getInputData().toShortHexString(), | ||
frame.getOutputData().toShortHexString(), | ||
frame.getReturnData().toShortHexString()); | ||
} | ||
|
||
@Override | ||
public void traceOriginAction(@NonNull MessageFrame frame) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public void sanitizeTracedActions(@NonNull MessageFrame frame) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public void tracePrecompileResult(@NonNull MessageFrame frame, @NonNull ContractActionType type) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public ContractActions contractActions() { | ||
return null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package org.hiero.mirror.web3.evm.contracts.execution.traceability; | ||
|
||
import static org.hiero.mirror.web3.evm.contracts.execution.traceability.TracerUtils.captureMemory; | ||
import static org.hiero.mirror.web3.evm.contracts.execution.traceability.TracerUtils.captureStack; | ||
import static org.hiero.mirror.web3.evm.contracts.execution.traceability.TracerUtils.getRevertReasonFromContractActions; | ||
import static org.hiero.mirror.web3.evm.contracts.execution.traceability.TracerUtils.isCallToHederaPrecompile; | ||
|
||
import com.hedera.hapi.streams.ContractActionType; | ||
import com.hedera.hapi.streams.ContractActions; | ||
import com.hedera.node.app.service.contract.impl.exec.ActionSidecarContentTracer; | ||
import com.hedera.node.app.service.contract.impl.state.ProxyWorldUpdater; | ||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
import edu.umd.cs.findbugs.annotations.Nullable; | ||
import jakarta.inject.Named; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.TreeMap; | ||
import java.util.stream.Collectors; | ||
import lombok.CustomLog; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.tuweni.bytes.Bytes; | ||
import org.hiero.mirror.web3.common.ContractCallContext; | ||
import org.hiero.mirror.web3.evm.config.PrecompiledContractProvider; | ||
import org.hyperledger.besu.datatypes.Address; | ||
import org.hyperledger.besu.evm.ModificationNotAllowedException; | ||
import org.hyperledger.besu.evm.frame.MessageFrame; | ||
import org.hyperledger.besu.evm.operation.Operation.OperationResult; | ||
import org.hyperledger.besu.evm.precompile.PrecompiledContract; | ||
|
||
@Named | ||
@CustomLog | ||
public class OpcodeActionTracer implements ActionSidecarContentTracer { | ||
|
||
private final Map<Address, PrecompiledContract> hederaPrecompiles; | ||
|
||
public OpcodeActionTracer(@NonNull final PrecompiledContractProvider precompiledContractProvider) { | ||
steven-sheehy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.hederaPrecompiles = precompiledContractProvider.getHederaPrecompiles().entrySet().stream() | ||
.collect(Collectors.toMap(e -> Address.fromHexString(e.getKey()), Map.Entry::getValue)); | ||
} | ||
|
||
@Override | ||
public void tracePostExecution(@NonNull final MessageFrame frame, @NonNull final OperationResult operationResult) { | ||
final var context = ContractCallContext.get(); | ||
|
||
final var options = context.getOpcodeTracerOptions(); | ||
final var memory = captureMemory(frame, options); | ||
final var stack = captureStack(frame, options); | ||
final var storage = captureStorage(frame, options); | ||
final var opcode = Opcode.builder() | ||
.pc(frame.getPC()) | ||
.op(frame.getCurrentOperation().getName()) | ||
.gas(frame.getRemainingGas()) | ||
.gasCost(operationResult.getGasCost()) | ||
.depth(frame.getDepth()) | ||
.stack(stack) | ||
.memory(memory) | ||
.storage(storage) | ||
.reason(frame.getRevertReason().map(Bytes::toString).orElse(null)) | ||
.build(); | ||
|
||
context.addOpcodes(opcode); | ||
} | ||
|
||
@Override | ||
public void tracePrecompileCall( | ||
@NonNull final MessageFrame frame, final long gasRequirement, @Nullable final Bytes output) { | ||
final var context = ContractCallContext.get(); | ||
final var revertReason = isCallToHederaPrecompile(frame, hederaPrecompiles) | ||
? getRevertReasonFromContractActions(context) | ||
: frame.getRevertReason(); | ||
|
||
final var opcode = Opcode.builder() | ||
.pc(frame.getPC()) | ||
.op( | ||
frame.getCurrentOperation() != null | ||
? frame.getCurrentOperation().getName() | ||
: StringUtils.EMPTY) | ||
.gas(frame.getRemainingGas()) | ||
.gasCost(output != null && !output.isEmpty() ? gasRequirement : 0L) | ||
.depth(frame.getDepth()) | ||
.stack(Collections.emptyList()) | ||
.memory(Collections.emptyList()) | ||
.storage(Collections.emptyMap()) | ||
.reason(revertReason.map(Bytes::toHexString).orElse(null)) | ||
.build(); | ||
context.addOpcodes(opcode); | ||
} | ||
|
||
private Map<Bytes, Bytes> captureStorage(final MessageFrame frame, final OpcodeTracerOptions options) { | ||
if (!options.isStorage()) { | ||
return Collections.emptyMap(); | ||
} | ||
|
||
try { | ||
final var updates = ((ProxyWorldUpdater) frame.getWorldUpdater()).pendingStorageUpdates(); | ||
return updates.stream() | ||
.flatMap(storageAccesses -> | ||
storageAccesses.accesses().stream()) // Properly flatten the nested structure | ||
.collect(Collectors.toMap( | ||
e -> Bytes.wrap(e.key().toArray()), | ||
e -> Bytes.wrap(e.value().toArray()), | ||
(v1, v2) -> v1, // in case of duplicates, keep the first value | ||
TreeMap::new)); | ||
|
||
} catch (final ModificationNotAllowedException e) { | ||
log.warn("Failed to retrieve storage contents", e); | ||
return Collections.emptyMap(); | ||
} | ||
} | ||
|
||
@Override | ||
public void traceOriginAction(@NonNull MessageFrame frame) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public void sanitizeTracedActions(@NonNull MessageFrame frame) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public void tracePrecompileResult(@NonNull MessageFrame frame, @NonNull ContractActionType type) { | ||
// NO-OP | ||
} | ||
|
||
@Override | ||
public ContractActions contractActions() { | ||
return null; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.