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, ReturnData, 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    // Build the inputs before the gas split so the created address (and the
90    // CREATE2 init-code hash) is computed once and cached for frame creation.
91    let mut create_inputs = CreateInputs::new(
92        context.interpreter.input.target_address(),
93        scheme,
94        value,
95        code,
96        0,
97        0,
98    );
99
100    // State gas for account creation + contract metadata (EIP-8037).
101    if context.host.is_amsterdam_eip8037_enabled() {
102        // The charge is conditional at access, applied in
103        // the creating frame before the 63/64 split. The destination is read
104        // (and charged for) only after the pre-access checks — endowment
105        // balance and sender nonce overflow — pass; failing those pushes 0
106        // without touching the destination. (The call-depth pre-access check
107        // lives at frame creation; its failure path refunds the charge.)
108        let caller = create_inputs.caller();
109        let caller_info = context
110            .host
111            .load_account_info_skip_cold_load(caller, false, false)?;
112        let caller_balance = caller_info.account.balance;
113        let caller_nonce = caller_info.account.nonce;
114        if caller_balance < value || caller_nonce == u64::MAX {
115            context.interpreter.return_data.clear();
116            push!(context.interpreter, U256::ZERO);
117            return Ok(());
118        }
119
120        // Single read of the destination: decides the charge by existence
121        // alone (independently of the collision outcome checked at frame
122        // creation) and adds it to the accessed addresses.
123        let created_address = create_inputs.created_address(caller_nonce);
124        let destination_alive = !context
125            .host
126            .load_account_info_skip_cold_load(created_address, false, false)?
127            .is_empty;
128        if !destination_alive {
129            state_gas!(
130                context.interpreter,
131                context.host.gas_params().create_state_gas()
132            );
133            create_inputs.set_charged_create_state_gas(true);
134        }
135    }
136
137    let mut gas_limit = context.interpreter.gas.remaining();
138
139    // EIP-150: Gas cost changes for IO-heavy operations
140    if context
141        .interpreter
142        .runtime_flag
143        .spec_id()
144        .is_enabled_in(SpecId::TANGERINE)
145    {
146        // Take remaining gas and deduce l64 part of it.
147        gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit);
148    }
149    gas!(context.interpreter, gas_limit);
150
151    create_inputs.set_gas_limit(gas_limit);
152    create_inputs.set_reservoir(context.interpreter.gas.reservoir());
153    context
154        .interpreter
155        .bytecode
156        .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
157            create_inputs,
158        ))));
159    Err(InstructionResult::Suspend)
160}
161
162/// Implements the CALL, CALLCODE, DELEGATECALL, and STATICCALL instructions.
163pub fn call<const KIND: u8, IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
164    use bytecode::opcode::{CALL, CALLCODE, DELEGATECALL, STATICCALL};
165
166    if !matches!(KIND, CALL | CALLCODE | DELEGATECALL | STATICCALL) {
167        unreachable!("invalid call kind")
168    }
169
170    if KIND == DELEGATECALL {
171        check!(context.interpreter, HOMESTEAD);
172    } else if KIND == STATICCALL {
173        check!(context.interpreter, BYZANTIUM);
174    }
175
176    let (local_gas_limit, to, value) = if matches!(KIND, CALL | CALLCODE) {
177        popn!([local_gas_limit, to, value], context.interpreter);
178        (local_gas_limit, to, value)
179    } else {
180        popn!([local_gas_limit, to], context.interpreter);
181        (local_gas_limit, to, U256::ZERO)
182    };
183    let to = to.into_address();
184    // Max gas limit is not possible in real ethereum situation.
185    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
186    let has_transfer = !value.is_zero();
187
188    if KIND == CALL && context.interpreter.runtime_flag.is_static() && has_transfer {
189        return Err(InstructionResult::CallNotAllowedInsideStatic);
190    }
191
192    let (input, return_memory_offset) =
193        get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;
194
195    let is_call = KIND == CALL;
196    let (gas_limit, bytecode, bytecode_hash, charged_new_account_state_gas) =
197        load_acc_and_calc_gas(&mut context, to, has_transfer, is_call, local_gas_limit)?;
198
199    let target_address = if matches!(KIND, CALLCODE | DELEGATECALL) {
200        context.interpreter.input.target_address()
201    } else {
202        to
203    };
204    let caller = if KIND == DELEGATECALL {
205        context.interpreter.input.caller_address()
206    } else {
207        context.interpreter.input.target_address()
208    };
209    let value = if KIND == DELEGATECALL {
210        CallValue::Apparent(context.interpreter.input.call_value())
211    } else {
212        CallValue::Transfer(value)
213    };
214    let scheme = match KIND {
215        CALL => CallScheme::Call,
216        CALLCODE => CallScheme::CallCode,
217        DELEGATECALL => CallScheme::DelegateCall,
218        STATICCALL => CallScheme::StaticCall,
219        _ => unreachable!(),
220    };
221    let is_static = context.interpreter.runtime_flag.is_static() || KIND == STATICCALL;
222
223    // Call host to interact with target contract
224    context
225        .interpreter
226        .bytecode
227        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
228            CallInputs {
229                input: CallInput::SharedBuffer(input),
230                gas_limit,
231                target_address,
232                caller,
233                bytecode_address: to,
234                known_bytecode: (bytecode_hash, bytecode),
235                value,
236                scheme,
237                is_static,
238                return_memory_offset,
239                reservoir: context.interpreter.gas.reservoir(),
240                charged_new_account_state_gas,
241            },
242        ))));
243    Err(InstructionResult::Suspend)
244}