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