revm_interpreter/instructions/
host.rs1use crate::{
2 gas::{self, warm_cold_cost, CALL_STIPEND},
3 instructions::utility::{IntoAddress, IntoU256},
4 interpreter::Interpreter,
5 interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr},
6 Host, InstructionResult,
7};
8use core::cmp::min;
9use primitives::{Bytes, Log, LogData, B256, U256};
10use specification::hardfork::SpecId::*;
11
12pub fn balance<WIRE: InterpreterTypes, H: Host + ?Sized>(
13 interpreter: &mut Interpreter<WIRE>,
14 host: &mut H,
15) {
16 popn_top!([], top, interpreter);
17 let address = top.into_address();
18 let Some(balance) = host.balance(address) else {
19 interpreter
20 .control
21 .set_instruction_result(InstructionResult::FatalExternalError);
22 return;
23 };
24 let spec_id = interpreter.runtime_flag.spec_id();
25 gas!(
26 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 interpreter: &mut Interpreter<WIRE>,
44 host: &mut H,
45) {
46 check!(interpreter, ISTANBUL);
47 gas!(interpreter, gas::LOW);
48 let Some(balance) = host.balance(interpreter.input.target_address()) else {
49 interpreter
50 .control
51 .set_instruction_result(InstructionResult::FatalExternalError);
52 return;
53 };
54 push!(interpreter, balance.data);
55}
56
57pub fn extcodesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
58 interpreter: &mut Interpreter<WIRE>,
59 host: &mut H,
60) {
61 popn_top!([], top, interpreter);
62 let address = top.into_address();
63 let Some(code) = host.code(address) else {
64 interpreter
65 .control
66 .set_instruction_result(InstructionResult::FatalExternalError);
67 return;
68 };
69 let spec_id = interpreter.runtime_flag.spec_id();
70 if spec_id.is_enabled_in(BERLIN) {
71 gas!(interpreter, warm_cold_cost(code.is_cold));
72 } else if spec_id.is_enabled_in(TANGERINE) {
73 gas!(interpreter, 700);
74 } else {
75 gas!(interpreter, 20);
76 }
77
78 *top = U256::from(code.len());
79}
80
81pub fn extcodehash<WIRE: InterpreterTypes, H: Host + ?Sized>(
83 interpreter: &mut Interpreter<WIRE>,
84 host: &mut H,
85) {
86 check!(interpreter, CONSTANTINOPLE);
87 popn_top!([], top, interpreter);
88 let address = top.into_address();
89 let Some(code_hash) = host.code_hash(address) else {
90 interpreter
91 .control
92 .set_instruction_result(InstructionResult::FatalExternalError);
93 return;
94 };
95 let spec_id = interpreter.runtime_flag.spec_id();
96 if spec_id.is_enabled_in(BERLIN) {
97 gas!(interpreter, warm_cold_cost(code_hash.is_cold));
98 } else if spec_id.is_enabled_in(ISTANBUL) {
99 gas!(interpreter, 700);
100 } else {
101 gas!(interpreter, 400);
102 }
103 *top = code_hash.into_u256();
104}
105
106pub fn extcodecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
107 interpreter: &mut Interpreter<WIRE>,
108 host: &mut H,
109) {
110 popn!([address, memory_offset, code_offset, len_u256], interpreter);
111 let address = address.into_address();
112 let Some(code) = host.code(address) else {
113 interpreter
114 .control
115 .set_instruction_result(InstructionResult::FatalExternalError);
116 return;
117 };
118
119 let len = as_usize_or_fail!(interpreter, len_u256);
120 gas_or_fail!(
121 interpreter,
122 gas::extcodecopy_cost(interpreter.runtime_flag.spec_id(), len, code.is_cold)
123 );
124 if len == 0 {
125 return;
126 }
127 let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
128 let code_offset = min(as_usize_saturated!(code_offset), code.len());
129 resize_memory!(interpreter, memory_offset, len);
130
131 interpreter
133 .memory
134 .set_data(memory_offset, code_offset, len, &code);
135}
136
137pub fn blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(
138 interpreter: &mut Interpreter<WIRE>,
139 host: &mut H,
140) {
141 gas!(interpreter, gas::BLOCKHASH);
142 popn_top!([], number, interpreter);
143
144 let number_u64 = as_u64_saturated!(number);
145 let Some(hash) = host.block_hash(number_u64) else {
146 interpreter
147 .control
148 .set_instruction_result(InstructionResult::FatalExternalError);
149 return;
150 };
151 *number = U256::from_be_bytes(hash.0);
152}
153
154pub fn sload<WIRE: InterpreterTypes, H: Host + ?Sized>(
155 interpreter: &mut Interpreter<WIRE>,
156 host: &mut H,
157) {
158 popn_top!([], index, interpreter);
159 let Some(value) = host.sload(interpreter.input.target_address(), *index) else {
160 interpreter
161 .control
162 .set_instruction_result(InstructionResult::FatalExternalError);
163 return;
164 };
165 gas!(
166 interpreter,
167 gas::sload_cost(interpreter.runtime_flag.spec_id(), value.is_cold)
168 );
169 *index = value.data;
170}
171
172pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(
173 interpreter: &mut Interpreter<WIRE>,
174 host: &mut H,
175) {
176 require_non_staticcall!(interpreter);
177
178 popn!([index, value], interpreter);
179 let Some(state_load) = host.sstore(interpreter.input.target_address(), index, value) else {
180 interpreter
181 .control
182 .set_instruction_result(InstructionResult::FatalExternalError);
183 return;
184 };
185
186 if interpreter.runtime_flag.spec_id().is_enabled_in(ISTANBUL)
188 && interpreter.control.gas().remaining() <= CALL_STIPEND
189 {
190 interpreter
191 .control
192 .set_instruction_result(InstructionResult::ReentrancySentryOOG);
193 return;
194 }
195 gas!(
196 interpreter,
197 gas::sstore_cost(
198 interpreter.runtime_flag.spec_id(),
199 &state_load.data,
200 state_load.is_cold
201 )
202 );
203
204 interpreter.control.gas().record_refund(gas::sstore_refund(
205 interpreter.runtime_flag.spec_id(),
206 &state_load.data,
207 ));
208}
209
210pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(
213 interpreter: &mut Interpreter<WIRE>,
214 host: &mut H,
215) {
216 check!(interpreter, CANCUN);
217 require_non_staticcall!(interpreter);
218 gas!(interpreter, gas::WARM_STORAGE_READ_COST);
219
220 popn!([index, value], interpreter);
221
222 host.tstore(interpreter.input.target_address(), index, value);
223}
224
225pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(
228 interpreter: &mut Interpreter<WIRE>,
229 host: &mut H,
230) {
231 check!(interpreter, CANCUN);
232 gas!(interpreter, gas::WARM_STORAGE_READ_COST);
233
234 popn_top!([], index, interpreter);
235
236 *index = host.tload(interpreter.input.target_address(), *index);
237}
238
239pub fn log<const N: usize, H: Host + ?Sized>(
240 interpreter: &mut Interpreter<impl InterpreterTypes>,
241 host: &mut H,
242) {
243 require_non_staticcall!(interpreter);
244
245 popn!([offset, len], interpreter);
246 let len = as_usize_or_fail!(interpreter, len);
247 gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64));
248 let data = if len == 0 {
249 Bytes::new()
250 } else {
251 let offset = as_usize_or_fail!(interpreter, offset);
252 resize_memory!(interpreter, offset, len);
253 Bytes::copy_from_slice(interpreter.memory.slice_len(offset, len).as_ref())
254 };
255 if interpreter.stack.len() < N {
256 interpreter
257 .control
258 .set_instruction_result(InstructionResult::StackUnderflow);
259 return;
260 }
261 let Some(topics) = interpreter.stack.popn::<N>() else {
262 interpreter
263 .control
264 .set_instruction_result(InstructionResult::StackUnderflow);
265 return;
266 };
267
268 let log = Log {
269 address: interpreter.input.target_address(),
270 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
271 .expect("LogData should have <=4 topics"),
272 };
273
274 host.log(log);
275}
276
277pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
278 interpreter: &mut Interpreter<WIRE>,
279 host: &mut H,
280) {
281 require_non_staticcall!(interpreter);
282 popn!([target], interpreter);
283 let target = target.into_address();
284 let Some(res) = host.selfdestruct(interpreter.input.target_address(), target) else {
285 interpreter
286 .control
287 .set_instruction_result(InstructionResult::FatalExternalError);
288 return;
289 };
290
291 if !interpreter.runtime_flag.spec_id().is_enabled_in(LONDON) && !res.previously_destroyed {
293 interpreter.control.gas().record_refund(gas::SELFDESTRUCT)
294 }
295
296 gas!(
297 interpreter,
298 gas::selfdestruct_cost(interpreter.runtime_flag.spec_id(), res)
299 );
300
301 interpreter
302 .control
303 .set_instruction_result(InstructionResult::SelfDestruct);
304}