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_types::{
19 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION,
20 base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber},
21 error::{IotaError, IotaResult, UserInputError, UserInputResult},
22 executable_transaction::VerifiedExecutableTransaction,
23 fp_bail, fp_ensure,
24 gas::IotaGasStatus,
25 metrics::BytecodeVerifierMetrics,
26 object::{Object, Owner},
27 transaction::{
28 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
29 ObjectReadResultKind, ProgrammableTransactionExt, ReceivingObjectReadResult,
30 ReceivingObjects, TransactionData, TransactionDataAPI, TransactionKind,
31 TransactionKindExt,
32 },
33 };
34 use tracing::{error, instrument};
35
36 trait IntoChecked {
37 fn into_checked(self) -> CheckedInputObjects;
38 }
39
40 impl IntoChecked for InputObjects {
41 fn into_checked(self) -> CheckedInputObjects {
42 CheckedInputObjects::new_with_checked_transaction_inputs(self)
43 }
44 }
45
46 fn get_gas_status(
51 objects: &InputObjects,
52 gas: &[ObjectRef],
53 protocol_config: &ProtocolConfig,
54 reference_gas_price: u64,
55 transaction: &TransactionData,
56 authentication_gas_budget: u64,
57 is_execute_transaction_to_effects: bool,
58 ) -> IotaResult<IotaGasStatus> {
59 if transaction.is_system_tx() {
60 Ok(IotaGasStatus::new_unmetered())
61 } else {
62 check_gas(
63 objects,
64 protocol_config,
65 reference_gas_price,
66 gas,
67 transaction.gas_price(),
68 transaction.gas_budget(),
69 authentication_gas_budget,
70 is_execute_transaction_to_effects,
71 )
72 }
73 }
74
75 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
76 pub fn check_transaction_input(
77 protocol_config: &ProtocolConfig,
78 reference_gas_price: u64,
79 transaction: &TransactionData,
80 input_objects: InputObjects,
81 receiving_objects: &ReceivingObjects,
82 metrics: &Arc<BytecodeVerifierMetrics>,
83 verifier_signing_config: &VerifierSigningConfig,
84 authentication_gas_budget: u64,
85 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
86 let gas_status = check_transaction_input_inner(
87 protocol_config,
88 reference_gas_price,
89 transaction,
90 &input_objects,
91 &[],
92 authentication_gas_budget,
93 false,
94 )?;
95 check_receiving_objects(&input_objects, receiving_objects)?;
96 check_non_system_packages_to_be_published(
98 transaction,
99 protocol_config,
100 metrics,
101 verifier_signing_config,
102 )?;
103
104 Ok((gas_status, input_objects.into_checked()))
105 }
106
107 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
108 pub fn check_transaction_input_with_given_gas(
109 protocol_config: &ProtocolConfig,
110 reference_gas_price: u64,
111 transaction: &TransactionData,
112 mut input_objects: InputObjects,
113 receiving_objects: ReceivingObjects,
114 gas_object: Object,
115 metrics: &Arc<BytecodeVerifierMetrics>,
116 verifier_signing_config: &VerifierSigningConfig,
117 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
118 let gas_object_ref = gas_object.compute_object_reference();
119 input_objects.push(ObjectReadResult::new_from_gas_object(&gas_object));
120
121 let gas_status = check_transaction_input_inner(
122 protocol_config,
123 reference_gas_price,
124 transaction,
125 &input_objects,
126 &[gas_object_ref],
127 0,
128 true,
129 )?;
130 check_receiving_objects(&input_objects, &receiving_objects)?;
131 check_non_system_packages_to_be_published(
133 transaction,
134 protocol_config,
135 metrics,
136 verifier_signing_config,
137 )?;
138
139 Ok((gas_status, input_objects.into_checked()))
140 }
141
142 #[instrument(level = "trace", skip_all)]
148 pub fn check_certificate_input(
149 cert: &VerifiedExecutableTransaction,
150 input_objects: InputObjects,
151 protocol_config: &ProtocolConfig,
152 reference_gas_price: u64,
153 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
154 let transaction = cert.data().transaction_data();
155 let gas_status = check_transaction_input_inner(
156 protocol_config,
157 reference_gas_price,
158 transaction,
159 &input_objects,
160 &[],
161 0,
162 true,
163 )?;
164 Ok((gas_status, input_objects.into_checked()))
169 }
170
171 #[instrument(level = "trace", skip_all)]
174 pub fn check_dev_inspect_input(
175 config: &ProtocolConfig,
176 kind: &TransactionKind,
177 input_objects: InputObjects,
178 _receiving_objects: ReceivingObjects,
180 ) -> IotaResult<CheckedInputObjects> {
181 kind.validity_check(config)?;
182 if kind.is_system() {
183 return Err(UserInputError::Unsupported(format!(
184 "Transaction kind {kind} is not supported in dev-inspect"
185 ))
186 .into());
187 }
188 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
189 for input_object in input_objects.iter() {
190 let Some(object) = input_object.as_object() else {
191 continue;
193 };
194
195 if !object.is_immutable() {
196 fp_ensure!(
197 used_objects.insert(object.id().into()),
198 UserInputError::MutableObjectUsedMoreThanOnce {
199 object_id: object.id()
200 }
201 .into()
202 );
203 }
204 }
205
206 Ok(input_objects.into_checked())
207 }
208
209 #[instrument(level = "trace", skip_all)]
215 pub fn check_move_authenticator_input_for_signing(
216 authenticator_input_objects: InputObjects,
217 ) -> IotaResult<CheckedInputObjects> {
218 check_move_authenticator_objects(&authenticator_input_objects)?;
219
220 Ok(authenticator_input_objects.into_checked())
221 }
222
223 pub fn aggregate_authenticator_input_objects(
227 per_authenticator_checked_input_objects: &[&CheckedInputObjects],
228 ) -> IotaResult<CheckedInputObjects> {
229 let mut aggregated_authenticator_input_objects =
230 CheckedInputObjects::new_with_checked_transaction_inputs(InputObjects::new(vec![]));
231
232 for authenticator_checked_input_objects in per_authenticator_checked_input_objects.iter() {
233 aggregated_authenticator_input_objects = checked_input_objects_union(
234 aggregated_authenticator_input_objects,
235 authenticator_checked_input_objects,
236 )?;
237 }
238
239 Ok(aggregated_authenticator_input_objects)
240 }
241
242 #[instrument(level = "trace", skip_all)]
254 pub fn check_certificate_and_move_authenticator_input(
255 cert: &VerifiedExecutableTransaction,
256 tx_input_objects: InputObjects,
257 per_authenticator_input_objects: Vec<InputObjects>,
258 authenticator_gas_budget: u64,
259 protocol_config: &ProtocolConfig,
260 reference_gas_price: u64,
261 ) -> IotaResult<(IotaGasStatus, Vec<CheckedInputObjects>, CheckedInputObjects)> {
262 per_authenticator_input_objects
264 .iter()
265 .try_for_each(check_move_authenticator_objects)?;
266
267 let transaction = cert.data().transaction_data();
269 let gas_status = check_transaction_input_inner(
270 protocol_config,
271 reference_gas_price,
272 transaction,
273 &tx_input_objects,
274 &[],
275 authenticator_gas_budget,
276 true,
277 )?;
278
279 let per_authenticator_checked_input_objects = per_authenticator_input_objects
280 .into_iter()
281 .map(|objects| objects.into_checked())
282 .collect::<Vec<_>>();
283
284 let mut input_objects_union = tx_input_objects.into_checked();
286 for objects in per_authenticator_checked_input_objects.iter() {
287 input_objects_union = checked_input_objects_union(input_objects_union, objects)?;
288 }
289
290 Ok((
291 gas_status,
292 per_authenticator_checked_input_objects,
293 input_objects_union,
294 ))
295 }
296
297 fn check_transaction_input_inner(
299 protocol_config: &ProtocolConfig,
300 reference_gas_price: u64,
301 transaction: &TransactionData,
302 input_objects: &InputObjects,
303 gas_override: &[ObjectRef],
305 authentication_gas_budget: u64,
306 is_execute_transaction_to_effects: bool,
307 ) -> IotaResult<IotaGasStatus> {
308 let gas = if gas_override.is_empty() {
310 transaction.gas()
311 } else {
312 gas_override
313 };
314
315 let gas_status = get_gas_status(
316 input_objects,
317 gas,
318 protocol_config,
319 reference_gas_price,
320 transaction,
321 authentication_gas_budget,
322 is_execute_transaction_to_effects,
323 )?;
324 check_objects(transaction, input_objects)?;
325
326 Ok(gas_status)
327 }
328
329 #[instrument(level = "trace", skip_all)]
330 fn check_receiving_objects(
331 input_objects: &InputObjects,
332 receiving_objects: &ReceivingObjects,
333 ) -> Result<(), IotaError> {
334 let mut objects_in_txn: HashSet<_> = input_objects
335 .object_kinds()
336 .map(|x| x.object_id())
337 .collect();
338
339 for ReceivingObjectReadResult { object_ref, object } in receiving_objects.iter() {
348 fp_ensure!(
349 object_ref.version < SequenceNumber::MAX_VALID_EXCL,
350 UserInputError::InvalidSequenceNumber.into()
351 );
352
353 let Some(object) = object.as_object() else {
354 continue;
356 };
357
358 if !(object.owner.is_address()
359 && object.version() == object_ref.version
360 && object.digest() == object_ref.digest)
361 {
362 fp_ensure!(
364 object.version() == object_ref.version,
365 UserInputError::ObjectVersionUnavailableForConsumption {
366 provided_obj_ref: *object_ref,
367 current_version: object.version(),
368 }
369 .into()
370 );
371
372 fp_ensure!(
374 !object.is_package(),
375 UserInputError::MovePackageAsObject {
376 object_id: object_ref.object_id
377 }
378 .into()
379 );
380
381 let expected_digest = object.digest();
383 fp_ensure!(
384 expected_digest == object_ref.digest,
385 UserInputError::InvalidObjectDigest {
386 object_id: object_ref.object_id,
387 expected_digest
388 }
389 .into()
390 );
391
392 match object.owner {
393 Owner::Address(_) => {
394 debug_assert!(
395 false,
396 "Receiving object {object_ref:?} is invalid but we expect it should be valid. {object:?}"
397 );
398 error!(
399 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
400 object_ref, object
401 );
402 fp_bail!(
405 UserInputError::ObjectNotFound {
406 object_id: object_ref.object_id,
407 version: Some(object_ref.version),
408 }
409 .into()
410 )
411 }
412 Owner::Object(owner) => {
413 fp_bail!(
414 UserInputError::InvalidChildObjectArgument {
415 child_id: object.id(),
416 parent_id: owner,
417 }
418 .into()
419 )
420 }
421 Owner::Shared(_) => fp_bail!(UserInputError::NotSharedObject.into()),
422 Owner::Immutable => fp_bail!(
423 UserInputError::MutableParameterExpected {
424 object_id: object_ref.object_id
425 }
426 .into()
427 ),
428 _ => {
429 unimplemented!("a new Owner enum variant was added and needs to be handled")
430 }
431 };
432 }
433
434 fp_ensure!(
435 !objects_in_txn.contains(&object_ref.object_id),
436 UserInputError::DuplicateObjectRefInput.into()
437 );
438
439 objects_in_txn.insert(object_ref.object_id);
440 }
441 Ok(())
442 }
443
444 #[instrument(level = "trace", skip_all)]
447 fn check_gas(
448 objects: &InputObjects,
449 protocol_config: &ProtocolConfig,
450 reference_gas_price: u64,
451 gas: &[ObjectRef],
452 gas_price: u64,
453 transaction_gas_budget: u64,
454 authentication_gas_budget: u64,
455 is_execute_transaction_to_effects: bool,
456 ) -> IotaResult<IotaGasStatus> {
457 let gas_budget_to_set = if authentication_gas_budget > 0 {
458 let protocol_max_auth_gas =
461 protocol_config.max_auth_gas_as_option().ok_or_else(|| {
462 UserInputError::Unsupported(
463 "Transaction requires authentication gas but max_auth_gas is not enabled"
464 .to_string(),
465 )
466 })?;
467
468 if is_execute_transaction_to_effects {
475 transaction_gas_budget
476 } else {
477 authentication_gas_budget.min(protocol_max_auth_gas)
478 }
479 } else {
480 transaction_gas_budget
483 };
484
485 let gas_budget_to_check = transaction_gas_budget;
488
489 let gas_status = IotaGasStatus::new(
490 gas_budget_to_set,
491 gas_price,
492 reference_gas_price,
493 protocol_config,
494 )?;
495
496 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
499 let mut gas_objects = vec![];
500 for obj_ref in gas {
501 let obj = objects.get(&obj_ref.object_id);
502 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
503 object_id: obj_ref.object_id,
504 version: Some(obj_ref.version),
505 })?;
506 gas_objects.push(obj);
507 }
508 gas_status.check_gas_balance(&gas_objects, gas_budget_to_check)?;
509 Ok(gas_status)
510 }
511
512 #[instrument(level = "trace", skip_all)]
515 fn check_objects(transaction: &TransactionData, objects: &InputObjects) -> UserInputResult<()> {
516 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
518 for object in objects.iter() {
519 if object.is_mutable() {
520 fp_ensure!(
521 used_objects.insert(object.id().into()),
522 UserInputError::MutableObjectUsedMoreThanOnce {
523 object_id: object.id()
524 }
525 );
526 }
527 }
528
529 if !transaction.is_genesis_tx() && objects.is_empty() {
530 return Err(UserInputError::ObjectInputArityViolation);
531 }
532
533 let gas_coins: HashSet<ObjectID> =
534 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.object_id));
535 for object in objects.iter() {
536 let input_object_kind = object.input_object_kind;
537
538 match &object.object {
539 ObjectReadResultKind::Object(object) => {
540 let owner_address = if gas_coins.contains(&object.id()) {
542 transaction.gas_owner()
543 } else {
544 transaction.sender()
545 };
546 let system_transaction = transaction.is_system_tx();
549 check_one_object(
550 &owner_address,
551 input_object_kind,
552 object,
553 system_transaction,
554 )?;
555 }
556 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
558 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
561 }
562 }
563
564 Ok(())
565 }
566
567 fn check_one_object(
569 owner: &IotaAddress,
570 object_kind: InputObjectKind,
571 object: &Object,
572 system_transaction: bool,
573 ) -> UserInputResult {
574 match object_kind {
575 InputObjectKind::MovePackage(package_id) => {
576 fp_ensure!(
577 object.data.as_package_opt().is_some(),
578 UserInputError::MoveObjectAsPackage {
579 object_id: package_id
580 }
581 );
582 }
583 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
584 fp_ensure!(
585 !object.is_package(),
586 UserInputError::MovePackageAsObject {
587 object_id: object_ref.object_id
588 }
589 );
590 fp_ensure!(
591 object_ref.version < SequenceNumber::MAX_VALID_EXCL,
592 UserInputError::InvalidSequenceNumber
593 );
594
595 assert_eq!(
597 object.version(),
598 object_ref.version,
599 "The fetched object version {} does not match the requested version {}, object id: {}",
600 object.version(),
601 object_ref.version,
602 object.id(),
603 );
604
605 let expected_digest = object.digest();
607 fp_ensure!(
608 expected_digest == object_ref.digest,
609 UserInputError::InvalidObjectDigest {
610 object_id: object_ref.object_id,
611 expected_digest
612 }
613 );
614
615 match object.owner {
616 Owner::Immutable => {
617 }
619 Owner::Address(actual_owner) => {
620 fp_ensure!(
622 owner == &actual_owner,
623 UserInputError::IncorrectUserSignature {
624 error: format!(
625 "Object {} is owned by account address {}, but given owner/signer address is {}",
626 object_ref.object_id, actual_owner, owner
627 ),
628 }
629 );
630 }
631 Owner::Object(owner) => {
632 return Err(UserInputError::InvalidChildObjectArgument {
633 child_id: object.id(),
634 parent_id: owner,
635 });
636 }
637 Owner::Shared(_) => {
638 return Err(UserInputError::NotSharedObject);
641 }
642 _ => {
643 unimplemented!("a new Owner enum variant was added and needs to be handled")
644 }
645 };
646 }
647 InputObjectKind::SharedMoveObject {
648 id: ObjectID::CLOCK,
649 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
650 mutable: true,
651 } => {
652 if system_transaction {
655 return Ok(());
656 } else {
657 return Err(UserInputError::ImmutableParameterExpected {
658 object_id: ObjectID::CLOCK,
659 });
660 }
661 }
662 InputObjectKind::SharedMoveObject {
663 id: ObjectID::AUTHENTICATOR_STATE,
664 ..
665 } => {
666 if system_transaction {
667 return Ok(());
668 } else {
669 return Err(UserInputError::InaccessibleSystemObject {
670 object_id: ObjectID::AUTHENTICATOR_STATE,
671 });
672 }
673 }
674 InputObjectKind::SharedMoveObject {
675 id: ObjectID::RANDOMNESS_STATE,
676 mutable: true,
677 ..
678 } => {
679 if system_transaction {
682 return Ok(());
683 } else {
684 return Err(UserInputError::ImmutableParameterExpected {
685 object_id: ObjectID::RANDOMNESS_STATE,
686 });
687 }
688 }
689 InputObjectKind::SharedMoveObject {
690 initial_shared_version: input_initial_shared_version,
691 ..
692 } => {
693 fp_ensure!(
694 object.version() < SequenceNumber::MAX_VALID_EXCL,
695 UserInputError::InvalidSequenceNumber
696 );
697
698 match object.owner {
699 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
700 return Err(UserInputError::NotSharedObject);
702 }
703 Owner::Shared(actual_initial_shared_version) => {
704 fp_ensure!(
705 input_initial_shared_version == actual_initial_shared_version,
706 UserInputError::SharedObjectStartingVersionMismatch
707 )
708 }
709 _ => {
710 unimplemented!("a new Owner enum variant was added and needs to be handled")
711 }
712 }
713 }
714 };
715 Ok(())
716 }
717
718 #[instrument(level = "trace", skip_all)]
721 fn check_move_authenticator_objects(
722 authenticator_objects: &InputObjects,
723 ) -> UserInputResult<()> {
724 for object in authenticator_objects.iter() {
725 let input_object_kind = object.input_object_kind;
726
727 match &object.object {
728 ObjectReadResultKind::Object(object) => {
729 check_one_move_authenticator_object(input_object_kind, object)?;
730 }
731 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
733 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
736 }
737 }
738
739 Ok(())
740 }
741
742 fn check_one_move_authenticator_object(
744 object_kind: InputObjectKind,
745 object: &Object,
746 ) -> UserInputResult {
747 match object_kind {
748 InputObjectKind::MovePackage(package_id) => {
749 return Err(UserInputError::PackageIsInMoveAuthenticatorInput { package_id });
750 }
751 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
752 fp_ensure!(
753 !object.is_package(),
754 UserInputError::MovePackageAsObject {
755 object_id: object_ref.object_id
756 }
757 );
758 fp_ensure!(
759 object_ref.version < SequenceNumber::MAX_VALID_EXCL,
760 UserInputError::InvalidSequenceNumber
761 );
762
763 assert_eq!(
765 object.version(),
766 object_ref.version,
767 "The fetched object version {} does not match the requested version {}, object id: {}",
768 object.version(),
769 object_ref.version,
770 object.id(),
771 );
772
773 let expected_digest = object.digest();
775 fp_ensure!(
776 expected_digest == object_ref.digest,
777 UserInputError::InvalidObjectDigest {
778 object_id: object_ref.object_id,
779 expected_digest
780 }
781 );
782
783 match object.owner {
784 Owner::Immutable => {
785 }
787 Owner::Address(_) => {
788 return Err(UserInputError::AddressOwnedIsInMoveAuthenticatorInput {
789 object_id: object.id(),
790 });
791 }
792 Owner::Object(_) => {
793 return Err(UserInputError::ObjectOwnedIsInMoveAuthenticatorInput {
794 object_id: object.id(),
795 });
796 }
797 Owner::Shared(_) => {
798 return Err(UserInputError::NotSharedObject);
801 }
802 _ => {
803 unimplemented!("a new Owner enum variant was added and needs to be handled")
804 }
805 };
806 }
807 InputObjectKind::SharedMoveObject {
808 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
809 ..
810 } => {
811 return Err(UserInputError::InaccessibleSystemObject {
812 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
813 });
814 }
815 InputObjectKind::SharedMoveObject {
816 id, mutable: true, ..
817 } => {
818 return Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput {
819 object_id: id,
820 });
821 }
822 InputObjectKind::SharedMoveObject {
823 initial_shared_version: input_initial_shared_version,
824 ..
825 } => {
826 fp_ensure!(
827 object.version() < SequenceNumber::MAX_VALID_EXCL,
828 UserInputError::InvalidSequenceNumber
829 );
830
831 match object.owner {
832 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
833 return Err(UserInputError::NotSharedObject);
835 }
836 Owner::Shared(actual_initial_shared_version) => {
837 fp_ensure!(
838 input_initial_shared_version == actual_initial_shared_version,
839 UserInputError::SharedObjectStartingVersionMismatch
840 )
841 }
842 _ => {
843 unimplemented!("a new Owner enum variant was added and needs to be handled")
844 }
845 }
846 }
847 };
848 Ok(())
849 }
850
851 fn checked_input_objects_union(
858 base_set: CheckedInputObjects,
859 other_set: &CheckedInputObjects,
860 ) -> IotaResult<CheckedInputObjects> {
861 let mut base_set = base_set.into_inner();
862 for other_object in other_set.inner().iter() {
863 if let Some(base_object) = base_set.find_object_id_mut(other_object.id()) {
864 assert_eq!(
866 base_object.object, other_object.object,
867 "The object read result for input objects with the same id must be equal"
868 );
869
870 if let ObjectReadResultKind::Object(_) = &other_object.object {
873 base_object
874 .input_object_kind
875 .left_union_with_checks(&other_object.input_object_kind)?;
876 }
877 } else {
878 base_set.push(other_object.clone());
879 }
880 }
881 Ok(base_set.into_checked())
882 }
883
884 #[instrument(level = "trace", skip_all)]
886 pub fn check_non_system_packages_to_be_published(
887 transaction: &TransactionData,
888 protocol_config: &ProtocolConfig,
889 metrics: &Arc<BytecodeVerifierMetrics>,
890 verifier_signing_config: &VerifierSigningConfig,
891 ) -> UserInputResult<()> {
892 if transaction.is_system_tx() {
894 return Ok(());
895 }
896
897 let TransactionKind::Programmable(pt) = transaction.kind() else {
898 return Ok(());
899 };
900
901 let signing_limits = Some(verifier_signing_config.limits_for_signing());
904 let mut verifier = iota_execution::verifier(protocol_config, signing_limits, metrics);
905 let mut meter = verifier.meter(verifier_signing_config.meter_config_for_signing());
906
907 let shared_meter_verifier_timer = metrics
909 .verifier_runtime_per_ptb_success_latency
910 .start_timer();
911
912 let verifier_status = pt
913 .non_system_packages_to_be_published()
914 .try_for_each(|module_bytes| {
915 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
916 })
917 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
918
919 match verifier_status {
920 Ok(_) => {
921 shared_meter_verifier_timer.stop_and_record();
923 }
924 Err(err) => {
925 metrics
928 .verifier_runtime_per_ptb_timeout_latency
929 .observe(shared_meter_verifier_timer.stop_and_discard());
930 return Err(err);
931 }
932 };
933
934 Ok(())
935 }
936}