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