Skip to content

Commit 682bd06

Browse files
committed
Finish porting ErrorInferrer and SolidityTracer
1 parent dc1c4b1 commit 682bd06

File tree

3 files changed

+1
-348
lines changed

3 files changed

+1
-348
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,78 +0,0 @@
1-
/* eslint "@typescript-eslint/no-non-null-assertion": "error" */
2-
3-
import { ErrorInferrer as ErrorInferrerRs } from "@nomicfoundation/edr";
4-
5-
import {
6-
DecodedCallMessageTrace,
7-
DecodedCreateMessageTrace,
8-
DecodedEvmMessageTrace,
9-
MessageTrace,
10-
} from "./message-trace";
11-
import { Instruction } from "./model";
12-
import { SolidityStackTrace } from "./solidity-stack-trace";
13-
14-
export { instructionToCallstackStackTraceEntry } from "@nomicfoundation/edr";
15-
16-
export interface SubmessageData {
17-
messageTrace: MessageTrace;
18-
stacktrace: SolidityStackTrace;
19-
stepIndex: number;
20-
}
21-
22-
/* eslint-disable @nomicfoundation/hardhat-internal-rules/only-hardhat-error */
23-
24-
export class ErrorInferrer {
25-
public inferBeforeTracingCallMessage(
26-
trace: DecodedCallMessageTrace
27-
): SolidityStackTrace | undefined {
28-
return ErrorInferrerRs.inferBeforeTracingCallMessage(trace);
29-
}
30-
31-
public inferBeforeTracingCreateMessage(
32-
trace: DecodedCreateMessageTrace
33-
): SolidityStackTrace | undefined {
34-
return ErrorInferrerRs.inferBeforeTracingCreateMessage(trace);
35-
}
36-
37-
public inferAfterTracing(
38-
trace: DecodedEvmMessageTrace,
39-
stacktrace: SolidityStackTrace,
40-
functionJumpdests: Instruction[],
41-
jumpedIntoFunction: boolean,
42-
lastSubmessageData: SubmessageData | undefined
43-
): SolidityStackTrace {
44-
const res = ErrorInferrerRs.inferAfterTracing(
45-
trace,
46-
stacktrace,
47-
functionJumpdests,
48-
jumpedIntoFunction,
49-
lastSubmessageData
50-
);
51-
52-
return (
53-
ErrorInferrerRs.checkLastSubmessage(
54-
trace,
55-
stacktrace,
56-
lastSubmessageData
57-
) ??
58-
// res ??
59-
ErrorInferrerRs.checkFailedLastCall(trace, stacktrace) ??
60-
ErrorInferrerRs.checkLastInstruction(
61-
trace,
62-
stacktrace,
63-
functionJumpdests,
64-
jumpedIntoFunction
65-
) ??
66-
ErrorInferrerRs.checkNonContractCalled(trace, stacktrace) ??
67-
ErrorInferrerRs.checkSolidity063UnmappedRevert(trace, stacktrace) ??
68-
ErrorInferrerRs.checkContractTooLarge(trace) ??
69-
ErrorInferrerRs.otherExecutionErrorStacktrace(trace, stacktrace)
70-
);
71-
}
72-
73-
public filterRedundantFrames(
74-
stacktrace: SolidityStackTrace
75-
): SolidityStackTrace {
76-
return ErrorInferrerRs.filterRedundantFrames(stacktrace);
77-
}
78-
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +0,0 @@
1-
/**
2-
* This file includes Solidity tracing heuristics for solc starting with version
3-
* 0.6.9.
4-
*
5-
* This solc version introduced a significant change to how sourcemaps are
6-
* handled for inline yul/internal functions. These were mapped to the
7-
* unmapped/-1 file before, which lead to many unmapped reverts. Now, they are
8-
* mapped to the part of the Solidity source that lead to their inlining.
9-
*
10-
* This change is a very positive change, as errors would point to the correct
11-
* line by default. The only problem is that we used to rely very heavily on
12-
* unmapped reverts to decide when our error detection heuristics were to be
13-
* run. In fact, this heuristics were first introduced because of unmapped
14-
* reverts.
15-
*
16-
* Instead of synthetically completing stack traces when unmapped reverts occur,
17-
* we now start from complete stack traces and adjust them if we can provide
18-
* more meaningful errors.
19-
*/
20-
21-
export {
22-
stackTraceMayRequireAdjustments,
23-
adjustStackTrace,
24-
} from "@nomicfoundation/edr";
Original file line numberDiff line numberDiff line change
@@ -1,246 +1 @@
1-
import { equalsBytes } from "@nomicfoundation/ethereumjs-util";
2-
3-
import { SolidityTracer as SolidityTracerRs } from "@nomicfoundation/edr";
4-
import { ExitCode } from "../provider/vm/exit";
5-
6-
import {
7-
ErrorInferrer,
8-
instructionToCallstackStackTraceEntry,
9-
SubmessageData,
10-
} from "./error-inferrer";
11-
import {
12-
adjustStackTrace,
13-
stackTraceMayRequireAdjustments,
14-
} from "./mapped-inlined-internal-functions-heuristics";
15-
import {
16-
DecodedCallMessageTrace,
17-
DecodedCreateMessageTrace,
18-
DecodedEvmMessageTrace,
19-
EvmMessageTrace,
20-
EvmStep,
21-
isCreateTrace,
22-
isDecodedCallTrace,
23-
isDecodedCreateTrace,
24-
isEvmStep,
25-
isPrecompileTrace,
26-
MessageTrace,
27-
PrecompileMessageTrace,
28-
} from "./message-trace";
29-
import { Instruction, JumpType } from "./model";
30-
import { Opcode } from "./opcodes";
31-
import {
32-
SolidityStackTrace,
33-
SolidityStackTraceEntry,
34-
StackTraceEntryType,
35-
} from "./solidity-stack-trace";
36-
37-
export class SolidityTracer {
38-
private _solidityTracerRs = new SolidityTracerRs();
39-
private _errorInferrer = new ErrorInferrer();
40-
41-
public getStackTrace(
42-
maybeDecodedMessageTrace: MessageTrace
43-
): SolidityStackTrace {
44-
if (!maybeDecodedMessageTrace.exit.isError()) {
45-
return [];
46-
}
47-
48-
if (isPrecompileTrace(maybeDecodedMessageTrace)) {
49-
return this._getPrecompileMessageStackTrace(maybeDecodedMessageTrace);
50-
}
51-
52-
if (isDecodedCreateTrace(maybeDecodedMessageTrace)) {
53-
return this._getCreateMessageStackTrace(maybeDecodedMessageTrace);
54-
}
55-
56-
if (isDecodedCallTrace(maybeDecodedMessageTrace)) {
57-
return this._getCallMessageStackTrace(maybeDecodedMessageTrace);
58-
}
59-
60-
return this._getUnrecognizedMessageStackTrace(maybeDecodedMessageTrace);
61-
}
62-
63-
private _getCallMessageStackTrace(
64-
trace: DecodedCallMessageTrace
65-
): SolidityStackTrace {
66-
const inferredError =
67-
this._errorInferrer.inferBeforeTracingCallMessage(trace);
68-
69-
if (inferredError !== undefined) {
70-
return inferredError;
71-
}
72-
73-
return this._traceEvmExecution(trace);
74-
}
75-
76-
private _getUnrecognizedMessageStackTrace(
77-
trace: EvmMessageTrace
78-
): SolidityStackTrace {
79-
const subtrace = this._getLastSubtrace(trace);
80-
81-
if (subtrace !== undefined) {
82-
// This is not a very exact heuristic, but most of the time it will be right, as solidity
83-
// reverts if a call fails, and most contracts are in solidity
84-
if (
85-
subtrace.exit.isError() &&
86-
equalsBytes(trace.returnData, subtrace.returnData)
87-
) {
88-
let unrecognizedEntry: SolidityStackTraceEntry;
89-
90-
if (isCreateTrace(trace)) {
91-
unrecognizedEntry = {
92-
type: StackTraceEntryType.UNRECOGNIZED_CREATE_CALLSTACK_ENTRY,
93-
};
94-
} else {
95-
unrecognizedEntry = {
96-
type: StackTraceEntryType.UNRECOGNIZED_CONTRACT_CALLSTACK_ENTRY,
97-
address: trace.address,
98-
};
99-
}
100-
101-
return [unrecognizedEntry, ...this.getStackTrace(subtrace)];
102-
}
103-
}
104-
105-
if (trace.exit.kind === ExitCode.CODESIZE_EXCEEDS_MAXIMUM) {
106-
return [
107-
{
108-
type: StackTraceEntryType.CONTRACT_TOO_LARGE_ERROR,
109-
},
110-
];
111-
}
112-
113-
const isInvalidOpcodeError = trace.exit.kind === ExitCode.INVALID_OPCODE;
114-
115-
if (isCreateTrace(trace)) {
116-
return [
117-
{
118-
type: StackTraceEntryType.UNRECOGNIZED_CREATE_ERROR,
119-
returnData: trace.returnData,
120-
isInvalidOpcodeError,
121-
},
122-
];
123-
}
124-
125-
return [
126-
{
127-
type: StackTraceEntryType.UNRECOGNIZED_CONTRACT_ERROR,
128-
address: trace.address,
129-
returnData: trace.returnData,
130-
isInvalidOpcodeError,
131-
},
132-
];
133-
}
134-
135-
private _getCreateMessageStackTrace(
136-
trace: DecodedCreateMessageTrace
137-
): SolidityStackTrace {
138-
const inferredError =
139-
this._errorInferrer.inferBeforeTracingCreateMessage(trace);
140-
141-
if (inferredError !== undefined) {
142-
return inferredError;
143-
}
144-
145-
return this._traceEvmExecution(trace);
146-
}
147-
148-
private _getPrecompileMessageStackTrace(
149-
trace: PrecompileMessageTrace
150-
): SolidityStackTrace {
151-
return [
152-
{
153-
type: StackTraceEntryType.PRECOMPILE_ERROR,
154-
precompile: trace.precompile,
155-
},
156-
];
157-
}
158-
159-
private _traceEvmExecution(
160-
trace: DecodedEvmMessageTrace
161-
): SolidityStackTrace {
162-
const stackTrace = this._rawTraceEvmExecution(trace);
163-
164-
if (stackTraceMayRequireAdjustments(stackTrace, trace)) {
165-
return adjustStackTrace(stackTrace, trace);
166-
}
167-
168-
return stackTrace;
169-
}
170-
171-
private _rawTraceEvmExecution(
172-
trace: DecodedEvmMessageTrace
173-
): SolidityStackTrace {
174-
const stacktrace: SolidityStackTrace = [];
175-
176-
let subtracesSeen = 0;
177-
178-
// There was a jump into a function according to the sourcemaps
179-
let jumpedIntoFunction = false;
180-
181-
const functionJumpdests: Instruction[] = [];
182-
183-
let lastSubmessageData: SubmessageData | undefined;
184-
185-
for (let stepIndex = 0; stepIndex < trace.steps.length; stepIndex++) {
186-
const step = trace.steps[stepIndex];
187-
const nextStep = trace.steps[stepIndex + 1];
188-
189-
if (isEvmStep(step)) {
190-
const inst = trace.bytecode.getInstruction(step.pc);
191-
192-
if (
193-
inst.jumpType === JumpType.INTO_FUNCTION &&
194-
nextStep !== undefined
195-
) {
196-
const nextEvmStep = nextStep as EvmStep; // A jump can't be followed by a subtrace
197-
const nextInst = trace.bytecode.getInstruction(nextEvmStep.pc);
198-
199-
if (nextInst !== undefined && nextInst.opcode === Opcode.JUMPDEST) {
200-
stacktrace.push(
201-
instructionToCallstackStackTraceEntry(trace.bytecode, inst)
202-
);
203-
if (nextInst.location !== undefined) {
204-
jumpedIntoFunction = true;
205-
}
206-
functionJumpdests.push(nextInst);
207-
}
208-
} else if (inst.jumpType === JumpType.OUTOF_FUNCTION) {
209-
stacktrace.pop();
210-
functionJumpdests.pop();
211-
}
212-
} else {
213-
subtracesSeen += 1;
214-
215-
// If there are more subtraces, this one didn't terminate the execution
216-
if (subtracesSeen < trace.numberOfSubtraces) {
217-
continue;
218-
}
219-
220-
const submessageTrace = this.getStackTrace(step);
221-
222-
lastSubmessageData = {
223-
messageTrace: step,
224-
stepIndex,
225-
stacktrace: submessageTrace,
226-
};
227-
}
228-
}
229-
230-
const stacktraceWithInferredError = this._errorInferrer.inferAfterTracing(
231-
trace,
232-
stacktrace,
233-
functionJumpdests,
234-
jumpedIntoFunction,
235-
lastSubmessageData
236-
);
237-
238-
return this._errorInferrer.filterRedundantFrames(
239-
stacktraceWithInferredError
240-
);
241-
}
242-
243-
private _getLastSubtrace(trace: EvmMessageTrace): MessageTrace | undefined {
244-
return this._solidityTracerRs.getLastSubtrace(trace);
245-
}
246-
}
1+
export { SolidityTracer } from "@nomicfoundation/edr";

0 commit comments

Comments
 (0)