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
23use crate::InstructionContext;
24
25pub fn eofcreate<WIRE: InterpreterTypes, H: Host + ?Sized>(
27 context: InstructionContext<'_, H, WIRE>,
28) {
29 require_eof!(context.interpreter);
30 require_non_staticcall!(context.interpreter);
31 gas!(context.interpreter, EOF_CREATE_GAS);
32 let initcontainer_index = context.interpreter.bytecode.read_u8();
33
34 popn!([salt, input_offset, input_size, value], context.interpreter);
35
36 let container = context
37 .interpreter
38 .bytecode
39 .eof_container(initcontainer_index as usize)
40 .expect("valid container")
41 .clone();
42
43 let Some(input_range) = resize_memory(context.interpreter, input_offset, input_size) else {
45 return;
46 };
47
48 let input = if !input_range.is_empty() {
49 context
50 .interpreter
51 .memory
52 .slice(input_range)
53 .to_vec()
54 .into()
55 } else {
56 Bytes::new()
57 };
58
59 let eof = Eof::decode(container.clone()).expect("Subcontainer is verified");
60
61 if !eof.body.is_data_filled {
62 panic!("Panic if data section is not full");
64 }
65
66 let created_address = new_eof_address(
68 context.interpreter.input.target_address(),
69 salt.to_be_bytes().into(),
70 );
71
72 let gas_limit = context.interpreter.gas.remaining_63_of_64_parts();
73 gas!(context.interpreter, gas_limit);
74
75 context
77 .interpreter
78 .bytecode
79 .set_action(InterpreterAction::NewFrame(FrameInput::EOFCreate(
80 Box::new(EOFCreateInputs::new_opcode(
81 context.interpreter.input.target_address(),
82 created_address,
83 value,
84 eof,
85 gas_limit,
86 CallInput::Bytes(input),
87 )),
88 )));
89
90 context.interpreter.bytecode.relative_jump(1);
92}
93
94pub fn txcreate<WIRE: InterpreterTypes, H: Host + ?Sized>(
96 context: InstructionContext<'_, H, WIRE>,
97) {
98 require_eof!(context.interpreter);
100 require_non_staticcall!(context.interpreter);
101 gas!(context.interpreter, EOF_CREATE_GAS);
102
103 popn!(
105 [tx_initcode_hash, salt, input_offset, input_size, value],
106 context.interpreter
107 );
108 let tx_initcode_hash = B256::from(tx_initcode_hash);
109
110 let Some(input_range) = resize_memory(context.interpreter, input_offset, input_size) else {
112 return;
113 };
114
115 let Some(initcode) = context.host.initcode_by_hash(tx_initcode_hash) else {
117 push!(context.interpreter, U256::ZERO);
119 return;
120 };
121
122 let input = if !input_range.is_empty() {
124 context
125 .interpreter
126 .memory
127 .slice(input_range)
128 .to_vec()
129 .into()
130 } else {
131 Bytes::new()
132 };
133
134 let eof = Eof::decode(initcode).expect("Subcontainer is verified");
136
137 let created_address = new_eof_address(
139 context.interpreter.input.target_address(),
140 salt.to_be_bytes().into(),
141 );
142
143 let gas_limit = context.interpreter.gas.remaining_63_of_64_parts();
144 gas!(context.interpreter, gas_limit);
145
146 context
148 .interpreter
149 .bytecode
150 .set_action(InterpreterAction::NewFrame(FrameInput::EOFCreate(
151 Box::new(EOFCreateInputs::new_opcode(
152 context.interpreter.input.target_address(),
153 created_address,
154 value,
155 eof,
156 gas_limit,
157 CallInput::Bytes(input),
158 )),
159 )));
160}
161
162pub fn return_contract<H: Host + ?Sized>(
163 context: InstructionContext<'_, H, impl InterpreterTypes>,
164) {
165 if !context.interpreter.runtime_flag.is_eof_init() {
166 context
167 .interpreter
168 .halt(InstructionResult::ReturnContractInNotInitEOF);
169 return;
170 }
171 let deploy_container_index = context.interpreter.bytecode.read_u8();
172 popn!([aux_data_offset, aux_data_size], context.interpreter);
173 let aux_data_size = as_usize_or_fail!(context.interpreter, aux_data_size);
174 let container = context
175 .interpreter
176 .bytecode
177 .eof_container(deploy_container_index as usize)
178 .expect("valid container")
179 .clone();
180
181 let (eof_header, _) = EofHeader::decode(&container).expect("valid EOF header");
183
184 let static_aux_size = eof_header.eof_size() - container.len();
185
186 let mut output = if aux_data_size != 0 {
188 let aux_data_offset = as_usize_or_fail!(context.interpreter, aux_data_offset);
189 resize_memory!(context.interpreter, aux_data_offset, aux_data_size);
190
191 let aux_slice = context
192 .interpreter
193 .memory
194 .slice_len(aux_data_offset, aux_data_size);
195
196 [&container, aux_slice.as_ref()].concat()
197 } else {
198 container.to_vec()
199 };
200
201 let new_data_size = eof_header.data_size as usize - static_aux_size + aux_data_size;
204 if new_data_size > 0xFFFF {
205 context
207 .interpreter
208 .halt(InstructionResult::EofAuxDataOverflow);
209 return;
210 }
211 if new_data_size < eof_header.data_size as usize {
212 context
214 .interpreter
215 .halt(InstructionResult::EofAuxDataTooSmall);
216 return;
217 }
218 let new_data_size = (new_data_size as u16).to_be_bytes();
219
220 output[eof_header.data_size_raw_i()..][..2].clone_from_slice(&new_data_size);
222 let output: Bytes = output.into();
223
224 let result = InstructionResult::ReturnContract;
225 let gas = context.interpreter.gas;
226 context
227 .interpreter
228 .bytecode
229 .set_action(InterpreterAction::Return(InterpreterResult {
230 output,
231 gas,
232 result,
233 }));
234}
235
236pub fn extcall_input(interpreter: &mut Interpreter<impl InterpreterTypes>) -> Option<Bytes> {
237 popn!([input_offset, input_size], interpreter, None);
238 let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?;
239
240 if return_memory_offset.is_empty() {
241 return Some(Bytes::new());
242 }
243
244 Some(Bytes::copy_from_slice(
245 interpreter.memory.slice(return_memory_offset).as_ref(),
246 ))
247}
248
249#[inline]
250pub fn extcall_gas_calc<WIRE: InterpreterTypes, H: Host + ?Sized>(
251 context: &mut InstructionContext<'_, H, WIRE>,
252 target: Address,
253 transfers_value: bool,
254) -> Option<u64> {
255 let Some(account_load) = context.host.load_account_delegated(target) else {
256 context
257 .interpreter
258 .halt(InstructionResult::FatalExternalError);
259 return None;
260 };
261
262 let call_cost = gas::call_cost(
265 context.interpreter.runtime_flag.spec_id(),
266 transfers_value,
267 account_load,
268 );
269 gas!(context.interpreter, call_cost, None);
270
271 let gas_reduce = max(context.interpreter.gas.remaining() / 64, 5000);
274 let gas_limit = context
275 .interpreter
276 .gas
277 .remaining()
278 .saturating_sub(gas_reduce);
279
280 if gas_limit < MIN_CALLEE_GAS {
286 let _ = context.interpreter.stack.push(U256::from(1));
289 context.interpreter.return_data.clear();
290 return None;
292 }
293
294 gas!(context.interpreter, gas_limit, None);
295 Some(gas_limit)
296}
297
298#[inline]
302pub fn pop_extcall_target_address(
303 interpreter: &mut Interpreter<impl InterpreterTypes>,
304) -> Option<Address> {
305 popn!([target_address], interpreter, None);
306 let target_address = B256::from(target_address);
307 if target_address[..12].iter().any(|i| *i != 0) {
309 interpreter.halt(InstructionResult::InvalidEXTCALLTarget);
310 return None;
311 }
312 Some(Address::from_word(target_address))
314}
315
316pub fn extcall<WIRE: InterpreterTypes, H: Host + ?Sized>(
317 mut context: InstructionContext<'_, H, WIRE>,
318) {
319 require_eof!(context.interpreter);
320
321 let Some(target_address) = pop_extcall_target_address(context.interpreter) else {
323 return;
324 };
325
326 let Some(input) = extcall_input(context.interpreter) else {
328 return;
329 };
330
331 popn!([value], context.interpreter);
332 let has_transfer = !value.is_zero();
333 if context.interpreter.runtime_flag.is_static() && has_transfer {
334 context
335 .interpreter
336 .halt(InstructionResult::CallNotAllowedInsideStatic);
337 return;
338 }
339
340 let Some(gas_limit) = extcall_gas_calc(&mut context, target_address, has_transfer) else {
341 return;
342 };
343
344 context
346 .interpreter
347 .bytecode
348 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
349 CallInputs {
350 input: CallInput::Bytes(input),
351 gas_limit,
352 target_address,
353 caller: context.interpreter.input.target_address(),
354 bytecode_address: target_address,
355 value: CallValue::Transfer(value),
356 scheme: CallScheme::ExtCall,
357 is_static: context.interpreter.runtime_flag.is_static(),
358 is_eof: true,
359 return_memory_offset: 0..0,
360 },
361 ))));
362}
363
364pub fn extdelegatecall<WIRE: InterpreterTypes, H: Host + ?Sized>(
365 mut context: InstructionContext<'_, H, WIRE>,
366) {
367 require_eof!(context.interpreter);
368
369 let Some(target_address) = pop_extcall_target_address(context.interpreter) else {
371 return;
372 };
373
374 let Some(input) = extcall_input(context.interpreter) else {
376 return;
377 };
378
379 let Some(gas_limit) = extcall_gas_calc(&mut context, target_address, false) else {
380 return;
381 };
382
383 context
385 .interpreter
386 .bytecode
387 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
388 CallInputs {
389 input: CallInput::Bytes(input),
390 gas_limit,
391 target_address: context.interpreter.input.target_address(),
392 caller: context.interpreter.input.caller_address(),
393 bytecode_address: target_address,
394 value: CallValue::Apparent(context.interpreter.input.call_value()),
395 scheme: CallScheme::ExtDelegateCall,
396 is_static: context.interpreter.runtime_flag.is_static(),
397 is_eof: true,
398 return_memory_offset: 0..0,
399 },
400 ))));
401}
402
403pub fn extstaticcall<WIRE: InterpreterTypes, H: Host + ?Sized>(
404 mut context: InstructionContext<'_, H, WIRE>,
405) {
406 require_eof!(context.interpreter);
407
408 let Some(target_address) = pop_extcall_target_address(context.interpreter) else {
410 return;
411 };
412
413 let Some(input) = extcall_input(context.interpreter) else {
415 return;
416 };
417
418 let Some(gas_limit) = extcall_gas_calc(&mut context, target_address, false) else {
419 return;
420 };
421
422 context
424 .interpreter
425 .bytecode
426 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
427 CallInputs {
428 input: CallInput::Bytes(input),
429 gas_limit,
430 target_address,
431 caller: context.interpreter.input.target_address(),
432 bytecode_address: target_address,
433 value: CallValue::Transfer(U256::ZERO),
434 scheme: CallScheme::ExtStaticCall,
435 is_static: true,
436 is_eof: true,
437 return_memory_offset: 0..0,
438 },
439 ))));
440}
441
442pub fn create<WIRE: InterpreterTypes, const IS_CREATE2: bool, H: Host + ?Sized>(
443 context: InstructionContext<'_, H, WIRE>,
444) {
445 require_non_staticcall!(context.interpreter);
446
447 if IS_CREATE2 {
449 check!(context.interpreter, PETERSBURG);
450 }
451
452 popn!([value, code_offset, len], context.interpreter);
453 let len = as_usize_or_fail!(context.interpreter, len);
454
455 let mut code = Bytes::new();
456 if len != 0 {
457 if context
459 .interpreter
460 .runtime_flag
461 .spec_id()
462 .is_enabled_in(SpecId::SHANGHAI)
463 {
464 if len > context.host.max_initcode_size() {
466 context
467 .interpreter
468 .halt(InstructionResult::CreateInitCodeSizeLimit);
469 return;
470 }
471 gas!(context.interpreter, gas::initcode_cost(len));
472 }
473
474 let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
475 resize_memory!(context.interpreter, code_offset, len);
476 code = Bytes::copy_from_slice(
477 context
478 .interpreter
479 .memory
480 .slice_len(code_offset, len)
481 .as_ref(),
482 );
483 }
484
485 let scheme = if IS_CREATE2 {
487 popn!([salt], context.interpreter);
488 gas_or_fail!(context.interpreter, gas::create2_cost(len));
490 CreateScheme::Create2 { salt }
491 } else {
492 gas!(context.interpreter, gas::CREATE);
493 CreateScheme::Create
494 };
495
496 let mut gas_limit = context.interpreter.gas.remaining();
497
498 if context
500 .interpreter
501 .runtime_flag
502 .spec_id()
503 .is_enabled_in(SpecId::TANGERINE)
504 {
505 gas_limit -= gas_limit / 64
507 }
508 gas!(context.interpreter, gas_limit);
509
510 context
512 .interpreter
513 .bytecode
514 .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
515 CreateInputs {
516 caller: context.interpreter.input.target_address(),
517 scheme,
518 value,
519 init_code: code,
520 gas_limit,
521 },
522 ))));
523}
524
525pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(context: InstructionContext<'_, H, WIRE>) {
526 popn!([local_gas_limit, to, value], context.interpreter);
527 let to = to.into_address();
528 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
530
531 let has_transfer = !value.is_zero();
532 if context.interpreter.runtime_flag.is_static() && has_transfer {
533 context
534 .interpreter
535 .halt(InstructionResult::CallNotAllowedInsideStatic);
536 return;
537 }
538
539 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
540 else {
541 return;
542 };
543
544 let Some(account_load) = context.host.load_account_delegated(to) else {
545 context
546 .interpreter
547 .halt(InstructionResult::FatalExternalError);
548 return;
549 };
550
551 let Some(mut gas_limit) = calc_call_gas(
552 context.interpreter,
553 account_load,
554 has_transfer,
555 local_gas_limit,
556 ) else {
557 return;
558 };
559
560 gas!(context.interpreter, gas_limit);
561
562 if has_transfer {
564 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
565 }
566
567 context
569 .interpreter
570 .bytecode
571 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
572 CallInputs {
573 input: CallInput::SharedBuffer(input),
574 gas_limit,
575 target_address: to,
576 caller: context.interpreter.input.target_address(),
577 bytecode_address: to,
578 value: CallValue::Transfer(value),
579 scheme: CallScheme::Call,
580 is_static: context.interpreter.runtime_flag.is_static(),
581 is_eof: false,
582 return_memory_offset,
583 },
584 ))));
585}
586
587pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
588 context: InstructionContext<'_, H, WIRE>,
589) {
590 popn!([local_gas_limit, to, value], context.interpreter);
591 let to = Address::from_word(B256::from(to));
592 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
594
595 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
597 else {
598 return;
599 };
600
601 let Some(mut load) = context.host.load_account_delegated(to) else {
602 context
603 .interpreter
604 .halt(InstructionResult::FatalExternalError);
605 return;
606 };
607
608 load.is_empty = false;
610 let Some(mut gas_limit) =
611 calc_call_gas(context.interpreter, load, !value.is_zero(), local_gas_limit)
612 else {
613 return;
614 };
615
616 gas!(context.interpreter, gas_limit);
617
618 if !value.is_zero() {
620 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
621 }
622
623 context
625 .interpreter
626 .bytecode
627 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
628 CallInputs {
629 input: CallInput::SharedBuffer(input),
630 gas_limit,
631 target_address: context.interpreter.input.target_address(),
632 caller: context.interpreter.input.target_address(),
633 bytecode_address: to,
634 value: CallValue::Transfer(value),
635 scheme: CallScheme::CallCode,
636 is_static: context.interpreter.runtime_flag.is_static(),
637 is_eof: false,
638 return_memory_offset,
639 },
640 ))));
641}
642
643pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
644 context: InstructionContext<'_, H, WIRE>,
645) {
646 check!(context.interpreter, HOMESTEAD);
647 popn!([local_gas_limit, to], context.interpreter);
648 let to = Address::from_word(B256::from(to));
649 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
651
652 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
653 else {
654 return;
655 };
656
657 let Some(mut load) = context.host.load_account_delegated(to) else {
658 context
659 .interpreter
660 .halt(InstructionResult::FatalExternalError);
661 return;
662 };
663
664 load.is_empty = false;
666 let Some(gas_limit) = calc_call_gas(context.interpreter, load, false, local_gas_limit) else {
667 return;
668 };
669
670 gas!(context.interpreter, gas_limit);
671
672 context
674 .interpreter
675 .bytecode
676 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
677 CallInputs {
678 input: CallInput::SharedBuffer(input),
679 gas_limit,
680 target_address: context.interpreter.input.target_address(),
681 caller: context.interpreter.input.caller_address(),
682 bytecode_address: to,
683 value: CallValue::Apparent(context.interpreter.input.call_value()),
684 scheme: CallScheme::DelegateCall,
685 is_static: context.interpreter.runtime_flag.is_static(),
686 is_eof: false,
687 return_memory_offset,
688 },
689 ))));
690}
691
692pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
693 context: InstructionContext<'_, H, WIRE>,
694) {
695 check!(context.interpreter, BYZANTIUM);
696 popn!([local_gas_limit, to], context.interpreter);
697 let to = Address::from_word(B256::from(to));
698 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
700
701 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
702 else {
703 return;
704 };
705
706 let Some(mut load) = context.host.load_account_delegated(to) else {
707 context
708 .interpreter
709 .halt(InstructionResult::FatalExternalError);
710 return;
711 };
712 load.is_empty = false;
714 let Some(gas_limit) = calc_call_gas(context.interpreter, load, false, local_gas_limit) else {
715 return;
716 };
717 gas!(context.interpreter, gas_limit);
718
719 context
721 .interpreter
722 .bytecode
723 .set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
724 CallInputs {
725 input: CallInput::SharedBuffer(input),
726 gas_limit,
727 target_address: to,
728 caller: context.interpreter.input.target_address(),
729 bytecode_address: to,
730 value: CallValue::Transfer(U256::ZERO),
731 scheme: CallScheme::StaticCall,
732 is_static: true,
733 is_eof: false,
734 return_memory_offset,
735 },
736 ))));
737}