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::{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            // EIP-1884: Repricing for trie-size-dependent opcodes
31            700
32        } else if spec_id.is_enabled_in(TANGERINE) {
33            400
34        } else {
35            20
36        }
37    );
38    *top = balance.data;
39}
40
41/// EIP-1884: Repricing for trie-size-dependent opcodes
42pub 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
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.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    // 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 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    // EIP-1706 Disable SSTORE with gasleft lower than call stipend
187    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
210/// EIP-1153: Transient storage opcodes
211/// Store value to transient storage
212pub 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
225/// EIP-1153: Transient storage opcodes
226/// Load value from transient storage
227pub 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    // EIP-3529: Reduction in refunds
292    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}