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 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
77 pub fn check_transaction_input(
78 protocol_config: &ProtocolConfig,
79 reference_gas_price: u64,
80 transaction: &Transaction,
81 input_objects: InputObjects,
82 receiving_objects: &ReceivingObjects,
83 metrics: &Arc<BytecodeVerifierMetrics>,
84 verifier_signing_config: &VerifierSigningConfig,
85 authentication_gas_budget: u64,
86 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
87 let gas_status = check_transaction_input_inner(
88 protocol_config,
89 reference_gas_price,
90 transaction,
91 &input_objects,
92 &[],
93 authentication_gas_budget,
94 false,
95 )?;
96 check_receiving_objects(&input_objects, receiving_objects)?;
97 check_non_system_packages_to_be_published(
99 transaction,
100 protocol_config,
101 metrics,
102 verifier_signing_config,
103 )?;
104
105 Ok((gas_status, input_objects.into_checked()))
106 }
107
108 #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
109 pub fn check_transaction_input_with_given_gas(
110 protocol_config: &ProtocolConfig,
111 reference_gas_price: u64,
112 transaction: &Transaction,
113 mut input_objects: InputObjects,
114 receiving_objects: ReceivingObjects,
115 gas_object: Object,
116 metrics: &Arc<BytecodeVerifierMetrics>,
117 verifier_signing_config: &VerifierSigningConfig,
118 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
119 let gas_object_ref = gas_object.object_ref();
120 input_objects.push(ObjectReadResult::new_from_gas_object(&gas_object));
121
122 let gas_status = check_transaction_input_inner(
123 protocol_config,
124 reference_gas_price,
125 transaction,
126 &input_objects,
127 &[gas_object_ref],
128 0,
129 true,
130 )?;
131 check_receiving_objects(&input_objects, &receiving_objects)?;
132 check_non_system_packages_to_be_published(
134 transaction,
135 protocol_config,
136 metrics,
137 verifier_signing_config,
138 )?;
139
140 Ok((gas_status, input_objects.into_checked()))
141 }
142
143 #[instrument(level = "trace", skip_all)]
149 pub fn check_certificate_input(
150 cert: &VerifiedExecutableTransaction,
151 input_objects: InputObjects,
152 protocol_config: &ProtocolConfig,
153 reference_gas_price: u64,
154 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
155 let transaction = cert.data().transaction();
156 let gas_status = check_transaction_input_inner(
157 protocol_config,
158 reference_gas_price,
159 transaction,
160 &input_objects,
161 &[],
162 0,
163 true,
164 )?;
165 Ok((gas_status, input_objects.into_checked()))
170 }
171
172 #[instrument(level = "trace", skip_all)]
178 pub fn check_simulation_input(
179 config: &ProtocolConfig,
180 kind: &TransactionKind,
181 input_objects: InputObjects,
182 _receiving_objects: ReceivingObjects,
184 ) -> IotaResult<CheckedInputObjects> {
185 kind.validity_check(config)?;
186 if kind.is_system() {
187 return Err(UserInputError::Unsupported(format!(
188 "Transaction kind {kind} is not supported in a simulation"
189 ))
190 .into());
191 }
192 let mut used_objects: HashSet<Address> = HashSet::new();
193 for input_object in input_objects.iter() {
194 let Some(object) = input_object.as_object() else {
195 continue;
197 };
198
199 if !object.is_immutable() {
200 fp_ensure!(
201 used_objects.insert(object.id().into()),
202 UserInputError::MutableObjectUsedMoreThanOnce {
203 object_id: object.id()
204 }
205 .into()
206 );
207 }
208 }
209
210 Ok(input_objects.into_checked())
211 }
212
213 #[instrument(level = "trace", skip_all)]
219 pub fn check_move_authenticator_input_for_validation(
220 authenticator_input_objects: InputObjects,
221 ) -> IotaResult<CheckedInputObjects> {
222 check_move_authenticator_objects(&authenticator_input_objects)?;
223
224 Ok(authenticator_input_objects.into_checked())
225 }
226
227 pub fn aggregate_authenticator_input_objects(
231 per_authenticator_checked_input_objects: &[&CheckedInputObjects],
232 ) -> IotaResult<CheckedInputObjects> {
233 let mut aggregated_authenticator_input_objects =
234 CheckedInputObjects::new_with_checked_transaction_inputs(InputObjects::new(vec![]));
235
236 for authenticator_checked_input_objects in per_authenticator_checked_input_objects.iter() {
237 aggregated_authenticator_input_objects = checked_input_objects_union(
238 aggregated_authenticator_input_objects,
239 authenticator_checked_input_objects,
240 )?;
241 }
242
243 Ok(aggregated_authenticator_input_objects)
244 }
245
246 #[instrument(level = "trace", skip_all)]
258 pub fn check_certificate_and_move_authenticator_input(
259 cert: &VerifiedExecutableTransaction,
260 tx_input_objects: InputObjects,
261 per_authenticator_input_objects: Vec<InputObjects>,
262 authenticator_gas_budget: u64,
263 protocol_config: &ProtocolConfig,
264 reference_gas_price: u64,
265 ) -> IotaResult<(IotaGasStatus, Vec<CheckedInputObjects>, CheckedInputObjects)> {
266 per_authenticator_input_objects
268 .iter()
269 .try_for_each(check_move_authenticator_objects)?;
270
271 let transaction = cert.data().transaction();
273 let gas_status = check_transaction_input_inner(
274 protocol_config,
275 reference_gas_price,
276 transaction,
277 &tx_input_objects,
278 &[],
279 authenticator_gas_budget,
280 true,
281 )?;
282
283 let per_authenticator_checked_input_objects = per_authenticator_input_objects
284 .into_iter()
285 .map(|objects| objects.into_checked())
286 .collect::<Vec<_>>();
287
288 let mut input_objects_union = tx_input_objects.into_checked();
290 for objects in per_authenticator_checked_input_objects.iter() {
291 input_objects_union = checked_input_objects_union(input_objects_union, objects)?;
292 }
293
294 Ok((
295 gas_status,
296 per_authenticator_checked_input_objects,
297 input_objects_union,
298 ))
299 }
300
301 fn check_transaction_input_inner(
303 protocol_config: &ProtocolConfig,
304 reference_gas_price: u64,
305 transaction: &Transaction,
306 input_objects: &InputObjects,
307 gas_override: &[ObjectReference],
309 authentication_gas_budget: u64,
310 is_execute_transaction_to_effects: bool,
311 ) -> IotaResult<IotaGasStatus> {
312 let gas = if gas_override.is_empty() {
314 transaction.gas()
315 } else {
316 gas_override
317 };
318
319 let gas_status = get_gas_status(
320 input_objects,
321 gas,
322 protocol_config,
323 reference_gas_price,
324 transaction,
325 authentication_gas_budget,
326 is_execute_transaction_to_effects,
327 )?;
328 check_objects(transaction, input_objects)?;
329
330 Ok(gas_status)
331 }
332
333 #[instrument(level = "trace", skip_all)]
334 fn check_receiving_objects(
335 input_objects: &InputObjects,
336 receiving_objects: &ReceivingObjects,
337 ) -> Result<(), IotaError> {
338 let mut objects_in_txn: HashSet<_> = input_objects
339 .object_kinds()
340 .map(|x| x.object_id())
341 .collect();
342
343 for ReceivingObjectReadResult { object_ref, object } in receiving_objects.iter() {
352 fp_ensure!(
353 object_ref.version < Version::MAX_VALID_EXCL,
354 UserInputError::InvalidSequenceNumber.into()
355 );
356
357 let Some(object) = object.as_object() else {
358 continue;
360 };
361
362 if !(object.owner.is_address()
363 && object.version() == object_ref.version
364 && object.digest() == object_ref.digest)
365 {
366 fp_ensure!(
368 object.version() == object_ref.version,
369 UserInputError::ObjectVersionUnavailableForConsumption {
370 provided_obj_ref: *object_ref,
371 current_version: object.version(),
372 }
373 .into()
374 );
375
376 fp_ensure!(
378 !object.is_package(),
379 UserInputError::MovePackageAsObject {
380 object_id: object_ref.object_id
381 }
382 .into()
383 );
384
385 let expected_digest = object.digest();
387 fp_ensure!(
388 expected_digest == object_ref.digest,
389 UserInputError::InvalidObjectDigest {
390 object_id: object_ref.object_id,
391 expected_digest
392 }
393 .into()
394 );
395
396 match object.owner {
397 Owner::Address(_) => {
398 debug_assert!(
399 false,
400 "Receiving object {object_ref:?} is invalid but we expect it should be valid. {object:?}"
401 );
402 error!(
403 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
404 object_ref, object
405 );
406 fp_bail!(
409 UserInputError::ObjectNotFound {
410 object_id: object_ref.object_id,
411 version: Some(object_ref.version),
412 }
413 .into()
414 )
415 }
416 Owner::Object(owner) => {
417 fp_bail!(
418 UserInputError::InvalidChildObjectArgument {
419 child_id: object.id(),
420 parent_id: owner,
421 }
422 .into()
423 )
424 }
425 Owner::Shared(_) => fp_bail!(UserInputError::NotSharedObject.into()),
426 Owner::Immutable => fp_bail!(
427 UserInputError::MutableParameterExpected {
428 object_id: object_ref.object_id
429 }
430 .into()
431 ),
432 _ => {
433 unimplemented!("a new Owner enum variant was added and needs to be handled")
434 }
435 };
436 }
437
438 fp_ensure!(
439 !objects_in_txn.contains(&object_ref.object_id),
440 UserInputError::DuplicateObjectRefInput.into()
441 );
442
443 objects_in_txn.insert(object_ref.object_id);
444 }
445 Ok(())
446 }
447
448 #[instrument(level = "trace", skip_all)]
451 fn check_gas(
452 objects: &InputObjects,
453 protocol_config: &ProtocolConfig,
454 reference_gas_price: u64,
455 gas: &[ObjectReference],
456 gas_price: u64,
457 transaction_gas_budget: u64,
458 authentication_gas_budget: u64,
459 is_execute_transaction_to_effects: bool,
460 ) -> IotaResult<IotaGasStatus> {
461 let gas_budget_to_set = if authentication_gas_budget > 0 {
462 let protocol_max_auth_gas =
465 protocol_config.max_auth_gas_as_option().ok_or_else(|| {
466 UserInputError::Unsupported(
467 "Transaction requires authentication gas but max_auth_gas is not enabled"
468 .to_string(),
469 )
470 })?;
471
472 if is_execute_transaction_to_effects {
479 transaction_gas_budget
480 } else {
481 authentication_gas_budget.min(protocol_max_auth_gas)
482 }
483 } else {
484 transaction_gas_budget
487 };
488
489 let gas_budget_to_check = transaction_gas_budget;
492
493 let gas_status = IotaGasStatus::new(
494 gas_budget_to_set,
495 gas_price,
496 reference_gas_price,
497 protocol_config,
498 )?;
499
500 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
503 let mut gas_objects = vec![];
504 for obj_ref in gas {
505 let obj = objects.get(&obj_ref.object_id);
506 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
507 object_id: obj_ref.object_id,
508 version: Some(obj_ref.version),
509 })?;
510 gas_objects.push(obj);
511 }
512 gas_status.check_gas_balance(&gas_objects, gas_budget_to_check)?;
513 Ok(gas_status)
514 }
515
516 #[instrument(level = "trace", skip_all)]
519 fn check_objects(transaction: &Transaction, objects: &InputObjects) -> UserInputResult<()> {
520 let mut used_objects: HashSet<Address> = HashSet::new();
522 for object in objects.iter() {
523 if object.is_mutable() {
524 fp_ensure!(
525 used_objects.insert(object.id().into()),
526 UserInputError::MutableObjectUsedMoreThanOnce {
527 object_id: object.id()
528 }
529 );
530 }
531 }
532
533 if !transaction.is_genesis_tx() && objects.is_empty() {
534 return Err(UserInputError::ObjectInputArityViolation);
535 }
536
537 let gas_coins: HashSet<ObjectId> =
538 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.object_id));
539 for object in objects.iter() {
540 let input_object_kind = object.input_object_kind;
541
542 match &object.object {
543 ObjectReadResultKind::Object(object) => {
544 let owner_address = if gas_coins.contains(&object.id()) {
546 transaction.gas_owner()
547 } else {
548 transaction.sender()
549 };
550 let system_transaction = transaction.is_system_tx();
553 check_one_object(
554 &owner_address,
555 input_object_kind,
556 object,
557 system_transaction,
558 )?;
559 }
560 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
562 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
565 }
566 }
567
568 Ok(())
569 }
570
571 fn check_one_object(
573 owner: &Address,
574 object_kind: InputObjectKind,
575 object: &Object,
576 system_transaction: bool,
577 ) -> UserInputResult {
578 match object_kind {
579 InputObjectKind::MovePackage(package_id) => {
580 fp_ensure!(
581 object.data.as_opt_package().is_some(),
582 UserInputError::MoveObjectAsPackage {
583 object_id: package_id
584 }
585 );
586 }
587 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
588 fp_ensure!(
589 !object.is_package(),
590 UserInputError::MovePackageAsObject {
591 object_id: object_ref.object_id
592 }
593 );
594 fp_ensure!(
595 object_ref.version < Version::MAX_VALID_EXCL,
596 UserInputError::InvalidSequenceNumber
597 );
598
599 assert_eq!(
601 object.version(),
602 object_ref.version,
603 "The fetched object version {} does not match the requested version {}, object id: {}",
604 object.version(),
605 object_ref.version,
606 object.id(),
607 );
608
609 let expected_digest = object.digest();
611 fp_ensure!(
612 expected_digest == object_ref.digest,
613 UserInputError::InvalidObjectDigest {
614 object_id: object_ref.object_id,
615 expected_digest
616 }
617 );
618
619 match object.owner {
620 Owner::Immutable => {
621 }
623 Owner::Address(actual_owner) => {
624 fp_ensure!(
626 owner == &actual_owner,
627 UserInputError::IncorrectUserSignature {
628 error: format!(
629 "Object {} is owned by account address {}, but given owner/signer address is {}",
630 object_ref.object_id, actual_owner, owner
631 ),
632 }
633 );
634 }
635 Owner::Object(owner) => {
636 return Err(UserInputError::InvalidChildObjectArgument {
637 child_id: object.id(),
638 parent_id: owner,
639 });
640 }
641 Owner::Shared(_) => {
642 return Err(UserInputError::NotSharedObject);
645 }
646 _ => {
647 unimplemented!("a new Owner enum variant was added and needs to be handled")
648 }
649 };
650 }
651 InputObjectKind::SharedMoveObject {
652 id: ObjectId::CLOCK,
653 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
654 mutable: true,
655 } => {
656 if system_transaction {
659 return Ok(());
660 } else {
661 return Err(UserInputError::ImmutableParameterExpected {
662 object_id: ObjectId::CLOCK,
663 });
664 }
665 }
666 InputObjectKind::SharedMoveObject {
667 id: ObjectId::AUTHENTICATOR_STATE,
668 ..
669 } => {
670 if system_transaction {
671 return Ok(());
672 } else {
673 return Err(UserInputError::InaccessibleSystemObject {
674 object_id: ObjectId::AUTHENTICATOR_STATE,
675 });
676 }
677 }
678 InputObjectKind::SharedMoveObject {
679 id: ObjectId::RANDOMNESS_STATE,
680 mutable: true,
681 ..
682 } => {
683 if system_transaction {
686 return Ok(());
687 } else {
688 return Err(UserInputError::ImmutableParameterExpected {
689 object_id: ObjectId::RANDOMNESS_STATE,
690 });
691 }
692 }
693 InputObjectKind::SharedMoveObject {
694 initial_shared_version: input_initial_shared_version,
695 ..
696 } => {
697 fp_ensure!(
698 object.version() < Version::MAX_VALID_EXCL,
699 UserInputError::InvalidSequenceNumber
700 );
701
702 match object.owner {
703 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
704 return Err(UserInputError::NotSharedObject);
706 }
707 Owner::Shared(actual_initial_shared_version) => {
708 fp_ensure!(
709 input_initial_shared_version == actual_initial_shared_version,
710 UserInputError::SharedObjectStartingVersionMismatch
711 )
712 }
713 _ => {
714 unimplemented!("a new Owner enum variant was added and needs to be handled")
715 }
716 }
717 }
718 };
719 Ok(())
720 }
721
722 #[instrument(level = "trace", skip_all)]
725 fn check_move_authenticator_objects(
726 authenticator_objects: &InputObjects,
727 ) -> UserInputResult<()> {
728 for object in authenticator_objects.iter() {
729 let input_object_kind = object.input_object_kind;
730
731 match &object.object {
732 ObjectReadResultKind::Object(object) => {
733 check_one_move_authenticator_object(input_object_kind, object)?;
734 }
735 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
737 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
740 }
741 }
742
743 Ok(())
744 }
745
746 fn check_one_move_authenticator_object(
748 object_kind: InputObjectKind,
749 object: &Object,
750 ) -> UserInputResult {
751 match object_kind {
752 InputObjectKind::MovePackage(package_id) => {
753 return Err(UserInputError::PackageIsInMoveAuthenticatorInput { package_id });
754 }
755 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
756 fp_ensure!(
757 !object.is_package(),
758 UserInputError::MovePackageAsObject {
759 object_id: object_ref.object_id
760 }
761 );
762 fp_ensure!(
763 object_ref.version < Version::MAX_VALID_EXCL,
764 UserInputError::InvalidSequenceNumber
765 );
766
767 assert_eq!(
769 object.version(),
770 object_ref.version,
771 "The fetched object version {} does not match the requested version {}, object id: {}",
772 object.version(),
773 object_ref.version,
774 object.id(),
775 );
776
777 let expected_digest = object.digest();
779 fp_ensure!(
780 expected_digest == object_ref.digest,
781 UserInputError::InvalidObjectDigest {
782 object_id: object_ref.object_id,
783 expected_digest
784 }
785 );
786
787 match object.owner {
788 Owner::Immutable => {
789 }
791 Owner::Address(_) => {
792 return Err(UserInputError::AddressOwnedIsInMoveAuthenticatorInput {
793 object_id: object.id(),
794 });
795 }
796 Owner::Object(_) => {
797 return Err(UserInputError::ObjectOwnedIsInMoveAuthenticatorInput {
798 object_id: object.id(),
799 });
800 }
801 Owner::Shared(_) => {
802 return Err(UserInputError::NotSharedObject);
805 }
806 _ => {
807 unimplemented!("a new Owner enum variant was added and needs to be handled")
808 }
809 };
810 }
811 InputObjectKind::SharedMoveObject {
812 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
813 ..
814 } => {
815 return Err(UserInputError::InaccessibleSystemObject {
816 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
817 });
818 }
819 InputObjectKind::SharedMoveObject {
820 id, mutable: true, ..
821 } => {
822 return Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput {
823 object_id: id,
824 });
825 }
826 InputObjectKind::SharedMoveObject {
827 initial_shared_version: input_initial_shared_version,
828 ..
829 } => {
830 fp_ensure!(
831 object.version() < Version::MAX_VALID_EXCL,
832 UserInputError::InvalidSequenceNumber
833 );
834
835 match object.owner {
836 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
837 return Err(UserInputError::NotSharedObject);
839 }
840 Owner::Shared(actual_initial_shared_version) => {
841 fp_ensure!(
842 input_initial_shared_version == actual_initial_shared_version,
843 UserInputError::SharedObjectStartingVersionMismatch
844 )
845 }
846 _ => {
847 unimplemented!("a new Owner enum variant was added and needs to be handled")
848 }
849 }
850 }
851 };
852 Ok(())
853 }
854
855 pub fn checked_input_objects_union(
862 base_set: CheckedInputObjects,
863 other_set: &CheckedInputObjects,
864 ) -> IotaResult<CheckedInputObjects> {
865 let mut base_set = base_set.into_inner();
866 for other_object in other_set.inner().iter() {
867 if let Some(base_object) = base_set.find_object_id_mut(other_object.id()) {
868 assert_eq!(
870 base_object.object, other_object.object,
871 "The object read result for input objects with the same id must be equal"
872 );
873
874 if let ObjectReadResultKind::Object(_) = &other_object.object {
877 base_object
878 .input_object_kind
879 .left_union_with_checks(&other_object.input_object_kind)?;
880 }
881 } else {
882 base_set.push(other_object.clone());
883 }
884 }
885 Ok(base_set.into_checked())
886 }
887
888 #[instrument(level = "trace", skip_all)]
890 pub fn check_non_system_packages_to_be_published(
891 transaction: &Transaction,
892 protocol_config: &ProtocolConfig,
893 metrics: &Arc<BytecodeVerifierMetrics>,
894 verifier_signing_config: &VerifierSigningConfig,
895 ) -> UserInputResult<()> {
896 if transaction.is_system_tx() {
898 return Ok(());
899 }
900
901 let TransactionKind::Programmable(pt) = transaction.kind() else {
902 return Ok(());
903 };
904
905 let signing_limits = Some(verifier_signing_config.limits_for_signing());
908 let mut verifier = iota_execution::verifier(protocol_config, signing_limits, metrics);
909 let mut meter = verifier.meter(verifier_signing_config.meter_config_for_signing());
910
911 let shared_meter_verifier_timer = metrics
913 .verifier_runtime_per_ptb_success_latency
914 .start_timer();
915
916 let verifier_status = pt
917 .non_system_packages_to_be_published()
918 .try_for_each(|module_bytes| {
919 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
920 })
921 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
922
923 match verifier_status {
924 Ok(_) => {
925 shared_meter_verifier_timer.stop_and_record();
927 }
928 Err(err) => {
929 metrics
932 .verifier_runtime_per_ptb_timeout_latency
933 .observe(shared_meter_verifier_timer.stop_and_discard());
934 return Err(err);
935 }
936 };
937
938 Ok(())
939 }
940}