|
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