iota_types/
move_package.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Move package.
6//!
7//! This module contains the [MovePackage] and types necessary for describing
8//! its update behavior and linkage information for module resolution during
9//! execution.
10//!
11//! Upgradeable packages form a version chain. This is simply the conceptual
12//! chain of package versions, with their monotonically increasing version
13//! numbers. Package { version: 1 } => Package { version: 2 } => ...
14//!
15//! The code contains terminology that may be confusing for the uninitiated,
16//! like `Module ID`, `Package ID`, `Storage ID` and `Runtime ID`. For avoidance
17//! of doubt these concepts are defined like so:
18//! - `Package ID` is the [ObjectID] representing the address by which the given
19//!   package may be found in storage.
20//! - `Runtime ID` will always mean the `Package ID`/`Storage ID` of the
21//!   initially published package. For a non upgradeable package this will
22//!   always be equal to `Storage ID`. For an upgradeable package, it will be
23//!   the `Storage ID` of the package's first deployed version.
24//! - `Storage ID` is the `Package ID`, and it is mostly used in to highlight
25//!   that we are talking about the current `Package ID` and not the `Runtime
26//!   ID`
27//! - `Module ID` is the the type
28//!   [ModuleID](move_core_types::language_storage::ModuleId).
29//!
30//! Some of these are redundant and have overlapping meaning, so whenever
31//! reasonable/necessary the possible naming will be listed. From all of these
32//! `Runtime ID` and `Module ID` are the most confusing. `Module ID` may be used
33//! with `Runtime ID` and `Storage ID` depending on the context. While `Runtime
34//! ID` is mostly used in name resolution during runtime, when a package with
35//! its modules has been loaded.
36
37use std::{
38    collections::{BTreeMap, BTreeSet},
39    hash::Hash,
40};
41
42use derive_more::Display;
43use iota_protocol_config::ProtocolConfig;
44pub use iota_sdk_types::move_package::{MovePackage, TypeOrigin, UpgradeInfo};
45use iota_sdk_types::{Identifier, Version};
46use move_binary_format::{
47    binary_config::BinaryConfig, file_format::CompiledModule, file_format_common::VERSION_6,
48    normalized,
49};
50use serde::{Deserialize, Serialize};
51use serde_with::{Bytes, serde_as};
52
53use crate::{
54    IotaAddress, TypeTag,
55    base_types::{ObjectID, SequenceNumber, StructTag},
56    collection_types::{Entry, VecMap},
57    derived_object,
58    error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
59    execution_status::PackageUpgradeError,
60    id::{ID, UID},
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)]
71/// Additional information about a function
72pub struct FnInfo {
73    /// If true, it's a function involved in testing (`[test]`, `[test_only]`,
74    /// `[expected_failure]`)
75    pub is_test: bool,
76    /// If set, function was marked to represent authenticator function of
77    /// given version.
78    pub authenticator_version: Option<u8>,
79}
80
81#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
82/// Uniquely identifies a function in a module
83pub struct FnInfoKey {
84    pub fn_name: String,
85    pub mod_name: String,
86    pub mod_addr: IotaAddress,
87}
88
89/// A map from function info keys to function info
90pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
91
92// NB: do _not_ add `Serialize` or `Deserialize` to this enum. Convert to u8
93// first  or use the associated constants before storing in any serialization
94// setting.
95/// Rust representation of upgrade policy constants in `iota::package`.
96#[repr(u8)]
97#[derive(Display, Debug, Clone, Copy)]
98pub enum UpgradePolicy {
99    #[display("COMPATIBLE")]
100    Compatible = 0,
101    #[display("ADDITIVE")]
102    Additive = 128,
103    #[display("DEP_ONLY")]
104    DepOnly = 192,
105}
106
107impl UpgradePolicy {
108    /// Convenience accessors to the upgrade policies as u8s.
109    pub const COMPATIBLE: u8 = Self::Compatible as u8;
110    pub const ADDITIVE: u8 = Self::Additive as u8;
111    pub const DEP_ONLY: u8 = Self::DepOnly as u8;
112
113    pub fn is_valid_policy(policy: &u8) -> bool {
114        Self::try_from(*policy).is_ok()
115    }
116}
117
118impl TryFrom<u8> for UpgradePolicy {
119    type Error = ();
120    fn try_from(value: u8) -> Result<Self, Self::Error> {
121        match value {
122            x if x == Self::Compatible as u8 => Ok(Self::Compatible),
123            x if x == Self::Additive as u8 => Ok(Self::Additive),
124            x if x == Self::DepOnly as u8 => Ok(Self::DepOnly),
125            _ => Err(()),
126        }
127    }
128}
129
130/// Rust representation of `iota::package::UpgradeCap`.
131#[derive(Debug, Serialize, Deserialize)]
132pub struct UpgradeCap {
133    pub id: UID,
134    pub package: ID,
135    pub version: u64,
136    pub policy: u8,
137}
138
139/// Rust representation of `iota::package::UpgradeTicket`.
140#[derive(Debug, Serialize, Deserialize)]
141pub struct UpgradeTicket {
142    pub cap: ID,
143    pub package: ID,
144    pub policy: u8,
145    pub digest: Vec<u8>,
146}
147
148/// Rust representation of `iota::package::UpgradeReceipt`.
149#[derive(Debug, Serialize, Deserialize)]
150pub struct UpgradeReceipt {
151    pub cap: ID,
152    pub package: ID,
153}
154
155mod move_package_ext {
156    pub trait Sealed {}
157    impl Sealed for super::MovePackage {}
158}
159
160pub trait MovePackageExt: Sized + move_package_ext::Sealed {
161    fn new_initial<'p>(
162        modules: &[CompiledModule],
163        protocol_config: &ProtocolConfig,
164        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
165    ) -> Result<MovePackage, ExecutionError>;
166
167    fn new_upgraded<'p>(
168        &self,
169        storage_id: ObjectID,
170        modules: &[CompiledModule],
171        protocol_config: &ProtocolConfig,
172        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
173    ) -> Result<MovePackage, ExecutionError>;
174
175    fn new_system(
176        version: SequenceNumber,
177        modules: &[CompiledModule],
178        dependencies: impl IntoIterator<Item = ObjectID>,
179    ) -> MovePackage;
180
181    fn from_module_iter_with_type_origin_table<'p>(
182        storage_id: ObjectID,
183        self_id: ObjectID,
184        version: SequenceNumber,
185        modules: &[CompiledModule],
186        protocol_config: &ProtocolConfig,
187        type_origin_table: Vec<TypeOrigin>,
188        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
189    ) -> Result<MovePackage, ExecutionError>;
190
191    fn original_package_id(&self) -> ObjectID;
192
193    fn deserialize_module(
194        &self,
195        module: &Identifier,
196        binary_config: &BinaryConfig,
197    ) -> IotaResult<CompiledModule>;
198
199    fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
200        &self,
201        pool: &mut Pool,
202        binary_config: &BinaryConfig,
203        include_code: bool,
204    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>>;
205}
206
207impl MovePackageExt for MovePackage {
208    /// Create an initial version of the package along with this version's type
209    /// origin and linkage tables.
210    ///
211    /// # Undefined behavior
212    ///
213    /// All passed modules must have the same `Runtime ID` or the behavior is
214    /// undefined.
215    fn new_initial<'p>(
216        modules: &[CompiledModule],
217        protocol_config: &ProtocolConfig,
218        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
219    ) -> Result<MovePackage, ExecutionError> {
220        let module = modules
221            .first()
222            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
223        let runtime_id = ObjectID::new(module.address().into_bytes());
224        let storage_id = runtime_id;
225        let type_origin_table = build_initial_type_origin_table(modules);
226
227        MovePackage::from_module_iter_with_type_origin_table(
228            storage_id,
229            runtime_id,
230            Version::OBJECT_START,
231            modules,
232            protocol_config,
233            type_origin_table,
234            transitive_dependencies,
235        )
236    }
237
238    /// Create an upgraded version of the package along with this version's type
239    /// origin and linkage tables.
240    ///
241    /// # Undefined behavior
242    ///
243    /// All passed modules must have the same `Runtime ID` or the behavior is
244    /// undefined.
245    fn new_upgraded<'p>(
246        &self,
247        storage_id: ObjectID,
248        modules: &[CompiledModule],
249        protocol_config: &ProtocolConfig,
250        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
251    ) -> Result<MovePackage, ExecutionError> {
252        let module = modules
253            .first()
254            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
255        let runtime_id = ObjectID::new(module.address().into_bytes());
256        let type_origin_table = build_upgraded_type_origin_table(self, modules, storage_id)?;
257        let mut new_version = self.version();
258        new_version.increment().unwrap();
259
260        MovePackage::from_module_iter_with_type_origin_table(
261            storage_id,
262            runtime_id,
263            new_version,
264            modules,
265            protocol_config,
266            type_origin_table,
267            transitive_dependencies,
268        )
269    }
270
271    fn new_system(
272        version: SequenceNumber,
273        modules: &[CompiledModule],
274        dependencies: impl IntoIterator<Item = ObjectID>,
275    ) -> MovePackage {
276        let module = modules
277            .first()
278            .expect("Tried to build a Move package from an empty iterator of Compiled modules");
279
280        let storage_id = ObjectID::new(module.address().into_bytes());
281        let type_origin_table = build_initial_type_origin_table(modules);
282
283        let linkage_table = BTreeMap::from_iter(dependencies.into_iter().map(|dep| {
284            let info = UpgradeInfo {
285                upgraded_id: dep,
286                // The upgraded version is used by other packages that transitively depend on this
287                // system package, to make sure that if they choose a different version to depend on
288                // compared to their dependencies, they pick a greater version.
289                //
290                // However, in the case of system packages, although they can be upgraded, unlike
291                // other packages, only one version can be in use on the network at any given time,
292                // so it is not possible for a package to require a different system package version
293                // compared to its dependencies.
294                //
295                // This reason, coupled with the fact that system packages can only depend on each
296                // other, mean that their own linkage tables always report a version of zero.
297                upgraded_version: SequenceNumber::default(),
298            };
299            (dep, info)
300        }));
301
302        let module_map = BTreeMap::from_iter(modules.iter().map(|module| {
303            let name = Identifier::new_unchecked(module.name().as_str());
304            let mut bytes = Vec::new();
305            module
306                .serialize_with_version(module.version, &mut bytes)
307                .unwrap();
308            (name, bytes)
309        }));
310
311        MovePackage::new(
312            storage_id,
313            version,
314            module_map,
315            u64::MAX, // System packages are not subject to the size limit
316            type_origin_table,
317            linkage_table,
318        )
319        .expect("System packages are not subject to a size limit")
320    }
321
322    fn from_module_iter_with_type_origin_table<'p>(
323        storage_id: ObjectID,
324        self_id: ObjectID,
325        version: SequenceNumber,
326        modules: &[CompiledModule],
327        protocol_config: &ProtocolConfig,
328        type_origin_table: Vec<TypeOrigin>,
329        transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
330    ) -> Result<MovePackage, ExecutionError> {
331        let mut module_map = BTreeMap::new();
332        let mut immediate_dependencies = BTreeSet::new();
333
334        for module in modules {
335            let name = Identifier::new_unchecked(module.name().as_str());
336
337            immediate_dependencies.extend(
338                module
339                    .immediate_dependencies()
340                    .into_iter()
341                    .map(|dep| ObjectID::new(dep.address().into_bytes())),
342            );
343
344            let mut bytes = Vec::new();
345            let version = if protocol_config.move_binary_format_version() > VERSION_6 {
346                module.version
347            } else {
348                VERSION_6
349            };
350            module.serialize_with_version(version, &mut bytes).unwrap();
351            module_map.insert(name, bytes);
352        }
353
354        immediate_dependencies.remove(&self_id);
355        let linkage_table = build_linkage_table(
356            immediate_dependencies,
357            transitive_dependencies,
358            protocol_config,
359        )?;
360
361        Ok(MovePackage::new(
362            storage_id,
363            version,
364            module_map,
365            protocol_config.max_move_package_size(),
366            type_origin_table,
367            linkage_table,
368        )?)
369    }
370
371    /// The `Package ID` of the first version of this package.
372    ///
373    /// Also referred to as `Runtime ID`.
374    ///
375    /// Regardless of which version of the package we are working with, this
376    /// function will always return the `Package ID`/`Storage ID` of the first
377    /// package version in the version chain.
378    fn original_package_id(&self) -> ObjectID {
379        if self.version == SequenceNumber::OBJECT_START {
380            // for a non-upgraded package, original ID is just the package ID
381            return self.id;
382        }
383
384        let bytes = self.modules.values().next().expect("Empty module map");
385        // Remember, that all modules will contain the `Package ID` of the first
386        // deployed package. This is why taking any of them will produce the
387        // original package id.
388        let module = CompiledModule::deserialize_with_defaults(bytes)
389            .expect("A Move package contains a module that cannot be deserialized");
390        ObjectID::new(module.address().into_bytes())
391    }
392
393    fn deserialize_module(
394        &self,
395        module: &Identifier,
396        binary_config: &BinaryConfig,
397    ) -> IotaResult<CompiledModule> {
398        // TODO use the session's cache
399        let bytes =
400            self.serialized_module_map()
401                .get(module)
402                .ok_or_else(|| IotaError::ModuleNotFound {
403                    module_name: module.to_string(),
404                })?;
405
406        CompiledModule::deserialize_with_config(bytes, binary_config).map_err(|error| {
407            IotaError::ModuleDeserializationFailure {
408                error: error.to_string(),
409            }
410        })
411    }
412
413    /// If `include_code` is set to `false`, the normalized module will skip
414    /// function bodies but still include the signatures.
415    fn normalize<S: Hash + Eq + Clone + ToString, Pool: normalized::StringPool<String = S>>(
416        &self,
417        pool: &mut Pool,
418        binary_config: &BinaryConfig,
419        include_code: bool,
420    ) -> IotaResult<BTreeMap<String, normalized::Module<S>>> {
421        normalize_modules(pool, self.modules.values(), binary_config, include_code)
422    }
423}
424
425impl UpgradeCap {
426    /// Create an `UpgradeCap` for the newly published package at `package_id`,
427    /// and associate it with the fresh `uid`.
428    pub fn new(uid: ObjectID, package_id: ObjectID) -> Self {
429        UpgradeCap {
430            id: UID::new(uid),
431            package: ID::new(package_id),
432            version: 1,
433            policy: UpgradePolicy::COMPATIBLE,
434        }
435    }
436}
437
438impl UpgradeReceipt {
439    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
440    /// using the `UpgradeTicket` and newly published package id.
441    pub fn new(upgrade_ticket: UpgradeTicket, upgraded_package_id: ObjectID) -> Self {
442        UpgradeReceipt {
443            cap: upgrade_ticket.cap,
444            package: ID::new(upgraded_package_id),
445        }
446    }
447}
448
449/// Checks if a function is annotated with one of the test-related annotations
450pub fn is_test_fun(name: &str, module: &CompiledModule, fn_info_map: &FnInfoMap) -> bool {
451    let mod_handle = module.self_handle();
452    let mod_addr = IotaAddress::new(
453        module
454            .address_identifier_at(mod_handle.address)
455            .into_bytes(),
456    );
457    let mod_name = module.name().to_string();
458    let fn_info_key = FnInfoKey {
459        fn_name: name.to_string(),
460        mod_name,
461        mod_addr,
462    };
463    match fn_info_map.get(&fn_info_key) {
464        Some(fn_info) => fn_info.is_test,
465        None => false,
466    }
467}
468
469pub fn get_authenticator_version_from_fun(
470    name: &str,
471    module: &CompiledModule,
472    fn_info_map: &FnInfoMap,
473) -> Option<u8> {
474    let mod_handle = module.self_handle();
475    let mod_addr = IotaAddress::from(
476        module
477            .address_identifier_at(mod_handle.address)
478            .into_bytes(),
479    );
480    let mod_name = module.name().to_string();
481    let fn_info_key = FnInfoKey {
482        fn_name: name.to_string(),
483        mod_name,
484        mod_addr,
485    };
486    match fn_info_map.get(&fn_info_key) {
487        Some(FnInfo {
488            is_test: _,
489            authenticator_version: Some(v),
490        }) => Some(*v),
491        _ => None,
492    }
493}
494
495/// If `include_code` is set to `false`, the normalized module will skip
496/// function bodies but still include the signatures.
497pub fn normalize_modules<
498    'a,
499    S: Hash + Eq + Clone + ToString,
500    Pool: normalized::StringPool<String = S>,
501    I,
502>(
503    pool: &mut Pool,
504    modules: I,
505    binary_config: &BinaryConfig,
506    include_code: bool,
507) -> IotaResult<BTreeMap<String, normalized::Module<S>>>
508where
509    I: Iterator<Item = &'a Vec<u8>>,
510{
511    let mut normalized_modules = BTreeMap::new();
512    for bytecode in modules {
513        let module =
514            CompiledModule::deserialize_with_config(bytecode, binary_config).map_err(|error| {
515                IotaError::ModuleDeserializationFailure {
516                    error: error.to_string(),
517                }
518            })?;
519        let normalized_module = normalized::Module::new(pool, &module, include_code);
520        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
521    }
522    Ok(normalized_modules)
523}
524
525/// If `include_code` is set to `false`, the normalized module will skip
526/// function bodies but still include the signatures.
527pub fn normalize_deserialized_modules<
528    'a,
529    S: Hash + Eq + Clone + ToString,
530    Pool: normalized::StringPool<String = S>,
531    I,
532>(
533    pool: &mut Pool,
534    modules: I,
535    include_code: bool,
536) -> BTreeMap<String, normalized::Module<S>>
537where
538    I: Iterator<Item = &'a CompiledModule>,
539{
540    let mut normalized_modules = BTreeMap::new();
541    for module in modules {
542        let normalized_module = normalized::Module::new(pool, module, include_code);
543        normalized_modules.insert(normalized_module.name().to_string(), normalized_module);
544    }
545    normalized_modules
546}
547
548fn build_linkage_table<'p>(
549    mut immediate_dependencies: BTreeSet<ObjectID>,
550    transitive_dependencies: impl IntoIterator<Item = &'p MovePackage>,
551    protocol_config: &ProtocolConfig,
552) -> Result<BTreeMap<ObjectID, UpgradeInfo>, ExecutionError> {
553    let mut linkage_table = BTreeMap::new();
554    let mut dep_linkage_tables = vec![];
555
556    for transitive_dep in transitive_dependencies.into_iter() {
557        // original_package_id will deserialize a module but only for the purpose of
558        // obtaining "original ID" of the package containing it so using max
559        // Move binary version during deserialization is OK
560        let original_id = MovePackage::original_package_id(transitive_dep);
561
562        let imm_dep = immediate_dependencies.remove(&original_id);
563
564        if protocol_config.dependency_linkage_error() {
565            dep_linkage_tables.push(&transitive_dep.linkage_table);
566
567            let existing = linkage_table.insert(
568                original_id,
569                UpgradeInfo {
570                    upgraded_id: transitive_dep.id,
571                    upgraded_version: transitive_dep.version,
572                },
573            );
574
575            if existing.is_some() {
576                return Err(ExecutionErrorKind::InvalidLinkage.into());
577            }
578        } else {
579            if imm_dep {
580                // Found an immediate dependency, mark it as seen, and stash a reference to its
581                // linkage table to check later.
582                dep_linkage_tables.push(&transitive_dep.linkage_table);
583            }
584            linkage_table.insert(
585                original_id,
586                UpgradeInfo {
587                    upgraded_id: transitive_dep.id,
588                    upgraded_version: transitive_dep.version,
589                },
590            );
591        }
592    }
593    // (1) Every dependency is represented in the transitive dependencies
594    if !immediate_dependencies.is_empty() {
595        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
596    }
597
598    // (2) Every dependency's linkage table is superseded by this linkage table
599    for dep_linkage_table in dep_linkage_tables {
600        for (original_id, dep_info) in dep_linkage_table {
601            let Some(our_info) = linkage_table.get(original_id) else {
602                return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
603            };
604
605            if our_info.upgraded_version < dep_info.upgraded_version {
606                return Err(ExecutionErrorKind::PublishUpgradeDependencyDowngrade.into());
607            }
608        }
609    }
610
611    Ok(linkage_table)
612}
613
614fn build_initial_type_origin_table(modules: &[CompiledModule]) -> Vec<TypeOrigin> {
615    modules
616        .iter()
617        .flat_map(|m| {
618            m.struct_defs()
619                .iter()
620                .map(|struct_def| {
621                    let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
622                    let module_name = m.name().to_string();
623                    let struct_name = m.identifier_at(struct_handle.name).to_string();
624                    let package = ObjectID::new(m.self_id().address().into_bytes());
625                    TypeOrigin {
626                        module_name: Identifier::new_unchecked(module_name),
627                        datatype_name: Identifier::new_unchecked(struct_name),
628                        package,
629                    }
630                })
631                .chain(m.enum_defs().iter().map(|enum_def| {
632                    let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
633                    let module_name = m.name().to_string();
634                    let enum_name = m.identifier_at(enum_handle.name).to_string();
635                    let package = ObjectID::new(m.self_id().address().into_bytes());
636                    TypeOrigin {
637                        module_name: Identifier::new_unchecked(module_name),
638                        datatype_name: Identifier::new_unchecked(enum_name),
639                        package,
640                    }
641                }))
642        })
643        .collect()
644}
645
646fn build_upgraded_type_origin_table(
647    predecessor: &MovePackage,
648    modules: &[CompiledModule],
649    storage_id: ObjectID,
650) -> Result<Vec<TypeOrigin>, ExecutionError> {
651    let mut new_table = vec![];
652    let mut existing_table = predecessor.type_origin_map();
653    for m in modules {
654        for struct_def in m.struct_defs() {
655            let struct_handle = m.datatype_handle_at(struct_def.struct_handle);
656            let module_name = Identifier::new_unchecked(m.name().as_str());
657            let struct_name =
658                Identifier::new_unchecked(m.identifier_at(struct_handle.name).as_str());
659            let mod_key = (module_name.clone(), struct_name.clone());
660            // if id exists in the predecessor's table, use it, otherwise use the id of the
661            // upgraded module
662            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
663            new_table.push(TypeOrigin {
664                module_name,
665                datatype_name: struct_name,
666                package,
667            });
668        }
669
670        for enum_def in m.enum_defs() {
671            let enum_handle = m.datatype_handle_at(enum_def.enum_handle);
672            let module_name = Identifier::new_unchecked(m.name().as_str());
673            let enum_name = Identifier::new_unchecked(m.identifier_at(enum_handle.name).as_str());
674            let mod_key = (module_name.clone(), enum_name.clone());
675            // if id exists in the predecessor's table, use it, otherwise use the id of the
676            // upgraded module
677            let package = existing_table.remove(&mod_key).unwrap_or(storage_id);
678            new_table.push(TypeOrigin {
679                module_name,
680                datatype_name: enum_name,
681                package,
682            });
683        }
684    }
685
686    if !existing_table.is_empty() {
687        Err(ExecutionError::from_kind(
688            ExecutionErrorKind::PackageUpgradeError {
689                kind: PackageUpgradeError::IncompatibleUpgrade,
690            },
691        ))
692    } else {
693        Ok(new_table)
694    }
695}
696
697/// IOTA specific metadata attached to the metadata section of file_format.
698#[serde_as]
699#[derive(Debug, Clone, Serialize, Deserialize)]
700pub struct RuntimeModuleMetadataWrapper {
701    pub version: u64,
702    #[serde_as(as = "Bytes")]
703    pub inner: Vec<u8>,
704}
705
706impl RuntimeModuleMetadataWrapper {
707    pub fn to_bcs_bytes(&self) -> Vec<u8> {
708        // Safe unwrap as the RuntimeModuleMetadataWrapper struct is always serializable
709        bcs::to_bytes(&self).unwrap()
710    }
711}
712
713impl From<RuntimeModuleMetadata> for RuntimeModuleMetadataWrapper {
714    fn from(metadata: RuntimeModuleMetadata) -> Self {
715        match metadata {
716            RuntimeModuleMetadata::V1(inner) => RuntimeModuleMetadataWrapper {
717                version: 1,
718                inner: inner.to_bcs_bytes(),
719            },
720        }
721    }
722}
723
724/// IOTA specific metadata attached to the metadata section of file_format.
725#[derive(Debug, Clone, Serialize, Deserialize)]
726pub enum RuntimeModuleMetadata {
727    V1(RuntimeModuleMetadataV1),
728}
729
730impl RuntimeModuleMetadata {
731    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttribute) {
732        match self {
733            RuntimeModuleMetadata::V1(metadata) => {
734                metadata.add_function_attribute(function_name, attribute)
735            }
736        }
737    }
738
739    pub fn is_empty(&self) -> bool {
740        match self {
741            RuntimeModuleMetadata::V1(metadata) => metadata.is_empty(),
742        }
743    }
744
745    pub fn fun_attributes_iter(
746        &self,
747    ) -> Box<dyn Iterator<Item = (&String, &Vec<IotaAttribute>)> + '_> {
748        match self {
749            RuntimeModuleMetadata::V1(metadata) => Box::new(metadata.fun_attributes.iter()),
750        }
751    }
752}
753
754impl Default for RuntimeModuleMetadata {
755    fn default() -> Self {
756        RuntimeModuleMetadata::V1(RuntimeModuleMetadataV1::default())
757    }
758}
759
760impl TryFrom<RuntimeModuleMetadataWrapper> for RuntimeModuleMetadata {
761    type Error = IotaError;
762
763    fn try_from(wrapper: RuntimeModuleMetadataWrapper) -> Result<Self, Self::Error> {
764        match wrapper.version {
765            1 => {
766                let inner: RuntimeModuleMetadataV1 =
767                    bcs::from_bytes(&wrapper.inner).map_err(|e| {
768                        IotaError::RuntimeModuleMetadataDeserialization {
769                            error: e.to_string(),
770                        }
771                    })?;
772                Ok(RuntimeModuleMetadata::V1(inner))
773            }
774            _ => Err(IotaError::RuntimeModuleMetadataDeserialization {
775                error: format!(
776                    "Unsupported runtime module metadata version: {}",
777                    wrapper.version
778                ),
779            }),
780        }
781    }
782}
783
784/// The list of iota attribute types recognized by the compiler.
785#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
786pub enum IotaAttribute {
787    Authenticator(AuthenticatorAttribute),
788}
789
790#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
791pub struct AuthenticatorAttribute {
792    pub version: u8,
793}
794
795impl IotaAttribute {
796    pub fn authenticator_attribute(version: u8) -> Self {
797        IotaAttribute::Authenticator(AuthenticatorAttribute { version })
798    }
799}
800
801/// V1 of IOTA specific metadata.
802#[derive(Debug, Clone, Serialize, Deserialize, Default)]
803pub struct RuntimeModuleMetadataV1 {
804    /// Attributes attached to functions, by definition index.
805    pub fun_attributes: BTreeMap<String, Vec<IotaAttribute>>,
806}
807
808impl RuntimeModuleMetadataV1 {
809    pub fn add_function_attribute(&mut self, function_name: String, attribute: IotaAttribute) {
810        self.fun_attributes
811            .entry(function_name)
812            .or_default()
813            .push(attribute);
814    }
815
816    pub fn is_empty(&self) -> bool {
817        self.fun_attributes.is_empty()
818    }
819
820    pub fn to_bcs_bytes(&self) -> Vec<u8> {
821        // Safe unwrap as the RuntimeModuleMetadataV1 struct is always serializable
822        bcs::to_bytes(&self).unwrap()
823    }
824}
825
826/// Enum for handling the PackageMetadata framework type. The PackageMetadata is
827/// IOTA specific metadata derived from a package and readable on-chain. This
828/// enums helps with the versioning, which is actually used as the object
829/// content, i.e., PackageMetadataV1 is the type used on-chain.
830#[derive(Debug, Clone, Serialize, Deserialize)]
831pub enum PackageMetadata {
832    V1(PackageMetadataV1),
833}
834
835impl PackageMetadata {
836    /// Create a `PackageMetadata` for the newly
837    /// published/upgraded package at `package_id`
838    pub fn new_v1(
839        uid: ObjectID,
840        storage_id: ObjectID,
841        runtime_id: ObjectID,
842        package_version: u64,
843        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
844    ) -> Self {
845        PackageMetadata::V1(PackageMetadataV1::new(
846            uid,
847            storage_id,
848            runtime_id,
849            package_version,
850            modules_metadata_map,
851        ))
852    }
853
854    pub fn type_(&self) -> StructTag {
855        match self {
856            PackageMetadata::V1(_) => PackageMetadataV1::type_(),
857        }
858    }
859
860    pub fn to_bcs_bytes(&self) -> Vec<u8> {
861        match self {
862            PackageMetadata::V1(inner) => inner.to_bcs_bytes(),
863        }
864    }
865}
866
867#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
868pub struct PackageMetadataKey {
869    // This field is required to make a Rust struct compatible with an empty Move one.
870    // An empty Move struct contains a 1-byte dummy bool field because empty fields are not
871    // allowed in the bytecode.
872    dummy_field: bool,
873}
874
875impl PackageMetadataKey {
876    pub fn tag() -> StructTag {
877        StructTag::new(
878            IotaAddress::FRAMEWORK,
879            PACKAGE_METADATA_MODULE_NAME,
880            PACKAGE_METADATA_KEY_STRUCT_NAME,
881            Vec::new(),
882        )
883    }
884
885    pub fn to_bcs_bytes(&self) -> Vec<u8> {
886        // Safe unwrap as the PackageMetadataKey struct is always serializable
887        bcs::to_bytes(&self).unwrap()
888    }
889}
890
891pub fn derive_package_metadata_id(package_storage_id: ObjectID) -> ObjectID {
892    derived_object::derive_object_id(
893        package_storage_id,
894        &PackageMetadataKey::tag().into(),
895        &PackageMetadataKey::default().to_bcs_bytes(),
896    )
897    .unwrap() // safe because type tag is known
898}
899
900/// V1 of IOTA specific package metadata.
901#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct PackageMetadataV1 {
903    // The package metadata object UID
904    pub uid: UID,
905    /// Storage ID of the package represented by this metadata
906    /// The object id of the runtime package metadata object is derived from
907    /// this value.
908    pub storage_id: ID,
909    /// Runtime ID of the package represented by this metadata. Runtime ID is
910    /// the Storage ID of the first version of a package.
911    pub runtime_id: ID,
912    /// Version of the package represented by this metadata
913    pub package_version: u64,
914    // Handles to internal package modules
915    pub modules_metadata: VecMap<String, ModuleMetadataV1>,
916}
917
918impl PackageMetadataV1 {
919    fn new(
920        uid: ObjectID,
921        storage_id: ObjectID,
922        runtime_id: ObjectID,
923        package_version: u64,
924        modules_metadata_map: BTreeMap<String, BTreeMap<String, TypeTag>>,
925    ) -> Self {
926        let mut modules_metadata = VecMap { contents: vec![] };
927
928        for (module_name, module_metadata_map) in modules_metadata_map {
929            let mut module_metadata = ModuleMetadataV1 {
930                authenticator_metadata: vec![],
931            };
932            for (function_name, account_type) in module_metadata_map {
933                module_metadata
934                    .authenticator_metadata
935                    .push(AuthenticatorMetadataV1 {
936                        function_name,
937                        account_type,
938                    });
939            }
940            modules_metadata.contents.push(Entry {
941                key: module_name,
942                value: module_metadata,
943            });
944        }
945
946        Self {
947            uid: UID::new(uid),
948            storage_id: ID::new(storage_id),
949            runtime_id: ID::new(runtime_id),
950            package_version,
951            modules_metadata,
952        }
953    }
954
955    pub fn type_() -> StructTag {
956        StructTag::new(
957            IotaAddress::FRAMEWORK,
958            PACKAGE_METADATA_MODULE_NAME,
959            PACKAGE_METADATA_V1_STRUCT_NAME,
960            vec![],
961        )
962    }
963
964    pub fn to_bcs_bytes(&self) -> Vec<u8> {
965        // Safe unwrap as the PackageMetadataV1 struct is always serializable
966        bcs::to_bytes(&self).unwrap()
967    }
968}
969
970/// V1 of IOTA specific module metadata. Only includes authenticator info.
971#[derive(Debug, Clone, Serialize, Deserialize)]
972pub struct ModuleMetadataV1 {
973    pub authenticator_metadata: Vec<AuthenticatorMetadataV1>,
974}
975
976impl ModuleMetadataV1 {
977    pub fn is_empty(&self) -> bool {
978        self.authenticator_metadata.is_empty()
979    }
980}
981
982/// V1 of IOTA specific authenticator info metadata.
983#[serde_as]
984#[derive(Debug, Clone, Serialize, Deserialize)]
985pub struct AuthenticatorMetadataV1 {
986    pub function_name: String,
987    #[serde_as(as = "TypeName")]
988    pub account_type: TypeTag,
989}