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