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