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