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#[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
145pub mod predicates {
148 use super::*;
149 use crate::storage::predicate::PredicateStorageProvider;
150
151 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 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 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 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(¶ms.gas_costs, ¶ms.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(¶ms.gas_costs, ¶ms.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 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 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 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 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 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 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 {
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 }
888 Transaction::Upgrade(_) => {
889 }
891 Transaction::Upload(_) => {
892 }
894 Transaction::Blob(_) => {
895 }
897 Transaction::Script(_) => {
898 }
900 Transaction::Mint(_) => {
901 }
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 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 let return_val = 1;
973 self.ret(return_val)?;
974 (
975 ScriptExecutionResult::Success,
976 ProgramState::Return(return_val),
977 )
978 } else {
979 loop {
981 let in_call = !self.frames.is_empty();
983
984 match self.execute::<false>() {
985 Ok(ExecuteState::Proceed) => continue,
987 Ok(ExecuteState::DebugEvent(d)) => {
989 self.debugger_set_last_state(ProgramState::RunProgram(d));
990 return Ok(ProgramState::RunProgram(d));
991 }
992 Ok(ExecuteState::Revert(r)) => {
994 break (ScriptExecutionResult::Revert, ProgramState::Revert(r))
995 }
996 Ok(ExecuteState::Return(_) | ExecuteState::ReturnData(_))
998 if in_call =>
999 {
1000 continue
1001 }
1002 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 Err(e) => match e.instruction_result() {
1014 Some(result) => {
1015 self.append_panic_receipt(result);
1016 break (ScriptExecutionResult::Panic, ProgramState::Revert(0));
1017 }
1018 None => return Err(e),
1023 },
1024 }
1025 }
1026 };
1027
1028 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 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 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 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 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 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 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}