1use alloc::collections::BTreeSet;
4
5use super::{
6 ExecutableTransaction,
7 Interpreter,
8 Memory,
9 MemoryInstance,
10 PanicContext,
11 RuntimeBalances,
12 gas::gas_charge,
13 internal::{
14 external_asset_id_balance_sub,
15 inc_pc,
16 internal_contract,
17 set_variable_output,
18 },
19};
20use crate::{
21 constraints::reg_key::*,
22 consts::*,
23 context::Context,
24 convert,
25 error::{
26 IoResult,
27 RuntimeError,
28 },
29 interpreter::receipts::ReceiptsCtx,
30 storage::{
31 BlobData,
32 ContractsAssetsStorage,
33 ContractsRawCode,
34 InterpreterStorage,
35 },
36 verification::Verifier,
37};
38use fuel_asm::{
39 PanicReason,
40 RegId,
41 Word,
42};
43use fuel_storage::StorageSize;
44use fuel_tx::{
45 Output,
46 Receipt,
47};
48use fuel_types::{
49 Address,
50 AssetId,
51 BlobId,
52 Bytes32,
53 ContractId,
54};
55
56impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
57where
58 M: Memory,
59 S: InterpreterStorage,
60 Tx: ExecutableTransaction,
61 V: Verifier,
62{
63 pub(crate) fn contract_balance(
64 &mut self,
65 ra: RegId,
66 b: Word,
67 c: Word,
68 ) -> Result<(), RuntimeError<S::DataError>> {
69 let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
70 let result = &mut w[WriteRegKey::try_from(ra)?];
71 let input = ContractBalanceCtx {
72 storage: &self.storage,
73 memory: self.memory.as_mut(),
74 pc,
75 input_contracts: &self.input_contracts,
76 panic_context: &mut self.panic_context,
77 verifier: &mut self.verifier,
78 };
79 input.contract_balance(result, b, c)?;
80 Ok(())
81 }
82
83 pub(crate) fn transfer(
84 &mut self,
85 a: Word,
86 b: Word,
87 c: Word,
88 ) -> IoResult<(), S::DataError> {
89 let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte();
90 let tx_offset = self.tx_offset();
91 let (
92 SystemRegisters {
93 cgas,
94 ggas,
95 fp,
96 is,
97 pc,
98 ..
99 },
100 _,
101 ) = split_registers(&mut self.registers);
102 let input = TransferCtx {
103 storage: &mut self.storage,
104 memory: self.memory.as_mut(),
105 context: &self.context,
106 balances: &mut self.balances,
107 receipts: &mut self.receipts,
108 new_storage_gas_per_byte,
109 tx: &mut self.tx,
110 input_contracts: &self.input_contracts,
111 panic_context: &mut self.panic_context,
112 tx_offset,
113 cgas,
114 ggas,
115 fp: fp.as_ref(),
116 is: is.as_ref(),
117 pc,
118 verifier: &mut self.verifier,
119 };
120 input.transfer(a, b, c)
121 }
122
123 pub(crate) fn transfer_output(
124 &mut self,
125 a: Word,
126 b: Word,
127 c: Word,
128 d: Word,
129 ) -> IoResult<(), S::DataError> {
130 let tx_offset = self.tx_offset();
131 let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte();
132 let (
133 SystemRegisters {
134 cgas,
135 ggas,
136 fp,
137 is,
138 pc,
139 ..
140 },
141 _,
142 ) = split_registers(&mut self.registers);
143 let input = TransferCtx {
144 storage: &mut self.storage,
145 memory: self.memory.as_mut(),
146 context: &self.context,
147 balances: &mut self.balances,
148 receipts: &mut self.receipts,
149 new_storage_gas_per_byte,
150 tx: &mut self.tx,
151 input_contracts: &self.input_contracts,
152 panic_context: &mut self.panic_context,
153 tx_offset,
154 cgas,
155 ggas,
156 fp: fp.as_ref(),
157 is: is.as_ref(),
158 pc,
159 verifier: &mut self.verifier,
160 };
161 input.transfer_output(a, b, c, d)
162 }
163
164 pub(crate) fn check_contract_exists(
165 &self,
166 contract: &ContractId,
167 ) -> IoResult<bool, S::DataError> {
168 self.storage
169 .storage_contract_exists(contract)
170 .map_err(RuntimeError::Storage)
171 }
172}
173
174struct ContractBalanceCtx<'vm, S, V> {
175 storage: &'vm S,
176 memory: &'vm mut MemoryInstance,
177 pc: RegMut<'vm, PC>,
178 input_contracts: &'vm BTreeSet<ContractId>,
179 panic_context: &'vm mut PanicContext,
180 verifier: &'vm mut V,
181}
182
183impl<S, V> ContractBalanceCtx<'_, S, V> {
184 pub(crate) fn contract_balance(
185 self,
186 result: &mut Word,
187 b: Word,
188 c: Word,
189 ) -> IoResult<(), S::Error>
190 where
191 S: ContractsAssetsStorage,
192 V: Verifier,
193 {
194 let asset_id = AssetId::new(self.memory.read_bytes(b)?);
195 let contract_id = ContractId::new(self.memory.read_bytes(c)?);
196
197 self.verifier.check_contract_in_inputs(
198 self.panic_context,
199 self.input_contracts,
200 &contract_id,
201 )?;
202
203 let balance = balance(self.storage, &contract_id, &asset_id)?;
204
205 *result = balance;
206
207 inc_pc(self.pc);
208 Ok(())
209 }
210}
211struct TransferCtx<'vm, S, Tx, V> {
212 storage: &'vm mut S,
213 memory: &'vm mut MemoryInstance,
214 context: &'vm Context,
215 balances: &'vm mut RuntimeBalances,
216 receipts: &'vm mut ReceiptsCtx,
217
218 new_storage_gas_per_byte: Word,
219 tx: &'vm mut Tx,
220 input_contracts: &'vm BTreeSet<ContractId>,
221 panic_context: &'vm mut PanicContext,
222 tx_offset: usize,
223 cgas: RegMut<'vm, CGAS>,
224 ggas: RegMut<'vm, GGAS>,
225 fp: Reg<'vm, FP>,
226 is: Reg<'vm, IS>,
227 pc: RegMut<'vm, PC>,
228 verifier: &'vm mut V,
229}
230
231impl<S, Tx, V> TransferCtx<'_, S, Tx, V> {
232 pub(crate) fn transfer(
238 self,
239 recipient_contract_id_offset: Word,
240 transfer_amount: Word,
241 asset_id_offset: Word,
242 ) -> IoResult<(), S::Error>
243 where
244 Tx: ExecutableTransaction,
245 S: ContractsAssetsStorage,
246 V: Verifier,
247 {
248 let amount = transfer_amount;
249 let destination =
250 ContractId::from(self.memory.read_bytes(recipient_contract_id_offset)?);
251 let asset_id = AssetId::from(self.memory.read_bytes(asset_id_offset)?);
252
253 self.verifier.check_contract_in_inputs(
254 self.panic_context,
255 self.input_contracts,
256 &destination,
257 )?;
258
259 if amount == 0 {
260 return Err(PanicReason::TransferZeroCoins.into())
261 }
262
263 let internal_context = match internal_contract(self.context, self.fp, self.memory)
264 {
265 Ok(source_contract) => Some(source_contract),
267 Err(PanicReason::ExpectedInternalContext) => None,
269 Err(e) => return Err(e.into()),
271 };
272
273 if let Some(source_contract) = internal_context {
274 balance_decrease(self.storage, &source_contract, &asset_id, amount)?;
276 } else {
277 external_asset_id_balance_sub(self.balances, self.memory, &asset_id, amount)?;
279 }
280 let created_new_entry =
282 balance_increase(self.storage, &destination, &asset_id, amount)?;
283 if created_new_entry {
284 gas_charge(
286 self.cgas,
287 self.ggas,
288 ((Bytes32::LEN + WORD_SIZE) as u64)
289 .saturating_mul(self.new_storage_gas_per_byte),
290 )?;
291 }
292
293 let receipt = Receipt::transfer(
294 internal_context.unwrap_or_default(),
295 destination,
296 amount,
297 asset_id,
298 *self.pc,
299 *self.is,
300 );
301
302 self.receipts.push(receipt)?;
303
304 inc_pc(self.pc);
305 Ok(())
306 }
307
308 pub(crate) fn transfer_output(
315 self,
316 recipient_offset: Word,
317 output_index: Word,
318 transfer_amount: Word,
319 asset_id_offset: Word,
320 ) -> IoResult<(), S::Error>
321 where
322 Tx: ExecutableTransaction,
323 S: ContractsAssetsStorage,
324 V: Verifier,
325 {
326 let out_idx =
327 convert::to_usize(output_index).ok_or(PanicReason::OutputNotFound)?;
328 let to = Address::from(self.memory.read_bytes(recipient_offset)?);
329 let asset_id = AssetId::from(self.memory.read_bytes(asset_id_offset)?);
330 let amount = transfer_amount;
331
332 if amount == 0 {
333 return Err(PanicReason::TransferZeroCoins.into())
334 }
335
336 let internal_context = match internal_contract(self.context, self.fp, self.memory)
337 {
338 Ok(source_contract) => Some(source_contract),
340 Err(PanicReason::ExpectedInternalContext) => None,
342 Err(e) => return Err(e.into()),
344 };
345
346 if let Some(source_contract) = internal_context {
347 balance_decrease(self.storage, &source_contract, &asset_id, amount)?;
349 } else {
350 external_asset_id_balance_sub(self.balances, self.memory, &asset_id, amount)?;
352 }
353
354 let variable = Output::variable(to, amount, asset_id);
356
357 set_variable_output(self.tx, self.memory, self.tx_offset, out_idx, variable)?;
358
359 let receipt = Receipt::transfer_out(
360 internal_context.unwrap_or_default(),
361 to,
362 amount,
363 asset_id,
364 *self.pc,
365 *self.is,
366 );
367
368 self.receipts.push(receipt)?;
369
370 inc_pc(self.pc);
371 Ok(())
372 }
373}
374
375pub(crate) fn contract_size<S>(
376 storage: &S,
377 contract: &ContractId,
378) -> IoResult<usize, S::Error>
379where
380 S: StorageSize<ContractsRawCode> + ?Sized,
381{
382 let size = storage
383 .size_of_value(contract)
384 .map_err(RuntimeError::Storage)?
385 .ok_or(PanicReason::ContractNotFound)?;
386 Ok(size)
387}
388
389pub(crate) fn blob_size<S>(storage: &S, blob_id: &BlobId) -> IoResult<usize, S::Error>
390where
391 S: StorageSize<BlobData> + ?Sized,
392{
393 let size = storage
394 .size_of_value(blob_id)
395 .map_err(RuntimeError::Storage)?
396 .ok_or(PanicReason::BlobNotFound)?;
397 Ok(size)
398}
399
400pub(crate) fn balance<S>(
401 storage: &S,
402 contract: &ContractId,
403 asset_id: &AssetId,
404) -> IoResult<Word, S::Error>
405where
406 S: ContractsAssetsStorage + ?Sized,
407{
408 Ok(storage
409 .contract_asset_id_balance(contract, asset_id)
410 .map_err(RuntimeError::Storage)?
411 .unwrap_or_default())
412}
413
414pub fn balance_increase<S>(
417 storage: &mut S,
418 contract: &ContractId,
419 asset_id: &AssetId,
420 amount: Word,
421) -> IoResult<bool, S::Error>
422where
423 S: ContractsAssetsStorage + ?Sized,
424{
425 if amount == 0 {
426 return Ok(false)
428 }
429
430 let balance = balance(storage, contract, asset_id)?;
431 let balance = balance
432 .checked_add(amount)
433 .ok_or(PanicReason::BalanceOverflow)?;
434 let old_value = storage
435 .contract_asset_id_balance_replace(contract, asset_id, balance)
436 .map_err(RuntimeError::Storage)?;
437 Ok(old_value.is_none())
438}
439
440pub fn balance_decrease<S>(
442 storage: &mut S,
443 contract: &ContractId,
444 asset_id: &AssetId,
445 amount: Word,
446) -> IoResult<(), S::Error>
447where
448 S: ContractsAssetsStorage + ?Sized,
449{
450 if amount == 0 {
451 return Ok(())
453 }
454
455 let balance = balance(storage, contract, asset_id)?;
456 let balance = balance
457 .checked_sub(amount)
458 .ok_or(PanicReason::NotEnoughBalance)?;
459 storage
460 .contract_asset_id_balance_insert(contract, asset_id, balance)
461 .map_err(RuntimeError::Storage)?;
462 Ok(())
463}