@@ -5,7 +5,7 @@ use foundry_evm_core::{
5
5
constants:: { CHEATCODE_ADDRESS , HARDHAT_CONSOLE_ADDRESS } ,
6
6
} ;
7
7
use revm:: {
8
- bytecode:: opcode:: { EXTCODESIZE , REVERT } ,
8
+ bytecode:: opcode,
9
9
context:: { ContextTr , JournalTr } ,
10
10
inspector:: JournalExt ,
11
11
interpreter:: {
@@ -101,7 +101,7 @@ impl RevertDiagnostic {
101
101
}
102
102
103
103
/// Injects the revert diagnostic into the debug traces. Should only be called after a revert.
104
- fn handle_revert_diagnostic ( & self , interp : & mut Interpreter ) {
104
+ fn broadcast_diagnostic ( & self , interp : & mut Interpreter ) {
105
105
if let Some ( reason) = self . reason ( ) {
106
106
interp. control . instruction_result = InstructionResult :: Revert ;
107
107
interp. control . next_action = InterpreterAction :: Return {
@@ -113,6 +113,79 @@ impl RevertDiagnostic {
113
113
} ;
114
114
}
115
115
}
116
+
117
+ /// When a `REVERT` opcode with zero data size occurs:
118
+ /// - if `non_contract_call` was set at the current depth, `broadcast_diagnostic` is called.
119
+ /// Otherwise, it is cleared.
120
+ /// - if `non_contract_size_check` was set at the current depth, `broadcast_diagnostic` is
121
+ /// called. Otherwise, it is cleared.
122
+ #[ cold]
123
+ fn handle_revert < CTX , D > ( & mut self , interp : & mut Interpreter , ctx : & mut CTX )
124
+ where
125
+ D : Database < Error = DatabaseError > ,
126
+ CTX : ContextTr < Db = D > ,
127
+ CTX :: Journal : JournalExt ,
128
+ {
129
+ // REVERT (offset, size)
130
+ if let Ok ( size) = interp. stack . peek ( 1 ) {
131
+ if size. is_zero ( ) {
132
+ // Check empty revert with same depth as a non-contract call
133
+ if let Some ( ( _, _, depth) ) = self . non_contract_call {
134
+ if ctx. journal_ref ( ) . depth ( ) == depth {
135
+ self . broadcast_diagnostic ( interp) ;
136
+ } else {
137
+ self . non_contract_call = None ;
138
+ }
139
+ return ;
140
+ }
141
+
142
+ // Check empty revert with same depth as a non-contract size check
143
+ if let Some ( ( _, depth) ) = self . non_contract_size_check {
144
+ if depth == ctx. journal_ref ( ) . depth ( ) {
145
+ self . broadcast_diagnostic ( interp) ;
146
+ } else {
147
+ self . non_contract_size_check = None ;
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ /// When an `EXTCODESIZE` opcode occurs:
155
+ /// - Optimistically caches the target address and current depth in `non_contract_size_check`,
156
+ /// pending later validation.
157
+ #[ cold]
158
+ fn handle_extcodesize < CTX , D > ( & mut self , interp : & mut Interpreter , ctx : & mut CTX )
159
+ where
160
+ D : Database < Error = DatabaseError > ,
161
+ CTX : ContextTr < Db = D > ,
162
+ CTX :: Journal : JournalExt ,
163
+ {
164
+ // EXTCODESIZE (address)
165
+ if let Ok ( word) = interp. stack . peek ( 0 ) {
166
+ let addr = Address :: from_word ( word. into ( ) ) ;
167
+ if IGNORE . contains ( & addr) || ctx. journal_ref ( ) . precompile_addresses ( ) . contains ( & addr) {
168
+ return ;
169
+ }
170
+
171
+ // Optimistically cache --> validated and cleared (if necessary) at `fn
172
+ // step_end()`
173
+ self . non_contract_size_check = Some ( ( addr, ctx. journal_ref ( ) . depth ( ) ) ) ;
174
+ self . is_extcodesize_step = true ;
175
+ }
176
+ }
177
+
178
+ /// Tracks `EXTCODESIZE` output. If the bytecode size is NOT 0, clears the cache.
179
+ #[ cold]
180
+ fn handle_extcodesize_output ( & mut self , interp : & mut Interpreter ) {
181
+ if let Ok ( size) = interp. stack . peek ( 0 ) {
182
+ if size != U256 :: ZERO {
183
+ self . non_contract_size_check = None ;
184
+ }
185
+ }
186
+
187
+ self . is_extcodesize_step = false ;
188
+ }
116
189
}
117
190
118
191
impl < CTX , D > Inspector < CTX , EthInterpreter > for RevertDiagnostic
@@ -139,69 +212,17 @@ where
139
212
}
140
213
141
214
/// Handles `REVERT` and `EXTCODESIZE` opcodes for diagnostics.
142
- ///
143
- /// When a `REVERT` opcode with zero data size occurs:
144
- /// - if `non_contract_call` was set at the current depth, `handle_revert_diagnostic` is
145
- /// called. Otherwise, it is cleared.
146
- /// - if `non_contract_call` was set at the current depth, `handle_revert_diagnostic` is
147
- /// called. Otherwise, it is cleared.
148
- ///
149
- /// When an `EXTCODESIZE` opcode occurs:
150
- /// - Optimistically caches the target address and current depth in `non_contract_size_check`,
151
- /// pending later validation.
152
215
fn step ( & mut self , interp : & mut Interpreter , ctx : & mut CTX ) {
153
- // REVERT (offset, size)
154
- if REVERT == interp. bytecode . opcode ( ) {
155
- if let Ok ( size) = interp. stack . peek ( 1 ) {
156
- if size. is_zero ( ) {
157
- // Check empty revert with same depth as a non-contract call
158
- if let Some ( ( _, _, depth) ) = self . non_contract_call {
159
- if ctx. journal_ref ( ) . depth ( ) == depth {
160
- self . handle_revert_diagnostic ( interp) ;
161
- } else {
162
- self . non_contract_call = None ;
163
- }
164
- return ;
165
- }
166
-
167
- // Check empty revert with same depth as a non-contract size check
168
- if let Some ( ( _, depth) ) = self . non_contract_size_check {
169
- if depth == ctx. journal_ref ( ) . depth ( ) {
170
- self . handle_revert_diagnostic ( interp) ;
171
- } else {
172
- self . non_contract_size_check = None ;
173
- }
174
- }
175
- }
176
- }
177
- }
178
- // EXTCODESIZE (address)
179
- else if EXTCODESIZE == interp. bytecode . opcode ( ) {
180
- if let Ok ( word) = interp. stack . peek ( 0 ) {
181
- let addr = Address :: from_word ( word. into ( ) ) ;
182
- if IGNORE . contains ( & addr) ||
183
- ctx. journal_ref ( ) . precompile_addresses ( ) . contains ( & addr)
184
- {
185
- return ;
186
- }
187
-
188
- // Optimistically cache --> validated and cleared (if necessary) at `fn step_end()`
189
- self . non_contract_size_check = Some ( ( addr, ctx. journal_ref ( ) . depth ( ) ) ) ;
190
- self . is_extcodesize_step = true ;
191
- }
216
+ match interp. bytecode . opcode ( ) {
217
+ opcode:: REVERT => self . handle_revert ( interp, ctx) ,
218
+ opcode:: EXTCODESIZE => self . handle_extcodesize ( interp, ctx) ,
219
+ _ => { }
192
220
}
193
221
}
194
222
195
- /// Tracks `EXTCODESIZE` output. If the bytecode size is 0, clears the cache.
196
223
fn step_end ( & mut self , interp : & mut Interpreter , _ctx : & mut CTX ) {
197
224
if self . is_extcodesize_step {
198
- if let Ok ( size) = interp. stack . peek ( 0 ) {
199
- if size != U256 :: ZERO {
200
- self . non_contract_size_check = None ;
201
- }
202
- }
203
-
204
- self . is_extcodesize_step = false ;
225
+ self . handle_extcodesize_output ( interp) ;
205
226
}
206
227
}
207
228
}
0 commit comments