hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
154 lines • 6.22 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VMTracer = void 0;
const ethereumjs_util_1 = require("@nomicfoundation/ethereumjs-util");
const errors_1 = require("../../core/errors");
const exit_1 = require("../provider/vm/exit");
const message_trace_1 = require("./message-trace");
/* eslint-disable @nomicfoundation/hardhat-internal-rules/only-hardhat-error */
const DUMMY_RETURN_DATA = Buffer.from([]);
const DUMMY_GAS_USED = 0n;
/**
* Consumes the incoming VM trace events, until an error occurs, to keep track
* of the last top level message trace/error.
*/
class VMTracer {
constructor() {
this.tracingSteps = [];
this._messageTraces = [];
// TODO: temporarily hardcoded to remove the need of using ethereumjs' common and evm here
this._maxPrecompileNumber = 10;
}
getLastTopLevelMessageTrace() {
return this._messageTraces[0];
}
getLastError() {
return this._lastError;
}
_shouldKeepTracing() {
return this._lastError === undefined;
}
addBeforeMessage(message) {
if (!this._shouldKeepTracing()) {
return;
}
try {
let trace;
if (message.depth === 0) {
this._messageTraces = [];
this.tracingSteps = [];
}
if (message.to === undefined) {
const createTrace = {
code: message.data,
steps: [],
value: message.value,
exit: new exit_1.Exit(exit_1.ExitCode.SUCCESS),
returnData: DUMMY_RETURN_DATA,
numberOfSubtraces: 0,
depth: message.depth,
deployedContract: undefined,
gasUsed: DUMMY_GAS_USED,
};
trace = createTrace;
}
else {
const toAsBigInt = (0, ethereumjs_util_1.bytesToBigInt)(message.to);
if (toAsBigInt > 0 && toAsBigInt <= this._maxPrecompileNumber) {
const precompileTrace = {
precompile: Number(toAsBigInt),
calldata: message.data,
value: message.value,
exit: new exit_1.Exit(exit_1.ExitCode.SUCCESS),
returnData: DUMMY_RETURN_DATA,
depth: message.depth,
gasUsed: DUMMY_GAS_USED,
};
trace = precompileTrace;
}
else {
const codeAddress = message.codeAddress;
// if we enter here, then `to` is not undefined, therefore
// `codeAddress` and `code` should be defined
(0, errors_1.assertHardhatInvariant)(codeAddress !== undefined, "codeAddress should be defined");
(0, errors_1.assertHardhatInvariant)(message.code !== undefined, "code should be defined");
const callTrace = {
code: message.code,
calldata: message.data,
steps: [],
value: message.value,
exit: new exit_1.Exit(exit_1.ExitCode.SUCCESS),
returnData: DUMMY_RETURN_DATA,
address: message.to,
numberOfSubtraces: 0,
depth: message.depth,
gasUsed: DUMMY_GAS_USED,
codeAddress,
};
trace = callTrace;
}
}
if (this._messageTraces.length > 0) {
const parentTrace = this._messageTraces[this._messageTraces.length - 1];
if ((0, message_trace_1.isPrecompileTrace)(parentTrace)) {
throw new Error("This should not happen: message execution started while a precompile was executing");
}
parentTrace.steps.push(trace);
parentTrace.numberOfSubtraces += 1;
}
this._messageTraces.push(trace);
}
catch (error) {
this._lastError = error;
}
}
addStep(step) {
if (!this._shouldKeepTracing()) {
return;
}
this.tracingSteps.push(step);
try {
const trace = this._messageTraces[this._messageTraces.length - 1];
if ((0, message_trace_1.isPrecompileTrace)(trace)) {
throw new Error("This should not happen: step event fired while a precompile was executing");
}
trace.steps.push({ pc: Number(step.pc) });
}
catch (error) {
this._lastError = error;
}
}
addAfterMessage(result) {
if (!this._shouldKeepTracing()) {
return;
}
try {
const trace = this._messageTraces[this._messageTraces.length - 1];
trace.gasUsed = result.result.gasUsed;
const executionResult = result.result;
if ((0, message_trace_1.isSuccessResult)(executionResult)) {
trace.exit = exit_1.Exit.fromEdrSuccessReason(executionResult.reason);
trace.returnData = executionResult.output.returnValue;
if ((0, message_trace_1.isCreateTrace)(trace)) {
trace.deployedContract = executionResult.output.address;
}
}
else if ((0, message_trace_1.isHaltResult)(executionResult)) {
trace.exit = exit_1.Exit.fromEdrExceptionalHalt(executionResult.reason);
trace.returnData = Buffer.from([]);
}
else {
trace.exit = new exit_1.Exit(exit_1.ExitCode.REVERT);
trace.returnData = executionResult.output;
}
if (this._messageTraces.length > 1) {
this._messageTraces.pop();
}
}
catch (error) {
this._lastError = error;
}
}
}
exports.VMTracer = VMTracer;
//# sourceMappingURL=vm-tracer.js.map