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    /// Get the current execution context
127    pub const fn context(&self) -> &Context {
128        &self.context
129    }
130
131    pub(crate) fn internal_contract(&self) -> Result<ContractId, PanicReason> {
132        internal_contract(&self.context, self.registers.fp(), self.memory.as_ref())
133    }
134
135    pub(crate) fn get_block_height(&self) -> Result<BlockHeight, PanicReason> {
136        self.context()
137            .block_height()
138            .ok_or(PanicReason::TransactionValidity)
139    }
140}
141
142pub(crate) fn clear_err(mut err: RegMut<ERR>) {
143    *err = 0;
144}
145
146pub(crate) fn set_err(mut err: RegMut<ERR>) {
147    *err = 1;
148}
149
150pub(crate) fn set_flag(
151    mut flag: RegMut<FLAG>,
152    pc: RegMut<PC>,
153    a: Word,
154) -> SimpleResult<()> {
155    let Some(flags) = Flags::from_bits(a) else {
156        return Err(PanicReason::InvalidFlags.into())
157    };
158
159    *flag = flags.bits();
160
161    Ok(inc_pc(pc)?)
162}
163
164pub(crate) fn inc_pc(mut pc: RegMut<PC>) -> Result<(), PanicReason> {
165    pc.checked_add(Instruction::SIZE as Word)
166        .ok_or(PanicReason::MemoryOverflow)
167        .map(|i| *pc = i)
168}
169
170pub(crate) fn tx_id(memory: &MemoryInstance) -> Bytes32 {
171    Bytes32::new(memory.read_bytes(0u64).expect("Bytes32::LEN < MEM_SIZE"))
172}
173
174/// Reduces the unspent balance of the base asset
175pub(crate) fn base_asset_balance_sub(
176    base_asset_id: &AssetId,
177    balances: &mut RuntimeBalances,
178    memory: &mut MemoryInstance,
179    value: Word,
180) -> SimpleResult<()> {
181    external_asset_id_balance_sub(balances, memory, base_asset_id, value)
182}
183
184/// Reduces the unspent balance of a given asset ID
185pub(crate) fn external_asset_id_balance_sub(
186    balances: &mut RuntimeBalances,
187    memory: &mut MemoryInstance,
188    asset_id: &AssetId,
189    value: Word,
190) -> SimpleResult<()> {
191    balances
192        .checked_balance_sub(memory, asset_id, value)
193        .ok_or(PanicReason::NotEnoughBalance)?;
194
195    Ok(())
196}
197
198pub(crate) fn current_contract(
199    context: &Context,
200    fp: Reg<FP>,
201    memory: &MemoryInstance,
202) -> Result<Option<ContractId>, PanicReason> {
203    if context.is_internal() {
204        Ok(Some(ContractId::new(memory.read_bytes(*fp)?)))
205    } else {
206        Ok(None)
207    }
208}
209
210pub(crate) fn internal_contract(
211    context: &Context,
212    fp: Reg<FP>,
213    memory: &MemoryInstance,
214) -> Result<ContractId, PanicReason> {
215    current_contract(context, fp, memory)?.ok_or(PanicReason::ExpectedInternalContext)
216}
217
218pub(crate) fn set_frame_pointer(
219    context: &mut Context,
220    mut register: RegMut<FP>,
221    fp: Word,
222) {
223    context.update_from_frame_pointer(fp);
224
225    *register = fp;
226}