revm_interpreter/instructions/
host.rs1use crate::{
2 instruction_context::GasStateTr,
3 instructions::utility::{IntoAddress, IntoU256},
4 interpreter_types::{InputsTr, InterpreterTypes as ITy, MemoryTr, RuntimeFlag, StackTr},
5 Gas, Host, InstructionContext as Ictx, InstructionExecResult as Result, InstructionResult,
6};
7use context_interface::{host::LoadError, journaled_state::AccountInfoLoad};
8use core::cmp::min;
9use primitives::{
10 hardfork::SpecId::{self, *},
11 Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256,
12};
13
14fn load_account<'a, H: Host + ?Sized>(
18 gas: &mut Gas,
19 host: &'a mut H,
20 address: primitives::Address,
21 load_code: bool,
22) -> core::result::Result<AccountInfoLoad<'a>, LoadError> {
23 let cold_load_gas = host.gas_params().cold_account_additional_cost();
24 let skip_cold_load = gas.remaining() < cold_load_gas;
25 let account = host.load_account_info_skip_cold_load(address, load_code, skip_cold_load)?;
26 if account.is_cold && !gas.record_regular_cost(cold_load_gas) {
27 return Err(LoadError::ColdLoadSkipped);
28 }
29 Ok(account)
30}
31
32pub fn balance<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
36 popn_top!([], top, context.interpreter);
37 let address = top.into_address();
38 let account = load_account(&mut context.interpreter.gas, context.host, address, false)?;
39 *top = account.balance;
40 Ok(())
41}
42
43pub fn selfbalance<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
45 check!(context.interpreter, ISTANBUL);
46
47 let balance = context
48 .host
49 .balance(context.interpreter.input.target_address())
50 .ok_or(InstructionResult::FatalExternalError)?;
51 push!(context.interpreter, balance.data);
52 Ok(())
53}
54
55pub fn extcodesize<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
59 popn_top!([], top, context.interpreter);
60 let address = top.into_address();
61 let account = load_account(&mut context.interpreter.gas, context.host, address, true)?;
62 *top = U256::from(account.code.as_ref().unwrap().len());
64 Ok(())
65}
66
67pub fn extcodehash<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
69 check!(context.interpreter, PETERSBURG);
70 popn_top!([], top, context.interpreter);
71 let address = top.into_address();
72 let account = load_account(&mut context.interpreter.gas, context.host, address, false)?;
73 let code_hash = if account.is_empty() {
75 B256::ZERO
76 } else {
77 account.code_hash
78 };
79 *top = code_hash.into_u256();
80 Ok(())
81}
82
83pub fn extcodecopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
87 popn!(
88 [address, memory_offset, code_offset, len_u256],
89 context.interpreter
90 );
91 let address = address.into_address();
92
93 let len = as_usize_or_fail!(context.interpreter, len_u256);
94 gas!(
95 context.interpreter,
96 context.host.gas_params().extcodecopy(len)
97 );
98
99 let mut memory_offset_usize = 0;
100 if len != 0 {
102 memory_offset_usize = as_usize_or_fail!(context.interpreter, memory_offset);
104 context
106 .interpreter
107 .resize_memory(context.host.gas_params(), memory_offset_usize, len)?;
108 }
109
110 let account = load_account(&mut context.interpreter.gas, context.host, address, true)?;
111 let code = account.code.as_ref().unwrap().original_bytes();
112
113 let code_offset_usize = min(as_usize_saturated!(code_offset), code.len());
114
115 context
118 .interpreter
119 .memory
120 .set_data(memory_offset_usize, code_offset_usize, len, &code);
121 Ok(())
122}
123
124pub fn blockhash<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
128 popn_top!([], number, context.interpreter);
129
130 let requested_number = *number;
131 let block_number = context.host.block_number();
132
133 let Some(diff) = block_number.checked_sub(requested_number) else {
134 *number = U256::ZERO;
135 return Ok(());
136 };
137
138 let diff = as_u64_saturated!(diff);
139
140 if diff == 0 {
142 *number = U256::ZERO;
143 return Ok(());
144 }
145
146 *number = if diff <= BLOCK_HASH_HISTORY {
147 let hash = context
148 .host
149 .block_hash(as_u64_saturated!(requested_number))
150 .ok_or(InstructionResult::FatalExternalError)?;
151 U256::from_be_bytes(hash.0)
152 } else {
153 U256::ZERO
154 };
155 Ok(())
156}
157
158pub fn sload<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
162 popn_top!([], index, context.interpreter);
163 let spec_id = context.interpreter.runtime_flag.spec_id();
164 let target = context.interpreter.input.target_address();
165
166 if spec_id.is_enabled_in(BERLIN) {
167 let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost();
168 let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost;
169 let storage = context
170 .host
171 .sload_skip_cold_load(target, *index, skip_cold)?;
172 if storage.is_cold {
173 gas!(context.interpreter, additional_cold_cost);
174 }
175 *index = storage.data;
176 } else {
177 let storage = context
178 .host
179 .sload(target, *index)
180 .ok_or(InstructionResult::FatalExternalError)?;
181 *index = storage.data;
182 };
183 Ok(())
184}
185
186pub fn sstore<IT, H, GS>(mut context: Ictx<'_, H, IT>) -> Result
190where
191 IT: ITy,
192 H: Host + ?Sized,
193 GS: GasStateTr<IT, H>,
194{
195 require_non_staticcall!(context.interpreter);
196 popn!([index, value], context.interpreter);
197
198 let target = context.interpreter.input.target_address();
199 let spec_id = context.interpreter.runtime_flag.spec_id();
200
201 let is_eip8037 = context.host.is_amsterdam_eip8037_enabled();
202
203 if spec_id.is_enabled_in(ISTANBUL)
206 && context.interpreter.gas.remaining() <= context.host.gas_params().call_stipend()
207 {
208 return Err(InstructionResult::ReentrancySentryOOG);
209 }
210
211 gas!(
212 context.interpreter,
213 context.host.gas_params().sstore_static_gas()
214 );
215
216 let state_load = if spec_id.is_enabled_in(BERLIN) {
217 let skip_cold = context.interpreter.gas.remaining()
218 < context.host.gas_params().cold_storage_additional_cost();
219 context
220 .host
221 .sstore_skip_cold_load(target, index, value, skip_cold)?
222 } else {
223 context
224 .host
225 .sstore(target, index, value)
226 .ok_or(InstructionResult::FatalExternalError)?
227 };
228
229 let outcome = GS::sstore_gas_state(&mut context, target, &state_load.data)?;
230
231 let is_istanbul = spec_id.is_enabled_in(ISTANBUL);
232
233 if !outcome.skip_gas {
234 gas!(
236 context.interpreter,
237 context.host.gas_params().sstore_dynamic_gas(
238 is_istanbul,
239 &state_load.data,
240 state_load.is_cold
241 )
242 );
243 }
244
245 if !outcome.skip_gas && is_eip8037 {
246 state_gas!(
247 context.interpreter,
248 context.host.gas_params().sstore_state_gas(&state_load.data)
249 );
250 let refill = context
255 .host
256 .gas_params()
257 .sstore_state_gas_refill(&state_load.data);
258 if refill > 0 {
259 context.interpreter.gas.refill_reservoir(refill);
260 }
261 }
262
263 if !outcome.skip_refund {
265 context.interpreter.gas.record_refund(
266 context
267 .host
268 .gas_params()
269 .sstore_refund(is_istanbul, &state_load.data),
270 );
271 }
272
273 Ok(())
274}
275
276pub fn tstore<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
279 check!(context.interpreter, CANCUN);
280 require_non_staticcall!(context.interpreter);
281 popn!([index, value], context.interpreter);
282
283 context
284 .host
285 .tstore(context.interpreter.input.target_address(), index, value);
286 Ok(())
287}
288
289pub fn tload<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
292 check!(context.interpreter, CANCUN);
293 popn_top!([], index, context.interpreter);
294
295 *index = context
296 .host
297 .tload(context.interpreter.input.target_address(), *index);
298 Ok(())
299}
300
301pub fn log<const N: usize, H: Host + ?Sized>(context: Ictx<'_, H, impl ITy>) -> Result {
305 require_non_staticcall!(context.interpreter);
306
307 popn!([offset, len], context.interpreter);
308 let len = as_usize_or_fail!(context.interpreter, len);
309 gas!(
310 context.interpreter,
311 context.host.gas_params().log_cost(N as u8, len as u64)
312 );
313 let data = if len == 0 {
314 Bytes::new()
315 } else {
316 let offset = as_usize_or_fail!(context.interpreter, offset);
317 context
319 .interpreter
320 .resize_memory(context.host.gas_params(), offset, len)?;
321 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
322 };
323 let Some(topics) = context.interpreter.stack.popn::<N>() else {
324 return Err(InstructionResult::StackUnderflow);
325 };
326
327 let log = Log {
328 address: context.interpreter.input.target_address(),
329 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
330 .expect("LogData should have <=4 topics"),
331 };
332
333 context.host.log(log);
334 Ok(())
335}
336
337pub fn selfdestruct<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
341 require_non_staticcall!(context.interpreter);
342 popn!([target], context.interpreter);
343 let target = target.into_address();
344 let spec = context.interpreter.runtime_flag.spec_id();
345
346 let cold_load_gas = context.host.gas_params().selfdestruct_cold_cost();
347
348 let skip_cold_load = context.interpreter.gas.remaining() < cold_load_gas;
349 let res = context.host.selfdestruct(
350 context.interpreter.input.target_address(),
351 target,
352 skip_cold_load,
353 )?;
354
355 let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
357 res.had_value && !res.target_exists
358 } else {
359 !res.target_exists
360 };
361
362 gas!(
363 context.interpreter,
364 context
365 .host
366 .gas_params()
367 .selfdestruct_cost(should_charge_topup, res.is_cold)
368 );
369
370 if context.host.is_amsterdam_eip8037_enabled() && should_charge_topup {
372 state_gas!(
373 context.interpreter,
374 context.host.gas_params().new_account_state_gas()
375 );
376 }
377
378 if !res.previously_destroyed {
379 context
380 .interpreter
381 .gas
382 .record_refund(context.host.gas_params().selfdestruct_refund());
383 }
384
385 Err(InstructionResult::SelfDestruct)
386}