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    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        predicate::PredicateStorageRequirements,
59        UploadedBytecode,
60        UploadedBytecodes,
61    },
62};
63use fuel_asm::PanicReason;
64use fuel_storage::{
65    StorageAsMut,
66    StorageAsRef,
67};
68use fuel_tx::{
69    field::{
70        BlobId as _,
71        BytecodeRoot,
72        BytecodeWitnessIndex,
73        ReceiptsRoot,
74        Salt,
75        Script as ScriptField,
76        ScriptGasLimit,
77        StorageSlots,
78        SubsectionIndex,
79        SubsectionsNumber,
80        UpgradePurpose as UpgradePurposeField,
81        Witnesses,
82    },
83    input::{
84        coin::CoinPredicate,
85        message::{
86            MessageCoinPredicate,
87            MessageDataPredicate,
88        },
89    },
90    Blob,
91    BlobIdExt,
92    ConsensusParameters,
93    Contract,
94    Create,
95    FeeParameters,
96    GasCosts,
97    Input,
98    Receipt,
99    ScriptExecutionResult,
100    Transaction,
101    Upgrade,
102    UpgradeMetadata,
103    UpgradePurpose,
104    Upload,
105    ValidityError,
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                    result.map(|_| (used_gas, index))
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                let result = result.map(|_| (gas_used, index));
351                checks.push(result);
352            }
353        }
354
355        finalize_check_predicate(kind, checks, params)
356    }
357
358    fn check_predicate<Tx>(
359        tx: Tx,
360        index: usize,
361        predicate_action: PredicateAction,
362        predicate: RuntimePredicate,
363        params: CheckPredicateParams,
364        memory: &mut MemoryInstance,
365        storage: &impl PredicateStorageRequirements,
366    ) -> (Word, Result<(), PredicateVerificationFailed>)
367    where
368        Tx: ExecutableTransaction,
369    {
370        if predicate_action == PredicateAction::Verifying {
371            match &tx.inputs()[index] {
372                Input::CoinPredicate(CoinPredicate {
373                    owner: address,
374                    predicate,
375                    ..
376                })
377                | Input::MessageDataPredicate(MessageDataPredicate {
378                    recipient: address,
379                    predicate,
380                    ..
381                })
382                | Input::MessageCoinPredicate(MessageCoinPredicate {
383                    predicate,
384                    recipient: address,
385                    ..
386                }) => {
387                    if !Input::is_predicate_owner_valid(address, &**predicate) {
388                        return (0, Err(PredicateVerificationFailed::InvalidOwner));
389                    }
390                }
391                _ => {}
392            }
393        }
394
395        let zero_gas_price = 0;
396        let interpreter_params = InterpreterParams::new(zero_gas_price, params);
397
398        let mut vm = Interpreter::<_, _, _>::with_storage(
399            memory,
400            PredicateStorage::new(storage),
401            interpreter_params,
402        );
403
404        let (context, available_gas) = match predicate_action {
405            PredicateAction::Verifying => {
406                let context = Context::PredicateVerification { program: predicate };
407                let available_gas = tx.inputs()[index]
408                    .predicate_gas_used()
409                    .expect("We only run predicates at this stage, so it should exist.");
410
411                (context, available_gas)
412            }
413            PredicateAction::Estimating { available_gas } => {
414                let context = Context::PredicateEstimation { program: predicate };
415
416                (context, available_gas)
417            }
418        };
419
420        if let Err(err) = vm.init_predicate(context, tx, available_gas) {
421            return (0, Err(err.into()));
422        }
423
424        let result = vm.verify_predicate();
425        let is_successful = matches!(result, Ok(ProgramState::Return(0x01)));
426
427        let Some(gas_used) = available_gas.checked_sub(vm.remaining_gas()) else {
428            return (0, Err(Bug::new(BugVariant::GlobalGasUnderflow).into()));
429        };
430
431        if let PredicateAction::Verifying = predicate_action {
432            if !is_successful {
433                return if let Err(err) = result {
434                    (gas_used, Err(err))
435                } else {
436                    (gas_used, Err(PredicateVerificationFailed::False))
437                }
438            }
439
440            if vm.remaining_gas() != 0 {
441                return (gas_used, Err(PredicateVerificationFailed::GasMismatch));
442            }
443        }
444
445        (gas_used, Ok(()))
446    }
447
448    fn finalize_check_predicate<Tx>(
449        mut kind: PredicateRunKind<Tx>,
450        checks: Vec<Result<(Word, usize), PredicateVerificationFailed>>,
451        params: &CheckPredicateParams,
452    ) -> Result<PredicatesChecked, PredicateVerificationFailed>
453    where
454        Tx: ExecutableTransaction,
455    {
456        if let PredicateRunKind::Estimating(tx) = &mut kind {
457            checks.iter().for_each(|result| {
458                if let Ok((gas_used, index)) = result {
459                    match &mut tx.inputs_mut()[*index] {
460                        Input::CoinPredicate(CoinPredicate {
461                            predicate_gas_used,
462                            ..
463                        })
464                        | Input::MessageCoinPredicate(MessageCoinPredicate {
465                            predicate_gas_used,
466                            ..
467                        })
468                        | Input::MessageDataPredicate(MessageDataPredicate {
469                            predicate_gas_used,
470                            ..
471                        }) => {
472                            *predicate_gas_used = *gas_used;
473                        }
474                        _ => {
475                            unreachable!(
476                                "It was checked before during iteration over predicates"
477                            )
478                        }
479                    }
480                }
481            });
482        }
483
484        let max_gas = kind.tx().max_gas(&params.gas_costs, &params.fee_params);
485        if max_gas > params.max_gas_per_tx {
486            return Err(
487                PredicateVerificationFailed::TransactionExceedsTotalGasAllowance(max_gas),
488            );
489        }
490
491        let cumulative_gas_used = checks.into_iter().try_fold(0u64, |acc, result| {
492            acc.checked_add(result.map(|(gas_used, _)| gas_used)?)
493                .ok_or(PredicateVerificationFailed::OutOfGas)
494        })?;
495
496        Ok(PredicatesChecked {
497            gas_used: cumulative_gas_used,
498        })
499    }
500}
501
502impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
503where
504    S: InterpreterStorage,
505{
506    fn deploy_inner(
507        create: &mut Create,
508        storage: &mut S,
509        initial_balances: InitialBalances,
510        gas_costs: &GasCosts,
511        fee_params: &FeeParameters,
512        base_asset_id: &AssetId,
513        gas_price: Word,
514    ) -> Result<(), InterpreterError<S::DataError>> {
515        let metadata = create.metadata().as_ref();
516        debug_assert!(
517            metadata.is_some(),
518            "`deploy_inner` is called without cached metadata"
519        );
520        let salt = create.salt();
521        let storage_slots = create.storage_slots();
522        let contract = Contract::try_from(&*create)?;
523        let root = if let Some(m) = metadata {
524            m.body.contract_root
525        } else {
526            contract.root()
527        };
528
529        let storage_root = if let Some(m) = metadata {
530            m.body.state_root
531        } else {
532            Contract::initial_state_root(storage_slots.iter())
533        };
534
535        let id = if let Some(m) = metadata {
536            m.body.contract_id
537        } else {
538            contract.id(salt, &root, &storage_root)
539        };
540
541        // Prevent redeployment of contracts
542        if storage
543            .storage_contract_exists(&id)
544            .map_err(RuntimeError::Storage)?
545        {
546            return Err(InterpreterError::Panic(
547                PanicReason::ContractIdAlreadyDeployed,
548            ));
549        }
550
551        storage
552            .deploy_contract_with_id(storage_slots, &contract, &id)
553            .map_err(RuntimeError::Storage)?;
554        Self::finalize_outputs(
555            create,
556            gas_costs,
557            fee_params,
558            base_asset_id,
559            false,
560            0,
561            &initial_balances,
562            &RuntimeBalances::try_from(initial_balances.clone())?,
563            gas_price,
564        )?;
565        Ok(())
566    }
567}
568
569impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
570where
571    S: InterpreterStorage,
572{
573    fn upgrade_inner(
574        upgrade: &mut Upgrade,
575        storage: &mut S,
576        initial_balances: InitialBalances,
577        gas_costs: &GasCosts,
578        fee_params: &FeeParameters,
579        base_asset_id: &AssetId,
580        gas_price: Word,
581    ) -> Result<(), InterpreterError<S::DataError>> {
582        let metadata = upgrade.metadata().as_ref();
583        debug_assert!(
584            metadata.is_some(),
585            "`upgrade_inner` is called without cached metadata"
586        );
587
588        match upgrade.upgrade_purpose() {
589            UpgradePurpose::ConsensusParameters { .. } => {
590                let consensus_parameters = if let Some(metadata) = metadata {
591                    Self::get_consensus_parameters(&metadata.body)?
592                } else {
593                    let metadata = UpgradeMetadata::compute(upgrade)?;
594                    Self::get_consensus_parameters(&metadata)?
595                };
596
597                let current_version = storage
598                    .consensus_parameters_version()
599                    .map_err(RuntimeError::Storage)?;
600                let next_version = current_version.saturating_add(1);
601
602                let prev = storage
603                    .set_consensus_parameters(next_version, &consensus_parameters)
604                    .map_err(RuntimeError::Storage)?;
605
606                if prev.is_some() {
607                    return Err(InterpreterError::Panic(
608                        PanicReason::OverridingConsensusParameters,
609                    ));
610                }
611            }
612            UpgradePurpose::StateTransition { root } => {
613                let exists = storage
614                    .contains_state_transition_bytecode_root(root)
615                    .map_err(RuntimeError::Storage)?;
616
617                if !exists {
618                    return Err(InterpreterError::Panic(
619                        PanicReason::UnknownStateTransactionBytecodeRoot,
620                    ))
621                }
622
623                let current_version = storage
624                    .state_transition_version()
625                    .map_err(RuntimeError::Storage)?;
626                let next_version = current_version.saturating_add(1);
627
628                let prev = storage
629                    .set_state_transition_bytecode(next_version, root)
630                    .map_err(RuntimeError::Storage)?;
631
632                if prev.is_some() {
633                    return Err(InterpreterError::Panic(
634                        PanicReason::OverridingStateTransactionBytecode,
635                    ));
636                }
637            }
638        }
639
640        Self::finalize_outputs(
641            upgrade,
642            gas_costs,
643            fee_params,
644            base_asset_id,
645            false,
646            0,
647            &initial_balances,
648            &RuntimeBalances::try_from(initial_balances.clone())?,
649            gas_price,
650        )?;
651        Ok(())
652    }
653
654    fn get_consensus_parameters(
655        metadata: &UpgradeMetadata,
656    ) -> Result<ConsensusParameters, InterpreterError<S::DataError>> {
657        match &metadata {
658            UpgradeMetadata::ConsensusParameters {
659                consensus_parameters,
660                ..
661            } => Ok(consensus_parameters.as_ref().clone()),
662            UpgradeMetadata::StateTransition => {
663                // It shouldn't be possible since `Check<Upgrade>` guarantees that.
664                Err(InterpreterError::CheckError(CheckError::Validity(
665                    ValidityError::TransactionMetadataMismatch,
666                )))
667            }
668        }
669    }
670}
671
672impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
673where
674    S: InterpreterStorage,
675{
676    fn upload_inner(
677        upload: &mut Upload,
678        storage: &mut S,
679        initial_balances: InitialBalances,
680        gas_costs: &GasCosts,
681        fee_params: &FeeParameters,
682        base_asset_id: &AssetId,
683        gas_price: Word,
684    ) -> Result<(), InterpreterError<S::DataError>> {
685        let root = *upload.bytecode_root();
686        let uploaded_bytecode = storage
687            .storage_as_ref::<UploadedBytecodes>()
688            .get(&root)
689            .map_err(RuntimeError::Storage)?
690            .map(|x| x.into_owned())
691            .unwrap_or_else(|| UploadedBytecode::Uncompleted {
692                bytecode: vec![],
693                uploaded_subsections_number: 0,
694            });
695
696        let new_bytecode = match uploaded_bytecode {
697            UploadedBytecode::Uncompleted {
698                bytecode,
699                uploaded_subsections_number,
700            } => Self::upload_bytecode_subsection(
701                upload,
702                bytecode,
703                uploaded_subsections_number,
704            )?,
705            UploadedBytecode::Completed(_) => {
706                return Err(InterpreterError::Panic(
707                    PanicReason::BytecodeAlreadyUploaded,
708                ));
709            }
710        };
711
712        storage
713            .storage_as_mut::<UploadedBytecodes>()
714            .insert(&root, &new_bytecode)
715            .map_err(RuntimeError::Storage)?;
716
717        Self::finalize_outputs(
718            upload,
719            gas_costs,
720            fee_params,
721            base_asset_id,
722            false,
723            0,
724            &initial_balances,
725            &RuntimeBalances::try_from(initial_balances.clone())?,
726            gas_price,
727        )?;
728        Ok(())
729    }
730
731    fn upload_bytecode_subsection(
732        upload: &Upload,
733        mut uploaded_bytecode: Vec<u8>,
734        uploaded_subsections_number: u16,
735    ) -> Result<UploadedBytecode, InterpreterError<S::DataError>> {
736        let index_of_next_subsection = uploaded_subsections_number;
737
738        if *upload.subsection_index() != index_of_next_subsection {
739            return Err(InterpreterError::Panic(
740                PanicReason::ThePartIsNotSequentiallyConnected,
741            ));
742        }
743
744        let bytecode_subsection = upload
745            .witnesses()
746            .get(*upload.bytecode_witness_index() as usize)
747            .ok_or(InterpreterError::Bug(Bug::new(
748                // It shouldn't be possible since `Checked<Upload>` guarantees
749                // the existence of the witness.
750                BugVariant::WitnessIndexOutOfBounds,
751            )))?;
752
753        uploaded_bytecode.extend(bytecode_subsection.as_ref());
754
755        let new_uploaded_subsections_number = uploaded_subsections_number
756            .checked_add(1)
757            .ok_or(InterpreterError::Panic(PanicReason::ArithmeticOverflow))?;
758
759        // It shouldn't be possible since `Checked<Upload>` guarantees
760        // the validity of the Merkle proof.
761        if new_uploaded_subsections_number > *upload.subsections_number() {
762            return Err(InterpreterError::Bug(Bug::new(
763                BugVariant::NextSubsectionIndexIsHigherThanTotalNumberOfParts,
764            )))
765        }
766
767        let updated_uploaded_bytecode =
768            if *upload.subsections_number() == new_uploaded_subsections_number {
769                UploadedBytecode::Completed(uploaded_bytecode)
770            } else {
771                UploadedBytecode::Uncompleted {
772                    bytecode: uploaded_bytecode,
773                    uploaded_subsections_number: new_uploaded_subsections_number,
774                }
775            };
776
777        Ok(updated_uploaded_bytecode)
778    }
779}
780
781impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
782where
783    S: InterpreterStorage,
784{
785    fn blob_inner(
786        blob: &mut Blob,
787        storage: &mut S,
788        initial_balances: InitialBalances,
789        gas_costs: &GasCosts,
790        fee_params: &FeeParameters,
791        base_asset_id: &AssetId,
792        gas_price: Word,
793    ) -> Result<(), InterpreterError<S::DataError>> {
794        let blob_data = blob
795            .witnesses()
796            .get(*blob.bytecode_witness_index() as usize)
797            .ok_or(InterpreterError::Bug(Bug::new(
798                // It shouldn't be possible since `Checked<Blob>` guarantees
799                // the existence of the witness.
800                BugVariant::WitnessIndexOutOfBounds,
801            )))?;
802
803        let blob_id = blob.blob_id();
804
805        debug_assert_eq!(
806            BlobId::compute(blob_data.as_ref()),
807            *blob_id,
808            "Tx has invalid BlobId",
809        );
810
811        let old = storage
812            .storage_as_mut::<BlobData>()
813            .replace(blob_id, blob_data.as_ref())
814            .map_err(RuntimeError::Storage)?;
815
816        if old.is_some() {
817            return Err(InterpreterError::Panic(PanicReason::BlobIdAlreadyUploaded));
818        }
819
820        Self::finalize_outputs(
821            blob,
822            gas_costs,
823            fee_params,
824            base_asset_id,
825            false,
826            0,
827            &initial_balances,
828            &RuntimeBalances::try_from(initial_balances.clone())?,
829            gas_price,
830        )?;
831
832        Ok(())
833    }
834}
835
836impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
837where
838    M: Memory,
839    S: InterpreterStorage,
840    Tx: ExecutableTransaction,
841    Ecal: EcalHandler,
842    V: Verifier,
843{
844    fn update_transaction_outputs(
845        &mut self,
846    ) -> Result<(), InterpreterError<S::DataError>> {
847        let outputs = self.transaction().outputs().len();
848        (0..outputs).try_for_each(|o| self.update_memory_output(o))?;
849        Ok(())
850    }
851
852    pub(crate) fn run(&mut self) -> Result<ProgramState, InterpreterError<S::DataError>> {
853        for input in self.transaction().inputs() {
854            if let Input::Contract(contract) = input {
855                if !self.check_contract_exists(&contract.contract_id)? {
856                    return Err(InterpreterError::Panic(
857                        PanicReason::InputContractDoesNotExist,
858                    ));
859                }
860            }
861        }
862
863        // TODO: Remove `Create`, `Upgrade`, and `Upload` from here
864        //  https://github.com/FuelLabs/fuel-vm/issues/251
865        let gas_costs = self.gas_costs().clone();
866        let fee_params = *self.fee_params();
867        let base_asset_id = *self.base_asset_id();
868        let gas_price = self.gas_price();
869
870        #[cfg(debug_assertions)]
871        // The `match` statement exists to ensure that all variants of `Transaction`
872        // are handled below. If a new variant is added, the compiler will
873        // emit an error.
874        {
875            let mint: Transaction = Transaction::mint(
876                Default::default(),
877                Default::default(),
878                Default::default(),
879                Default::default(),
880                Default::default(),
881                Default::default(),
882            )
883            .into();
884            match mint {
885                Transaction::Create(_) => {
886                    // Handled in the `self.tx.as_create_mut()` branch.
887                }
888                Transaction::Upgrade(_) => {
889                    // Handled in the `self.tx.as_upgrade_mut()` branch.
890                }
891                Transaction::Upload(_) => {
892                    // Handled in the `self.tx.as_upload_mut()` branch.
893                }
894                Transaction::Blob(_) => {
895                    // Handled in the `self.tx.as_blob_mut()` branch.
896                }
897                Transaction::Script(_) => {
898                    // Handled in the `else` branch.
899                }
900                Transaction::Mint(_) => {
901                    // The `Mint` transaction doesn't implement `ExecutableTransaction`.
902                }
903            };
904        }
905
906        let state = if let Some(create) = self.tx.as_create_mut() {
907            Self::deploy_inner(
908                create,
909                &mut self.storage,
910                self.initial_balances.clone(),
911                &gas_costs,
912                &fee_params,
913                &base_asset_id,
914                gas_price,
915            )?;
916            self.update_transaction_outputs()?;
917            ProgramState::Return(1)
918        } else if let Some(upgrade) = self.tx.as_upgrade_mut() {
919            Self::upgrade_inner(
920                upgrade,
921                &mut self.storage,
922                self.initial_balances.clone(),
923                &gas_costs,
924                &fee_params,
925                &base_asset_id,
926                gas_price,
927            )?;
928            self.update_transaction_outputs()?;
929            ProgramState::Return(1)
930        } else if let Some(upload) = self.tx.as_upload_mut() {
931            Self::upload_inner(
932                upload,
933                &mut self.storage,
934                self.initial_balances.clone(),
935                &gas_costs,
936                &fee_params,
937                &base_asset_id,
938                gas_price,
939            )?;
940            self.update_transaction_outputs()?;
941            ProgramState::Return(1)
942        } else if let Some(blob) = self.tx.as_blob_mut() {
943            Self::blob_inner(
944                blob,
945                &mut self.storage,
946                self.initial_balances.clone(),
947                &gas_costs,
948                &fee_params,
949                &base_asset_id,
950                gas_price,
951            )?;
952            self.update_transaction_outputs()?;
953            ProgramState::Return(1)
954        } else {
955            // This must be a `Script`.
956            self.run_program()?
957        };
958
959        Ok(state)
960    }
961
962    pub(crate) fn run_program(
963        &mut self,
964    ) -> Result<ProgramState, InterpreterError<S::DataError>> {
965        let Some(script) = self.tx.as_script() else {
966            unreachable!("Only `Script` transactions can be executed inside of the VM")
967        };
968        let gas_limit = *script.script_gas_limit();
969
970        let (result, state) = if script.script().is_empty() {
971            // Empty script is special-cased to simply return `1` as successful execution.
972            let return_val = 1;
973            self.ret(return_val)?;
974            (
975                ScriptExecutionResult::Success,
976                ProgramState::Return(return_val),
977            )
978        } else {
979            // TODO set tree balance
980            loop {
981                // Check whether the instruction will be executed in a call context
982                let in_call = !self.frames.is_empty();
983
984                match self.execute::<false>() {
985                    // Proceeding with the execution normally
986                    Ok(ExecuteState::Proceed) => continue,
987                    // Debugger events are returned directly to the caller
988                    Ok(ExecuteState::DebugEvent(d)) => {
989                        self.debugger_set_last_state(ProgramState::RunProgram(d));
990                        return Ok(ProgramState::RunProgram(d));
991                    }
992                    // Reverting terminated execution immediately
993                    Ok(ExecuteState::Revert(r)) => {
994                        break (ScriptExecutionResult::Revert, ProgramState::Revert(r))
995                    }
996                    // Returning in call context is ignored
997                    Ok(ExecuteState::Return(_) | ExecuteState::ReturnData(_))
998                        if in_call =>
999                    {
1000                        continue
1001                    }
1002                    // In non-call context, returning terminates the execution
1003                    Ok(ExecuteState::Return(r)) => {
1004                        break (ScriptExecutionResult::Success, ProgramState::Return(r))
1005                    }
1006                    Ok(ExecuteState::ReturnData(d)) => {
1007                        break (
1008                            ScriptExecutionResult::Success,
1009                            ProgramState::ReturnData(d),
1010                        )
1011                    }
1012                    // Error always terminates the execution
1013                    Err(e) => match e.instruction_result() {
1014                        Some(result) => {
1015                            self.append_panic_receipt(result);
1016                            break (ScriptExecutionResult::Panic, ProgramState::Revert(0));
1017                        }
1018                        // This isn't a specified case of an erroneous program and should
1019                        // be propagated. If applicable, OS errors
1020                        // will fall into this category.
1021                        // The VM state is not finalized in this case.
1022                        None => return Err(e),
1023                    },
1024                }
1025            }
1026        };
1027
1028        // Produce result receipt
1029        let gas_used = gas_limit
1030            .checked_sub(self.remaining_gas())
1031            .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
1032        self.receipts
1033            .push(Receipt::script_result(result, gas_used))?;
1034
1035        // Finalize the outputs
1036        let fee_params = *self.fee_params();
1037        let base_asset_id = *self.base_asset_id();
1038        let gas_costs = self.gas_costs().clone();
1039        let gas_price = self.gas_price();
1040        Self::finalize_outputs(
1041            &mut self.tx,
1042            &gas_costs,
1043            &fee_params,
1044            &base_asset_id,
1045            matches!(state, ProgramState::Revert(_)),
1046            gas_used,
1047            &self.initial_balances,
1048            &self.balances,
1049            gas_price,
1050        )?;
1051        self.update_transaction_outputs()?;
1052
1053        let Some(script) = self.tx.as_script_mut() else {
1054            unreachable!("This is checked to hold in the beginning of this function");
1055        };
1056        *script.receipts_root_mut() = self.receipts.root();
1057
1058        Ok(state)
1059    }
1060}
1061
1062impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
1063where
1064    M: Memory,
1065    S: InterpreterStorage,
1066    Tx: ExecutableTransaction,
1067    <Tx as IntoChecked>::Metadata: CheckedMetadata,
1068    Ecal: EcalHandler,
1069    V: Verifier,
1070{
1071    /// Initialize a pre-allocated instance of [`Interpreter`] with the provided
1072    /// transaction and execute it. The result will be bound to the lifetime
1073    /// of the interpreter and will avoid unnecessary copy with the data
1074    /// that can be referenced from the interpreter instance itself.
1075    pub fn transact(
1076        &mut self,
1077        tx: Ready<Tx>,
1078    ) -> Result<StateTransitionRef<'_, Tx>, InterpreterError<S::DataError>> {
1079        self.verify_ready_tx(&tx)?;
1080
1081        let state_result = self.init_script(tx).and_then(|_| self.run());
1082
1083        let state = state_result?;
1084        Ok(StateTransitionRef::new(
1085            state,
1086            self.transaction(),
1087            self.receipts(),
1088        ))
1089    }
1090}
1091
1092impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
1093where
1094    S: InterpreterStorage,
1095{
1096    /// Deploys `Create` transaction without initialization VM and without invalidation of
1097    /// the last state of execution of the `Script` transaction.
1098    ///
1099    /// Returns `Create` transaction with all modifications after execution.
1100    pub fn deploy(
1101        &mut self,
1102        tx: Ready<Create>,
1103    ) -> Result<Create, InterpreterError<S::DataError>> {
1104        self.verify_ready_tx(&tx)?;
1105
1106        let (_, checked) = tx.decompose();
1107        let (mut create, metadata): (Create, <Create as IntoChecked>::Metadata) =
1108            checked.into();
1109        let base_asset_id = *self.base_asset_id();
1110        let gas_price = self.gas_price();
1111        Self::deploy_inner(
1112            &mut create,
1113            &mut self.storage,
1114            metadata.balances(),
1115            &self.interpreter_params.gas_costs,
1116            &self.interpreter_params.fee_params,
1117            &base_asset_id,
1118            gas_price,
1119        )?;
1120        Ok(create)
1121    }
1122}
1123
1124impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
1125where
1126    S: InterpreterStorage,
1127{
1128    /// Executes `Upgrade` transaction without initialization VM and without invalidation
1129    /// of the last state of execution of the `Script` transaction.
1130    ///
1131    /// Returns `Upgrade` transaction with all modifications after execution.
1132    pub fn upgrade(
1133        &mut self,
1134        tx: Ready<Upgrade>,
1135    ) -> Result<Upgrade, InterpreterError<S::DataError>> {
1136        self.verify_ready_tx(&tx)?;
1137
1138        let (_, checked) = tx.decompose();
1139        let (mut upgrade, metadata): (Upgrade, <Upgrade as IntoChecked>::Metadata) =
1140            checked.into();
1141        let base_asset_id = *self.base_asset_id();
1142        let gas_price = self.gas_price();
1143        Self::upgrade_inner(
1144            &mut upgrade,
1145            &mut self.storage,
1146            metadata.balances(),
1147            &self.interpreter_params.gas_costs,
1148            &self.interpreter_params.fee_params,
1149            &base_asset_id,
1150            gas_price,
1151        )?;
1152        Ok(upgrade)
1153    }
1154}
1155
1156impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
1157where
1158    S: InterpreterStorage,
1159{
1160    /// Executes `Upload` transaction without initialization VM and without invalidation
1161    /// of the last state of execution of the `Script` transaction.
1162    ///
1163    /// Returns `Upload` transaction with all modifications after execution.
1164    pub fn upload(
1165        &mut self,
1166        tx: Ready<Upload>,
1167    ) -> Result<Upload, InterpreterError<S::DataError>> {
1168        self.verify_ready_tx(&tx)?;
1169
1170        let (_, checked) = tx.decompose();
1171        let (mut upload, metadata): (Upload, <Upload as IntoChecked>::Metadata) =
1172            checked.into();
1173        let base_asset_id = *self.base_asset_id();
1174        let gas_price = self.gas_price();
1175        Self::upload_inner(
1176            &mut upload,
1177            &mut self.storage,
1178            metadata.balances(),
1179            &self.interpreter_params.gas_costs,
1180            &self.interpreter_params.fee_params,
1181            &base_asset_id,
1182            gas_price,
1183        )?;
1184        Ok(upload)
1185    }
1186}
1187
1188impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
1189where
1190    S: InterpreterStorage,
1191{
1192    /// Executes `Blob` transaction without initialization VM and without invalidation
1193    /// of the last state of execution of the `Script` transaction.
1194    ///
1195    /// Returns `Blob` transaction with all modifications after execution.
1196    pub fn blob(
1197        &mut self,
1198        tx: Ready<Blob>,
1199    ) -> Result<Blob, InterpreterError<S::DataError>> {
1200        self.verify_ready_tx(&tx)?;
1201
1202        let (_, checked) = tx.decompose();
1203        let (mut blob, metadata): (Blob, <Blob as IntoChecked>::Metadata) =
1204            checked.into();
1205        let base_asset_id = *self.base_asset_id();
1206        let gas_price = self.gas_price();
1207        Self::blob_inner(
1208            &mut blob,
1209            &mut self.storage,
1210            metadata.balances(),
1211            &self.interpreter_params.gas_costs,
1212            &self.interpreter_params.fee_params,
1213            &base_asset_id,
1214            gas_price,
1215        )?;
1216        Ok(blob)
1217    }
1218}
1219
1220impl<M, S: InterpreterStorage, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V> {
1221    fn verify_ready_tx<Tx2: IntoChecked>(
1222        &self,
1223        tx: &Ready<Tx2>,
1224    ) -> Result<(), InterpreterError<S::DataError>> {
1225        self.gas_price_matches(tx)?;
1226        Ok(())
1227    }
1228
1229    fn gas_price_matches<Tx2: IntoChecked>(
1230        &self,
1231        tx: &Ready<Tx2>,
1232    ) -> Result<(), InterpreterError<S::DataError>> {
1233        if tx.gas_price() != self.gas_price() {
1234            Err(InterpreterError::ReadyTransactionWrongGasPrice {
1235                expected: self.gas_price(),
1236                actual: tx.gas_price(),
1237            })
1238        } else {
1239            Ok(())
1240        }
1241    }
1242}