revm_interpreter/instructions/
contract.rs

1mod call_helpers;
2
3pub use call_helpers::{
4    get_memory_input_and_out_ranges, load_acc_and_calc_gas, load_account_delegated,
5    load_account_delegated_handle_error, resize_memory,
6};
7
8use crate::{
9    gas,
10    instructions::utility::IntoAddress,
11    interpreter_action::FrameInput,
12    interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr},
13    CallInput, CallInputs, CallScheme, CallValue, CreateInputs, Host, InstructionResult,
14    InterpreterAction,
15};
16use context_interface::CreateScheme;
17use primitives::{hardfork::SpecId, Address, Bytes, B256, U256};
18use std::boxed::Box;
19
20use crate::InstructionContext;
21
22/// Implements the CREATE/CREATE2 instruction.
23///
24/// Creates a new contract with provided bytecode.
25pub fn create<WIRE: InterpreterTypes, const IS_CREATE2: bool, H: Host + ?Sized>(
26    context: InstructionContext<'_, H, WIRE>,
27) {
28    require_non_staticcall!(context.interpreter);
29
30    // EIP-1014: Skinny CREATE2
31    if IS_CREATE2 {
32        check!(context.interpreter, PETERSBURG);
33    }
34
35    popn!([value, code_offset, len], context.interpreter);
36    let len = as_usize_or_fail!(context.interpreter, len);
37
38    let mut code = Bytes::new();
39    if len != 0 {
40        // EIP-3860: Limit and meter initcode
41        if context
42            .interpreter
43            .runtime_flag
44            .spec_id()
45            .is_enabled_in(SpecId::SHANGHAI)
46        {
47            // Limit is set as double of max contract bytecode size
48            if len > context.host.max_initcode_size() {
49                context
50                    .interpreter
51                    .halt(InstructionResult::CreateInitCodeSizeLimit);
52                return;
53            }
54            gas!(
55                context.interpreter,
56                context.interpreter.gas_params.initcode_cost(len)
57            );
58        }
59
60        let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
61        resize_memory!(context.interpreter, code_offset, len);
62
63        code = Bytes::copy_from_slice(
64            context
65                .interpreter
66                .memory
67                .slice_len(code_offset, len)
68                .as_ref(),
69        );
70    }
71
72    // EIP-1014: Skinny CREATE2
73    let scheme = if IS_CREATE2 {
74        popn!([salt], context.interpreter);
75        // SAFETY: `len` is reasonable in size as gas for it is already deducted.
76        gas!(
77            context.interpreter,
78            context.interpreter.gas_params.create2_cost(len)
79        );
80        CreateScheme::Create2 { salt }
81    } else {
82        gas!(
83            context.interpreter,
84            context.interpreter.gas_params.create_cost()
85        );
86        CreateScheme::Create
87    };
88
89    let mut gas_limit = context.interpreter.gas.remaining();
90
91    // EIP-150: Gas cost changes for IO-heavy operations
92    if context
93        .interpreter
94        .runtime_flag
95        .spec_id()
96        .is_enabled_in(SpecId::TANGERINE)
97    {
98        // Take remaining gas and deduce l64 part of it.
99        gas_limit = context
100            .interpreter
101            .gas_params
102            .call_stipend_reduction(gas_limit);
103    }
104    gas!(context.interpreter, gas_limit);
105
106    // Call host to interact with target contract
107    context
108        .interpreter
109        .bytecode
110        .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
111            CreateInputs::new(
112                context.interpreter.input.target_address(),
113                scheme,
114                value,
115                code,
116                gas_limit,
117            ),
118        ))));
119}
120
121/// Implements the CALL instruction.
122///
123/// Message call with value transfer to another account.
124pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
125    mut context: InstructionContext<'_, H, WIRE>,
126) {
127    popn!([local_gas_limit, to, value], context.interpreter);
128    let to = to.into_address();
129    // Max gas limit is not possible in real ethereum situation.
130    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
131    let has_transfer = !value.is_zero();
132
133    if context.interpreter.runtime_flag.is_static() && has_transfer {
134        context
135            .interpreter
136            .halt(InstructionResult::CallNotAllowedInsideStatic);
137        return;
138    }
139
140    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
141    else {
142        return;
143    };
144
145    let Some((gas_limit, bytecode, bytecode_hash)) =
146        load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit)
147    else {
148        return;
149    };
150
151    // Call host to interact with target contract
152    context
153        .interpreter
154        .bytecode
155        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
156            CallInputs {
157                input: CallInput::SharedBuffer(input),
158                gas_limit,
159                target_address: to,
160                caller: context.interpreter.input.target_address(),
161                bytecode_address: to,
162                known_bytecode: Some((bytecode_hash, bytecode)),
163                value: CallValue::Transfer(value),
164                scheme: CallScheme::Call,
165                is_static: context.interpreter.runtime_flag.is_static(),
166                return_memory_offset,
167            },
168        ))));
169}
170
171/// Implements the CALLCODE instruction.
172///
173/// Message call with alternative account's code.
174pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
175    mut context: InstructionContext<'_, H, WIRE>,
176) {
177    popn!([local_gas_limit, to, value], context.interpreter);
178    let to = Address::from_word(B256::from(to));
179    // Max gas limit is not possible in real ethereum situation.
180    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
181    let has_transfer = !value.is_zero();
182
183    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
184    else {
185        return;
186    };
187
188    let Some((gas_limit, bytecode, bytecode_hash)) =
189        load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)
190    else {
191        return;
192    };
193
194    // Call host to interact with target contract
195    context
196        .interpreter
197        .bytecode
198        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
199            CallInputs {
200                input: CallInput::SharedBuffer(input),
201                gas_limit,
202                target_address: context.interpreter.input.target_address(),
203                caller: context.interpreter.input.target_address(),
204                bytecode_address: to,
205                known_bytecode: Some((bytecode_hash, bytecode)),
206                value: CallValue::Transfer(value),
207                scheme: CallScheme::CallCode,
208                is_static: context.interpreter.runtime_flag.is_static(),
209                return_memory_offset,
210            },
211        ))));
212}
213
214/// Implements the DELEGATECALL instruction.
215///
216/// Message call with alternative account's code but same sender and value.
217pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
218    mut context: InstructionContext<'_, H, WIRE>,
219) {
220    check!(context.interpreter, HOMESTEAD);
221    popn!([local_gas_limit, to], context.interpreter);
222    let to = Address::from_word(B256::from(to));
223    // Max gas limit is not possible in real ethereum situation.
224    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
225
226    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
227    else {
228        return;
229    };
230
231    let Some((gas_limit, bytecode, bytecode_hash)) =
232        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
233    else {
234        return;
235    };
236
237    // Call host to interact with target contract
238    context
239        .interpreter
240        .bytecode
241        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
242            CallInputs {
243                input: CallInput::SharedBuffer(input),
244                gas_limit,
245                target_address: context.interpreter.input.target_address(),
246                caller: context.interpreter.input.caller_address(),
247                bytecode_address: to,
248                known_bytecode: Some((bytecode_hash, bytecode)),
249                value: CallValue::Apparent(context.interpreter.input.call_value()),
250                scheme: CallScheme::DelegateCall,
251                is_static: context.interpreter.runtime_flag.is_static(),
252                return_memory_offset,
253            },
254        ))));
255}
256
257/// Implements the STATICCALL instruction.
258///
259/// Static message call (cannot modify state).
260pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
261    mut context: InstructionContext<'_, H, WIRE>,
262) {
263    check!(context.interpreter, BYZANTIUM);
264    popn!([local_gas_limit, to], context.interpreter);
265    let to = Address::from_word(B256::from(to));
266    // Max gas limit is not possible in real ethereum situation.
267    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
268
269    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
270    else {
271        return;
272    };
273
274    let Some((gas_limit, bytecode, bytecode_hash)) =
275        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
276    else {
277        return;
278    };
279
280    // Call host to interact with target contract
281    context
282        .interpreter
283        .bytecode
284        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
285            CallInputs {
286                input: CallInput::SharedBuffer(input),
287                gas_limit,
288                target_address: to,
289                caller: context.interpreter.input.target_address(),
290                bytecode_address: to,
291                known_bytecode: Some((bytecode_hash, bytecode)),
292                value: CallValue::Transfer(U256::ZERO),
293                scheme: CallScheme::StaticCall,
294                is_static: true,
295                return_memory_offset,
296            },
297        ))));
298}