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 ExecutableTransaction,
16 Interpreter,
17 Memory,
18 MemoryInstance,
19 PanicContext,
20 RuntimeBalances,
21 contract::{
22 balance_decrease,
23 balance_increase,
24 contract_size,
25 },
26 gas::{
27 dependent_gas_charge_without_base,
28 gas_charge,
29 },
30 internal::{
31 current_contract,
32 external_asset_id_balance_sub,
33 inc_pc,
34 internal_contract,
35 set_frame_pointer,
36 },
37 receipts::ReceiptsCtx,
38 },
39 prelude::{
40 Bug,
41 BugVariant,
42 },
43 storage::{
44 ContractsRawCode,
45 InterpreterStorage,
46 },
47 verification::Verifier,
48};
49use alloc::{
50 collections::BTreeSet,
51 vec::Vec,
52};
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 AssetId,
71 Bytes32,
72 ContractId,
73 Word,
74 bytes::padded_len_usize,
75 canonical::Serialize,
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, V> Interpreter<M, S, Tx, Ecal, V>
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 Assign,
258 RelativeIS,
260 RelativeForwards,
262 RelativeBackwards,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
267pub struct JumpArgs {
268 condition: bool,
270 mode: JumpMode,
272 dynamic: Word,
274 fixed: Word,
276}
277
278impl JumpArgs {
279 pub(crate) fn new(mode: JumpMode) -> Self {
280 Self {
281 condition: true,
282 mode,
283 dynamic: 0,
284 fixed: 0,
285 }
286 }
287
288 pub(crate) fn with_condition(mut self, condition: bool) -> Self {
289 self.condition = condition;
290 self
291 }
292
293 pub(crate) fn to_address(mut self, addr: Word) -> Self {
294 self.dynamic = addr;
295 self
296 }
297
298 pub(crate) fn plus_fixed(mut self, addr: Word) -> Self {
299 self.fixed = addr;
300 self
301 }
302
303 pub(crate) fn jump(&self, is: Reg<IS>, mut pc: RegMut<PC>) -> SimpleResult<()> {
304 if !self.condition {
305 return Ok(inc_pc(pc)?)
306 }
307
308 let target_addr = match self.mode {
309 JumpMode::Assign => self
310 .dynamic
311 .saturating_add(self.fixed.saturating_mul(Instruction::SIZE as Word)),
312 JumpMode::RelativeIS => {
313 let offset_instructions = self.dynamic.saturating_add(self.fixed);
314 let offset_bytes =
315 offset_instructions.saturating_mul(Instruction::SIZE as Word);
316 is.saturating_add(offset_bytes)
317 }
318 JumpMode::RelativeForwards => {
321 let offset_instructions =
322 self.dynamic.saturating_add(self.fixed).saturating_add(1);
323 let offset_bytes =
324 offset_instructions.saturating_mul(Instruction::SIZE as Word);
325 pc.saturating_add(offset_bytes)
326 }
327 JumpMode::RelativeBackwards => {
328 let offset_instructions =
329 self.dynamic.saturating_add(self.fixed).saturating_add(1);
330 let offset_bytes =
331 offset_instructions.saturating_mul(Instruction::SIZE as Word);
332 pc.checked_sub(offset_bytes)
333 .ok_or(PanicReason::MemoryOverflow)?
334 }
335 };
336
337 if target_addr >= VM_MAX_RAM {
338 return Err(PanicReason::MemoryOverflow.into())
339 }
340
341 *pc = target_addr;
342 Ok(())
343 }
344}
345
346impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
347where
348 M: Memory,
349 S: InterpreterStorage,
350 Tx: ExecutableTransaction,
351 V: Verifier,
352{
353 pub fn prepare_call(
355 &mut self,
356 ra: RegId,
357 rb: RegId,
358 rc: RegId,
359 rd: RegId,
360 ) -> IoResult<(), S::DataError> {
361 self.prepare_call_inner(
362 self.registers[ra],
363 self.registers[rb],
364 self.registers[rc],
365 self.registers[rd],
366 )
367 }
368
369 fn prepare_call_inner(
371 &mut self,
372 call_params_pointer: Word,
373 amount_of_coins_to_forward: Word,
374 asset_id_pointer: Word,
375 amount_of_gas_to_forward: Word,
376 ) -> IoResult<(), S::DataError> {
377 let params = PrepareCallParams {
378 call_params_pointer,
379 asset_id_pointer,
380 amount_of_coins_to_forward,
381 amount_of_gas_to_forward,
382 };
383 let gas_cost = self.gas_costs().call();
384 let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte();
385 self.gas_charge(gas_cost.base())?;
388 let current_contract =
389 current_contract(&self.context, self.registers.fp(), self.memory.as_ref())?;
390
391 PrepareCallCtx {
392 params,
393 registers: (&mut self.registers).into(),
394 memory: self.memory.as_mut(),
395 context: &mut self.context,
396 gas_cost,
397 runtime_balances: &mut self.balances,
398 storage: &mut self.storage,
399 input_contracts: &self.input_contracts,
400 panic_context: &mut self.panic_context,
401 new_storage_gas_per_byte,
402 receipts: &mut self.receipts,
403 frames: &mut self.frames,
404 current_contract,
405 verifier: &mut self.verifier,
406 }
407 .prepare_call()
408 }
409}
410
411#[cfg_attr(test, derive(Default))]
412struct PrepareCallParams {
413 pub call_params_pointer: Word,
415 pub amount_of_coins_to_forward: Word,
417 pub asset_id_pointer: Word,
419 pub amount_of_gas_to_forward: Word,
421}
422
423struct PrepareCallSystemRegisters<'a> {
424 hp: Reg<'a, HP>,
425 sp: RegMut<'a, SP>,
426 ssp: RegMut<'a, SSP>,
427 fp: RegMut<'a, FP>,
428 pc: RegMut<'a, PC>,
429 is: RegMut<'a, IS>,
430 bal: RegMut<'a, BAL>,
431 cgas: RegMut<'a, CGAS>,
432 ggas: RegMut<'a, GGAS>,
433 flag: RegMut<'a, FLAG>,
434}
435
436struct PrepareCallRegisters<'a> {
437 system_registers: PrepareCallSystemRegisters<'a>,
438 program_registers: ProgramRegistersRef<'a>,
439 unused_registers: PrepareCallUnusedRegisters<'a>,
440}
441
442struct PrepareCallUnusedRegisters<'a> {
443 zero: Reg<'a, ZERO>,
444 one: Reg<'a, ONE>,
445 of: Reg<'a, OF>,
446 err: Reg<'a, ERR>,
447 ret: Reg<'a, RET>,
448 retl: Reg<'a, RETL>,
449}
450
451impl PrepareCallRegisters<'_> {
452 fn copy_registers(&self) -> [Word; VM_REGISTER_COUNT] {
453 copy_registers(&self.into(), &self.program_registers)
454 }
455}
456
457struct PrepareCallCtx<'vm, S, V> {
458 params: PrepareCallParams,
459 registers: PrepareCallRegisters<'vm>,
460 memory: &'vm mut MemoryInstance,
461 context: &'vm mut Context,
462 gas_cost: DependentCost,
463 runtime_balances: &'vm mut RuntimeBalances,
464 new_storage_gas_per_byte: Word,
465 storage: &'vm mut S,
466 input_contracts: &'vm BTreeSet<ContractId>,
467 panic_context: &'vm mut PanicContext,
468 receipts: &'vm mut ReceiptsCtx,
469 frames: &'vm mut Vec<CallFrame>,
470 current_contract: Option<ContractId>,
471 verifier: &'vm mut V,
472}
473
474impl<S, V> PrepareCallCtx<'_, S, V> {
475 fn prepare_call(mut self) -> IoResult<(), S::DataError>
476 where
477 S: InterpreterStorage,
478 V: Verifier,
479 {
480 let call_bytes = self
481 .memory
482 .read(self.params.call_params_pointer, Call::LEN)?;
483 let call = Call::try_from(call_bytes)?;
484 let asset_id =
485 AssetId::new(self.memory.read_bytes(self.params.asset_id_pointer)?);
486
487 let code_size = contract_size(&self.storage, call.to())? as usize;
488 let code_size_padded =
489 padded_len_usize(code_size).ok_or(PanicReason::MemoryOverflow)?;
490
491 let total_size_in_stack = CallFrame::serialized_size()
492 .checked_add(code_size_padded)
493 .ok_or_else(|| Bug::new(BugVariant::CodeSizeOverflow))?;
494
495 dependent_gas_charge_without_base(
496 self.registers.system_registers.cgas.as_mut(),
497 self.registers.system_registers.ggas.as_mut(),
498 self.gas_cost,
499 code_size_padded as Word,
500 )?;
501
502 let amount = self.params.amount_of_coins_to_forward;
503 if let Some(source_contract) = self.current_contract {
504 balance_decrease(self.storage, &source_contract, &asset_id, amount)?;
505 } else {
506 external_asset_id_balance_sub(
507 self.runtime_balances,
508 self.memory,
509 &asset_id,
510 amount,
511 )?;
512 }
513
514 self.verifier.check_contract_in_inputs(
515 self.panic_context,
516 self.input_contracts,
517 call.to(),
518 )?;
519
520 let created_new_entry = balance_increase(
522 self.storage,
523 call.to(),
524 &asset_id,
525 self.params.amount_of_coins_to_forward,
526 )?;
527
528 if created_new_entry {
529 gas_charge(
531 self.registers.system_registers.cgas.as_mut(),
532 self.registers.system_registers.ggas.as_mut(),
533 ((Bytes32::LEN + WORD_SIZE) as u64)
534 .saturating_mul(self.new_storage_gas_per_byte),
535 )?;
536 }
537
538 let forward_gas_amount = cmp::min(
539 *self.registers.system_registers.cgas,
540 self.params.amount_of_gas_to_forward,
541 );
542
543 *self.registers.system_registers.cgas = (*self.registers.system_registers.cgas)
545 .checked_sub(forward_gas_amount)
546 .ok_or_else(|| Bug::new(BugVariant::ContextGasUnderflow))?;
547
548 let mut frame = CallFrame::new(
550 *call.to(),
551 asset_id,
552 self.registers.copy_registers(),
553 code_size_padded,
554 call.a(),
555 call.b(),
556 )
557 .ok_or(PanicReason::MemoryOverflow)?;
558 *frame.context_gas_mut() = *self.registers.system_registers.cgas;
559 *frame.global_gas_mut() = *self.registers.system_registers.ggas;
560
561 let old_sp = *self.registers.system_registers.sp;
563 let new_sp = old_sp.saturating_add(total_size_in_stack as Word);
564 self.memory.grow_stack(new_sp)?;
565 *self.registers.system_registers.sp = new_sp;
566 *self.registers.system_registers.ssp = new_sp;
567
568 let id = internal_contract(
569 self.context,
570 self.registers.system_registers.fp.as_ref(),
571 self.memory,
572 )
573 .unwrap_or_default();
574
575 set_frame_pointer(
576 self.context,
577 self.registers.system_registers.fp.as_mut(),
578 old_sp,
579 );
580
581 let dst = self.memory.write_noownerchecks(
584 *self.registers.system_registers.fp,
585 total_size_in_stack,
586 )?;
587 let (mem_frame, mem_code) = dst.split_at_mut(CallFrame::serialized_size());
588 mem_frame.copy_from_slice(&frame.to_bytes());
589 let (mem_code, mem_code_padding) = mem_code.split_at_mut(code_size);
590 read_contract(call.to(), self.storage, mem_code)?;
591 mem_code_padding.fill(0);
592
593 #[allow(clippy::arithmetic_side_effects)] let code_start =
595 (*self.registers.system_registers.fp) + CallFrame::serialized_size() as Word;
596
597 *self.registers.system_registers.pc = code_start;
598 *self.registers.system_registers.bal = self.params.amount_of_coins_to_forward;
599 *self.registers.system_registers.is = *self.registers.system_registers.pc;
600 *self.registers.system_registers.cgas = forward_gas_amount;
601 *self.registers.system_registers.flag = 0;
602
603 let receipt = Receipt::call(
604 id,
605 *call.to(),
606 self.params.amount_of_coins_to_forward,
607 asset_id,
608 forward_gas_amount,
609 call.a(),
610 call.b(),
611 *self.registers.system_registers.pc,
612 *self.registers.system_registers.is,
613 );
614
615 self.receipts.push(receipt)?;
616
617 self.frames.push(frame);
618
619 Ok(())
620 }
621}
622
623fn read_contract<S>(
624 contract: &ContractId,
625 storage: &S,
626 dst: &mut [u8],
627) -> IoResult<(), S::Error>
628where
629 S: StorageSize<ContractsRawCode> + StorageRead<ContractsRawCode> + StorageAsRef,
630{
631 if !storage
632 .storage::<ContractsRawCode>()
633 .read(contract, 0, dst)
634 .map_err(RuntimeError::Storage)?
635 {
636 return Err(PanicReason::ContractNotFound.into());
637 }
638 Ok(())
639}
640
641impl<'a> From<&'a PrepareCallRegisters<'_>> for SystemRegistersRef<'a> {
642 fn from(registers: &'a PrepareCallRegisters) -> Self {
643 Self {
644 hp: registers.system_registers.hp,
645 sp: registers.system_registers.sp.as_ref(),
646 ssp: registers.system_registers.ssp.as_ref(),
647 fp: registers.system_registers.fp.as_ref(),
648 pc: registers.system_registers.pc.as_ref(),
649 is: registers.system_registers.is.as_ref(),
650 bal: registers.system_registers.bal.as_ref(),
651 cgas: registers.system_registers.cgas.as_ref(),
652 ggas: registers.system_registers.ggas.as_ref(),
653 flag: registers.system_registers.flag.as_ref(),
654 zero: registers.unused_registers.zero,
655 one: registers.unused_registers.one,
656 of: registers.unused_registers.of,
657 err: registers.unused_registers.err,
658 ret: registers.unused_registers.ret,
659 retl: registers.unused_registers.retl,
660 }
661 }
662}
663
664impl<'reg> From<&'reg mut [Word; VM_REGISTER_COUNT]> for PrepareCallRegisters<'reg> {
665 fn from(registers: &'reg mut [Word; VM_REGISTER_COUNT]) -> Self {
666 let (r, w) = split_registers(registers);
667 let (r, u) = r.into();
668 Self {
669 system_registers: r,
670 program_registers: w.into(),
671 unused_registers: u,
672 }
673 }
674}
675
676impl<'reg> From<SystemRegisters<'reg>>
677 for (
678 PrepareCallSystemRegisters<'reg>,
679 PrepareCallUnusedRegisters<'reg>,
680 )
681{
682 fn from(registers: SystemRegisters<'reg>) -> Self {
683 let read = PrepareCallSystemRegisters {
684 hp: registers.hp.into(),
685 sp: registers.sp,
686 ssp: registers.ssp,
687 fp: registers.fp,
688 pc: registers.pc,
689 is: registers.is,
690 bal: registers.bal,
691 cgas: registers.cgas,
692 ggas: registers.ggas,
693 flag: registers.flag,
694 };
695
696 (
697 read,
698 PrepareCallUnusedRegisters {
699 zero: registers.zero.into(),
700 one: registers.one.into(),
701 of: registers.of.into(),
702 err: registers.err.into(),
703 ret: registers.ret.into(),
704 retl: registers.retl.into(),
705 },
706 )
707 }
708}