1pub mod deny;
6
7pub use checked::*;
8
9#[iota_macros::with_checked_arithmetic]
10mod checked {
11 use std::{
12 collections::{BTreeMap, HashSet},
13 sync::Arc,
14 };
15
16 use iota_config::verifier_signing_config::VerifierSigningConfig;
17 use iota_protocol_config::ProtocolConfig;
18 use iota_sdk_types::{
19 Address, ObjectId, ObjectReference, Owner, Transaction, TransactionKind, Version,
20 };
21 use iota_types::{
22 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION,
23 error::{IotaError, IotaResult, UserInputError, UserInputResult},
24 executable_transaction::VerifiedExecutableTransaction,
25 fp_bail, fp_ensure,
26 gas::IotaGasStatus,
27 metrics::BytecodeVerifierMetrics,
28 object::Object,
29 transaction::{
30 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
31 ObjectReadResultKind, ProgrammableTransactionExt, ReceivingObjectReadResult,
32 ReceivingObjects, TransactionAPI, TransactionKindExt,
33 },
34 };
35 use tracing::{error, instrument};
36
37 trait IntoChecked {
38 fn into_checked(self) -> CheckedInputObjects;
39 }
40
41 impl IntoChecked for InputObjects {
42 fn into_checked(self) -> CheckedInputObjects {
43 CheckedInputObjects::new_with_checked_transaction_inputs(self)
44 }
45 }
46
47 fn get_gas_status(
52 objects: &InputObjects,
53 gas: &[ObjectReference],
54 protocol_config: &ProtocolConfig,
55 reference_gas_price: u64,
56 transaction: &Transaction,
57 authentication_gas_budget: u64,
58 is_execute_transaction_to_effects: bool,
59 ) -> IotaResult<IotaGasStatus> {
60 if transaction.is_system_tx() {
61 Ok(IotaGasStatus::new_unmetered())
62 } else {
63 check_gas(
64 objects,
65 protocol_config,
66 reference_gas_price,
67 gas,
68 transaction.gas_price(),
69 transaction.gas_budget(),
70 authentication_gas_budget,
71 is_execute_transaction_to_effects,
72 )
73 }
74 }
75
76 #[derive(Clone, Copy, Debug)]
79 pub enum VerifierLimitsSource<'a> {
80 NodeConfig(&'a VerifierSigningConfig),
84
85 ProtocolConfig,
89 }
90
91 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
92 pub fn check_transaction_input(
93 protocol_config: &ProtocolConfig,
94 reference_gas_price: u64,
95 transaction: &Transaction,
96 input_objects: InputObjects,
97 receiving_objects: &ReceivingObjects,
98 metrics: &Arc<BytecodeVerifierMetrics>,
99 verifier_limits_source: VerifierLimitsSource<'_>,
100 authentication_gas_budget: u64,
101 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
102 let gas_status = check_transaction_input_inner(
103 protocol_config,
104 reference_gas_price,
105 transaction,
106 &input_objects,
107 &[],
108 authentication_gas_budget,
109 false,
110 )?;
111 check_receiving_objects(&input_objects, receiving_objects)?;
112 check_non_system_packages_to_be_published(
114 transaction,
115 protocol_config,
116 metrics,
117 verifier_limits_source,
118 )?;
119
120 Ok((gas_status, input_objects.into_checked()))
121 }
122
123 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
124 pub fn check_transaction_input_with_given_gas(
125 protocol_config: &ProtocolConfig,
126 reference_gas_price: u64,
127 transaction: &Transaction,
128 mut input_objects: InputObjects,
129 receiving_objects: ReceivingObjects,
130 gas_object: Object,
131 metrics: &Arc<BytecodeVerifierMetrics>,
132 verifier_limits_source: VerifierLimitsSource<'_>,
133 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
134 let gas_object_ref = gas_object.object_ref();
135 input_objects.push(ObjectReadResult::new_from_gas_object(&gas_object));
136
137 let gas_status = check_transaction_input_inner(
138 protocol_config,
139 reference_gas_price,
140 transaction,
141 &input_objects,
142 &[gas_object_ref],
143 0,
144 true,
145 )?;
146 check_receiving_objects(&input_objects, &receiving_objects)?;
147 check_non_system_packages_to_be_published(
149 transaction,
150 protocol_config,
151 metrics,
152 verifier_limits_source,
153 )?;
154
155 Ok((gas_status, input_objects.into_checked()))
156 }
157
158 #[instrument(level = "trace", skip_all)]
164 pub fn check_certificate_input(
165 cert: &VerifiedExecutableTransaction,
166 input_objects: InputObjects,
167 protocol_config: &ProtocolConfig,
168 reference_gas_price: u64,
169 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
170 let transaction = cert.data().transaction();
171 let gas_status = check_transaction_input_inner(
172 protocol_config,
173 reference_gas_price,
174 transaction,
175 &input_objects,
176 &[],
177 0,
178 true,
179 )?;
180 Ok((gas_status, input_objects.into_checked()))
185 }
186
187 #[instrument(level = "trace", skip_all)]
193 pub fn check_simulation_input(
194 config: &ProtocolConfig,
195 kind: &TransactionKind,
196 input_objects: InputObjects,
197 _receiving_objects: ReceivingObjects,
199 ) -> IotaResult<CheckedInputObjects> {
200 kind.validity_check(config)?;
201 if kind.is_system() {
202 return Err(UserInputError::Unsupported(format!(
203 "Transaction kind {kind} is not supported in a simulation"
204 ))
205 .into());
206 }
207 let mut used_objects: HashSet<Address> = HashSet::new();
208 for input_object in input_objects.iter() {
209 let Some(object) = input_object.as_object() else {
210 continue;
212 };
213
214 if !object.is_immutable() {
215 fp_ensure!(
216 used_objects.insert(object.id().into()),
217 UserInputError::MutableObjectUsedMoreThanOnce {
218 object_id: object.id()
219 }
220 .into()
221 );
222 }
223 }
224
225 Ok(input_objects.into_checked())
226 }
227
228 #[instrument(level = "trace", skip_all)]
234 pub fn check_move_authenticator_input_for_validation(
235 authenticator_input_objects: InputObjects,
236 ) -> IotaResult<CheckedInputObjects> {
237 check_move_authenticator_objects(&authenticator_input_objects)?;
238
239 Ok(authenticator_input_objects.into_checked())
240 }
241
242 pub fn aggregate_authenticator_input_objects(
246 per_authenticator_checked_input_objects: &[&CheckedInputObjects],
247 ) -> IotaResult<CheckedInputObjects> {
248 let mut aggregated_authenticator_input_objects =
249 CheckedInputObjects::new_with_checked_transaction_inputs(InputObjects::new(vec![]));
250
251 for authenticator_checked_input_objects in per_authenticator_checked_input_objects.iter() {
252 aggregated_authenticator_input_objects = checked_input_objects_union(
253 aggregated_authenticator_input_objects,
254 authenticator_checked_input_objects,
255 )?;
256 }
257
258 Ok(aggregated_authenticator_input_objects)
259 }
260
261 #[instrument(level = "trace", skip_all)]
273 pub fn check_certificate_and_move_authenticator_input(
274 cert: &VerifiedExecutableTransaction,
275 tx_input_objects: InputObjects,
276 per_authenticator_input_objects: Vec<InputObjects>,
277 authenticator_gas_budget: u64,
278 protocol_config: &ProtocolConfig,
279 reference_gas_price: u64,
280 ) -> IotaResult<(IotaGasStatus, Vec<CheckedInputObjects>, CheckedInputObjects)> {
281 per_authenticator_input_objects
283 .iter()
284 .try_for_each(check_move_authenticator_objects)?;
285
286 let transaction = cert.data().transaction();
288 let gas_status = check_transaction_input_inner(
289 protocol_config,
290 reference_gas_price,
291 transaction,
292 &tx_input_objects,
293 &[],
294 authenticator_gas_budget,
295 true,
296 )?;
297
298 let per_authenticator_checked_input_objects = per_authenticator_input_objects
299 .into_iter()
300 .map(|objects| objects.into_checked())
301 .collect::<Vec<_>>();
302
303 let mut input_objects_union = tx_input_objects.into_checked();
305 for objects in per_authenticator_checked_input_objects.iter() {
306 input_objects_union = checked_input_objects_union(input_objects_union, objects)?;
307 }
308
309 Ok((
310 gas_status,
311 per_authenticator_checked_input_objects,
312 input_objects_union,
313 ))
314 }
315
316 fn check_transaction_input_inner(
318 protocol_config: &ProtocolConfig,
319 reference_gas_price: u64,
320 transaction: &Transaction,
321 input_objects: &InputObjects,
322 gas_override: &[ObjectReference],
324 authentication_gas_budget: u64,
325 is_execute_transaction_to_effects: bool,
326 ) -> IotaResult<IotaGasStatus> {
327 let gas = if gas_override.is_empty() {
329 transaction.gas()
330 } else {
331 gas_override
332 };
333
334 let gas_status = get_gas_status(
335 input_objects,
336 gas,
337 protocol_config,
338 reference_gas_price,
339 transaction,
340 authentication_gas_budget,
341 is_execute_transaction_to_effects,
342 )?;
343 check_objects(transaction, input_objects)?;
344
345 Ok(gas_status)
346 }
347
348 #[instrument(level = "trace", skip_all)]
349 fn check_receiving_objects(
350 input_objects: &InputObjects,
351 receiving_objects: &ReceivingObjects,
352 ) -> Result<(), IotaError> {
353 let mut objects_in_txn: HashSet<_> = input_objects
354 .object_kinds()
355 .map(|x| x.object_id())
356 .collect();
357
358 for ReceivingObjectReadResult { object_ref, object } in receiving_objects.iter() {
367 fp_ensure!(
368 object_ref.version < Version::MAX_VALID_EXCL,
369 UserInputError::InvalidSequenceNumber.into()
370 );
371
372 let Some(object) = object.as_object() else {
373 continue;
375 };
376
377 if !(object.owner.is_address()
378 && object.version() == object_ref.version
379 && object.digest() == object_ref.digest)
380 {
381 fp_ensure!(
383 object.version() == object_ref.version,
384 UserInputError::ObjectVersionUnavailableForConsumption {
385 provided_obj_ref: *object_ref,
386 current_version: object.version(),
387 }
388 .into()
389 );
390
391 fp_ensure!(
393 !object.is_package(),
394 UserInputError::MovePackageAsObject {
395 object_id: object_ref.object_id
396 }
397 .into()
398 );
399
400 let expected_digest = object.digest();
402 fp_ensure!(
403 expected_digest == object_ref.digest,
404 UserInputError::InvalidObjectDigest {
405 object_id: object_ref.object_id,
406 expected_digest
407 }
408 .into()
409 );
410
411 match object.owner {
412 Owner::Address(_) => {
413 debug_assert!(
414 false,
415 "Receiving object {object_ref:?} is invalid but we expect it should be valid. {object:?}"
416 );
417 error!(
418 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
419 object_ref, object
420 );
421 fp_bail!(
424 UserInputError::ObjectNotFound {
425 object_id: object_ref.object_id,
426 version: Some(object_ref.version),
427 }
428 .into()
429 )
430 }
431 Owner::Object(owner) => {
432 fp_bail!(
433 UserInputError::InvalidChildObjectArgument {
434 child_id: object.id(),
435 parent_id: owner,
436 }
437 .into()
438 )
439 }
440 Owner::Shared(_) => fp_bail!(UserInputError::NotSharedObject.into()),
441 Owner::Immutable => fp_bail!(
442 UserInputError::MutableParameterExpected {
443 object_id: object_ref.object_id
444 }
445 .into()
446 ),
447 _ => {
448 unimplemented!("a new Owner enum variant was added and needs to be handled")
449 }
450 };
451 }
452
453 fp_ensure!(
454 !objects_in_txn.contains(&object_ref.object_id),
455 UserInputError::DuplicateObjectRefInput.into()
456 );
457
458 objects_in_txn.insert(object_ref.object_id);
459 }
460 Ok(())
461 }
462
463 #[instrument(level = "trace", skip_all)]
466 fn check_gas(
467 objects: &InputObjects,
468 protocol_config: &ProtocolConfig,
469 reference_gas_price: u64,
470 gas: &[ObjectReference],
471 gas_price: u64,
472 transaction_gas_budget: u64,
473 authentication_gas_budget: u64,
474 is_execute_transaction_to_effects: bool,
475 ) -> IotaResult<IotaGasStatus> {
476 let gas_budget_to_set = if authentication_gas_budget > 0 {
477 let protocol_max_auth_gas =
480 protocol_config.max_auth_gas_as_option().ok_or_else(|| {
481 UserInputError::Unsupported(
482 "Transaction requires authentication gas but max_auth_gas is not enabled"
483 .to_string(),
484 )
485 })?;
486
487 if is_execute_transaction_to_effects {
494 transaction_gas_budget
495 } else {
496 authentication_gas_budget.min(protocol_max_auth_gas)
497 }
498 } else {
499 transaction_gas_budget
502 };
503
504 let gas_budget_to_check = transaction_gas_budget;
507
508 let gas_status = IotaGasStatus::new(
509 gas_budget_to_set,
510 gas_price,
511 reference_gas_price,
512 protocol_config,
513 )?;
514
515 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
518 let mut gas_objects = vec![];
519 for obj_ref in gas {
520 let obj = objects.get(&obj_ref.object_id);
521 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
522 object_id: obj_ref.object_id,
523 version: Some(obj_ref.version),
524 })?;
525 gas_objects.push(obj);
526 }
527 gas_status.check_gas_balance(&gas_objects, gas_budget_to_check)?;
528 Ok(gas_status)
529 }
530
531 #[instrument(level = "trace", skip_all)]
534 fn check_objects(transaction: &Transaction, objects: &InputObjects) -> UserInputResult<()> {
535 let mut used_objects: HashSet<Address> = HashSet::new();
537 for object in objects.iter() {
538 if object.is_mutable() {
539 fp_ensure!(
540 used_objects.insert(object.id().into()),
541 UserInputError::MutableObjectUsedMoreThanOnce {
542 object_id: object.id()
543 }
544 );
545 }
546 }
547
548 if !transaction.is_genesis_tx() && objects.is_empty() {
549 return Err(UserInputError::ObjectInputArityViolation);
550 }
551
552 let gas_coins: HashSet<ObjectId> =
553 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.object_id));
554 for object in objects.iter() {
555 let input_object_kind = object.input_object_kind;
556
557 match &object.object {
558 ObjectReadResultKind::Object(object) => {
559 let owner_address = if gas_coins.contains(&object.id()) {
561 transaction.gas_owner()
562 } else {
563 transaction.sender()
564 };
565 let system_transaction = transaction.is_system_tx();
568 check_one_object(
569 &owner_address,
570 input_object_kind,
571 object,
572 system_transaction,
573 )?;
574 }
575 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
577 ObjectReadResultKind::CancelledTransactionObject(_) => (),
580 }
581 }
582
583 Ok(())
584 }
585
586 fn check_one_object(
588 owner: &Address,
589 object_kind: InputObjectKind,
590 object: &Object,
591 system_transaction: bool,
592 ) -> UserInputResult {
593 match object_kind {
594 InputObjectKind::MovePackage(package_id) => {
595 fp_ensure!(
596 object.data.as_opt_package().is_some(),
597 UserInputError::MoveObjectAsPackage {
598 object_id: package_id
599 }
600 );
601 }
602 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
603 fp_ensure!(
604 !object.is_package(),
605 UserInputError::MovePackageAsObject {
606 object_id: object_ref.object_id
607 }
608 );
609 fp_ensure!(
610 object_ref.version < Version::MAX_VALID_EXCL,
611 UserInputError::InvalidSequenceNumber
612 );
613
614 assert_eq!(
616 object.version(),
617 object_ref.version,
618 "The fetched object version {} does not match the requested version {}, object id: {}",
619 object.version(),
620 object_ref.version,
621 object.id(),
622 );
623
624 let expected_digest = object.digest();
626 fp_ensure!(
627 expected_digest == object_ref.digest,
628 UserInputError::InvalidObjectDigest {
629 object_id: object_ref.object_id,
630 expected_digest
631 }
632 );
633
634 match object.owner {
635 Owner::Immutable => {
636 }
638 Owner::Address(actual_owner) => {
639 fp_ensure!(
641 owner == &actual_owner,
642 UserInputError::IncorrectUserSignature {
643 error: format!(
644 "Object {} is owned by account address {}, but given owner/signer address is {}",
645 object_ref.object_id, actual_owner, owner
646 ),
647 }
648 );
649 }
650 Owner::Object(owner) => {
651 return Err(UserInputError::InvalidChildObjectArgument {
652 child_id: object.id(),
653 parent_id: owner,
654 });
655 }
656 Owner::Shared(_) => {
657 return Err(UserInputError::NotSharedObject);
660 }
661 _ => {
662 unimplemented!("a new Owner enum variant was added and needs to be handled")
663 }
664 };
665 }
666 InputObjectKind::SharedMoveObject {
667 id: ObjectId::CLOCK,
668 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
669 mutable: true,
670 } => {
671 if system_transaction {
674 return Ok(());
675 } else {
676 return Err(UserInputError::ImmutableParameterExpected {
677 object_id: ObjectId::CLOCK,
678 });
679 }
680 }
681 InputObjectKind::SharedMoveObject {
682 id: ObjectId::AUTHENTICATOR_STATE,
683 ..
684 } => {
685 if system_transaction {
686 return Ok(());
687 } else {
688 return Err(UserInputError::InaccessibleSystemObject {
689 object_id: ObjectId::AUTHENTICATOR_STATE,
690 });
691 }
692 }
693 InputObjectKind::SharedMoveObject {
694 id: ObjectId::RANDOMNESS_STATE,
695 mutable: true,
696 ..
697 } => {
698 if system_transaction {
701 return Ok(());
702 } else {
703 return Err(UserInputError::ImmutableParameterExpected {
704 object_id: ObjectId::RANDOMNESS_STATE,
705 });
706 }
707 }
708 InputObjectKind::SharedMoveObject {
709 id: ObjectId::TRANSACTION_DENY_RULES,
710 ..
711 } => {
712 if system_transaction {
715 return Ok(());
716 } else {
717 return Err(UserInputError::InaccessibleSystemObject {
718 object_id: ObjectId::TRANSACTION_DENY_RULES,
719 });
720 }
721 }
722 InputObjectKind::SharedMoveObject {
723 initial_shared_version: input_initial_shared_version,
724 ..
725 } => {
726 fp_ensure!(
727 object.version() < Version::MAX_VALID_EXCL,
728 UserInputError::InvalidSequenceNumber
729 );
730
731 match object.owner {
732 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
733 return Err(UserInputError::NotSharedObject);
735 }
736 Owner::Shared(actual_initial_shared_version) => {
737 fp_ensure!(
738 input_initial_shared_version == actual_initial_shared_version,
739 UserInputError::SharedObjectStartingVersionMismatch
740 )
741 }
742 _ => {
743 unimplemented!("a new Owner enum variant was added and needs to be handled")
744 }
745 }
746 }
747 };
748 Ok(())
749 }
750
751 #[instrument(level = "trace", skip_all)]
754 fn check_move_authenticator_objects(
755 authenticator_objects: &InputObjects,
756 ) -> UserInputResult<()> {
757 for object in authenticator_objects.iter() {
758 let input_object_kind = object.input_object_kind;
759
760 match &object.object {
761 ObjectReadResultKind::Object(object) => {
762 check_one_move_authenticator_object(input_object_kind, object)?;
763 }
764 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
766 ObjectReadResultKind::CancelledTransactionObject(_) => (),
769 }
770 }
771
772 Ok(())
773 }
774
775 fn check_one_move_authenticator_object(
777 object_kind: InputObjectKind,
778 object: &Object,
779 ) -> UserInputResult {
780 match object_kind {
781 InputObjectKind::MovePackage(package_id) => {
782 return Err(UserInputError::PackageIsInMoveAuthenticatorInput { package_id });
783 }
784 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
785 fp_ensure!(
786 !object.is_package(),
787 UserInputError::MovePackageAsObject {
788 object_id: object_ref.object_id
789 }
790 );
791 fp_ensure!(
792 object_ref.version < Version::MAX_VALID_EXCL,
793 UserInputError::InvalidSequenceNumber
794 );
795
796 assert_eq!(
798 object.version(),
799 object_ref.version,
800 "The fetched object version {} does not match the requested version {}, object id: {}",
801 object.version(),
802 object_ref.version,
803 object.id(),
804 );
805
806 let expected_digest = object.digest();
808 fp_ensure!(
809 expected_digest == object_ref.digest,
810 UserInputError::InvalidObjectDigest {
811 object_id: object_ref.object_id,
812 expected_digest
813 }
814 );
815
816 match object.owner {
817 Owner::Immutable => {
818 }
820 Owner::Address(_) => {
821 return Err(UserInputError::AddressOwnedIsInMoveAuthenticatorInput {
822 object_id: object.id(),
823 });
824 }
825 Owner::Object(_) => {
826 return Err(UserInputError::ObjectOwnedIsInMoveAuthenticatorInput {
827 object_id: object.id(),
828 });
829 }
830 Owner::Shared(_) => {
831 return Err(UserInputError::NotSharedObject);
834 }
835 _ => {
836 unimplemented!("a new Owner enum variant was added and needs to be handled")
837 }
838 };
839 }
840 InputObjectKind::SharedMoveObject {
841 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
842 ..
843 } => {
844 return Err(UserInputError::InaccessibleSystemObject {
845 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
846 });
847 }
848 InputObjectKind::SharedMoveObject {
849 id, mutable: true, ..
850 } => {
851 return Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput {
852 object_id: id,
853 });
854 }
855 InputObjectKind::SharedMoveObject {
856 initial_shared_version: input_initial_shared_version,
857 ..
858 } => {
859 fp_ensure!(
860 object.version() < Version::MAX_VALID_EXCL,
861 UserInputError::InvalidSequenceNumber
862 );
863
864 match object.owner {
865 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
866 return Err(UserInputError::NotSharedObject);
868 }
869 Owner::Shared(actual_initial_shared_version) => {
870 fp_ensure!(
871 input_initial_shared_version == actual_initial_shared_version,
872 UserInputError::SharedObjectStartingVersionMismatch
873 )
874 }
875 _ => {
876 unimplemented!("a new Owner enum variant was added and needs to be handled")
877 }
878 }
879 }
880 };
881 Ok(())
882 }
883
884 pub fn checked_input_objects_union(
891 base_set: CheckedInputObjects,
892 other_set: &CheckedInputObjects,
893 ) -> IotaResult<CheckedInputObjects> {
894 let mut base_set = base_set.into_inner();
895 for other_object in other_set.inner().iter() {
896 if let Some(base_object) = base_set.find_object_id_mut(other_object.id()) {
897 assert_eq!(
899 base_object.object, other_object.object,
900 "The object read result for input objects with the same id must be equal"
901 );
902
903 if let ObjectReadResultKind::Object(_) = &other_object.object {
906 base_object
907 .input_object_kind
908 .left_union_with_checks(&other_object.input_object_kind)?;
909 }
910 } else {
911 base_set.push(other_object.clone());
912 }
913 }
914 Ok(base_set.into_checked())
915 }
916
917 #[instrument(level = "trace", skip_all)]
919 pub fn check_non_system_packages_to_be_published(
920 transaction: &Transaction,
921 protocol_config: &ProtocolConfig,
922 metrics: &Arc<BytecodeVerifierMetrics>,
923 verifier_limits_source: VerifierLimitsSource<'_>,
924 ) -> UserInputResult<()> {
925 if transaction.is_system_tx() {
927 return Ok(());
928 }
929
930 let TransactionKind::Programmable(pt) = transaction.kind() else {
931 return Ok(());
932 };
933
934 let (signing_limits, meter_config) = match verifier_limits_source {
936 VerifierLimitsSource::NodeConfig(config) => (
937 config.limits_for_signing(),
938 config.meter_config_for_signing(),
939 ),
940 VerifierLimitsSource::ProtocolConfig => (
941 protocol_config.verifier_signing_limits(),
942 protocol_config.meter_config(),
943 ),
944 };
945 let mut verifier = iota_execution::verifier(protocol_config, Some(signing_limits), metrics);
946 let mut meter = verifier.meter(meter_config);
947
948 let shared_meter_verifier_timer = metrics
950 .verifier_runtime_per_ptb_success_latency
951 .start_timer();
952
953 let verifier_status = pt
954 .non_system_packages_to_be_published()
955 .try_for_each(|module_bytes| {
956 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
957 })
958 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
959
960 match verifier_status {
961 Ok(_) => {
962 shared_meter_verifier_timer.stop_and_record();
964 }
965 Err(err) => {
966 metrics
969 .verifier_runtime_per_ptb_timeout_latency
970 .observe(shared_meter_verifier_timer.stop_and_discard());
971 return Err(err);
972 }
973 };
974
975 Ok(())
976 }
977}