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(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
173pub(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
183pub(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}