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, Bytes, 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<const IS_CREATE2: bool, IT: ITy, 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    // Charged upfront on the parent's tracker; `return_create` refunds the same
91    // amount (derived from cfg) on entry and re-records it on a successful commit.
92    if context.host.is_amsterdam_eip8037_enabled() {
93        state_gas!(
94            context.interpreter,
95            context.host.gas_params().create_state_gas()
96        );
97    }
98
99    let mut gas_limit = context.interpreter.gas.remaining();
100
101    // EIP-150: Gas cost changes for IO-heavy operations
102    if context
103        .interpreter
104        .runtime_flag
105        .spec_id()
106        .is_enabled_in(SpecId::TANGERINE)
107    {
108        // Take remaining gas and deduce l64 part of it.
109        gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit);
110    }
111    gas!(context.interpreter, gas_limit);
112
113    // Call host to interact with target contract
114    let create_inputs = CreateInputs::new(
115        context.interpreter.input.target_address(),
116        scheme,
117        value,
118        code,
119        gas_limit,
120        context.interpreter.gas.reservoir(),
121    );
122    context
123        .interpreter
124        .bytecode
125        .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
126            create_inputs,
127        ))));
128    Err(InstructionResult::Suspend)
129}
130
131/// Implements the CALL, CALLCODE, DELEGATECALL, and STATICCALL instructions.
132pub fn call<const KIND: u8, IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
133    use bytecode::opcode::{CALL, CALLCODE, DELEGATECALL, STATICCALL};
134
135    if !matches!(KIND, CALL | CALLCODE | DELEGATECALL | STATICCALL) {
136        unreachable!("invalid call kind")
137    }
138
139    if KIND == DELEGATECALL {
140        check!(context.interpreter, HOMESTEAD);
141    } else if KIND == STATICCALL {
142        check!(context.interpreter, BYZANTIUM);
143    }
144
145    let (local_gas_limit, to, value) = if matches!(KIND, CALL | CALLCODE) {
146        popn!([local_gas_limit, to, value], context.interpreter);
147        (local_gas_limit, to, value)
148    } else {
149        popn!([local_gas_limit, to], context.interpreter);
150        (local_gas_limit, to, U256::ZERO)
151    };
152    let to = to.into_address();
153    // Max gas limit is not possible in real ethereum situation.
154    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
155    let has_transfer = !value.is_zero();
156
157    if KIND == CALL && context.interpreter.runtime_flag.is_static() && has_transfer {
158        return Err(InstructionResult::CallNotAllowedInsideStatic);
159    }
160
161    let (input, return_memory_offset) =
162        get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;
163
164    let is_call = KIND == CALL;
165    let (gas_limit, bytecode, bytecode_hash, charged_new_account_state_gas) =
166        load_acc_and_calc_gas(&mut context, to, has_transfer, is_call, local_gas_limit)?;
167
168    let target_address = if matches!(KIND, CALLCODE | DELEGATECALL) {
169        context.interpreter.input.target_address()
170    } else {
171        to
172    };
173    let caller = if KIND == DELEGATECALL {
174        context.interpreter.input.caller_address()
175    } else {
176        context.interpreter.input.target_address()
177    };
178    let value = if KIND == DELEGATECALL {
179        CallValue::Apparent(context.interpreter.input.call_value())
180    } else {
181        CallValue::Transfer(value)
182    };
183    let scheme = match KIND {
184        CALL => CallScheme::Call,
185        CALLCODE => CallScheme::CallCode,
186        DELEGATECALL => CallScheme::DelegateCall,
187        STATICCALL => CallScheme::StaticCall,
188        _ => unreachable!(),
189    };
190    let is_static = context.interpreter.runtime_flag.is_static() || KIND == STATICCALL;
191
192    // Call host to interact with target contract
193    context
194        .interpreter
195        .bytecode
196        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
197            CallInputs {
198                input: CallInput::SharedBuffer(input),
199                gas_limit,
200                target_address,
201                caller,
202                bytecode_address: to,
203                known_bytecode: (bytecode_hash, bytecode),
204                value,
205                scheme,
206                is_static,
207                return_memory_offset,
208                reservoir: context.interpreter.gas.reservoir(),
209                charged_new_account_state_gas,
210            },
211        ))));
212    Err(InstructionResult::Suspend)
213}