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_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION,
20 IOTA_RANDOMNESS_STATE_OBJECT_ID,
21 base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber},
22 error::{IotaError, IotaResult, UserInputError, UserInputResult},
23 executable_transaction::VerifiedExecutableTransaction,
24 fp_bail, fp_ensure,
25 gas::IotaGasStatus,
26 metrics::BytecodeVerifierMetrics,
27 object::{Object, Owner},
28 transaction::{
29 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
30 ObjectReadResultKind, ReceivingObjectReadResult, ReceivingObjects, TransactionData,
31 TransactionDataAPI, TransactionKind,
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_tx() {
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 {
348 object_ref: (object_id, version, object_digest),
349 object,
350 } in receiving_objects.iter()
351 {
352 fp_ensure!(
353 *version < SequenceNumber::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_owned()
363 && object.version() == *version
364 && object.digest() == *object_digest)
365 {
366 fp_ensure!(
368 object.version() == *version,
369 UserInputError::ObjectVersionUnavailableForConsumption {
370 provided_obj_ref: (*object_id, *version, *object_digest),
371 current_version: object.version(),
372 }
373 .into()
374 );
375
376 fp_ensure!(
378 !object.is_package(),
379 UserInputError::MovePackageAsObject {
380 object_id: *object_id
381 }
382 .into()
383 );
384
385 let expected_digest = object.digest();
387 fp_ensure!(
388 expected_digest == *object_digest,
389 UserInputError::InvalidObjectDigest {
390 object_id: *object_id,
391 expected_digest
392 }
393 .into()
394 );
395
396 match object.owner {
397 Owner::AddressOwner(_) => {
398 debug_assert!(
399 false,
400 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
401 (*object_id, *version, *object_id),
402 object
403 );
404 error!(
405 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
406 (*object_id, *version, *object_id),
407 object
408 );
409 fp_bail!(
412 UserInputError::ObjectNotFound {
413 object_id: *object_id,
414 version: Some(*version),
415 }
416 .into()
417 )
418 }
419 Owner::ObjectOwner(owner) => {
420 fp_bail!(
421 UserInputError::InvalidChildObjectArgument {
422 child_id: object.id(),
423 parent_id: owner.into(),
424 }
425 .into()
426 )
427 }
428 Owner::Shared { .. } => fp_bail!(UserInputError::NotSharedObject.into()),
429 Owner::Immutable => fp_bail!(
430 UserInputError::MutableParameterExpected {
431 object_id: *object_id
432 }
433 .into()
434 ),
435 };
436 }
437
438 fp_ensure!(
439 !objects_in_txn.contains(object_id),
440 UserInputError::DuplicateObjectRefInput.into()
441 );
442
443 objects_in_txn.insert(*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: &[ObjectRef],
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.0);
506 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
507 object_id: obj_ref.0,
508 version: Some(obj_ref.1),
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: &TransactionData, objects: &InputObjects) -> UserInputResult<()> {
520 let mut used_objects: HashSet<IotaAddress> = 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.0));
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: &IotaAddress,
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.try_as_package().is_some(),
582 UserInputError::MoveObjectAsPackage {
583 object_id: package_id
584 }
585 );
586 }
587 InputObjectKind::ImmOrOwnedMoveObject((object_id, sequence_number, object_digest)) => {
588 fp_ensure!(
589 !object.is_package(),
590 UserInputError::MovePackageAsObject { object_id }
591 );
592 fp_ensure!(
593 sequence_number < SequenceNumber::MAX_VALID_EXCL,
594 UserInputError::InvalidSequenceNumber
595 );
596
597 assert_eq!(
599 object.version(),
600 sequence_number,
601 "The fetched object version {} does not match the requested version {}, object id: {}",
602 object.version(),
603 sequence_number,
604 object.id(),
605 );
606
607 let expected_digest = object.digest();
609 fp_ensure!(
610 expected_digest == object_digest,
611 UserInputError::InvalidObjectDigest {
612 object_id,
613 expected_digest
614 }
615 );
616
617 match object.owner {
618 Owner::Immutable => {
619 }
621 Owner::AddressOwner(actual_owner) => {
622 fp_ensure!(
624 owner == &actual_owner,
625 UserInputError::IncorrectUserSignature {
626 error: format!(
627 "Object {object_id:?} is owned by account address {actual_owner}, but given owner/signer address is {owner}"
628 ),
629 }
630 );
631 }
632 Owner::ObjectOwner(owner) => {
633 return Err(UserInputError::InvalidChildObjectArgument {
634 child_id: object.id(),
635 parent_id: owner.into(),
636 });
637 }
638 Owner::Shared { .. } => {
639 return Err(UserInputError::NotSharedObject);
642 }
643 };
644 }
645 InputObjectKind::SharedMoveObject {
646 id: IOTA_CLOCK_OBJECT_ID,
647 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
648 mutable: true,
649 } => {
650 if system_transaction {
653 return Ok(());
654 } else {
655 return Err(UserInputError::ImmutableParameterExpected {
656 object_id: IOTA_CLOCK_OBJECT_ID,
657 });
658 }
659 }
660 InputObjectKind::SharedMoveObject {
661 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
662 ..
663 } => {
664 if system_transaction {
665 return Ok(());
666 } else {
667 return Err(UserInputError::InaccessibleSystemObject {
668 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
669 });
670 }
671 }
672 InputObjectKind::SharedMoveObject {
673 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
674 mutable: true,
675 ..
676 } => {
677 if system_transaction {
680 return Ok(());
681 } else {
682 return Err(UserInputError::ImmutableParameterExpected {
683 object_id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
684 });
685 }
686 }
687 InputObjectKind::SharedMoveObject {
688 initial_shared_version: input_initial_shared_version,
689 ..
690 } => {
691 fp_ensure!(
692 object.version() < SequenceNumber::MAX_VALID_EXCL,
693 UserInputError::InvalidSequenceNumber
694 );
695
696 match object.owner {
697 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
698 return Err(UserInputError::NotSharedObject);
700 }
701 Owner::Shared {
702 initial_shared_version: actual_initial_shared_version,
703 } => {
704 fp_ensure!(
705 input_initial_shared_version == actual_initial_shared_version,
706 UserInputError::SharedObjectStartingVersionMismatch
707 )
708 }
709 }
710 }
711 };
712 Ok(())
713 }
714
715 #[instrument(level = "trace", skip_all)]
718 fn check_move_authenticator_objects(
719 authenticator_objects: &InputObjects,
720 ) -> UserInputResult<()> {
721 for object in authenticator_objects.iter() {
722 let input_object_kind = object.input_object_kind;
723
724 match &object.object {
725 ObjectReadResultKind::Object(object) => {
726 check_one_move_authenticator_object(input_object_kind, object)?;
727 }
728 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
730 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
733 }
734 }
735
736 Ok(())
737 }
738
739 fn check_one_move_authenticator_object(
741 object_kind: InputObjectKind,
742 object: &Object,
743 ) -> UserInputResult {
744 match object_kind {
745 InputObjectKind::MovePackage(package_id) => {
746 return Err(UserInputError::PackageIsInMoveAuthenticatorInput { package_id });
747 }
748 InputObjectKind::ImmOrOwnedMoveObject((object_id, sequence_number, object_digest)) => {
749 fp_ensure!(
750 !object.is_package(),
751 UserInputError::MovePackageAsObject { object_id }
752 );
753 fp_ensure!(
754 sequence_number < SequenceNumber::MAX_VALID_EXCL,
755 UserInputError::InvalidSequenceNumber
756 );
757
758 assert_eq!(
760 object.version(),
761 sequence_number,
762 "The fetched object version {} does not match the requested version {}, object id: {}",
763 object.version(),
764 sequence_number,
765 object.id(),
766 );
767
768 let expected_digest = object.digest();
770 fp_ensure!(
771 expected_digest == object_digest,
772 UserInputError::InvalidObjectDigest {
773 object_id,
774 expected_digest
775 }
776 );
777
778 match object.owner {
779 Owner::Immutable => {
780 }
782 Owner::AddressOwner { .. } => {
783 return Err(UserInputError::AddressOwnedIsInMoveAuthenticatorInput {
784 object_id: object.id(),
785 });
786 }
787 Owner::ObjectOwner { .. } => {
788 return Err(UserInputError::ObjectOwnedIsInMoveAuthenticatorInput {
789 object_id: object.id(),
790 });
791 }
792 Owner::Shared { .. } => {
793 return Err(UserInputError::NotSharedObject);
796 }
797 };
798 }
799 InputObjectKind::SharedMoveObject {
800 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
801 ..
802 } => {
803 return Err(UserInputError::InaccessibleSystemObject {
804 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
805 });
806 }
807 InputObjectKind::SharedMoveObject {
808 id, mutable: true, ..
809 } => {
810 return Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput {
811 object_id: id,
812 });
813 }
814 InputObjectKind::SharedMoveObject {
815 initial_shared_version: input_initial_shared_version,
816 ..
817 } => {
818 fp_ensure!(
819 object.version() < SequenceNumber::MAX_VALID_EXCL,
820 UserInputError::InvalidSequenceNumber
821 );
822
823 match object.owner {
824 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
825 return Err(UserInputError::NotSharedObject);
827 }
828 Owner::Shared {
829 initial_shared_version: actual_initial_shared_version,
830 } => {
831 fp_ensure!(
832 input_initial_shared_version == actual_initial_shared_version,
833 UserInputError::SharedObjectStartingVersionMismatch
834 )
835 }
836 }
837 }
838 };
839 Ok(())
840 }
841
842 fn checked_input_objects_union(
849 base_set: CheckedInputObjects,
850 other_set: &CheckedInputObjects,
851 ) -> IotaResult<CheckedInputObjects> {
852 let mut base_set = base_set.into_inner();
853 for other_object in other_set.inner().iter() {
854 if let Some(base_object) = base_set.find_object_id_mut(other_object.id()) {
855 assert_eq!(
857 base_object.object, other_object.object,
858 "The object read result for input objects with the same id must be equal"
859 );
860
861 if let ObjectReadResultKind::Object(_) = &other_object.object {
864 base_object
865 .input_object_kind
866 .left_union_with_checks(&other_object.input_object_kind)?;
867 }
868 } else {
869 base_set.push(other_object.clone());
870 }
871 }
872 Ok(base_set.into_checked())
873 }
874
875 #[instrument(level = "trace", skip_all)]
877 pub fn check_non_system_packages_to_be_published(
878 transaction: &TransactionData,
879 protocol_config: &ProtocolConfig,
880 metrics: &Arc<BytecodeVerifierMetrics>,
881 verifier_signing_config: &VerifierSigningConfig,
882 ) -> UserInputResult<()> {
883 if transaction.is_system_tx() {
885 return Ok(());
886 }
887
888 let TransactionKind::ProgrammableTransaction(pt) = transaction.kind() else {
889 return Ok(());
890 };
891
892 let signing_limits = Some(verifier_signing_config.limits_for_signing());
895 let mut verifier = iota_execution::verifier(protocol_config, signing_limits, metrics);
896 let mut meter = verifier.meter(verifier_signing_config.meter_config_for_signing());
897
898 let shared_meter_verifier_timer = metrics
900 .verifier_runtime_per_ptb_success_latency
901 .start_timer();
902
903 let verifier_status = pt
904 .non_system_packages_to_be_published()
905 .try_for_each(|module_bytes| {
906 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
907 })
908 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
909
910 match verifier_status {
911 Ok(_) => {
912 shared_meter_verifier_timer.stop_and_record();
914 }
915 Err(err) => {
916 metrics
919 .verifier_runtime_per_ptb_timeout_latency
920 .observe(shared_meter_verifier_timer.stop_and_discard());
921 return Err(err);
922 }
923 };
924
925 Ok(())
926 }
927}