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