iota_package_resolver/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    borrow::Cow,
7    collections::{BTreeMap, BTreeSet},
8    num::NonZeroUsize,
9    sync::{Arc, Mutex},
10};
11
12use async_trait::async_trait;
13use iota_types::{
14    Identifier,
15    base_types::{SequenceNumber, is_primitive_type_tag},
16    move_package::{MovePackage, TypeOrigin},
17    object::Object,
18    transaction::{Argument, CallArg, Command, ProgrammableTransaction},
19};
20use lru::LruCache;
21use move_binary_format::{
22    CompiledModule,
23    errors::Location,
24    file_format::{
25        AbilitySet, DatatypeHandleIndex, DatatypeTyParameter, EnumDefinitionIndex,
26        FunctionDefinitionIndex, Signature as MoveSignature, SignatureIndex, SignatureToken,
27        StructDefinitionIndex, StructFieldInformation, TableIndex, Visibility,
28    },
29};
30use move_command_line_common::{
31    display::{RenderResult, try_render_constant},
32    error_bitset::ErrorBitset,
33};
34use move_core_types::{
35    account_address::AccountAddress,
36    annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
37    language_storage::{ModuleId, StructTag, TypeTag},
38};
39
40use crate::error::Error;
41
42pub mod error;
43
44// TODO Move to ServiceConfig
45
46const PACKAGE_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
47
48pub type Result<T> = std::result::Result<T, Error>;
49
50/// The Resolver is responsible for providing information about types. It relies
51/// on its internal `package_store` to load packages and then type definitions
52/// from those packages.
53#[derive(Debug)]
54pub struct Resolver<S> {
55    package_store: S,
56    limits: Option<Limits>,
57}
58
59/// Optional configuration that imposes limits on the work that the resolver can
60/// do for each request.
61#[derive(Debug)]
62pub struct Limits {
63    /// Maximum recursion depth through type parameters.
64    pub max_type_argument_depth: usize,
65    /// Maximum number of type arguments in a single type instantiation.
66    pub max_type_argument_width: usize,
67    /// Maximum size for the resolution context.
68    pub max_type_nodes: usize,
69    /// Maximum recursion depth through struct fields.
70    pub max_move_value_depth: usize,
71}
72
73/// Store which fetches package for the given address from the backend db and
74/// caches it locally in an lru cache. On every call to `fetch` it checks
75/// backend db and if package version is stale locally, it updates the local
76/// state before returning to the user
77pub struct PackageStoreWithLruCache<T> {
78    pub(crate) packages: Mutex<LruCache<AccountAddress, Arc<Package>>>,
79    pub(crate) inner: T,
80}
81
82#[derive(Clone, Debug)]
83pub struct Package {
84    /// The ID this package was loaded from on-chain.
85    storage_id: AccountAddress,
86
87    /// The ID that this package is associated with at runtime.  Bytecode in
88    /// other packages refers to types and functions from this package using
89    /// this ID.
90    runtime_id: AccountAddress,
91
92    /// The package's transitive dependencies as a mapping from the package's
93    /// runtime ID (the ID it is referred to by in other packages) to its
94    /// storage ID (the ID it is loaded from on chain).
95    linkage: Linkage,
96
97    /// The version this package was loaded at -- necessary for handling race
98    /// conditions when loading system packages.
99    version: SequenceNumber,
100
101    modules: BTreeMap<String, Module>,
102}
103
104type Linkage = BTreeMap<AccountAddress, AccountAddress>;
105
106/// A `CleverError` is a special kind of abort code that is used to encode more
107/// information than a normal abort code. These clever errors are used to encode
108/// the line number, error constant name, and error constant value as pool
109/// indices packed into a format satisfying the `ErrorBitset` format. This
110/// struct is the "inflated" view of that data, providing the module ID, line
111/// number, and error constant name and value (if available).
112#[derive(Clone, Debug)]
113pub struct CleverError {
114    /// The (storage) module ID of the module that the assertion failed in.
115    pub module_id: ModuleId,
116    /// Inner error information. This is either a complete error, just a line
117    /// number, or bytes that should be treated opaquely.
118    pub error_info: ErrorConstants,
119    /// The line number in the source file where the error occurred.
120    pub source_line_number: u16,
121}
122
123/// The `ErrorConstants` enum is used to represent the different kinds of error
124/// information that can be returned from a clever error when looking at the
125/// constant values for the clever error. These values are either:
126/// * `None` - No constant information is available, only a line number.
127/// * `Rendered` - The error is a complete error, with an error identifier and
128///   constant that can be rendered in a human-readable format (see in-line doc
129///   comments for exact types of values supported).
130/// * `Raw` - If there is an error constant value, but it is not a renderable
131///   type (e.g., a `vector<address>`), then it is treated as opaque and the
132///   bytes are returned.
133#[derive(Clone, Debug)]
134pub enum ErrorConstants {
135    /// No constant information is available, only a line number.
136    None,
137    /// The error is a complete error, with an error identifier and constant
138    /// that can be rendered. The rendered string representation of the
139    /// constant is returned only when the constant value is one of the
140    /// following types:
141    /// * A vector of bytes convertible to a valid UTF-8 string; or
142    /// * A numeric value (u8, u16, u32, u64, u128, u256); or
143    /// * A boolean value; or
144    /// * An address value
145    ///
146    /// Otherwise, the `Raw` bytes of the error constant are returned.
147    Rendered {
148        /// The name of the error constant.
149        identifier: String,
150        /// The value of the error constant.
151        constant: String,
152    },
153    /// If there is an error constant value, but ii is not one of the above
154    /// types, then it is treated as opaque and the bytes are returned. The
155    /// caller is responsible for determining how best to display the error
156    /// constant in this case.
157    Raw {
158        /// The name of the error constant.
159        identifier: String,
160        /// The raw (BCS) bytes of the error constant.
161        bytes: Vec<u8>,
162    },
163}
164
165#[derive(Clone, Debug)]
166pub struct Module {
167    bytecode: CompiledModule,
168
169    /// Index mapping struct names to their defining ID, and the index for their
170    /// definition in the bytecode, to speed up definition lookups.
171    struct_index: BTreeMap<String, (AccountAddress, StructDefinitionIndex)>,
172
173    /// Index mapping enum names to their defining ID and the index of their
174    /// definition in the bytecode. This speeds up definition lookups.
175    enum_index: BTreeMap<String, (AccountAddress, EnumDefinitionIndex)>,
176
177    /// Index mapping function names to the index for their definition in the
178    /// bytecode, to speed up definition lookups.
179    function_index: BTreeMap<String, FunctionDefinitionIndex>,
180}
181
182/// Deserialized representation of a struct definition.
183#[derive(Debug)]
184pub struct DataDef {
185    /// The storage ID of the package that first introduced this type.
186    pub defining_id: AccountAddress,
187
188    /// This type's abilities.
189    pub abilities: AbilitySet,
190
191    /// Ability constraints and phantom status for type parameters
192    pub type_params: Vec<DatatypeTyParameter>,
193
194    /// The internal data of the datatype. This can either be a sequence of
195    /// fields, or a sequence of variants.
196    pub data: MoveData,
197}
198
199#[derive(Debug)]
200pub enum MoveData {
201    /// Serialized representation of fields (names and deserialized signatures).
202    /// Signatures refer to packages at their runtime IDs (not their storage
203    /// ID or defining ID).
204    Struct(Vec<(String, OpenSignatureBody)>),
205
206    /// Serialized representation of variants (names and deserialized
207    /// signatures).
208    Enum(Vec<VariantDef>),
209}
210
211/// Deserialized representation of an enum definition. These are always held
212/// inside an `EnumDef`.
213#[derive(Debug)]
214pub struct VariantDef {
215    /// The name of the enum variant
216    pub name: String,
217
218    /// The serialized representation of the variant's signature. Signatures
219    /// refer to packages at their runtime IDs (not their storage ID or
220    /// defining ID).
221    pub signatures: Vec<(String, OpenSignatureBody)>,
222}
223
224/// Deserialized representation of a function definition
225#[derive(Debug)]
226pub struct FunctionDef {
227    /// Whether the function is `public`, `private` or `public(friend)`.
228    pub visibility: Visibility,
229
230    /// Whether the function is marked `entry` or not.
231    pub is_entry: bool,
232
233    /// Ability constraints for type parameters
234    pub type_params: Vec<AbilitySet>,
235
236    /// Formal parameter types.
237    pub parameters: Vec<OpenSignature>,
238
239    /// Return types.
240    pub return_: Vec<OpenSignature>,
241}
242
243/// Fully qualified struct identifier.  Uses copy-on-write strings so that when
244/// it is used as a key to a map, an instance can be created to query the map
245/// without having to allocate strings on the heap.
246#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
247pub struct DatatypeRef<'m, 'n> {
248    pub package: AccountAddress,
249    pub module: Cow<'m, str>,
250    pub name: Cow<'n, str>,
251}
252
253/// A `StructRef` that owns its strings.
254pub type DatatypeKey = DatatypeRef<'static, 'static>;
255
256#[derive(Copy, Clone, Debug)]
257pub enum Reference {
258    Immutable,
259    Mutable,
260}
261
262/// A function parameter or return signature, with its type parameters
263/// instantiated.
264#[derive(Clone, Debug)]
265pub struct Signature {
266    pub ref_: Option<Reference>,
267    pub body: TypeTag,
268}
269
270/// Deserialized representation of a type signature that could appear as a
271/// function parameter or return.
272#[derive(Clone, Debug)]
273pub struct OpenSignature {
274    pub ref_: Option<Reference>,
275    pub body: OpenSignatureBody,
276}
277
278/// Deserialized representation of a type signature that could appear as a field
279/// type for a struct.
280#[derive(Clone, Debug)]
281pub enum OpenSignatureBody {
282    Address,
283    Bool,
284    U8,
285    U16,
286    U32,
287    U64,
288    U128,
289    U256,
290    Vector(Box<OpenSignatureBody>),
291    Datatype(DatatypeKey, Vec<OpenSignatureBody>),
292    TypeParameter(u16),
293}
294
295/// Information necessary to convert a type tag into a type layout.
296#[derive(Debug, Default)]
297struct ResolutionContext<'l> {
298    /// Definitions (field information) for structs referred to by types added
299    /// to this context.
300    datatypes: BTreeMap<DatatypeKey, DataDef>,
301
302    /// Limits configuration from the calling resolver.
303    limits: Option<&'l Limits>,
304}
305
306/// Interface to abstract over access to a store of live packages.  Used to
307/// override the default store during testing.
308#[async_trait]
309pub trait PackageStore: Send + Sync + 'static {
310    /// Read package contents. Fails if `id` is not an object, not a package, or
311    /// is malformed in some way.
312    async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>>;
313}
314
315macro_rules! as_ref_impl {
316    ($type:ty) => {
317        #[async_trait]
318        impl PackageStore for $type {
319            async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
320                self.as_ref().fetch(id).await
321            }
322        }
323    };
324}
325
326as_ref_impl!(Arc<dyn PackageStore>);
327as_ref_impl!(Box<dyn PackageStore>);
328
329/// Check $value does not exceed $limit in config, if the limit config exists,
330/// returning an error containing the max value and actual value otherwise.
331macro_rules! check_max_limit {
332    ($err:ident, $config:expr; $limit:ident $op:tt $value:expr) => {
333        if let Some(l) = $config {
334            let max = l.$limit;
335            let val = $value;
336            if !(max $op val) {
337                return Err(Error::$err(max, val));
338            }
339        }
340    };
341}
342
343impl<S> Resolver<S> {
344    pub fn new(package_store: S) -> Self {
345        Self {
346            package_store,
347            limits: None,
348        }
349    }
350
351    pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
352        Self {
353            package_store,
354            limits: Some(limits),
355        }
356    }
357
358    pub fn package_store(&self) -> &S {
359        &self.package_store
360    }
361
362    pub fn package_store_mut(&mut self) -> &mut S {
363        &mut self.package_store
364    }
365}
366
367impl<S: PackageStore> Resolver<S> {
368    /// Return the type layout corresponding to the given type tag.  The layout
369    /// always refers to structs in terms of their defining ID (i.e. their
370    /// package ID always points to the first package that introduced them).
371    pub async fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
372        let mut context = ResolutionContext::new(self.limits.as_ref());
373
374        // (1). Fetch all the information from this store that is necessary to resolve
375        // types referenced by this tag.
376        context
377            .add_type_tag(
378                &mut tag,
379                &self.package_store,
380                // visit_fields
381                true,
382                // visit_phantoms
383                true,
384            )
385            .await?;
386
387        // (2). Use that information to resolve the tag into a layout.
388        let max_depth = self
389            .limits
390            .as_ref()
391            .map_or(usize::MAX, |l| l.max_move_value_depth);
392
393        Ok(context.resolve_type_layout(&tag, max_depth)?.0)
394    }
395
396    /// Return the abilities of a concrete type, based on the abilities in its
397    /// type definition, and the abilities of its concrete type parameters:
398    /// An instance of a generic type has `store`, `copy, or `drop` if its
399    /// definition has the ability, and all its non-phantom type parameters
400    /// have the ability as well. Similar rules apply for `key` except that it
401    /// requires its type parameters to have `store`.
402    pub async fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
403        let mut context = ResolutionContext::new(self.limits.as_ref());
404
405        // (1). Fetch all the information from this store that is necessary to resolve
406        // types referenced by this tag.
407        context
408            .add_type_tag(
409                &mut tag,
410                &self.package_store,
411                // visit_fields
412                false,
413                // visit_phantoms
414                false,
415            )
416            .await?;
417
418        // (2). Use that information to calculate the type's abilities.
419        context.resolve_abilities(&tag)
420    }
421
422    /// Returns the signatures of parameters to function `pkg::module::function`
423    /// in the package store, assuming the function exists.
424    pub async fn function_parameters(
425        &self,
426        pkg: AccountAddress,
427        module: &str,
428        function: &str,
429    ) -> Result<Vec<OpenSignature>> {
430        let mut context = ResolutionContext::new(self.limits.as_ref());
431
432        let package = self.package_store.fetch(pkg).await?;
433        let Some(def) = package.module(module)?.function_def(function)? else {
434            return Err(Error::FunctionNotFound(
435                pkg,
436                module.to_string(),
437                function.to_string(),
438            ));
439        };
440
441        let mut sigs = def.parameters.clone();
442
443        // (1). Fetch all the information from this store that is necessary to resolve
444        // types referenced by this tag.
445        for sig in &sigs {
446            context
447                .add_signature(
448                    sig.body.clone(),
449                    &self.package_store,
450                    package.as_ref(),
451                    // visit_fields
452                    false,
453                )
454                .await?;
455        }
456
457        // (2). Use that information to relocate package IDs in the signature.
458        for sig in &mut sigs {
459            context.relocate_signature(&mut sig.body)?;
460        }
461
462        Ok(sigs)
463    }
464
465    /// Attempts to infer the type layouts for pure inputs to the programmable
466    /// transaction.
467    ///
468    /// The returned vector contains an element for each input to `tx`. Elements
469    /// corresponding to pure inputs that are used as arguments to
470    /// transaction commands will contain `Some(layout)`. Elements for other
471    /// inputs (non-pure inputs, and unused pure inputs) will be `None`.
472    ///
473    /// Layout resolution can fail if a type/module/package doesn't exist, if
474    /// layout resolution hits a limit, or if a pure input is somehow used
475    /// in multiple conflicting occasions (with different types).
476    pub async fn pure_input_layouts(
477        &self,
478        tx: &ProgrammableTransaction,
479    ) -> Result<Vec<Option<MoveTypeLayout>>> {
480        let mut tags = vec![None; tx.inputs.len()];
481        let mut register_type = |arg: &Argument, tag: &TypeTag| {
482            let &Argument::Input(ix) = arg else {
483                return Ok(());
484            };
485
486            if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
487                return Ok(());
488            }
489
490            let Some(type_) = tags.get_mut(ix as usize) else {
491                return Ok(());
492            };
493
494            match type_ {
495                None => *type_ = Some(tag.clone()),
496                Some(prev) => {
497                    if prev != tag {
498                        return Err(Error::InputTypeConflict(ix, prev.clone(), tag.clone()));
499                    }
500                }
501            }
502
503            Ok(())
504        };
505
506        // (1). Infer type tags for pure inputs from their uses.
507        for cmd in &tx.commands {
508            match cmd {
509                Command::MoveCall(call) => {
510                    let Ok(params) = self
511                        .function_parameters(
512                            call.package.into(),
513                            call.module.as_str(),
514                            call.function.as_str(),
515                        )
516                        .await
517                    else {
518                        continue;
519                    };
520
521                    for (open_sig, arg) in params.iter().zip(call.arguments.iter()) {
522                        let sig = open_sig.instantiate(&call.type_arguments)?;
523                        register_type(arg, &sig.body)?;
524                    }
525                }
526
527                Command::TransferObjects(_, arg) => register_type(arg, &TypeTag::Address)?,
528
529                Command::SplitCoins(_, amounts) => {
530                    for amount in amounts {
531                        register_type(amount, &TypeTag::U64)?;
532                    }
533                }
534
535                Command::MakeMoveVec(Some(tag), elems) if is_primitive_type_tag(tag) => {
536                    for elem in elems {
537                        register_type(elem, tag)?;
538                    }
539                }
540
541                _ => { /* nop */ }
542            }
543        }
544
545        // (2). Gather all the unique type tags to convert into layouts. There are
546        // relatively few primitive types so this is worth doing to avoid
547        // redundant work.
548        let unique_tags: BTreeSet<_> = tags.iter().filter_map(|t| t.clone()).collect();
549
550        // (3). Convert the type tags into layouts.
551        let mut layouts = BTreeMap::new();
552        for tag in unique_tags {
553            let layout = self.type_layout(tag.clone()).await?;
554            layouts.insert(tag, layout);
555        }
556
557        // (4) Prepare the result vector.
558        Ok(tags
559            .iter()
560            .map(|t| t.as_ref().and_then(|t| layouts.get(t).cloned()))
561            .collect())
562    }
563
564    /// Resolves a runtime address in a `ModuleId` to a storage `ModuleId`
565    /// according to the linkage table in the `context` which must refer to
566    /// a package.
567    /// * Will fail if the wrong context is provided, i.e., is not a package, or
568    ///   does not exist.
569    /// * Will fail if an invalid `context` is provided for the `location`,
570    ///   i.e., the package at `context` does not contain the module that
571    ///   `location` refers to.
572    pub async fn resolve_module_id(
573        &self,
574        module_id: ModuleId,
575        context: AccountAddress,
576    ) -> Result<ModuleId> {
577        let package = self.package_store.fetch(context).await?;
578        let storage_id = package.relocate(*module_id.address())?;
579        Ok(ModuleId::new(storage_id, module_id.name().to_owned()))
580    }
581
582    /// Resolves an abort code following the clever error format to a
583    /// `CleverError` enum. The `module_id` must be the storage ID of the
584    /// module (which can e.g., be gotten from the `resolve_module_id`
585    /// function) and not the runtime ID.
586    ///
587    /// If the `abort_code` is not a clever error (i.e., does not follow the
588    /// tagging and layout as defined in `ErrorBitset`), this function will
589    /// return `None`.
590    ///
591    /// In the case where it is a clever error but only a line number is present
592    /// (i.e., the error is the result of an `assert!(<cond>)` source
593    /// expression) a `CleverError::LineNumberOnly` is returned. Otherwise a
594    /// `CleverError::CompleteError` is returned.
595    ///
596    /// If for any reason we are unable to resolve the abort code to a
597    /// `CleverError`, this function will return `None`.
598    pub async fn resolve_clever_error(
599        &self,
600        module_id: ModuleId,
601        abort_code: u64,
602    ) -> Option<CleverError> {
603        let bitset = ErrorBitset::from_u64(abort_code)?;
604        let package = self.package_store.fetch(*module_id.address()).await.ok()?;
605        let module = package.module(module_id.name().as_str()).ok()?.bytecode();
606        let source_line_number = bitset.line_number()?;
607
608        // We only have a line number in our clever error, so return early.
609        if bitset.identifier_index().is_none() && bitset.constant_index().is_none() {
610            return Some(CleverError {
611                module_id,
612                error_info: ErrorConstants::None,
613                source_line_number,
614            });
615        } else if bitset.identifier_index().is_none() || bitset.constant_index().is_none() {
616            return None;
617        }
618
619        let error_identifier_constant = module
620            .constant_pool()
621            .get(bitset.identifier_index()? as usize)?;
622        let error_value_constant = module
623            .constant_pool()
624            .get(bitset.constant_index()? as usize)?;
625
626        if !matches!(&error_identifier_constant.type_, SignatureToken::Vector(x) if x.as_ref() == &SignatureToken::U8)
627        {
628            return None;
629        };
630
631        let error_identifier = bcs::from_bytes::<Vec<u8>>(&error_identifier_constant.data)
632            .ok()
633            .and_then(|x| String::from_utf8(x).ok())?;
634        let bytes = error_value_constant.data.clone();
635
636        let rendered = try_render_constant(error_value_constant);
637
638        let error_info = match rendered {
639            RenderResult::NotRendered => ErrorConstants::Raw {
640                identifier: error_identifier,
641                bytes,
642            },
643            RenderResult::AsString(s) | RenderResult::AsValue(s) => ErrorConstants::Rendered {
644                identifier: error_identifier,
645                constant: s,
646            },
647        };
648
649        Some(CleverError {
650            module_id,
651            error_info,
652            source_line_number,
653        })
654    }
655}
656
657impl<T> PackageStoreWithLruCache<T> {
658    pub fn new(inner: T) -> Self {
659        let packages = Mutex::new(LruCache::new(PACKAGE_CACHE_SIZE));
660        Self { packages, inner }
661    }
662
663    /// Removes all packages with ids in `ids` from the cache, if they exist.
664    /// Does nothing for ids that are not in the cache. Accepts `self`
665    /// immutably as it operates under the lock.
666    pub fn evict(&self, ids: impl IntoIterator<Item = AccountAddress>) {
667        let mut packages = self.packages.lock().unwrap();
668        for id in ids {
669            packages.pop(&id);
670        }
671    }
672}
673
674#[async_trait]
675impl<T: PackageStore> PackageStore for PackageStoreWithLruCache<T> {
676    async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
677        if let Some(package) = {
678            // Release the lock after getting the package
679            let mut packages = self.packages.lock().unwrap();
680            packages.get(&id).cloned()
681        } {
682            return Ok(package);
683        };
684
685        let package = self.inner.fetch(id).await?;
686
687        // Try and insert the package into the cache, accounting for races.  In most
688        // cases the racing fetches will produce the same package, but for
689        // system packages, they may not, so favour the package that has the
690        // newer version, or if they are the same, the package that is already
691        // in the cache.
692
693        let mut packages = self.packages.lock().unwrap();
694        Ok(match packages.peek(&id) {
695            Some(prev) if package.version <= prev.version => {
696                let package = prev.clone();
697                packages.promote(&id);
698                package
699            }
700
701            Some(_) | None => {
702                packages.push(id, package.clone());
703                package
704            }
705        })
706    }
707}
708
709impl Package {
710    pub fn read_from_object(object: &Object) -> Result<Self> {
711        let storage_id = AccountAddress::from(object.id());
712        let Some(package) = object.data.try_as_package() else {
713            return Err(Error::NotAPackage(storage_id));
714        };
715
716        Self::read_from_package(package)
717    }
718
719    pub fn read_from_package(package: &MovePackage) -> Result<Self> {
720        let storage_id = AccountAddress::from(package.id());
721        let mut type_origins: BTreeMap<String, BTreeMap<String, AccountAddress>> = BTreeMap::new();
722        for TypeOrigin {
723            module_name,
724            datatype_name,
725            package,
726        } in package.type_origin_table()
727        {
728            type_origins
729                .entry(module_name.to_string())
730                .or_default()
731                .insert(datatype_name.to_string(), AccountAddress::from(*package));
732        }
733
734        let mut runtime_id = None;
735        let mut modules = BTreeMap::new();
736        for (name, bytes) in package.serialized_module_map() {
737            let origins = type_origins.remove(name).unwrap_or_default();
738            let bytecode = CompiledModule::deserialize_with_defaults(bytes)
739                .map_err(|e| Error::Deserialize(e.finish(Location::Undefined)))?;
740
741            runtime_id = Some(*bytecode.address());
742
743            let name = name.clone();
744            match Module::read(bytecode, origins) {
745                Ok(module) => modules.insert(name, module),
746                Err(struct_) => return Err(Error::NoTypeOrigin(storage_id, name, struct_)),
747            };
748        }
749
750        let Some(runtime_id) = runtime_id else {
751            return Err(Error::EmptyPackage(storage_id));
752        };
753
754        let linkage = package
755            .linkage_table()
756            .iter()
757            .map(|(&dep, linkage)| (dep.into(), linkage.upgraded_id.into()))
758            .collect();
759
760        Ok(Package {
761            storage_id,
762            runtime_id,
763            version: package.version(),
764            modules,
765            linkage,
766        })
767    }
768
769    pub fn module(&self, module: &str) -> Result<&Module> {
770        self.modules
771            .get(module)
772            .ok_or_else(|| Error::ModuleNotFound(self.storage_id, module.to_string()))
773    }
774
775    pub fn modules(&self) -> &BTreeMap<String, Module> {
776        &self.modules
777    }
778
779    fn data_def(&self, module_name: &str, datatype_name: &str) -> Result<DataDef> {
780        let module = self.module(module_name)?;
781        let Some(data_def) = module.data_def(datatype_name)? else {
782            return Err(Error::DatatypeNotFound(
783                self.storage_id,
784                module_name.to_string(),
785                datatype_name.to_string(),
786            ));
787        };
788        Ok(data_def)
789    }
790
791    /// Translate the `runtime_id` of a package to a specific storage ID using
792    /// this package's linkage table.  Returns an error if the package in
793    /// question is not present in the linkage table.
794    fn relocate(&self, runtime_id: AccountAddress) -> Result<AccountAddress> {
795        // Special case the current package, because it doesn't get an entry in the
796        // linkage table.
797        if runtime_id == self.runtime_id {
798            return Ok(self.storage_id);
799        }
800
801        self.linkage
802            .get(&runtime_id)
803            .ok_or_else(|| Error::LinkageNotFound(runtime_id))
804            .copied()
805    }
806}
807
808impl Module {
809    /// Deserialize a module from its bytecode, and a table containing the
810    /// origins of its structs. Fails if the origin table is missing an
811    /// entry for one of its types, returning the name of the type in that
812    /// case.
813    fn read(
814        bytecode: CompiledModule,
815        mut origins: BTreeMap<String, AccountAddress>,
816    ) -> std::result::Result<Self, String> {
817        let mut struct_index = BTreeMap::new();
818        for (index, def) in bytecode.struct_defs.iter().enumerate() {
819            let sh = bytecode.datatype_handle_at(def.struct_handle);
820            let struct_ = bytecode.identifier_at(sh.name).to_string();
821            let index = StructDefinitionIndex::new(index as TableIndex);
822
823            let Some(defining_id) = origins.remove(&struct_) else {
824                return Err(struct_);
825            };
826
827            struct_index.insert(struct_, (defining_id, index));
828        }
829
830        let mut enum_index = BTreeMap::new();
831        for (index, def) in bytecode.enum_defs.iter().enumerate() {
832            let eh = bytecode.datatype_handle_at(def.enum_handle);
833            let enum_ = bytecode.identifier_at(eh.name).to_string();
834            let index = EnumDefinitionIndex::new(index as TableIndex);
835
836            let Some(defining_id) = origins.remove(&enum_) else {
837                return Err(enum_);
838            };
839
840            enum_index.insert(enum_, (defining_id, index));
841        }
842
843        let mut function_index = BTreeMap::new();
844        for (index, def) in bytecode.function_defs.iter().enumerate() {
845            let fh = bytecode.function_handle_at(def.function);
846            let function = bytecode.identifier_at(fh.name).to_string();
847            let index = FunctionDefinitionIndex::new(index as TableIndex);
848
849            function_index.insert(function, index);
850        }
851
852        Ok(Module {
853            bytecode,
854            struct_index,
855            enum_index,
856            function_index,
857        })
858    }
859
860    pub fn bytecode(&self) -> &CompiledModule {
861        &self.bytecode
862    }
863
864    /// The module's name
865    pub fn name(&self) -> &str {
866        self.bytecode
867            .identifier_at(self.bytecode.self_handle().name)
868            .as_str()
869    }
870
871    /// Iterate over the structs with names strictly after `after` (or from the
872    /// beginning), and strictly before `before` (or to the end).
873    pub fn structs(
874        &self,
875        after: Option<&str>,
876        before: Option<&str>,
877    ) -> impl DoubleEndedIterator<Item = &str> + Clone {
878        use std::ops::Bound as B;
879        self.struct_index
880            .range::<str, _>((
881                after.map_or(B::Unbounded, B::Excluded),
882                before.map_or(B::Unbounded, B::Excluded),
883            ))
884            .map(|(name, _)| name.as_str())
885    }
886
887    /// Iterate over the enums with names strictly after `after` (or from the
888    /// beginning), and strictly before `before` (or to the end).
889    pub fn enums(
890        &self,
891        after: Option<&str>,
892        before: Option<&str>,
893    ) -> impl DoubleEndedIterator<Item = &str> + Clone {
894        use std::ops::Bound as B;
895        self.enum_index
896            .range::<str, _>((
897                after.map_or(B::Unbounded, B::Excluded),
898                before.map_or(B::Unbounded, B::Excluded),
899            ))
900            .map(|(name, _)| name.as_str())
901    }
902
903    /// Iterate over the datatypes with names strictly after `after` (or from
904    /// the beginning), and strictly before `before` (or to the end). Enums
905    /// and structs will be interleaved, and will be sorted by their names.
906    pub fn datatypes(
907        &self,
908        after: Option<&str>,
909        before: Option<&str>,
910    ) -> impl DoubleEndedIterator<Item = &str> + Clone {
911        let mut names = self
912            .structs(after, before)
913            .chain(self.enums(after, before))
914            .collect::<Vec<_>>();
915        names.sort();
916        names.into_iter()
917    }
918
919    /// Get the struct definition corresponding to the struct with name `name`
920    /// in this module. Returns `Ok(None)` if the struct cannot be found in
921    /// this module, `Err(...)` if there was an error deserializing it, and
922    /// `Ok(Some(def))` on success.
923    pub fn struct_def(&self, name: &str) -> Result<Option<DataDef>> {
924        let Some(&(defining_id, index)) = self.struct_index.get(name) else {
925            return Ok(None);
926        };
927
928        let struct_def = self.bytecode.struct_def_at(index);
929        let struct_handle = self.bytecode.datatype_handle_at(struct_def.struct_handle);
930        let abilities = struct_handle.abilities;
931        let type_params = struct_handle.type_parameters.clone();
932
933        let fields = match &struct_def.field_information {
934            StructFieldInformation::Native => vec![],
935            StructFieldInformation::Declared(fields) => fields
936                .iter()
937                .map(|f| {
938                    Ok((
939                        self.bytecode.identifier_at(f.name).to_string(),
940                        OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
941                    ))
942                })
943                .collect::<Result<_>>()?,
944        };
945
946        Ok(Some(DataDef {
947            defining_id,
948            abilities,
949            type_params,
950            data: MoveData::Struct(fields),
951        }))
952    }
953
954    /// Get the enum definition corresponding to the enum with name `name` in
955    /// this module. Returns `Ok(None)` if the enum cannot be found in this
956    /// module, `Err(...)` if there was an error deserializing it, and
957    /// `Ok(Some(def))` on success.
958    pub fn enum_def(&self, name: &str) -> Result<Option<DataDef>> {
959        let Some(&(defining_id, index)) = self.enum_index.get(name) else {
960            return Ok(None);
961        };
962
963        let enum_def = self.bytecode.enum_def_at(index);
964        let enum_handle = self.bytecode.datatype_handle_at(enum_def.enum_handle);
965        let abilities = enum_handle.abilities;
966        let type_params = enum_handle.type_parameters.clone();
967
968        let variants = enum_def
969            .variants
970            .iter()
971            .map(|variant| {
972                let name = self
973                    .bytecode
974                    .identifier_at(variant.variant_name)
975                    .to_string();
976                let signatures = variant
977                    .fields
978                    .iter()
979                    .map(|f| {
980                        Ok((
981                            self.bytecode.identifier_at(f.name).to_string(),
982                            OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
983                        ))
984                    })
985                    .collect::<Result<_>>()?;
986
987                Ok(VariantDef { name, signatures })
988            })
989            .collect::<Result<_>>()?;
990
991        Ok(Some(DataDef {
992            defining_id,
993            abilities,
994            type_params,
995            data: MoveData::Enum(variants),
996        }))
997    }
998
999    /// Get the data definition corresponding to the data type with name `name`
1000    /// in this module. Returns `Ok(None)` if the datatype cannot be found
1001    /// in this module, `Err(...)` if there was an error deserializing it,
1002    /// and `Ok(Some(def))` on success.
1003    pub fn data_def(&self, name: &str) -> Result<Option<DataDef>> {
1004        self.struct_def(name)
1005            .transpose()
1006            .or_else(|| self.enum_def(name).transpose())
1007            .transpose()
1008    }
1009
1010    /// Iterate over the functions with names strictly after `after` (or from
1011    /// the beginning), and strictly before `before` (or to the end).
1012    pub fn functions(
1013        &self,
1014        after: Option<&str>,
1015        before: Option<&str>,
1016    ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1017        use std::ops::Bound as B;
1018        self.function_index
1019            .range::<str, _>((
1020                after.map_or(B::Unbounded, B::Excluded),
1021                before.map_or(B::Unbounded, B::Excluded),
1022            ))
1023            .map(|(name, _)| name.as_str())
1024    }
1025
1026    /// Get the function definition corresponding to the function with name
1027    /// `name` in this module. Returns `Ok(None)` if the function cannot be
1028    /// found in this module, `Err(...)` if there was an error deserializing
1029    /// it, and `Ok(Some(def))` on success.
1030    pub fn function_def(&self, name: &str) -> Result<Option<FunctionDef>> {
1031        let Some(&index) = self.function_index.get(name) else {
1032            return Ok(None);
1033        };
1034
1035        let function_def = self.bytecode.function_def_at(index);
1036        let function_handle = self.bytecode.function_handle_at(function_def.function);
1037
1038        Ok(Some(FunctionDef {
1039            visibility: function_def.visibility,
1040            is_entry: function_def.is_entry,
1041            type_params: function_handle.type_parameters.clone(),
1042            parameters: read_signature(function_handle.parameters, &self.bytecode)?,
1043            return_: read_signature(function_handle.return_, &self.bytecode)?,
1044        }))
1045    }
1046}
1047
1048impl OpenSignature {
1049    fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1050        use SignatureToken as S;
1051        Ok(match sig {
1052            S::Reference(sig) => OpenSignature {
1053                ref_: Some(Reference::Immutable),
1054                body: OpenSignatureBody::read(sig, bytecode)?,
1055            },
1056
1057            S::MutableReference(sig) => OpenSignature {
1058                ref_: Some(Reference::Mutable),
1059                body: OpenSignatureBody::read(sig, bytecode)?,
1060            },
1061
1062            sig => OpenSignature {
1063                ref_: None,
1064                body: OpenSignatureBody::read(sig, bytecode)?,
1065            },
1066        })
1067    }
1068
1069    /// Return a specific instantiation of this signature, with `type_params` as
1070    /// the actual type parameters. This function does not check that the
1071    /// supplied type parameters are valid (meet the ability constraints of
1072    /// the struct or function this signature is part of), but will
1073    /// produce an error if the signature references a type parameter that is
1074    /// out of bounds.
1075    pub fn instantiate(&self, type_params: &[TypeTag]) -> Result<Signature> {
1076        Ok(Signature {
1077            ref_: self.ref_,
1078            body: self.body.instantiate(type_params)?,
1079        })
1080    }
1081}
1082
1083impl OpenSignatureBody {
1084    fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1085        use OpenSignatureBody as O;
1086        use SignatureToken as S;
1087
1088        Ok(match sig {
1089            S::Signer => return Err(Error::UnexpectedSigner),
1090            S::Reference(_) | S::MutableReference(_) => return Err(Error::UnexpectedReference),
1091
1092            S::Address => O::Address,
1093            S::Bool => O::Bool,
1094            S::U8 => O::U8,
1095            S::U16 => O::U16,
1096            S::U32 => O::U32,
1097            S::U64 => O::U64,
1098            S::U128 => O::U128,
1099            S::U256 => O::U256,
1100            S::TypeParameter(ix) => O::TypeParameter(*ix),
1101
1102            S::Vector(sig) => O::Vector(Box::new(OpenSignatureBody::read(sig, bytecode)?)),
1103
1104            S::Datatype(ix) => O::Datatype(DatatypeKey::read(*ix, bytecode), vec![]),
1105            S::DatatypeInstantiation(inst) => {
1106                let (ix, params) = &**inst;
1107                O::Datatype(
1108                    DatatypeKey::read(*ix, bytecode),
1109                    params
1110                        .iter()
1111                        .map(|sig| OpenSignatureBody::read(sig, bytecode))
1112                        .collect::<Result<_>>()?,
1113                )
1114            }
1115        })
1116    }
1117
1118    fn instantiate(&self, type_params: &[TypeTag]) -> Result<TypeTag> {
1119        use OpenSignatureBody as O;
1120        use TypeTag as T;
1121
1122        Ok(match self {
1123            O::Address => T::Address,
1124            O::Bool => T::Bool,
1125            O::U8 => T::U8,
1126            O::U16 => T::U16,
1127            O::U32 => T::U32,
1128            O::U64 => T::U64,
1129            O::U128 => T::U128,
1130            O::U256 => T::U256,
1131            O::Vector(s) => T::Vector(Box::new(s.instantiate(type_params)?)),
1132
1133            O::Datatype(key, dty_params) => T::Struct(Box::new(StructTag {
1134                address: key.package,
1135                module: ident(&key.module)?,
1136                name: ident(&key.name)?,
1137                type_params: dty_params
1138                    .iter()
1139                    .map(|p| p.instantiate(type_params))
1140                    .collect::<Result<_>>()?,
1141            })),
1142
1143            O::TypeParameter(ix) => type_params
1144                .get(*ix as usize)
1145                .cloned()
1146                .ok_or_else(|| Error::TypeParamOOB(*ix, type_params.len()))?,
1147        })
1148    }
1149}
1150
1151impl DatatypeRef<'_, '_> {
1152    pub fn as_key(&self) -> DatatypeKey {
1153        DatatypeKey {
1154            package: self.package,
1155            module: self.module.to_string().into(),
1156            name: self.name.to_string().into(),
1157        }
1158    }
1159}
1160
1161impl DatatypeKey {
1162    fn read(ix: DatatypeHandleIndex, bytecode: &CompiledModule) -> Self {
1163        let sh = bytecode.datatype_handle_at(ix);
1164        let mh = bytecode.module_handle_at(sh.module);
1165
1166        let package = *bytecode.address_identifier_at(mh.address);
1167        let module = bytecode.identifier_at(mh.name).to_string().into();
1168        let name = bytecode.identifier_at(sh.name).to_string().into();
1169
1170        DatatypeKey {
1171            package,
1172            module,
1173            name,
1174        }
1175    }
1176}
1177
1178impl<'l> ResolutionContext<'l> {
1179    fn new(limits: Option<&'l Limits>) -> Self {
1180        ResolutionContext {
1181            datatypes: BTreeMap::new(),
1182            limits,
1183        }
1184    }
1185
1186    /// Gather definitions for types that contribute to the definition of `tag`
1187    /// into this resolution context, fetching data from the `store` as
1188    /// necessary. Also updates package addresses in `tag` to point to
1189    /// runtime IDs instead of storage IDs to ensure queries made using these
1190    /// addresses during the subsequent resolution phase find the relevant type
1191    /// information in the context.
1192    ///
1193    /// The `visit_fields` flag controls whether the traversal looks inside
1194    /// types at their fields (which is necessary for layout resolution) or
1195    /// not (only explores the outer type and any type parameters).
1196    ///
1197    /// The `visit_phantoms` flag controls whether the traversal recurses
1198    /// through phantom type parameters (which is also necessary for type
1199    /// resolution) or not.
1200    async fn add_type_tag<S: PackageStore + ?Sized>(
1201        &mut self,
1202        tag: &mut TypeTag,
1203        store: &S,
1204        visit_fields: bool,
1205        visit_phantoms: bool,
1206    ) -> Result<()> {
1207        use TypeTag as T;
1208
1209        struct ToVisit<'t> {
1210            tag: &'t mut TypeTag,
1211            depth: usize,
1212        }
1213
1214        let mut frontier = vec![ToVisit { tag, depth: 0 }];
1215        while let Some(ToVisit { tag, depth }) = frontier.pop() {
1216            macro_rules! push_ty_param {
1217                ($tag:expr) => {{
1218                    check_max_limit!(
1219                        TypeParamNesting, self.limits;
1220                        max_type_argument_depth > depth
1221                    );
1222
1223                    frontier.push(ToVisit { tag: $tag, depth: depth + 1 })
1224                }}
1225            }
1226
1227            match tag {
1228                T::Address
1229                | T::Bool
1230                | T::U8
1231                | T::U16
1232                | T::U32
1233                | T::U64
1234                | T::U128
1235                | T::U256
1236                | T::Signer => {
1237                    // Nothing further to add to context
1238                }
1239
1240                T::Vector(tag) => push_ty_param!(tag),
1241
1242                T::Struct(s) => {
1243                    let context = store.fetch(s.address).await?;
1244                    let def = context
1245                        .clone()
1246                        .data_def(s.module.as_str(), s.name.as_str())?;
1247
1248                    // Normalize `address` (the ID of a package that contains the definition of this
1249                    // struct) to be a runtime ID, because that's what the resolution context uses
1250                    // for keys.  Take care to do this before generating the key that is used to
1251                    // query and/or write into `self.structs.
1252                    s.address = context.runtime_id;
1253                    let key = DatatypeRef::from(s.as_ref()).as_key();
1254
1255                    if def.type_params.len() != s.type_params.len() {
1256                        return Err(Error::TypeArityMismatch(
1257                            def.type_params.len(),
1258                            s.type_params.len(),
1259                        ));
1260                    }
1261
1262                    check_max_limit!(
1263                        TooManyTypeParams, self.limits;
1264                        max_type_argument_width >= s.type_params.len()
1265                    );
1266
1267                    for (param, def) in s.type_params.iter_mut().zip(def.type_params.iter()) {
1268                        if !def.is_phantom || visit_phantoms {
1269                            push_ty_param!(param);
1270                        }
1271                    }
1272
1273                    if self.datatypes.contains_key(&key) {
1274                        continue;
1275                    }
1276
1277                    if visit_fields {
1278                        match &def.data {
1279                            MoveData::Struct(fields) => {
1280                                for (_, sig) in fields {
1281                                    self.add_signature(sig.clone(), store, &context, visit_fields)
1282                                        .await?;
1283                                }
1284                            }
1285                            MoveData::Enum(variants) => {
1286                                for variant in variants {
1287                                    for (_, sig) in &variant.signatures {
1288                                        self.add_signature(
1289                                            sig.clone(),
1290                                            store,
1291                                            &context,
1292                                            visit_fields,
1293                                        )
1294                                        .await?;
1295                                    }
1296                                }
1297                            }
1298                        };
1299                    }
1300
1301                    check_max_limit!(
1302                        TooManyTypeNodes, self.limits;
1303                        max_type_nodes > self.datatypes.len()
1304                    );
1305
1306                    self.datatypes.insert(key, def);
1307                }
1308            }
1309        }
1310
1311        Ok(())
1312    }
1313
1314    // Like `add_type_tag` but for type signatures.  Needs a linkage table to
1315    // translate runtime IDs into storage IDs.
1316    async fn add_signature<T: PackageStore + ?Sized>(
1317        &mut self,
1318        sig: OpenSignatureBody,
1319        store: &T,
1320        context: &Package,
1321        visit_fields: bool,
1322    ) -> Result<()> {
1323        use OpenSignatureBody as O;
1324
1325        let mut frontier = vec![sig];
1326        while let Some(sig) = frontier.pop() {
1327            match sig {
1328                O::Address
1329                | O::Bool
1330                | O::U8
1331                | O::U16
1332                | O::U32
1333                | O::U64
1334                | O::U128
1335                | O::U256
1336                | O::TypeParameter(_) => {
1337                    // Nothing further to add to context
1338                }
1339
1340                O::Vector(sig) => frontier.push(*sig),
1341
1342                O::Datatype(key, params) => {
1343                    check_max_limit!(
1344                        TooManyTypeParams, self.limits;
1345                        max_type_argument_width >= params.len()
1346                    );
1347
1348                    let params_count = params.len();
1349                    let data_count = self.datatypes.len();
1350                    frontier.extend(params.into_iter());
1351
1352                    let type_params = if let Some(def) = self.datatypes.get(&key) {
1353                        &def.type_params
1354                    } else {
1355                        check_max_limit!(
1356                            TooManyTypeNodes, self.limits;
1357                            max_type_nodes > data_count
1358                        );
1359
1360                        // Need to resolve the datatype, so fetch the package that contains it.
1361                        let storage_id = context.relocate(key.package)?;
1362                        let package = store.fetch(storage_id).await?;
1363
1364                        let def = package.data_def(&key.module, &key.name)?;
1365                        if visit_fields {
1366                            match &def.data {
1367                                MoveData::Struct(fields) => {
1368                                    frontier.extend(fields.iter().map(|f| &f.1).cloned());
1369                                }
1370                                MoveData::Enum(variants) => {
1371                                    frontier.extend(
1372                                        variants
1373                                            .iter()
1374                                            .flat_map(|v| v.signatures.iter().map(|(_, s)| s))
1375                                            .cloned(),
1376                                    );
1377                                }
1378                            };
1379                        }
1380
1381                        &self.datatypes.entry(key).or_insert(def).type_params
1382                    };
1383
1384                    if type_params.len() != params_count {
1385                        return Err(Error::TypeArityMismatch(type_params.len(), params_count));
1386                    }
1387                }
1388            }
1389        }
1390
1391        Ok(())
1392    }
1393
1394    /// Translate a type `tag` into its layout using only the information
1395    /// contained in this context. Requires that the necessary information
1396    /// was added to the context through calls to `add_type_tag` and
1397    /// `add_signature` before being called.
1398    ///
1399    /// `max_depth` controls how deep the layout is allowed to grow to. The
1400    /// actual depth reached is returned alongside the layout (assuming it
1401    /// does not exceed `max_depth`).
1402    fn resolve_type_layout(
1403        &self,
1404        tag: &TypeTag,
1405        max_depth: usize,
1406    ) -> Result<(MoveTypeLayout, usize)> {
1407        use MoveTypeLayout as L;
1408        use TypeTag as T;
1409
1410        if max_depth == 0 {
1411            return Err(Error::ValueNesting(
1412                self.limits.map_or(0, |l| l.max_move_value_depth),
1413            ));
1414        }
1415
1416        Ok(match tag {
1417            T::Signer => return Err(Error::UnexpectedSigner),
1418
1419            T::Address => (L::Address, 1),
1420            T::Bool => (L::Bool, 1),
1421            T::U8 => (L::U8, 1),
1422            T::U16 => (L::U16, 1),
1423            T::U32 => (L::U32, 1),
1424            T::U64 => (L::U64, 1),
1425            T::U128 => (L::U128, 1),
1426            T::U256 => (L::U256, 1),
1427
1428            T::Vector(tag) => {
1429                let (layout, depth) = self.resolve_type_layout(tag, max_depth - 1)?;
1430                (L::Vector(Box::new(layout)), depth + 1)
1431            }
1432
1433            T::Struct(s) => {
1434                // TODO (optimization): Could introduce a layout cache to further speed up
1435                // resolution.  Relevant entries in that cache would need to be gathered in the
1436                // ResolutionContext as it is built, and then used here to avoid the recursive
1437                // exploration.  This optimisation is complicated by the fact that in the cache,
1438                // these layouts are naturally keyed based on defining ID, but during
1439                // resolution, they are keyed by runtime IDs.
1440
1441                // TODO (optimization): This could be made more efficient by only generating
1442                // layouts for non-phantom types.  This efficiency could be
1443                // extended to the exploration phase (i.e. only explore layouts
1444                // of non-phantom types). But this optimisation is complicated
1445                // by the fact that we still need to create a correct type tag for a
1446                // phantom parameter, which is currently done by converting a type layout into a
1447                // tag.
1448                let param_layouts = s
1449                    .type_params
1450                    .iter()
1451                    // Reduce the max depth because we know these type parameters will be nested
1452                    // wthin this struct.
1453                    .map(|tag| self.resolve_type_layout(tag, max_depth - 1))
1454                    .collect::<Result<Vec<_>>>()?;
1455
1456                // SAFETY: `param_layouts` contains `MoveTypeLayout`-s that are generated by
1457                // this `ResolutionContext`, which guarantees that struct
1458                // layouts come with types, which is necessary to avoid errors
1459                // when converting layouts into type tags.
1460                let type_params = param_layouts.iter().map(|l| TypeTag::from(&l.0)).collect();
1461
1462                // SAFETY: `add_type_tag` ensures `datatyps` has an element with this key.
1463                let key = DatatypeRef::from(s.as_ref());
1464                let def = &self.datatypes[&key];
1465
1466                let type_ = StructTag {
1467                    address: def.defining_id,
1468                    module: s.module.clone(),
1469                    name: s.name.clone(),
1470                    type_params,
1471                };
1472
1473                self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1474            }
1475        })
1476    }
1477
1478    /// Translates a datatype definition into a type layout.  Needs to be
1479    /// provided the layouts of type parameters which are substituted when a
1480    /// type parameter is encountered.
1481    ///
1482    /// `max_depth` controls how deep the layout is allowed to grow to. The
1483    /// actual depth reached is returned alongside the layout (assuming it
1484    /// does not exceed `max_depth`).
1485    fn resolve_datatype_signature(
1486        &self,
1487        data_def: &DataDef,
1488        type_: StructTag,
1489        param_layouts: Vec<(MoveTypeLayout, usize)>,
1490        max_depth: usize,
1491    ) -> Result<(MoveTypeLayout, usize)> {
1492        Ok(match &data_def.data {
1493            MoveData::Struct(fields) => {
1494                let mut resolved_fields = Vec::with_capacity(fields.len());
1495                let mut field_depth = 0;
1496
1497                for (name, sig) in fields {
1498                    let (layout, depth) =
1499                        self.resolve_signature_layout(sig, &param_layouts, max_depth - 1)?;
1500
1501                    field_depth = field_depth.max(depth);
1502                    resolved_fields.push(MoveFieldLayout {
1503                        name: ident(name.as_str())?,
1504                        layout,
1505                    })
1506                }
1507
1508                (
1509                    MoveTypeLayout::Struct(Box::new(MoveStructLayout {
1510                        type_,
1511                        fields: resolved_fields,
1512                    })),
1513                    field_depth + 1,
1514                )
1515            }
1516            MoveData::Enum(variants) => {
1517                let mut field_depth = 0;
1518                let mut resolved_variants = BTreeMap::new();
1519
1520                for (tag, variant) in variants.iter().enumerate() {
1521                    let mut fields = Vec::with_capacity(variant.signatures.len());
1522                    for (name, sig) in &variant.signatures {
1523                        // Note: We decrement the depth here because we're already under the variant
1524                        let (layout, depth) =
1525                            self.resolve_signature_layout(sig, &param_layouts, max_depth - 1)?;
1526
1527                        field_depth = field_depth.max(depth);
1528                        fields.push(MoveFieldLayout {
1529                            name: ident(name.as_str())?,
1530                            layout,
1531                        })
1532                    }
1533                    resolved_variants.insert((ident(variant.name.as_str())?, tag as u16), fields);
1534                }
1535
1536                (
1537                    MoveTypeLayout::Enum(Box::new(MoveEnumLayout {
1538                        type_,
1539                        variants: resolved_variants,
1540                    })),
1541                    field_depth + 1,
1542                )
1543            }
1544        })
1545    }
1546
1547    /// Like `resolve_type_tag` but for signatures.  Needs to be provided the
1548    /// layouts of type parameters which are substituted when a type
1549    /// parameter is encountered.
1550    ///
1551    /// `max_depth` controls how deep the layout is allowed to grow to. The
1552    /// actual depth reached is returned alongside the layout (assuming it
1553    /// does not exceed `max_depth`).
1554    fn resolve_signature_layout(
1555        &self,
1556        sig: &OpenSignatureBody,
1557        param_layouts: &[(MoveTypeLayout, usize)],
1558        max_depth: usize,
1559    ) -> Result<(MoveTypeLayout, usize)> {
1560        use MoveTypeLayout as L;
1561        use OpenSignatureBody as O;
1562
1563        if max_depth == 0 {
1564            return Err(Error::ValueNesting(
1565                self.limits.map_or(0, |l| l.max_move_value_depth),
1566            ));
1567        }
1568
1569        Ok(match sig {
1570            O::Address => (L::Address, 1),
1571            O::Bool => (L::Bool, 1),
1572            O::U8 => (L::U8, 1),
1573            O::U16 => (L::U16, 1),
1574            O::U32 => (L::U32, 1),
1575            O::U64 => (L::U64, 1),
1576            O::U128 => (L::U128, 1),
1577            O::U256 => (L::U256, 1),
1578
1579            O::TypeParameter(ix) => {
1580                let (layout, depth) = param_layouts
1581                    .get(*ix as usize)
1582                    .ok_or_else(|| Error::TypeParamOOB(*ix, param_layouts.len()))
1583                    .cloned()?;
1584
1585                // We need to re-check the type parameter before we use it because it might have
1586                // been fine when it was created, but result in too deep a layout when we use it
1587                // at this position.
1588                if depth > max_depth {
1589                    return Err(Error::ValueNesting(
1590                        self.limits.map_or(0, |l| l.max_move_value_depth),
1591                    ));
1592                }
1593
1594                (layout, depth)
1595            }
1596
1597            O::Vector(sig) => {
1598                let (layout, depth) =
1599                    self.resolve_signature_layout(sig.as_ref(), param_layouts, max_depth - 1)?;
1600
1601                (L::Vector(Box::new(layout)), depth + 1)
1602            }
1603
1604            O::Datatype(key, params) => {
1605                // SAFETY: `add_signature` ensures `datatypes` has an element with this key.
1606                let def = &self.datatypes[key];
1607
1608                let param_layouts = params
1609                    .iter()
1610                    .map(|sig| self.resolve_signature_layout(sig, param_layouts, max_depth - 1))
1611                    .collect::<Result<Vec<_>>>()?;
1612
1613                // SAFETY: `param_layouts` contains `MoveTypeLayout`-s that are generated by
1614                // this `ResolutionContext`, which guarantees that struct
1615                // layouts come with types, which is necessary to avoid errors
1616                // when converting layouts into type tags.
1617                let type_params: Vec<TypeTag> =
1618                    param_layouts.iter().map(|l| TypeTag::from(&l.0)).collect();
1619
1620                let type_ = StructTag {
1621                    address: def.defining_id,
1622                    module: ident(&key.module)?,
1623                    name: ident(&key.name)?,
1624                    type_params,
1625                };
1626
1627                self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1628            }
1629        })
1630    }
1631
1632    /// Calculate the abilities for a concrete type `tag`. Requires that the
1633    /// necessary information was added to the context through calls to
1634    /// `add_type_tag` before being called.
1635    fn resolve_abilities(&self, tag: &TypeTag) -> Result<AbilitySet> {
1636        use TypeTag as T;
1637        Ok(match tag {
1638            T::Signer => return Err(Error::UnexpectedSigner),
1639
1640            T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => {
1641                AbilitySet::PRIMITIVES
1642            }
1643
1644            T::Vector(tag) => self.resolve_abilities(tag)?.intersect(AbilitySet::VECTOR),
1645
1646            T::Struct(s) => {
1647                // SAFETY: `add_type_tag` ensures `datatypes` has an element with this key.
1648                let key = DatatypeRef::from(s.as_ref());
1649                let def = &self.datatypes[&key];
1650
1651                if def.type_params.len() != s.type_params.len() {
1652                    return Err(Error::TypeArityMismatch(
1653                        def.type_params.len(),
1654                        s.type_params.len(),
1655                    ));
1656                }
1657
1658                let param_abilities: Result<Vec<AbilitySet>> = s
1659                    .type_params
1660                    .iter()
1661                    .zip(def.type_params.iter())
1662                    .map(|(p, d)| {
1663                        if d.is_phantom {
1664                            Ok(AbilitySet::EMPTY)
1665                        } else {
1666                            self.resolve_abilities(p)
1667                        }
1668                    })
1669                    .collect();
1670
1671                AbilitySet::polymorphic_abilities(
1672                    def.abilities,
1673                    def.type_params.iter().map(|p| p.is_phantom),
1674                    param_abilities?.into_iter(),
1675                )
1676                // This error is unexpected because the only reason it would fail is because of a
1677                // type parameter arity mismatch, which we check for above.
1678                .map_err(|e| Error::Unexpected(Arc::new(e)))?
1679            }
1680        })
1681    }
1682
1683    /// Translate the (runtime) package IDs in `sig` to defining IDs using only
1684    /// the information contained in this context. Requires that the
1685    /// necessary information was added to the context through calls to
1686    /// `add_signature` before being called.
1687    fn relocate_signature(&self, sig: &mut OpenSignatureBody) -> Result<()> {
1688        use OpenSignatureBody as O;
1689
1690        match sig {
1691            O::Address | O::Bool | O::U8 | O::U16 | O::U32 | O::U64 | O::U128 | O::U256 => {
1692                // nop
1693            }
1694
1695            O::TypeParameter(_) => { /* nop */ }
1696
1697            O::Vector(sig) => self.relocate_signature(sig.as_mut())?,
1698
1699            O::Datatype(key, params) => {
1700                // SAFETY: `add_signature` ensures `datatypes` has an element with this key.
1701                let defining_id = &self.datatypes[key].defining_id;
1702                for param in params {
1703                    self.relocate_signature(param)?;
1704                }
1705
1706                key.package = *defining_id;
1707            }
1708        }
1709
1710        Ok(())
1711    }
1712}
1713
1714impl<'s> From<&'s StructTag> for DatatypeRef<'s, 's> {
1715    fn from(tag: &'s StructTag) -> Self {
1716        DatatypeRef {
1717            package: tag.address,
1718            module: tag.module.as_str().into(),
1719            name: tag.name.as_str().into(),
1720        }
1721    }
1722}
1723
1724/// Translate a string into an `Identifier`, but translating errors into this
1725/// module's error type.
1726fn ident(s: &str) -> Result<Identifier> {
1727    Identifier::new(s).map_err(|_| Error::NotAnIdentifier(s.to_string()))
1728}
1729
1730/// Read and deserialize a signature index (from function parameter or return
1731/// types) into a vector of signatures.
1732fn read_signature(idx: SignatureIndex, bytecode: &CompiledModule) -> Result<Vec<OpenSignature>> {
1733    let MoveSignature(tokens) = bytecode.signature_at(idx);
1734    let mut sigs = Vec::with_capacity(tokens.len());
1735
1736    for token in tokens {
1737        sigs.push(OpenSignature::read(token, bytecode)?);
1738    }
1739
1740    Ok(sigs)
1741}
1742
1743#[cfg(test)]
1744mod tests {
1745    use std::{
1746        path::PathBuf,
1747        str::FromStr,
1748        sync::{Arc, RwLock},
1749    };
1750
1751    use async_trait::async_trait;
1752    use iota_move_build::{BuildConfig, CompiledPackage};
1753    use iota_types::{
1754        base_types::random_object_ref,
1755        error::IotaResult,
1756        transaction::{ObjectArg, ProgrammableMoveCall},
1757    };
1758    use move_binary_format::file_format::Ability;
1759    use move_compiler::compiled_unit::NamedCompiledModule;
1760    use move_core_types::ident_str;
1761
1762    use super::*;
1763
1764    fn fmt(struct_layout: MoveTypeLayout, enum_layout: MoveTypeLayout) -> String {
1765        format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",)
1766    }
1767
1768    /// Layout for a type that only refers to base types or other types in the
1769    /// same module.
1770    #[tokio::test]
1771    async fn test_simple_type() {
1772        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1773        let package_resolver = Resolver::new(cache);
1774        let struct_layout = package_resolver
1775            .type_layout(type_("0xa0::m::T0"))
1776            .await
1777            .unwrap();
1778        let enum_layout = package_resolver
1779            .type_layout(type_("0xa0::m::E0"))
1780            .await
1781            .unwrap();
1782        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1783    }
1784
1785    /// A type that refers to types from other modules in the same package.
1786    #[tokio::test]
1787    async fn test_cross_module() {
1788        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1789        let resolver = Resolver::new(cache);
1790        let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap();
1791        let enum_layout = resolver.type_layout(type_("0xa0::n::E0")).await.unwrap();
1792        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1793    }
1794
1795    /// A type that refers to types a different package.
1796    #[tokio::test]
1797    async fn test_cross_package() {
1798        let (_, cache) = package_cache([
1799            (1, build_package("a0").unwrap(), a0_types()),
1800            (1, build_package("b0").unwrap(), b0_types()),
1801        ]);
1802        let resolver = Resolver::new(cache);
1803
1804        let struct_layout = resolver.type_layout(type_("0xb0::m::T0")).await.unwrap();
1805        let enum_layout = resolver.type_layout(type_("0xb0::m::E0")).await.unwrap();
1806        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1807    }
1808
1809    /// A type from an upgraded package, mixing structs defined in the original
1810    /// package and the upgraded package.
1811    #[tokio::test]
1812    async fn test_upgraded_package() {
1813        let (_, cache) = package_cache([
1814            (1, build_package("a0").unwrap(), a0_types()),
1815            (2, build_package("a1").unwrap(), a1_types()),
1816        ]);
1817        let resolver = Resolver::new(cache);
1818
1819        let struct_layout = resolver.type_layout(type_("0xa1::n::T1")).await.unwrap();
1820        let enum_layout = resolver.type_layout(type_("0xa1::n::E1")).await.unwrap();
1821        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1822    }
1823
1824    /// A generic type instantiation where the type parameters are resolved
1825    /// relative to linkage contexts from different versions of the same
1826    /// package.
1827    #[tokio::test]
1828    async fn test_multiple_linkage_contexts() {
1829        let (_, cache) = package_cache([
1830            (1, build_package("a0").unwrap(), a0_types()),
1831            (2, build_package("a1").unwrap(), a1_types()),
1832        ]);
1833        let resolver = Resolver::new(cache);
1834
1835        let struct_layout = resolver
1836            .type_layout(type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"))
1837            .await
1838            .unwrap();
1839        let enum_layout = resolver
1840            .type_layout(type_("0xa0::m::E1<0xa0::m::E0, 0xa1::m::E3>"))
1841            .await
1842            .unwrap();
1843        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1844    }
1845
1846    /// Refer to a type, not by its defining ID, but by the ID of some later
1847    /// version of that package.  This doesn't currently work during
1848    /// execution but it simplifies making queries: A type can be referred
1849    /// to using the ID of any package that declares it, rather than only the
1850    /// package that first declared it (whose ID is its defining ID).
1851    #[tokio::test]
1852    async fn test_upgraded_package_non_defining_id() {
1853        let (_, cache) = package_cache([
1854            (1, build_package("a0").unwrap(), a0_types()),
1855            (2, build_package("a1").unwrap(), a1_types()),
1856        ]);
1857        let resolver = Resolver::new(cache);
1858
1859        let struct_layout = resolver
1860            .type_layout(type_("0xa1::m::T1<0xa1::m::T3, 0xa1::m::T0>"))
1861            .await
1862            .unwrap();
1863        let enum_layout = resolver
1864            .type_layout(type_("0xa1::m::E1<0xa1::m::E3, 0xa1::m::E0>"))
1865            .await
1866            .unwrap();
1867        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1868    }
1869
1870    /// A type that refers to a types in a relinked package.  C depends on B and
1871    /// overrides its dependency on A from v1 to v2.  The type in C refers
1872    /// to types that were defined in both B, A v1, and A v2.
1873    #[tokio::test]
1874    async fn test_relinking() {
1875        let (_, cache) = package_cache([
1876            (1, build_package("a0").unwrap(), a0_types()),
1877            (2, build_package("a1").unwrap(), a1_types()),
1878            (1, build_package("b0").unwrap(), b0_types()),
1879            (1, build_package("c0").unwrap(), c0_types()),
1880        ]);
1881        let resolver = Resolver::new(cache);
1882
1883        let struct_layout = resolver.type_layout(type_("0xc0::m::T0")).await.unwrap();
1884        let enum_layout = resolver.type_layout(type_("0xc0::m::E0")).await.unwrap();
1885        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1886    }
1887
1888    #[tokio::test]
1889    async fn test_value_nesting_boundary() {
1890        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1891
1892        let resolver = Resolver::new_with_limits(
1893            cache,
1894            Limits {
1895                max_type_argument_width: 100,
1896                max_type_argument_depth: 100,
1897                max_type_nodes: 100,
1898                max_move_value_depth: 3,
1899            },
1900        );
1901
1902        // The layout of this type is fine, because it is *just* at the correct depth.
1903        let struct_layout = resolver
1904            .type_layout(type_("0xa0::m::T1<u8, u8>"))
1905            .await
1906            .unwrap();
1907        let enum_layout = resolver
1908            .type_layout(type_("0xa0::m::E1<u8, u8>"))
1909            .await
1910            .unwrap();
1911        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1912    }
1913
1914    #[tokio::test]
1915    async fn test_err_value_nesting_simple() {
1916        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1917
1918        let resolver = Resolver::new_with_limits(
1919            cache,
1920            Limits {
1921                max_type_argument_width: 100,
1922                max_type_argument_depth: 100,
1923                max_type_nodes: 100,
1924                max_move_value_depth: 2,
1925            },
1926        );
1927
1928        // The depth limit is now too low, so this will fail.
1929        let struct_err = resolver
1930            .type_layout(type_("0xa0::m::T1<u8, u8>"))
1931            .await
1932            .unwrap_err();
1933        let enum_err = resolver
1934            .type_layout(type_("0xa0::m::E1<u8, u8>"))
1935            .await
1936            .unwrap_err();
1937        assert!(matches!(struct_err, Error::ValueNesting(2)));
1938        assert!(matches!(enum_err, Error::ValueNesting(2)));
1939    }
1940
1941    #[tokio::test]
1942    async fn test_err_value_nesting_big_type_param() {
1943        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1944
1945        let resolver = Resolver::new_with_limits(
1946            cache,
1947            Limits {
1948                max_type_argument_width: 100,
1949                max_type_argument_depth: 100,
1950                max_type_nodes: 100,
1951                max_move_value_depth: 3,
1952            },
1953        );
1954
1955        // This layout calculation will fail early because we know that the type
1956        // parameter we're calculating will eventually contribute to a layout
1957        // that exceeds the max depth.
1958        let struct_err = resolver
1959            .type_layout(type_("0xa0::m::T1<vector<vector<u8>>, u8>"))
1960            .await
1961            .unwrap_err();
1962        let enum_err = resolver
1963            .type_layout(type_("0xa0::m::E1<vector<vector<u8>>, u8>"))
1964            .await
1965            .unwrap_err();
1966        assert!(matches!(struct_err, Error::ValueNesting(3)));
1967        assert!(matches!(enum_err, Error::ValueNesting(3)));
1968    }
1969
1970    #[tokio::test]
1971    async fn test_err_value_nesting_big_phantom_type_param() {
1972        let (_, cache) = package_cache([
1973            (1, build_package("iota").unwrap(), iota_types()),
1974            (1, build_package("d0").unwrap(), d0_types()),
1975        ]);
1976
1977        let resolver = Resolver::new_with_limits(
1978            cache,
1979            Limits {
1980                max_type_argument_width: 100,
1981                max_type_argument_depth: 100,
1982                max_type_nodes: 100,
1983                max_move_value_depth: 3,
1984            },
1985        );
1986
1987        // Check that this layout request would succeed.
1988        let _ = resolver
1989            .type_layout(type_("0xd0::m::O<u8, u8>"))
1990            .await
1991            .unwrap();
1992        let _ = resolver
1993            .type_layout(type_("0xd0::m::EO<u8, u8>"))
1994            .await
1995            .unwrap();
1996
1997        // But this one fails, even though the big layout is for a phantom type
1998        // parameter. This may change in future if we optimise the way we handle
1999        // phantom type parameters to not calculate their full layout, just
2000        // their type tag.
2001        let struct_err = resolver
2002            .type_layout(type_("0xd0::m::O<u8, vector<vector<u8>>>"))
2003            .await
2004            .unwrap_err();
2005        let enum_err = resolver
2006            .type_layout(type_("0xd0::m::EO<u8, vector<vector<u8>>>"))
2007            .await
2008            .unwrap_err();
2009        assert!(matches!(struct_err, Error::ValueNesting(3)));
2010        assert!(matches!(enum_err, Error::ValueNesting(3)));
2011    }
2012
2013    #[tokio::test]
2014    async fn test_err_value_nesting_type_param_application() {
2015        let (_, cache) = package_cache([
2016            (1, build_package("iota").unwrap(), iota_types()),
2017            (1, build_package("d0").unwrap(), d0_types()),
2018        ]);
2019
2020        let resolver = Resolver::new_with_limits(
2021            cache,
2022            Limits {
2023                max_type_argument_width: 100,
2024                max_type_argument_depth: 100,
2025                max_type_nodes: 100,
2026                max_move_value_depth: 3,
2027            },
2028        );
2029
2030        // Make sure that even if all type parameters individually meet the depth
2031        // requirements, that we correctly fail if they extend the layout's
2032        // depth on application.
2033        let struct_err = resolver
2034            .type_layout(type_("0xd0::m::O<vector<u8>, u8>"))
2035            .await
2036            .unwrap_err();
2037        let enum_err = resolver
2038            .type_layout(type_("0xd0::m::EO<vector<u8>, u8>"))
2039            .await
2040            .unwrap_err();
2041
2042        assert!(matches!(struct_err, Error::ValueNesting(3)));
2043        assert!(matches!(enum_err, Error::ValueNesting(3)));
2044    }
2045
2046    #[tokio::test]
2047    async fn test_system_package_invalidation() {
2048        let (inner, cache) = package_cache([(1, build_package("s0").unwrap(), s0_types())]);
2049        let resolver = Resolver::new(cache);
2050
2051        let struct_not_found = resolver.type_layout(type_("0x1::m::T1")).await.unwrap_err();
2052        let enum_not_found = resolver.type_layout(type_("0x1::m::E1")).await.unwrap_err();
2053        assert!(matches!(struct_not_found, Error::DatatypeNotFound(_, _, _)));
2054        assert!(matches!(enum_not_found, Error::DatatypeNotFound(_, _, _)));
2055
2056        // Add a new version of the system package into the store underlying the cache.
2057        inner.write().unwrap().replace(
2058            addr("0x1"),
2059            cached_package(
2060                2,
2061                BTreeMap::new(),
2062                &build_package("s1").unwrap(),
2063                &s1_types(),
2064            ),
2065        );
2066
2067        // Evict the package from the cache
2068        resolver.package_store().evict([addr("0x1")]);
2069
2070        let struct_layout = resolver.type_layout(type_("0x1::m::T1")).await.unwrap();
2071        let enum_layout = resolver.type_layout(type_("0x1::m::E1")).await.unwrap();
2072        insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2073    }
2074
2075    #[tokio::test]
2076    async fn test_caching() {
2077        let (inner, cache) = package_cache([
2078            (1, build_package("a0").unwrap(), a0_types()),
2079            (1, build_package("s0").unwrap(), s0_types()),
2080        ]);
2081        let resolver = Resolver::new(cache);
2082
2083        assert_eq!(inner.read().unwrap().fetches, 0);
2084        let l0 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2085
2086        // Load A0.
2087        assert_eq!(inner.read().unwrap().fetches, 1);
2088
2089        // Layouts are the same, no need to reload the package.
2090        let l1 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2091        assert_eq!(format!("{l0}"), format!("{l1}"));
2092        assert_eq!(inner.read().unwrap().fetches, 1);
2093
2094        // Different type, but same package, so no extra fetch.
2095        let l2 = resolver.type_layout(type_("0xa0::m::T2")).await.unwrap();
2096        assert_ne!(format!("{l0}"), format!("{l2}"));
2097        assert_eq!(inner.read().unwrap().fetches, 1);
2098
2099        // Enum types won't trigger a fetch either.
2100        resolver.type_layout(type_("0xa0::m::E0")).await.unwrap();
2101        assert_eq!(inner.read().unwrap().fetches, 1);
2102
2103        // New package to load.
2104        let l3 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2105        assert_eq!(inner.read().unwrap().fetches, 2);
2106
2107        // Reload the same system package type, it gets fetched from cache
2108        let l4 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2109        assert_eq!(format!("{l3}"), format!("{l4}"));
2110        assert_eq!(inner.read().unwrap().fetches, 2);
2111
2112        // Reload a same system package type (enum), which will cause a version check.
2113        let el4 = resolver.type_layout(type_("0x1::m::E0")).await.unwrap();
2114        assert_ne!(format!("{el4}"), format!("{l4}"));
2115        assert_eq!(inner.read().unwrap().fetches, 2);
2116
2117        // Upgrade the system package
2118        inner.write().unwrap().replace(
2119            addr("0x1"),
2120            cached_package(
2121                2,
2122                BTreeMap::new(),
2123                &build_package("s1").unwrap(),
2124                &s1_types(),
2125            ),
2126        );
2127
2128        // Evict the package from the cache
2129        resolver.package_store().evict([addr("0x1")]);
2130
2131        // Reload the system system type again. It will be refetched (even though the
2132        // type is the same as before). This usage pattern (layouts for system
2133        // types) is why a layout cache would be particularly helpful (future
2134        // optimisation).
2135        let l5 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2136        assert_eq!(format!("{l4}"), format!("{l5}"));
2137        assert_eq!(inner.read().unwrap().fetches, 3);
2138    }
2139
2140    #[tokio::test]
2141    async fn test_err_not_a_package() {
2142        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2143        let resolver = Resolver::new(cache);
2144        let err = resolver
2145            .type_layout(type_("0x42::m::T0"))
2146            .await
2147            .unwrap_err();
2148        assert!(matches!(err, Error::PackageNotFound(_)));
2149    }
2150
2151    #[tokio::test]
2152    async fn test_err_no_module() {
2153        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2154        let resolver = Resolver::new(cache);
2155        let err = resolver
2156            .type_layout(type_("0xa0::l::T0"))
2157            .await
2158            .unwrap_err();
2159        assert!(matches!(err, Error::ModuleNotFound(_, _)));
2160    }
2161
2162    #[tokio::test]
2163    async fn test_err_no_struct() {
2164        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2165        let resolver = Resolver::new(cache);
2166
2167        let err = resolver
2168            .type_layout(type_("0xa0::m::T9"))
2169            .await
2170            .unwrap_err();
2171        assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
2172    }
2173
2174    #[tokio::test]
2175    async fn test_err_type_arity() {
2176        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2177        let resolver = Resolver::new(cache);
2178
2179        // Too few
2180        let err = resolver
2181            .type_layout(type_("0xa0::m::T1<u8>"))
2182            .await
2183            .unwrap_err();
2184        assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2185
2186        // Too many
2187        let err = resolver
2188            .type_layout(type_("0xa0::m::T1<u8, u16, u32>"))
2189            .await
2190            .unwrap_err();
2191        assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2192    }
2193
2194    #[tokio::test]
2195    async fn test_structs() {
2196        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2197        let a0 = cache.fetch(addr("0xa0")).await.unwrap();
2198        let m = a0.module("m").unwrap();
2199
2200        assert_eq!(
2201            m.structs(None, None).collect::<Vec<_>>(),
2202            vec!["T0", "T1", "T2"],
2203        );
2204
2205        assert_eq!(m.structs(None, Some("T1")).collect::<Vec<_>>(), vec!["T0"],);
2206
2207        assert_eq!(
2208            m.structs(Some("T0"), Some("T2")).collect::<Vec<_>>(),
2209            vec!["T1"],
2210        );
2211
2212        assert_eq!(m.structs(Some("T1"), None).collect::<Vec<_>>(), vec!["T2"],);
2213
2214        let t0 = m.struct_def("T0").unwrap().unwrap();
2215        let t1 = m.struct_def("T1").unwrap().unwrap();
2216        let t2 = m.struct_def("T2").unwrap().unwrap();
2217
2218        insta::assert_snapshot!(format!(
2219            "a0::m::T0: {t0:#?}\n\
2220             a0::m::T1: {t1:#?}\n\
2221             a0::m::T2: {t2:#?}",
2222        ));
2223    }
2224
2225    #[tokio::test]
2226    async fn test_enums() {
2227        let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2228        let a0 = cache
2229            .fetch(AccountAddress::from_str("0xa0").unwrap())
2230            .await
2231            .unwrap();
2232        let m = a0.module("m").unwrap();
2233
2234        assert_eq!(
2235            m.enums(None, None).collect::<Vec<_>>(),
2236            vec!["E0", "E1", "E2"],
2237        );
2238
2239        assert_eq!(m.enums(None, Some("E1")).collect::<Vec<_>>(), vec!["E0"],);
2240
2241        assert_eq!(
2242            m.enums(Some("E0"), Some("E2")).collect::<Vec<_>>(),
2243            vec!["E1"],
2244        );
2245
2246        assert_eq!(m.enums(Some("E1"), None).collect::<Vec<_>>(), vec!["E2"],);
2247
2248        let e0 = m.enum_def("E0").unwrap().unwrap();
2249        let e1 = m.enum_def("E1").unwrap().unwrap();
2250        let e2 = m.enum_def("E2").unwrap().unwrap();
2251
2252        insta::assert_snapshot!(format!(
2253            "a0::m::E0: {e0:#?}\n\
2254             a0::m::E1: {e1:#?}\n\
2255             a0::m::E2: {e2:#?}",
2256        ));
2257    }
2258
2259    #[tokio::test]
2260    async fn test_functions() {
2261        let (_, cache) = package_cache([
2262            (1, build_package("a0").unwrap(), a0_types()),
2263            (2, build_package("a1").unwrap(), a1_types()),
2264            (1, build_package("b0").unwrap(), b0_types()),
2265            (1, build_package("c0").unwrap(), c0_types()),
2266        ]);
2267
2268        let c0 = cache.fetch(addr("0xc0")).await.unwrap();
2269        let m = c0.module("m").unwrap();
2270
2271        assert_eq!(
2272            m.functions(None, None).collect::<Vec<_>>(),
2273            vec!["bar", "baz", "foo"],
2274        );
2275
2276        assert_eq!(
2277            m.functions(None, Some("baz")).collect::<Vec<_>>(),
2278            vec!["bar"],
2279        );
2280
2281        assert_eq!(
2282            m.functions(Some("bar"), Some("foo")).collect::<Vec<_>>(),
2283            vec!["baz"],
2284        );
2285
2286        assert_eq!(
2287            m.functions(Some("baz"), None).collect::<Vec<_>>(),
2288            vec!["foo"],
2289        );
2290
2291        let foo = m.function_def("foo").unwrap().unwrap();
2292        let bar = m.function_def("bar").unwrap().unwrap();
2293        let baz = m.function_def("baz").unwrap().unwrap();
2294
2295        insta::assert_snapshot!(format!(
2296            "c0::m::foo: {foo:#?}\n\
2297             c0::m::bar: {bar:#?}\n\
2298             c0::m::baz: {baz:#?}"
2299        ));
2300    }
2301
2302    #[tokio::test]
2303    async fn test_function_parameters() {
2304        let (_, cache) = package_cache([
2305            (1, build_package("a0").unwrap(), a0_types()),
2306            (2, build_package("a1").unwrap(), a1_types()),
2307            (1, build_package("b0").unwrap(), b0_types()),
2308            (1, build_package("c0").unwrap(), c0_types()),
2309        ]);
2310
2311        let resolver = Resolver::new(cache);
2312        let c0 = addr("0xc0");
2313
2314        let foo = resolver.function_parameters(c0, "m", "foo").await.unwrap();
2315        let bar = resolver.function_parameters(c0, "m", "bar").await.unwrap();
2316        let baz = resolver.function_parameters(c0, "m", "baz").await.unwrap();
2317
2318        insta::assert_snapshot!(format!(
2319            "c0::m::foo: {foo:#?}\n\
2320             c0::m::bar: {bar:#?}\n\
2321             c0::m::baz: {baz:#?}"
2322        ));
2323    }
2324
2325    #[tokio::test]
2326    async fn test_signature_instantiation() {
2327        use OpenSignatureBody as O;
2328        use TypeTag as T;
2329
2330        let sig = O::Datatype(
2331            key("0x2::table::Table"),
2332            vec![
2333                O::TypeParameter(1),
2334                O::Vector(Box::new(O::Datatype(
2335                    key("0x1::option::Option"),
2336                    vec![O::TypeParameter(0)],
2337                ))),
2338            ],
2339        );
2340
2341        insta::assert_debug_snapshot!(sig.instantiate(&[T::U64, T::Bool]).unwrap());
2342    }
2343
2344    #[tokio::test]
2345    async fn test_signature_instantiation_error() {
2346        use OpenSignatureBody as O;
2347        use TypeTag as T;
2348
2349        let sig = O::Datatype(
2350            key("0x2::table::Table"),
2351            vec![
2352                O::TypeParameter(1),
2353                O::Vector(Box::new(O::Datatype(
2354                    key("0x1::option::Option"),
2355                    vec![O::TypeParameter(99)],
2356                ))),
2357            ],
2358        );
2359
2360        insta::assert_snapshot!(
2361            sig.instantiate(&[T::U64, T::Bool]).unwrap_err(),
2362            @"Type Parameter 99 out of bounds (2)"
2363        );
2364    }
2365
2366    /// Primitive types should have the expected primitive abilities
2367    #[tokio::test]
2368    async fn test_primitive_abilities() {
2369        use Ability as A;
2370        use AbilitySet as S;
2371
2372        let (_, cache) = package_cache([]);
2373        let resolver = Resolver::new(cache);
2374
2375        for prim in ["address", "bool", "u8", "u16", "u32", "u64", "u128", "u256"] {
2376            assert_eq!(
2377                resolver.abilities(type_(prim)).await.unwrap(),
2378                S::EMPTY | A::Copy | A::Drop | A::Store,
2379                "Unexpected primitive abilities for: {prim}",
2380            );
2381        }
2382    }
2383
2384    /// Generic type abilities depend on the abilities of their type parameters.
2385    #[tokio::test]
2386    async fn test_simple_generic_abilities() {
2387        use Ability as A;
2388        use AbilitySet as S;
2389
2390        let (_, cache) = package_cache([
2391            (1, build_package("iota").unwrap(), iota_types()),
2392            (1, build_package("d0").unwrap(), d0_types()),
2393        ]);
2394        let resolver = Resolver::new(cache);
2395
2396        let a1 = resolver
2397            .abilities(type_("0xd0::m::T<u32, u64>"))
2398            .await
2399            .unwrap();
2400        assert_eq!(a1, S::EMPTY | A::Copy | A::Drop | A::Store);
2401
2402        let a2 = resolver
2403            .abilities(type_("0xd0::m::T<0xd0::m::S, u64>"))
2404            .await
2405            .unwrap();
2406        assert_eq!(a2, S::EMPTY | A::Drop | A::Store);
2407
2408        let a3 = resolver
2409            .abilities(type_("0xd0::m::T<0xd0::m::R, 0xd0::m::S>"))
2410            .await
2411            .unwrap();
2412        assert_eq!(a3, S::EMPTY | A::Drop);
2413
2414        let a4 = resolver
2415            .abilities(type_("0xd0::m::T<0xd0::m::Q, 0xd0::m::R>"))
2416            .await
2417            .unwrap();
2418        assert_eq!(a4, S::EMPTY);
2419    }
2420
2421    /// Generic abilities also need to handle nested type parameters
2422    #[tokio::test]
2423    async fn test_nested_generic_abilities() {
2424        use Ability as A;
2425        use AbilitySet as S;
2426
2427        let (_, cache) = package_cache([
2428            (1, build_package("iota").unwrap(), iota_types()),
2429            (1, build_package("d0").unwrap(), d0_types()),
2430        ]);
2431        let resolver = Resolver::new(cache);
2432
2433        let a1 = resolver
2434            .abilities(type_("0xd0::m::T<0xd0::m::T<0xd0::m::R, u32>, u64>"))
2435            .await
2436            .unwrap();
2437        assert_eq!(a1, S::EMPTY | A::Copy | A::Drop);
2438    }
2439
2440    /// Key is different from other abilities in that it requires fields to have
2441    /// `store`, rather than itself.
2442    #[tokio::test]
2443    async fn test_key_abilities() {
2444        use Ability as A;
2445        use AbilitySet as S;
2446
2447        let (_, cache) = package_cache([
2448            (1, build_package("iota").unwrap(), iota_types()),
2449            (1, build_package("d0").unwrap(), d0_types()),
2450        ]);
2451        let resolver = Resolver::new(cache);
2452
2453        let a1 = resolver
2454            .abilities(type_("0xd0::m::O<u32, u64>"))
2455            .await
2456            .unwrap();
2457        assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2458
2459        let a2 = resolver
2460            .abilities(type_("0xd0::m::O<0xd0::m::S, u64>"))
2461            .await
2462            .unwrap();
2463        assert_eq!(a2, S::EMPTY | A::Key | A::Store);
2464
2465        // We would not be able to get an instance of this type, but in case the
2466        // question is asked, its abilities would be empty.
2467        let a3 = resolver
2468            .abilities(type_("0xd0::m::O<0xd0::m::R, u64>"))
2469            .await
2470            .unwrap();
2471        assert_eq!(a3, S::EMPTY);
2472
2473        // Key does not propagate up by itself, so this type is also uninhabitable.
2474        let a4 = resolver
2475            .abilities(type_("0xd0::m::O<0xd0::m::P, u32>"))
2476            .await
2477            .unwrap();
2478        assert_eq!(a4, S::EMPTY);
2479    }
2480
2481    /// Phantom types don't impact abilities
2482    #[tokio::test]
2483    async fn test_phantom_abilities() {
2484        use Ability as A;
2485        use AbilitySet as S;
2486
2487        let (_, cache) = package_cache([
2488            (1, build_package("iota").unwrap(), iota_types()),
2489            (1, build_package("d0").unwrap(), d0_types()),
2490        ]);
2491        let resolver = Resolver::new(cache);
2492
2493        let a1 = resolver
2494            .abilities(type_("0xd0::m::O<u32, 0xd0::m::R>"))
2495            .await
2496            .unwrap();
2497        assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2498    }
2499
2500    #[tokio::test]
2501    async fn test_err_ability_arity() {
2502        let (_, cache) = package_cache([
2503            (1, build_package("iota").unwrap(), iota_types()),
2504            (1, build_package("d0").unwrap(), d0_types()),
2505        ]);
2506        let resolver = Resolver::new(cache);
2507
2508        // Too few
2509        let err = resolver
2510            .abilities(type_("0xd0::m::T<u8>"))
2511            .await
2512            .unwrap_err();
2513        assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2514
2515        // Too many
2516        let err = resolver
2517            .abilities(type_("0xd0::m::T<u8, u16, u32>"))
2518            .await
2519            .unwrap_err();
2520        assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2521    }
2522
2523    #[tokio::test]
2524    async fn test_err_ability_signer() {
2525        let (_, cache) = package_cache([]);
2526        let resolver = Resolver::new(cache);
2527
2528        let err = resolver.abilities(type_("signer")).await.unwrap_err();
2529        assert!(matches!(err, Error::UnexpectedSigner));
2530    }
2531
2532    #[tokio::test]
2533    async fn test_err_too_many_type_params() {
2534        let (_, cache) = package_cache([
2535            (1, build_package("iota").unwrap(), iota_types()),
2536            (1, build_package("d0").unwrap(), d0_types()),
2537        ]);
2538
2539        let resolver = Resolver::new_with_limits(
2540            cache,
2541            Limits {
2542                max_type_argument_width: 1,
2543                max_type_argument_depth: 100,
2544                max_type_nodes: 100,
2545                max_move_value_depth: 100,
2546            },
2547        );
2548
2549        let err = resolver
2550            .abilities(type_("0xd0::m::O<u32, u64>"))
2551            .await
2552            .unwrap_err();
2553        assert!(matches!(err, Error::TooManyTypeParams(1, 2)));
2554    }
2555
2556    #[tokio::test]
2557    async fn test_err_too_many_type_nodes() {
2558        use Ability as A;
2559        use AbilitySet as S;
2560
2561        let (_, cache) = package_cache([
2562            (1, build_package("iota").unwrap(), iota_types()),
2563            (1, build_package("d0").unwrap(), d0_types()),
2564        ]);
2565
2566        let resolver = Resolver::new_with_limits(
2567            cache,
2568            Limits {
2569                max_type_argument_width: 100,
2570                max_type_argument_depth: 100,
2571                max_type_nodes: 2,
2572                max_move_value_depth: 100,
2573            },
2574        );
2575
2576        // This request is OK, because one of O's type parameters is phantom, so we can
2577        // avoid loading its definition.
2578        let a1 = resolver
2579            .abilities(type_("0xd0::m::O<0xd0::m::S, 0xd0::m::Q>"))
2580            .await
2581            .unwrap();
2582        assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2583
2584        // But this request will hit the limit
2585        let err = resolver
2586            .abilities(type_("0xd0::m::T<0xd0::m::P, 0xd0::m::Q>"))
2587            .await
2588            .unwrap_err();
2589        assert!(matches!(err, Error::TooManyTypeNodes(2, _)));
2590    }
2591
2592    #[tokio::test]
2593    async fn test_err_type_param_nesting() {
2594        use Ability as A;
2595        use AbilitySet as S;
2596
2597        let (_, cache) = package_cache([
2598            (1, build_package("iota").unwrap(), iota_types()),
2599            (1, build_package("d0").unwrap(), d0_types()),
2600        ]);
2601
2602        let resolver = Resolver::new_with_limits(
2603            cache,
2604            Limits {
2605                max_type_argument_width: 100,
2606                max_type_argument_depth: 2,
2607                max_type_nodes: 100,
2608                max_move_value_depth: 100,
2609            },
2610        );
2611
2612        // This request is OK, because one of O's type parameters is phantom, so we can
2613        // avoid loading its definition.
2614        let a1 = resolver
2615            .abilities(type_(
2616                "0xd0::m::O<0xd0::m::S, 0xd0::m::T<vector<u32>, vector<u64>>>",
2617            ))
2618            .await
2619            .unwrap();
2620        assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2621
2622        // But this request will hit the limit
2623        let err = resolver
2624            .abilities(type_("vector<0xd0::m::T<0xd0::m::O<u64, u32>, u16>>"))
2625            .await
2626            .unwrap_err();
2627        assert!(matches!(err, Error::TypeParamNesting(2, _)));
2628    }
2629
2630    #[tokio::test]
2631    async fn test_pure_input_layouts() {
2632        use CallArg as I;
2633        use ObjectArg::ImmOrOwnedObject as O;
2634        use TypeTag as T;
2635
2636        let (_, cache) = package_cache([
2637            (1, build_package("std").unwrap(), std_types()),
2638            (1, build_package("iota").unwrap(), iota_types()),
2639            (1, build_package("e0").unwrap(), e0_types()),
2640        ]);
2641
2642        let resolver = Resolver::new(cache);
2643
2644        // Helper function to generate a PTB calling 0xe0::m::foo.
2645        fn ptb(t: TypeTag, y: CallArg) -> ProgrammableTransaction {
2646            ProgrammableTransaction {
2647                inputs: vec![
2648                    I::Object(O(random_object_ref())),
2649                    I::Pure(bcs::to_bytes(&42u64).unwrap()),
2650                    I::Object(O(random_object_ref())),
2651                    y,
2652                    I::Object(O(random_object_ref())),
2653                    I::Pure(bcs::to_bytes("hello").unwrap()),
2654                    I::Pure(bcs::to_bytes("world").unwrap()),
2655                ],
2656                commands: vec![Command::MoveCall(Box::new(ProgrammableMoveCall {
2657                    package: addr("0xe0").into(),
2658                    module: ident_str!("m").to_owned(),
2659                    function: ident_str!("foo").to_owned(),
2660                    type_arguments: vec![t],
2661                    arguments: (0..=6).map(Argument::Input).collect(),
2662                }))],
2663            }
2664        }
2665
2666        let ptb_u64 = ptb(T::U64, I::Pure(bcs::to_bytes(&1u64).unwrap()));
2667
2668        let ptb_opt = ptb(
2669            TypeTag::Struct(Box::new(StructTag {
2670                address: addr("0x1"),
2671                module: ident_str!("option").to_owned(),
2672                name: ident_str!("Option").to_owned(),
2673                type_params: vec![TypeTag::U64],
2674            })),
2675            I::Pure(bcs::to_bytes(&[vec![1u64], vec![], vec![3]]).unwrap()),
2676        );
2677
2678        let ptb_obj = ptb(
2679            TypeTag::Struct(Box::new(StructTag {
2680                address: addr("0xe0"),
2681                module: ident_str!("m").to_owned(),
2682                name: ident_str!("O").to_owned(),
2683                type_params: vec![],
2684            })),
2685            I::Object(O(random_object_ref())),
2686        );
2687
2688        let inputs_u64 = resolver.pure_input_layouts(&ptb_u64).await.unwrap();
2689        let inputs_opt = resolver.pure_input_layouts(&ptb_opt).await.unwrap();
2690        let inputs_obj = resolver.pure_input_layouts(&ptb_obj).await.unwrap();
2691
2692        // Make the output format a little nicer for the snapshot
2693        let mut output = "---\n".to_string();
2694        for inputs in [inputs_u64, inputs_opt, inputs_obj] {
2695            for input in inputs {
2696                if let Some(layout) = input {
2697                    output += &format!("{layout:#}\n");
2698                } else {
2699                    output += "???\n";
2700                }
2701            }
2702            output += "---\n";
2703        }
2704
2705        insta::assert_snapshot!(output);
2706    }
2707
2708    /// Like the test above, but the inputs are re-used, which we want to detect
2709    /// (but is fine because they are assigned the same type at each usage).
2710    #[tokio::test]
2711    async fn test_pure_input_layouts_overlapping() {
2712        use CallArg as I;
2713        use ObjectArg::ImmOrOwnedObject as O;
2714        use TypeTag as T;
2715
2716        let (_, cache) = package_cache([
2717            (1, build_package("std").unwrap(), std_types()),
2718            (1, build_package("iota").unwrap(), iota_types()),
2719            (1, build_package("e0").unwrap(), e0_types()),
2720        ]);
2721
2722        let resolver = Resolver::new(cache);
2723
2724        // Helper function to generate a PTB calling 0xe0::m::foo.
2725        let ptb = ProgrammableTransaction {
2726            inputs: vec![
2727                I::Object(O(random_object_ref())),
2728                I::Pure(bcs::to_bytes(&42u64).unwrap()),
2729                I::Object(O(random_object_ref())),
2730                I::Pure(bcs::to_bytes(&43u64).unwrap()),
2731                I::Object(O(random_object_ref())),
2732                I::Pure(bcs::to_bytes("hello").unwrap()),
2733                I::Pure(bcs::to_bytes("world").unwrap()),
2734            ],
2735            commands: vec![
2736                Command::MoveCall(Box::new(ProgrammableMoveCall {
2737                    package: addr("0xe0").into(),
2738                    module: ident_str!("m").to_owned(),
2739                    function: ident_str!("foo").to_owned(),
2740                    type_arguments: vec![T::U64],
2741                    arguments: (0..=6).map(Argument::Input).collect(),
2742                })),
2743                Command::MoveCall(Box::new(ProgrammableMoveCall {
2744                    package: addr("0xe0").into(),
2745                    module: ident_str!("m").to_owned(),
2746                    function: ident_str!("foo").to_owned(),
2747                    type_arguments: vec![T::U64],
2748                    arguments: (0..=6).map(Argument::Input).collect(),
2749                })),
2750            ],
2751        };
2752
2753        let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
2754
2755        // Make the output format a little nicer for the snapshot
2756        let mut output = String::new();
2757        for input in inputs {
2758            if let Some(layout) = input {
2759                output += &format!("{layout:#}\n");
2760            } else {
2761                output += "???\n";
2762            }
2763        }
2764
2765        insta::assert_snapshot!(output);
2766    }
2767
2768    #[tokio::test]
2769    async fn test_pure_input_layouts_conflicting() {
2770        use CallArg as I;
2771        use ObjectArg::ImmOrOwnedObject as O;
2772        use TypeTag as T;
2773
2774        let (_, cache) = package_cache([
2775            (1, build_package("std").unwrap(), std_types()),
2776            (1, build_package("iota").unwrap(), iota_types()),
2777            (1, build_package("e0").unwrap(), e0_types()),
2778        ]);
2779
2780        let resolver = Resolver::new(cache);
2781
2782        let ptb = ProgrammableTransaction {
2783            inputs: vec![
2784                I::Object(O(random_object_ref())),
2785                I::Pure(bcs::to_bytes(&42u64).unwrap()),
2786                I::Object(O(random_object_ref())),
2787                I::Pure(bcs::to_bytes(&43u64).unwrap()),
2788                I::Object(O(random_object_ref())),
2789                I::Pure(bcs::to_bytes("hello").unwrap()),
2790                I::Pure(bcs::to_bytes("world").unwrap()),
2791            ],
2792            commands: vec![
2793                Command::MoveCall(Box::new(ProgrammableMoveCall {
2794                    package: addr("0xe0").into(),
2795                    module: ident_str!("m").to_owned(),
2796                    function: ident_str!("foo").to_owned(),
2797                    type_arguments: vec![T::U64],
2798                    arguments: (0..=6).map(Argument::Input).collect(),
2799                })),
2800                // This command is using the input that was previously used as a U64, but now as a
2801                // U32, which will cause an error.
2802                Command::MakeMoveVec(Some(T::U32), vec![Argument::Input(3)]),
2803            ],
2804        };
2805
2806        insta::assert_snapshot!(
2807            resolver.pure_input_layouts(&ptb).await.unwrap_err(),
2808            @"Conflicting types for input 3: u64 and u32"
2809        );
2810    }
2811
2812    // *** Test Helpers
2813    // ************************************************************************
2814    // **
2815
2816    type TypeOriginTable = Vec<DatatypeKey>;
2817
2818    fn a0_types() -> TypeOriginTable {
2819        vec![
2820            datakey("0xa0", "m", "T0"),
2821            datakey("0xa0", "m", "T1"),
2822            datakey("0xa0", "m", "T2"),
2823            datakey("0xa0", "m", "E0"),
2824            datakey("0xa0", "m", "E1"),
2825            datakey("0xa0", "m", "E2"),
2826            datakey("0xa0", "n", "T0"),
2827            datakey("0xa0", "n", "E0"),
2828        ]
2829    }
2830
2831    fn a1_types() -> TypeOriginTable {
2832        let mut types = a0_types();
2833
2834        types.extend([
2835            datakey("0xa1", "m", "T3"),
2836            datakey("0xa1", "m", "T4"),
2837            datakey("0xa1", "n", "T1"),
2838            datakey("0xa1", "m", "E3"),
2839            datakey("0xa1", "m", "E4"),
2840            datakey("0xa1", "n", "E1"),
2841        ]);
2842
2843        types
2844    }
2845
2846    fn b0_types() -> TypeOriginTable {
2847        vec![datakey("0xb0", "m", "T0"), datakey("0xb0", "m", "E0")]
2848    }
2849
2850    fn c0_types() -> TypeOriginTable {
2851        vec![datakey("0xc0", "m", "T0"), datakey("0xc0", "m", "E0")]
2852    }
2853
2854    fn d0_types() -> TypeOriginTable {
2855        vec![
2856            datakey("0xd0", "m", "O"),
2857            datakey("0xd0", "m", "P"),
2858            datakey("0xd0", "m", "Q"),
2859            datakey("0xd0", "m", "R"),
2860            datakey("0xd0", "m", "S"),
2861            datakey("0xd0", "m", "T"),
2862            datakey("0xd0", "m", "EO"),
2863            datakey("0xd0", "m", "EP"),
2864            datakey("0xd0", "m", "EQ"),
2865            datakey("0xd0", "m", "ER"),
2866            datakey("0xd0", "m", "ES"),
2867            datakey("0xd0", "m", "ET"),
2868        ]
2869    }
2870
2871    fn e0_types() -> TypeOriginTable {
2872        vec![datakey("0xe0", "m", "O")]
2873    }
2874
2875    fn s0_types() -> TypeOriginTable {
2876        vec![datakey("0x1", "m", "T0"), datakey("0x1", "m", "E0")]
2877    }
2878
2879    fn s1_types() -> TypeOriginTable {
2880        let mut types = s0_types();
2881
2882        types.extend([datakey("0x1", "m", "T1"), datakey("0x1", "m", "E1")]);
2883
2884        types
2885    }
2886
2887    fn iota_types() -> TypeOriginTable {
2888        vec![datakey("0x2", "object", "UID")]
2889    }
2890
2891    fn std_types() -> TypeOriginTable {
2892        vec![
2893            datakey("0x1", "ascii", "String"),
2894            datakey("0x1", "option", "Option"),
2895            datakey("0x1", "string", "String"),
2896        ]
2897    }
2898
2899    /// Build an in-memory package cache from locally compiled packages.
2900    /// Assumes that all packages in `packages` are published (all modules
2901    /// have a non-zero package address and all packages
2902    /// have a 'published-at' address), and their transitive dependencies are
2903    /// also in `packages`.
2904    fn package_cache(
2905        packages: impl IntoIterator<Item = (u64, CompiledPackage, TypeOriginTable)>,
2906    ) -> (
2907        Arc<RwLock<InnerStore>>,
2908        PackageStoreWithLruCache<InMemoryPackageStore>,
2909    ) {
2910        let packages_by_storage_id: BTreeMap<AccountAddress, _> = packages
2911            .into_iter()
2912            .map(|(version, package, origins)| {
2913                (package_storage_id(&package), (version, package, origins))
2914            })
2915            .collect();
2916
2917        let packages = packages_by_storage_id
2918            .iter()
2919            .map(|(&storage_id, (version, compiled_package, origins))| {
2920                let linkage = compiled_package
2921                    .dependency_ids
2922                    .published
2923                    .values()
2924                    .map(|dep_id| {
2925                        let storage_id = AccountAddress::from(*dep_id);
2926                        let runtime_id = package_runtime_id(
2927                            &packages_by_storage_id
2928                                .get(&storage_id)
2929                                .unwrap_or_else(|| panic!("Dependency {storage_id} not in store"))
2930                                .1,
2931                        );
2932
2933                        (runtime_id, storage_id)
2934                    })
2935                    .collect();
2936
2937                let package = cached_package(*version, linkage, compiled_package, origins);
2938                (storage_id, package)
2939            })
2940            .collect();
2941
2942        let inner = Arc::new(RwLock::new(InnerStore {
2943            packages,
2944            fetches: 0,
2945        }));
2946
2947        let store = InMemoryPackageStore {
2948            inner: inner.clone(),
2949        };
2950
2951        (inner, PackageStoreWithLruCache::new(store))
2952    }
2953
2954    fn cached_package(
2955        version: u64,
2956        linkage: Linkage,
2957        package: &CompiledPackage,
2958        origins: &TypeOriginTable,
2959    ) -> Package {
2960        let storage_id = package_storage_id(package);
2961        let runtime_id = package_runtime_id(package);
2962        let version = SequenceNumber::from_u64(version);
2963
2964        let mut modules = BTreeMap::new();
2965        for unit in &package.package.root_compiled_units {
2966            let NamedCompiledModule { name, module, .. } = &unit.unit;
2967
2968            let origins = origins
2969                .iter()
2970                .filter(|key| key.module == name.as_str())
2971                .map(|key| (key.name.to_string(), key.package))
2972                .collect();
2973
2974            let module = match Module::read(module.clone(), origins) {
2975                Ok(module) => module,
2976                Err(struct_) => {
2977                    panic!("Missing type origin for {}::{struct_}", module.self_id());
2978                }
2979            };
2980
2981            modules.insert(name.to_string(), module);
2982        }
2983
2984        Package {
2985            storage_id,
2986            runtime_id,
2987            linkage,
2988            version,
2989            modules,
2990        }
2991    }
2992
2993    fn package_storage_id(package: &CompiledPackage) -> AccountAddress {
2994        AccountAddress::from(*package.published_at.as_ref().unwrap_or_else(|_| {
2995            panic!(
2996                "Package {} doesn't have published-at set",
2997                package.package.compiled_package_info.package_name,
2998            )
2999        }))
3000    }
3001
3002    fn package_runtime_id(package: &CompiledPackage) -> AccountAddress {
3003        *package
3004            .published_root_module()
3005            .expect("No compiled module")
3006            .address()
3007    }
3008
3009    fn build_package(dir: &str) -> IotaResult<CompiledPackage> {
3010        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3011        path.extend(["tests", "packages", dir]);
3012        BuildConfig::new_for_testing().build(&path)
3013    }
3014
3015    fn addr(a: &str) -> AccountAddress {
3016        AccountAddress::from_str(a).unwrap()
3017    }
3018
3019    fn datakey(a: &str, m: &'static str, n: &'static str) -> DatatypeKey {
3020        DatatypeKey {
3021            package: addr(a),
3022            module: m.into(),
3023            name: n.into(),
3024        }
3025    }
3026
3027    fn type_(t: &str) -> TypeTag {
3028        TypeTag::from_str(t).unwrap()
3029    }
3030
3031    fn key(t: &str) -> DatatypeKey {
3032        let tag = StructTag::from_str(t).unwrap();
3033        DatatypeRef::from(&tag).as_key()
3034    }
3035
3036    struct InMemoryPackageStore {
3037        /// All the contents are stored in an `InnerStore` that can be probed
3038        /// and queried from outside.
3039        inner: Arc<RwLock<InnerStore>>,
3040    }
3041
3042    struct InnerStore {
3043        packages: BTreeMap<AccountAddress, Package>,
3044        fetches: usize,
3045    }
3046
3047    #[async_trait]
3048    impl PackageStore for InMemoryPackageStore {
3049        async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
3050            let mut inner = self.inner.as_ref().write().unwrap();
3051            inner.fetches += 1;
3052            inner
3053                .packages
3054                .get(&id)
3055                .cloned()
3056                .ok_or_else(|| Error::PackageNotFound(id))
3057                .map(Arc::new)
3058        }
3059    }
3060
3061    impl InnerStore {
3062        fn replace(&mut self, id: AccountAddress, package: Package) {
3063            self.packages.insert(id, package);
3064        }
3065    }
3066}