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 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
66pub(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 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
174pub(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
184pub(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}