Skip to main content

fuel_vm/interpreter/
blockchain.rs

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    /// Loads contract ID pointed by `contract_id_addr`, and then for that contract,
95    /// copies `length_unpadded` bytes from it starting from offset `contract_offset` into
96    /// the stack.
97    ///
98    /// ```txt
99    /// contract_id = mem[$rA, 32]
100    /// contract_code = contracts[contract_id]
101    /// mem[$ssp, $rC] = contract_code[$rB, $rC]
102    /// ```
103    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        // Charge only for the `base` execution.
112        // We will charge for the contracts size in the `load_contract_code`.
113        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        // Charge only for the `base` execution.
207        // We will charge for the contract's size in the `code_copy`.
208        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        // Charge only for the `base` execution.
280        // We will charge for the contracts size in the `code_size`.
281        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    /// Loads contract ID pointed by `a`, and then for that contract,
355    /// copies `c` bytes from it starting from offset `b` into the stack.
356    /// ```txt
357    /// contract_id = mem[$rA, 32]
358    /// contract_code = contracts[contract_id]
359    /// mem[$ssp, $rC] = contract_code[$rB, $rC]
360    /// ```
361    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        // only blobs are allowed in predicates
376        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        // Fetch the storage contract
400        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        // Set up ownership registers for the copy using old ssp
413        let owner =
414            OwnershipRegisters::only_allow_stack_write(new_sp, *self.ssp, *self.hp);
415
416        // Mark stack space as allocated
417        *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        // Update frame code size, if we have a stack frame (i.e. fp > 0)
433        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    /// Loads blob ID pointed by `a`, and then for that blob,
454    /// copies `c` bytes from it starting from offset `b` into the stack.
455    /// ```txt
456    /// blob_id = mem[$rA, 32]
457    /// blob_code = blobs[blob_id]
458    /// mem[$ssp, $rC] = blob_code[$rB, $rC]
459    /// ```
460    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        // Fetch the storage blob
484        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        // Set up ownership registers for the copy using old ssp
496        let owner =
497            OwnershipRegisters::only_allow_stack_write(new_sp, *self.ssp, *self.hp);
498
499        // Mark stack space as allocated
500        *self.sp = new_sp;
501        *self.ssp = new_sp;
502
503        // Copy the code.
504        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        // Update frame code size, if we have a stack frame (i.e. fp > 0)
517        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    /// Copies `c` bytes from starting the memory `a` and offset `b` into the
538    /// stack.
539    ///
540    /// ```txt
541    /// mem[$ssp, $rC] = memory[$rA + $rB, $rC]
542    /// ```
543    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        // Fetch the storage blob
569        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        // Set up ownership registers for the copy using old ssp
581        let owner = OwnershipRegisters::only_allow_stack_write(new_sp, ssp, *self.hp);
582        let src = input_src_addr.saturating_add(input_offset);
583
584        // Copy the code
585        self.memory.memcopy(dst, src, length_unpadded, owner)?;
586
587        // Write padding
588        if length_padding > 0 {
589            self.memory
590                .write(owner, dst.saturating_add(length_unpadded), length_padding)?
591                .fill(0);
592        }
593
594        // Mark stack space as allocated
595        *self.sp = new_sp;
596        *self.ssp = new_sp;
597
598        // Update frame code size, if we have a stack frame (i.e. fp > 0)
599        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            // New data was written, charge gas for it
690            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    /// A
941    recipient_mem_address: Word,
942    /// B
943    msg_data_ptr: Word,
944    /// C
945    msg_data_len: Word,
946    /// D
947    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        // validations passed, perform the mutations
967
968        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}