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#[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
146pub mod predicates {
149 use super::*;
150 use crate::storage::predicate::PredicateStorageProvider;
151
152 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 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 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 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(¶ms.gas_costs, ¶ms.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(¶ms.gas_costs, ¶ms.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 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 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 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 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 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 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 {
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 }
931 Transaction::Upgrade(_) => {
932 }
934 Transaction::Upload(_) => {
935 }
937 Transaction::Blob(_) => {
938 }
940 Transaction::Script(_) => {
941 }
943 Transaction::Mint(_) => {
944 }
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 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 let return_val = 1;
1016 self.ret(return_val)?;
1017 (
1018 ScriptExecutionResult::Success,
1019 ProgramState::Return(return_val),
1020 )
1021 } else {
1022 loop {
1024 let in_call = !self.frames.is_empty();
1026
1027 match self.execute::<false>() {
1028 Ok(ExecuteState::Proceed) => continue,
1030 Ok(ExecuteState::DebugEvent(d)) => {
1032 self.debugger_set_last_state(ProgramState::RunProgram(d));
1033 return Ok(ProgramState::RunProgram(d));
1034 }
1035 Ok(ExecuteState::Revert(r)) => {
1037 break (ScriptExecutionResult::Revert, ProgramState::Revert(r))
1038 }
1039 Ok(ExecuteState::Return(_) | ExecuteState::ReturnData(_))
1041 if in_call =>
1042 {
1043 continue
1044 }
1045 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 Err(e) => match e.instruction_result() {
1057 Some(result) => {
1058 self.append_panic_receipt(result);
1059 break (ScriptExecutionResult::Panic, ProgramState::Revert(0));
1060 }
1061 None => return Err(e),
1066 },
1067 }
1068 }
1069 };
1070
1071 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 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 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 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 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 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 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 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}