revm_interpreter/instructions/
host.rs

1use 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            // EIP-1884: Repricing for trie-size-dependent opcodes
28            700
29        } else if spec_id.is_enabled_in(TANGERINE) {
30            400
31        } else {
32            20
33        }
34    );
35    *top = balance.data;
36}
37
38/// EIP-1884: Repricing for trie-size-dependent opcodes
39pub 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
80/// EIP-1052: EXTCODEHASH opcode
81pub 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    // Note: This can't panic because we resized memory to fit.
136    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    // blockhash should push zero if number is same as current block number.
159    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    // EIP-1706 Disable SSTORE with gasleft lower than call stipend
214    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
241/// EIP-1153: Transient storage opcodes
242/// Store value to transient storage
243pub 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
255/// EIP-1153: Transient storage opcodes
256/// Load value from transient storage
257pub 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    // EIP-3529: Reduction in refunds
319    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}