revm_interpreter/instructions/
contract.rs

1mod call_helpers;
2
3pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_memory};
4
5use crate::{
6    gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD, MIN_CALLEE_GAS},
7    instructions::utility::IntoAddress,
8    interpreter::Interpreter,
9    interpreter_action::FrameInput,
10    interpreter_types::{
11        EofContainer, Immediates, InputsTr, InterpreterTypes, Jumps, LoopControl, MemoryTr,
12        ReturnData, RuntimeFlag, StackTr,
13    },
14    CallInputs, CallScheme, CallValue, CreateInputs, EOFCreateInputs, Host, InstructionResult,
15    InterpreterAction, InterpreterResult,
16};
17use bytecode::eof::{Eof, EofHeader};
18use context_interface::CreateScheme;
19use core::cmp::max;
20use primitives::{hardfork::SpecId, keccak256, Address, Bytes, B256, U256};
21use std::boxed::Box;
22
23/// EOF Create instruction
24pub fn eofcreate<WIRE: InterpreterTypes, H: Host + ?Sized>(
25    interpreter: &mut Interpreter<WIRE>,
26    _host: &mut H,
27) {
28    require_eof!(interpreter);
29    require_non_staticcall!(interpreter);
30    gas!(interpreter, EOF_CREATE_GAS);
31    let initcontainer_index = interpreter.bytecode.read_u8();
32
33    popn!([value, salt, data_offset, data_size], interpreter);
34
35    let container = interpreter
36        .bytecode
37        .eof_container(initcontainer_index as usize)
38        .expect("valid container")
39        .clone();
40
41    // Resize memory and get return range.
42    let Some(input_range) = resize_memory(interpreter, data_offset, data_size) else {
43        return;
44    };
45
46    let input = if !input_range.is_empty() {
47        interpreter.memory.slice(input_range).to_vec().into()
48    } else {
49        Bytes::new()
50    };
51
52    let eof = Eof::decode(container.clone()).expect("Subcontainer is verified");
53
54    if !eof.body.is_data_filled {
55        // Should be always false as it is verified by eof verification.
56        panic!("Panic if data section is not full");
57    }
58
59    // Deduct gas for hash that is needed to calculate address.
60    gas_or_fail!(interpreter, cost_per_word(container.len(), KECCAK256WORD));
61
62    let created_address = interpreter
63        .input
64        .target_address()
65        .create2(salt.to_be_bytes(), keccak256(container));
66
67    let gas_limit = interpreter.control.gas().remaining_63_of_64_parts();
68    gas!(interpreter, gas_limit);
69    // Send container for execution container is preverified.
70    interpreter.control.set_next_action(
71        InterpreterAction::NewFrame(FrameInput::EOFCreate(Box::new(
72            EOFCreateInputs::new_opcode(
73                interpreter.input.target_address(),
74                created_address,
75                value,
76                eof,
77                gas_limit,
78                input,
79            ),
80        ))),
81        InstructionResult::CallOrCreate,
82    );
83
84    interpreter.bytecode.relative_jump(1);
85}
86
87pub fn return_contract<H: Host + ?Sized>(
88    interpreter: &mut Interpreter<impl InterpreterTypes>,
89    _host: &mut H,
90) {
91    if !interpreter.runtime_flag.is_eof_init() {
92        interpreter
93            .control
94            .set_instruction_result(InstructionResult::ReturnContractInNotInitEOF);
95        return;
96    }
97    let deploy_container_index = interpreter.bytecode.read_u8();
98    popn!([aux_data_offset, aux_data_size], interpreter);
99    let aux_data_size = as_usize_or_fail!(interpreter, aux_data_size);
100    let container = interpreter
101        .bytecode
102        .eof_container(deploy_container_index as usize)
103        .expect("valid container")
104        .clone();
105
106    // Convert to EOF so we can check data section size.
107    let (eof_header, _) = EofHeader::decode(&container).expect("valid EOF header");
108
109    let static_aux_size = eof_header.eof_size() - container.len();
110
111    // Important: Offset must be ignored if len is zeros
112    let mut output = if aux_data_size != 0 {
113        let aux_data_offset = as_usize_or_fail!(interpreter, aux_data_offset);
114        resize_memory!(interpreter, aux_data_offset, aux_data_size);
115
116        let aux_slice = interpreter.memory.slice_len(aux_data_offset, aux_data_size);
117
118        [&container, aux_slice.as_ref()].concat()
119    } else {
120        container.to_vec()
121    };
122
123    // `data_size - static_aux_size` give us current data `container` size.
124    // And with `aux_slice` len we can calculate new data size.
125    let new_data_size = eof_header.data_size as usize - static_aux_size + aux_data_size;
126    if new_data_size > 0xFFFF {
127        // Aux data is too big
128        interpreter
129            .control
130            .set_instruction_result(InstructionResult::EofAuxDataOverflow);
131        return;
132    }
133    if new_data_size < eof_header.data_size as usize {
134        // Aux data is too small
135        interpreter
136            .control
137            .set_instruction_result(InstructionResult::EofAuxDataTooSmall);
138        return;
139    }
140    let new_data_size = (new_data_size as u16).to_be_bytes();
141
142    // Set new data size in eof bytes as we know exact index.
143    output[eof_header.data_size_raw_i()..][..2].clone_from_slice(&new_data_size);
144    let output: Bytes = output.into();
145
146    let result = InstructionResult::ReturnContract;
147    let gas = *interpreter.control.gas();
148    interpreter.control.set_next_action(
149        crate::InterpreterAction::Return {
150            result: InterpreterResult {
151                output,
152                gas,
153                result,
154            },
155        },
156        result,
157    );
158}
159
160pub fn extcall_input(interpreter: &mut Interpreter<impl InterpreterTypes>) -> Option<Bytes> {
161    popn!([input_offset, input_size], interpreter, None);
162    let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?;
163
164    if return_memory_offset.is_empty() {
165        return Some(Bytes::new());
166    }
167
168    Some(Bytes::copy_from_slice(
169        interpreter.memory.slice(return_memory_offset).as_ref(),
170    ))
171}
172
173pub fn extcall_gas_calc<WIRE: InterpreterTypes, H: Host + ?Sized>(
174    interpreter: &mut Interpreter<WIRE>,
175    host: &mut H,
176    target: Address,
177    transfers_value: bool,
178) -> Option<u64> {
179    let Some(account_load) = host.load_account_delegated(target) else {
180        interpreter
181            .control
182            .set_instruction_result(InstructionResult::FatalExternalError);
183        return None;
184    };
185
186    // account_load.is_empty will be accounted if there is transfer value
187    // Berlin can be hardcoded as extcall came after berlin.
188    let call_cost = gas::call_cost(
189        interpreter.runtime_flag.spec_id(),
190        transfers_value,
191        account_load,
192    );
193    gas!(interpreter, call_cost, None);
194
195    // Calculate the gas available to callee as caller’s
196    // remaining gas reduced by max(ceil(gas/64), MIN_RETAINED_GAS) (MIN_RETAINED_GAS is 5000).
197    let gas_reduce = max(interpreter.control.gas().remaining() / 64, 5000);
198    let gas_limit = interpreter
199        .control
200        .gas()
201        .remaining()
202        .saturating_sub(gas_reduce);
203
204    // The MIN_CALLEE_GAS rule is a replacement for stipend:
205    // it simplifies the reasoning about the gas costs and is
206    // applied uniformly for all introduced EXT*CALL instructions.
207    //
208    // If Gas available to callee is less than MIN_CALLEE_GAS trigger light failure (Same as Revert).
209    if gas_limit < MIN_CALLEE_GAS {
210        // Push 1 to stack to indicate that call light failed.
211        // It is safe to ignore stack overflow error as we already popped multiple values from stack.
212        let _ = interpreter.stack.push(U256::from(1));
213        interpreter.return_data.clear();
214        // Return none to continue execution.
215        return None;
216    }
217
218    gas!(interpreter, gas_limit, None);
219    Some(gas_limit)
220}
221
222/// Pop target address from stack and check if it is valid.
223///
224/// Valid address has first 12 bytes as zeroes.
225#[inline]
226pub fn pop_extcall_target_address(
227    interpreter: &mut Interpreter<impl InterpreterTypes>,
228) -> Option<Address> {
229    popn!([target_address], interpreter, None);
230    let target_address = B256::from(target_address);
231    // Check if target is left padded with zeroes.
232    if target_address[..12].iter().any(|i| *i != 0) {
233        interpreter
234            .control
235            .set_instruction_result(InstructionResult::InvalidEXTCALLTarget);
236        return None;
237    }
238    // Discard first 12 bytes.
239    Some(Address::from_word(target_address))
240}
241
242pub fn extcall<WIRE: InterpreterTypes, H: Host + ?Sized>(
243    interpreter: &mut Interpreter<WIRE>,
244    host: &mut H,
245) {
246    require_eof!(interpreter);
247
248    // Pop target address
249    let Some(target_address) = pop_extcall_target_address(interpreter) else {
250        return;
251    };
252
253    // Input call
254    let Some(input) = extcall_input(interpreter) else {
255        return;
256    };
257
258    popn!([value], interpreter);
259    let has_transfer = !value.is_zero();
260    if interpreter.runtime_flag.is_static() && has_transfer {
261        interpreter
262            .control
263            .set_instruction_result(InstructionResult::CallNotAllowedInsideStatic);
264        return;
265    }
266
267    let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, has_transfer) else {
268        return;
269    };
270
271    // Call host to interact with target contract
272    interpreter.control.set_next_action(
273        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
274            input,
275            gas_limit,
276            target_address,
277            caller: interpreter.input.target_address(),
278            bytecode_address: target_address,
279            value: CallValue::Transfer(value),
280            scheme: CallScheme::ExtCall,
281            is_static: interpreter.runtime_flag.is_static(),
282            is_eof: true,
283            return_memory_offset: 0..0,
284        }))),
285        InstructionResult::CallOrCreate,
286    );
287}
288
289pub fn extdelegatecall<WIRE: InterpreterTypes, H: Host + ?Sized>(
290    interpreter: &mut Interpreter<WIRE>,
291    host: &mut H,
292) {
293    require_eof!(interpreter);
294
295    // Pop target address
296    let Some(target_address) = pop_extcall_target_address(interpreter) else {
297        return;
298    };
299
300    // Input call
301    let Some(input) = extcall_input(interpreter) else {
302        return;
303    };
304
305    let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
306        return;
307    };
308
309    // Call host to interact with target contract
310    interpreter.control.set_next_action(
311        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
312            input,
313            gas_limit,
314            target_address: interpreter.input.target_address(),
315            caller: interpreter.input.caller_address(),
316            bytecode_address: target_address,
317            value: CallValue::Apparent(interpreter.input.call_value()),
318            scheme: CallScheme::ExtDelegateCall,
319            is_static: interpreter.runtime_flag.is_static(),
320            is_eof: true,
321            return_memory_offset: 0..0,
322        }))),
323        InstructionResult::CallOrCreate,
324    );
325}
326
327pub fn extstaticcall<WIRE: InterpreterTypes, H: Host + ?Sized>(
328    interpreter: &mut Interpreter<WIRE>,
329    host: &mut H,
330) {
331    require_eof!(interpreter);
332
333    // Pop target address
334    let Some(target_address) = pop_extcall_target_address(interpreter) else {
335        return;
336    };
337
338    // Input call
339    let Some(input) = extcall_input(interpreter) else {
340        return;
341    };
342
343    let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
344        return;
345    };
346
347    // Call host to interact with target contract
348    interpreter.control.set_next_action(
349        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
350            input,
351            gas_limit,
352            target_address,
353            caller: interpreter.input.target_address(),
354            bytecode_address: target_address,
355            value: CallValue::Transfer(U256::ZERO),
356            scheme: CallScheme::ExtStaticCall,
357            is_static: true,
358            is_eof: true,
359            return_memory_offset: 0..0,
360        }))),
361        InstructionResult::CallOrCreate,
362    );
363}
364
365pub fn create<WIRE: InterpreterTypes, const IS_CREATE2: bool, H: Host + ?Sized>(
366    interpreter: &mut Interpreter<WIRE>,
367    host: &mut H,
368) {
369    require_non_staticcall!(interpreter);
370
371    // EIP-1014: Skinny CREATE2
372    if IS_CREATE2 {
373        check!(interpreter, PETERSBURG);
374    }
375
376    popn!([value, code_offset, len], interpreter);
377    let len = as_usize_or_fail!(interpreter, len);
378
379    let mut code = Bytes::new();
380    if len != 0 {
381        // EIP-3860: Limit and meter initcode
382        if interpreter
383            .runtime_flag
384            .spec_id()
385            .is_enabled_in(SpecId::SHANGHAI)
386        {
387            // Limit is set as double of max contract bytecode size
388            if len > host.max_initcode_size() {
389                interpreter
390                    .control
391                    .set_instruction_result(InstructionResult::CreateInitCodeSizeLimit);
392                return;
393            }
394            gas!(interpreter, gas::initcode_cost(len));
395        }
396
397        let code_offset = as_usize_or_fail!(interpreter, code_offset);
398        resize_memory!(interpreter, code_offset, len);
399        code = Bytes::copy_from_slice(interpreter.memory.slice_len(code_offset, len).as_ref());
400    }
401
402    // EIP-1014: Skinny CREATE2
403    let scheme = if IS_CREATE2 {
404        popn!([salt], interpreter);
405        // SAFETY: `len` is reasonable in size as gas for it is already deducted.
406        gas_or_fail!(interpreter, gas::create2_cost(len));
407        CreateScheme::Create2 { salt }
408    } else {
409        gas!(interpreter, gas::CREATE);
410        CreateScheme::Create
411    };
412
413    let mut gas_limit = interpreter.control.gas().remaining();
414
415    // EIP-150: Gas cost changes for IO-heavy operations
416    if interpreter
417        .runtime_flag
418        .spec_id()
419        .is_enabled_in(SpecId::TANGERINE)
420    {
421        // Take remaining gas and deduce l64 part of it.
422        gas_limit -= gas_limit / 64
423    }
424    gas!(interpreter, gas_limit);
425
426    // Call host to interact with target contract
427    interpreter.control.set_next_action(
428        InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs {
429            caller: interpreter.input.target_address(),
430            scheme,
431            value,
432            init_code: code,
433            gas_limit,
434        }))),
435        InstructionResult::CallOrCreate,
436    );
437}
438
439pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
440    interpreter: &mut Interpreter<WIRE>,
441    host: &mut H,
442) {
443    popn!([local_gas_limit, to, value], interpreter);
444    let to = to.into_address();
445    // Max gas limit is not possible in real ethereum situation.
446    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
447
448    let has_transfer = !value.is_zero();
449    if interpreter.runtime_flag.is_static() && has_transfer {
450        interpreter
451            .control
452            .set_instruction_result(InstructionResult::CallNotAllowedInsideStatic);
453        return;
454    }
455
456    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
457        return;
458    };
459
460    let Some(account_load) = host.load_account_delegated(to) else {
461        interpreter
462            .control
463            .set_instruction_result(InstructionResult::FatalExternalError);
464        return;
465    };
466
467    let Some(mut gas_limit) =
468        calc_call_gas(interpreter, account_load, has_transfer, local_gas_limit)
469    else {
470        return;
471    };
472
473    gas!(interpreter, gas_limit);
474
475    // Add call stipend if there is value to be transferred.
476    if has_transfer {
477        gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
478    }
479
480    // Call host to interact with target contract
481    interpreter.control.set_next_action(
482        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
483            input,
484            gas_limit,
485            target_address: to,
486            caller: interpreter.input.target_address(),
487            bytecode_address: to,
488            value: CallValue::Transfer(value),
489            scheme: CallScheme::Call,
490            is_static: interpreter.runtime_flag.is_static(),
491            is_eof: false,
492            return_memory_offset,
493        }))),
494        InstructionResult::CallOrCreate,
495    );
496}
497
498pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
499    interpreter: &mut Interpreter<WIRE>,
500    host: &mut H,
501) {
502    popn!([local_gas_limit, to, value], interpreter);
503    let to = Address::from_word(B256::from(to));
504    // Max gas limit is not possible in real ethereum situation.
505    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
506
507    //pop!(interpreter, value);
508    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
509        return;
510    };
511
512    let Some(mut load) = host.load_account_delegated(to) else {
513        interpreter
514            .control
515            .set_instruction_result(InstructionResult::FatalExternalError);
516        return;
517    };
518
519    // Set `is_empty` to false as we are not creating this account.
520    load.is_empty = false;
521    let Some(mut gas_limit) = calc_call_gas(interpreter, load, !value.is_zero(), local_gas_limit)
522    else {
523        return;
524    };
525
526    gas!(interpreter, gas_limit);
527
528    // Add call stipend if there is value to be transferred.
529    if !value.is_zero() {
530        gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
531    }
532
533    // Call host to interact with target contract
534    interpreter.control.set_next_action(
535        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
536            input,
537            gas_limit,
538            target_address: interpreter.input.target_address(),
539            caller: interpreter.input.target_address(),
540            bytecode_address: to,
541            value: CallValue::Transfer(value),
542            scheme: CallScheme::CallCode,
543            is_static: interpreter.runtime_flag.is_static(),
544            is_eof: false,
545            return_memory_offset,
546        }))),
547        InstructionResult::CallOrCreate,
548    );
549}
550
551pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
552    interpreter: &mut Interpreter<WIRE>,
553    host: &mut H,
554) {
555    check!(interpreter, HOMESTEAD);
556    popn!([local_gas_limit, to], interpreter);
557    let to = Address::from_word(B256::from(to));
558    // Max gas limit is not possible in real ethereum situation.
559    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
560
561    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
562        return;
563    };
564
565    let Some(mut load) = host.load_account_delegated(to) else {
566        interpreter
567            .control
568            .set_instruction_result(InstructionResult::FatalExternalError);
569        return;
570    };
571
572    // Set is_empty to false as we are not creating this account.
573    load.is_empty = false;
574    let Some(gas_limit) = calc_call_gas(interpreter, load, false, local_gas_limit) else {
575        return;
576    };
577
578    gas!(interpreter, gas_limit);
579
580    // Call host to interact with target contract
581    interpreter.control.set_next_action(
582        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
583            input,
584            gas_limit,
585            target_address: interpreter.input.target_address(),
586            caller: interpreter.input.caller_address(),
587            bytecode_address: to,
588            value: CallValue::Apparent(interpreter.input.call_value()),
589            scheme: CallScheme::DelegateCall,
590            is_static: interpreter.runtime_flag.is_static(),
591            is_eof: false,
592            return_memory_offset,
593        }))),
594        InstructionResult::CallOrCreate,
595    );
596}
597
598pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
599    interpreter: &mut Interpreter<WIRE>,
600    host: &mut H,
601) {
602    check!(interpreter, BYZANTIUM);
603    popn!([local_gas_limit, to], interpreter);
604    let to = Address::from_word(B256::from(to));
605    // Max gas limit is not possible in real ethereum situation.
606    let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
607
608    let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
609        return;
610    };
611
612    let Some(mut load) = host.load_account_delegated(to) else {
613        interpreter
614            .control
615            .set_instruction_result(InstructionResult::FatalExternalError);
616        return;
617    };
618    // Set `is_empty` to false as we are not creating this account.
619    load.is_empty = false;
620    let Some(gas_limit) = calc_call_gas(interpreter, load, false, local_gas_limit) else {
621        return;
622    };
623    gas!(interpreter, gas_limit);
624
625    // Call host to interact with target contract
626    interpreter.control.set_next_action(
627        InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
628            input,
629            gas_limit,
630            target_address: to,
631            caller: interpreter.input.target_address(),
632            bytecode_address: to,
633            value: CallValue::Transfer(U256::ZERO),
634            scheme: CallScheme::StaticCall,
635            is_static: true,
636            is_eof: false,
637            return_memory_offset,
638        }))),
639        InstructionResult::CallOrCreate,
640    );
641}