Skip to main content

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;
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)]
74/// Additional information about a function
75pub struct FnInfo {
76    /// If true, it's a function involved in testing (`[test]`, `[test_only]`,
77    /// `[expected_failure]`)
78    pub is_test: bool,
79    /// If set, function was marked to represent authenticator function of
80    /// given version.
81    pub authenticator_version: Option<u8>,
82    pub is_view: bool,
83}
84
85#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
86/// Uniquely identifies a function in a module
87pub struct FnInfoKey {
88    pub fn_name: String,
89    pub mod_name: String,
90    pub mod_addr: Address,
91}
92
93/// A map from function info keys to function info
94pub type FnInfoMap = BTreeMap<FnInfoKey, FnInfo>;
95
96// NB: do _not_ add `Serialize` or `Deserialize` to this enum. Convert to u8
97// first  or use the associated constants before storing in any serialization
98// setting.
99/// Rust representation of upgrade policy constants in `iota::package`.
100#[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    /// Convenience accessors to the upgrade policies as u8s.
113    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/// Rust representation of `iota::package::UpgradeCap`.
135#[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/// Rust representation of `iota::package::UpgradeTicket`.
144#[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/// Rust representation of `iota::package::UpgradeReceipt`.
153#[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    /// Create an initial version of the package along with this version's type
213    /// origin and linkage tables.
214    ///
215    /// # Undefined behavior
216    ///
217    /// All passed modules must have the same `Runtime ID` or the behavior is
218    /// undefined.
219    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    /// Create an upgraded version of the package along with this version's type
243    /// origin and linkage tables.
244    ///
245    /// # Undefined behavior
246    ///
247    /// All passed modules must have the same `Runtime ID` or the behavior is
248    /// undefined.
249    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                // The upgraded version is used by other packages that transitively depend on this
291                // system package, to make sure that if they choose a different version to depend on
292                // compared to their dependencies, they pick a greater version.
293                //
294                // However, in the case of system packages, although they can be upgraded, unlike
295                // other packages, only one version can be in use on the network at any given time,
296                // so it is not possible for a package to require a different system package version
297                // compared to its dependencies.
298                //
299                // This reason, coupled with the fact that system packages can only depend on each
300                // other, mean that their own linkage tables always report a version of zero.
301                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        // Not size-checked: an upgrade of a system package is exempt from the
316        // bound, and `compare_system_package` checks a package being added
317        // itself, against `max_package_size`.
318        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        // This is the one path that writes a package other than a system
374        // package upgrade: a first publish -- by a user, at genesis, or of a
375        // system package added at an epoch change -- and a user package
376        // upgrade. So the bound for the package's address is applied here. A
377        // system package upgrade goes through `new_system` and is exempt, and
378        // a package read back from the network is rebuilt with
379        // `MovePackage::new` and not checked again.
380        package.check_size(max_package_size(storage_id, protocol_config))?;
381
382        Ok(package)
383    }
384
385    /// The `Package ID` of the first version of this package.
386    ///
387    /// Also referred to as `Runtime ID`.
388    ///
389    /// Regardless of which version of the package we are working with, this
390    /// function will always return the `Package ID`/`Storage ID` of the first
391    /// package version in the version chain.
392    fn original_package_id(&self) -> ObjectId {
393        if self.version == Version::OBJECT_START {
394            // for a non-upgraded package, original ID is just the package ID
395            return self.id;
396        }
397
398        let bytes = self.modules.values().next().expect("Empty module map");
399        // Remember, that all modules will contain the `Package ID` of the first
400        // deployed package. This is why taking any of them will produce the
401        // original package id.
402        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        // TODO use the session's cache
413        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    /// If `include_code` is set to `false`, the normalized module will skip
428    /// function bodies but still include the signatures.
429    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    /// Create an `UpgradeCap` for the newly published package at `package_id`,
441    /// and associate it with the fresh `uid`.
442    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    /// Create an `UpgradeReceipt` for the upgraded package at `package_id`
454    /// using the `UpgradeTicket` and newly published package id.
455    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
463/// Checks if a function is annotated with one of the test-related annotations
464pub 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
510/// Returns true if a function is marked as a view function.
511pub 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
535/// If `include_code` is set to `false`, the normalized module will skip
536/// function bodies but still include the signatures.
537pub 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
565/// If `include_code` is set to `false`, the normalized module will skip
566/// function bodies but still include the signatures.
567///
568/// The returned metadata is the IOTA-specific runtime metadata attached to the
569/// module, or the default empty metadata when the module has no IOTA metadata.
570pub 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
603/// If `include_code` is set to `false`, the normalized module will skip
604/// function bodies but still include the signatures.
605pub 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
626/// If `include_code` is set to `false`, the normalized module will skip
627/// function bodies but still include the signatures.
628///
629/// The returned metadata is the IOTA-specific runtime metadata attached to the
630/// module, or the default empty metadata when the module has no IOTA metadata.
631pub 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
683/// The size bound a package published at `storage_id` must stay within.
684///
685/// A system package published for the first time — at genesis, or when a new
686/// one is added at an epoch change — goes through the same path as a user
687/// package, but is not a user package and is bound by
688/// `max_move_system_package_size`. When that is unset, system packages are
689/// bound by `max_move_package_size` like any other package. Upgrades of an
690/// existing system package never reach this check at all (see
691/// `MovePackage::new_system`).
692pub 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        // original_package_id will deserialize a module but only for the purpose of
712        // obtaining "original ID" of the package containing it so using max
713        // Move binary version during deserialization is OK
714        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                // Found an immediate dependency, mark it as seen, and stash a reference to its
735                // linkage table to check later.
736                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    // (1) Every dependency is represented in the transitive dependencies
748    if !immediate_dependencies.is_empty() {
749        return Err(ExecutionErrorKind::PublishUpgradeMissingDependency.into());
750    }
751
752    // (2) Every dependency's linkage table is superseded by this linkage table
753    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            // if id exists in the predecessor's table, use it, otherwise use the id of the
810            // upgraded module
811            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            // if id exists in the predecessor's table, use it, otherwise use the id of the
825            // upgraded module
826            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/// Protocol-dependent switches that the low-level package build and
847/// verification routines need.
848///
849/// Derived from the network's [`ProtocolConfig`], it lets those routines depend
850/// on a small, explicit set of protocol-gated flags rather than the full
851/// [`ProtocolConfig`].
852#[derive(Debug, Clone, Copy, Default)]
853pub struct ProtocolBuildConfig {
854    /// Build the module metadata with view function information and enable the
855    /// verifier to check the correctness of the view function attribute.
856    pub allow_view_function: bool,
857    /// Maximum size (in bytes) a published package may occupy on-chain. `None`
858    /// when the config was not derived from a network protocol config, in which
859    /// case the real limit is unknown.
860    pub max_move_package_size: Option<u64>,
861    /// Maximum size (in bytes) a published system package may occupy on-chain.
862    /// Set together with `max_move_package_size`: `None` next to a known user
863    /// bound means the network's protocol version holds system packages to
864    /// `max_move_package_size`, while `None` next to an unknown user bound
865    /// means the real limit is unknown too.
866    pub max_move_system_package_size: Option<u64>,
867}
868
869impl ProtocolBuildConfig {
870    /// Derives the build config from a network [`ProtocolConfig`].
871    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/// IOTA specific metadata attached to the metadata section of file_format.
895#[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        // Safe unwrap as the RuntimeModuleMetadataWrapper struct is always serializable
906        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/// IOTA specific metadata attached to the metadata section of file_format.
956#[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    /// Records `attribute` for `function_name`.
972    ///
973    /// The attribute's version must match the metadata's version: a
974    /// [`IotaAttribute::V1`] belongs in [`RuntimeModuleMetadata::V1`] and a
975    /// [`IotaAttribute::V2`] in [`RuntimeModuleMetadata::V2`].
976    ///
977    /// # Panics
978    ///
979    /// Panics if the attribute's version does not match the metadata's
980    /// version.
981    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/// Version-agnostic wrapper over the IOTA attribute types, for passing an
1002/// attribute of either version to [`RuntimeModuleMetadata`].
1003///
1004/// This wrapper is an in-memory convenience only and is never serialized.
1005#[derive(Debug, Clone)]
1006pub enum IotaAttribute {
1007    V1(IotaAttributeV1),
1008    V2(IotaAttributeV2),
1009}
1010
1011/// The list of iota attribute types recognized by the compiler.
1012#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1013pub enum IotaAttributeV1 {
1014    Authenticator(AuthenticatorAttribute),
1015}
1016
1017/// The list of iota attribute types recognized by the compiler.
1018#[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/// V1 of IOTA specific metadata.
1046#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1047pub struct RuntimeModuleMetadataV1 {
1048    /// Attributes attached to functions, by definition index.
1049    pub fun_attributes: BTreeMap<String, Vec<IotaAttributeV1>>,
1050}
1051
1052/// V2 of IOTA specific metadata.
1053#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1054pub struct RuntimeModuleMetadataV2 {
1055    /// Attributes attached to functions, by definition index.
1056    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        // Safe unwrap as the RuntimeModuleMetadataV1 struct is always serializable
1077        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        // Safe unwrap as the RuntimeModuleMetadataV2 struct is always serializable
1098        bcs::to_bytes(&self).unwrap()
1099    }
1100}
1101
1102/// Enum for handling the PackageMetadata framework type. The PackageMetadata is
1103/// IOTA specific metadata derived from a package and readable on-chain. This
1104/// enums helps with the versioning, which is actually used as the object
1105/// content, i.e., PackageMetadataV1 is the type used on-chain.
1106#[derive(Debug, Clone, Serialize, Deserialize)]
1107pub enum PackageMetadata {
1108    V1(PackageMetadataV1),
1109}
1110
1111impl PackageMetadata {
1112    /// Create a `PackageMetadata` for the newly
1113    /// published/upgraded package at `package_id`
1114    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    // This field is required to make a Rust struct compatible with an empty Move one.
1146    // An empty Move struct contains a 1-byte dummy bool field because empty fields are not
1147    // allowed in the bytecode.
1148    dummy_field: bool,
1149}
1150
1151impl PackageMetadataKey {
1152    pub fn to_bcs_bytes(&self) -> Vec<u8> {
1153        // Safe unwrap as the PackageMetadataKey struct is always serializable
1154        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/// V1 of IOTA specific package metadata.
1166#[derive(Debug, Clone, Serialize, Deserialize)]
1167pub struct PackageMetadataV1 {
1168    // The package metadata object UID
1169    pub uid: UID,
1170    /// Storage ID of the package represented by this metadata
1171    /// The object id of the runtime package metadata object is derived from
1172    /// this value.
1173    pub storage_id: ID,
1174    /// Runtime ID of the package represented by this metadata. Runtime ID is
1175    /// the Storage ID of the first version of a package.
1176    pub runtime_id: ID,
1177    /// Version of the package represented by this metadata
1178    pub package_version: u64,
1179    // Handles to internal package modules
1180    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        // Safe unwrap as the PackageMetadataV1 struct is always serializable
1222        bcs::to_bytes(&self).unwrap()
1223    }
1224}
1225
1226/// V1 of IOTA specific module metadata. Only includes authenticator info.
1227#[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/// V1 of IOTA specific authenticator info metadata.
1239#[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    /// A protocol version where `max_move_system_package_size` is unset.
1254    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}