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 interpreter::Interpreter,
8 primitives::{
9 eof::EofHeader, keccak256, Address, BerlinSpec, Bytes, Eof, Spec, SpecId::*, B256, U256,
10 },
11 CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInputs, Host,
12 InstructionResult, InterpreterAction, InterpreterResult, MAX_INITCODE_SIZE,
13};
14use core::cmp::max;
15use std::boxed::Box;
16
17pub fn eofcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
19 require_eof!(interpreter);
20 require_non_staticcall!(interpreter);
21 gas!(interpreter, EOF_CREATE_GAS);
22 let initcontainer_index = unsafe { *interpreter.instruction_pointer };
23 pop!(interpreter, value, salt, data_offset, data_size);
24
25 let sub_container = interpreter
26 .eof()
27 .expect("EOF is set")
28 .body
29 .container_section
30 .get(initcontainer_index as usize)
31 .cloned()
32 .expect("EOF is checked");
33
34 let Some(input_range) = resize_memory(interpreter, data_offset, data_size) else {
36 return;
37 };
38
39 let input = if !input_range.is_empty() {
40 interpreter
41 .shared_memory
42 .slice_range(input_range)
43 .to_vec()
44 .into()
45 } else {
46 Bytes::new()
47 };
48
49 let eof = Eof::decode(sub_container.clone()).expect("Subcontainer is verified");
50
51 if !eof.body.is_data_filled {
52 panic!("Panic if data section is not full");
54 }
55
56 gas_or_fail!(
58 interpreter,
59 cost_per_word(sub_container.len() as u64, KECCAK256WORD)
60 );
61
62 let created_address = interpreter
63 .contract
64 .target_address
65 .create2(salt.to_be_bytes(), keccak256(sub_container));
66
67 let gas_limit = interpreter.gas().remaining_63_of_64_parts();
68 gas!(interpreter, gas_limit);
69 interpreter.instruction_result = InstructionResult::CallOrCreate;
71 interpreter.next_action = InterpreterAction::EOFCreate {
72 inputs: Box::new(EOFCreateInputs::new_opcode(
73 interpreter.contract.target_address,
74 created_address,
75 value,
76 eof,
77 gas_limit,
78 input,
79 )),
80 };
81
82 interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) };
83}
84
85pub fn return_contract<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
86 require_init_eof!(interpreter);
87 let deploy_container_index = unsafe { *interpreter.instruction_pointer };
88 pop!(interpreter, aux_data_offset, aux_data_size);
89 let aux_data_size = as_usize_or_fail!(interpreter, aux_data_size);
90 let container = interpreter
92 .eof()
93 .expect("EOF is set")
94 .body
95 .container_section
96 .get(deploy_container_index as usize)
97 .expect("EOF is checked")
98 .clone();
99
100 let (eof_header, _) = EofHeader::decode(&container).expect("valid EOF header");
102
103 let aux_slice = if aux_data_size != 0 {
104 let aux_data_offset = as_usize_or_fail!(interpreter, aux_data_offset);
105 resize_memory!(interpreter, aux_data_offset, aux_data_size);
106
107 interpreter
108 .shared_memory
109 .slice(aux_data_offset, aux_data_size)
110 } else {
111 &[]
112 };
113
114 let static_aux_size = eof_header.eof_size() - container.len();
115
116 let new_data_size = eof_header.data_size as usize - static_aux_size + aux_slice.len();
119 if new_data_size > 0xFFFF {
120 interpreter.instruction_result = InstructionResult::EofAuxDataOverflow;
122 return;
123 }
124 if new_data_size < eof_header.data_size as usize {
125 interpreter.instruction_result = InstructionResult::EofAuxDataTooSmall;
127 return;
128 }
129 let new_data_size = (new_data_size as u16).to_be_bytes();
130
131 let mut output = [&container, aux_slice].concat();
132 output[eof_header.data_size_raw_i()..][..2].clone_from_slice(&new_data_size);
134 let output: Bytes = output.into();
135
136 let result = InstructionResult::ReturnContract;
137 interpreter.instruction_result = result;
138 interpreter.next_action = crate::InterpreterAction::Return {
139 result: InterpreterResult {
140 output,
141 gas: interpreter.gas,
142 result,
143 },
144 };
145}
146
147pub fn extcall_input(interpreter: &mut Interpreter) -> Option<Bytes> {
148 pop_ret!(interpreter, input_offset, input_size, None);
149
150 let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?;
151
152 if return_memory_offset.is_empty() {
153 return Some(Bytes::new());
154 }
155
156 Some(Bytes::copy_from_slice(
157 interpreter
158 .shared_memory
159 .slice_range(return_memory_offset.clone()),
160 ))
161}
162
163pub fn extcall_gas_calc<H: Host + ?Sized>(
164 interpreter: &mut Interpreter,
165 host: &mut H,
166 target: Address,
167 transfers_value: bool,
168) -> Option<u64> {
169 let Some(account_load) = host.load_account_delegated(target) else {
170 interpreter.instruction_result = InstructionResult::FatalExternalError;
171 return None;
172 };
173 let call_cost = gas::call_cost(BerlinSpec::SPEC_ID, transfers_value, account_load);
175 gas!(interpreter, call_cost, None);
176
177 let gas_reduce = max(interpreter.gas.remaining() / 64, 5000);
180 let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce);
181
182 if gas_limit < MIN_CALLEE_GAS {
188 let _ = interpreter.stack_mut().push(U256::from(1));
191 interpreter.return_data_buffer.clear();
192 return None;
194 }
195
196 gas!(interpreter, gas_limit, None);
197 Some(gas_limit)
198}
199
200#[inline]
204pub fn pop_extcall_target_address(interpreter: &mut Interpreter) -> Option<Address> {
205 pop_ret!(interpreter, target_address, None);
206 let target_address = B256::from(target_address);
207 if target_address[..12].iter().any(|i| *i != 0) {
209 interpreter.instruction_result = InstructionResult::InvalidEXTCALLTarget;
210 return None;
211 }
212 Some(Address::from_word(target_address))
214}
215
216pub fn extcall<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
217 require_eof!(interpreter);
218
219 let Some(target_address) = pop_extcall_target_address(interpreter) else {
221 return;
222 };
223
224 let Some(input) = extcall_input(interpreter) else {
226 return;
227 };
228
229 pop!(interpreter, value);
230 let has_transfer = !value.is_zero();
231 if interpreter.is_static && has_transfer {
232 interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic;
233 return;
234 }
235
236 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, has_transfer) else {
237 return;
238 };
239
240 interpreter.next_action = InterpreterAction::Call {
242 inputs: Box::new(CallInputs {
243 input,
244 gas_limit,
245 target_address,
246 caller: interpreter.contract.target_address,
247 bytecode_address: target_address,
248 value: CallValue::Transfer(value),
249 scheme: CallScheme::ExtCall,
250 is_static: interpreter.is_static,
251 is_eof: true,
252 return_memory_offset: 0..0,
253 }),
254 };
255 interpreter.instruction_result = InstructionResult::CallOrCreate;
256}
257
258pub fn extdelegatecall<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
259 require_eof!(interpreter);
260
261 let Some(target_address) = pop_extcall_target_address(interpreter) else {
263 return;
264 };
265
266 let Some(input) = extcall_input(interpreter) else {
268 return;
269 };
270
271 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
272 return;
273 };
274
275 interpreter.next_action = InterpreterAction::Call {
277 inputs: Box::new(CallInputs {
278 input,
279 gas_limit,
280 target_address: interpreter.contract.target_address,
281 caller: interpreter.contract.caller,
282 bytecode_address: target_address,
283 value: CallValue::Apparent(interpreter.contract.call_value),
284 scheme: CallScheme::ExtDelegateCall,
285 is_static: interpreter.is_static,
286 is_eof: true,
287 return_memory_offset: 0..0,
288 }),
289 };
290 interpreter.instruction_result = InstructionResult::CallOrCreate;
291}
292
293pub fn extstaticcall<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
294 require_eof!(interpreter);
295
296 let Some(target_address) = pop_extcall_target_address(interpreter) else {
298 return;
299 };
300
301 let Some(input) = extcall_input(interpreter) else {
303 return;
304 };
305
306 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
307 return;
308 };
309
310 interpreter.next_action = InterpreterAction::Call {
312 inputs: Box::new(CallInputs {
313 input,
314 gas_limit,
315 target_address,
316 caller: interpreter.contract.target_address,
317 bytecode_address: target_address,
318 value: CallValue::Transfer(U256::ZERO),
319 scheme: CallScheme::ExtStaticCall,
320 is_static: true,
321 is_eof: true,
322 return_memory_offset: 0..0,
323 }),
324 };
325 interpreter.instruction_result = InstructionResult::CallOrCreate;
326}
327
328pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
329 interpreter: &mut Interpreter,
330 host: &mut H,
331) {
332 require_non_staticcall!(interpreter);
333
334 if IS_CREATE2 {
336 check!(interpreter, PETERSBURG);
337 }
338
339 pop!(interpreter, value, code_offset, len);
340 let len = as_usize_or_fail!(interpreter, len);
341
342 let mut code = Bytes::new();
343 if len != 0 {
344 if SPEC::enabled(SHANGHAI) {
346 let max_initcode_size = host
348 .env()
349 .cfg
350 .limit_contract_code_size
351 .map(|limit| limit.saturating_mul(2))
352 .unwrap_or(MAX_INITCODE_SIZE);
353 if len > max_initcode_size {
354 interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit;
355 return;
356 }
357 gas!(interpreter, gas::initcode_cost(len as u64));
358 }
359
360 let code_offset = as_usize_or_fail!(interpreter, code_offset);
361 resize_memory!(interpreter, code_offset, len);
362 code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len));
363 }
364
365 let scheme = if IS_CREATE2 {
367 pop!(interpreter, salt);
368 gas_or_fail!(interpreter, gas::create2_cost(len.try_into().unwrap()));
370 CreateScheme::Create2 { salt }
371 } else {
372 gas!(interpreter, gas::CREATE);
373 CreateScheme::Create
374 };
375
376 let mut gas_limit = interpreter.gas().remaining();
377
378 if SPEC::enabled(TANGERINE) {
380 gas_limit -= gas_limit / 64
382 }
383 gas!(interpreter, gas_limit);
384
385 interpreter.next_action = InterpreterAction::Create {
387 inputs: Box::new(CreateInputs {
388 caller: interpreter.contract.target_address,
389 scheme,
390 value,
391 init_code: code,
392 gas_limit,
393 }),
394 };
395 interpreter.instruction_result = InstructionResult::CallOrCreate;
396}
397
398pub fn call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
399 pop!(interpreter, local_gas_limit);
400 pop_address!(interpreter, to);
401 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
403
404 pop!(interpreter, value);
405 let has_transfer = !value.is_zero();
406 if interpreter.is_static && has_transfer {
407 interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic;
408 return;
409 }
410
411 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
412 return;
413 };
414
415 let Some(account_load) = host.load_account_delegated(to) else {
416 interpreter.instruction_result = InstructionResult::FatalExternalError;
417 return;
418 };
419 let Some(mut gas_limit) =
420 calc_call_gas::<SPEC>(interpreter, account_load, has_transfer, local_gas_limit)
421 else {
422 return;
423 };
424
425 gas!(interpreter, gas_limit);
426
427 if has_transfer {
429 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
430 }
431
432 interpreter.next_action = InterpreterAction::Call {
434 inputs: Box::new(CallInputs {
435 input,
436 gas_limit,
437 target_address: to,
438 caller: interpreter.contract.target_address,
439 bytecode_address: to,
440 value: CallValue::Transfer(value),
441 scheme: CallScheme::Call,
442 is_static: interpreter.is_static,
443 is_eof: false,
444 return_memory_offset,
445 }),
446 };
447 interpreter.instruction_result = InstructionResult::CallOrCreate;
448}
449
450pub fn call_code<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
451 pop!(interpreter, local_gas_limit);
452 pop_address!(interpreter, to);
453 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
455
456 pop!(interpreter, value);
457 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
458 return;
459 };
460
461 let Some(mut load) = host.load_account_delegated(to) else {
462 interpreter.instruction_result = InstructionResult::FatalExternalError;
463 return;
464 };
465 load.is_empty = false;
467 let Some(mut gas_limit) =
468 calc_call_gas::<SPEC>(interpreter, load, !value.is_zero(), local_gas_limit)
469 else {
470 return;
471 };
472
473 gas!(interpreter, gas_limit);
474
475 if !value.is_zero() {
477 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
478 }
479
480 interpreter.next_action = InterpreterAction::Call {
482 inputs: Box::new(CallInputs {
483 input,
484 gas_limit,
485 target_address: interpreter.contract.target_address,
486 caller: interpreter.contract.target_address,
487 bytecode_address: to,
488 value: CallValue::Transfer(value),
489 scheme: CallScheme::CallCode,
490 is_static: interpreter.is_static,
491 is_eof: false,
492 return_memory_offset,
493 }),
494 };
495 interpreter.instruction_result = InstructionResult::CallOrCreate;
496}
497
498pub fn delegate_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
499 check!(interpreter, HOMESTEAD);
500 pop!(interpreter, local_gas_limit);
501 pop_address!(interpreter, to);
502 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
504
505 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
506 return;
507 };
508
509 let Some(mut load) = host.load_account_delegated(to) else {
510 interpreter.instruction_result = InstructionResult::FatalExternalError;
511 return;
512 };
513 load.is_empty = false;
515 let Some(gas_limit) = calc_call_gas::<SPEC>(interpreter, load, false, local_gas_limit) else {
516 return;
517 };
518
519 gas!(interpreter, gas_limit);
520
521 interpreter.next_action = InterpreterAction::Call {
523 inputs: Box::new(CallInputs {
524 input,
525 gas_limit,
526 target_address: interpreter.contract.target_address,
527 caller: interpreter.contract.caller,
528 bytecode_address: to,
529 value: CallValue::Apparent(interpreter.contract.call_value),
530 scheme: CallScheme::DelegateCall,
531 is_static: interpreter.is_static,
532 is_eof: false,
533 return_memory_offset,
534 }),
535 };
536 interpreter.instruction_result = InstructionResult::CallOrCreate;
537}
538
539pub fn static_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
540 check!(interpreter, BYZANTIUM);
541 pop!(interpreter, local_gas_limit);
542 pop_address!(interpreter, to);
543 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
545
546 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
547 return;
548 };
549
550 let Some(mut load) = host.load_account_delegated(to) else {
551 interpreter.instruction_result = InstructionResult::FatalExternalError;
552 return;
553 };
554 load.is_empty = false;
556 let Some(gas_limit) = calc_call_gas::<SPEC>(interpreter, load, false, local_gas_limit) else {
557 return;
558 };
559 gas!(interpreter, gas_limit);
560
561 interpreter.next_action = InterpreterAction::Call {
563 inputs: Box::new(CallInputs {
564 input,
565 gas_limit,
566 target_address: to,
567 caller: interpreter.contract.target_address,
568 bytecode_address: to,
569 value: CallValue::Transfer(U256::ZERO),
570 scheme: CallScheme::StaticCall,
571 is_static: true,
572 is_eof: false,
573 return_memory_offset,
574 }),
575 };
576 interpreter.instruction_result = InstructionResult::CallOrCreate;
577}