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_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 fn input_object_ids(input_objects: &InputObjects) -> HashSet<ObjectId> {
349 input_objects
350 .object_kinds()
351 .map(|x| x.object_id())
352 .collect()
353 }
354
355 fn check_receiving_version_bound(object_ref: &ObjectReference) -> Result<(), IotaError> {
356 fp_ensure!(
357 object_ref.version < Version::MAX_VALID_EXCL,
358 UserInputError::InvalidSequenceNumber.into()
359 );
360 Ok(())
361 }
362
363 fn check_not_already_in_transaction(
364 objects_in_txn: &mut HashSet<ObjectId>,
365 object_ref: &ObjectReference,
366 ) -> Result<(), IotaError> {
367 fp_ensure!(
368 objects_in_txn.insert(object_ref.object_id),
369 UserInputError::DuplicateObjectRefInput.into()
370 );
371 Ok(())
372 }
373
374 #[instrument(level = "trace", skip_all)]
375 fn check_receiving_objects(
376 input_objects: &InputObjects,
377 receiving_objects: &ReceivingObjects,
378 ) -> Result<(), IotaError> {
379 let mut objects_in_txn = input_object_ids(input_objects);
380
381 for ReceivingObjectReadResult { object_ref, object } in receiving_objects.iter() {
390 check_receiving_version_bound(object_ref)?;
391
392 let Some(object) = object.as_object() else {
393 continue;
395 };
396
397 if !(object.owner.is_address()
398 && object.version() == object_ref.version
399 && object.digest() == object_ref.digest)
400 {
401 fp_ensure!(
403 object.version() == object_ref.version,
404 UserInputError::ObjectVersionUnavailableForConsumption {
405 provided_obj_ref: *object_ref,
406 current_version: object.version(),
407 }
408 .into()
409 );
410
411 fp_ensure!(
413 !object.is_package(),
414 UserInputError::MovePackageAsObject {
415 object_id: object_ref.object_id
416 }
417 .into()
418 );
419
420 let expected_digest = object.digest();
422 fp_ensure!(
423 expected_digest == object_ref.digest,
424 UserInputError::InvalidObjectDigest {
425 object_id: object_ref.object_id,
426 expected_digest
427 }
428 .into()
429 );
430
431 match object.owner {
432 Owner::Address(_) => {
433 debug_assert!(
434 false,
435 "Receiving object {object_ref:?} is invalid but we expect it should be valid. {object:?}"
436 );
437 error!(
438 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
439 object_ref, object
440 );
441 fp_bail!(
444 UserInputError::ObjectNotFound {
445 object_id: object_ref.object_id,
446 version: Some(object_ref.version),
447 }
448 .into()
449 )
450 }
451 Owner::Object(owner) => {
452 fp_bail!(
453 UserInputError::InvalidChildObjectArgument {
454 child_id: object.id(),
455 parent_id: owner,
456 }
457 .into()
458 )
459 }
460 Owner::Shared(_) => fp_bail!(UserInputError::NotSharedObject.into()),
461 Owner::Immutable => fp_bail!(
462 UserInputError::MutableParameterExpected {
463 object_id: object_ref.object_id
464 }
465 .into()
466 ),
467 _ => {
468 unimplemented!("a new Owner enum variant was added and needs to be handled")
469 }
470 };
471 }
472
473 check_not_already_in_transaction(&mut objects_in_txn, object_ref)?;
474 }
475 Ok(())
476 }
477
478 #[instrument(level = "trace", skip_all)]
481 fn check_gas(
482 objects: &InputObjects,
483 protocol_config: &ProtocolConfig,
484 reference_gas_price: u64,
485 gas: &[ObjectReference],
486 gas_price: u64,
487 transaction_gas_budget: u64,
488 authentication_gas_budget: u64,
489 is_execute_transaction_to_effects: bool,
490 ) -> IotaResult<IotaGasStatus> {
491 let gas_budget_to_set = if authentication_gas_budget > 0 {
492 let protocol_max_auth_gas =
495 protocol_config.max_auth_gas_as_option().ok_or_else(|| {
496 UserInputError::Unsupported(
497 "Transaction requires authentication gas but max_auth_gas is not enabled"
498 .to_string(),
499 )
500 })?;
501
502 if is_execute_transaction_to_effects {
509 transaction_gas_budget
510 } else {
511 authentication_gas_budget.min(protocol_max_auth_gas)
512 }
513 } else {
514 transaction_gas_budget
517 };
518
519 let gas_budget_to_check = transaction_gas_budget;
522
523 let gas_status = IotaGasStatus::new(
524 gas_budget_to_set,
525 gas_price,
526 reference_gas_price,
527 protocol_config,
528 )?;
529
530 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
533 let mut gas_objects = vec![];
534 for obj_ref in gas {
535 let obj = objects.get(&obj_ref.object_id);
536 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
537 object_id: obj_ref.object_id,
538 version: Some(obj_ref.version),
539 })?;
540 gas_objects.push(obj);
541 }
542 gas_status.check_gas_balance(&gas_objects, gas_budget_to_check)?;
543 Ok(gas_status)
544 }
545
546 #[instrument(level = "trace", skip_all)]
549 fn check_objects(transaction: &Transaction, objects: &InputObjects) -> UserInputResult<()> {
550 let mut used_objects: HashSet<Address> = HashSet::new();
552 for object in objects.iter() {
553 if object.is_mutable() {
554 fp_ensure!(
555 used_objects.insert(object.id().into()),
556 UserInputError::MutableObjectUsedMoreThanOnce {
557 object_id: object.id()
558 }
559 );
560 }
561 }
562
563 if !transaction.is_genesis_tx() && objects.is_empty() {
564 return Err(UserInputError::ObjectInputArityViolation);
565 }
566
567 let gas_coins: HashSet<ObjectId> =
568 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.object_id));
569 for object in objects.iter() {
570 let input_object_kind = object.input_object_kind;
571
572 match &object.object {
573 ObjectReadResultKind::Object(object) => {
574 let owner_address = if gas_coins.contains(&object.id()) {
576 transaction.gas_owner()
577 } else {
578 transaction.sender()
579 };
580 let system_transaction = transaction.is_system_tx();
583 check_one_object(
584 &owner_address,
585 input_object_kind,
586 object,
587 system_transaction,
588 )?;
589 }
590 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
592 ObjectReadResultKind::CancelledTransactionObject(_) => (),
595 ObjectReadResultKind::UnreadSharedObject => {
598 check_shared_object_declaration(
599 &input_object_kind,
600 transaction.is_system_tx(),
601 )?;
602 }
603 }
604 }
605
606 Ok(())
607 }
608
609 fn check_one_object(
611 owner: &Address,
612 object_kind: InputObjectKind,
613 object: &Object,
614 system_transaction: bool,
615 ) -> UserInputResult {
616 match object_kind {
617 InputObjectKind::MovePackage(package_id) => {
618 fp_ensure!(
619 object.data.as_opt_package().is_some(),
620 UserInputError::MoveObjectAsPackage {
621 object_id: package_id
622 }
623 );
624 }
625 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
626 fp_ensure!(
627 !object.is_package(),
628 UserInputError::MovePackageAsObject {
629 object_id: object_ref.object_id
630 }
631 );
632 fp_ensure!(
633 object_ref.version < Version::MAX_VALID_EXCL,
634 UserInputError::InvalidSequenceNumber
635 );
636
637 assert_eq!(
639 object.version(),
640 object_ref.version,
641 "The fetched object version {} does not match the requested version {}, object id: {}",
642 object.version(),
643 object_ref.version,
644 object.id(),
645 );
646
647 let expected_digest = object.digest();
649 fp_ensure!(
650 expected_digest == object_ref.digest,
651 UserInputError::InvalidObjectDigest {
652 object_id: object_ref.object_id,
653 expected_digest
654 }
655 );
656
657 match object.owner {
658 Owner::Immutable => {
659 }
661 Owner::Address(actual_owner) => {
662 fp_ensure!(
664 owner == &actual_owner,
665 UserInputError::IncorrectUserSignature {
666 error: format!(
667 "Object {} is owned by account address {}, but given owner/signer address is {}",
668 object_ref.object_id, actual_owner, owner
669 ),
670 }
671 );
672 }
673 Owner::Object(owner) => {
674 return Err(UserInputError::InvalidChildObjectArgument {
675 child_id: object.id(),
676 parent_id: owner,
677 });
678 }
679 Owner::Shared(_) => {
680 return Err(UserInputError::NotSharedObject);
683 }
684 _ => {
685 unimplemented!("a new Owner enum variant was added and needs to be handled")
686 }
687 };
688 }
689 InputObjectKind::SharedMoveObject {
690 initial_shared_version: input_initial_shared_version,
691 ..
692 } => {
693 if check_shared_object_declaration(&object_kind, system_transaction)? {
694 return Ok(());
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
720 Ok(())
721 }
722
723 fn check_shared_object_declaration(
730 object_kind: &InputObjectKind,
731 system_transaction: bool,
732 ) -> UserInputResult<bool> {
733 match object_kind {
734 InputObjectKind::SharedMoveObject {
735 id: ObjectId::CLOCK,
736 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
737 mutable: true,
738 } => {
739 if system_transaction {
742 Ok(true)
743 } else {
744 Err(UserInputError::ImmutableParameterExpected {
745 object_id: ObjectId::CLOCK,
746 })
747 }
748 }
749 InputObjectKind::SharedMoveObject {
750 id: ObjectId::AUTHENTICATOR_STATE,
751 ..
752 } => {
753 if system_transaction {
754 Ok(true)
755 } else {
756 Err(UserInputError::InaccessibleSystemObject {
757 object_id: ObjectId::AUTHENTICATOR_STATE,
758 })
759 }
760 }
761 InputObjectKind::SharedMoveObject {
762 id: ObjectId::RANDOMNESS_STATE,
763 mutable: true,
764 ..
765 } => {
766 if system_transaction {
769 Ok(true)
770 } else {
771 Err(UserInputError::ImmutableParameterExpected {
772 object_id: ObjectId::RANDOMNESS_STATE,
773 })
774 }
775 }
776 InputObjectKind::SharedMoveObject {
777 id: ObjectId::TRANSACTION_DENY_RULES,
778 ..
779 } => {
780 if system_transaction {
783 Ok(true)
784 } else {
785 Err(UserInputError::InaccessibleSystemObject {
786 object_id: ObjectId::TRANSACTION_DENY_RULES,
787 })
788 }
789 }
790 _ => Ok(false),
791 }
792 }
793
794 #[instrument(level = "trace", skip_all)]
798 fn check_move_authenticator_objects(
799 authenticator_objects: &InputObjects,
800 ) -> UserInputResult<()> {
801 for object in authenticator_objects.iter() {
802 let input_object_kind = object.input_object_kind;
803
804 match &object.object {
805 ObjectReadResultKind::Object(object) => {
806 check_one_move_authenticator_object(input_object_kind, object)?;
807 }
808 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
810 ObjectReadResultKind::CancelledTransactionObject(_) => (),
813 ObjectReadResultKind::UnreadSharedObject => {
816 check_move_authenticator_shared_object_declaration(&input_object_kind)?;
817 }
818 }
819 }
820
821 Ok(())
822 }
823
824 fn check_one_move_authenticator_object(
826 object_kind: InputObjectKind,
827 object: &Object,
828 ) -> UserInputResult {
829 match object_kind {
830 InputObjectKind::MovePackage(package_id) => {
831 fp_ensure!(
832 object.data.as_opt_package().is_some(),
833 UserInputError::MoveObjectAsPackage {
834 object_id: package_id
835 }
836 );
837 }
838 InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
839 fp_ensure!(
840 !object.is_package(),
841 UserInputError::MovePackageAsObject {
842 object_id: object_ref.object_id
843 }
844 );
845 fp_ensure!(
846 object_ref.version < Version::MAX_VALID_EXCL,
847 UserInputError::InvalidSequenceNumber
848 );
849
850 assert_eq!(
852 object.version(),
853 object_ref.version,
854 "The fetched object version {} does not match the requested version {}, object id: {}",
855 object.version(),
856 object_ref.version,
857 object.id(),
858 );
859
860 let expected_digest = object.digest();
862 fp_ensure!(
863 expected_digest == object_ref.digest,
864 UserInputError::InvalidObjectDigest {
865 object_id: object_ref.object_id,
866 expected_digest
867 }
868 );
869
870 match object.owner {
871 Owner::Immutable => {
872 }
874 Owner::Address(_) => {
875 return Err(UserInputError::AddressOwnedIsInMoveAuthenticatorInput {
876 object_id: object.id(),
877 });
878 }
879 Owner::Object(_) => {
880 return Err(UserInputError::ObjectOwnedIsInMoveAuthenticatorInput {
881 object_id: object.id(),
882 });
883 }
884 Owner::Shared(_) => {
885 return Err(UserInputError::NotSharedObject);
888 }
889 _ => {
890 unimplemented!("a new Owner enum variant was added and needs to be handled")
891 }
892 };
893 }
894 InputObjectKind::SharedMoveObject {
895 id: ObjectId::AUTHENTICATOR_STATE,
896 ..
897 } => {
898 return Err(UserInputError::InaccessibleSystemObject {
899 object_id: ObjectId::AUTHENTICATOR_STATE,
900 });
901 }
902 InputObjectKind::SharedMoveObject {
903 id, mutable: true, ..
904 } => {
905 return Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput {
906 object_id: id,
907 });
908 }
909 InputObjectKind::SharedMoveObject {
910 initial_shared_version: input_initial_shared_version,
911 ..
912 } => {
913 fp_ensure!(
914 object.version() < Version::MAX_VALID_EXCL,
915 UserInputError::InvalidSequenceNumber
916 );
917
918 match object.owner {
919 Owner::Address(_) | Owner::Object(_) | Owner::Immutable => {
920 return Err(UserInputError::NotSharedObject);
922 }
923 Owner::Shared(actual_initial_shared_version) => {
924 fp_ensure!(
925 input_initial_shared_version == actual_initial_shared_version,
926 UserInputError::SharedObjectStartingVersionMismatch
927 )
928 }
929 _ => {
930 unimplemented!("a new Owner enum variant was added and needs to be handled")
931 }
932 }
933 }
934 };
935 Ok(())
936 }
937
938 fn check_move_authenticator_shared_object_declaration(
942 object_kind: &InputObjectKind,
943 ) -> UserInputResult {
944 match object_kind {
945 InputObjectKind::SharedMoveObject {
946 id: ObjectId::AUTHENTICATOR_STATE,
947 ..
948 } => Err(UserInputError::InaccessibleSystemObject {
949 object_id: ObjectId::AUTHENTICATOR_STATE,
950 }),
951 InputObjectKind::SharedMoveObject {
952 id, mutable: true, ..
953 } => Err(UserInputError::MutableSharedIsInMoveAuthenticatorInput { object_id: *id }),
954 _ => Ok(()),
955 }
956 }
957
958 pub fn checked_input_objects_union(
965 base_set: CheckedInputObjects,
966 other_set: &CheckedInputObjects,
967 ) -> IotaResult<CheckedInputObjects> {
968 let mut base_set = base_set.into_inner();
969 for other_object in other_set.inner().iter() {
970 if let Some(base_object) = base_set.find_object_id_mut(other_object.id()) {
971 assert_eq!(
973 base_object.object, other_object.object,
974 "The object read result for input objects with the same id must be equal"
975 );
976
977 if let ObjectReadResultKind::Object(_) = &other_object.object {
980 base_object
981 .input_object_kind
982 .left_union_with_checks(&other_object.input_object_kind)?;
983 }
984 } else {
985 base_set.push(other_object.clone());
986 }
987 }
988 Ok(base_set.into_checked())
989 }
990
991 #[instrument(level = "trace", skip_all)]
993 pub fn check_non_system_packages_to_be_published(
994 transaction: &Transaction,
995 protocol_config: &ProtocolConfig,
996 metrics: &Arc<BytecodeVerifierMetrics>,
997 verifier_limits_source: VerifierLimitsSource<'_>,
998 ) -> UserInputResult<()> {
999 if transaction.is_system_tx() {
1001 return Ok(());
1002 }
1003
1004 let TransactionKind::Programmable(pt) = transaction.kind() else {
1005 return Ok(());
1006 };
1007
1008 let (verifier_config, meter_config) = match verifier_limits_source {
1010 VerifierLimitsSource::NodeConfig(config) => (
1011 protocol_config.verifier_config(Some(config.limits_for_signing())),
1012 config.meter_config_for_signing(),
1013 ),
1014 VerifierLimitsSource::ProtocolConfig => (
1015 protocol_config.post_consensus_verifier_config(),
1016 protocol_config.meter_config(),
1017 ),
1018 };
1019 let mut verifier = iota_execution::verifier(protocol_config, verifier_config, metrics);
1020 let mut meter = verifier.meter(meter_config);
1021
1022 let shared_meter_verifier_timer = metrics
1024 .verifier_runtime_per_ptb_success_latency
1025 .start_timer();
1026
1027 let verifier_status = pt
1028 .non_system_packages_to_be_published()
1029 .try_for_each(|module_bytes| {
1030 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
1031 })
1032 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
1033
1034 match verifier_status {
1035 Ok(_) => {
1036 shared_meter_verifier_timer.stop_and_record();
1038 }
1039 Err(err) => {
1040 metrics
1043 .verifier_runtime_per_ptb_timeout_latency
1044 .observe(shared_meter_verifier_timer.stop_and_discard());
1045 return Err(err);
1046 }
1047 };
1048
1049 Ok(())
1050 }
1051
1052 #[cfg(test)]
1053 mod tests {
1054 use iota_sdk_types::{MovePackage, TransactionDigest};
1055
1056 use super::*;
1057
1058 fn package(id: ObjectId) -> Object {
1059 Object::new_from_package(
1060 MovePackage::new(
1061 id,
1062 Version::OBJECT_START,
1063 BTreeMap::new(),
1064 vec![],
1065 BTreeMap::new(),
1066 ),
1067 TransactionDigest::genesis_marker(),
1068 )
1069 }
1070
1071 fn read_as_package(id: ObjectId, object: Object) -> InputObjects {
1073 InputObjects::new(vec![ObjectReadResult::new(
1074 InputObjectKind::MovePackage(id),
1075 object.into(),
1076 )])
1077 }
1078
1079 #[test]
1080 fn authenticator_check_accepts_its_type_arg_package() {
1081 let id = ObjectId::random();
1082 check_move_authenticator_objects(&read_as_package(id, package(id))).unwrap();
1083 }
1084
1085 #[test]
1086 fn authenticator_check_rejects_an_object_named_as_a_type_arg_package() {
1087 let id = ObjectId::random();
1088 assert_eq!(
1089 check_move_authenticator_objects(&read_as_package(
1090 id,
1091 Object::immutable_with_id_for_testing(id)
1092 ))
1093 .unwrap_err(),
1094 UserInputError::MoveObjectAsPackage { object_id: id }
1095 );
1096 }
1097 }
1098}