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, true)
88 } else {
89 let Ok(account) = context
90 .host
91 .load_account_info_skip_cold_load(address, true, 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.interpreter.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!(context.interpreter, memory_offset_usize, len);
133 }
134
135 let code = if spec_id.is_enabled_in(BERLIN) {
136 let account = berlin_load_account!(context, address, true);
137 account.code.as_ref().unwrap().original_bytes()
138 } else {
139 let Some(code) = context.host.load_account_code(address) else {
140 return context.interpreter.halt_fatal();
141 };
142 code.data
143 };
144
145 let code_offset_usize = min(as_usize_saturated!(code_offset), code.len());
146
147 context
150 .interpreter
151 .memory
152 .set_data(memory_offset_usize, code_offset_usize, len, &code);
153}
154
155pub fn blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(
159 context: InstructionContext<'_, H, WIRE>,
160) {
161 popn_top!([], number, context.interpreter);
162
163 let requested_number = *number;
164 let block_number = context.host.block_number();
165
166 let Some(diff) = block_number.checked_sub(requested_number) else {
167 *number = U256::ZERO;
168 return;
169 };
170
171 let diff = as_u64_saturated!(diff);
172
173 if diff == 0 {
175 *number = U256::ZERO;
176 return;
177 }
178
179 *number = if diff <= BLOCK_HASH_HISTORY {
180 let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else {
181 return context.interpreter.halt_fatal();
182 };
183 U256::from_be_bytes(hash.0)
184 } else {
185 U256::ZERO
186 }
187}
188
189pub fn sload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
193 popn_top!([], index, context.interpreter);
194 let spec_id = context.interpreter.runtime_flag.spec_id();
195 let target = context.interpreter.input.target_address();
196
197 if spec_id.is_enabled_in(BERLIN) {
198 let additional_cold_cost = context
199 .interpreter
200 .gas_params
201 .cold_storage_additional_cost();
202 let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost;
203 let res = context.host.sload_skip_cold_load(target, *index, skip_cold);
204 match res {
205 Ok(storage) => {
206 if storage.is_cold {
207 gas!(context.interpreter, additional_cold_cost);
208 }
209
210 *index = storage.data;
211 }
212 Err(LoadError::ColdLoadSkipped) => context.interpreter.halt_oog(),
213 Err(LoadError::DBError) => context.interpreter.halt_fatal(),
214 }
215 } else {
216 let Some(storage) = context.host.sload(target, *index) else {
217 return context.interpreter.halt_fatal();
218 };
219 *index = storage.data;
220 };
221}
222
223pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
227 require_non_staticcall!(context.interpreter);
228 popn!([index, value], context.interpreter);
229
230 let target = context.interpreter.input.target_address();
231 let spec_id = context.interpreter.runtime_flag.spec_id();
232
233 if spec_id.is_enabled_in(ISTANBUL)
236 && context.interpreter.gas.remaining() <= context.interpreter.gas_params.call_stipend()
237 {
238 context
239 .interpreter
240 .halt(InstructionResult::ReentrancySentryOOG);
241 return;
242 }
243
244 gas!(
245 context.interpreter,
246 context.interpreter.gas_params.sstore_static_gas()
247 );
248
249 let state_load = if spec_id.is_enabled_in(BERLIN) {
250 let additional_cold_cost = context
251 .interpreter
252 .gas_params
253 .cold_storage_additional_cost();
254 let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost;
255 let res = context
256 .host
257 .sstore_skip_cold_load(target, index, value, skip_cold);
258 match res {
259 Ok(load) => load,
260 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
261 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
262 }
263 } else {
264 let Some(load) = context.host.sstore(target, index, value) else {
265 return context.interpreter.halt_fatal();
266 };
267 load
268 };
269
270 let is_istanbul = spec_id.is_enabled_in(ISTANBUL);
271
272 gas!(
274 context.interpreter,
275 context.interpreter.gas_params.sstore_dynamic_gas(
276 is_istanbul,
277 &state_load.data,
278 state_load.is_cold
279 )
280 );
281
282 context.interpreter.gas.record_refund(
284 context
285 .interpreter
286 .gas_params
287 .sstore_refund(is_istanbul, &state_load.data),
288 );
289}
290
291pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
294 check!(context.interpreter, CANCUN);
295 require_non_staticcall!(context.interpreter);
296 popn!([index, value], context.interpreter);
297
298 context
299 .host
300 .tstore(context.interpreter.input.target_address(), index, value);
301}
302
303pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
306 check!(context.interpreter, CANCUN);
307 popn_top!([], index, context.interpreter);
308
309 *index = context
310 .host
311 .tload(context.interpreter.input.target_address(), *index);
312}
313
314pub fn log<const N: usize, H: Host + ?Sized>(
318 context: InstructionContext<'_, H, impl InterpreterTypes>,
319) {
320 require_non_staticcall!(context.interpreter);
321
322 popn!([offset, len], context.interpreter);
323 let len = as_usize_or_fail!(context.interpreter, len);
324 gas!(
325 context.interpreter,
326 context.interpreter.gas_params.log_cost(N as u8, len as u64)
327 );
328 let data = if len == 0 {
329 Bytes::new()
330 } else {
331 let offset = as_usize_or_fail!(context.interpreter, offset);
332 resize_memory!(context.interpreter, offset, len);
334 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
335 };
336 let Some(topics) = context.interpreter.stack.popn::<N>() else {
337 context.interpreter.halt_underflow();
338 return;
339 };
340
341 let log = Log {
342 address: context.interpreter.input.target_address(),
343 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
344 .expect("LogData should have <=4 topics"),
345 };
346
347 context.host.log(log);
348}
349
350pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
354 context: InstructionContext<'_, H, WIRE>,
355) {
356 require_non_staticcall!(context.interpreter);
357 popn!([target], context.interpreter);
358 let target = target.into_address();
359 let spec = context.interpreter.runtime_flag.spec_id();
360
361 let cold_load_gas = context
362 .interpreter
363 .gas_params
364 .cold_account_additional_cost();
365
366 let skip_cold_load = context.interpreter.gas.remaining() < cold_load_gas;
367 let res = match context.host.selfdestruct(
368 context.interpreter.input.target_address(),
369 target,
370 skip_cold_load,
371 ) {
372 Ok(res) => res,
373 Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(),
374 Err(LoadError::DBError) => return context.interpreter.halt_fatal(),
375 };
376
377 let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
379 res.had_value && !res.target_exists
380 } else {
381 !res.target_exists
382 };
383
384 gas!(
385 context.interpreter,
386 context
387 .interpreter
388 .gas_params
389 .selfdestruct_cost(should_charge_topup, res.is_cold)
390 );
391
392 if !res.previously_destroyed {
393 context
394 .interpreter
395 .gas
396 .record_refund(context.interpreter.gas_params.selfdestruct_refund());
397 }
398
399 context.interpreter.halt(InstructionResult::SelfDestruct);
400}