1mod call_helpers;
2
3pub use call_helpers::{
4 calc_call_gas, get_memory_input_and_out_ranges, resize_memory_and_return_range,
5};
6use rtvm_primitives::{keccak256, BerlinSpec};
7
8use crate::{
9 analysis::validate_eof,
10 gas::{self, cost_per_word, BASE, EOF_CREATE_GAS, KECCAK256WORD},
11 instructions::utility::read_u16,
12 interpreter::Interpreter,
13 primitives::{Address, Bytes, Eof, Spec, SpecId::*, B256, U256},
14 CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInput, Host,
15 InstructionResult, InterpreterAction, InterpreterResult, LoadAccountResult, MAX_INITCODE_SIZE,
16};
17use core::{cmp::max, ops::Range};
18use std::boxed::Box;
19
20pub fn resize_memory(
24 interpreter: &mut Interpreter,
25 offset: U256,
26 len: U256,
27) -> Option<Range<usize>> {
28 let len = as_usize_or_fail_ret!(interpreter, len, None);
29 if len != 0 {
30 let offset = as_usize_or_fail_ret!(interpreter, offset, None);
31 resize_memory!(interpreter, offset, len, None);
32 Some(offset..offset + len)
34 } else {
35 Some(usize::MAX..usize::MAX)
37 }
38}
39
40pub fn eofcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
42 require_eof!(interpreter);
43 gas!(interpreter, EOF_CREATE_GAS);
44 let initcontainer_index = unsafe { *interpreter.instruction_pointer };
45 pop!(interpreter, value, salt, data_offset, data_size);
46
47 let sub_container = interpreter
48 .eof()
49 .expect("EOF is set")
50 .body
51 .container_section
52 .get(initcontainer_index as usize)
53 .cloned()
54 .expect("EOF is checked");
55
56 let Some(return_range) = resize_memory(interpreter, data_offset, data_size) else {
58 return;
59 };
60
61 let eof = Eof::decode(sub_container.clone()).expect("Subcontainer is verified");
62
63 if !eof.body.is_data_filled {
64 panic!("Panic if data section is not full");
66 }
67
68 gas_or_fail!(
70 interpreter,
71 cost_per_word(sub_container.len() as u64, KECCAK256WORD)
72 );
73
74 let created_address = interpreter
75 .contract
76 .caller
77 .create2(salt.to_be_bytes(), keccak256(sub_container));
78
79 interpreter.next_action = InterpreterAction::EOFCreate {
81 inputs: Box::new(EOFCreateInput::new(
82 interpreter.contract.target_address,
83 created_address,
84 value,
85 eof,
86 interpreter.gas().remaining(),
87 return_range,
88 )),
89 };
90
91 interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) };
92}
93
94pub fn txcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
95 require_eof!(interpreter);
96 gas!(interpreter, EOF_CREATE_GAS);
97 pop!(
98 interpreter,
99 tx_initcode_hash,
100 value,
101 salt,
102 data_offset,
103 data_size
104 );
105 let tx_initcode_hash = B256::from(tx_initcode_hash);
106
107 let Some(return_range) = resize_memory(interpreter, data_offset, data_size) else {
109 return;
110 };
111
112 let Some(initcode) = host
114 .env()
115 .tx
116 .eof_initcodes_hashed
117 .get(&tx_initcode_hash)
118 .cloned()
119 else {
120 push!(interpreter, U256::ZERO);
121 return;
122 };
123
124 gas_or_fail!(interpreter, cost_per_word(initcode.len() as u64, BASE));
126
127 gas_or_fail!(
129 interpreter,
130 cost_per_word(initcode.len() as u64, KECCAK256WORD)
131 );
132
133 let Ok(eof) = Eof::decode(initcode.clone()) else {
134 push!(interpreter, U256::ZERO);
135 return;
136 };
137
138 if !eof.body.is_data_filled {
140 push!(interpreter, U256::ZERO);
141 return;
142 }
143
144 if validate_eof(&eof).is_err() {
146 push!(interpreter, U256::ZERO);
147 return;
148 }
149
150 let created_address = interpreter
152 .contract
153 .caller
154 .create2(salt.to_be_bytes(), tx_initcode_hash);
155
156 let gas_limit = interpreter.gas().remaining();
157 gas!(interpreter, gas_limit);
159
160 interpreter.next_action = InterpreterAction::EOFCreate {
161 inputs: Box::new(EOFCreateInput::new(
162 interpreter.contract.target_address,
163 created_address,
164 value,
165 eof,
166 gas_limit,
167 return_range,
168 )),
169 };
170 interpreter.instruction_result = InstructionResult::CallOrCreate;
171}
172
173pub fn return_contract<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
174 require_init_eof!(interpreter);
175 let deploy_container_index = unsafe { read_u16(interpreter.instruction_pointer) };
176 pop!(interpreter, aux_data_offset, aux_data_size);
177 let aux_data_size = as_usize_or_fail!(interpreter, aux_data_size);
178 let container = interpreter
180 .eof()
181 .expect("EOF is set")
182 .body
183 .container_section
184 .get(deploy_container_index as usize)
185 .expect("EOF is checked");
186
187 let new_eof = Eof::decode(container.clone()).expect("Container is verified");
189
190 let aux_slice = if aux_data_size != 0 {
191 let aux_data_offset = as_usize_or_fail!(interpreter, aux_data_offset);
192 resize_memory!(interpreter, aux_data_offset, aux_data_size);
193
194 interpreter
195 .shared_memory
196 .slice(aux_data_offset, aux_data_size)
197 } else {
198 &[]
199 };
200
201 let new_data_size = new_eof.body.data_section.len() + aux_slice.len();
202 if new_data_size > 0xFFFF {
203 interpreter.instruction_result = InstructionResult::FatalExternalError;
205 return;
206 }
207 if new_data_size < new_eof.header.data_size as usize {
208 interpreter.instruction_result = InstructionResult::FatalExternalError;
210 return;
211 }
212
213 let output = [new_eof.raw(), aux_slice].concat().into();
215
216 let result = InstructionResult::ReturnContract;
217 interpreter.instruction_result = result;
218 interpreter.next_action = crate::InterpreterAction::Return {
219 result: InterpreterResult {
220 output,
221 gas: interpreter.gas,
222 result,
223 },
224 };
225}
226
227pub fn extcall_input(interpreter: &mut Interpreter) -> Option<Bytes> {
228 pop_ret!(interpreter, input_offset, input_size, None);
229
230 let return_memory_offset =
231 resize_memory_and_return_range(interpreter, input_offset, input_size)?;
232
233 Some(Bytes::copy_from_slice(
234 interpreter
235 .shared_memory
236 .slice_range(return_memory_offset.clone()),
237 ))
238}
239
240pub fn extcall_gas_calc<H: Host + ?Sized>(
241 interpreter: &mut Interpreter,
242 host: &mut H,
243 target: Address,
244 transfers_value: bool,
245) -> Option<u64> {
246 let Some(load_result) = host.load_account(target) else {
247 interpreter.instruction_result = InstructionResult::FatalExternalError;
248 return None;
249 };
250
251 if load_result.is_cold {
252 gas!(interpreter, gas::COLD_ACCOUNT_ACCESS_COST, None);
253 }
254
255 let call_cost = gas::call_cost(
257 BerlinSpec::SPEC_ID,
258 transfers_value,
259 load_result.is_cold,
260 load_result.is_empty,
261 );
262 gas!(interpreter, call_cost, None);
263
264 let gas_reduce = max(interpreter.gas.remaining() / 64, 5000);
267 let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce);
268
269 if gas_limit < 2300 {
270 interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic;
271 return None;
274 }
275
276 gas!(interpreter, gas_limit, None);
279 Some(gas_limit)
280}
281
282pub fn extcall<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
283 require_eof!(interpreter);
284 pop_address!(interpreter, target_address);
285
286 let Some(input) = extcall_input(interpreter) else {
288 return;
289 };
290
291 pop!(interpreter, value);
292 let has_transfer = value != U256::ZERO;
293
294 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, has_transfer) else {
295 return;
296 };
297 interpreter.next_action = InterpreterAction::Call {
301 inputs: Box::new(CallInputs {
302 input,
303 gas_limit,
304 target_address,
305 caller: interpreter.contract.target_address,
306 bytecode_address: target_address,
307 value: CallValue::Transfer(value),
308 scheme: CallScheme::Call,
309 is_static: interpreter.is_static,
310 is_eof: true,
311 return_memory_offset: 0..0,
312 }),
313 };
314 interpreter.instruction_result = InstructionResult::CallOrCreate;
315}
316
317pub fn extdcall<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
318 require_eof!(interpreter);
319 pop_address!(interpreter, target_address);
320
321 let Some(input) = extcall_input(interpreter) else {
323 return;
324 };
325
326 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
327 return;
328 };
329 interpreter.next_action = InterpreterAction::Call {
333 inputs: Box::new(CallInputs {
334 input,
335 gas_limit,
336 target_address,
337 caller: interpreter.contract.target_address,
338 bytecode_address: target_address,
339 value: CallValue::Apparent(interpreter.contract.call_value),
340 scheme: CallScheme::DelegateCall,
342 is_static: interpreter.is_static,
343 is_eof: true,
344 return_memory_offset: 0..0,
345 }),
346 };
347 interpreter.instruction_result = InstructionResult::CallOrCreate;
348}
349
350pub fn extscall<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
351 require_eof!(interpreter);
352 pop_address!(interpreter, target_address);
353
354 let Some(input) = extcall_input(interpreter) else {
356 return;
357 };
358
359 let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else {
360 return;
361 };
362
363 interpreter.next_action = InterpreterAction::Call {
365 inputs: Box::new(CallInputs {
366 input,
367 gas_limit,
368 target_address,
369 caller: interpreter.contract.target_address,
370 bytecode_address: target_address,
371 value: CallValue::Transfer(U256::ZERO),
372 scheme: CallScheme::Call,
373 is_static: interpreter.is_static,
374 is_eof: true,
375 return_memory_offset: 0..0,
376 }),
377 };
378 interpreter.instruction_result = InstructionResult::CallOrCreate;
379}
380
381pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
382 interpreter: &mut Interpreter,
383 host: &mut H,
384) {
385 require_non_staticcall!(interpreter);
386
387 if IS_CREATE2 {
389 check!(interpreter, PETERSBURG);
390 }
391
392 pop!(interpreter, value, code_offset, len);
393 let len = as_usize_or_fail!(interpreter, len);
394
395 let mut code = Bytes::new();
396 if len != 0 {
397 if SPEC::enabled(SHANGHAI) {
399 let max_initcode_size = host
401 .env()
402 .cfg
403 .limit_contract_code_size
404 .map(|limit| limit.saturating_mul(2))
405 .unwrap_or(MAX_INITCODE_SIZE);
406 if len > max_initcode_size {
407 interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit;
408 return;
409 }
410 gas!(interpreter, gas::initcode_cost(len as u64));
411 }
412
413 let code_offset = as_usize_or_fail!(interpreter, code_offset);
414 resize_memory!(interpreter, code_offset, len);
415 code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len));
416 }
417
418 let scheme = if IS_CREATE2 {
420 pop!(interpreter, salt);
421 gas_or_fail!(interpreter, gas::create2_cost(len.try_into().unwrap()));
423 CreateScheme::Create2 { salt }
424 } else {
425 gas!(interpreter, gas::CREATE);
426 CreateScheme::Create
427 };
428
429 let mut gas_limit = interpreter.gas().remaining();
430
431 if SPEC::enabled(TANGERINE) {
433 gas_limit -= gas_limit / 64
435 }
436 gas!(interpreter, gas_limit);
437
438 interpreter.next_action = InterpreterAction::Create {
440 inputs: Box::new(CreateInputs {
441 caller: interpreter.contract.target_address,
442 scheme,
443 value,
444 init_code: code,
445 gas_limit,
446 }),
447 };
448 interpreter.instruction_result = InstructionResult::CallOrCreate;
449}
450
451pub fn call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
452 pop!(interpreter, local_gas_limit);
453 pop_address!(interpreter, to);
454 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
456
457 pop!(interpreter, value);
458 let has_transfer = value != U256::ZERO;
459 if interpreter.is_static && has_transfer {
460 interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic;
461 return;
462 }
463
464 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
465 return;
466 };
467
468 let Some(LoadAccountResult { is_cold, is_empty }) = host.load_account(to) else {
469 interpreter.instruction_result = InstructionResult::FatalExternalError;
470 return;
471 };
472 let Some(mut gas_limit) = calc_call_gas::<H, SPEC>(
473 interpreter,
474 is_cold,
475 has_transfer,
476 is_empty,
477 local_gas_limit,
478 ) else {
479 return;
480 };
481
482 gas!(interpreter, gas_limit);
483
484 if has_transfer {
486 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
487 }
488
489 interpreter.next_action = InterpreterAction::Call {
491 inputs: Box::new(CallInputs {
492 input,
493 gas_limit,
494 target_address: to,
495 caller: interpreter.contract.target_address,
496 bytecode_address: to,
497 value: CallValue::Transfer(value),
498 scheme: CallScheme::Call,
499 is_static: interpreter.is_static,
500 is_eof: false,
501 return_memory_offset,
502 }),
503 };
504 interpreter.instruction_result = InstructionResult::CallOrCreate;
505}
506
507pub fn call_code<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
508 pop!(interpreter, local_gas_limit);
509 pop_address!(interpreter, to);
510 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
512
513 pop!(interpreter, value);
514 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
515 return;
516 };
517
518 let Some(LoadAccountResult { is_cold, .. }) = host.load_account(to) else {
519 interpreter.instruction_result = InstructionResult::FatalExternalError;
520 return;
521 };
522
523 let Some(mut gas_limit) = calc_call_gas::<H, SPEC>(
524 interpreter,
525 is_cold,
526 value != U256::ZERO,
527 false,
528 local_gas_limit,
529 ) else {
530 return;
531 };
532
533 gas!(interpreter, gas_limit);
534
535 if value != U256::ZERO {
537 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
538 }
539
540 interpreter.next_action = InterpreterAction::Call {
542 inputs: Box::new(CallInputs {
543 input,
544 gas_limit,
545 target_address: interpreter.contract.target_address,
546 caller: interpreter.contract.target_address,
547 bytecode_address: to,
548 value: CallValue::Transfer(value),
549 scheme: CallScheme::CallCode,
550 is_static: interpreter.is_static,
551 is_eof: false,
552 return_memory_offset,
553 }),
554 };
555 interpreter.instruction_result = InstructionResult::CallOrCreate;
556}
557
558pub fn delegate_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
559 check!(interpreter, HOMESTEAD);
560 pop!(interpreter, local_gas_limit);
561 pop_address!(interpreter, to);
562 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
564
565 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
566 return;
567 };
568
569 let Some(LoadAccountResult { is_cold, .. }) = host.load_account(to) else {
570 interpreter.instruction_result = InstructionResult::FatalExternalError;
571 return;
572 };
573 let Some(gas_limit) =
574 calc_call_gas::<H, SPEC>(interpreter, is_cold, false, false, local_gas_limit)
575 else {
576 return;
577 };
578
579 gas!(interpreter, gas_limit);
580
581 interpreter.next_action = InterpreterAction::Call {
583 inputs: Box::new(CallInputs {
584 input,
585 gas_limit,
586 target_address: interpreter.contract.target_address,
587 caller: interpreter.contract.caller,
588 bytecode_address: to,
589 value: CallValue::Apparent(interpreter.contract.call_value),
590 scheme: CallScheme::DelegateCall,
591 is_static: interpreter.is_static,
592 is_eof: false,
593 return_memory_offset,
594 }),
595 };
596 interpreter.instruction_result = InstructionResult::CallOrCreate;
597}
598
599pub fn static_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
600 check!(interpreter, BYZANTIUM);
601 pop!(interpreter, local_gas_limit);
602 pop_address!(interpreter, to);
603 let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
605
606 let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
607 return;
608 };
609
610 let Some(LoadAccountResult { is_cold, .. }) = host.load_account(to) else {
611 interpreter.instruction_result = InstructionResult::FatalExternalError;
612 return;
613 };
614
615 let Some(gas_limit) =
616 calc_call_gas::<H, SPEC>(interpreter, is_cold, false, false, local_gas_limit)
617 else {
618 return;
619 };
620 gas!(interpreter, gas_limit);
621
622 interpreter.next_action = InterpreterAction::Call {
624 inputs: Box::new(CallInputs {
625 input,
626 gas_limit,
627 target_address: to,
628 caller: interpreter.contract.target_address,
629 bytecode_address: to,
630 value: CallValue::Transfer(U256::ZERO),
631 scheme: CallScheme::StaticCall,
632 is_static: true,
633 is_eof: false,
634 return_memory_offset,
635 }),
636 };
637 interpreter.instruction_result = InstructionResult::CallOrCreate;
638}