1use std::{
38 collections::{BTreeMap, BTreeSet},
39 hash::Hash,
40};
41
42use derive_more::Display;
43use iota_protocol_config::ProtocolConfig;
44use iota_sdk_move_types::iota_framework::vec_map::{Entry, VecMap};
45use iota_sdk_types::{
46 Identifier, MovePackage, ObjectId, PackageUpgradeError, StructTag, TypeOrigin, TypeTag,
47 UpgradeInfo, Version,
48};
49use move_binary_format::{
50 binary_config::BinaryConfig,
51 file_format::CompiledModule,
52 file_format_common::{IOTA_METADATA_KEY, VERSION_6},
53 normalized,
54};
55use move_core_types::identifier::IdentStr;
56use serde::{Deserialize, Serialize};
57use serde_with::{Bytes, serde_as};
58
59use crate::{
60 Address,
61 error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
62 id::{ID, UID},
63 iota_sdk_types_conversions::identifier_core_to_sdk,
64 iota_serde::TypeName,
65};
66
67pub const PACKAGE_METADATA_MODULE_NAME: Identifier = Identifier::from_static("package_metadata");
68pub const PACKAGE_METADATA_V1_STRUCT_NAME: Identifier =
69 Identifier::from_static("PackageMetadataV1");
70pub const PACKAGE_METADATA_KEY_STRUCT_NAME: Identifier =
71 Identifier::from_static("PackageMetadataKey");
72
73#[derive(Clone, Debug)]
74pub struct FnInfo {
76 pub is_test: bool,
79 pub authenticator_version: Option<u8>,
82 pub is_view: bool,
83}
84
85#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
86pub struct FnInfoKey {
88 pub fn_name: String,
89 pub mod_name: String,
90 pub mod_addr: Address,
91}
92
93pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
95
96#[repr(u8)]
101#[derive(Display, Debug, Clone, Copy)]
102pub enum UpgradePolicy {
103 #[display("COMPATIBLE")]
104 Compatible = 0,
105 #[display("ADDITIVE")]
106 Additive = 128,
107 #[display("DEP_ONLY")]
108 DepOnly = 192,
109}
110
111impl UpgradePolicy {
112 pub const COMPATIBLE: u8 = Self::Compatible as u8;
114 pub const ADDITIVE: u8 = Self::Additive as u8;
115 pub const DEP_ONLY: u8 = Self::DepOnly as u8;
116
117 pub fn is_valid_policy(policy: &u8) -> bool {
118 Self::try_from(*policy).is_ok()
119 }
120}
121
122impl TryFrom<u8> for UpgradePolicy {
123 type Error = ();
124 fn try_from(value: u8) -> Result<Self, Self::Error> {
125 match value {
126 x if x == Self::Compatible as u8 => Ok(Self::Compatible),
127 x if x == Self::Additive as u8 => Ok(Self::Additive),
128 x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
129 _ => Err(()),
130 }
131 }
132}
133
134#[derive(Debug, Serialize, Deserialize)]
136pub struct UpgradeCap {
137 pub id: UID,
138 pub package: ID,
139 pub version: u64,
140 pub policy: u8,
141}
142
143#[derive(Debug, Serialize, Deserialize)]
145pub struct UpgradeTicket {
146 pub cap: ID,
147 pub package: ID,
148 pub policy: u8,
149 pub digest: Vec<u8>,
150}
151
152#[derive(Debug, Serialize, Deserialize)]
154pub struct UpgradeReceipt {
155 pub cap: ID,
156 pub package: ID,
157}
158
159mod move_package_ext {
160 pub trait Sealed {}
161 impl Sealed for super::MovePackage {}
162}
163
164pub trait MovePackageExt: Sized + move_package_ext::Sealed {
165 fn new_initial<'p>(
166 modules: &[CompiledModule],
167 protocol_config: &ProtocolConfig,
168 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
169 ) -> Result<MovePackage, ExecutionError>;
170
171 fn new_upgraded<'p>(
172 &self,
173 storage_id: ObjectId,
174 modules: &[CompiledModule],
175 protocol_config: &ProtocolConfig,
176 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
177 ) -> Result<MovePackage, ExecutionError>;
178
179 fn new_system(
180 version: Version,
181 modules: &[CompiledModule],
182 dependencies: impl IntoIterator<Item = ObjectId>,
183 ) -> MovePackage;
184
185 fn from_module_iter_with_type_origin_table<'p>(
186 storage_id: ObjectId,
187 self_id: ObjectId,
188 version: Version,
189 modules: &[CompiledModule],
190 protocol_config: &ProtocolConfig,
191 type_origin_table: Vec<TypeOrigin>,
192 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
193 ) -> Result<MovePackage, ExecutionError>;
194
195 fn original_package_id(&self) -> ObjectId;
196
197 fn deserialize_module(
198 &self,
199 module: &Identifier,
200 binary_config: &BinaryConfig,
201 ) -> IotaResult<CompiledModule>;
202
203 fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
204 &self,
205 pool: &mut Pool,
206 binary_config: &BinaryConfig,
207 include_code: bool,
208 ) -> IotaResult<BTreeMap<String, normalized::Module<S>>>;
209}
210
211impl MovePackageExt for MovePackage {
212 fn new_initial<'p>(
220 modules: &[CompiledModule],
221 protocol_config: &ProtocolConfig,
222 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
223 ) -> Result<MovePackage, ExecutionError> {
224 let module = modules
225 .first()
226 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
227 let runtime_id = ObjectId::new(module.address().into_bytes());
228 let storage_id = runtime_id;
229 let type_origin_table = build_initial_type_origin_table(modules);
230
231 MovePackage::from_module_iter_with_type_origin_table(
232 storage_id,
233 runtime_id,
234 Version::OBJECT_START,
235 modules,
236 protocol_config,
237 type_origin_table,
238 transitive_dependencies,
239 )
240 }
241
242 fn new_upgraded<'p>(
250 &self,
251 storage_id: ObjectId,
252 modules: &[CompiledModule],
253 protocol_config: &ProtocolConfig,
254 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
255 ) -> Result<MovePackage, ExecutionError> {
256 let module = modules
257 .first()
258 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
259 let runtime_id = ObjectId::new(module.address().into_bytes());
260 let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
261 let mut new_version = self.version();
262 new_version.increment().unwrap();
263
264 MovePackage::from_module_iter_with_type_origin_table(
265 storage_id,
266 runtime_id,
267 new_version,
268 modules,
269 protocol_config,
270 type_origin_table,
271 transitive_dependencies,
272 )
273 }
274
275 fn new_system(
276 version: Version,
277 modules: &[CompiledModule],
278 dependencies: impl IntoIterator<Item = ObjectId>,
279 ) -> MovePackage {
280 let module = modules
281 .first()
282 .expect("Tried to build a Move package from an empty iterator of Compiled modules");
283
284 let storage_id = ObjectId::new(module.address().into_bytes());
285 let type_origin_table = build_initial_type_origin_table(modules);
286
287 let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
288 let info = UpgradeInfo {
289 upgraded_id: dep,
290 upgraded_version: Version::default(),
302 };
303 (dep, info)
304 }));
305
306 let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
307 let name = identifier_core_to_sdk(module.name());
308 let mut bytes = Vec::new();
309 module
310 .serialize_with_version(module.version, &mut bytes)
311 .unwrap();
312 (name, bytes)
313 }));
314
315 MovePackage::new(
319 storage_id,
320 version,
321 module_map,
322 type_origin_table,
323 linkage_table,
324 )
325 }
326
327 fn from_module_iter_with_type_origin_table<'p>(
328 storage_id: ObjectId,
329 self_id: ObjectId,
330 version: Version,
331 modules: &[CompiledModule],
332 protocol_config: &ProtocolConfig,
333 type_origin_table: Vec<TypeOrigin>,
334 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
335 ) -> Result<MovePackage, ExecutionError> {
336 let mut module_map = BTreeMap::new();
337 let mut immediate_dependencies = BTreeSet::new();
338
339 for module in modules {
340 let name = identifier_core_to_sdk(module.name());
341
342 immediate_dependencies.extend(
343 module
344 .immediate_dependencies()
345 .into_iter()
346 .map(|dep| ObjectId::new(dep.address().into_bytes())),
347 );
348
349 let mut bytes = Vec::new();
350 let version = if protocol_config.move_binary_format_version() > VERSION_6 {
351 module.version
352 } else {
353 VERSION_6
354 };
355 module.serialize_with_version(version, &mut bytes).unwrap();
356 module_map.insert(name, bytes);
357 }
358
359 immediate_dependencies.remove(&self_id);
360 let linkage_table = build_linkage_table(
361 immediate_dependencies,
362 transitive_dependencies,
363 protocol_config,
364 )?;
365
366 let package = MovePackage::new(
367 storage_id,
368 version,
369 module_map,
370 type_origin_table,
371 linkage_table,
372 );
373 package.check_size(max_package_size(storage_id, protocol_config))?;
381
382 Ok(package)
383 }
384
385 fn original_package_id(&self) -> ObjectId {
393 if self.version == Version::OBJECT_START {
394 return self.id;
396 }
397
398 let bytes = self.modules.values().next().expect("Empty module map");
399 let module = CompiledModule::deserialize_with_defaults(bytes)
403 .expect("A Move package contains a module that cannot be deserialized");
404 ObjectId::new(module.address().into_bytes())
405 }
406
407 fn deserialize_module(
408 &self,
409 module: &Identifier,
410 binary_config: &BinaryConfig,
411 ) -> IotaResult<CompiledModule> {
412 let bytes =
414 self.serialized_module_map()
415 .get(module)
416 .ok_or_else(|| IotaError::ModuleNotFound {
417 module_name: module.to_string(),
418 })?;
419
420 CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
421 IotaError::ModuleDeserializationFailure {
422 error: error.to_string(),
423 }
424 })
425 }
426
427 fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
430 &self,
431 pool: &mut Pool,
432 binary_config: &BinaryConfig,
433 include_code: bool,
434 ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
435 normalize_modules(pool, self.modules.values(), binary_config, include_code)
436 }
437}
438
439impl UpgradeCap {
440 pub fn new(uid: ObjectId, package_id: ObjectId) -> Self {
443 UpgradeCap {
444 id: UID::new(uid),
445 package: ID::new(package_id),
446 version: 1,
447 policy: UpgradePolicy::COMPATIBLE,
448 }
449 }
450}
451
452impl UpgradeReceipt {
453 pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectId) -> Self {
456 UpgradeReceipt {
457 cap: upgrade_ticket.cap,
458 package: ID::new(upgraded_package_id),
459 }
460 }
461}
462
463pub fn is_test_fun(name: &str, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
465 let mod_handle = module.self_handle();
466 let mod_addr = Address::new(
467 module
468 .address_identifier_at(mod_handle.address)
469 .into_bytes(),
470 );
471 let mod_name = module.name().to_string();
472 let fn_info_key = FnInfoKey {
473 fn_name: name.to_string(),
474 mod_name,
475 mod_addr,
476 };
477 match fn_info_map.get(&fn_info_key) {
478 Some(fn_info) => fn_info.is_test,
479 None => false,
480 }
481}
482
483pub fn get_authenticator_version_from_fun(
484 name: &str,
485 module: &CompiledModule,
486 fn_info_map: &FnInfoMap,
487) -> Option<u8> {
488 let mod_handle = module.self_handle();
489 let mod_addr = Address::from(
490 module
491 .address_identifier_at(mod_handle.address)
492 .into_bytes(),
493 );
494 let mod_name = module.name().to_string();
495 let fn_info_key = FnInfoKey {
496 fn_name: name.to_string(),
497 mod_name,
498 mod_addr,
499 };
500 match fn_info_map.get(&fn_info_key) {
501 Some(FnInfo {
502 is_test: _,
503 authenticator_version: Some(v),
504 is_view: _,
505 }) => Some(*v),
506 _ => None,
507 }
508}
509
510pub fn is_view_function_from_fn_info(
512 name: &IdentStr,
513 module: &CompiledModule,
514 fn_info_map: &FnInfoMap,
515) -> bool {
516 let fn_name = name.to_string();
517 let mod_handle = module.self_handle();
518 let mod_addr = Address::from(
519 module
520 .address_identifier_at(mod_handle.address)
521 .into_bytes(),
522 );
523 let mod_name = module.name().to_string();
524 let fn_info_key = FnInfoKey {
525 fn_name,
526 mod_name,
527 mod_addr,
528 };
529 fn_info_map
530 .get(&fn_info_key)
531 .map(|info| info.is_view)
532 .unwrap_or(false)
533}
534
535pub fn normalize_modules<
538 'a,
539 S: Hash + Eq + Clone + ToString,
540 Pool: normalized::StringPool<String = S>,
541 I,
542>(
543 pool: &mut Pool,
544 modules: I,
545 binary_config: &BinaryConfig,
546 include_code: bool,
547) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
548where
549 I: Iterator<Item = &'a Vec<u8>>,
550{
551 let mut normalized_modules = BTreeMap::new();
552 for bytecode in modules {
553 let module =
554 CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
555 IotaError::ModuleDeserializationFailure {
556 error: error.to_string(),
557 }
558 })?;
559 let normalized_module = normalized::Module::new(pool, &module, include_code);
560 normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
561 }
562 Ok(normalized_modules)
563}
564
565pub fn normalize_modules_with_metadata<
571 'a,
572 S: Hash + Eq + Clone + ToString,
573 Pool: normalized::StringPool<String = S>,
574 I,
575>(
576 pool: &mut Pool,
577 modules: I,
578 binary_config: &BinaryConfig,
579 include_code: bool,
580 protocol_config: Option<&ProtocolConfig>,
581) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
582where
583 I: Iterator<Item = &'a Vec<u8>>,
584{
585 let mut normalized_modules = BTreeMap::new();
586 for bytecode in modules {
587 let module =
588 CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
589 IotaError::ModuleDeserializationFailure {
590 error: error.to_string(),
591 }
592 })?;
593 let metadata = runtime_module_metadata(&module, protocol_config)?;
594 let normalized_module = normalized::Module::new(pool, &module, include_code);
595 normalized_modules.insert(
596 normalized_module.name().to_string(),
597 (normalized_module, metadata),
598 );
599 }
600 Ok(normalized_modules)
601}
602
603pub fn normalize_deserialized_modules<
606 'a,
607 S: Hash + Eq + Clone + ToString,
608 Pool: normalized::StringPool<String = S>,
609 I,
610>(
611 pool: &mut Pool,
612 modules: I,
613 include_code: bool,
614) -> BTreeMap<String, normalized::Module<S>>
615where
616 I: Iterator<Item = &'a CompiledModule>,
617{
618 let mut normalized_modules = BTreeMap::new();
619 for module in modules {
620 let normalized_module = normalized::Module::new(pool, module, include_code);
621 normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
622 }
623 normalized_modules
624}
625
626pub fn normalize_deserialized_modules_with_metadata<
632 'a,
633 S: Hash + Eq + Clone + ToString,
634 Pool: normalized::StringPool<String = S>,
635 I,
636>(
637 pool: &mut Pool,
638 modules: I,
639 include_code: bool,
640 protocol_config: Option<&ProtocolConfig>,
641) -> IotaResult<BTreeMap<String, (normalized::Module<S>, RuntimeModuleMetadata)>>
642where
643 I: Iterator<Item = &'a CompiledModule>,
644{
645 let mut normalized_modules = BTreeMap::new();
646 for module in modules {
647 let metadata = runtime_module_metadata(module, protocol_config)?;
648 let normalized_module = normalized::Module::new(pool, module, include_code);
649 normalized_modules.insert(
650 normalized_module.name().to_string(),
651 (normalized_module, metadata),
652 );
653 }
654 Ok(normalized_modules)
655}
656
657fn runtime_module_metadata(
658 module: &CompiledModule,
659 protocol_config: Option<&ProtocolConfig>,
660) -> IotaResult<RuntimeModuleMetadata> {
661 let build_config = ProtocolBuildConfig::from(protocol_config);
662 let Some(metadata) = module
663 .metadata
664 .iter()
665 .find(|metadata| metadata.key == IOTA_METADATA_KEY)
666 else {
667 if build_config.allow_view_function {
668 return Ok(RuntimeModuleMetadata::v2());
669 } else {
670 return Ok(RuntimeModuleMetadata::v1());
671 }
672 };
673
674 let metadata_wrapper: RuntimeModuleMetadataWrapper =
675 bcs::from_bytes(&metadata.value).map_err(|error| {
676 IotaError::RuntimeModuleMetadataDeserialization {
677 error: error.to_string(),
678 }
679 })?;
680 metadata_wrapper.try_into_runtime_module_metadata(&build_config)
681}
682
683pub fn max_package_size(storage_id: ObjectId, protocol_config: &ProtocolConfig) -> u64 {
693 if storage_id.is_system_package() {
694 protocol_config
695 .max_move_system_package_size_as_option()
696 .unwrap_or_else(|| protocol_config.max_move_package_size())
697 } else {
698 protocol_config.max_move_package_size()
699 }
700}
701
702fn build_linkage_table<'p>(
703 mut immediate_dependencies: BTreeSet<ObjectId>,
704 transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
705 protocol_config: &ProtocolConfig,
706) -> Result<BTreeMap<ObjectId, UpgradeInfo>, ExecutionError> {
707 let mut linkage_table = BTreeMap::new();
708 let mut dep_linkage_tables = vec![];
709
710 for transitive_dep in transitive_dependencies.into_iter() {
711 let original_id = MovePackage::original_package_id(transitive_dep);
715
716 let imm_dep = immediate_dependencies.remove(&original_id);
717
718 if protocol_config.dependency_linkage_error() {
719 dep_linkage_tables.push(&transitive_dep.linkage_table);
720
721 let existing = linkage_table.insert(
722 original_id,
723 UpgradeInfo {
724 upgraded_id: transitive_dep.id,
725 upgraded_version: transitive_dep.version,
726 },
727 );
728
729 if existing.is_some() {
730 return Err(ExecutionErrorKind::InvalidLinkage.into());
731 }
732 } else {
733 if imm_dep {
734 dep_linkage_tables.push(&transitive_dep.linkage_table);
737 }
738 linkage_table.insert(
739 original_id,
740 UpgradeInfo {
741 upgraded_id: transitive_dep.id,
742 upgraded_version: transitive_dep.version,
743 },
744 );
745 }
746 }
747 if !immediate_dependencies.is_empty() {
749 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
750 }
751
752 for dep_linkage_table in dep_linkage_tables {
754 for (original_id, dep_info) in dep_linkage_table {
755 let Some(our_info) = linkage_table.get(original_id) else {
756 return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
757 };
758
759 if our_info.upgraded_version < dep_info.upgraded_version {
760 return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
761 }
762 }
763 }
764
765 Ok(linkage_table)
766}
767
768fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
769 modules
770 .iter()
771 .flat_map(|m| {
772 m.struct_defs()
773 .iter()
774 .map(|struct_def| {
775 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
776 let package = ObjectId::new(m.self_id().address().into_bytes());
777 TypeOrigin {
778 module_name: identifier_core_to_sdk(m.name()),
779 datatype_name: identifier_core_to_sdk(m.identifier_at(struct_handle.name)),
780 package,
781 }
782 })
783 .chain(m.enum_defs().iter().map(|enum_def| {
784 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
785 let package = ObjectId::new(m.self_id().address().into_bytes());
786 TypeOrigin {
787 module_name: identifier_core_to_sdk(m.name()),
788 datatype_name: identifier_core_to_sdk(m.identifier_at(enum_handle.name)),
789 package,
790 }
791 }))
792 })
793 .collect()
794}
795
796fn build_upgraded_type_origin_table(
797 predecessor: &MovePackage,
798 modules: &[CompiledModule],
799 storage_id: ObjectId,
800) -> Result<Vec<TypeOrigin>, ExecutionError> {
801 let mut new_table = vec![];
802 let mut existing_table = predecessor.type_origin_map();
803 for m in modules {
804 for struct_def in m.struct_defs() {
805 let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
806 let module_name = identifier_core_to_sdk(m.name());
807 let struct_name = identifier_core_to_sdk(m.identifier_at(struct_handle.name));
808 let mod_key = (module_name.clone(), struct_name.clone());
809 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
812 new_table.push(TypeOrigin {
813 module_name,
814 datatype_name: struct_name,
815 package,
816 });
817 }
818
819 for enum_def in m.enum_defs() {
820 let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
821 let module_name = identifier_core_to_sdk(m.name());
822 let enum_name = identifier_core_to_sdk(m.identifier_at(enum_handle.name));
823 let mod_key = (module_name.clone(), enum_name.clone());
824 let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
827 new_table.push(TypeOrigin {
828 module_name,
829 datatype_name: enum_name,
830 package,
831 });
832 }
833 }
834
835 if !existing_table.is_empty() {
836 Err(ExecutionError::from_kind(
837 ExecutionErrorKind::PackageUpgradeError {
838 kind: PackageUpgradeError::IncompatibleUpgrade,
839 },
840 ))
841 } else {
842 Ok(new_table)
843 }
844}
845
846#[derive(Debug, Clone, Copy, Default)]
853pub struct ProtocolBuildConfig {
854 pub allow_view_function: bool,
857 pub max_move_package_size: Option<u64>,
861 pub max_move_system_package_size: Option<u64>,
867}
868
869impl ProtocolBuildConfig {
870 pub fn from_protocol_config(protocol_config: &ProtocolConfig) -> Self {
872 Self {
873 allow_view_function: protocol_config.package_metadata_with_dynamic_module_metadata(),
874 max_move_package_size: Some(protocol_config.max_move_package_size()),
875 max_move_system_package_size: protocol_config.max_move_system_package_size_as_option(),
876 }
877 }
878}
879
880impl From<&ProtocolConfig> for ProtocolBuildConfig {
881 fn from(protocol_config: &ProtocolConfig) -> Self {
882 Self::from_protocol_config(protocol_config)
883 }
884}
885
886impl From<Option<&ProtocolConfig>> for ProtocolBuildConfig {
887 fn from(protocol_config: Option<&ProtocolConfig>) -> Self {
888 protocol_config
889 .map(Self::from_protocol_config)
890 .unwrap_or_default()
891 }
892}
893
894#[serde_as]
896#[derive(Debug, Clone, Serialize, Deserialize)]
897pub struct RuntimeModuleMetadataWrapper {
898 pub version: u64,
899 #[serde_as(as = "Bytes")]
900 pub inner: Vec<u8>,
901}
902
903impl RuntimeModuleMetadataWrapper {
904 pub fn to_bcs_bytes(&self) -> Vec<u8> {
905 bcs::to_bytes(&self).unwrap()
907 }
908
909 pub fn try_into_runtime_module_metadata(
910 &self,
911 protocol_build_config: &ProtocolBuildConfig,
912 ) -> Result<RuntimeModuleMetadata, IotaError> {
913 match self.version {
914 1 => {
915 let inner: RuntimeModuleMetadataV1 = bcs::from_bytes(&self.inner).map_err(|e| {
916 IotaError::RuntimeModuleMetadataDeserialization {
917 error: e.to_string(),
918 }
919 })?;
920 Ok(RuntimeModuleMetadata::V1(inner))
921 }
922 2 if protocol_build_config.allow_view_function => {
923 let inner: RuntimeModuleMetadataV2 = bcs::from_bytes(&self.inner).map_err(|e| {
924 IotaError::RuntimeModuleMetadataDeserialization {
925 error: e.to_string(),
926 }
927 })?;
928 Ok(RuntimeModuleMetadata::V2(inner))
929 }
930 _ => Err(IotaError::RuntimeModuleMetadataDeserialization {
931 error: format!(
932 "Unsupported runtime module metadata version: {}",
933 self.version
934 ),
935 }),
936 }
937 }
938}
939
940impl From<RuntimeModuleMetadata> for RuntimeModuleMetadataWrapper {
941 fn from(metadata: RuntimeModuleMetadata) -> Self {
942 match metadata {
943 RuntimeModuleMetadata::V1(inner) => RuntimeModuleMetadataWrapper {
944 version: 1,
945 inner: inner.to_bcs_bytes(),
946 },
947 RuntimeModuleMetadata::V2(inner) => RuntimeModuleMetadataWrapper {
948 version: 2,
949 inner: inner.to_bcs_bytes(),
950 },
951 }
952 }
953}
954
955#[derive(Debug, Clone, Serialize, Deserialize)]
957pub enum RuntimeModuleMetadata {
958 V1(RuntimeModuleMetadataV1),
959 V2(RuntimeModuleMetadataV2),
960}
961
962impl RuntimeModuleMetadata {
963 pub fn v1() -> Self {
964 RuntimeModuleMetadata::V1(RuntimeModuleMetadataV1::default())
965 }
966
967 pub fn v2() -> Self {
968 RuntimeModuleMetadata::V2(RuntimeModuleMetadataV2::default())
969 }
970
971 pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttribute) {
982 match (self, attribute) {
983 (RuntimeModuleMetadata::V1(metadata), IotaAttribute::V1(attribute)) => {
984 metadata.add_function_attribute(function_name, attribute)
985 }
986 (RuntimeModuleMetadata::V2(metadata), IotaAttribute::V2(attribute)) => {
987 metadata.add_function_attribute(function_name, attribute)
988 }
989 _ => panic!("attribute version does not match runtime module metadata version"),
990 }
991 }
992
993 pub fn is_empty(&self) -> bool {
994 match self {
995 RuntimeModuleMetadata::V1(metadata) => metadata.is_empty(),
996 RuntimeModuleMetadata::V2(metadata) => metadata.is_empty(),
997 }
998 }
999}
1000
1001#[derive(Debug, Clone)]
1006pub enum IotaAttribute {
1007 V1(IotaAttributeV1),
1008 V2(IotaAttributeV2),
1009}
1010
1011#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1013pub enum IotaAttributeV1 {
1014 Authenticator(AuthenticatorAttribute),
1015}
1016
1017#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1019pub enum IotaAttributeV2 {
1020 Authenticator(AuthenticatorAttribute),
1021 View,
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1025pub struct AuthenticatorAttribute {
1026 pub version: u8,
1027}
1028
1029impl IotaAttributeV1 {
1030 pub fn authenticator_attribute(version: u8) -> Self {
1031 IotaAttributeV1::Authenticator(AuthenticatorAttribute { version })
1032 }
1033}
1034
1035impl IotaAttributeV2 {
1036 pub fn authenticator_attribute(version: u8) -> Self {
1037 IotaAttributeV2::Authenticator(AuthenticatorAttribute { version })
1038 }
1039
1040 pub fn view_attribute() -> Self {
1041 IotaAttributeV2::View
1042 }
1043}
1044
1045#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1047pub struct RuntimeModuleMetadataV1 {
1048 pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV1>>,
1050}
1051
1052#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1054pub struct RuntimeModuleMetadataV2 {
1055 pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV2>>,
1057}
1058
1059impl RuntimeModuleMetadataV1 {
1060 pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV1) {
1061 self.fun_attributes
1062 .entry(function_name)
1063 .or_default()
1064 .push(attribute);
1065 }
1066
1067 pub fn is_empty(&self) -> bool {
1068 self.fun_attributes.is_empty()
1069 }
1070
1071 pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV1>)> {
1072 self.fun_attributes.iter()
1073 }
1074
1075 pub fn to_bcs_bytes(&self) -> Vec<u8> {
1076 bcs::to_bytes(&self).unwrap()
1078 }
1079}
1080
1081impl RuntimeModuleMetadataV2 {
1082 pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttributeV2) {
1083 self.fun_attributes
1084 .entry(function_name)
1085 .or_default()
1086 .push(attribute);
1087 }
1088
1089 pub fn is_empty(&self) -> bool {
1090 self.fun_attributes.is_empty()
1091 }
1092 pub fn fun_attributes_iter(&self) -> impl Iterator<Item = (&String, &Vec<IotaAttributeV2>)> {
1093 self.fun_attributes.iter()
1094 }
1095
1096 pub fn to_bcs_bytes(&self) -> Vec<u8> {
1097 bcs::to_bytes(&self).unwrap()
1099 }
1100}
1101
1102#[derive(Debug, Clone, Serialize, Deserialize)]
1107pub enum PackageMetadata {
1108 V1(PackageMetadataV1),
1109}
1110
1111impl PackageMetadata {
1112 pub fn new_v1(
1115 uid: ObjectId,
1116 storage_id: ObjectId,
1117 runtime_id: ObjectId,
1118 package_version: u64,
1119 modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1120 ) -> Self {
1121 PackageMetadata::V1(PackageMetadataV1::new(
1122 uid,
1123 storage_id,
1124 runtime_id,
1125 package_version,
1126 modules_metadata_map,
1127 ))
1128 }
1129
1130 pub fn struct_tag(&self) -> StructTag {
1131 match self {
1132 PackageMetadata::V1(_) => StructTag::new_package_metadata_v1(),
1133 }
1134 }
1135
1136 pub fn to_bcs_bytes(&self) -> Vec<u8> {
1137 match self {
1138 PackageMetadata::V1(inner) => inner.to_bcs_bytes(),
1139 }
1140 }
1141}
1142
1143#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
1144pub struct PackageMetadataKey {
1145 dummy_field: bool,
1149}
1150
1151impl PackageMetadataKey {
1152 pub fn to_bcs_bytes(&self) -> Vec<u8> {
1153 bcs::to_bytes(&self).unwrap()
1155 }
1156}
1157
1158pub fn derive_package_metadata_id(package_storage_id: ObjectId) -> ObjectId {
1159 package_storage_id.derive_object_id(
1160 &StructTag::new_package_metadata_key().into(),
1161 &PackageMetadataKey::default().to_bcs_bytes(),
1162 )
1163}
1164
1165#[derive(Debug, Clone, Serialize, Deserialize)]
1167pub struct PackageMetadataV1 {
1168 pub uid: UID,
1170 pub storage_id: ID,
1174 pub runtime_id: ID,
1177 pub package_version: u64,
1179 pub modules_metadata: VecMap<String, ModuleMetadataV1>,
1181}
1182
1183impl PackageMetadataV1 {
1184 fn new(
1185 uid: ObjectId,
1186 storage_id: ObjectId,
1187 runtime_id: ObjectId,
1188 package_version: u64,
1189 modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
1190 ) -> Self {
1191 let mut modules_metadata = VecMap { contents: vec![] };
1192
1193 for (module_name, module_metadata_map) in modules_metadata_map {
1194 let mut module_metadata = ModuleMetadataV1 {
1195 authenticator_metadata: vec![],
1196 };
1197 for (function_name, account_type) in module_metadata_map {
1198 module_metadata
1199 .authenticator_metadata
1200 .push(AuthenticatorMetadataV1 {
1201 function_name,
1202 account_type,
1203 });
1204 }
1205 modules_metadata.contents.push(Entry {
1206 key: module_name,
1207 value: module_metadata,
1208 });
1209 }
1210
1211 Self {
1212 uid: UID::new(uid),
1213 storage_id: ID::new(storage_id),
1214 runtime_id: ID::new(runtime_id),
1215 package_version,
1216 modules_metadata,
1217 }
1218 }
1219
1220 pub fn to_bcs_bytes(&self) -> Vec<u8> {
1221 bcs::to_bytes(&self).unwrap()
1223 }
1224}
1225
1226#[derive(Debug, Clone, Serialize, Deserialize)]
1228pub struct ModuleMetadataV1 {
1229 pub authenticator_metadata: Vec<AuthenticatorMetadataV1>,
1230}
1231
1232impl ModuleMetadataV1 {
1233 pub fn is_empty(&self) -> bool {
1234 self.authenticator_metadata.is_empty()
1235 }
1236}
1237
1238#[serde_as]
1240#[derive(Debug, Clone, Serialize, Deserialize)]
1241pub struct AuthenticatorMetadataV1 {
1242 pub function_name: String,
1243 #[serde_as(as = "TypeName")]
1244 pub account_type: TypeTag,
1245}
1246
1247#[cfg(test)]
1248mod tests {
1249 use iota_protocol_config::{Chain, ProtocolVersion};
1250
1251 use super::*;
1252
1253 const VERSION_WITHOUT_SYSTEM_PACKAGE_SIZE: u64 = 37;
1255
1256 fn config(version: u64) -> ProtocolConfig {
1257 ProtocolConfig::get_for_version(ProtocolVersion::new(version), Chain::Unknown)
1258 }
1259
1260 #[test]
1261 fn user_packages_keep_the_user_bound() {
1262 let protocol_config = config(ProtocolVersion::MAX.as_u64());
1263 let user_package = ObjectId::random();
1264
1265 assert!(!user_package.is_system_package());
1266 assert_eq!(
1267 max_package_size(user_package, &protocol_config),
1268 protocol_config.max_move_package_size(),
1269 );
1270 }
1271
1272 #[test]
1273 fn system_packages_get_the_system_bound() {
1274 let protocol_config = config(ProtocolVersion::MAX.as_u64());
1275
1276 assert_eq!(
1277 max_package_size(ObjectId::FRAMEWORK, &protocol_config),
1278 protocol_config.max_move_system_package_size(),
1279 );
1280 assert!(
1281 protocol_config.max_move_system_package_size()
1282 > protocol_config.max_move_package_size(),
1283 );
1284 }
1285
1286 #[test]
1287 fn system_packages_fall_back_to_the_user_bound_when_unset() {
1288 let protocol_config = config(VERSION_WITHOUT_SYSTEM_PACKAGE_SIZE);
1289
1290 assert!(
1291 protocol_config
1292 .max_move_system_package_size_as_option()
1293 .is_none()
1294 );
1295 assert_eq!(
1296 max_package_size(ObjectId::FRAMEWORK, &protocol_config),
1297 protocol_config.max_move_package_size(),
1298 );
1299 }
1300}