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