1use crate::{
2 gas::{
3 self, selfdestruct_cold_beneficiary_cost, CALL_STIPEND,
4 COLD_ACCOUNT_ACCESS_COST_ADDITIONAL, COLD_SLOAD_COST_ADDITIONAL, ISTANBUL_SLOAD_GAS,
5 WARM_STORAGE_READ_COST,
6 },
7 instructions::utility::{IntoAddress, IntoU256},
8 interpreter_types::{InputsTr, InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
9 Host, InstructionResult,
10};
11use context_interface::host::LoadError;
12use core::cmp::min;
13use primitives::{hardfork::SpecId::*, Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256};
14
15use crate::InstructionContext;
16
17pub fn balance<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
21 popn_top!([], top, context.interpreter);
22 let address = top.into_address();
23 let spec_id = context.interpreter.runtime_flag.spec_id();
24 if spec_id.is_enabled_in(BERLIN) {
25 let account = berlin_load_account!(context, address, false);
26 *top = account.balance;
27 } else {
28 let gas = if spec_id.is_enabled_in(ISTANBUL) {
29 700
31 } else if spec_id.is_enabled_in(TANGERINE) {
32 400
33 } else {
34 20
35 };
36 gas!(context.interpreter, gas);
37 let Ok(account) = context
38 .host
39 .load_account_info_skip_cold_load(address, false, false)
40 else {
41 return context.interpreter.halt_fatal();
42 };
43 *top = account.balance;
44 };
45}
46
47pub fn selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(
49 context: InstructionContext<'_, H, WIRE>,
50) {
51 check!(context.interpreter, ISTANBUL);
52 let Some(balance) = context
55 .host
56 .balance(context.interpreter.input.target_address())
57 else {
58 return context.interpreter.halt_fatal();
59 };
60 push!(context.interpreter, balance.data);
61}
62
63pub fn extcodesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
67 context: InstructionContext<'_, H, WIRE>,
68) {
69 popn_top!([], top, context.interpreter);
70 let address = top.into_address();
71 let spec_id = context.interpreter.runtime_flag.spec_id();
72 if spec_id.is_enabled_in(BERLIN) {
73 let account = berlin_load_account!(context, address, true);
74 *top = U256::from(account.code.as_ref().unwrap().len());
76 } else {
77 let gas = if spec_id.is_enabled_in(TANGERINE) {
78 700
79 } else {
80 20
81 };
82 gas!(context.interpreter, gas);
83 let Ok(account) = context
84 .host
85 .load_account_info_skip_cold_load(address, true, false)
86 else {
87 return context.interpreter.halt_fatal();
88 };
89 *top = U256::from(account.code.as_ref().unwrap().len());
91 }
92}
93
94pub fn extcodehash<WIRE: InterpreterTypes, H: Host + ?Sized>(
96 context: InstructionContext<'_, H, WIRE>,
97) {
98 check!(context.interpreter, CONSTANTINOPLE);
99 popn_top!([], top, context.interpreter);
100 let address = top.into_address();
101
102 let spec_id = context.interpreter.runtime_flag.spec_id();
103 let account = if spec_id.is_enabled_in(BERLIN) {
104 berlin_load_account!(context, address, true)
105 } else {
106 let gas = if spec_id.is_enabled_in(ISTANBUL) {
107 700
108 } else {
109 400
110 };
111 gas!(context.interpreter, gas);
112 let Ok(account) = context
113 .host
114 .load_account_info_skip_cold_load(address, true, false)
115 else {
116 return context.interpreter.halt_fatal();
117 };
118 account
119 };
120 let code_hash = if account.is_empty() {
122 B256::ZERO
123 } else {
124 account.code_hash
125 };
126 *top = code_hash.into_u256();
127}
128
129pub fn extcodecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
133 context: InstructionContext<'_, H, WIRE>,
134) {
135 popn!(
136 [address, memory_offset, code_offset, len_u256],
137 context.interpreter
138 );
139 let address = address.into_address();
140
141 let spec_id = context.interpreter.runtime_flag.spec_id();
142
143 let len = as_usize_or_fail!(context.interpreter, len_u256);
144 gas!(
145 context.interpreter,
146 gas::copy_cost(0, len).unwrap_or(u64::MAX)
147 );
148
149 let mut memory_offset_usize = 0;
150 if len != 0 {
152 memory_offset_usize = as_usize_or_fail!(context.interpreter, memory_offset);
154 resize_memory!(context.interpreter, memory_offset_usize, len);
155 }
156
157 let code = if spec_id.is_enabled_in(BERLIN) {
158 let account = berlin_load_account!(context, address, true);
159 account.code.as_ref().unwrap().original_bytes()
160 } else {
161 let gas = if spec_id.is_enabled_in(TANGERINE) {
162 700
163 } else {
164 20
165 };
166 gas!(context.interpreter, gas);
167
168 let Some(code) = context.host.load_account_code(address) else {
169 return context.interpreter.halt_fatal();
170 };
171 code.data
172 };
173
174 let code_offset_usize = min(as_usize_saturated!(code_offset), code.len());
175
176 context
179 .interpreter
180 .memory
181 .set_data(memory_offset_usize, code_offset_usize, len, &code);
182}
183
184pub fn blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(
188 context: InstructionContext<'_, H, WIRE>,
189) {
190 popn_top!([], number, context.interpreter);
192
193 let requested_number = *number;
194 let block_number = context.host.block_number();
195
196 let Some(diff) = block_number.checked_sub(requested_number) else {
197 *number = U256::ZERO;
198 return;
199 };
200
201 let diff = as_u64_saturated!(diff);
202
203 if diff == 0 {
205 *number = U256::ZERO;
206 return;
207 }
208
209 *number = if diff <= BLOCK_HASH_HISTORY {
210 let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else {
211 return context.interpreter.halt_fatal();
212 };
213 U256::from_be_bytes(hash.0)
214 } else {
215 U256::ZERO
216 }
217}
218
219pub fn sload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
223 popn_top!([], index, context.interpreter);
224 let spec_id = context.interpreter.runtime_flag.spec_id();
225 let target = context.interpreter.input.target_address();
226
227 let gas = if spec_id.is_enabled_in(BERLIN) {
229 WARM_STORAGE_READ_COST
230 } else if spec_id.is_enabled_in(ISTANBUL) {
231 ISTANBUL_SLOAD_GAS
233 } else if spec_id.is_enabled_in(TANGERINE) {
234 200
236 } else {
237 50
238 };
239 gas!(context.interpreter, gas);
240 if spec_id.is_enabled_in(BERLIN) {
241 let skip_cold = context.interpreter.gas.remaining() < COLD_SLOAD_COST_ADDITIONAL;
242 let res = context.host.sload_skip_cold_load(target, *index, skip_cold);
243 match res {
244 Ok(storage) => {
245 if storage.is_cold {
246 gas!(context.interpreter, COLD_SLOAD_COST_ADDITIONAL);
247 }
248
249 *index = storage.data;
250 }
251 Err(LoadError::ColdLoadSkipped) => context.interpreter.halt_oog(),
252 Err(LoadError::DBError) => context.interpreter.halt_fatal(),
253 }
254 } else {
255 let Some(storage) = context.host.sload(target, *index) else {
256 return context.interpreter.halt_fatal();
257 };
258 *index = storage.data;
259 };
260}
261
262pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
266 require_non_staticcall!(context.interpreter);
267 popn!([index, value], context.interpreter);
268
269 let target = context.interpreter.input.target_address();
270 let spec_id = context.interpreter.runtime_flag.spec_id();
271
272 if context
274 .interpreter
275 .runtime_flag
276 .spec_id()
277 .is_enabled_in(ISTANBUL)
278 && context.interpreter.gas.remaining() <= CALL_STIPEND
279 {
280 context
281 .interpreter
282 .halt(InstructionResult::ReentrancySentryOOG);
283 return;
284 }
285
286 gas!(
288 context.interpreter,
289 gas::static_sstore_cost(context.interpreter.runtime_flag.spec_id())
290 );
291
292 let state_load = if spec_id.is_enabled_in(BERLIN) {
293 let skip_cold = context.interpreter.gas.remaining() < COLD_SLOAD_COST_ADDITIONAL;
294 let res = context
295 .host
296 .sstore_skip_cold_load(target, index, value, skip_cold);
297 match res {
298 Ok(load) => load,
299 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
300 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
301 }
302 } else {
303 let Some(load) = context.host.sstore(target, index, value) else {
304 return context.interpreter.halt_fatal();
305 };
306 load
307 };
308
309 gas!(
311 context.interpreter,
312 gas::dyn_sstore_cost(
313 context.interpreter.runtime_flag.spec_id(),
314 &state_load.data,
315 state_load.is_cold
316 )
317 );
318
319 context.interpreter.gas.record_refund(gas::sstore_refund(
321 context.interpreter.runtime_flag.spec_id(),
322 &state_load.data,
323 ));
324}
325
326pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
329 check!(context.interpreter, CANCUN);
330 require_non_staticcall!(context.interpreter);
331 popn!([index, value], context.interpreter);
334
335 context
336 .host
337 .tstore(context.interpreter.input.target_address(), index, value);
338}
339
340pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
343 check!(context.interpreter, CANCUN);
344 popn_top!([], index, context.interpreter);
347
348 *index = context
349 .host
350 .tload(context.interpreter.input.target_address(), *index);
351}
352
353pub fn log<const N: usize, H: Host + ?Sized>(
357 context: InstructionContext<'_, H, impl InterpreterTypes>,
358) {
359 require_non_staticcall!(context.interpreter);
360
361 popn!([offset, len], context.interpreter);
362 let len = as_usize_or_fail!(context.interpreter, len);
363 gas_or_fail!(context.interpreter, gas::log_cost(N as u8, len as u64));
364 let data = if len == 0 {
365 Bytes::new()
366 } else {
367 let offset = as_usize_or_fail!(context.interpreter, offset);
368 resize_memory!(context.interpreter, offset, len);
369 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
370 };
371 let Some(topics) = context.interpreter.stack.popn::<N>() else {
372 context.interpreter.halt_underflow();
373 return;
374 };
375
376 let log = Log {
377 address: context.interpreter.input.target_address(),
378 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
379 .expect("LogData should have <=4 topics"),
380 };
381
382 context.host.log(log);
383}
384
385pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
389 context: InstructionContext<'_, H, WIRE>,
390) {
391 require_non_staticcall!(context.interpreter);
392 popn!([target], context.interpreter);
393 let target = target.into_address();
394 let spec = context.interpreter.runtime_flag.spec_id();
395
396 gas!(context.interpreter, gas::static_selfdestruct_cost(spec));
398
399 let skip_cold = context.interpreter.gas.remaining() < selfdestruct_cold_beneficiary_cost(spec);
400 let res = match context.host.selfdestruct(
401 context.interpreter.input.target_address(),
402 target,
403 skip_cold,
404 ) {
405 Ok(res) => res,
406 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
407 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
408 };
409
410 gas!(context.interpreter, gas::dyn_selfdestruct_cost(spec, &res));
411
412 if !context
414 .interpreter
415 .runtime_flag
416 .spec_id()
417 .is_enabled_in(LONDON)
418 && !res.previously_destroyed
419 {
420 context
421 .interpreter
422 .gas
423 .record_refund(gas::SELFDESTRUCT_REFUND);
424 }
425
426 context.interpreter.halt(InstructionResult::SelfDestruct);
427}