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