fuel_vm/interpreter/executors/
main.rs

1#[cfg(test)]
2mod tests;
3
4use crate::{
5    checked_transaction::{
6        Checked,
7        IntoChecked,
8        ParallelExecutor,
9    },
10    context::Context,
11    error::{
12        Bug,
13        InterpreterError,
14        PredicateVerificationFailed,
15    },
16    interpreter::{
17        CheckedMetadata,
18        EcalHandler,
19        ExecutableTransaction,
20        InitialBalances,
21        Interpreter,
22        Memory,
23        RuntimeBalances,
24    },
25    pool::VmMemoryPool,
26    predicate::RuntimePredicate,
27    prelude::{
28        BugVariant,
29        RuntimeError,
30    },
31    state::{
32        ExecuteState,
33        ProgramState,
34        StateTransitionRef,
35    },
36    storage::{
37        predicate::PredicateStorage,
38        BlobData,
39        InterpreterStorage,
40    },
41};
42use alloc::{
43    vec,
44    vec::Vec,
45};
46use core::fmt::Debug;
47
48use crate::{
49    checked_transaction::{
50        CheckError,
51        CheckPredicateParams,
52        Ready,
53    },
54    interpreter::InterpreterParams,
55    prelude::MemoryInstance,
56    storage::{
57        predicate::PredicateStorageRequirements,
58        UploadedBytecode,
59        UploadedBytecodes,
60    },
61};
62use fuel_asm::PanicReason;
63use fuel_storage::{
64    StorageAsMut,
65    StorageAsRef,
66};
67use fuel_tx::{
68    field::{
69        BlobId as _,
70        BytecodeRoot,
71        BytecodeWitnessIndex,
72        ReceiptsRoot,
73        Salt,
74        Script as ScriptField,
75        ScriptGasLimit,
76        StorageSlots,
77        SubsectionIndex,
78        SubsectionsNumber,
79        UpgradePurpose as UpgradePurposeField,
80        Witnesses,
81    },
82    input::{
83        coin::CoinPredicate,
84        message::{
85            MessageCoinPredicate,
86            MessageDataPredicate,
87        },
88    },
89    Blob,
90    BlobIdExt,
91    ConsensusParameters,
92    Contract,
93    Create,
94    FeeParameters,
95    GasCosts,
96    Input,
97    Receipt,
98    ScriptExecutionResult,
99    Transaction,
100    Upgrade,
101    UpgradeMetadata,
102    UpgradePurpose,
103    Upload,
104    ValidityError,
105};
106use fuel_types::{
107    AssetId,
108    BlobId,
109    Word,
110};
111
112/// Predicates were checked succesfully
113#[derive(Debug, Clone, Copy)]
114pub struct PredicatesChecked {
115    gas_used: Word,
116}
117
118impl PredicatesChecked {
119    pub fn gas_used(&self) -> Word {
120        self.gas_used
121    }
122}
123
124enum PredicateRunKind<'a, Tx> {
125    Verifying(&'a Tx),
126    Estimating(&'a mut Tx),
127}
128
129impl<'a, Tx> PredicateRunKind<'a, Tx> {
130    fn tx(&self) -> &Tx {
131        match self {
132            PredicateRunKind::Verifying(tx) => tx,
133            PredicateRunKind::Estimating(tx) => tx,
134        }
135    }
136}
137
138#[derive(Debug, Clone, Copy)]
139enum PredicateAction {
140    Verifying,
141    Estimating { available_gas: Word },
142}
143
144/// The module contains functions to check predicates defined in the inputs of a
145/// transaction.
146pub mod predicates {
147    use super::*;
148    use crate::storage::predicate::PredicateStorageProvider;
149
150    /// Initialize the VM with the provided transaction and check all predicates defined
151    /// in the inputs.
152    ///
153    /// The storage provider is not used since contract opcodes are not allowed for
154    /// predicates.
155    pub fn check_predicates<Tx>(
156        checked: &Checked<Tx>,
157        params: &CheckPredicateParams,
158        mut memory: impl Memory,
159        storage: &impl PredicateStorageRequirements,
160    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
161    where
162        Tx: ExecutableTransaction,
163        <Tx as IntoChecked>::Metadata: CheckedMetadata,
164    {
165        let tx = checked.transaction();
166        run_predicates(
167            PredicateRunKind::Verifying(tx),
168            params,
169            memory.as_mut(),
170            storage,
171        )
172    }
173
174    /// Initialize the VM with the provided transaction and check all predicates defined
175    /// in the inputs in parallel.
176    ///
177    /// The storage provider is not used since contract opcodes are not allowed for
178    /// predicates.
179    pub async fn check_predicates_async<Tx, E>(
180        checked: &Checked<Tx>,
181        params: &CheckPredicateParams,
182        pool: &impl VmMemoryPool,
183        storage: &impl PredicateStorageProvider,
184    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
185    where
186        Tx: ExecutableTransaction + Send + 'static,
187        <Tx as IntoChecked>::Metadata: CheckedMetadata,
188        E: ParallelExecutor,
189    {
190        let tx = checked.transaction();
191
192        let predicates_checked = run_predicate_async::<Tx, E>(
193            PredicateRunKind::Verifying(tx),
194            params,
195            pool,
196            storage,
197        )
198        .await?;
199
200        Ok(predicates_checked)
201    }
202
203    /// Initialize the VM with the provided transaction, check all predicates defined in
204    /// the inputs and set the predicate_gas_used to be the actual gas consumed during
205    /// execution for each predicate.
206    ///
207    /// The storage provider is not used since contract opcodes are not allowed for
208    /// predicates.
209    pub fn estimate_predicates<Tx>(
210        transaction: &mut Tx,
211        params: &CheckPredicateParams,
212        mut memory: impl Memory,
213        storage: &impl PredicateStorageRequirements,
214    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
215    where
216        Tx: ExecutableTransaction,
217    {
218        let predicates_checked = run_predicates(
219            PredicateRunKind::Estimating(transaction),
220            params,
221            memory.as_mut(),
222            storage,
223        )?;
224        Ok(predicates_checked)
225    }
226
227    /// Initialize the VM with the provided transaction, check all predicates defined in
228    /// the inputs and set the predicate_gas_used to be the actual gas consumed during
229    /// execution for each predicate in parallel.
230    ///
231    /// The storage provider is not used since contract opcodes are not allowed for
232    /// predicates.
233    pub async fn estimate_predicates_async<Tx, E>(
234        transaction: &mut Tx,
235        params: &CheckPredicateParams,
236        pool: &impl VmMemoryPool,
237        storage: &impl PredicateStorageProvider,
238    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
239    where
240        Tx: ExecutableTransaction + Send + 'static,
241        E: ParallelExecutor,
242    {
243        let predicates_checked = run_predicate_async::<Tx, E>(
244            PredicateRunKind::Estimating(transaction),
245            params,
246            pool,
247            storage,
248        )
249        .await?;
250
251        Ok(predicates_checked)
252    }
253
254    async fn run_predicate_async<Tx, E>(
255        kind: PredicateRunKind<'_, Tx>,
256        params: &CheckPredicateParams,
257        pool: &impl VmMemoryPool,
258        storage: &impl PredicateStorageProvider,
259    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
260    where
261        Tx: ExecutableTransaction + Send + 'static,
262        E: ParallelExecutor,
263    {
264        let mut checks = vec![];
265        let tx_offset = params.tx_offset;
266
267        let predicate_action = match kind {
268            PredicateRunKind::Verifying(_) => PredicateAction::Verifying,
269            PredicateRunKind::Estimating(_) => {
270                let max_gas_per_tx = params.max_gas_per_tx;
271                let max_gas_per_predicate = params.max_gas_per_predicate;
272                let available_gas = core::cmp::min(max_gas_per_predicate, max_gas_per_tx);
273
274                PredicateAction::Estimating { available_gas }
275            }
276        };
277
278        for index in 0..kind.tx().inputs().len() {
279            if let Some(predicate) =
280                RuntimePredicate::from_tx(kind.tx(), tx_offset, index)
281            {
282                let tx = kind.tx().clone();
283                let my_params = params.clone();
284                let mut memory = pool.get_new().await;
285                let storage_instance = storage.storage();
286
287                let verify_task = E::create_task(move || {
288                    let (used_gas, result) = check_predicate(
289                        tx,
290                        index,
291                        predicate_action,
292                        predicate,
293                        my_params,
294                        memory.as_mut(),
295                        &storage_instance,
296                    );
297
298                    result.map(|_| (used_gas, index))
299                });
300
301                checks.push(verify_task);
302            }
303        }
304
305        let checks = E::execute_tasks(checks).await;
306
307        finalize_check_predicate(kind, checks, params)
308    }
309
310    fn run_predicates<Tx>(
311        kind: PredicateRunKind<'_, Tx>,
312        params: &CheckPredicateParams,
313        mut memory: impl Memory,
314        storage: &impl PredicateStorageRequirements,
315    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
316    where
317        Tx: ExecutableTransaction,
318    {
319        let mut checks = vec![];
320
321        let max_gas = kind.tx().max_gas(&params.gas_costs, &params.fee_params);
322        let max_gas_per_tx = params.max_gas_per_tx;
323        let max_gas_per_predicate = params.max_gas_per_predicate;
324        let mut global_available_gas = max_gas_per_tx.saturating_sub(max_gas);
325
326        for index in 0..kind.tx().inputs().len() {
327            let tx = kind.tx().clone();
328
329            if let Some(predicate) =
330                RuntimePredicate::from_tx(&tx, params.tx_offset, index)
331            {
332                let available_gas = global_available_gas.min(max_gas_per_predicate);
333                let predicate_action = match kind {
334                    PredicateRunKind::Verifying(_) => PredicateAction::Verifying,
335                    PredicateRunKind::Estimating(_) => {
336                        PredicateAction::Estimating { available_gas }
337                    }
338                };
339                let (gas_used, result) = check_predicate(
340                    tx,
341                    index,
342                    predicate_action,
343                    predicate,
344                    params.clone(),
345                    memory.as_mut(),
346                    storage,
347                );
348                global_available_gas = global_available_gas.saturating_sub(gas_used);
349                let result = result.map(|_| (gas_used, index));
350                checks.push(result);
351            }
352        }
353
354        finalize_check_predicate(kind, checks, params)
355    }
356
357    fn check_predicate<Tx>(
358        tx: Tx,
359        index: usize,
360        predicate_action: PredicateAction,
361        predicate: RuntimePredicate,
362        params: CheckPredicateParams,
363        memory: &mut MemoryInstance,
364        storage: &impl PredicateStorageRequirements,
365    ) -> (Word, Result<(), PredicateVerificationFailed>)
366    where
367        Tx: ExecutableTransaction,
368    {
369        match &tx.inputs()[index] {
370            Input::CoinPredicate(CoinPredicate {
371                owner: address,
372                predicate,
373                ..
374            })
375            | Input::MessageDataPredicate(MessageDataPredicate {
376                recipient: address,
377                predicate,
378                ..
379            })
380            | Input::MessageCoinPredicate(MessageCoinPredicate {
381                predicate,
382                recipient: address,
383                ..
384            }) => {
385                if !Input::is_predicate_owner_valid(address, &**predicate) {
386                    return (0, Err(PredicateVerificationFailed::InvalidOwner));
387                }
388            }
389            _ => {}
390        }
391
392        let zero_gas_price = 0;
393        let interpreter_params = InterpreterParams::new(zero_gas_price, params);
394
395        let mut vm = Interpreter::<_, _, _>::with_storage(
396            memory,
397            PredicateStorage::new(storage),
398            interpreter_params,
399        );
400
401        let (context, available_gas) = match predicate_action {
402            PredicateAction::Verifying => {
403                let context = Context::PredicateVerification { program: predicate };
404                let available_gas = tx.inputs()[index]
405                    .predicate_gas_used()
406                    .expect("We only run predicates at this stage, so it should exist.");
407
408                (context, available_gas)
409            }
410            PredicateAction::Estimating { available_gas } => {
411                let context = Context::PredicateEstimation { program: predicate };
412
413                (context, available_gas)
414            }
415        };
416
417        if let Err(err) = vm.init_predicate(context, tx, available_gas) {
418            return (0, Err(err.into()));
419        }
420
421        let result = vm.verify_predicate();
422        let is_successful = matches!(result, Ok(ProgramState::Return(0x01)));
423
424        let Some(gas_used) = available_gas.checked_sub(vm.remaining_gas()) else {
425            return (0, Err(Bug::new(BugVariant::GlobalGasUnderflow).into()));
426        };
427
428        if let PredicateAction::Verifying = predicate_action {
429            if !is_successful {
430                return if let Err(err) = result {
431                    (gas_used, Err(err))
432                } else {
433                    (gas_used, Err(PredicateVerificationFailed::False))
434                }
435            }
436
437            if vm.remaining_gas() != 0 {
438                return (gas_used, Err(PredicateVerificationFailed::GasMismatch));
439            }
440        }
441
442        (gas_used, Ok(()))
443    }
444
445    fn finalize_check_predicate<Tx>(
446        mut kind: PredicateRunKind<Tx>,
447        checks: Vec<Result<(Word, usize), PredicateVerificationFailed>>,
448        params: &CheckPredicateParams,
449    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
450    where
451        Tx: ExecutableTransaction,
452    {
453        if let PredicateRunKind::Estimating(tx) = &mut kind {
454            checks.iter().for_each(|result| {
455                if let Ok((gas_used, index)) = result {
456                    match &mut tx.inputs_mut()[*index] {
457                        Input::CoinPredicate(CoinPredicate {
458                            predicate_gas_used,
459                            ..
460                        })
461                        | Input::MessageCoinPredicate(MessageCoinPredicate {
462                            predicate_gas_used,
463                            ..
464                        })
465                        | Input::MessageDataPredicate(MessageDataPredicate {
466                            predicate_gas_used,
467                            ..
468                        }) => {
469                            *predicate_gas_used = *gas_used;
470                        }
471                        _ => {
472                            unreachable!(
473                                "It was checked before during iteration over predicates"
474                            )
475                        }
476                    }
477                }
478            });
479        }
480
481        let max_gas = kind.tx().max_gas(&params.gas_costs, &params.fee_params);
482        if max_gas > params.max_gas_per_tx {
483            return Err(
484                PredicateVerificationFailed::TransactionExceedsTotalGasAllowance(max_gas),
485            );
486        }
487
488        let cumulative_gas_used = checks.into_iter().try_fold(0u64, |acc, result| {
489            acc.checked_add(result.map(|(gas_used, _)| gas_used)?)
490                .ok_or(PredicateVerificationFailed::OutOfGas)
491        })?;
492
493        Ok(PredicatesChecked {
494            gas_used: cumulative_gas_used,
495        })
496    }
497}
498
499impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
500where
501    S: InterpreterStorage,
502{
503    fn deploy_inner(
504        create: &mut Create,
505        storage: &mut S,
506        initial_balances: InitialBalances,
507        gas_costs: &GasCosts,
508        fee_params: &FeeParameters,
509        base_asset_id: &AssetId,
510        gas_price: Word,
511    ) -> Result<(), InterpreterError<S::DataError>> {
512        let metadata = create.metadata().as_ref();
513        debug_assert!(
514            metadata.is_some(),
515            "`deploy_inner` is called without cached metadata"
516        );
517        let salt = create.salt();
518        let storage_slots = create.storage_slots();
519        let contract = Contract::try_from(&*create)?;
520        let root = if let Some(m) = metadata {
521            m.body.contract_root
522        } else {
523            contract.root()
524        };
525
526        let storage_root = if let Some(m) = metadata {
527            m.body.state_root
528        } else {
529            Contract::initial_state_root(storage_slots.iter())
530        };
531
532        let id = if let Some(m) = metadata {
533            m.body.contract_id
534        } else {
535            contract.id(salt, &root, &storage_root)
536        };
537
538        // Prevent redeployment of contracts
539        if storage
540            .storage_contract_exists(&id)
541            .map_err(RuntimeError::Storage)?
542        {
543            return Err(InterpreterError::Panic(
544                PanicReason::ContractIdAlreadyDeployed,
545            ));
546        }
547
548        storage
549            .deploy_contract_with_id(storage_slots, &contract, &id)
550            .map_err(RuntimeError::Storage)?;
551        Self::finalize_outputs(
552            create,
553            gas_costs,
554            fee_params,
555            base_asset_id,
556            false,
557            0,
558            &initial_balances,
559            &RuntimeBalances::try_from(initial_balances.clone())?,
560            gas_price,
561        )?;
562        Ok(())
563    }
564}
565
566impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
567where
568    S: InterpreterStorage,
569{
570    fn upgrade_inner(
571        upgrade: &mut Upgrade,
572        storage: &mut S,
573        initial_balances: InitialBalances,
574        gas_costs: &GasCosts,
575        fee_params: &FeeParameters,
576        base_asset_id: &AssetId,
577        gas_price: Word,
578    ) -> Result<(), InterpreterError<S::DataError>> {
579        let metadata = upgrade.metadata().as_ref();
580        debug_assert!(
581            metadata.is_some(),
582            "`upgrade_inner` is called without cached metadata"
583        );
584
585        match upgrade.upgrade_purpose() {
586            UpgradePurpose::ConsensusParameters { .. } => {
587                let consensus_parameters = if let Some(metadata) = metadata {
588                    Self::get_consensus_parameters(&metadata.body)?
589                } else {
590                    let metadata = UpgradeMetadata::compute(upgrade)?;
591                    Self::get_consensus_parameters(&metadata)?
592                };
593
594                let current_version = storage
595                    .consensus_parameters_version()
596                    .map_err(RuntimeError::Storage)?;
597                let next_version = current_version.saturating_add(1);
598
599                let prev = storage
600                    .set_consensus_parameters(next_version, &consensus_parameters)
601                    .map_err(RuntimeError::Storage)?;
602
603                if prev.is_some() {
604                    return Err(InterpreterError::Panic(
605                        PanicReason::OverridingConsensusParameters,
606                    ));
607                }
608            }
609            UpgradePurpose::StateTransition { root } => {
610                let exists = storage
611                    .contains_state_transition_bytecode_root(root)
612                    .map_err(RuntimeError::Storage)?;
613
614                if !exists {
615                    return Err(InterpreterError::Panic(
616                        PanicReason::UnknownStateTransactionBytecodeRoot,
617                    ))
618                }
619
620                let current_version = storage
621                    .state_transition_version()
622                    .map_err(RuntimeError::Storage)?;
623                let next_version = current_version.saturating_add(1);
624
625                let prev = storage
626                    .set_state_transition_bytecode(next_version, root)
627                    .map_err(RuntimeError::Storage)?;
628
629                if prev.is_some() {
630                    return Err(InterpreterError::Panic(
631                        PanicReason::OverridingStateTransactionBytecode,
632                    ));
633                }
634            }
635        }
636
637        Self::finalize_outputs(
638            upgrade,
639            gas_costs,
640            fee_params,
641            base_asset_id,
642            false,
643            0,
644            &initial_balances,
645            &RuntimeBalances::try_from(initial_balances.clone())?,
646            gas_price,
647        )?;
648        Ok(())
649    }
650
651    fn get_consensus_parameters(
652        metadata: &UpgradeMetadata,
653    ) -> Result<ConsensusParameters, InterpreterError<S::DataError>> {
654        match &metadata {
655            UpgradeMetadata::ConsensusParameters {
656                consensus_parameters,
657                ..
658            } => Ok(consensus_parameters.as_ref().clone()),
659            UpgradeMetadata::StateTransition => {
660                // It shouldn't be possible since `Check<Upgrade>` guarantees that.
661                Err(InterpreterError::CheckError(CheckError::Validity(
662                    ValidityError::TransactionMetadataMismatch,
663                )))
664            }
665        }
666    }
667}
668
669impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
670where
671    S: InterpreterStorage,
672{
673    fn upload_inner(
674        upload: &mut Upload,
675        storage: &mut S,
676        initial_balances: InitialBalances,
677        gas_costs: &GasCosts,
678        fee_params: &FeeParameters,
679        base_asset_id: &AssetId,
680        gas_price: Word,
681    ) -> Result<(), InterpreterError<S::DataError>> {
682        let root = *upload.bytecode_root();
683        let uploaded_bytecode = storage
684            .storage_as_ref::<UploadedBytecodes>()
685            .get(&root)
686            .map_err(RuntimeError::Storage)?
687            .map(|x| x.into_owned())
688            .unwrap_or_else(|| UploadedBytecode::Uncompleted {
689                bytecode: vec![],
690                uploaded_subsections_number: 0,
691            });
692
693        let new_bytecode = match uploaded_bytecode {
694            UploadedBytecode::Uncompleted {
695                bytecode,
696                uploaded_subsections_number,
697            } => Self::upload_bytecode_subsection(
698                upload,
699                bytecode,
700                uploaded_subsections_number,
701            )?,
702            UploadedBytecode::Completed(_) => {
703                return Err(InterpreterError::Panic(
704                    PanicReason::BytecodeAlreadyUploaded,
705                ));
706            }
707        };
708
709        storage
710            .storage_as_mut::<UploadedBytecodes>()
711            .insert(&root, &new_bytecode)
712            .map_err(RuntimeError::Storage)?;
713
714        Self::finalize_outputs(
715            upload,
716            gas_costs,
717            fee_params,
718            base_asset_id,
719            false,
720            0,
721            &initial_balances,
722            &RuntimeBalances::try_from(initial_balances.clone())?,
723            gas_price,
724        )?;
725        Ok(())
726    }
727
728    fn upload_bytecode_subsection(
729        upload: &Upload,
730        mut uploaded_bytecode: Vec<u8>,
731        uploaded_subsections_number: u16,
732    ) -> Result<UploadedBytecode, InterpreterError<S::DataError>> {
733        let index_of_next_subsection = uploaded_subsections_number;
734
735        if *upload.subsection_index() != index_of_next_subsection {
736            return Err(InterpreterError::Panic(
737                PanicReason::ThePartIsNotSequentiallyConnected,
738            ));
739        }
740
741        let bytecode_subsection = upload
742            .witnesses()
743            .get(*upload.bytecode_witness_index() as usize)
744            .ok_or(InterpreterError::Bug(Bug::new(
745                // It shouldn't be possible since `Checked<Upload>` guarantees
746                // the existence of the witness.
747                BugVariant::WitnessIndexOutOfBounds,
748            )))?;
749
750        uploaded_bytecode.extend(bytecode_subsection.as_ref());
751
752        let new_uploaded_subsections_number = uploaded_subsections_number
753            .checked_add(1)
754            .ok_or(InterpreterError::Panic(PanicReason::ArithmeticOverflow))?;
755
756        // It shouldn't be possible since `Checked<Upload>` guarantees
757        // the validity of the Merkle proof.
758        if new_uploaded_subsections_number > *upload.subsections_number() {
759            return Err(InterpreterError::Bug(Bug::new(
760                BugVariant::NextSubsectionIndexIsHigherThanTotalNumberOfParts,
761            )))
762        }
763
764        let updated_uploaded_bytecode =
765            if *upload.subsections_number() == new_uploaded_subsections_number {
766                UploadedBytecode::Completed(uploaded_bytecode)
767            } else {
768                UploadedBytecode::Uncompleted {
769                    bytecode: uploaded_bytecode,
770                    uploaded_subsections_number: new_uploaded_subsections_number,
771                }
772            };
773
774        Ok(updated_uploaded_bytecode)
775    }
776}
777
778impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
779where
780    S: InterpreterStorage,
781{
782    fn blob_inner(
783        blob: &mut Blob,
784        storage: &mut S,
785        initial_balances: InitialBalances,
786        gas_costs: &GasCosts,
787        fee_params: &FeeParameters,
788        base_asset_id: &AssetId,
789        gas_price: Word,
790    ) -> Result<(), InterpreterError<S::DataError>> {
791        let blob_data = blob
792            .witnesses()
793            .get(*blob.bytecode_witness_index() as usize)
794            .ok_or(InterpreterError::Bug(Bug::new(
795                // It shouldn't be possible since `Checked<Blob>` guarantees
796                // the existence of the witness.
797                BugVariant::WitnessIndexOutOfBounds,
798            )))?;
799
800        let blob_id = blob.blob_id();
801
802        debug_assert_eq!(
803            BlobId::compute(blob_data.as_ref()),
804            *blob_id,
805            "Tx has invalid BlobId",
806        );
807
808        let old = storage
809            .storage_as_mut::<BlobData>()
810            .replace(blob_id, blob_data.as_ref())
811            .map_err(RuntimeError::Storage)?;
812
813        if old.is_some() {
814            return Err(InterpreterError::Panic(PanicReason::BlobIdAlreadyUploaded));
815        }
816
817        Self::finalize_outputs(
818            blob,
819            gas_costs,
820            fee_params,
821            base_asset_id,
822            false,
823            0,
824            &initial_balances,
825            &RuntimeBalances::try_from(initial_balances.clone())?,
826            gas_price,
827        )?;
828
829        Ok(())
830    }
831}
832
833impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
834where
835    M: Memory,
836    S: InterpreterStorage,
837    Tx: ExecutableTransaction,
838    Ecal: EcalHandler,
839{
840    fn update_transaction_outputs(
841        &mut self,
842    ) -> Result<(), InterpreterError<S::DataError>> {
843        let outputs = self.transaction().outputs().len();
844        (0..outputs).try_for_each(|o| self.update_memory_output(o))?;
845        Ok(())
846    }
847
848    pub(crate) fn run(&mut self) -> Result<ProgramState, InterpreterError<S::DataError>> {
849        for input in self.transaction().inputs() {
850            if let Input::Contract(contract) = input {
851                if !self.check_contract_exists(&contract.contract_id)? {
852                    return Err(InterpreterError::Panic(
853                        PanicReason::InputContractDoesNotExist,
854                    ));
855                }
856            }
857        }
858
859        // TODO: Remove `Create`, `Upgrade`, and `Upload` from here
860        //  https://github.com/FuelLabs/fuel-vm/issues/251
861        let gas_costs = self.gas_costs().clone();
862        let fee_params = *self.fee_params();
863        let base_asset_id = *self.base_asset_id();
864        let gas_price = self.gas_price();
865
866        #[cfg(debug_assertions)]
867        // The `match` statement exists to ensure that all variants of `Transaction`
868        // are handled below. If a new variant is added, the compiler will
869        // emit an error.
870        {
871            let mint: Transaction = Transaction::mint(
872                Default::default(),
873                Default::default(),
874                Default::default(),
875                Default::default(),
876                Default::default(),
877                Default::default(),
878            )
879            .into();
880            match mint {
881                Transaction::Create(_) => {
882                    // Handled in the `self.tx.as_create_mut()` branch.
883                }
884                Transaction::Upgrade(_) => {
885                    // Handled in the `self.tx.as_upgrade_mut()` branch.
886                }
887                Transaction::Upload(_) => {
888                    // Handled in the `self.tx.as_upload_mut()` branch.
889                }
890                Transaction::Blob(_) => {
891                    // Handled in the `self.tx.as_blob_mut()` branch.
892                }
893                Transaction::Script(_) => {
894                    // Handled in the `else` branch.
895                }
896                Transaction::Mint(_) => {
897                    // The `Mint` transaction doesn't implement `ExecutableTransaction`.
898                }
899            };
900        }
901
902        let state = if let Some(create) = self.tx.as_create_mut() {
903            Self::deploy_inner(
904                create,
905                &mut self.storage,
906                self.initial_balances.clone(),
907                &gas_costs,
908                &fee_params,
909                &base_asset_id,
910                gas_price,
911            )?;
912            ProgramState::Return(1)
913        } else if let Some(upgrade) = self.tx.as_upgrade_mut() {
914            Self::upgrade_inner(
915                upgrade,
916                &mut self.storage,
917                self.initial_balances.clone(),
918                &gas_costs,
919                &fee_params,
920                &base_asset_id,
921                gas_price,
922            )?;
923            ProgramState::Return(1)
924        } else if let Some(upload) = self.tx.as_upload_mut() {
925            Self::upload_inner(
926                upload,
927                &mut self.storage,
928                self.initial_balances.clone(),
929                &gas_costs,
930                &fee_params,
931                &base_asset_id,
932                gas_price,
933            )?;
934            ProgramState::Return(1)
935        } else if let Some(blob) = self.tx.as_blob_mut() {
936            Self::blob_inner(
937                blob,
938                &mut self.storage,
939                self.initial_balances.clone(),
940                &gas_costs,
941                &fee_params,
942                &base_asset_id,
943                gas_price,
944            )?;
945            ProgramState::Return(1)
946        } else {
947            let gas_limit;
948            let is_empty_script;
949            if let Some(script) = self.transaction().as_script() {
950                gas_limit = *script.script_gas_limit();
951                is_empty_script = script.script().is_empty();
952            } else {
953                unreachable!(
954                    "Only `Script` transactions can be executed inside of the VM"
955                )
956            }
957
958            // TODO set tree balance
959
960            // `Interpreter` supports only `Create` and `Script` transactions. It is not
961            // `Create` -> it is `Script`.
962            let program = if !is_empty_script {
963                self.run_program()
964            } else {
965                // Return `1` as successful execution.
966                let return_val = 1;
967                self.ret(return_val)?;
968                Ok(ProgramState::Return(return_val))
969            };
970
971            let gas_used = gas_limit
972                .checked_sub(self.remaining_gas())
973                .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
974
975            // Catch VM panic and don't propagate, generating a receipt
976            let (status, program) = match program {
977                Ok(s) => {
978                    // either a revert or success
979                    let res = if let ProgramState::Revert(_) = &s {
980                        ScriptExecutionResult::Revert
981                    } else {
982                        ScriptExecutionResult::Success
983                    };
984                    (res, s)
985                }
986
987                Err(e) => match e.instruction_result() {
988                    Some(result) => {
989                        self.append_panic_receipt(result);
990
991                        (ScriptExecutionResult::Panic, ProgramState::Revert(0))
992                    }
993
994                    // This isn't a specified case of an erroneous program and should be
995                    // propagated. If applicable, OS errors will fall into this category.
996                    None => return Err(e),
997                },
998            };
999
1000            let receipt = Receipt::script_result(status, gas_used);
1001
1002            self.receipts.push(receipt)?;
1003
1004            if program.is_debug() {
1005                self.debugger_set_last_state(program);
1006            }
1007
1008            let revert = matches!(program, ProgramState::Revert(_));
1009            let gas_price = self.gas_price();
1010            Self::finalize_outputs(
1011                &mut self.tx,
1012                &gas_costs,
1013                &fee_params,
1014                &base_asset_id,
1015                revert,
1016                gas_used,
1017                &self.initial_balances,
1018                &self.balances,
1019                gas_price,
1020            )?;
1021
1022            program
1023        };
1024        self.update_transaction_outputs()?;
1025
1026        Ok(state)
1027    }
1028
1029    pub(crate) fn run_program(
1030        &mut self,
1031    ) -> Result<ProgramState, InterpreterError<S::DataError>> {
1032        loop {
1033            // Check whether the instruction will be executed in a call context
1034            let in_call = !self.frames.is_empty();
1035
1036            let state = self.execute()?;
1037
1038            if in_call {
1039                // Only reverts should terminate execution from a call context
1040                match state {
1041                    ExecuteState::Revert(r) => return Ok(ProgramState::Revert(r)),
1042                    ExecuteState::DebugEvent(d) => return Ok(ProgramState::RunProgram(d)),
1043                    _ => {}
1044                }
1045            } else {
1046                match state {
1047                    ExecuteState::Return(r) => return Ok(ProgramState::Return(r)),
1048                    ExecuteState::ReturnData(d) => return Ok(ProgramState::ReturnData(d)),
1049                    ExecuteState::Revert(r) => return Ok(ProgramState::Revert(r)),
1050                    ExecuteState::DebugEvent(d) => return Ok(ProgramState::RunProgram(d)),
1051                    ExecuteState::Proceed => {}
1052                }
1053            }
1054        }
1055    }
1056
1057    /// Update tx fields after execution
1058    pub(crate) fn post_execute(&mut self) {
1059        if let Some(script) = self.tx.as_script_mut() {
1060            *script.receipts_root_mut() = self.receipts.root();
1061        }
1062    }
1063}
1064
1065impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
1066where
1067    M: Memory,
1068    S: InterpreterStorage,
1069    Tx: ExecutableTransaction,
1070    <Tx as IntoChecked>::Metadata: CheckedMetadata,
1071    Ecal: EcalHandler,
1072{
1073    /// Initialize a pre-allocated instance of [`Interpreter`] with the provided
1074    /// transaction and execute it. The result will be bound to the lifetime
1075    /// of the interpreter and will avoid unnecessary copy with the data
1076    /// that can be referenced from the interpreter instance itself.
1077    pub fn transact(
1078        &mut self,
1079        tx: Ready<Tx>,
1080    ) -> Result<StateTransitionRef<'_, Tx>, InterpreterError<S::DataError>> {
1081        self.verify_ready_tx(&tx)?;
1082
1083        let state_result = self.init_script(tx).and_then(|_| self.run());
1084        self.post_execute();
1085
1086        #[cfg(feature = "profile-any")]
1087        {
1088            let r = match &state_result {
1089                Ok(state) => Ok(state),
1090                Err(err) => Err(err.erase_generics()),
1091            };
1092            self.profiler.on_transaction(r);
1093        }
1094
1095        let state = state_result?;
1096        Ok(StateTransitionRef::new(
1097            state,
1098            self.transaction(),
1099            self.receipts(),
1100        ))
1101    }
1102}
1103
1104impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
1105where
1106    S: InterpreterStorage,
1107{
1108    /// Deploys `Create` transaction without initialization VM and without invalidation of
1109    /// the last state of execution of the `Script` transaction.
1110    ///
1111    /// Returns `Create` transaction with all modifications after execution.
1112    pub fn deploy(
1113        &mut self,
1114        tx: Ready<Create>,
1115    ) -> Result<Create, InterpreterError<S::DataError>> {
1116        self.verify_ready_tx(&tx)?;
1117
1118        let (_, checked) = tx.decompose();
1119        let (mut create, metadata): (Create, <Create as IntoChecked>::Metadata) =
1120            checked.into();
1121        let base_asset_id = *self.base_asset_id();
1122        let gas_price = self.gas_price();
1123        Self::deploy_inner(
1124            &mut create,
1125            &mut self.storage,
1126            metadata.balances(),
1127            &self.interpreter_params.gas_costs,
1128            &self.interpreter_params.fee_params,
1129            &base_asset_id,
1130            gas_price,
1131        )?;
1132        Ok(create)
1133    }
1134}
1135
1136impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
1137where
1138    S: InterpreterStorage,
1139{
1140    /// Executes `Upgrade` transaction without initialization VM and without invalidation
1141    /// of the last state of execution of the `Script` transaction.
1142    ///
1143    /// Returns `Upgrade` transaction with all modifications after execution.
1144    pub fn upgrade(
1145        &mut self,
1146        tx: Ready<Upgrade>,
1147    ) -> Result<Upgrade, InterpreterError<S::DataError>> {
1148        self.verify_ready_tx(&tx)?;
1149
1150        let (_, checked) = tx.decompose();
1151        let (mut upgrade, metadata): (Upgrade, <Upgrade as IntoChecked>::Metadata) =
1152            checked.into();
1153        let base_asset_id = *self.base_asset_id();
1154        let gas_price = self.gas_price();
1155        Self::upgrade_inner(
1156            &mut upgrade,
1157            &mut self.storage,
1158            metadata.balances(),
1159            &self.interpreter_params.gas_costs,
1160            &self.interpreter_params.fee_params,
1161            &base_asset_id,
1162            gas_price,
1163        )?;
1164        Ok(upgrade)
1165    }
1166}
1167
1168impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
1169where
1170    S: InterpreterStorage,
1171{
1172    /// Executes `Upload` transaction without initialization VM and without invalidation
1173    /// of the last state of execution of the `Script` transaction.
1174    ///
1175    /// Returns `Upload` transaction with all modifications after execution.
1176    pub fn upload(
1177        &mut self,
1178        tx: Ready<Upload>,
1179    ) -> Result<Upload, InterpreterError<S::DataError>> {
1180        self.verify_ready_tx(&tx)?;
1181
1182        let (_, checked) = tx.decompose();
1183        let (mut upload, metadata): (Upload, <Upload as IntoChecked>::Metadata) =
1184            checked.into();
1185        let base_asset_id = *self.base_asset_id();
1186        let gas_price = self.gas_price();
1187        Self::upload_inner(
1188            &mut upload,
1189            &mut self.storage,
1190            metadata.balances(),
1191            &self.interpreter_params.gas_costs,
1192            &self.interpreter_params.fee_params,
1193            &base_asset_id,
1194            gas_price,
1195        )?;
1196        Ok(upload)
1197    }
1198}
1199
1200impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
1201where
1202    S: InterpreterStorage,
1203{
1204    /// Executes `Blob` transaction without initialization VM and without invalidation
1205    /// of the last state of execution of the `Script` transaction.
1206    ///
1207    /// Returns `Blob` transaction with all modifications after execution.
1208    pub fn blob(
1209        &mut self,
1210        tx: Ready<Blob>,
1211    ) -> Result<Blob, InterpreterError<S::DataError>> {
1212        self.verify_ready_tx(&tx)?;
1213
1214        let (_, checked) = tx.decompose();
1215        let (mut blob, metadata): (Blob, <Blob as IntoChecked>::Metadata) =
1216            checked.into();
1217        let base_asset_id = *self.base_asset_id();
1218        let gas_price = self.gas_price();
1219        Self::blob_inner(
1220            &mut blob,
1221            &mut self.storage,
1222            metadata.balances(),
1223            &self.interpreter_params.gas_costs,
1224            &self.interpreter_params.fee_params,
1225            &base_asset_id,
1226            gas_price,
1227        )?;
1228        Ok(blob)
1229    }
1230}
1231
1232impl<M, S: InterpreterStorage, Tx, Ecal> Interpreter<M, S, Tx, Ecal> {
1233    fn verify_ready_tx<Tx2: IntoChecked>(
1234        &self,
1235        tx: &Ready<Tx2>,
1236    ) -> Result<(), InterpreterError<S::DataError>> {
1237        self.gas_price_matches(tx)?;
1238        Ok(())
1239    }
1240
1241    fn gas_price_matches<Tx2: IntoChecked>(
1242        &self,
1243        tx: &Ready<Tx2>,
1244    ) -> Result<(), InterpreterError<S::DataError>> {
1245        if tx.gas_price() != self.gas_price() {
1246            Err(InterpreterError::ReadyTransactionWrongGasPrice {
1247                expected: self.gas_price(),
1248                actual: tx.gas_price(),
1249            })
1250        } else {
1251            Ok(())
1252        }
1253    }
1254}