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, new_account_cost, 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!(context.interpreter, gas::initcode_cost(len));
55        }
56
57        let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
58        resize_memory!(context.interpreter, code_offset, len);
59        code = Bytes::copy_from_slice(
60            context
61                .interpreter
62                .memory
63                .slice_len(code_offset, len)
64                .as_ref(),
65        );
66    }
67
68    // EIP-1014: Skinny CREATE2
69    let scheme = if IS_CREATE2 {
70        popn!([salt], context.interpreter);
71        // SAFETY: `len` is reasonable in size as gas for it is already deducted.
72        gas_or_fail!(context.interpreter, gas::create2_cost(len));
73        CreateScheme::Create2 { salt }
74    } else {
75        gas!(context.interpreter, gas::CREATE);
76        CreateScheme::Create
77    };
78
79    let mut gas_limit = context.interpreter.gas.remaining();
80
81    // EIP-150: Gas cost changes for IO-heavy operations
82    if context
83        .interpreter
84        .runtime_flag
85        .spec_id()
86        .is_enabled_in(SpecId::TANGERINE)
87    {
88        // Take remaining gas and deduce l64 part of it.
89        gas_limit -= gas_limit / 64
90    }
91    gas!(context.interpreter, gas_limit);
92
93    // Call host to interact with target contract
94    context
95        .interpreter
96        .bytecode
97        .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
98            CreateInputs {
99                caller: context.interpreter.input.target_address(),
100                scheme,
101                value,
102                init_code: code,
103                gas_limit,
104            },
105        ))));
106}
107
108/// Implements the CALL instruction.
109///
110/// Message call with value transfer to another account.
111pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
112    mut context: InstructionContext<'_, H, WIRE>,
113) {
114    popn!([local_gas_limit, to, value], context.interpreter);
115    let to = to.into_address();
116    // Max gas limit is not possible in real ethereum situation.
117    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
118    let has_transfer = !value.is_zero();
119
120    if context.interpreter.runtime_flag.is_static() && has_transfer {
121        context
122            .interpreter
123            .halt(InstructionResult::CallNotAllowedInsideStatic);
124        return;
125    }
126    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
127    else {
128        return;
129    };
130    let Some((gas_limit, bytecode, bytecode_hash)) =
131        load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit)
132    else {
133        return;
134    };
135
136    // Call host to interact with target contract
137    context
138        .interpreter
139        .bytecode
140        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
141            CallInputs {
142                input: CallInput::SharedBuffer(input),
143                gas_limit,
144                target_address: to,
145                caller: context.interpreter.input.target_address(),
146                bytecode_address: to,
147                known_bytecode: Some((bytecode_hash, bytecode)),
148                value: CallValue::Transfer(value),
149                scheme: CallScheme::Call,
150                is_static: context.interpreter.runtime_flag.is_static(),
151                return_memory_offset,
152            },
153        ))));
154}
155
156/// Implements the CALLCODE instruction.
157///
158/// Message call with alternative account's code.
159pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
160    mut context: InstructionContext<'_, H, WIRE>,
161) {
162    popn!([local_gas_limit, to, value], context.interpreter);
163    let to = Address::from_word(B256::from(to));
164    // Max gas limit is not possible in real ethereum situation.
165    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
166    let has_transfer = !value.is_zero();
167
168    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
169    else {
170        return;
171    };
172
173    let Some((gas_limit, bytecode, bytecode_hash)) =
174        load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)
175    else {
176        return;
177    };
178
179    // Call host to interact with target contract
180    context
181        .interpreter
182        .bytecode
183        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
184            CallInputs {
185                input: CallInput::SharedBuffer(input),
186                gas_limit,
187                target_address: context.interpreter.input.target_address(),
188                caller: context.interpreter.input.target_address(),
189                bytecode_address: to,
190                known_bytecode: Some((bytecode_hash, bytecode)),
191                value: CallValue::Transfer(value),
192                scheme: CallScheme::CallCode,
193                is_static: context.interpreter.runtime_flag.is_static(),
194                return_memory_offset,
195            },
196        ))));
197}
198
199/// Implements the DELEGATECALL instruction.
200///
201/// Message call with alternative account's code but same sender and value.
202pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
203    mut context: InstructionContext<'_, H, WIRE>,
204) {
205    check!(context.interpreter, HOMESTEAD);
206    popn!([local_gas_limit, to], context.interpreter);
207    let to = Address::from_word(B256::from(to));
208    // Max gas limit is not possible in real ethereum situation.
209    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
210
211    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
212    else {
213        return;
214    };
215
216    let Some((gas_limit, bytecode, bytecode_hash)) =
217        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
218    else {
219        return;
220    };
221
222    // Call host to interact with target contract
223    context
224        .interpreter
225        .bytecode
226        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
227            CallInputs {
228                input: CallInput::SharedBuffer(input),
229                gas_limit,
230                target_address: context.interpreter.input.target_address(),
231                caller: context.interpreter.input.caller_address(),
232                bytecode_address: to,
233                known_bytecode: Some((bytecode_hash, bytecode)),
234                value: CallValue::Apparent(context.interpreter.input.call_value()),
235                scheme: CallScheme::DelegateCall,
236                is_static: context.interpreter.runtime_flag.is_static(),
237                return_memory_offset,
238            },
239        ))));
240}
241
242/// Implements the STATICCALL instruction.
243///
244/// Static message call (cannot modify state).
245pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
246    mut context: InstructionContext<'_, H, WIRE>,
247) {
248    check!(context.interpreter, BYZANTIUM);
249    popn!([local_gas_limit, to], context.interpreter);
250    let to = Address::from_word(B256::from(to));
251    // Max gas limit is not possible in real ethereum situation.
252    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
253
254    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
255    else {
256        return;
257    };
258
259    let Some((gas_limit, bytecode, bytecode_hash)) =
260        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
261    else {
262        return;
263    };
264
265    // Call host to interact with target contract
266    context
267        .interpreter
268        .bytecode
269        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
270            CallInputs {
271                input: CallInput::SharedBuffer(input),
272                gas_limit,
273                target_address: to,
274                caller: context.interpreter.input.target_address(),
275                bytecode_address: to,
276                known_bytecode: Some((bytecode_hash, bytecode)),
277                value: CallValue::Transfer(U256::ZERO),
278                scheme: CallScheme::StaticCall,
279                is_static: true,
280                return_memory_offset,
281            },
282        ))));
283}