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