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                bytecode,
148                bytecode_hash,
149                value: CallValue::Transfer(value),
150                scheme: CallScheme::Call,
151                is_static: context.interpreter.runtime_flag.is_static(),
152                return_memory_offset,
153            },
154        ))));
155}
156
157/// Implements the CALLCODE instruction.
158///
159/// Message call with alternative account's code.
160pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
161    mut context: InstructionContext<'_, H, WIRE>,
162) {
163    popn!([local_gas_limit, to, value], context.interpreter);
164    let to = Address::from_word(B256::from(to));
165    // Max gas limit is not possible in real ethereum situation.
166    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
167    let has_transfer = !value.is_zero();
168
169    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
170    else {
171        return;
172    };
173
174    let Some((gas_limit, bytecode, bytecode_hash)) =
175        load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)
176    else {
177        return;
178    };
179
180    // Call host to interact with target contract
181    context
182        .interpreter
183        .bytecode
184        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
185            CallInputs {
186                input: CallInput::SharedBuffer(input),
187                gas_limit,
188                target_address: context.interpreter.input.target_address(),
189                caller: context.interpreter.input.target_address(),
190                bytecode_address: to,
191                bytecode,
192                bytecode_hash,
193                value: CallValue::Transfer(value),
194                scheme: CallScheme::CallCode,
195                is_static: context.interpreter.runtime_flag.is_static(),
196                return_memory_offset,
197            },
198        ))));
199}
200
201/// Implements the DELEGATECALL instruction.
202///
203/// Message call with alternative account's code but same sender and value.
204pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
205    mut context: InstructionContext<'_, H, WIRE>,
206) {
207    check!(context.interpreter, HOMESTEAD);
208    popn!([local_gas_limit, to], context.interpreter);
209    let to = Address::from_word(B256::from(to));
210    // Max gas limit is not possible in real ethereum situation.
211    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
212
213    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
214    else {
215        return;
216    };
217
218    let Some((gas_limit, bytecode, bytecode_hash)) =
219        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
220    else {
221        return;
222    };
223
224    // Call host to interact with target contract
225    context
226        .interpreter
227        .bytecode
228        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
229            CallInputs {
230                input: CallInput::SharedBuffer(input),
231                gas_limit,
232                target_address: context.interpreter.input.target_address(),
233                caller: context.interpreter.input.caller_address(),
234                bytecode_address: to,
235                bytecode,
236                bytecode_hash,
237                value: CallValue::Apparent(context.interpreter.input.call_value()),
238                scheme: CallScheme::DelegateCall,
239                is_static: context.interpreter.runtime_flag.is_static(),
240                return_memory_offset,
241            },
242        ))));
243}
244
245/// Implements the STATICCALL instruction.
246///
247/// Static message call (cannot modify state).
248pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
249    mut context: InstructionContext<'_, H, WIRE>,
250) {
251    check!(context.interpreter, BYZANTIUM);
252    popn!([local_gas_limit, to], context.interpreter);
253    let to = Address::from_word(B256::from(to));
254    // Max gas limit is not possible in real ethereum situation.
255    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
256
257    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
258    else {
259        return;
260    };
261
262    let Some((gas_limit, bytecode, bytecode_hash)) =
263        load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
264    else {
265        return;
266    };
267
268    // Call host to interact with target contract
269    context
270        .interpreter
271        .bytecode
272        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
273            CallInputs {
274                input: CallInput::SharedBuffer(input),
275                gas_limit,
276                target_address: to,
277                caller: context.interpreter.input.target_address(),
278                bytecode_address: to,
279                bytecode,
280                bytecode_hash,
281                value: CallValue::Transfer(U256::ZERO),
282                scheme: CallScheme::StaticCall,
283                is_static: true,
284                return_memory_offset,
285            },
286        ))));
287}