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