1use crate::{
2 call::{
3 Call,
4 CallFrame,
5 },
6 constraints::reg_key::*,
7 consts::*,
8 context::Context,
9 error::{
10 IoResult,
11 RuntimeError,
12 SimpleResult,
13 },
14 interpreter::{
15 contract::{
16 balance_decrease,
17 balance_increase,
18 contract_size,
19 },
20 gas::{
21 dependent_gas_charge_without_base,
22 gas_charge,
23 ProfileGas,
24 },
25 internal::{
26 current_contract,
27 external_asset_id_balance_sub,
28 inc_pc,
29 internal_contract,
30 set_frame_pointer,
31 },
32 receipts::ReceiptsCtx,
33 ExecutableTransaction,
34 InputContracts,
35 Interpreter,
36 Memory,
37 MemoryInstance,
38 PanicContext,
39 RuntimeBalances,
40 },
41 prelude::{
42 Bug,
43 BugVariant,
44 },
45 profiler::Profiler,
46 storage::{
47 ContractsAssetsStorage,
48 ContractsRawCode,
49 InterpreterStorage,
50 },
51};
52use alloc::vec::Vec;
53use core::cmp;
54use fuel_asm::{
55 Instruction,
56 PanicInstruction,
57 RegId,
58};
59use fuel_storage::{
60 StorageAsRef,
61 StorageRead,
62 StorageSize,
63};
64use fuel_tx::{
65 DependentCost,
66 PanicReason,
67 Receipt,
68};
69use fuel_types::{
70 bytes::padded_len_usize,
71 canonical::Serialize,
72 AssetId,
73 Bytes32,
74 ContractId,
75 Word,
76};
77
78#[cfg(test)]
79mod jump_tests;
80#[cfg(test)]
81mod ret_tests;
82#[cfg(test)]
83mod tests;
84
85impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
86where
87 M: Memory,
88 Tx: ExecutableTransaction,
89{
90 pub(crate) fn jump(&mut self, args: JumpArgs) -> SimpleResult<()> {
91 let (SystemRegisters { pc, is, .. }, _) = split_registers(&mut self.registers);
92 args.jump(is.as_ref(), pc)
93 }
94
95 pub(crate) fn ret(&mut self, a: Word) -> SimpleResult<()> {
96 let current_contract =
97 current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?;
98 let input = RetCtx {
99 receipts: &mut self.receipts,
100 frames: &mut self.frames,
101 registers: &mut self.registers,
102 memory: self.memory.as_ref(),
103 context: &mut self.context,
104 current_contract,
105 };
106 input.ret(a)
107 }
108
109 pub(crate) fn ret_data(&mut self, a: Word, b: Word) -> SimpleResult<Bytes32> {
110 let current_contract =
111 current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?;
112 let input = RetCtx {
113 frames: &mut self.frames,
114 registers: &mut self.registers,
115 memory: self.memory.as_mut(),
116 receipts: &mut self.receipts,
117 context: &mut self.context,
118 current_contract,
119 };
120 input.ret_data(a, b)
121 }
122
123 pub(crate) fn revert(&mut self, a: Word) -> SimpleResult<()> {
124 let current_contract =
125 current_contract(&self.context, self.registers.fp(), self.memory.as_ref())
126 .unwrap_or(Some(ContractId::zeroed()));
127 revert(
128 &mut self.receipts,
129 current_contract,
130 self.registers.pc(),
131 self.registers.is(),
132 a,
133 )
134 }
135
136 pub(crate) fn append_panic_receipt(&mut self, result: PanicInstruction) {
137 let pc = self.registers[RegId::PC];
138 let is = self.registers[RegId::IS];
139
140 let mut receipt =
141 Receipt::panic(self.internal_contract().unwrap_or_default(), result, pc, is);
142
143 match self.panic_context {
144 PanicContext::None => {}
145 PanicContext::ContractId(contract_id) => {
146 receipt = receipt.with_panic_contract_id(Some(contract_id));
147 }
148 };
149 self.panic_context = PanicContext::None;
150
151 self.receipts
152 .push(receipt)
153 .expect("Appending a panic receipt cannot fail");
154 }
155}
156
157struct RetCtx<'vm> {
158 frames: &'vm mut Vec<CallFrame>,
159 registers: &'vm mut [Word; VM_REGISTER_COUNT],
160 memory: &'vm MemoryInstance,
161 receipts: &'vm mut ReceiptsCtx,
162 context: &'vm mut Context,
163 current_contract: Option<ContractId>,
164}
165
166impl RetCtx<'_> {
167 pub(crate) fn ret(self, a: Word) -> SimpleResult<()> {
168 let receipt = Receipt::ret(
169 self.current_contract.unwrap_or_else(ContractId::zeroed),
170 a,
171 self.registers[RegId::PC],
172 self.registers[RegId::IS],
173 );
174
175 self.registers[RegId::RET] = a;
176 self.registers[RegId::RETL] = 0;
177
178 self.return_from_context(receipt)
180 }
181
182 pub(crate) fn return_from_context(mut self, receipt: Receipt) -> SimpleResult<()> {
183 if let Some(frame) = self.frames.pop() {
184 let registers = &mut self.registers;
185 let context = &mut self.context;
186
187 registers[RegId::CGAS] = registers[RegId::CGAS]
188 .checked_add(frame.context_gas())
189 .ok_or_else(|| Bug::new(BugVariant::ContextGasOverflow))?;
190
191 let cgas = registers[RegId::CGAS];
192 let ggas = registers[RegId::GGAS];
193 let ret = registers[RegId::RET];
194 let retl = registers[RegId::RETL];
195 let hp = registers[RegId::HP];
196
197 registers.copy_from_slice(frame.registers());
198
199 registers[RegId::CGAS] = cgas;
200 registers[RegId::GGAS] = ggas;
201 registers[RegId::RET] = ret;
202 registers[RegId::RETL] = retl;
203 registers[RegId::HP] = hp;
204
205 let fp = registers[RegId::FP];
206 set_frame_pointer(context, registers.fp_mut(), fp);
207 }
208
209 self.receipts.push(receipt)?;
210
211 Ok(inc_pc(self.registers.pc_mut())?)
212 }
213
214 pub(crate) fn ret_data(self, a: Word, b: Word) -> SimpleResult<Bytes32> {
215 let data = self.memory.read(a, b)?.to_vec();
216
217 let receipt = Receipt::return_data(
218 self.current_contract.unwrap_or_else(ContractId::zeroed),
219 a,
220 self.registers[RegId::PC],
221 self.registers[RegId::IS],
222 data,
223 );
224 let digest = *receipt
225 .digest()
226 .expect("Receipt is created above and `digest` should exist");
227
228 self.registers[RegId::RET] = a;
229 self.registers[RegId::RETL] = b;
230
231 self.return_from_context(receipt)?;
232
233 Ok(digest)
234 }
235}
236
237pub(crate) fn revert(
238 receipts: &mut ReceiptsCtx,
239 current_contract: Option<ContractId>,
240 pc: Reg<PC>,
241 is: Reg<IS>,
242 a: Word,
243) -> SimpleResult<()> {
244 let receipt = Receipt::revert(
245 current_contract.unwrap_or_else(ContractId::zeroed),
246 a,
247 *pc,
248 *is,
249 );
250
251 receipts.push(receipt)
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
255pub enum JumpMode {
256 Absolute,
258 RelativeForwards,
260 RelativeBackwards,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
265pub struct JumpArgs {
266 condition: bool,
268 mode: JumpMode,
270 dynamic: Word,
272 fixed: Word,
274}
275
276impl JumpArgs {
277 pub(crate) fn new(mode: JumpMode) -> Self {
278 Self {
279 condition: true,
280 mode,
281 dynamic: 0,
282 fixed: 0,
283 }
284 }
285
286 pub(crate) fn with_condition(mut self, condition: bool) -> Self {
287 self.condition = condition;
288 self
289 }
290
291 pub(crate) fn to_address(mut self, addr: Word) -> Self {
292 self.dynamic = addr;
293 self
294 }
295
296 pub(crate) fn plus_fixed(mut self, addr: Word) -> Self {
297 self.fixed = addr;
298 self
299 }
300
301 pub(crate) fn jump(&self, is: Reg<IS>, mut pc: RegMut<PC>) -> SimpleResult<()> {
302 if !self.condition {
303 return Ok(inc_pc(pc)?)
304 }
305
306 let offset_instructions = match self.mode {
307 JumpMode::Absolute => self.dynamic.saturating_add(self.fixed),
308 JumpMode::RelativeForwards | JumpMode::RelativeBackwards => {
311 self.dynamic.saturating_add(self.fixed).saturating_add(1)
312 }
313 };
314
315 let offset_bytes = offset_instructions.saturating_mul(Instruction::SIZE as Word);
316
317 let target_addr = match self.mode {
318 JumpMode::Absolute => is.saturating_add(offset_bytes),
319 JumpMode::RelativeForwards => pc.saturating_add(offset_bytes),
320 JumpMode::RelativeBackwards => pc
321 .checked_sub(offset_bytes)
322 .ok_or(PanicReason::MemoryOverflow)?,
323 };
324
325 if target_addr >= VM_MAX_RAM {
326 return Err(PanicReason::MemoryOverflow.into())
327 }
328
329 *pc = target_addr;
330 Ok(())
331 }
332}
333
334impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
335where
336 M: Memory,
337 S: InterpreterStorage,
338 Tx: ExecutableTransaction,
339{
340 pub fn prepare_call(
342 &mut self,
343 ra: RegId,
344 rb: RegId,
345 rc: RegId,
346 rd: RegId,
347 ) -> IoResult<(), S::DataError> {
348 self.prepare_call_inner(
349 self.registers[ra],
350 self.registers[rb],
351 self.registers[rc],
352 self.registers[rd],
353 )
354 }
355
356 fn prepare_call_inner(
358 &mut self,
359 call_params_pointer: Word,
360 amount_of_coins_to_forward: Word,
361 asset_id_pointer: Word,
362 amount_of_gas_to_forward: Word,
363 ) -> IoResult<(), S::DataError> {
364 let params = PrepareCallParams {
365 call_params_pointer,
366 asset_id_pointer,
367 amount_of_coins_to_forward,
368 amount_of_gas_to_forward,
369 };
370 let gas_cost = self.gas_costs().call();
371 let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte();
372 self.gas_charge(gas_cost.base())?;
375 let current_contract =
376 current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?;
377
378 PrepareCallCtx {
379 params,
380 registers: (&mut self.registers).into(),
381 memory: self.memory.as_mut(),
382 context: &mut self.context,
383 gas_cost,
384 runtime_balances: &mut self.balances,
385 storage: &mut self.storage,
386 input_contracts: InputContracts::new(
387 &self.input_contracts,
388 &mut self.panic_context,
389 ),
390 new_storage_gas_per_byte,
391 receipts: &mut self.receipts,
392 frames: &mut self.frames,
393 current_contract,
394 profiler: &mut self.profiler,
395 }
396 .prepare_call()
397 }
398}
399
400#[cfg_attr(test, derive(Default))]
401struct PrepareCallParams {
402 pub call_params_pointer: Word,
404 pub amount_of_coins_to_forward: Word,
406 pub asset_id_pointer: Word,
408 pub amount_of_gas_to_forward: Word,
410}
411
412struct PrepareCallSystemRegisters<'a> {
413 hp: Reg<'a, HP>,
414 sp: RegMut<'a, SP>,
415 ssp: RegMut<'a, SSP>,
416 fp: RegMut<'a, FP>,
417 pc: RegMut<'a, PC>,
418 is: RegMut<'a, IS>,
419 bal: RegMut<'a, BAL>,
420 cgas: RegMut<'a, CGAS>,
421 ggas: RegMut<'a, GGAS>,
422 flag: RegMut<'a, FLAG>,
423}
424
425struct PrepareCallRegisters<'a> {
426 system_registers: PrepareCallSystemRegisters<'a>,
427 program_registers: ProgramRegistersRef<'a>,
428 unused_registers: PrepareCallUnusedRegisters<'a>,
429}
430
431struct PrepareCallUnusedRegisters<'a> {
432 zero: Reg<'a, ZERO>,
433 one: Reg<'a, ONE>,
434 of: Reg<'a, OF>,
435 err: Reg<'a, ERR>,
436 ret: Reg<'a, RET>,
437 retl: Reg<'a, RETL>,
438}
439
440impl<'a> PrepareCallRegisters<'a> {
441 fn copy_registers(&self) -> [Word; VM_REGISTER_COUNT] {
442 copy_registers(&self.into(), &self.program_registers)
443 }
444}
445
446struct PrepareCallCtx<'vm, S> {
447 params: PrepareCallParams,
448 registers: PrepareCallRegisters<'vm>,
449 memory: &'vm mut MemoryInstance,
450 context: &'vm mut Context,
451 gas_cost: DependentCost,
452 runtime_balances: &'vm mut RuntimeBalances,
453 new_storage_gas_per_byte: Word,
454 storage: &'vm mut S,
455 input_contracts: InputContracts<'vm>,
456 receipts: &'vm mut ReceiptsCtx,
457 frames: &'vm mut Vec<CallFrame>,
458 current_contract: Option<ContractId>,
459 profiler: &'vm mut Profiler,
460}
461
462impl<'vm, S> PrepareCallCtx<'vm, S>
463where
464 S: InterpreterStorage,
465{
466 fn prepare_call(mut self) -> IoResult<(), S::DataError>
467 where
468 S: StorageSize<ContractsRawCode>
469 + ContractsAssetsStorage
470 + StorageRead<ContractsRawCode>
471 + StorageAsRef,
472 {
473 let call_bytes = self
474 .memory
475 .read(self.params.call_params_pointer, Call::LEN)?;
476 let call = Call::try_from(call_bytes)?;
477 let asset_id =
478 AssetId::new(self.memory.read_bytes(self.params.asset_id_pointer)?);
479
480 let code_size = contract_size(&self.storage, call.to())? as usize;
481 let code_size_padded =
482 padded_len_usize(code_size).ok_or(PanicReason::MemoryOverflow)?;
483
484 let total_size_in_stack = CallFrame::serialized_size()
485 .checked_add(code_size_padded)
486 .ok_or_else(|| Bug::new(BugVariant::CodeSizeOverflow))?;
487
488 let profiler = ProfileGas {
489 pc: self.registers.system_registers.pc.as_ref(),
490 is: self.registers.system_registers.is.as_ref(),
491 current_contract: self.current_contract,
492 profiler: self.profiler,
493 };
494 dependent_gas_charge_without_base(
495 self.registers.system_registers.cgas.as_mut(),
496 self.registers.system_registers.ggas.as_mut(),
497 profiler,
498 self.gas_cost,
499 code_size_padded as Word,
500 )?;
501
502 if let Some(source_contract) = self.current_contract {
503 balance_decrease(
504 self.storage,
505 &source_contract,
506 &asset_id,
507 self.params.amount_of_coins_to_forward,
508 )?;
509 } else {
510 let amount = self.params.amount_of_coins_to_forward;
511 external_asset_id_balance_sub(
512 self.runtime_balances,
513 self.memory,
514 &asset_id,
515 amount,
516 )?;
517 }
518
519 self.input_contracts.check(call.to())?;
520
521 let (_, created_new_entry) = balance_increase(
523 self.storage,
524 call.to(),
525 &asset_id,
526 self.params.amount_of_coins_to_forward,
527 )?;
528
529 if created_new_entry {
530 let profiler = ProfileGas {
532 pc: self.registers.system_registers.pc.as_ref(),
533 is: self.registers.system_registers.is.as_ref(),
534 current_contract: self.current_contract,
535 profiler: self.profiler,
536 };
537 gas_charge(
538 self.registers.system_registers.cgas.as_mut(),
539 self.registers.system_registers.ggas.as_mut(),
540 profiler,
541 ((Bytes32::LEN + WORD_SIZE) as u64)
542 .saturating_mul(self.new_storage_gas_per_byte),
543 )?;
544 }
545
546 let forward_gas_amount = cmp::min(
547 *self.registers.system_registers.cgas,
548 self.params.amount_of_gas_to_forward,
549 );
550
551 *self.registers.system_registers.cgas = (*self.registers.system_registers.cgas)
553 .checked_sub(forward_gas_amount)
554 .ok_or_else(|| Bug::new(BugVariant::ContextGasUnderflow))?;
555
556 let mut frame = CallFrame::new(
558 *call.to(),
559 asset_id,
560 self.registers.copy_registers(),
561 code_size_padded,
562 call.a(),
563 call.b(),
564 )
565 .ok_or(PanicReason::MemoryOverflow)?;
566 *frame.context_gas_mut() = *self.registers.system_registers.cgas;
567 *frame.global_gas_mut() = *self.registers.system_registers.ggas;
568
569 let old_sp = *self.registers.system_registers.sp;
571 let new_sp = old_sp.saturating_add(total_size_in_stack as Word);
572 self.memory.grow_stack(new_sp)?;
573 *self.registers.system_registers.sp = new_sp;
574 *self.registers.system_registers.ssp = new_sp;
575
576 let id = internal_contract(
577 self.context,
578 self.registers.system_registers.fp.as_ref(),
579 self.memory,
580 )
581 .unwrap_or_default();
582
583 set_frame_pointer(
584 self.context,
585 self.registers.system_registers.fp.as_mut(),
586 old_sp,
587 );
588
589 let dst = self.memory.write_noownerchecks(
592 *self.registers.system_registers.fp,
593 total_size_in_stack,
594 )?;
595 let (mem_frame, mem_code) = dst.split_at_mut(CallFrame::serialized_size());
596 mem_frame.copy_from_slice(&frame.to_bytes());
597 let (mem_code, mem_code_padding) = mem_code.split_at_mut(code_size);
598 read_contract(call.to(), self.storage, mem_code)?;
599 mem_code_padding.fill(0);
600
601 #[allow(clippy::arithmetic_side_effects)] let code_start =
603 (*self.registers.system_registers.fp) + CallFrame::serialized_size() as Word;
604
605 *self.registers.system_registers.pc = code_start;
606 *self.registers.system_registers.bal = self.params.amount_of_coins_to_forward;
607 *self.registers.system_registers.is = *self.registers.system_registers.pc;
608 *self.registers.system_registers.cgas = forward_gas_amount;
609 *self.registers.system_registers.flag = 0;
610
611 let receipt = Receipt::call(
612 id,
613 *call.to(),
614 self.params.amount_of_coins_to_forward,
615 asset_id,
616 forward_gas_amount,
617 call.a(),
618 call.b(),
619 *self.registers.system_registers.pc,
620 *self.registers.system_registers.is,
621 );
622
623 self.receipts.push(receipt)?;
624
625 self.frames.push(frame);
626
627 Ok(())
628 }
629}
630
631fn read_contract<S>(
632 contract: &ContractId,
633 storage: &S,
634 dst: &mut [u8],
635) -> IoResult<(), S::Error>
636where
637 S: StorageSize<ContractsRawCode> + StorageRead<ContractsRawCode> + StorageAsRef,
638{
639 let bytes_read = storage
640 .storage::<ContractsRawCode>()
641 .read(contract, 0, dst)
642 .map_err(RuntimeError::Storage)?
643 .ok_or(PanicReason::ContractNotFound)?;
644 if bytes_read != dst.len() {
645 return Err(PanicReason::ContractMismatch.into())
646 }
647 Ok(())
648}
649
650impl<'a> From<&'a PrepareCallRegisters<'_>> for SystemRegistersRef<'a> {
651 fn from(registers: &'a PrepareCallRegisters) -> Self {
652 Self {
653 hp: registers.system_registers.hp,
654 sp: registers.system_registers.sp.as_ref(),
655 ssp: registers.system_registers.ssp.as_ref(),
656 fp: registers.system_registers.fp.as_ref(),
657 pc: registers.system_registers.pc.as_ref(),
658 is: registers.system_registers.is.as_ref(),
659 bal: registers.system_registers.bal.as_ref(),
660 cgas: registers.system_registers.cgas.as_ref(),
661 ggas: registers.system_registers.ggas.as_ref(),
662 flag: registers.system_registers.flag.as_ref(),
663 zero: registers.unused_registers.zero,
664 one: registers.unused_registers.one,
665 of: registers.unused_registers.of,
666 err: registers.unused_registers.err,
667 ret: registers.unused_registers.ret,
668 retl: registers.unused_registers.retl,
669 }
670 }
671}
672
673impl<'reg> From<&'reg mut [Word; VM_REGISTER_COUNT]> for PrepareCallRegisters<'reg> {
674 fn from(registers: &'reg mut [Word; VM_REGISTER_COUNT]) -> Self {
675 let (r, w) = split_registers(registers);
676 let (r, u) = r.into();
677 Self {
678 system_registers: r,
679 program_registers: w.into(),
680 unused_registers: u,
681 }
682 }
683}
684
685impl<'reg> From<SystemRegisters<'reg>>
686 for (
687 PrepareCallSystemRegisters<'reg>,
688 PrepareCallUnusedRegisters<'reg>,
689 )
690{
691 fn from(registers: SystemRegisters<'reg>) -> Self {
692 let read = PrepareCallSystemRegisters {
693 hp: registers.hp.into(),
694 sp: registers.sp,
695 ssp: registers.ssp,
696 fp: registers.fp,
697 pc: registers.pc,
698 is: registers.is,
699 bal: registers.bal,
700 cgas: registers.cgas,
701 ggas: registers.ggas,
702 flag: registers.flag,
703 };
704
705 (
706 read,
707 PrepareCallUnusedRegisters {
708 zero: registers.zero.into(),
709 one: registers.one.into(),
710 of: registers.of.into(),
711 err: registers.err.into(),
712 ret: registers.ret.into(),
713 retl: registers.retl.into(),
714 },
715 )
716 }
717}