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