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::{constants::CALL_STACK_LIMIT, 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, sender nonce overflow, and call depth — pass; failing those
106        // pushes 0 without touching the destination, keeping it out of the
107        // EIP-7928 block access list and the warm set.
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
115            || caller_nonce == u64::MAX
116            || context.interpreter.input.depth() + 1 > CALL_STACK_LIMIT as usize
117        {
118            context.interpreter.return_data.clear();
119            push!(context.interpreter, U256::ZERO);
120            return Ok(());
121        }
122
123        // Single read of the destination: decides the charge by existence
124        // alone (independently of the collision outcome checked at frame
125        // creation) and adds it to the accessed addresses.
126        let created_address = create_inputs.created_address(caller_nonce);
127        let destination_alive = !context
128            .host
129            .load_account_info_skip_cold_load(created_address, false, false)?
130            .is_empty;
131        if !destination_alive {
132            state_gas!(
133                context.interpreter,
134                context.host.gas_params().create_state_gas()
135            );
136            create_inputs.set_charged_create_state_gas(true);
137        }
138    }
139
140    let mut gas_limit = context.interpreter.gas.remaining();
141
142    // EIP-150: Gas cost changes for IO-heavy operations
143    if context
144        .interpreter
145        .runtime_flag
146        .spec_id()
147        .is_enabled_in(SpecId::TANGERINE)
148    {
149        // Take remaining gas and deduce l64 part of it.
150        gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit);
151    }
152    gas!(context.interpreter, gas_limit);
153
154    create_inputs.set_gas_limit(gas_limit);
155    create_inputs.set_reservoir(context.interpreter.gas.reservoir());
156    context
157        .interpreter
158        .bytecode
159        .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
160            create_inputs,
161        ))));
162    Err(InstructionResult::Suspend)
163}
164
165/// Implements the CALL, CALLCODE, DELEGATECALL, and STATICCALL instructions.
166pub fn call<const KIND: u8, IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
167    use bytecode::opcode::{CALL, CALLCODE, DELEGATECALL, STATICCALL};
168
169    if !matches!(KIND, CALL | CALLCODE | DELEGATECALL | STATICCALL) {
170        unreachable!("invalid call kind")
171    }
172
173    if KIND == DELEGATECALL {
174        check!(context.interpreter, HOMESTEAD);
175    } else if KIND == STATICCALL {
176        check!(context.interpreter, BYZANTIUM);
177    }
178
179    let (local_gas_limit, to, value) = if matches!(KIND, CALL | CALLCODE) {
180        popn!([local_gas_limit, to, value], context.interpreter);
181        (local_gas_limit, to, value)
182    } else {
183        popn!([local_gas_limit, to], context.interpreter);
184        (local_gas_limit, to, U256::ZERO)
185    };
186    let to = to.into_address();
187    // Max gas limit is not possible in real ethereum situation.
188    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
189    let has_transfer = !value.is_zero();
190
191    if KIND == CALL && context.interpreter.runtime_flag.is_static() && has_transfer {
192        return Err(InstructionResult::CallNotAllowedInsideStatic);
193    }
194
195    let (input, return_memory_offset) =
196        get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;
197
198    let is_call = KIND == CALL;
199    let (gas_limit, bytecode, bytecode_hash, charged_new_account_state_gas) =
200        load_acc_and_calc_gas(&mut context, to, has_transfer, is_call, local_gas_limit)?;
201
202    let target_address = if matches!(KIND, CALLCODE | DELEGATECALL) {
203        context.interpreter.input.target_address()
204    } else {
205        to
206    };
207    let caller = if KIND == DELEGATECALL {
208        context.interpreter.input.caller_address()
209    } else {
210        context.interpreter.input.target_address()
211    };
212    let value = if KIND == DELEGATECALL {
213        CallValue::Apparent(context.interpreter.input.call_value())
214    } else {
215        CallValue::Transfer(value)
216    };
217    let scheme = match KIND {
218        CALL => CallScheme::Call,
219        CALLCODE => CallScheme::CallCode,
220        DELEGATECALL => CallScheme::DelegateCall,
221        STATICCALL => CallScheme::StaticCall,
222        _ => unreachable!(),
223    };
224    let is_static = context.interpreter.runtime_flag.is_static() || KIND == STATICCALL;
225
226    // Call host to interact with target contract
227    context
228        .interpreter
229        .bytecode
230        .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
231            CallInputs {
232                input: CallInput::SharedBuffer(input),
233                gas_limit,
234                target_address,
235                caller,
236                bytecode_address: to,
237                known_bytecode: (bytecode_hash, bytecode),
238                value,
239                scheme,
240                is_static,
241                return_memory_offset,
242                reservoir: context.interpreter.gas.reservoir(),
243                charged_new_account_state_gas,
244            },
245        ))));
246    Err(InstructionResult::Suspend)
247}
248
249#[cfg(test)]
250mod tests {
251    use crate::{
252        host::DummyHost,
253        instructions::{gas_table, instruction_table},
254        interpreter::{EthInterpreter, ExtBytecode, InputsImpl, SharedMemory},
255        Interpreter, InterpreterAction,
256    };
257    use bytecode::opcode::*;
258    use bytecode::Bytecode;
259    use primitives::{constants::CALL_STACK_LIMIT, hardfork::SpecId, Bytes, U256};
260
261    #[test]
262    fn create_too_deep_pushes_zero_without_destination_access_eip8037() {
263        // EIP-8037: the depth pre-check fails in the opcode itself, pushing 0 without
264        // requesting a create frame or reading the destination account, which would
265        // otherwise leak the address into the EIP-7928 block access list.
266        let bytecode =
267            Bytecode::new_raw(Bytes::copy_from_slice(&[PUSH0, PUSH0, PUSH0, CREATE, STOP]));
268        let mut interpreter = Interpreter::<EthInterpreter>::new(
269            SharedMemory::new(),
270            ExtBytecode::new(bytecode),
271            InputsImpl {
272                depth: CALL_STACK_LIMIT as usize,
273                ..Default::default()
274            },
275            false,
276            SpecId::AMSTERDAM,
277            1_000_000,
278        );
279        let table = instruction_table::<EthInterpreter, DummyHost>();
280        let gas = gas_table();
281        let mut host = DummyHost::new(SpecId::AMSTERDAM);
282        let action = interpreter.run_plain(&table, &gas, &mut host);
283        assert!(!matches!(action, InterpreterAction::NewFrame(_)));
284        assert_eq!(interpreter.stack.len(), 1);
285        assert_eq!(interpreter.stack.data()[0], U256::ZERO);
286    }
287}