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