fuel_vm/interpreter/
internal.rs

1use super::{
2    ExecutableTransaction,
3    Interpreter,
4    Memory,
5    MemoryInstance,
6    RuntimeBalances,
7};
8use crate::{
9    constraints::reg_key::*,
10    context::Context,
11    error::SimpleResult,
12};
13
14use fuel_asm::{
15    Flags,
16    Instruction,
17    PanicReason,
18    RegId,
19};
20use fuel_tx::{
21    Output,
22    field::Outputs,
23};
24use fuel_types::{
25    AssetId,
26    BlockHeight,
27    Bytes32,
28    ContractId,
29    Word,
30    canonical::Serialize,
31};
32
33use core::ops::Range;
34
35#[cfg(test)]
36mod message_tests;
37#[cfg(test)]
38mod tests;
39
40impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
41where
42    M: Memory,
43    Tx: ExecutableTransaction,
44{
45    pub(crate) fn update_memory_output(&mut self, idx: usize) -> SimpleResult<()> {
46        let tx_offset = self.tx_offset();
47        update_memory_output(&self.tx, self.memory.as_mut(), tx_offset, idx)
48    }
49
50    /// Sets a non-system register to a value. If a system register is passed, return a
51    /// panic. Writes to the zero register are ignored.
52    pub fn set_user_reg_or_discard(&mut self, reg: RegId, val: Word) -> SimpleResult<()> {
53        if reg == RegId::ZERO {
54            return Ok(());
55        }
56
57        if reg < RegId::WRITABLE {
58            return Err(PanicReason::ReservedRegisterNotWritable.into());
59        }
60
61        self.registers[reg] = val;
62        Ok(())
63    }
64}
65
66/// Increase the variable output with a given asset ID. Modifies both the referenced tx
67/// and the serialized tx in vm memory.
68pub(crate) fn set_variable_output<Tx: ExecutableTransaction>(
69    tx: &mut Tx,
70    memory: &mut MemoryInstance,
71    tx_offset: usize,
72    idx: usize,
73    variable: Output,
74) -> SimpleResult<()> {
75    tx.replace_variable_output(idx, variable)?;
76    update_memory_output(tx, memory, tx_offset, idx)
77}
78
79fn absolute_output_offset<Tx: Outputs>(
80    tx: &Tx,
81    tx_offset: usize,
82    idx: usize,
83) -> Option<usize> {
84    tx.outputs_offset_at(idx)
85        .map(|offset| tx_offset.saturating_add(offset))
86}
87
88pub(crate) fn absolute_output_mem_range<Tx: Outputs>(
89    tx: &Tx,
90    tx_offset: usize,
91    idx: usize,
92) -> Option<Range<usize>> {
93    let offset = absolute_output_offset(tx, tx_offset, idx)?;
94    let size = tx.outputs().get(idx)?.size();
95    Some(offset..offset.saturating_add(size))
96}
97
98pub(crate) fn update_memory_output<Tx: ExecutableTransaction>(
99    tx: &Tx,
100    memory: &mut MemoryInstance,
101    tx_offset: usize,
102    idx: usize,
103) -> SimpleResult<()> {
104    let range = absolute_output_mem_range(tx, tx_offset, idx)
105        .ok_or(PanicReason::OutputNotFound)?;
106    let mut mem = memory.write_noownerchecks(range.start, range.len())?;
107    let output = tx
108        .outputs()
109        .get(idx)
110        .expect("Invalid output index; checked above");
111    output
112        .encode(&mut mem)
113        .expect("Unable to write output into given memory range");
114    Ok(())
115}
116
117impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
118where
119    M: Memory,
120{
121    pub(crate) fn set_flag(&mut self, a: Word) -> SimpleResult<()> {
122        let (SystemRegisters { flag, pc, .. }, _) = split_registers(&mut self.registers);
123        set_flag(flag, pc, a)
124    }
125
126    pub(crate) const fn context(&self) -> &Context {
127        &self.context
128    }
129
130    pub(crate) fn internal_contract(&self) -> Result<ContractId, PanicReason> {
131        internal_contract(&self.context, self.registers.fp(), self.memory.as_ref())
132    }
133
134    pub(crate) fn get_block_height(&self) -> Result<BlockHeight, PanicReason> {
135        self.context()
136            .block_height()
137            .ok_or(PanicReason::TransactionValidity)
138    }
139}
140
141pub(crate) fn clear_err(mut err: RegMut<ERR>) {
142    *err = 0;
143}
144
145pub(crate) fn set_err(mut err: RegMut<ERR>) {
146    *err = 1;
147}
148
149pub(crate) fn set_flag(
150    mut flag: RegMut<FLAG>,
151    pc: RegMut<PC>,
152    a: Word,
153) -> SimpleResult<()> {
154    let Some(flags) = Flags::from_bits(a) else {
155        return Err(PanicReason::InvalidFlags.into())
156    };
157
158    *flag = flags.bits();
159
160    Ok(inc_pc(pc)?)
161}
162
163pub(crate) fn inc_pc(mut pc: RegMut<PC>) -> Result<(), PanicReason> {
164    pc.checked_add(Instruction::SIZE as Word)
165        .ok_or(PanicReason::MemoryOverflow)
166        .map(|i| *pc = i)
167}
168
169pub(crate) fn tx_id(memory: &MemoryInstance) -> Bytes32 {
170    Bytes32::new(memory.read_bytes(0u64).expect("Bytes32::LEN < MEM_SIZE"))
171}
172
173/// Reduces the unspent balance of the base asset
174pub(crate) fn base_asset_balance_sub(
175    base_asset_id: &AssetId,
176    balances: &mut RuntimeBalances,
177    memory: &mut MemoryInstance,
178    value: Word,
179) -> SimpleResult<()> {
180    external_asset_id_balance_sub(balances, memory, base_asset_id, value)
181}
182
183/// Reduces the unspent balance of a given asset ID
184pub(crate) fn external_asset_id_balance_sub(
185    balances: &mut RuntimeBalances,
186    memory: &mut MemoryInstance,
187    asset_id: &AssetId,
188    value: Word,
189) -> SimpleResult<()> {
190    balances
191        .checked_balance_sub(memory, asset_id, value)
192        .ok_or(PanicReason::NotEnoughBalance)?;
193
194    Ok(())
195}
196
197pub(crate) fn current_contract(
198    context: &Context,
199    fp: Reg<FP>,
200    memory: &MemoryInstance,
201) -> Result<Option<ContractId>, PanicReason> {
202    if context.is_internal() {
203        Ok(Some(ContractId::new(memory.read_bytes(*fp)?)))
204    } else {
205        Ok(None)
206    }
207}
208
209pub(crate) fn internal_contract(
210    context: &Context,
211    fp: Reg<FP>,
212    memory: &MemoryInstance,
213) -> Result<ContractId, PanicReason> {
214    current_contract(context, fp, memory)?.ok_or(PanicReason::ExpectedInternalContext)
215}
216
217pub(crate) fn set_frame_pointer(
218    context: &mut Context,
219    mut register: RegMut<FP>,
220    fp: Word,
221) {
222    context.update_from_frame_pointer(fp);
223
224    *register = fp;
225}