revm_interpreter/instructions/
host.rs1use crate::{
2 instructions::utility::{IntoAddress, IntoU256},
3 interpreter_types::{InputsTr, InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
4 Host, InstructionResult,
5};
6use context_interface::host::LoadError;
7use core::cmp::min;
8use primitives::{
9 hardfork::SpecId::{self, *},
10 Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256,
11};
12
13use crate::InstructionContext;
14
15pub fn balance<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
19 popn_top!([], top, context.interpreter);
20 let address = top.into_address();
21 let spec_id = context.interpreter.runtime_flag.spec_id();
22 if spec_id.is_enabled_in(BERLIN) {
23 let account = berlin_load_account!(context, address, false);
24 *top = account.balance;
25 } else {
26 let Ok(account) = context
27 .host
28 .load_account_info_skip_cold_load(address, false, false)
29 else {
30 return context.interpreter.halt_fatal();
31 };
32 *top = account.balance;
33 };
34}
35
36pub fn selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(
38 context: InstructionContext<'_, H, WIRE>,
39) {
40 check!(context.interpreter, ISTANBUL);
41
42 let Some(balance) = context
43 .host
44 .balance(context.interpreter.input.target_address())
45 else {
46 return context.interpreter.halt_fatal();
47 };
48 push!(context.interpreter, balance.data);
49}
50
51pub fn extcodesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
55 context: InstructionContext<'_, H, WIRE>,
56) {
57 popn_top!([], top, context.interpreter);
58 let address = top.into_address();
59
60 let spec_id = context.interpreter.runtime_flag.spec_id();
61 if spec_id.is_enabled_in(BERLIN) {
62 let account = berlin_load_account!(context, address, true);
63 *top = U256::from(account.code.as_ref().unwrap().len());
65 } else {
66 let Ok(account) = context
67 .host
68 .load_account_info_skip_cold_load(address, true, false)
69 else {
70 return context.interpreter.halt_fatal();
71 };
72 *top = U256::from(account.code.as_ref().unwrap().len());
74 }
75}
76
77pub fn extcodehash<WIRE: InterpreterTypes, H: Host + ?Sized>(
79 context: InstructionContext<'_, H, WIRE>,
80) {
81 check!(context.interpreter, CONSTANTINOPLE);
82 popn_top!([], top, context.interpreter);
83 let address = top.into_address();
84
85 let spec_id = context.interpreter.runtime_flag.spec_id();
86 let account = if spec_id.is_enabled_in(BERLIN) {
87 berlin_load_account!(context, address, false)
88 } else {
89 let Ok(account) = context
90 .host
91 .load_account_info_skip_cold_load(address, false, false)
92 else {
93 return context.interpreter.halt_fatal();
94 };
95 account
96 };
97 let code_hash = if account.is_empty() {
99 B256::ZERO
100 } else {
101 account.code_hash
102 };
103 *top = code_hash.into_u256();
104}
105
106pub fn extcodecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
110 context: InstructionContext<'_, H, WIRE>,
111) {
112 popn!(
113 [address, memory_offset, code_offset, len_u256],
114 context.interpreter
115 );
116 let address = address.into_address();
117
118 let spec_id = context.interpreter.runtime_flag.spec_id();
119
120 let len = as_usize_or_fail!(context.interpreter, len_u256);
121 gas!(
122 context.interpreter,
123 context.host.gas_params().extcodecopy(len)
124 );
125
126 let mut memory_offset_usize = 0;
127 if len != 0 {
129 memory_offset_usize = as_usize_or_fail!(context.interpreter, memory_offset);
131 resize_memory!(
133 context.interpreter,
134 context.host.gas_params(),
135 memory_offset_usize,
136 len
137 );
138 }
139
140 let code = if spec_id.is_enabled_in(BERLIN) {
141 let account = berlin_load_account!(context, address, true);
142 account.code.as_ref().unwrap().original_bytes()
143 } else {
144 let Some(code) = context.host.load_account_code(address) else {
145 return context.interpreter.halt_fatal();
146 };
147 code.data
148 };
149
150 let code_offset_usize = min(as_usize_saturated!(code_offset), code.len());
151
152 context
155 .interpreter
156 .memory
157 .set_data(memory_offset_usize, code_offset_usize, len, &code);
158}
159
160pub fn blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(
164 context: InstructionContext<'_, H, WIRE>,
165) {
166 popn_top!([], number, context.interpreter);
167
168 let requested_number = *number;
169 let block_number = context.host.block_number();
170
171 let Some(diff) = block_number.checked_sub(requested_number) else {
172 *number = U256::ZERO;
173 return;
174 };
175
176 let diff = as_u64_saturated!(diff);
177
178 if diff == 0 {
180 *number = U256::ZERO;
181 return;
182 }
183
184 *number = if diff <= BLOCK_HASH_HISTORY {
185 let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else {
186 return context.interpreter.halt_fatal();
187 };
188 U256::from_be_bytes(hash.0)
189 } else {
190 U256::ZERO
191 }
192}
193
194pub fn sload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
198 popn_top!([], index, context.interpreter);
199 let spec_id = context.interpreter.runtime_flag.spec_id();
200 let target = context.interpreter.input.target_address();
201
202 if spec_id.is_enabled_in(BERLIN) {
203 let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost();
204 let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost;
205 let res = context.host.sload_skip_cold_load(target, *index, skip_cold);
206 match res {
207 Ok(storage) => {
208 if storage.is_cold {
209 gas!(context.interpreter, additional_cold_cost);
210 }
211
212 *index = storage.data;
213 }
214 Err(LoadError::ColdLoadSkipped) => context.interpreter.halt_oog(),
215 Err(LoadError::DBError) => context.interpreter.halt_fatal(),
216 }
217 } else {
218 let Some(storage) = context.host.sload(target, *index) else {
219 return context.interpreter.halt_fatal();
220 };
221 *index = storage.data;
222 };
223}
224
225pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
229 require_non_staticcall!(context.interpreter);
230 popn!([index, value], context.interpreter);
231
232 let target = context.interpreter.input.target_address();
233 let spec_id = context.interpreter.runtime_flag.spec_id();
234
235 if spec_id.is_enabled_in(ISTANBUL)
238 && context.interpreter.gas.remaining() <= context.host.gas_params().call_stipend()
239 {
240 context
241 .interpreter
242 .halt(InstructionResult::ReentrancySentryOOG);
243 return;
244 }
245
246 gas!(
247 context.interpreter,
248 context.host.gas_params().sstore_static_gas()
249 );
250
251 let state_load = if spec_id.is_enabled_in(BERLIN) {
252 let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost();
253 let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost;
254 let res = context
255 .host
256 .sstore_skip_cold_load(target, index, value, skip_cold);
257 match res {
258 Ok(load) => load,
259 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
260 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
261 }
262 } else {
263 let Some(load) = context.host.sstore(target, index, value) else {
264 return context.interpreter.halt_fatal();
265 };
266 load
267 };
268
269 let is_istanbul = spec_id.is_enabled_in(ISTANBUL);
270
271 gas!(
273 context.interpreter,
274 context.host.gas_params().sstore_dynamic_gas(
275 is_istanbul,
276 &state_load.data,
277 state_load.is_cold
278 )
279 );
280
281 context.interpreter.gas.record_refund(
283 context
284 .host
285 .gas_params()
286 .sstore_refund(is_istanbul, &state_load.data),
287 );
288}
289
290pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
293 check!(context.interpreter, CANCUN);
294 require_non_staticcall!(context.interpreter);
295 popn!([index, value], context.interpreter);
296
297 context
298 .host
299 .tstore(context.interpreter.input.target_address(), index, value);
300}
301
302pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
305 check!(context.interpreter, CANCUN);
306 popn_top!([], index, context.interpreter);
307
308 *index = context
309 .host
310 .tload(context.interpreter.input.target_address(), *index);
311}
312
313pub fn log<const N: usize, H: Host + ?Sized>(
317 context: InstructionContext<'_, H, impl InterpreterTypes>,
318) {
319 require_non_staticcall!(context.interpreter);
320
321 popn!([offset, len], context.interpreter);
322 let len = as_usize_or_fail!(context.interpreter, len);
323 gas!(
324 context.interpreter,
325 context.host.gas_params().log_cost(N as u8, len as u64)
326 );
327 let data = if len == 0 {
328 Bytes::new()
329 } else {
330 let offset = as_usize_or_fail!(context.interpreter, offset);
331 resize_memory!(context.interpreter, context.host.gas_params(), offset, len);
333 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
334 };
335 let Some(topics) = context.interpreter.stack.popn::<N>() else {
336 context.interpreter.halt_underflow();
337 return;
338 };
339
340 let log = Log {
341 address: context.interpreter.input.target_address(),
342 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
343 .expect("LogData should have <=4 topics"),
344 };
345
346 context.host.log(log);
347}
348
349pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
353 context: InstructionContext<'_, H, WIRE>,
354) {
355 require_non_staticcall!(context.interpreter);
356 popn!([target], context.interpreter);
357 let target = target.into_address();
358 let spec = context.interpreter.runtime_flag.spec_id();
359
360 let cold_load_gas = context.host.gas_params().selfdestruct_cold_cost();
361
362 let skip_cold_load = context.interpreter.gas.remaining() < cold_load_gas;
363 let res = match context.host.selfdestruct(
364 context.interpreter.input.target_address(),
365 target,
366 skip_cold_load,
367 ) {
368 Ok(res) => res,
369 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
370 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
371 };
372
373 let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
375 res.had_value && !res.target_exists
376 } else {
377 !res.target_exists
378 };
379
380 gas!(
381 context.interpreter,
382 context
383 .host
384 .gas_params()
385 .selfdestruct_cost(should_charge_topup, res.is_cold)
386 );
387
388 if !res.previously_destroyed {
389 context
390 .interpreter
391 .gas
392 .record_refund(context.host.gas_params().selfdestruct_refund());
393 }
394
395 context.interpreter.halt(InstructionResult::SelfDestruct);
396}