1use crate::{
2 call::CallFrame,
3 constraints::reg_key::*,
4 context::Context,
5 error::{
6 IoResult,
7 RuntimeError,
8 SimpleResult,
9 },
10 interpreter::{
11 ExecutableTransaction,
12 Interpreter,
13 Memory,
14 MemoryInstance,
15 RuntimeBalances,
16 contract::{
17 balance,
18 balance_decrease,
19 blob_size,
20 contract_size,
21 },
22 gas::{
23 dependent_gas_charge_without_base,
24 gas_charge,
25 },
26 internal::{
27 base_asset_balance_sub,
28 inc_pc,
29 internal_contract,
30 tx_id,
31 },
32 memory::{
33 OwnershipRegisters,
34 copy_from_storage_zero_fill,
35 },
36 receipts::ReceiptsCtx,
37 },
38 storage::{
39 BlobData,
40 ContractsAssetsStorage,
41 ContractsRawCode,
42 InterpreterStorage,
43 },
44 verification::Verifier,
45};
46use alloc::collections::BTreeSet;
47use fuel_asm::{
48 Imm06,
49 PanicReason,
50 RegId,
51};
52use fuel_storage::StorageSize;
53use fuel_tx::{
54 BlobId,
55 ContractIdExt,
56 DependentCost,
57 Receipt,
58 consts::BALANCE_ENTRY_SIZE,
59};
60use fuel_types::{
61 Address,
62 AssetId,
63 BlockHeight,
64 Bytes32,
65 ContractId,
66 SubAssetId,
67 Word,
68 bytes::{
69 self,
70 padded_len_word,
71 },
72};
73
74use super::PanicContext;
75
76#[cfg(test)]
77mod code_tests;
78#[cfg(test)]
79mod croo_tests;
80#[cfg(test)]
81mod other_tests;
82#[cfg(test)]
83mod smo_tests;
84#[cfg(test)]
85mod test;
86
87impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
88where
89 M: Memory,
90 Tx: ExecutableTransaction,
91 S: InterpreterStorage,
92 V: Verifier,
93{
94 pub(crate) fn load_contract_code(
104 &mut self,
105 addr: Word,
106 offset: Word,
107 length_unpadded: Word,
108 mode: Imm06,
109 ) -> IoResult<(), S::DataError> {
110 let gas_cost = self.gas_costs().ldc();
111 self.gas_charge(gas_cost.base())?;
114 let contract_max_size = self.contract_max_size();
115 let (
116 SystemRegisters {
117 cgas,
118 ggas,
119 ssp,
120 sp,
121 hp,
122 fp,
123 pc,
124 ..
125 },
126 _,
127 ) = split_registers(&mut self.registers);
128 let input = LoadContractCodeCtx {
129 memory: self.memory.as_mut(),
130 context: &self.context,
131 storage: &mut self.storage,
132 contract_max_size,
133 input_contracts: &self.input_contracts,
134 panic_context: &mut self.panic_context,
135 gas_cost,
136 cgas,
137 ggas,
138 ssp,
139 sp,
140 hp: hp.as_ref(),
141 fp: fp.as_ref(),
142 pc,
143 verifier: &mut self.verifier,
144 };
145
146 match mode.to_u8() {
147 0 => input.load_contract_code(addr, offset, length_unpadded),
148 1 => input.load_blob_code(addr, offset, length_unpadded),
149 2 => input.load_memory_code(addr, offset, length_unpadded),
150 _ => Err(PanicReason::InvalidImmediateValue.into()),
151 }
152 }
153
154 pub(crate) fn burn(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> {
155 let (SystemRegisters { fp, pc, is, .. }, _) =
156 split_registers(&mut self.registers);
157 BurnCtx {
158 storage: &mut self.storage,
159 context: &self.context,
160 memory: self.memory.as_ref(),
161 receipts: &mut self.receipts,
162 fp: fp.as_ref(),
163 pc,
164 is: is.as_ref(),
165 }
166 .burn(a, b)
167 }
168
169 pub(crate) fn mint(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> {
170 let new_storage_gas_per_byte = self.gas_costs().new_storage_per_byte();
171 let (
172 SystemRegisters {
173 cgas,
174 ggas,
175 fp,
176 pc,
177 is,
178 ..
179 },
180 _,
181 ) = split_registers(&mut self.registers);
182 MintCtx {
183 storage: &mut self.storage,
184 context: &self.context,
185 memory: self.memory.as_ref(),
186 receipts: &mut self.receipts,
187
188 new_storage_gas_per_byte,
189 cgas,
190 ggas,
191 fp: fp.as_ref(),
192 pc,
193 is: is.as_ref(),
194 }
195 .mint(a, b)
196 }
197
198 pub(crate) fn code_copy(
199 &mut self,
200 a: Word,
201 b: Word,
202 c: Word,
203 d: Word,
204 ) -> IoResult<(), S::DataError> {
205 let gas_cost = self.gas_costs().ccp();
206 self.gas_charge(gas_cost.base())?;
209 let owner = self.ownership_registers();
210 let (SystemRegisters { cgas, ggas, pc, .. }, _) =
211 split_registers(&mut self.registers);
212 let input = CodeCopyCtx {
213 memory: self.memory.as_mut(),
214 input_contracts: &self.input_contracts,
215 panic_context: &mut self.panic_context,
216 storage: &mut self.storage,
217 owner,
218 gas_cost,
219 cgas,
220 ggas,
221 pc,
222 verifier: &mut self.verifier,
223 };
224 input.code_copy(a, b, c, d)
225 }
226
227 pub(crate) fn block_hash(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> {
228 let owner = self.ownership_registers();
229 block_hash(
230 &self.storage,
231 self.memory.as_mut(),
232 owner,
233 self.registers.pc_mut(),
234 a,
235 b,
236 )
237 }
238
239 pub(crate) fn block_height(&mut self, ra: RegId) -> IoResult<(), S::DataError> {
240 let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
241 let result = &mut w[WriteRegKey::try_from(ra)?];
242 Ok(block_height(&self.context, pc, result)?)
243 }
244
245 pub(crate) fn block_proposer(&mut self, a: Word) -> IoResult<(), S::DataError> {
246 let owner = self.ownership_registers();
247 coinbase(
248 &self.storage,
249 self.memory.as_mut(),
250 owner,
251 self.registers.pc_mut(),
252 a,
253 )
254 }
255
256 pub(crate) fn code_root(&mut self, a: Word, b: Word) -> IoResult<(), S::DataError> {
257 let gas_cost = self.gas_costs().croo();
258 self.gas_charge(gas_cost.base())?;
259 let owner = self.ownership_registers();
260 let (SystemRegisters { cgas, ggas, pc, .. }, _) =
261 split_registers(&mut self.registers);
262 CodeRootCtx {
263 memory: self.memory.as_mut(),
264 storage: &mut self.storage,
265 gas_cost,
266 input_contracts: &self.input_contracts,
267 panic_context: &mut self.panic_context,
268 cgas,
269 ggas,
270 owner,
271 pc,
272 verifier: &mut self.verifier,
273 }
274 .code_root(a, b)
275 }
276
277 pub(crate) fn code_size(&mut self, ra: RegId, b: Word) -> IoResult<(), S::DataError> {
278 let gas_cost = self.gas_costs().csiz();
279 self.gas_charge(gas_cost.base())?;
282 let (SystemRegisters { cgas, ggas, pc, .. }, mut w) =
283 split_registers(&mut self.registers);
284 let result = &mut w[WriteRegKey::try_from(ra)?];
285 let input = CodeSizeCtx {
286 memory: self.memory.as_mut(),
287 storage: &mut self.storage,
288 gas_cost,
289 input_contracts: &self.input_contracts,
290 panic_context: &mut self.panic_context,
291 cgas,
292 ggas,
293 pc,
294 verifier: &mut self.verifier,
295 };
296 input.code_size(result, b)
297 }
298
299 pub(crate) fn timestamp(&mut self, ra: RegId, b: Word) -> IoResult<(), S::DataError> {
300 let block_height = self.get_block_height()?;
301 let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
302 let result = &mut w[WriteRegKey::try_from(ra)?];
303 timestamp(&self.storage, block_height, pc, result, b)
304 }
305
306 pub(crate) fn message_output(
307 &mut self,
308 a: Word,
309 b: Word,
310 c: Word,
311 d: Word,
312 ) -> IoResult<(), S::DataError> {
313 let base_asset_id = self.interpreter_params.base_asset_id;
314 let max_message_data_length = self.max_message_data_length();
315 let (SystemRegisters { fp, pc, .. }, _) = split_registers(&mut self.registers);
316 let input = MessageOutputCtx {
317 base_asset_id,
318 max_message_data_length,
319 memory: self.memory.as_mut(),
320 receipts: &mut self.receipts,
321 balances: &mut self.balances,
322 storage: &mut self.storage,
323 current_contract: self.frames.last().map(|frame| frame.to()).copied(),
324 fp: fp.as_ref(),
325 pc,
326 recipient_mem_address: a,
327 msg_data_ptr: b,
328 msg_data_len: c,
329 amount_coins_to_send: d,
330 };
331 input.message_output()
332 }
333}
334
335struct LoadContractCodeCtx<'vm, S, V> {
336 contract_max_size: u64,
337 memory: &'vm mut MemoryInstance,
338 context: &'vm Context,
339 input_contracts: &'vm BTreeSet<ContractId>,
340 panic_context: &'vm mut PanicContext,
341 storage: &'vm S,
342 gas_cost: DependentCost,
343 cgas: RegMut<'vm, CGAS>,
344 ggas: RegMut<'vm, GGAS>,
345 ssp: RegMut<'vm, SSP>,
346 sp: RegMut<'vm, SP>,
347 hp: Reg<'vm, HP>,
348 fp: Reg<'vm, FP>,
349 pc: RegMut<'vm, PC>,
350 verifier: &'vm mut V,
351}
352
353impl<S, V> LoadContractCodeCtx<'_, S, V> {
354 pub(crate) fn load_contract_code(
362 mut self,
363 contract_id_addr: Word,
364 contract_offset: Word,
365 length_unpadded: Word,
366 ) -> IoResult<(), S::DataError>
367 where
368 S: InterpreterStorage,
369 V: Verifier,
370 {
371 let ssp = *self.ssp;
372 let sp = *self.sp;
373 let region_start = ssp;
374
375 if self.context.is_predicate() {
377 return Err(PanicReason::ContractInstructionNotAllowed.into())
378 }
379
380 if ssp != sp {
381 return Err(PanicReason::ExpectedUnallocatedStack.into())
382 }
383
384 let contract_id = ContractId::from(self.memory.read_bytes(contract_id_addr)?);
385
386 let length =
387 padded_len_word(length_unpadded).ok_or(PanicReason::MemoryOverflow)?;
388
389 if length > self.contract_max_size {
390 return Err(PanicReason::ContractMaxSize.into())
391 }
392
393 self.verifier.check_contract_in_inputs(
394 self.panic_context,
395 self.input_contracts,
396 &contract_id,
397 )?;
398
399 let contract_len = contract_size(&self.storage, &contract_id)?;
401 let charge_len = core::cmp::max(contract_len as u64, length);
402 dependent_gas_charge_without_base(
403 self.cgas,
404 self.ggas,
405 self.gas_cost,
406 charge_len,
407 )?;
408
409 let new_sp = ssp.saturating_add(length);
410 self.memory.grow_stack(new_sp)?;
411
412 let owner =
414 OwnershipRegisters::only_allow_stack_write(new_sp, *self.ssp, *self.hp);
415
416 *self.sp = new_sp;
418 *self.ssp = new_sp;
419
420 copy_from_storage_zero_fill::<ContractsRawCode, _>(
421 self.memory,
422 owner,
423 self.storage,
424 region_start,
425 length,
426 &contract_id,
427 contract_offset,
428 contract_len,
429 PanicReason::ContractNotFound,
430 )?;
431
432 if self.context.is_internal() {
434 let code_size_ptr =
435 (*self.fp).saturating_add(CallFrame::code_size_offset() as Word);
436 let old_code_size =
437 Word::from_be_bytes(self.memory.read_bytes(code_size_ptr)?);
438 let old_code_size =
439 padded_len_word(old_code_size).ok_or(PanicReason::MemoryOverflow)?;
440 let new_code_size = old_code_size
441 .checked_add(length as Word)
442 .ok_or(PanicReason::MemoryOverflow)?;
443
444 self.memory
445 .write_bytes_noownerchecks(code_size_ptr, new_code_size.to_be_bytes())?;
446 }
447
448 inc_pc(self.pc);
449
450 Ok(())
451 }
452
453 pub(crate) fn load_blob_code(
461 mut self,
462 blob_id_addr: Word,
463 blob_offset: Word,
464 length_unpadded: Word,
465 ) -> IoResult<(), S::DataError>
466 where
467 S: InterpreterStorage,
468 {
469 let ssp = *self.ssp;
470 let sp = *self.sp;
471 let region_start = ssp;
472
473 if ssp != sp {
474 return Err(PanicReason::ExpectedUnallocatedStack.into())
475 }
476
477 let blob_id = BlobId::from(self.memory.read_bytes(blob_id_addr)?);
478
479 let length = bytes::padded_len_word(length_unpadded).unwrap_or(Word::MAX);
480
481 let blob_len = blob_size(self.storage, &blob_id)?;
482
483 let charge_len = core::cmp::max(blob_len as u64, length);
485 dependent_gas_charge_without_base(
486 self.cgas,
487 self.ggas,
488 self.gas_cost,
489 charge_len,
490 )?;
491
492 let new_sp = ssp.saturating_add(length);
493 self.memory.grow_stack(new_sp)?;
494
495 let owner =
497 OwnershipRegisters::only_allow_stack_write(new_sp, *self.ssp, *self.hp);
498
499 *self.sp = new_sp;
501 *self.ssp = new_sp;
502
503 copy_from_storage_zero_fill::<BlobData, _>(
505 self.memory,
506 owner,
507 self.storage,
508 region_start,
509 length,
510 &blob_id,
511 blob_offset,
512 blob_len,
513 PanicReason::BlobNotFound,
514 )?;
515
516 if self.context.is_internal() {
518 let code_size_ptr =
519 (*self.fp).saturating_add(CallFrame::code_size_offset() as Word);
520 let old_code_size =
521 Word::from_be_bytes(self.memory.read_bytes(code_size_ptr)?);
522 let old_code_size = padded_len_word(old_code_size)
523 .expect("Code size cannot overflow with padding");
524 let new_code_size = old_code_size
525 .checked_add(length as Word)
526 .ok_or(PanicReason::MemoryOverflow)?;
527
528 self.memory
529 .write_bytes_noownerchecks(code_size_ptr, new_code_size.to_be_bytes())?;
530 }
531
532 inc_pc(self.pc);
533
534 Ok(())
535 }
536
537 pub(crate) fn load_memory_code(
544 mut self,
545 input_src_addr: Word,
546 input_offset: Word,
547 length_unpadded: Word,
548 ) -> IoResult<(), S::DataError>
549 where
550 S: InterpreterStorage,
551 {
552 let ssp = *self.ssp;
553 let sp = *self.sp;
554 let dst = ssp;
555
556 if ssp != sp {
557 return Err(PanicReason::ExpectedUnallocatedStack.into())
558 }
559
560 if length_unpadded == 0 {
561 inc_pc(self.pc);
562 return Ok(())
563 }
564
565 let length = bytes::padded_len_word(length_unpadded).unwrap_or(Word::MAX);
566 let length_padding = length.saturating_sub(length_unpadded);
567
568 let charge_len = length;
570 dependent_gas_charge_without_base(
571 self.cgas,
572 self.ggas,
573 self.gas_cost,
574 charge_len,
575 )?;
576
577 let new_sp = ssp.saturating_add(length);
578 self.memory.grow_stack(new_sp)?;
579
580 let owner = OwnershipRegisters::only_allow_stack_write(new_sp, ssp, *self.hp);
582 let src = input_src_addr.saturating_add(input_offset);
583
584 self.memory.memcopy(dst, src, length_unpadded, owner)?;
586
587 if length_padding > 0 {
589 self.memory
590 .write(owner, dst.saturating_add(length_unpadded), length_padding)?
591 .fill(0);
592 }
593
594 *self.sp = new_sp;
596 *self.ssp = new_sp;
597
598 if self.context.is_internal() {
600 let code_size_ptr =
601 (*self.fp).saturating_add(CallFrame::code_size_offset() as Word);
602 let old_code_size =
603 Word::from_be_bytes(self.memory.read_bytes(code_size_ptr)?);
604 let old_code_size = padded_len_word(old_code_size)
605 .expect("Code size cannot overflow with padding");
606 let new_code_size = old_code_size
607 .checked_add(length as Word)
608 .ok_or(PanicReason::MemoryOverflow)?;
609
610 self.memory
611 .write_bytes_noownerchecks(code_size_ptr, new_code_size.to_be_bytes())?;
612 }
613
614 inc_pc(self.pc);
615
616 Ok(())
617 }
618}
619
620struct BurnCtx<'vm, S> {
621 storage: &'vm mut S,
622 context: &'vm Context,
623 memory: &'vm MemoryInstance,
624 receipts: &'vm mut ReceiptsCtx,
625 fp: Reg<'vm, FP>,
626 pc: RegMut<'vm, PC>,
627 is: Reg<'vm, IS>,
628}
629
630impl<S> BurnCtx<'_, S>
631where
632 S: ContractsAssetsStorage,
633{
634 pub(crate) fn burn(self, a: Word, b: Word) -> IoResult<(), S::Error> {
635 let contract_id = internal_contract(self.context, self.fp, self.memory)?;
636 let sub_id = SubAssetId::new(self.memory.read_bytes(b)?);
637 let asset_id = contract_id.asset_id(&sub_id);
638
639 let balance = balance(self.storage, &contract_id, &asset_id)?;
640 let balance = balance
641 .checked_sub(a)
642 .ok_or(PanicReason::NotEnoughBalance)?;
643
644 self.storage
645 .contract_asset_id_balance_insert(&contract_id, &asset_id, balance)
646 .map_err(RuntimeError::Storage)?;
647
648 let receipt = Receipt::burn(sub_id, contract_id, a, *self.pc, *self.is);
649
650 self.receipts.push(receipt)?;
651
652 inc_pc(self.pc);
653 Ok(())
654 }
655}
656
657struct MintCtx<'vm, S> {
658 storage: &'vm mut S,
659 context: &'vm Context,
660 memory: &'vm MemoryInstance,
661
662 receipts: &'vm mut ReceiptsCtx,
663 new_storage_gas_per_byte: Word,
664 cgas: RegMut<'vm, CGAS>,
665 ggas: RegMut<'vm, GGAS>,
666 fp: Reg<'vm, FP>,
667 pc: RegMut<'vm, PC>,
668 is: Reg<'vm, IS>,
669}
670
671impl<S> MintCtx<'_, S>
672where
673 S: ContractsAssetsStorage,
674{
675 pub(crate) fn mint(self, a: Word, b: Word) -> Result<(), RuntimeError<S::Error>> {
676 let contract_id = internal_contract(self.context, self.fp, self.memory)?;
677 let sub_id = SubAssetId::new(self.memory.read_bytes(b)?);
678 let asset_id = contract_id.asset_id(&sub_id);
679
680 let balance = balance(self.storage, &contract_id, &asset_id)?;
681 let balance = balance.checked_add(a).ok_or(PanicReason::BalanceOverflow)?;
682
683 let old_value = self
684 .storage
685 .contract_asset_id_balance_replace(&contract_id, &asset_id, balance)
686 .map_err(RuntimeError::Storage)?;
687
688 if old_value.is_none() {
689 gas_charge(
691 self.cgas,
692 self.ggas,
693 (BALANCE_ENTRY_SIZE as u64).saturating_mul(self.new_storage_gas_per_byte),
694 )?;
695 }
696
697 let receipt = Receipt::mint(sub_id, contract_id, a, *self.pc, *self.is);
698
699 self.receipts.push(receipt)?;
700
701 inc_pc(self.pc);
702 Ok(())
703 }
704}
705
706struct CodeCopyCtx<'vm, S, V> {
707 memory: &'vm mut MemoryInstance,
708 input_contracts: &'vm BTreeSet<ContractId>,
709 panic_context: &'vm mut PanicContext,
710 storage: &'vm S,
711 owner: OwnershipRegisters,
712 gas_cost: DependentCost,
713 cgas: RegMut<'vm, CGAS>,
714 ggas: RegMut<'vm, GGAS>,
715 pc: RegMut<'vm, PC>,
716 verifier: &'vm mut V,
717}
718
719impl<S, V> CodeCopyCtx<'_, S, V> {
720 pub(crate) fn code_copy(
721 self,
722 dst_addr: Word,
723 contract_id_addr: Word,
724 contract_offset: Word,
725 length: Word,
726 ) -> IoResult<(), S::DataError>
727 where
728 S: InterpreterStorage,
729 V: Verifier,
730 {
731 let contract_id = ContractId::from(self.memory.read_bytes(contract_id_addr)?);
732
733 self.memory.write(self.owner, dst_addr, length)?;
734 self.verifier.check_contract_in_inputs(
735 self.panic_context,
736 self.input_contracts,
737 &contract_id,
738 )?;
739
740 let contract_len = contract_size(&self.storage, &contract_id)?;
741 let charge_len = core::cmp::max(contract_len as u64, length);
742 dependent_gas_charge_without_base(
743 self.cgas,
744 self.ggas,
745 self.gas_cost,
746 charge_len,
747 )?;
748
749 copy_from_storage_zero_fill::<ContractsRawCode, _>(
750 self.memory,
751 self.owner,
752 self.storage,
753 dst_addr,
754 length,
755 &contract_id,
756 contract_offset,
757 contract_len,
758 PanicReason::ContractNotFound,
759 )?;
760
761 inc_pc(self.pc);
762 Ok(())
763 }
764}
765
766pub(crate) fn block_hash<S: InterpreterStorage>(
767 storage: &S,
768 memory: &mut MemoryInstance,
769 owner: OwnershipRegisters,
770 pc: RegMut<PC>,
771 a: Word,
772 b: Word,
773) -> IoResult<(), S::DataError> {
774 let height = u32::try_from(b)
775 .map_err(|_| PanicReason::InvalidBlockHeight)?
776 .into();
777 let hash = storage.block_hash(height).map_err(RuntimeError::Storage)?;
778
779 memory.write_bytes(owner, a, *hash)?;
780
781 inc_pc(pc);
782 Ok(())
783}
784
785pub(crate) fn block_height(
786 context: &Context,
787 pc: RegMut<PC>,
788 result: &mut Word,
789) -> SimpleResult<()> {
790 context
791 .block_height()
792 .map(|h| *h as Word)
793 .map(|h| *result = h)
794 .ok_or(PanicReason::TransactionValidity)?;
795
796 inc_pc(pc);
797 Ok(())
798}
799
800pub(crate) fn coinbase<S: InterpreterStorage>(
801 storage: &S,
802 memory: &mut MemoryInstance,
803 owner: OwnershipRegisters,
804 pc: RegMut<PC>,
805 a: Word,
806) -> IoResult<(), S::DataError> {
807 let coinbase = storage.coinbase().map_err(RuntimeError::Storage)?;
808 memory.write_bytes(owner, a, *coinbase)?;
809 inc_pc(pc);
810 Ok(())
811}
812
813struct CodeRootCtx<'vm, S, V> {
814 storage: &'vm S,
815 memory: &'vm mut MemoryInstance,
816 gas_cost: DependentCost,
817 input_contracts: &'vm BTreeSet<ContractId>,
818 panic_context: &'vm mut PanicContext,
819 cgas: RegMut<'vm, CGAS>,
820 ggas: RegMut<'vm, GGAS>,
821 owner: OwnershipRegisters,
822 pc: RegMut<'vm, PC>,
823 verifier: &'vm mut V,
824}
825
826impl<S, V> CodeRootCtx<'_, S, V> {
827 pub(crate) fn code_root(self, a: Word, b: Word) -> IoResult<(), S::DataError>
828 where
829 S: InterpreterStorage,
830 V: Verifier,
831 {
832 self.memory.write_noownerchecks(a, Bytes32::LEN)?;
833
834 let contract_id = ContractId::new(self.memory.read_bytes(b)?);
835
836 self.verifier.check_contract_in_inputs(
837 self.panic_context,
838 self.input_contracts,
839 &contract_id,
840 )?;
841
842 let len = contract_size(self.storage, &contract_id)?;
843 dependent_gas_charge_without_base(
844 self.cgas,
845 self.ggas,
846 self.gas_cost,
847 len as u64,
848 )?;
849 let root = self
850 .storage
851 .storage_contract(&contract_id)
852 .transpose()
853 .ok_or(PanicReason::ContractNotFound)?
854 .map_err(RuntimeError::Storage)?
855 .root();
856
857 self.memory.write_bytes(self.owner, a, *root)?;
858
859 inc_pc(self.pc);
860 Ok(())
861 }
862}
863
864struct CodeSizeCtx<'vm, S, V> {
865 storage: &'vm S,
866 memory: &'vm mut MemoryInstance,
867 gas_cost: DependentCost,
868 input_contracts: &'vm BTreeSet<ContractId>,
869 panic_context: &'vm mut PanicContext,
870 cgas: RegMut<'vm, CGAS>,
871 ggas: RegMut<'vm, GGAS>,
872 pc: RegMut<'vm, PC>,
873 verifier: &'vm mut V,
874}
875
876impl<S, V> CodeSizeCtx<'_, S, V> {
877 pub(crate) fn code_size(
878 self,
879 result: &mut Word,
880 b: Word,
881 ) -> Result<(), RuntimeError<S::Error>>
882 where
883 S: StorageSize<ContractsRawCode>,
884 V: Verifier,
885 {
886 let contract_id = ContractId::new(self.memory.read_bytes(b)?);
887
888 self.verifier.check_contract_in_inputs(
889 self.panic_context,
890 self.input_contracts,
891 &contract_id,
892 )?;
893
894 let len = contract_size(self.storage, &contract_id)?;
895 dependent_gas_charge_without_base(
896 self.cgas,
897 self.ggas,
898 self.gas_cost,
899 len as u64,
900 )?;
901 *result = len as u64;
902
903 inc_pc(self.pc);
904 Ok(())
905 }
906}
907
908pub(crate) fn timestamp<S: InterpreterStorage>(
909 storage: &S,
910 block_height: BlockHeight,
911 pc: RegMut<PC>,
912 result: &mut Word,
913 b: Word,
914) -> IoResult<(), S::DataError> {
915 let b = u32::try_from(b)
916 .map_err(|_| PanicReason::InvalidBlockHeight)?
917 .into();
918 (b <= block_height)
919 .then_some(())
920 .ok_or(PanicReason::TransactionValidity)?;
921
922 *result = storage.timestamp(b).map_err(RuntimeError::Storage)?;
923
924 inc_pc(pc);
925 Ok(())
926}
927struct MessageOutputCtx<'vm, S>
928where
929 S: ContractsAssetsStorage + ?Sized,
930{
931 base_asset_id: AssetId,
932 max_message_data_length: u64,
933 memory: &'vm mut MemoryInstance,
934 receipts: &'vm mut ReceiptsCtx,
935 balances: &'vm mut RuntimeBalances,
936 storage: &'vm mut S,
937 current_contract: Option<ContractId>,
938 fp: Reg<'vm, FP>,
939 pc: RegMut<'vm, PC>,
940 recipient_mem_address: Word,
942 msg_data_ptr: Word,
944 msg_data_len: Word,
946 amount_coins_to_send: Word,
948}
949
950impl<S> MessageOutputCtx<'_, S>
951where
952 S: ContractsAssetsStorage + ?Sized,
953{
954 pub(crate) fn message_output(self) -> Result<(), RuntimeError<S::Error>> {
955 if self.msg_data_len > self.max_message_data_length {
956 return Err(RuntimeError::Recoverable(PanicReason::MessageDataTooLong));
957 }
958
959 let msg_data = self
960 .memory
961 .read(self.msg_data_ptr, self.msg_data_len)?
962 .to_vec();
963 let recipient = Address::new(self.memory.read_bytes(self.recipient_mem_address)?);
964 let sender = Address::new(self.memory.read_bytes(*self.fp)?);
965
966 if let Some(source_contract) = self.current_contract {
969 balance_decrease(
970 self.storage,
971 &source_contract,
972 &self.base_asset_id,
973 self.amount_coins_to_send,
974 )?;
975 } else {
976 base_asset_balance_sub(
977 &self.base_asset_id,
978 self.balances,
979 self.memory,
980 self.amount_coins_to_send,
981 )?;
982 }
983
984 let txid = tx_id(self.memory);
985 let receipt = Receipt::message_out(
986 &txid,
987 self.receipts.len() as Word,
988 sender,
989 recipient,
990 self.amount_coins_to_send,
991 msg_data,
992 );
993
994 self.receipts.push(receipt)?;
995
996 inc_pc(self.pc);
997 Ok(())
998 }
999}