revm_interpreter/instructions/
host.rs1use crate::{
2 gas::{self, warm_cold_cost, CALL_STIPEND},
3 instructions::utility::{IntoAddress, IntoU256},
4 interpreter_types::{InputsTr, InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
5 Host, InstructionResult,
6};
7use core::cmp::min;
8use primitives::{hardfork::SpecId::*, Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256};
9
10use crate::InstructionContext;
11
12pub fn balance<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
16 popn_top!([], top, context.interpreter);
17 let address = top.into_address();
18 let Some(balance) = context.host.balance(address) else {
19 context
20 .interpreter
21 .halt(InstructionResult::FatalExternalError);
22 return;
23 };
24 let spec_id = context.interpreter.runtime_flag.spec_id();
25 gas!(
26 context.interpreter,
27 if spec_id.is_enabled_in(BERLIN) {
28 warm_cold_cost(balance.is_cold)
29 } else if spec_id.is_enabled_in(ISTANBUL) {
30 700
32 } else if spec_id.is_enabled_in(TANGERINE) {
33 400
34 } else {
35 20
36 }
37 );
38 *top = balance.data;
39}
40
41pub fn selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(
43 context: InstructionContext<'_, H, WIRE>,
44) {
45 check!(context.interpreter, ISTANBUL);
46 gas!(context.interpreter, gas::LOW);
47
48 let Some(balance) = context
49 .host
50 .balance(context.interpreter.input.target_address())
51 else {
52 context
53 .interpreter
54 .halt(InstructionResult::FatalExternalError);
55 return;
56 };
57 push!(context.interpreter, balance.data);
58}
59
60pub fn extcodesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
64 context: InstructionContext<'_, H, WIRE>,
65) {
66 popn_top!([], top, context.interpreter);
67 let address = top.into_address();
68 let Some(code) = context.host.load_account_code(address) else {
69 context
70 .interpreter
71 .halt(InstructionResult::FatalExternalError);
72 return;
73 };
74 let spec_id = context.interpreter.runtime_flag.spec_id();
75 if spec_id.is_enabled_in(BERLIN) {
76 gas!(context.interpreter, warm_cold_cost(code.is_cold));
77 } else if spec_id.is_enabled_in(TANGERINE) {
78 gas!(context.interpreter, 700);
79 } else {
80 gas!(context.interpreter, 20);
81 }
82
83 *top = U256::from(code.len());
84}
85
86pub fn extcodehash<WIRE: InterpreterTypes, H: Host + ?Sized>(
88 context: InstructionContext<'_, H, WIRE>,
89) {
90 check!(context.interpreter, CONSTANTINOPLE);
91 popn_top!([], top, context.interpreter);
92 let address = top.into_address();
93 let Some(code_hash) = context.host.load_account_code_hash(address) else {
94 context
95 .interpreter
96 .halt(InstructionResult::FatalExternalError);
97 return;
98 };
99 let spec_id = context.interpreter.runtime_flag.spec_id();
100 if spec_id.is_enabled_in(BERLIN) {
101 gas!(context.interpreter, warm_cold_cost(code_hash.is_cold));
102 } else if spec_id.is_enabled_in(ISTANBUL) {
103 gas!(context.interpreter, 700);
104 } else {
105 gas!(context.interpreter, 400);
106 }
107 *top = code_hash.into_u256();
108}
109
110pub fn extcodecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
114 context: InstructionContext<'_, H, WIRE>,
115) {
116 popn!(
117 [address, memory_offset, code_offset, len_u256],
118 context.interpreter
119 );
120 let address = address.into_address();
121 let Some(code) = context.host.load_account_code(address) else {
122 context
123 .interpreter
124 .halt(InstructionResult::FatalExternalError);
125 return;
126 };
127
128 let len = as_usize_or_fail!(context.interpreter, len_u256);
129 gas_or_fail!(
130 context.interpreter,
131 gas::extcodecopy_cost(
132 context.interpreter.runtime_flag.spec_id(),
133 len,
134 code.is_cold
135 )
136 );
137 if len == 0 {
138 return;
139 }
140 let memory_offset = as_usize_or_fail!(context.interpreter, memory_offset);
141 let code_offset = min(as_usize_saturated!(code_offset), code.len());
142 resize_memory!(context.interpreter, memory_offset, len);
143
144 context
146 .interpreter
147 .memory
148 .set_data(memory_offset, code_offset, len, &code);
149}
150
151pub fn blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(
155 context: InstructionContext<'_, H, WIRE>,
156) {
157 gas!(context.interpreter, gas::BLOCKHASH);
158 popn_top!([], number, context.interpreter);
159
160 let requested_number = *number;
161 let block_number = context.host.block_number();
162
163 let Some(diff) = block_number.checked_sub(requested_number) else {
164 *number = U256::ZERO;
165 return;
166 };
167
168 let diff = as_u64_saturated!(diff);
169
170 if diff == 0 {
172 *number = U256::ZERO;
173 return;
174 }
175
176 *number = if diff <= BLOCK_HASH_HISTORY {
177 let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else {
178 context
179 .interpreter
180 .halt(InstructionResult::FatalExternalError);
181 return;
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
195 let Some(value) = context
196 .host
197 .sload(context.interpreter.input.target_address(), *index)
198 else {
199 context
200 .interpreter
201 .halt(InstructionResult::FatalExternalError);
202 return;
203 };
204
205 gas!(
206 context.interpreter,
207 gas::sload_cost(context.interpreter.runtime_flag.spec_id(), value.is_cold)
208 );
209 *index = value.data;
210}
211
212pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
216 require_non_staticcall!(context.interpreter);
217
218 popn!([index, value], context.interpreter);
219
220 let Some(state_load) =
221 context
222 .host
223 .sstore(context.interpreter.input.target_address(), index, value)
224 else {
225 context
226 .interpreter
227 .halt(InstructionResult::FatalExternalError);
228 return;
229 };
230
231 if context
233 .interpreter
234 .runtime_flag
235 .spec_id()
236 .is_enabled_in(ISTANBUL)
237 && context.interpreter.gas.remaining() <= CALL_STIPEND
238 {
239 context
240 .interpreter
241 .halt(InstructionResult::ReentrancySentryOOG);
242 return;
243 }
244 gas!(
245 context.interpreter,
246 gas::sstore_cost(
247 context.interpreter.runtime_flag.spec_id(),
248 &state_load.data,
249 state_load.is_cold
250 )
251 );
252
253 context.interpreter.gas.record_refund(gas::sstore_refund(
254 context.interpreter.runtime_flag.spec_id(),
255 &state_load.data,
256 ));
257}
258
259pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
262 check!(context.interpreter, CANCUN);
263 require_non_staticcall!(context.interpreter);
264 gas!(context.interpreter, gas::WARM_STORAGE_READ_COST);
265
266 popn!([index, value], context.interpreter);
267
268 context
269 .host
270 .tstore(context.interpreter.input.target_address(), index, value);
271}
272
273pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
276 check!(context.interpreter, CANCUN);
277 gas!(context.interpreter, gas::WARM_STORAGE_READ_COST);
278
279 popn_top!([], index, context.interpreter);
280
281 *index = context
282 .host
283 .tload(context.interpreter.input.target_address(), *index);
284}
285
286pub fn log<const N: usize, H: Host + ?Sized>(
290 context: InstructionContext<'_, H, impl InterpreterTypes>,
291) {
292 require_non_staticcall!(context.interpreter);
293
294 popn!([offset, len], context.interpreter);
295 let len = as_usize_or_fail!(context.interpreter, len);
296 gas_or_fail!(context.interpreter, gas::log_cost(N as u8, len as u64));
297 let data = if len == 0 {
298 Bytes::new()
299 } else {
300 let offset = as_usize_or_fail!(context.interpreter, offset);
301 resize_memory!(context.interpreter, offset, len);
302 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
303 };
304 if context.interpreter.stack.len() < N {
305 context.interpreter.halt(InstructionResult::StackUnderflow);
306 return;
307 }
308 let Some(topics) = context.interpreter.stack.popn::<N>() else {
309 context.interpreter.halt(InstructionResult::StackUnderflow);
310 return;
311 };
312
313 let log = Log {
314 address: context.interpreter.input.target_address(),
315 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
316 .expect("LogData should have <=4 topics"),
317 };
318
319 context.host.log(log);
320}
321
322pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
326 context: InstructionContext<'_, H, WIRE>,
327) {
328 require_non_staticcall!(context.interpreter);
329 popn!([target], context.interpreter);
330 let target = target.into_address();
331
332 let Some(res) = context
333 .host
334 .selfdestruct(context.interpreter.input.target_address(), target)
335 else {
336 context
337 .interpreter
338 .halt(InstructionResult::FatalExternalError);
339 return;
340 };
341
342 if !context
344 .interpreter
345 .runtime_flag
346 .spec_id()
347 .is_enabled_in(LONDON)
348 && !res.previously_destroyed
349 {
350 context.interpreter.gas.record_refund(gas::SELFDESTRUCT)
351 }
352
353 gas!(
354 context.interpreter,
355 gas::selfdestruct_cost(context.interpreter.runtime_flag.spec_id(), res)
356 );
357
358 context.interpreter.halt(InstructionResult::SelfDestruct);
359}