Skip to main content

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