revm_interpreter/instructions/
host.rs

1use 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::{hardfork::SpecId::*, Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256};
10
11pub fn balance<WIRE: InterpreterTypes, H: Host + ?Sized>(
12    interpreter: &mut Interpreter<WIRE>,
13    host: &mut H,
14) {
15    popn_top!([], top, interpreter);
16    let address = top.into_address();
17    let Some(balance) = host.balance(address) else {
18        interpreter
19            .control
20            .set_instruction_result(InstructionResult::FatalExternalError);
21        return;
22    };
23    let spec_id = interpreter.runtime_flag.spec_id();
24    gas!(
25        interpreter,
26        if spec_id.is_enabled_in(BERLIN) {
27            warm_cold_cost(balance.is_cold)
28        } else if spec_id.is_enabled_in(ISTANBUL) {
29            // EIP-1884: Repricing for trie-size-dependent opcodes
30            700
31        } else if spec_id.is_enabled_in(TANGERINE) {
32            400
33        } else {
34            20
35        }
36    );
37    *top = balance.data;
38}
39
40/// EIP-1884: Repricing for trie-size-dependent opcodes
41pub fn selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(
42    interpreter: &mut Interpreter<WIRE>,
43    host: &mut H,
44) {
45    check!(interpreter, ISTANBUL);
46    gas!(interpreter, gas::LOW);
47
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.load_account_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
81/// EIP-1052: EXTCODEHASH opcode
82pub 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.load_account_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.load_account_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    // Note: This can't panic because we resized memory to fit.
132    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 requested_number = as_u64_saturated!(number);
145
146    let block_number = host.block_number();
147
148    let Some(diff) = block_number.checked_sub(requested_number) else {
149        *number = U256::ZERO;
150        return;
151    };
152
153    // blockhash should push zero if number is same as current block number.
154    if diff == 0 {
155        *number = U256::ZERO;
156        return;
157    }
158
159    *number = if diff <= BLOCK_HASH_HISTORY {
160        let Some(hash) = host.block_hash(requested_number) else {
161            interpreter
162                .control
163                .set_instruction_result(InstructionResult::FatalExternalError);
164            return;
165        };
166        U256::from_be_bytes(hash.0)
167    } else {
168        U256::ZERO
169    }
170}
171
172pub fn sload<WIRE: InterpreterTypes, H: Host + ?Sized>(
173    interpreter: &mut Interpreter<WIRE>,
174    host: &mut H,
175) {
176    popn_top!([], index, interpreter);
177
178    let Some(value) = host.sload(interpreter.input.target_address(), *index) else {
179        interpreter
180            .control
181            .set_instruction_result(InstructionResult::FatalExternalError);
182        return;
183    };
184
185    gas!(
186        interpreter,
187        gas::sload_cost(interpreter.runtime_flag.spec_id(), value.is_cold)
188    );
189    *index = value.data;
190}
191
192pub fn sstore<WIRE: InterpreterTypes, H: Host + ?Sized>(
193    interpreter: &mut Interpreter<WIRE>,
194    host: &mut H,
195) {
196    require_non_staticcall!(interpreter);
197
198    popn!([index, value], interpreter);
199
200    let Some(state_load) = host.sstore(interpreter.input.target_address(), index, value) else {
201        interpreter
202            .control
203            .set_instruction_result(InstructionResult::FatalExternalError);
204        return;
205    };
206
207    // EIP-1706 Disable SSTORE with gasleft lower than call stipend
208    if interpreter.runtime_flag.spec_id().is_enabled_in(ISTANBUL)
209        && interpreter.control.gas().remaining() <= CALL_STIPEND
210    {
211        interpreter
212            .control
213            .set_instruction_result(InstructionResult::ReentrancySentryOOG);
214        return;
215    }
216    gas!(
217        interpreter,
218        gas::sstore_cost(
219            interpreter.runtime_flag.spec_id(),
220            &state_load.data,
221            state_load.is_cold
222        )
223    );
224
225    interpreter
226        .control
227        .gas_mut()
228        .record_refund(gas::sstore_refund(
229            interpreter.runtime_flag.spec_id(),
230            &state_load.data,
231        ));
232}
233
234/// EIP-1153: Transient storage opcodes
235/// Store value to transient storage
236pub fn tstore<WIRE: InterpreterTypes, H: Host + ?Sized>(
237    interpreter: &mut Interpreter<WIRE>,
238    host: &mut H,
239) {
240    check!(interpreter, CANCUN);
241    require_non_staticcall!(interpreter);
242    gas!(interpreter, gas::WARM_STORAGE_READ_COST);
243
244    popn!([index, value], interpreter);
245
246    host.tstore(interpreter.input.target_address(), index, value);
247}
248
249/// EIP-1153: Transient storage opcodes
250/// Load value from transient storage
251pub fn tload<WIRE: InterpreterTypes, H: Host + ?Sized>(
252    interpreter: &mut Interpreter<WIRE>,
253    host: &mut H,
254) {
255    check!(interpreter, CANCUN);
256    gas!(interpreter, gas::WARM_STORAGE_READ_COST);
257
258    popn_top!([], index, interpreter);
259
260    *index = host.tload(interpreter.input.target_address(), *index);
261}
262
263pub fn log<const N: usize, H: Host + ?Sized>(
264    interpreter: &mut Interpreter<impl InterpreterTypes>,
265    host: &mut H,
266) {
267    require_non_staticcall!(interpreter);
268
269    popn!([offset, len], interpreter);
270    let len = as_usize_or_fail!(interpreter, len);
271    gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64));
272    let data = if len == 0 {
273        Bytes::new()
274    } else {
275        let offset = as_usize_or_fail!(interpreter, offset);
276        resize_memory!(interpreter, offset, len);
277        Bytes::copy_from_slice(interpreter.memory.slice_len(offset, len).as_ref())
278    };
279    if interpreter.stack.len() < N {
280        interpreter
281            .control
282            .set_instruction_result(InstructionResult::StackUnderflow);
283        return;
284    }
285    let Some(topics) = interpreter.stack.popn::<N>() else {
286        interpreter
287            .control
288            .set_instruction_result(InstructionResult::StackUnderflow);
289        return;
290    };
291
292    let log = Log {
293        address: 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    host.log(log);
299}
300
301pub fn selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
302    interpreter: &mut Interpreter<WIRE>,
303    host: &mut H,
304) {
305    require_non_staticcall!(interpreter);
306    popn!([target], interpreter);
307    let target = target.into_address();
308
309    let Some(res) = host.selfdestruct(interpreter.input.target_address(), target) else {
310        interpreter
311            .control
312            .set_instruction_result(InstructionResult::FatalExternalError);
313        return;
314    };
315
316    // EIP-3529: Reduction in refunds
317    if !interpreter.runtime_flag.spec_id().is_enabled_in(LONDON) && !res.previously_destroyed {
318        interpreter
319            .control
320            .gas_mut()
321            .record_refund(gas::SELFDESTRUCT)
322    }
323
324    gas!(
325        interpreter,
326        gas::selfdestruct_cost(interpreter.runtime_flag.spec_id(), res)
327    );
328
329    interpreter
330        .control
331        .set_instruction_result(InstructionResult::SelfDestruct);
332}