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