1use 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 base_types::{
15 Identifier, IotaAddress, SequenceNumber, StructTag, TypeTag, is_primitive_type_tag,
16 },
17 iota_sdk_types_conversions::{struct_tag_sdk_to_core, type_tag_core_to_sdk},
18 move_package::{MovePackage, TypeOrigin},
19 object::Object,
20 transaction::{Argument, CallArg, Command, MakeMoveVector, ProgrammableTransaction},
21};
22use lru::LruCache;
23use move_binary_format::{
24 CompiledModule,
25 errors::Location,
26 file_format::{
27 AbilitySet, DatatypeHandleIndex, DatatypeTyParameter, EnumDefinitionIndex,
28 FunctionDefinitionIndex, Signature as MoveSignature, SignatureIndex, SignatureToken,
29 StructDefinitionIndex, StructFieldInformation, TableIndex, Visibility,
30 },
31};
32use move_command_line_common::{
33 display::{RenderResult, try_render_constant},
34 error_bitset::ErrorBitset,
35};
36use move_core_types::{
37 account_address::AccountAddress,
38 annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
39 language_storage::ModuleId,
40};
41
42use crate::error::Error;
43
44pub mod error;
45
46const PACKAGE_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
49
50pub type Result<T> = std::result::Result<T, Error>;
51
52#[derive(Debug)]
56pub struct Resolver<S> {
57 package_store: S,
58 limits: Option<Limits>,
59}
60
61#[derive(Debug)]
64pub struct Limits {
65 pub max_type_argument_depth: usize,
67 pub max_type_argument_width: usize,
69 pub max_type_nodes: usize,
71 pub max_move_value_depth: usize,
73}
74
75pub struct PackageStoreWithLruCache<T> {
80 pub(crate) packages: Mutex<LruCache<IotaAddress, Arc<Package>>>,
81 pub(crate) inner: T,
82}
83
84#[derive(Clone, Debug)]
85pub struct Package {
86 storage_id: IotaAddress,
88
89 runtime_id: IotaAddress,
93
94 linkage: Linkage,
98
99 version: SequenceNumber,
102
103 modules: BTreeMap<String, Module>,
104}
105
106type Linkage = BTreeMap<IotaAddress, IotaAddress>;
107
108#[derive(Clone, Debug)]
115pub struct CleverError {
116 pub module_id: ModuleId,
118 pub error_info: ErrorConstants,
121 pub source_line_number: u16,
123}
124
125#[derive(Clone, Debug)]
136pub enum ErrorConstants {
137 None,
139 Rendered {
150 identifier: String,
152 constant: String,
154 },
155 Raw {
160 identifier: String,
162 bytes: Vec<u8>,
164 },
165}
166
167#[derive(Clone, Debug)]
168pub struct Module {
169 bytecode: CompiledModule,
170
171 struct_index: BTreeMap<String, (IotaAddress, StructDefinitionIndex)>,
174
175 enum_index: BTreeMap<String, (IotaAddress, EnumDefinitionIndex)>,
178 function_index: BTreeMap<String, FunctionDefinitionIndex>,
181}
182
183#[derive(Debug)]
185pub struct DataDef {
186 pub defining_id: IotaAddress,
188
189 pub abilities: AbilitySet,
191
192 pub type_params: Vec<DatatypeTyParameter>,
194
195 pub data: MoveData,
198}
199
200#[derive(Debug)]
201pub enum MoveData {
202 Struct(Vec<(String, OpenSignatureBody)>),
206
207 Enum(Vec<VariantDef>),
210}
211
212#[derive(Debug)]
215pub struct VariantDef {
216 pub name: String,
218
219 pub signatures: Vec<(String, OpenSignatureBody)>,
223}
224
225#[derive(Debug)]
227pub struct FunctionDef {
228 pub visibility: Visibility,
230
231 pub is_entry: bool,
233
234 pub type_params: Vec<AbilitySet>,
236
237 pub parameters: Vec<OpenSignature>,
239
240 pub return_: Vec<OpenSignature>,
242}
243
244#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
248pub struct DatatypeRef<'m, 'n> {
249 pub package: IotaAddress,
250 pub module: Cow<'m, str>,
251 pub name: Cow<'n, str>,
252}
253
254pub type DatatypeKey = DatatypeRef<'static, 'static>;
256
257#[derive(Copy, Clone, Debug)]
258pub enum Reference {
259 Immutable,
260 Mutable,
261}
262
263#[derive(Clone, Debug)]
266pub struct Signature {
267 pub ref_: Option<Reference>,
268 pub body: TypeTag,
269}
270
271#[derive(Clone, Debug)]
274pub struct OpenSignature {
275 pub ref_: Option<Reference>,
276 pub body: OpenSignatureBody,
277}
278
279#[derive(Clone, Debug)]
282pub enum OpenSignatureBody {
283 Address,
284 Bool,
285 U8,
286 U16,
287 U32,
288 U64,
289 U128,
290 U256,
291 Vector(Box<OpenSignatureBody>),
292 Datatype(DatatypeKey, Vec<OpenSignatureBody>),
293 TypeParameter(u16),
294}
295
296#[derive(Debug, Default)]
298struct ResolutionContext<'l> {
299 datatypes: BTreeMap<DatatypeKey, DataDef>,
302
303 limits: Option<&'l Limits>,
305}
306
307#[async_trait]
310pub trait PackageStore: Send + Sync + 'static {
311 async fn fetch(&self, id: IotaAddress) -> Result<Arc<Package>>;
314}
315
316macro_rules! as_ref_impl {
317 ($type:ty) => {
318 #[async_trait]
319 impl PackageStore for $type {
320 async fn fetch(&self, id: IotaAddress) -> Result<Arc<Package>> {
321 self.as_ref().fetch(id).await
322 }
323 }
324 };
325}
326
327as_ref_impl!(Arc<dyn PackageStore>);
328as_ref_impl!(Box<dyn PackageStore>);
329
330macro_rules! check_max_limit {
333 ($err:ident, $config:expr; $limit:ident $op:tt $value:expr) => {
334 if let Some(l) = $config {
335 let max = l.$limit;
336 let val = $value;
337 if !(max $op val) {
338 return Err(Error::$err(max, val));
339 }
340 }
341 };
342}
343
344impl<S> Resolver<S> {
345 pub fn new(package_store: S) -> Self {
346 Self {
347 package_store,
348 limits: None,
349 }
350 }
351
352 pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
353 Self {
354 package_store,
355 limits: Some(limits),
356 }
357 }
358
359 pub fn package_store(&self) -> &S {
360 &self.package_store
361 }
362
363 pub fn package_store_mut(&mut self) -> &mut S {
364 &mut self.package_store
365 }
366}
367
368impl<S: PackageStore> Resolver<S> {
369 pub async fn canonical_type(&self, mut tag: TypeTag) -> Result<TypeTag> {
380 let mut context = ResolutionContext::new(self.limits.as_ref());
381
382 context
385 .add_type_tag(
386 &mut tag,
387 &self.package_store,
388 false,
390 true,
392 )
393 .await?;
394
395 context.canonicalize_type(&mut tag)?;
397 Ok(tag)
398 }
399
400 pub async fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
404 let mut context = ResolutionContext::new(self.limits.as_ref());
405
406 context
409 .add_type_tag(
410 &mut tag,
411 &self.package_store,
412 true,
414 true,
416 )
417 .await?;
418
419 let max_depth = self
421 .limits
422 .as_ref()
423 .map_or(usize::MAX, |l| l.max_move_value_depth);
424
425 Ok(context.resolve_type_layout(&tag, max_depth)?.0)
426 }
427
428 pub async fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
435 let mut context = ResolutionContext::new(self.limits.as_ref());
436
437 context
440 .add_type_tag(
441 &mut tag,
442 &self.package_store,
443 false,
445 false,
447 )
448 .await?;
449
450 context.resolve_abilities(&tag)
452 }
453
454 pub async fn function_signature(
457 &self,
458 pkg: IotaAddress,
459 module: &str,
460 function: &str,
461 ) -> Result<FunctionDef> {
462 let mut context = ResolutionContext::new(self.limits.as_ref());
463
464 let package = self.package_store.fetch(pkg).await?;
465 let Some(mut def) = package.module(module)?.function_def(function)? else {
466 return Err(Error::FunctionNotFound(
467 pkg,
468 module.to_string(),
469 function.to_string(),
470 ));
471 };
472
473 for sig in def.parameters.iter().chain(def.return_.iter()) {
476 context
477 .add_signature(
478 sig.body.clone(),
479 &self.package_store,
480 package.as_ref(),
481 false,
483 )
484 .await?;
485 }
486
487 for sig in def.parameters.iter_mut().chain(def.return_.iter_mut()) {
489 context.relocate_signature(&mut sig.body)?;
490 }
491
492 Ok(def)
493 }
494
495 pub async fn pure_input_layouts(
507 &self,
508 tx: &ProgrammableTransaction,
509 ) -> Result<Vec<Option<MoveTypeLayout>>> {
510 let mut tags = vec![None; tx.inputs.len()];
511 let mut register_type = |arg: &Argument, tag: &TypeTag| {
512 let &Argument::Input(ix) = arg else {
513 return Ok(());
514 };
515
516 if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
517 return Ok(());
518 }
519
520 let Some(type_) = tags.get_mut(ix as usize) else {
521 return Ok(());
522 };
523
524 match type_ {
525 None => *type_ = Some(tag.clone()),
526 Some(prev) => {
527 if prev != tag {
528 return Err(Error::InputTypeConflict(ix, prev.clone(), tag.clone()));
529 }
530 }
531 }
532
533 Ok(())
534 };
535
536 for cmd in &tx.commands {
538 match cmd {
539 Command::MoveCall(cmd) => {
540 let Ok(signature) = self
541 .function_signature(
542 cmd.package.into(),
543 cmd.module.as_str(),
544 cmd.function.as_str(),
545 )
546 .await
547 else {
548 continue;
549 };
550
551 for (open_sig, arg) in signature.parameters.iter().zip(cmd.arguments.iter()) {
552 let sig = open_sig.instantiate(&cmd.type_arguments)?;
553 register_type(arg, &sig.body)?;
554 }
555 }
556 Command::TransferObjects(cmd) => register_type(&cmd.address, &TypeTag::Address)?,
557 Command::SplitCoins(cmd) => {
558 for amount in &cmd.amounts {
559 register_type(amount, &TypeTag::U64)?;
560 }
561 }
562 Command::MakeMoveVector(MakeMoveVector {
563 type_: Some(tag),
564 elements,
565 }) => {
566 if is_primitive_type_tag(tag) {
567 for elem in elements {
568 register_type(elem, tag)?;
569 }
570 }
571 }
572 _ => { }
573 }
574 }
575
576 let unique_tags: BTreeSet<_> = tags.iter().filter_map(|t| t.clone()).collect();
580
581 let mut layouts = BTreeMap::new();
583 for tag in unique_tags {
584 let layout = self.type_layout(tag.clone()).await?;
585 layouts.insert(tag, layout);
586 }
587
588 Ok(tags
590 .iter()
591 .map(|t| t.as_ref().and_then(|t| layouts.get(t).cloned()))
592 .collect())
593 }
594
595 pub async fn resolve_module_id(
604 &self,
605 module_id: ModuleId,
606 context: IotaAddress,
607 ) -> Result<ModuleId> {
608 let package = self.package_store.fetch(context).await?;
609 let storage_id = package.relocate(IotaAddress::new(module_id.address().into_bytes()))?;
610 Ok(ModuleId::new(
611 AccountAddress::new(storage_id.into_bytes()),
612 module_id.name().to_owned(),
613 ))
614 }
615
616 pub async fn resolve_clever_error(
633 &self,
634 module_id: ModuleId,
635 abort_code: u64,
636 ) -> Option<CleverError> {
637 let bitset = ErrorBitset::from_u64(abort_code)?;
638 let package = self
639 .package_store
640 .fetch(IotaAddress::new(module_id.address().into_bytes()))
641 .await
642 .ok()?;
643 let module = package.module(module_id.name().as_str()).ok()?.bytecode();
644 let source_line_number = bitset.line_number()?;
645
646 if bitset.identifier_index().is_none() && bitset.constant_index().is_none() {
648 return Some(CleverError {
649 module_id,
650 error_info: ErrorConstants::None,
651 source_line_number,
652 });
653 } else if bitset.identifier_index().is_none() || bitset.constant_index().is_none() {
654 return None;
655 }
656
657 let error_identifier_constant = module
658 .constant_pool()
659 .get(bitset.identifier_index()? as usize)?;
660 let error_value_constant = module
661 .constant_pool()
662 .get(bitset.constant_index()? as usize)?;
663
664 if !matches!(&error_identifier_constant.type_, SignatureToken::Vector(x) if x.as_ref() == &SignatureToken::U8)
665 {
666 return None;
667 };
668
669 let error_identifier = bcs::from_bytes::<Vec<u8>>(&error_identifier_constant.data)
670 .ok()
671 .and_then(|x| String::from_utf8(x).ok())?;
672 let bytes = error_value_constant.data.clone();
673
674 let rendered = try_render_constant(error_value_constant);
675
676 let error_info = match rendered {
677 RenderResult::NotRendered => ErrorConstants::Raw {
678 identifier: error_identifier,
679 bytes,
680 },
681 RenderResult::AsString(s) | RenderResult::AsValue(s) => ErrorConstants::Rendered {
682 identifier: error_identifier,
683 constant: s,
684 },
685 };
686
687 Some(CleverError {
688 module_id,
689 error_info,
690 source_line_number,
691 })
692 }
693}
694
695impl<T> PackageStoreWithLruCache<T> {
696 pub fn new(inner: T) -> Self {
697 let packages = Mutex::new(LruCache::new(PACKAGE_CACHE_SIZE));
698 Self { packages, inner }
699 }
700
701 pub fn evict(&self, ids: impl IntoIterator<Item = IotaAddress>) {
705 let mut packages = self.packages.lock().unwrap();
706 for id in ids {
707 packages.pop(&id);
708 }
709 }
710}
711
712#[async_trait]
713impl<T: PackageStore> PackageStore for PackageStoreWithLruCache<T> {
714 async fn fetch(&self, id: IotaAddress) -> Result<Arc<Package>> {
715 if let Some(package) = {
716 let mut packages = self.packages.lock().unwrap();
718 packages.get(&id).cloned()
719 } {
720 return Ok(package);
721 };
722
723 let package = self.inner.fetch(id).await?;
724
725 let mut packages = self.packages.lock().unwrap();
732 Ok(match packages.peek(&id) {
733 Some(prev) if package.version <= prev.version => {
734 let package = prev.clone();
735 packages.promote(&id);
736 package
737 }
738
739 Some(_) | None => {
740 packages.push(id, package.clone());
741 package
742 }
743 })
744 }
745}
746
747impl Package {
748 pub fn read_from_object(object: &Object) -> Result<Self> {
749 let Some(package) = object.data.as_package_opt() else {
750 return Err(Error::NotAPackage(object.id().into()));
751 };
752
753 Self::read_from_package(package)
754 }
755
756 pub fn read_from_package(package: &MovePackage) -> Result<Self> {
757 let mut type_origins: BTreeMap<String, BTreeMap<String, IotaAddress>> = BTreeMap::new();
758 for TypeOrigin {
759 module_name,
760 datatype_name,
761 package,
762 } in package.type_origin_table()
763 {
764 type_origins
765 .entry(module_name.to_string())
766 .or_default()
767 .insert(datatype_name.to_string(), (*package).into());
768 }
769
770 let mut runtime_id = None;
771 let mut modules = BTreeMap::new();
772 for (name, bytes) in package.serialized_module_map() {
773 let origins = type_origins.remove(&name.to_string()).unwrap_or_default();
774 let bytecode = CompiledModule::deserialize_with_defaults(bytes)
775 .map_err(|e| Error::Deserialize(e.finish(Location::Undefined)))?;
776
777 runtime_id = Some(IotaAddress::new(bytecode.address().into_bytes()));
778
779 let name = name.clone();
780 match Module::read(bytecode, origins) {
781 Ok(module) => modules.insert(name.to_string(), module),
782 Err(struct_) => {
783 return Err(Error::NoTypeOrigin(
784 package.id().into(),
785 name.to_string(),
786 struct_,
787 ));
788 }
789 };
790 }
791
792 let Some(runtime_id) = runtime_id else {
793 return Err(Error::EmptyPackage(package.id().into()));
794 };
795
796 let linkage = package
797 .linkage_table()
798 .iter()
799 .map(|(&dep, linkage)| (dep.into(), linkage.upgraded_id.into()))
800 .collect();
801
802 Ok(Package {
803 storage_id: package.id().into(),
804 runtime_id,
805 version: package.version(),
806 modules,
807 linkage,
808 })
809 }
810
811 pub fn module(&self, module: &str) -> Result<&Module> {
812 self.modules
813 .get(module)
814 .ok_or_else(|| Error::ModuleNotFound(self.storage_id, module.to_string()))
815 }
816
817 pub fn modules(&self) -> &BTreeMap<String, Module> {
818 &self.modules
819 }
820
821 fn data_def(&self, module_name: &str, datatype_name: &str) -> Result<DataDef> {
822 let module = self.module(module_name)?;
823 let Some(data_def) = module.data_def(datatype_name)? else {
824 return Err(Error::DatatypeNotFound(
825 self.storage_id,
826 module_name.to_string(),
827 datatype_name.to_string(),
828 ));
829 };
830 Ok(data_def)
831 }
832
833 fn relocate(&self, runtime_id: IotaAddress) -> Result<IotaAddress> {
837 if runtime_id == self.runtime_id {
840 return Ok(self.storage_id);
841 }
842
843 self.linkage
844 .get(&runtime_id)
845 .ok_or_else(|| Error::LinkageNotFound(runtime_id))
846 .copied()
847 }
848}
849
850impl Module {
851 fn read(
856 bytecode: CompiledModule,
857 mut origins: BTreeMap<String, IotaAddress>,
858 ) -> std::result::Result<Self, String> {
859 let mut struct_index = BTreeMap::new();
860 for (index, def) in bytecode.struct_defs.iter().enumerate() {
861 let sh = bytecode.datatype_handle_at(def.struct_handle);
862 let struct_ = bytecode.identifier_at(sh.name).to_string();
863 let index = StructDefinitionIndex::new(index as TableIndex);
864
865 let Some(defining_id) = origins.remove(&struct_) else {
866 return Err(struct_);
867 };
868
869 struct_index.insert(struct_, (defining_id, index));
870 }
871
872 let mut enum_index = BTreeMap::new();
873 for (index, def) in bytecode.enum_defs.iter().enumerate() {
874 let eh = bytecode.datatype_handle_at(def.enum_handle);
875 let enum_ = bytecode.identifier_at(eh.name).to_string();
876 let index = EnumDefinitionIndex::new(index as TableIndex);
877
878 let Some(defining_id) = origins.remove(&enum_) else {
879 return Err(enum_);
880 };
881
882 enum_index.insert(enum_, (defining_id, index));
883 }
884
885 let mut function_index = BTreeMap::new();
886 for (index, def) in bytecode.function_defs.iter().enumerate() {
887 let fh = bytecode.function_handle_at(def.function);
888 let function = bytecode.identifier_at(fh.name).to_string();
889 let index = FunctionDefinitionIndex::new(index as TableIndex);
890
891 function_index.insert(function, index);
892 }
893
894 Ok(Module {
895 bytecode,
896 struct_index,
897 enum_index,
898 function_index,
899 })
900 }
901
902 pub fn bytecode(&self) -> &CompiledModule {
903 &self.bytecode
904 }
905
906 pub fn name(&self) -> &str {
908 self.bytecode
909 .identifier_at(self.bytecode.self_handle().name)
910 .as_str()
911 }
912
913 pub fn structs(
916 &self,
917 after: Option<&str>,
918 before: Option<&str>,
919 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
920 use std::ops::Bound as B;
921 self.struct_index
922 .range::<str, _>((
923 after.map_or(B::Unbounded, B::Excluded),
924 before.map_or(B::Unbounded, B::Excluded),
925 ))
926 .map(|(name, _)| name.as_str())
927 }
928
929 pub fn enums(
932 &self,
933 after: Option<&str>,
934 before: Option<&str>,
935 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
936 use std::ops::Bound as B;
937 self.enum_index
938 .range::<str, _>((
939 after.map_or(B::Unbounded, B::Excluded),
940 before.map_or(B::Unbounded, B::Excluded),
941 ))
942 .map(|(name, _)| name.as_str())
943 }
944
945 pub fn datatypes(
949 &self,
950 after: Option<&str>,
951 before: Option<&str>,
952 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
953 let mut names = self
954 .structs(after, before)
955 .chain(self.enums(after, before))
956 .collect::<Vec<_>>();
957 names.sort();
958 names.into_iter()
959 }
960
961 pub fn struct_def(&self, name: &str) -> Result<Option<DataDef>> {
966 let Some(&(defining_id, index)) = self.struct_index.get(name) else {
967 return Ok(None);
968 };
969
970 let struct_def = self.bytecode.struct_def_at(index);
971 let struct_handle = self.bytecode.datatype_handle_at(struct_def.struct_handle);
972 let abilities = struct_handle.abilities;
973 let type_params = struct_handle.type_parameters.clone();
974
975 let fields = match &struct_def.field_information {
976 StructFieldInformation::Native => vec![],
977 StructFieldInformation::Declared(fields) => 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
988 Ok(Some(DataDef {
989 defining_id,
990 abilities,
991 type_params,
992 data: MoveData::Struct(fields),
993 }))
994 }
995
996 pub fn enum_def(&self, name: &str) -> Result<Option<DataDef>> {
1001 let Some(&(defining_id, index)) = self.enum_index.get(name) else {
1002 return Ok(None);
1003 };
1004
1005 let enum_def = self.bytecode.enum_def_at(index);
1006 let enum_handle = self.bytecode.datatype_handle_at(enum_def.enum_handle);
1007 let abilities = enum_handle.abilities;
1008 let type_params = enum_handle.type_parameters.clone();
1009
1010 let variants = enum_def
1011 .variants
1012 .iter()
1013 .map(|variant| {
1014 let name = self
1015 .bytecode
1016 .identifier_at(variant.variant_name)
1017 .to_string();
1018 let signatures = variant
1019 .fields
1020 .iter()
1021 .map(|f| {
1022 Ok((
1023 self.bytecode.identifier_at(f.name).to_string(),
1024 OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
1025 ))
1026 })
1027 .collect::<Result<_>>()?;
1028
1029 Ok(VariantDef { name, signatures })
1030 })
1031 .collect::<Result<_>>()?;
1032
1033 Ok(Some(DataDef {
1034 defining_id,
1035 abilities,
1036 type_params,
1037 data: MoveData::Enum(variants),
1038 }))
1039 }
1040
1041 pub fn data_def(&self, name: &str) -> Result<Option<DataDef>> {
1046 self.struct_def(name)
1047 .transpose()
1048 .or_else(|| self.enum_def(name).transpose())
1049 .transpose()
1050 }
1051
1052 pub fn functions(
1055 &self,
1056 after: Option<&str>,
1057 before: Option<&str>,
1058 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1059 use std::ops::Bound as B;
1060 self.function_index
1061 .range::<str, _>((
1062 after.map_or(B::Unbounded, B::Excluded),
1063 before.map_or(B::Unbounded, B::Excluded),
1064 ))
1065 .map(|(name, _)| name.as_str())
1066 }
1067
1068 pub fn function_def(&self, name: &str) -> Result<Option<FunctionDef>> {
1073 let Some(&index) = self.function_index.get(name) else {
1074 return Ok(None);
1075 };
1076
1077 let function_def = self.bytecode.function_def_at(index);
1078 let function_handle = self.bytecode.function_handle_at(function_def.function);
1079
1080 Ok(Some(FunctionDef {
1081 visibility: function_def.visibility,
1082 is_entry: function_def.is_entry,
1083 type_params: function_handle.type_parameters.clone(),
1084 parameters: read_signature(function_handle.parameters, &self.bytecode)?,
1085 return_: read_signature(function_handle.return_, &self.bytecode)?,
1086 }))
1087 }
1088}
1089
1090impl OpenSignature {
1091 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1092 use SignatureToken as S;
1093 Ok(match sig {
1094 S::Reference(sig) => OpenSignature {
1095 ref_: Some(Reference::Immutable),
1096 body: OpenSignatureBody::read(sig, bytecode)?,
1097 },
1098
1099 S::MutableReference(sig) => OpenSignature {
1100 ref_: Some(Reference::Mutable),
1101 body: OpenSignatureBody::read(sig, bytecode)?,
1102 },
1103
1104 sig => OpenSignature {
1105 ref_: None,
1106 body: OpenSignatureBody::read(sig, bytecode)?,
1107 },
1108 })
1109 }
1110
1111 pub fn instantiate(&self, type_params: &[TypeTag]) -> Result<Signature> {
1118 Ok(Signature {
1119 ref_: self.ref_,
1120 body: self.body.instantiate(type_params)?,
1121 })
1122 }
1123}
1124
1125impl OpenSignatureBody {
1126 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1127 use OpenSignatureBody as O;
1128 use SignatureToken as S;
1129
1130 Ok(match sig {
1131 S::Signer => return Err(Error::UnexpectedSigner),
1132 S::Reference(_) | S::MutableReference(_) => return Err(Error::UnexpectedReference),
1133
1134 S::Address => O::Address,
1135 S::Bool => O::Bool,
1136 S::U8 => O::U8,
1137 S::U16 => O::U16,
1138 S::U32 => O::U32,
1139 S::U64 => O::U64,
1140 S::U128 => O::U128,
1141 S::U256 => O::U256,
1142 S::TypeParameter(ix) => O::TypeParameter(*ix),
1143
1144 S::Vector(sig) => O::Vector(Box::new(OpenSignatureBody::read(sig, bytecode)?)),
1145
1146 S::Datatype(ix) => O::Datatype(DatatypeKey::read(*ix, bytecode), vec![]),
1147 S::DatatypeInstantiation(inst) => {
1148 let (ix, params) = &**inst;
1149 O::Datatype(
1150 DatatypeKey::read(*ix, bytecode),
1151 params
1152 .iter()
1153 .map(|sig| OpenSignatureBody::read(sig, bytecode))
1154 .collect::<Result<_>>()?,
1155 )
1156 }
1157 })
1158 }
1159
1160 fn instantiate(&self, type_params: &[TypeTag]) -> Result<TypeTag> {
1161 use OpenSignatureBody as O;
1162 use TypeTag as T;
1163
1164 Ok(match self {
1165 O::Address => T::Address,
1166 O::Bool => T::Bool,
1167 O::U8 => T::U8,
1168 O::U16 => T::U16,
1169 O::U32 => T::U32,
1170 O::U64 => T::U64,
1171 O::U128 => T::U128,
1172 O::U256 => T::U256,
1173 O::Vector(s) => T::Vector(Box::new(s.instantiate(type_params)?)),
1174
1175 O::Datatype(key, dty_params) => T::Struct(Box::new(StructTag::new(
1176 key.package,
1177 ident(&key.module)?,
1178 ident(&key.name)?,
1179 dty_params
1180 .iter()
1181 .map(|p| p.instantiate(type_params))
1182 .collect::<Result<_>>()?,
1183 ))),
1184
1185 O::TypeParameter(ix) => type_params
1186 .get(*ix as usize)
1187 .ok_or_else(|| Error::TypeParamOOB(*ix, type_params.len()))?
1188 .clone(),
1189 })
1190 }
1191}
1192
1193impl DatatypeRef<'_, '_> {
1194 pub fn as_key(&self) -> DatatypeKey {
1195 DatatypeKey {
1196 package: self.package,
1197 module: self.module.to_string().into(),
1198 name: self.name.to_string().into(),
1199 }
1200 }
1201}
1202
1203impl DatatypeKey {
1204 fn read(ix: DatatypeHandleIndex, bytecode: &CompiledModule) -> Self {
1205 let sh = bytecode.datatype_handle_at(ix);
1206 let mh = bytecode.module_handle_at(sh.module);
1207
1208 let package = IotaAddress::new(bytecode.address_identifier_at(mh.address).into_bytes());
1209 let module = bytecode.identifier_at(mh.name).to_string().into();
1210 let name = bytecode.identifier_at(sh.name).to_string().into();
1211
1212 DatatypeKey {
1213 package,
1214 module,
1215 name,
1216 }
1217 }
1218}
1219
1220impl<'l> ResolutionContext<'l> {
1221 fn new(limits: Option<&'l Limits>) -> Self {
1222 ResolutionContext {
1223 datatypes: BTreeMap::new(),
1224 limits,
1225 }
1226 }
1227
1228 async fn add_type_tag<S: PackageStore + ?Sized>(
1243 &mut self,
1244 tag: &mut TypeTag,
1245 store: &S,
1246 visit_fields: bool,
1247 visit_phantoms: bool,
1248 ) -> Result<()> {
1249 use TypeTag as T;
1250
1251 struct ToVisit<'t> {
1252 tag: &'t mut TypeTag,
1253 depth: usize,
1254 }
1255
1256 let mut frontier = vec![ToVisit { tag, depth: 0 }];
1257 while let Some(ToVisit { tag, depth }) = frontier.pop() {
1258 macro_rules! push_ty_param {
1259 ($tag:expr) => {{
1260 check_max_limit!(
1261 TypeParamNesting, self.limits;
1262 max_type_argument_depth > depth
1263 );
1264
1265 frontier.push(ToVisit { tag: $tag, depth: depth + 1 })
1266 }}
1267 }
1268
1269 match tag {
1270 T::Address
1271 | T::Bool
1272 | T::U8
1273 | T::U16
1274 | T::U32
1275 | T::U64
1276 | T::U128
1277 | T::U256
1278 | T::Signer => {
1279 }
1281
1282 T::Vector(tag) => push_ty_param!(tag),
1283
1284 T::Struct(s) => {
1285 let context = store.fetch(s.address()).await?;
1286 let def = context
1287 .clone()
1288 .data_def(s.module().as_str(), s.name().as_str())?;
1289
1290 *s.as_mut() = StructTag::new(
1295 context.runtime_id,
1296 s.module().clone(),
1297 s.name().clone(),
1298 s.type_params().to_vec(),
1299 );
1300 let key = DatatypeRef::from(s.as_ref()).as_key();
1301
1302 if def.type_params.len() != s.type_params().len() {
1303 return Err(Error::TypeArityMismatch(
1304 def.type_params.len(),
1305 s.type_params().len(),
1306 ));
1307 }
1308
1309 check_max_limit!(
1310 TooManyTypeParams, self.limits;
1311 max_type_argument_width >= s.type_params().len()
1312 );
1313
1314 for (param, def) in s.type_params_mut().iter_mut().zip(def.type_params.iter()) {
1315 if !def.is_phantom || visit_phantoms {
1316 push_ty_param!(param);
1317 }
1318 }
1319
1320 if self.datatypes.contains_key(&key) {
1321 continue;
1322 }
1323
1324 if visit_fields {
1325 match &def.data {
1326 MoveData::Struct(fields) => {
1327 for (_, sig) in fields {
1328 self.add_signature(sig.clone(), store, &context, visit_fields)
1329 .await?;
1330 }
1331 }
1332 MoveData::Enum(variants) => {
1333 for variant in variants {
1334 for (_, sig) in &variant.signatures {
1335 self.add_signature(
1336 sig.clone(),
1337 store,
1338 &context,
1339 visit_fields,
1340 )
1341 .await?;
1342 }
1343 }
1344 }
1345 };
1346 }
1347
1348 check_max_limit!(
1349 TooManyTypeNodes, self.limits;
1350 max_type_nodes > self.datatypes.len()
1351 );
1352
1353 self.datatypes.insert(key, def);
1354 }
1355 }
1356 }
1357
1358 Ok(())
1359 }
1360
1361 async fn add_signature<T: PackageStore + ?Sized>(
1364 &mut self,
1365 sig: OpenSignatureBody,
1366 store: &T,
1367 context: &Package,
1368 visit_fields: bool,
1369 ) -> Result<()> {
1370 use OpenSignatureBody as O;
1371
1372 let mut frontier = vec![sig];
1373 while let Some(sig) = frontier.pop() {
1374 match sig {
1375 O::Address
1376 | O::Bool
1377 | O::U8
1378 | O::U16
1379 | O::U32
1380 | O::U64
1381 | O::U128
1382 | O::U256
1383 | O::TypeParameter(_) => {
1384 }
1386
1387 O::Vector(sig) => frontier.push(*sig),
1388
1389 O::Datatype(key, params) => {
1390 check_max_limit!(
1391 TooManyTypeParams, self.limits;
1392 max_type_argument_width >= params.len()
1393 );
1394
1395 let params_count = params.len();
1396 let data_count = self.datatypes.len();
1397 frontier.extend(params);
1398
1399 let type_params = if let Some(def) = self.datatypes.get(&key) {
1400 &def.type_params
1401 } else {
1402 check_max_limit!(
1403 TooManyTypeNodes, self.limits;
1404 max_type_nodes > data_count
1405 );
1406
1407 let storage_id = context.relocate(key.package)?;
1409 let package = store.fetch(storage_id).await?;
1410
1411 let def = package.data_def(&key.module, &key.name)?;
1412 if visit_fields {
1413 match &def.data {
1414 MoveData::Struct(fields) => {
1415 frontier.extend(fields.iter().map(|f| &f.1).cloned());
1416 }
1417 MoveData::Enum(variants) => {
1418 frontier.extend(
1419 variants
1420 .iter()
1421 .flat_map(|v| v.signatures.iter().map(|(_, s)| s))
1422 .cloned(),
1423 );
1424 }
1425 };
1426 }
1427
1428 &self.datatypes.entry(key).or_insert(def).type_params
1429 };
1430
1431 if type_params.len() != params_count {
1432 return Err(Error::TypeArityMismatch(type_params.len(), params_count));
1433 }
1434 }
1435 }
1436 }
1437
1438 Ok(())
1439 }
1440
1441 fn canonicalize_type(&self, tag: &mut TypeTag) -> Result<()> {
1446 use TypeTag as T;
1447
1448 match tag {
1449 T::Signer => return Err(Error::UnexpectedSigner),
1450 T::Address | T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 => {
1451 }
1453
1454 T::Vector(tag) => self.canonicalize_type(tag.as_mut())?,
1455
1456 T::Struct(s) => {
1457 let mut type_params = s.type_params().to_vec();
1458 for tag in &mut type_params {
1459 self.canonicalize_type(tag)?;
1460 }
1461
1462 let key = DatatypeRef::from(s.as_ref());
1464 let def = &self.datatypes[&key];
1465
1466 *s.as_mut() = StructTag::new(
1467 def.defining_id,
1468 s.module().clone(),
1469 s.name().clone(),
1470 type_params,
1471 );
1472 }
1473 }
1474
1475 Ok(())
1476 }
1477
1478 fn resolve_type_layout(
1487 &self,
1488 tag: &TypeTag,
1489 max_depth: usize,
1490 ) -> Result<(MoveTypeLayout, usize)> {
1491 use MoveTypeLayout as L;
1492 use TypeTag as T;
1493
1494 if max_depth == 0 {
1495 return Err(Error::ValueNesting(
1496 self.limits.map_or(0, |l| l.max_move_value_depth),
1497 ));
1498 }
1499
1500 Ok(match tag {
1501 T::Signer => return Err(Error::UnexpectedSigner),
1502
1503 T::Address => (L::Address, 1),
1504 T::Bool => (L::Bool, 1),
1505 T::U8 => (L::U8, 1),
1506 T::U16 => (L::U16, 1),
1507 T::U32 => (L::U32, 1),
1508 T::U64 => (L::U64, 1),
1509 T::U128 => (L::U128, 1),
1510 T::U256 => (L::U256, 1),
1511
1512 T::Vector(tag) => {
1513 let (layout, depth) = self.resolve_type_layout(tag, max_depth - 1)?;
1514 (L::Vector(Box::new(layout)), depth + 1)
1515 }
1516
1517 T::Struct(s) => {
1518 let param_layouts = s
1533 .type_params()
1534 .iter()
1535 .map(|tag| self.resolve_type_layout(tag, max_depth - 1))
1538 .collect::<Result<Vec<_>>>()?;
1539
1540 let type_params = param_layouts
1545 .iter()
1546 .map(|l| move_core_types::language_storage::TypeTag::from(&l.0))
1547 .map(|tt| type_tag_core_to_sdk(&tt))
1548 .collect();
1549
1550 let key = DatatypeRef::from(s.as_ref());
1552 let def = &self.datatypes[&key];
1553
1554 let type_ = StructTag::new(
1555 def.defining_id,
1556 s.module().clone(),
1557 s.name().clone(),
1558 type_params,
1559 );
1560
1561 self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1562 }
1563 })
1564 }
1565
1566 fn resolve_datatype_signature(
1574 &self,
1575 data_def: &DataDef,
1576 type_: StructTag,
1577 param_layouts: Vec<(MoveTypeLayout, usize)>,
1578 max_depth: usize,
1579 ) -> Result<(MoveTypeLayout, usize)> {
1580 Ok(match &data_def.data {
1581 MoveData::Struct(fields) => {
1582 let mut resolved_fields = Vec::with_capacity(fields.len());
1583 let mut field_depth = 0;
1584
1585 for (name, sig) in fields {
1586 let (layout, depth) =
1587 self.resolve_signature_layout(sig, ¶m_layouts, max_depth - 1)?;
1588
1589 field_depth = field_depth.max(depth);
1590 resolved_fields.push(MoveFieldLayout {
1591 name: move_core_types::identifier::Identifier::new(name.as_str())
1592 .map_err(|_| Error::NotAnIdentifier(name.to_string()))?,
1593 layout,
1594 })
1595 }
1596
1597 (
1598 MoveTypeLayout::Struct(Box::new(MoveStructLayout {
1599 type_: struct_tag_sdk_to_core(&type_),
1600 fields: resolved_fields,
1601 })),
1602 field_depth + 1,
1603 )
1604 }
1605 MoveData::Enum(variants) => {
1606 let mut field_depth = 0;
1607 let mut resolved_variants = BTreeMap::new();
1608
1609 for (tag, variant) in variants.iter().enumerate() {
1610 let mut fields = Vec::with_capacity(variant.signatures.len());
1611 for (name, sig) in &variant.signatures {
1612 let (layout, depth) =
1614 self.resolve_signature_layout(sig, ¶m_layouts, max_depth - 1)?;
1615
1616 field_depth = field_depth.max(depth);
1617 fields.push(MoveFieldLayout {
1618 name: move_core_types::identifier::Identifier::new(name.as_str())
1619 .map_err(|_| Error::NotAnIdentifier(name.to_string()))?,
1620 layout,
1621 })
1622 }
1623 resolved_variants.insert(
1624 (
1625 move_core_types::identifier::Identifier::new(variant.name.as_str())
1626 .map_err(|_| Error::NotAnIdentifier(variant.name.to_string()))?,
1627 tag as u16,
1628 ),
1629 fields,
1630 );
1631 }
1632
1633 (
1634 MoveTypeLayout::Enum(Box::new(MoveEnumLayout {
1635 type_: struct_tag_sdk_to_core(&type_),
1636 variants: resolved_variants,
1637 })),
1638 field_depth + 1,
1639 )
1640 }
1641 })
1642 }
1643
1644 fn resolve_signature_layout(
1652 &self,
1653 sig: &OpenSignatureBody,
1654 param_layouts: &[(MoveTypeLayout, usize)],
1655 max_depth: usize,
1656 ) -> Result<(MoveTypeLayout, usize)> {
1657 use MoveTypeLayout as L;
1658 use OpenSignatureBody as O;
1659
1660 if max_depth == 0 {
1661 return Err(Error::ValueNesting(
1662 self.limits.map_or(0, |l| l.max_move_value_depth),
1663 ));
1664 }
1665
1666 Ok(match sig {
1667 O::Address => (L::Address, 1),
1668 O::Bool => (L::Bool, 1),
1669 O::U8 => (L::U8, 1),
1670 O::U16 => (L::U16, 1),
1671 O::U32 => (L::U32, 1),
1672 O::U64 => (L::U64, 1),
1673 O::U128 => (L::U128, 1),
1674 O::U256 => (L::U256, 1),
1675
1676 O::TypeParameter(ix) => {
1677 let (layout, depth) = param_layouts
1678 .get(*ix as usize)
1679 .ok_or_else(|| Error::TypeParamOOB(*ix, param_layouts.len()))
1680 .cloned()?;
1681
1682 if depth > max_depth {
1686 return Err(Error::ValueNesting(
1687 self.limits.map_or(0, |l| l.max_move_value_depth),
1688 ));
1689 }
1690
1691 (layout, depth)
1692 }
1693
1694 O::Vector(sig) => {
1695 let (layout, depth) =
1696 self.resolve_signature_layout(sig.as_ref(), param_layouts, max_depth - 1)?;
1697
1698 (L::Vector(Box::new(layout)), depth + 1)
1699 }
1700
1701 O::Datatype(key, params) => {
1702 let def = &self.datatypes[key];
1704
1705 let param_layouts = params
1706 .iter()
1707 .map(|sig| self.resolve_signature_layout(sig, param_layouts, max_depth - 1))
1708 .collect::<Result<Vec<_>>>()?;
1709
1710 let type_params: Vec<TypeTag> = param_layouts
1715 .iter()
1716 .map(|l| move_core_types::language_storage::TypeTag::from(&l.0))
1717 .map(|tt| type_tag_core_to_sdk(&tt))
1718 .collect();
1719
1720 let type_ = StructTag::new(
1721 def.defining_id,
1722 ident(&key.module)?,
1723 ident(&key.name)?,
1724 type_params,
1725 );
1726
1727 self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1728 }
1729 })
1730 }
1731
1732 fn resolve_abilities(&self, tag: &TypeTag) -> Result<AbilitySet> {
1736 use TypeTag as T;
1737 Ok(match tag {
1738 T::Signer => return Err(Error::UnexpectedSigner),
1739
1740 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => {
1741 AbilitySet::PRIMITIVES
1742 }
1743
1744 T::Vector(tag) => self.resolve_abilities(tag)?.intersect(AbilitySet::VECTOR),
1745
1746 T::Struct(s) => {
1747 let key = DatatypeRef::from(s.as_ref());
1749 let def = &self.datatypes[&key];
1750
1751 if def.type_params.len() != s.type_params().len() {
1752 return Err(Error::TypeArityMismatch(
1753 def.type_params.len(),
1754 s.type_params().len(),
1755 ));
1756 }
1757
1758 let param_abilities: Result<Vec<AbilitySet>> = s
1759 .type_params()
1760 .iter()
1761 .zip(def.type_params.iter())
1762 .map(|(p, d)| {
1763 if d.is_phantom {
1764 Ok(AbilitySet::EMPTY)
1765 } else {
1766 self.resolve_abilities(p)
1767 }
1768 })
1769 .collect();
1770
1771 AbilitySet::polymorphic_abilities(
1772 def.abilities,
1773 def.type_params.iter().map(|p| p.is_phantom),
1774 param_abilities?,
1775 )
1776 .map_err(|e| Error::Unexpected(Arc::new(e)))?
1779 }
1780 })
1781 }
1782
1783 fn relocate_signature(&self, sig: &mut OpenSignatureBody) -> Result<()> {
1788 use OpenSignatureBody as O;
1789
1790 match sig {
1791 O::Address | O::Bool | O::U8 | O::U16 | O::U32 | O::U64 | O::U128 | O::U256 => {
1792 }
1794
1795 O::TypeParameter(_) => { }
1796
1797 O::Vector(sig) => self.relocate_signature(sig.as_mut())?,
1798
1799 O::Datatype(key, params) => {
1800 let defining_id = &self.datatypes[key].defining_id;
1802 for param in params {
1803 self.relocate_signature(param)?;
1804 }
1805
1806 key.package = *defining_id;
1807 }
1808 }
1809
1810 Ok(())
1811 }
1812}
1813
1814impl<'s> From<&'s StructTag> for DatatypeRef<'s, 's> {
1815 fn from(tag: &'s StructTag) -> Self {
1816 DatatypeRef {
1817 package: tag.address(),
1818 module: tag.module().as_str().into(),
1819 name: tag.name().as_str().into(),
1820 }
1821 }
1822}
1823
1824fn ident(s: &str) -> Result<Identifier> {
1827 Identifier::new(s).map_err(|_| Error::NotAnIdentifier(s.to_string()))
1828}
1829
1830fn read_signature(idx: SignatureIndex, bytecode: &CompiledModule) -> Result<Vec<OpenSignature>> {
1833 let MoveSignature(tokens) = bytecode.signature_at(idx);
1834 let mut sigs = Vec::with_capacity(tokens.len());
1835
1836 for token in tokens {
1837 sigs.push(OpenSignature::read(token, bytecode)?);
1838 }
1839
1840 Ok(sigs)
1841}
1842
1843#[cfg(test)]
1844mod tests {
1845 use std::{
1846 path::PathBuf,
1847 str::FromStr,
1848 sync::{Arc, RwLock},
1849 };
1850
1851 use async_trait::async_trait;
1852 use iota_move_build::{BuildConfig, CompiledPackage};
1853 use iota_types::{
1854 base_types::{Identifier, ObjectID, random_object_ref},
1855 error::IotaResult,
1856 };
1857 use move_binary_format::file_format::Ability;
1858 use move_compiler::compiled_unit::NamedCompiledModule;
1859
1860 use super::*;
1861
1862 fn fmt(struct_layout: MoveTypeLayout, enum_layout: MoveTypeLayout) -> String {
1863 format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",)
1864 }
1865
1866 #[tokio::test]
1867 async fn test_simple_canonical_type() {
1868 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1869 let package_resolver = Resolver::new(cache);
1870
1871 let input = type_("0xa0::m::T0");
1872 let expect = input.clone();
1873 let actual = package_resolver.canonical_type(input).await.unwrap();
1874 assert_eq!(expect, actual);
1875 }
1876
1877 #[tokio::test]
1878 async fn test_upgraded_canonical_type() {
1879 let (_, cache) = package_cache([
1880 (1, build_package("a0").unwrap(), a0_types()),
1881 (2, build_package("a1").unwrap(), a1_types()),
1882 ]);
1883
1884 let package_resolver = Resolver::new(cache);
1885
1886 let input = type_("0xa1::m::T3");
1887 let expect = input.clone();
1888 let actual = package_resolver.canonical_type(input).await.unwrap();
1889 assert_eq!(expect, actual);
1890 }
1891
1892 #[tokio::test]
1893 async fn test_latest_canonical_type() {
1894 let (_, cache) = package_cache([
1895 (1, build_package("a0").unwrap(), a0_types()),
1896 (2, build_package("a1").unwrap(), a1_types()),
1897 ]);
1898
1899 let package_resolver = Resolver::new(cache);
1900
1901 let input = type_("0xa1::m::T0");
1902 let expect = type_("0xa0::m::T0");
1903 let actual = package_resolver.canonical_type(input).await.unwrap();
1904 assert_eq!(expect, actual);
1905 }
1906
1907 #[tokio::test]
1908 async fn test_type_param_canonical_type() {
1909 let (_, cache) = package_cache([
1910 (1, build_package("a0").unwrap(), a0_types()),
1911 (2, build_package("a1").unwrap(), a1_types()),
1912 ]);
1913
1914 let package_resolver = Resolver::new(cache);
1915
1916 let input = type_("0xa1::m::T1<0xa1::m::T0, 0xa1::m::T3>");
1917 let expect = type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>");
1918 let actual = package_resolver.canonical_type(input).await.unwrap();
1919 assert_eq!(expect, actual);
1920 }
1921
1922 #[tokio::test]
1923 async fn test_canonical_err_package_too_old() {
1924 let (_, cache) = package_cache([
1925 (1, build_package("a0").unwrap(), a0_types()),
1926 (2, build_package("a1").unwrap(), a1_types()),
1927 ]);
1928
1929 let package_resolver = Resolver::new(cache);
1930
1931 let input = type_("0xa0::m::T3");
1932 let err = package_resolver.canonical_type(input).await.unwrap_err();
1933 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
1934 }
1935
1936 #[tokio::test]
1937 async fn test_canonical_err_signer() {
1938 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1939
1940 let package_resolver = Resolver::new(cache);
1941
1942 let input = type_("0xa0::m::T1<0xa0::m::T0, signer>");
1943 let err = package_resolver.canonical_type(input).await.unwrap_err();
1944 assert!(matches!(err, Error::UnexpectedSigner));
1945 }
1946
1947 #[tokio::test]
1950 async fn test_simple_type_layout() {
1951 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1952 let package_resolver = Resolver::new(cache);
1953 let struct_layout = package_resolver
1954 .type_layout(type_("0xa0::m::T0"))
1955 .await
1956 .unwrap();
1957 let enum_layout = package_resolver
1958 .type_layout(type_("0xa0::m::E0"))
1959 .await
1960 .unwrap();
1961 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1962 }
1963
1964 #[tokio::test]
1966 async fn test_cross_module_layout() {
1967 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1968 let resolver = Resolver::new(cache);
1969 let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap();
1970 let enum_layout = resolver.type_layout(type_("0xa0::n::E0")).await.unwrap();
1971 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1972 }
1973
1974 #[tokio::test]
1976 async fn test_cross_package_layout() {
1977 let (_, cache) = package_cache([
1978 (1, build_package("a0").unwrap(), a0_types()),
1979 (1, build_package("b0").unwrap(), b0_types()),
1980 ]);
1981 let resolver = Resolver::new(cache);
1982
1983 let struct_layout = resolver.type_layout(type_("0xb0::m::T0")).await.unwrap();
1984 let enum_layout = resolver.type_layout(type_("0xb0::m::E0")).await.unwrap();
1985 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1986 }
1987
1988 #[tokio::test]
1991 async fn test_upgraded_package_layout() {
1992 let (_, cache) = package_cache([
1993 (1, build_package("a0").unwrap(), a0_types()),
1994 (2, build_package("a1").unwrap(), a1_types()),
1995 ]);
1996 let resolver = Resolver::new(cache);
1997
1998 let struct_layout = resolver.type_layout(type_("0xa1::n::T1")).await.unwrap();
1999 let enum_layout = resolver.type_layout(type_("0xa1::n::E1")).await.unwrap();
2000 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2001 }
2002
2003 #[tokio::test]
2007 async fn test_multiple_linkage_contexts_layout() {
2008 let (_, cache) = package_cache([
2009 (1, build_package("a0").unwrap(), a0_types()),
2010 (2, build_package("a1").unwrap(), a1_types()),
2011 ]);
2012 let resolver = Resolver::new(cache);
2013
2014 let struct_layout = resolver
2015 .type_layout(type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"))
2016 .await
2017 .unwrap();
2018 let enum_layout = resolver
2019 .type_layout(type_("0xa0::m::E1<0xa0::m::E0, 0xa1::m::E3>"))
2020 .await
2021 .unwrap();
2022 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2023 }
2024
2025 #[tokio::test]
2031 async fn test_upgraded_package_non_defining_id_layout() {
2032 let (_, cache) = package_cache([
2033 (1, build_package("a0").unwrap(), a0_types()),
2034 (2, build_package("a1").unwrap(), a1_types()),
2035 ]);
2036 let resolver = Resolver::new(cache);
2037
2038 let struct_layout = resolver
2039 .type_layout(type_("0xa1::m::T1<0xa1::m::T3, 0xa1::m::T0>"))
2040 .await
2041 .unwrap();
2042 let enum_layout = resolver
2043 .type_layout(type_("0xa1::m::E1<0xa1::m::E3, 0xa1::m::E0>"))
2044 .await
2045 .unwrap();
2046 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2047 }
2048
2049 #[tokio::test]
2053 async fn test_relinking_layout() {
2054 let (_, cache) = package_cache([
2055 (1, build_package("a0").unwrap(), a0_types()),
2056 (2, build_package("a1").unwrap(), a1_types()),
2057 (1, build_package("b0").unwrap(), b0_types()),
2058 (1, build_package("c0").unwrap(), c0_types()),
2059 ]);
2060 let resolver = Resolver::new(cache);
2061
2062 let struct_layout = resolver.type_layout(type_("0xc0::m::T0")).await.unwrap();
2063 let enum_layout = resolver.type_layout(type_("0xc0::m::E0")).await.unwrap();
2064 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2065 }
2066
2067 #[tokio::test]
2068 async fn test_value_nesting_boundary_layout() {
2069 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2070
2071 let resolver = Resolver::new_with_limits(
2072 cache,
2073 Limits {
2074 max_type_argument_width: 100,
2075 max_type_argument_depth: 100,
2076 max_type_nodes: 100,
2077 max_move_value_depth: 3,
2078 },
2079 );
2080
2081 let struct_layout = resolver
2083 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2084 .await
2085 .unwrap();
2086 let enum_layout = resolver
2087 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2088 .await
2089 .unwrap();
2090 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2091 }
2092
2093 #[tokio::test]
2094 async fn test_err_value_nesting_simple_layout() {
2095 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2096
2097 let resolver = Resolver::new_with_limits(
2098 cache,
2099 Limits {
2100 max_type_argument_width: 100,
2101 max_type_argument_depth: 100,
2102 max_type_nodes: 100,
2103 max_move_value_depth: 2,
2104 },
2105 );
2106
2107 let struct_err = resolver
2109 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2110 .await
2111 .unwrap_err();
2112 let enum_err = resolver
2113 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2114 .await
2115 .unwrap_err();
2116 assert!(matches!(struct_err, Error::ValueNesting(2)));
2117 assert!(matches!(enum_err, Error::ValueNesting(2)));
2118 }
2119
2120 #[tokio::test]
2121 async fn test_err_value_nesting_big_type_param_layout() {
2122 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2123
2124 let resolver = Resolver::new_with_limits(
2125 cache,
2126 Limits {
2127 max_type_argument_width: 100,
2128 max_type_argument_depth: 100,
2129 max_type_nodes: 100,
2130 max_move_value_depth: 3,
2131 },
2132 );
2133
2134 let struct_err = resolver
2138 .type_layout(type_("0xa0::m::T1<vector<vector<u8>>, u8>"))
2139 .await
2140 .unwrap_err();
2141 let enum_err = resolver
2142 .type_layout(type_("0xa0::m::E1<vector<vector<u8>>, u8>"))
2143 .await
2144 .unwrap_err();
2145 assert!(matches!(struct_err, Error::ValueNesting(3)));
2146 assert!(matches!(enum_err, Error::ValueNesting(3)));
2147 }
2148
2149 #[tokio::test]
2150 async fn test_err_value_nesting_big_phantom_type_param_layout() {
2151 let (_, cache) = package_cache([
2152 (1, build_package("iota").unwrap(), iota_types()),
2153 (1, build_package("d0").unwrap(), d0_types()),
2154 ]);
2155
2156 let resolver = Resolver::new_with_limits(
2157 cache,
2158 Limits {
2159 max_type_argument_width: 100,
2160 max_type_argument_depth: 100,
2161 max_type_nodes: 100,
2162 max_move_value_depth: 3,
2163 },
2164 );
2165
2166 let _ = resolver
2168 .type_layout(type_("0xd0::m::O<u8, u8>"))
2169 .await
2170 .unwrap();
2171 let _ = resolver
2172 .type_layout(type_("0xd0::m::EO<u8, u8>"))
2173 .await
2174 .unwrap();
2175
2176 let struct_err = resolver
2181 .type_layout(type_("0xd0::m::O<u8, vector<vector<u8>>>"))
2182 .await
2183 .unwrap_err();
2184 let enum_err = resolver
2185 .type_layout(type_("0xd0::m::EO<u8, vector<vector<u8>>>"))
2186 .await
2187 .unwrap_err();
2188 assert!(matches!(struct_err, Error::ValueNesting(3)));
2189 assert!(matches!(enum_err, Error::ValueNesting(3)));
2190 }
2191
2192 #[tokio::test]
2193 async fn test_err_value_nesting_type_param_application_layout() {
2194 let (_, cache) = package_cache([
2195 (1, build_package("iota").unwrap(), iota_types()),
2196 (1, build_package("d0").unwrap(), d0_types()),
2197 ]);
2198
2199 let resolver = Resolver::new_with_limits(
2200 cache,
2201 Limits {
2202 max_type_argument_width: 100,
2203 max_type_argument_depth: 100,
2204 max_type_nodes: 100,
2205 max_move_value_depth: 3,
2206 },
2207 );
2208
2209 let struct_err = resolver
2213 .type_layout(type_("0xd0::m::O<vector<u8>, u8>"))
2214 .await
2215 .unwrap_err();
2216 let enum_err = resolver
2217 .type_layout(type_("0xd0::m::EO<vector<u8>, u8>"))
2218 .await
2219 .unwrap_err();
2220
2221 assert!(matches!(struct_err, Error::ValueNesting(3)));
2222 assert!(matches!(enum_err, Error::ValueNesting(3)));
2223 }
2224
2225 #[tokio::test]
2226 async fn test_system_package_invalidation() {
2227 let (inner, cache) = package_cache([(1, build_package("s0").unwrap(), s0_types())]);
2228 let resolver = Resolver::new(cache);
2229
2230 let struct_not_found = resolver.type_layout(type_("0x1::m::T1")).await.unwrap_err();
2231 let enum_not_found = resolver.type_layout(type_("0x1::m::E1")).await.unwrap_err();
2232 assert!(matches!(struct_not_found, Error::DatatypeNotFound(_, _, _)));
2233 assert!(matches!(enum_not_found, Error::DatatypeNotFound(_, _, _)));
2234
2235 inner.write().unwrap().replace(
2237 addr("0x1"),
2238 cached_package(
2239 2,
2240 BTreeMap::new(),
2241 &build_package("s1").unwrap(),
2242 &s1_types(),
2243 ),
2244 );
2245
2246 resolver.package_store().evict([addr("0x1")]);
2248
2249 let struct_layout = resolver.type_layout(type_("0x1::m::T1")).await.unwrap();
2250 let enum_layout = resolver.type_layout(type_("0x1::m::E1")).await.unwrap();
2251 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2252 }
2253
2254 #[tokio::test]
2255 async fn test_caching() {
2256 let (inner, cache) = package_cache([
2257 (1, build_package("a0").unwrap(), a0_types()),
2258 (1, build_package("s0").unwrap(), s0_types()),
2259 ]);
2260 let resolver = Resolver::new(cache);
2261
2262 assert_eq!(inner.read().unwrap().fetches, 0);
2263 let l0 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2264
2265 assert_eq!(inner.read().unwrap().fetches, 1);
2267
2268 let l1 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2270 assert_eq!(format!("{l0}"), format!("{l1}"));
2271 assert_eq!(inner.read().unwrap().fetches, 1);
2272
2273 let l2 = resolver.type_layout(type_("0xa0::m::T2")).await.unwrap();
2275 assert_ne!(format!("{l0}"), format!("{l2}"));
2276 assert_eq!(inner.read().unwrap().fetches, 1);
2277
2278 resolver.type_layout(type_("0xa0::m::E0")).await.unwrap();
2280 assert_eq!(inner.read().unwrap().fetches, 1);
2281
2282 let l3 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2284 assert_eq!(inner.read().unwrap().fetches, 2);
2285
2286 let l4 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2288 assert_eq!(format!("{l3}"), format!("{l4}"));
2289 assert_eq!(inner.read().unwrap().fetches, 2);
2290
2291 let el4 = resolver.type_layout(type_("0x1::m::E0")).await.unwrap();
2293 assert_ne!(format!("{el4}"), format!("{l4}"));
2294 assert_eq!(inner.read().unwrap().fetches, 2);
2295
2296 inner.write().unwrap().replace(
2298 addr("0x1"),
2299 cached_package(
2300 2,
2301 BTreeMap::new(),
2302 &build_package("s1").unwrap(),
2303 &s1_types(),
2304 ),
2305 );
2306
2307 resolver.package_store().evict([addr("0x1")]);
2309
2310 let l5 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2315 assert_eq!(format!("{l4}"), format!("{l5}"));
2316 assert_eq!(inner.read().unwrap().fetches, 3);
2317 }
2318
2319 #[tokio::test]
2320 async fn test_layout_err_not_a_package() {
2321 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2322 let resolver = Resolver::new(cache);
2323 let err = resolver
2324 .type_layout(type_("0x42::m::T0"))
2325 .await
2326 .unwrap_err();
2327 assert!(matches!(err, Error::PackageNotFound(_)));
2328 }
2329
2330 #[tokio::test]
2331 async fn test_layout_err_no_module() {
2332 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2333 let resolver = Resolver::new(cache);
2334 let err = resolver
2335 .type_layout(type_("0xa0::l::T0"))
2336 .await
2337 .unwrap_err();
2338 assert!(matches!(err, Error::ModuleNotFound(_, _)));
2339 }
2340
2341 #[tokio::test]
2342 async fn test_layout_err_no_struct() {
2343 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2344 let resolver = Resolver::new(cache);
2345
2346 let err = resolver
2347 .type_layout(type_("0xa0::m::T9"))
2348 .await
2349 .unwrap_err();
2350 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
2351 }
2352
2353 #[tokio::test]
2354 async fn test_layout_err_type_arity() {
2355 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2356 let resolver = Resolver::new(cache);
2357
2358 let err = resolver
2360 .type_layout(type_("0xa0::m::T1<u8>"))
2361 .await
2362 .unwrap_err();
2363 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2364
2365 let err = resolver
2367 .type_layout(type_("0xa0::m::T1<u8, u16, u32>"))
2368 .await
2369 .unwrap_err();
2370 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2371 }
2372
2373 #[tokio::test]
2374 async fn test_structs() {
2375 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2376 let a0 = cache.fetch(addr("0xa0")).await.unwrap();
2377 let m = a0.module("m").unwrap();
2378
2379 assert_eq!(
2380 m.structs(None, None).collect::<Vec<_>>(),
2381 vec!["T0", "T1", "T2"],
2382 );
2383
2384 assert_eq!(m.structs(None, Some("T1")).collect::<Vec<_>>(), vec!["T0"],);
2385
2386 assert_eq!(
2387 m.structs(Some("T0"), Some("T2")).collect::<Vec<_>>(),
2388 vec!["T1"],
2389 );
2390
2391 assert_eq!(m.structs(Some("T1"), None).collect::<Vec<_>>(), vec!["T2"],);
2392
2393 let t0 = m.struct_def("T0").unwrap().unwrap();
2394 let t1 = m.struct_def("T1").unwrap().unwrap();
2395 let t2 = m.struct_def("T2").unwrap().unwrap();
2396
2397 insta::assert_snapshot!(format!(
2398 "a0::m::T0: {t0:#?}\n\
2399 a0::m::T1: {t1:#?}\n\
2400 a0::m::T2: {t2:#?}",
2401 ));
2402 }
2403
2404 #[tokio::test]
2405 async fn test_enums() {
2406 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2407 let a0 = cache
2408 .fetch(IotaAddress::from_str("0xa0").unwrap())
2409 .await
2410 .unwrap();
2411 let m = a0.module("m").unwrap();
2412
2413 assert_eq!(
2414 m.enums(None, None).collect::<Vec<_>>(),
2415 vec!["E0", "E1", "E2"],
2416 );
2417
2418 assert_eq!(m.enums(None, Some("E1")).collect::<Vec<_>>(), vec!["E0"],);
2419
2420 assert_eq!(
2421 m.enums(Some("E0"), Some("E2")).collect::<Vec<_>>(),
2422 vec!["E1"],
2423 );
2424
2425 assert_eq!(m.enums(Some("E1"), None).collect::<Vec<_>>(), vec!["E2"],);
2426
2427 let e0 = m.enum_def("E0").unwrap().unwrap();
2428 let e1 = m.enum_def("E1").unwrap().unwrap();
2429 let e2 = m.enum_def("E2").unwrap().unwrap();
2430
2431 insta::assert_snapshot!(format!(
2432 "a0::m::E0: {e0:#?}\n\
2433 a0::m::E1: {e1:#?}\n\
2434 a0::m::E2: {e2:#?}",
2435 ));
2436 }
2437
2438 #[tokio::test]
2439 async fn test_functions() {
2440 let (_, cache) = package_cache([
2441 (1, build_package("a0").unwrap(), a0_types()),
2442 (2, build_package("a1").unwrap(), a1_types()),
2443 (1, build_package("b0").unwrap(), b0_types()),
2444 (1, build_package("c0").unwrap(), c0_types()),
2445 ]);
2446
2447 let c0 = cache.fetch(addr("0xc0")).await.unwrap();
2448 let m = c0.module("m").unwrap();
2449
2450 assert_eq!(
2451 m.functions(None, None).collect::<Vec<_>>(),
2452 vec!["bar", "baz", "foo"],
2453 );
2454
2455 assert_eq!(
2456 m.functions(None, Some("baz")).collect::<Vec<_>>(),
2457 vec!["bar"],
2458 );
2459
2460 assert_eq!(
2461 m.functions(Some("bar"), Some("foo")).collect::<Vec<_>>(),
2462 vec!["baz"],
2463 );
2464
2465 assert_eq!(
2466 m.functions(Some("baz"), None).collect::<Vec<_>>(),
2467 vec!["foo"],
2468 );
2469
2470 let foo = m.function_def("foo").unwrap().unwrap();
2471 let bar = m.function_def("bar").unwrap().unwrap();
2472 let baz = m.function_def("baz").unwrap().unwrap();
2473
2474 insta::assert_snapshot!(format!(
2475 "c0::m::foo: {foo:#?}\n\
2476 c0::m::bar: {bar:#?}\n\
2477 c0::m::baz: {baz:#?}"
2478 ));
2479 }
2480
2481 #[tokio::test]
2482 async fn test_function_parameters() {
2483 let (_, cache) = package_cache([
2484 (1, build_package("a0").unwrap(), a0_types()),
2485 (2, build_package("a1").unwrap(), a1_types()),
2486 (1, build_package("b0").unwrap(), b0_types()),
2487 (1, build_package("c0").unwrap(), c0_types()),
2488 ]);
2489
2490 let resolver = Resolver::new(cache);
2491 let c0 = addr("0xc0");
2492
2493 let foo = resolver.function_signature(c0, "m", "foo").await.unwrap();
2494 let bar = resolver.function_signature(c0, "m", "bar").await.unwrap();
2495 let baz = resolver.function_signature(c0, "m", "baz").await.unwrap();
2496
2497 insta::assert_snapshot!(format!(
2498 "c0::m::foo: {foo:#?}\n\
2499 c0::m::bar: {bar:#?}\n\
2500 c0::m::baz: {baz:#?}"
2501 ));
2502 }
2503
2504 #[tokio::test]
2505 async fn test_signature_instantiation() {
2506 use OpenSignatureBody as O;
2507 use TypeTag as T;
2508
2509 let sig = O::Datatype(
2510 key("0x2::table::Table"),
2511 vec![
2512 O::TypeParameter(1),
2513 O::Vector(Box::new(O::Datatype(
2514 key("0x1::option::Option"),
2515 vec![O::TypeParameter(0)],
2516 ))),
2517 ],
2518 );
2519
2520 insta::assert_debug_snapshot!(sig.instantiate(&[T::U64, T::Bool]).unwrap());
2521 }
2522
2523 #[tokio::test]
2524 async fn test_signature_instantiation_error() {
2525 use OpenSignatureBody as O;
2526 use TypeTag as T;
2527
2528 let sig = O::Datatype(
2529 key("0x2::table::Table"),
2530 vec![
2531 O::TypeParameter(1),
2532 O::Vector(Box::new(O::Datatype(
2533 key("0x1::option::Option"),
2534 vec![O::TypeParameter(99)],
2535 ))),
2536 ],
2537 );
2538
2539 insta::assert_snapshot!(
2540 sig.instantiate(&[T::U64, T::Bool]).unwrap_err(),
2541 @"Type Parameter 99 out of bounds (2)"
2542 );
2543 }
2544
2545 #[tokio::test]
2547 async fn test_primitive_abilities() {
2548 use Ability as A;
2549 use AbilitySet as S;
2550
2551 let (_, cache) = package_cache([]);
2552 let resolver = Resolver::new(cache);
2553
2554 for prim in ["address", "bool", "u8", "u16", "u32", "u64", "u128", "u256"] {
2555 assert_eq!(
2556 resolver.abilities(type_(prim)).await.unwrap(),
2557 S::EMPTY | A::Copy | A::Drop | A::Store,
2558 "Unexpected primitive abilities for: {prim}",
2559 );
2560 }
2561 }
2562
2563 #[tokio::test]
2565 async fn test_simple_generic_abilities() {
2566 use Ability as A;
2567 use AbilitySet as S;
2568
2569 let (_, cache) = package_cache([
2570 (1, build_package("iota").unwrap(), iota_types()),
2571 (1, build_package("d0").unwrap(), d0_types()),
2572 ]);
2573 let resolver = Resolver::new(cache);
2574
2575 let a1 = resolver
2576 .abilities(type_("0xd0::m::T<u32, u64>"))
2577 .await
2578 .unwrap();
2579 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop | A::Store);
2580
2581 let a2 = resolver
2582 .abilities(type_("0xd0::m::T<0xd0::m::S, u64>"))
2583 .await
2584 .unwrap();
2585 assert_eq!(a2, S::EMPTY | A::Drop | A::Store);
2586
2587 let a3 = resolver
2588 .abilities(type_("0xd0::m::T<0xd0::m::R, 0xd0::m::S>"))
2589 .await
2590 .unwrap();
2591 assert_eq!(a3, S::EMPTY | A::Drop);
2592
2593 let a4 = resolver
2594 .abilities(type_("0xd0::m::T<0xd0::m::Q, 0xd0::m::R>"))
2595 .await
2596 .unwrap();
2597 assert_eq!(a4, S::EMPTY);
2598 }
2599
2600 #[tokio::test]
2602 async fn test_nested_generic_abilities() {
2603 use Ability as A;
2604 use AbilitySet as S;
2605
2606 let (_, cache) = package_cache([
2607 (1, build_package("iota").unwrap(), iota_types()),
2608 (1, build_package("d0").unwrap(), d0_types()),
2609 ]);
2610 let resolver = Resolver::new(cache);
2611
2612 let a1 = resolver
2613 .abilities(type_("0xd0::m::T<0xd0::m::T<0xd0::m::R, u32>, u64>"))
2614 .await
2615 .unwrap();
2616 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop);
2617 }
2618
2619 #[tokio::test]
2622 async fn test_key_abilities() {
2623 use Ability as A;
2624 use AbilitySet as S;
2625
2626 let (_, cache) = package_cache([
2627 (1, build_package("iota").unwrap(), iota_types()),
2628 (1, build_package("d0").unwrap(), d0_types()),
2629 ]);
2630 let resolver = Resolver::new(cache);
2631
2632 let a1 = resolver
2633 .abilities(type_("0xd0::m::O<u32, u64>"))
2634 .await
2635 .unwrap();
2636 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2637
2638 let a2 = resolver
2639 .abilities(type_("0xd0::m::O<0xd0::m::S, u64>"))
2640 .await
2641 .unwrap();
2642 assert_eq!(a2, S::EMPTY | A::Key | A::Store);
2643
2644 let a3 = resolver
2647 .abilities(type_("0xd0::m::O<0xd0::m::R, u64>"))
2648 .await
2649 .unwrap();
2650 assert_eq!(a3, S::EMPTY);
2651
2652 let a4 = resolver
2654 .abilities(type_("0xd0::m::O<0xd0::m::P, u32>"))
2655 .await
2656 .unwrap();
2657 assert_eq!(a4, S::EMPTY);
2658 }
2659
2660 #[tokio::test]
2662 async fn test_phantom_abilities() {
2663 use Ability as A;
2664 use AbilitySet as S;
2665
2666 let (_, cache) = package_cache([
2667 (1, build_package("iota").unwrap(), iota_types()),
2668 (1, build_package("d0").unwrap(), d0_types()),
2669 ]);
2670 let resolver = Resolver::new(cache);
2671
2672 let a1 = resolver
2673 .abilities(type_("0xd0::m::O<u32, 0xd0::m::R>"))
2674 .await
2675 .unwrap();
2676 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2677 }
2678
2679 #[tokio::test]
2680 async fn test_err_ability_arity() {
2681 let (_, cache) = package_cache([
2682 (1, build_package("iota").unwrap(), iota_types()),
2683 (1, build_package("d0").unwrap(), d0_types()),
2684 ]);
2685 let resolver = Resolver::new(cache);
2686
2687 let err = resolver
2689 .abilities(type_("0xd0::m::T<u8>"))
2690 .await
2691 .unwrap_err();
2692 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2693
2694 let err = resolver
2696 .abilities(type_("0xd0::m::T<u8, u16, u32>"))
2697 .await
2698 .unwrap_err();
2699 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2700 }
2701
2702 #[tokio::test]
2703 async fn test_err_ability_signer() {
2704 let (_, cache) = package_cache([]);
2705 let resolver = Resolver::new(cache);
2706
2707 let err = resolver.abilities(type_("signer")).await.unwrap_err();
2708 assert!(matches!(err, Error::UnexpectedSigner));
2709 }
2710
2711 #[tokio::test]
2712 async fn test_err_too_many_type_params() {
2713 let (_, cache) = package_cache([
2714 (1, build_package("iota").unwrap(), iota_types()),
2715 (1, build_package("d0").unwrap(), d0_types()),
2716 ]);
2717
2718 let resolver = Resolver::new_with_limits(
2719 cache,
2720 Limits {
2721 max_type_argument_width: 1,
2722 max_type_argument_depth: 100,
2723 max_type_nodes: 100,
2724 max_move_value_depth: 100,
2725 },
2726 );
2727
2728 let err = resolver
2729 .abilities(type_("0xd0::m::O<u32, u64>"))
2730 .await
2731 .unwrap_err();
2732 assert!(matches!(err, Error::TooManyTypeParams(1, 2)));
2733 }
2734
2735 #[tokio::test]
2736 async fn test_err_too_many_type_nodes() {
2737 use Ability as A;
2738 use AbilitySet as S;
2739
2740 let (_, cache) = package_cache([
2741 (1, build_package("iota").unwrap(), iota_types()),
2742 (1, build_package("d0").unwrap(), d0_types()),
2743 ]);
2744
2745 let resolver = Resolver::new_with_limits(
2746 cache,
2747 Limits {
2748 max_type_argument_width: 100,
2749 max_type_argument_depth: 100,
2750 max_type_nodes: 2,
2751 max_move_value_depth: 100,
2752 },
2753 );
2754
2755 let a1 = resolver
2758 .abilities(type_("0xd0::m::O<0xd0::m::S, 0xd0::m::Q>"))
2759 .await
2760 .unwrap();
2761 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2762
2763 let err = resolver
2765 .abilities(type_("0xd0::m::T<0xd0::m::P, 0xd0::m::Q>"))
2766 .await
2767 .unwrap_err();
2768 assert!(matches!(err, Error::TooManyTypeNodes(2, _)));
2769 }
2770
2771 #[tokio::test]
2772 async fn test_err_type_param_nesting() {
2773 use Ability as A;
2774 use AbilitySet as S;
2775
2776 let (_, cache) = package_cache([
2777 (1, build_package("iota").unwrap(), iota_types()),
2778 (1, build_package("d0").unwrap(), d0_types()),
2779 ]);
2780
2781 let resolver = Resolver::new_with_limits(
2782 cache,
2783 Limits {
2784 max_type_argument_width: 100,
2785 max_type_argument_depth: 2,
2786 max_type_nodes: 100,
2787 max_move_value_depth: 100,
2788 },
2789 );
2790
2791 let a1 = resolver
2794 .abilities(type_(
2795 "0xd0::m::O<0xd0::m::S, 0xd0::m::T<vector<u32>, vector<u64>>>",
2796 ))
2797 .await
2798 .unwrap();
2799 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2800
2801 let err = resolver
2803 .abilities(type_("vector<0xd0::m::T<0xd0::m::O<u64, u32>, u16>>"))
2804 .await
2805 .unwrap_err();
2806 assert!(matches!(err, Error::TypeParamNesting(2, _)));
2807 }
2808
2809 #[tokio::test]
2810 async fn test_pure_input_layouts() {
2811 use CallArg as I;
2812 use TypeTag as T;
2813
2814 let (_, cache) = package_cache([
2815 (1, build_package("std").unwrap(), std_types()),
2816 (1, build_package("iota").unwrap(), iota_types()),
2817 (1, build_package("e0").unwrap(), e0_types()),
2818 ]);
2819
2820 let resolver = Resolver::new(cache);
2821
2822 fn ptb(t: TypeTag, y: CallArg) -> ProgrammableTransaction {
2824 ProgrammableTransaction {
2825 inputs: vec![
2826 I::ImmutableOrOwned(random_object_ref()),
2827 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2828 I::ImmutableOrOwned(random_object_ref()),
2829 y,
2830 I::ImmutableOrOwned(random_object_ref()),
2831 I::Pure(bcs::to_bytes("hello").unwrap()),
2832 I::Pure(bcs::to_bytes("world").unwrap()),
2833 ],
2834 commands: vec![Command::new_move_call(
2835 obj_id("0xe0"),
2836 Identifier::from_static("m"),
2837 Identifier::from_static("foo"),
2838 vec![t],
2839 (0..=6).map(Argument::Input).collect(),
2840 )],
2841 }
2842 }
2843
2844 let ptb_u64 = ptb(T::U64, I::Pure(bcs::to_bytes(&1u64).unwrap()));
2845
2846 let ptb_opt = ptb(
2847 TypeTag::Struct(Box::new(StructTag::new(
2848 addr("0x1"),
2849 Identifier::OPTION_MODULE,
2850 Identifier::from_static("Option"),
2851 vec![TypeTag::U64],
2852 ))),
2853 I::Pure(bcs::to_bytes(&[vec![1u64], vec![], vec![3]]).unwrap()),
2854 );
2855
2856 let ptb_obj = ptb(
2857 TypeTag::Struct(Box::new(StructTag::new(
2858 addr("0xe0"),
2859 Identifier::from_static("m"),
2860 Identifier::from_static("O"),
2861 vec![],
2862 ))),
2863 I::ImmutableOrOwned(random_object_ref()),
2864 );
2865
2866 let inputs_u64 = resolver.pure_input_layouts(&ptb_u64).await.unwrap();
2867 let inputs_opt = resolver.pure_input_layouts(&ptb_opt).await.unwrap();
2868 let inputs_obj = resolver.pure_input_layouts(&ptb_obj).await.unwrap();
2869
2870 let mut output = "---\n".to_string();
2872 for inputs in [inputs_u64, inputs_opt, inputs_obj] {
2873 for input in inputs {
2874 if let Some(layout) = input {
2875 output += &format!("{layout:#}\n");
2876 } else {
2877 output += "???\n";
2878 }
2879 }
2880 output += "---\n";
2881 }
2882
2883 insta::assert_snapshot!(output);
2884 }
2885
2886 #[tokio::test]
2889 async fn test_pure_input_layouts_overlapping() {
2890 use CallArg as I;
2891 use TypeTag as T;
2892
2893 let (_, cache) = package_cache([
2894 (1, build_package("std").unwrap(), std_types()),
2895 (1, build_package("iota").unwrap(), iota_types()),
2896 (1, build_package("e0").unwrap(), e0_types()),
2897 ]);
2898
2899 let resolver = Resolver::new(cache);
2900
2901 let ptb = ProgrammableTransaction {
2903 inputs: vec![
2904 I::ImmutableOrOwned(random_object_ref()),
2905 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2906 I::ImmutableOrOwned(random_object_ref()),
2907 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2908 I::ImmutableOrOwned(random_object_ref()),
2909 I::Pure(bcs::to_bytes("hello").unwrap()),
2910 I::Pure(bcs::to_bytes("world").unwrap()),
2911 ],
2912 commands: vec![
2913 Command::new_move_call(
2914 obj_id("0xe0"),
2915 Identifier::from_static("m"),
2916 Identifier::from_static("foo"),
2917 vec![T::U64],
2918 (0..=6).map(Argument::Input).collect(),
2919 ),
2920 Command::new_move_call(
2921 obj_id("0xe0"),
2922 Identifier::from_static("m"),
2923 Identifier::from_static("foo"),
2924 vec![T::U64],
2925 (0..=6).map(Argument::Input).collect(),
2926 ),
2927 ],
2928 };
2929
2930 let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
2931
2932 let mut output = String::new();
2934 for input in inputs {
2935 if let Some(layout) = input {
2936 output += &format!("{layout:#}\n");
2937 } else {
2938 output += "???\n";
2939 }
2940 }
2941
2942 insta::assert_snapshot!(output);
2943 }
2944 #[tokio::test]
2945 async fn test_pure_input_layouts_conflicting() {
2946 use CallArg as I;
2947 use TypeTag as T;
2948
2949 let (_, cache) = package_cache([
2950 (1, build_package("std").unwrap(), std_types()),
2951 (1, build_package("iota").unwrap(), iota_types()),
2952 (1, build_package("e0").unwrap(), e0_types()),
2953 ]);
2954
2955 let resolver = Resolver::new(cache);
2956
2957 let ptb = ProgrammableTransaction {
2958 inputs: vec![
2959 I::ImmutableOrOwned(random_object_ref()),
2960 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2961 I::ImmutableOrOwned(random_object_ref()),
2962 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2963 I::ImmutableOrOwned(random_object_ref()),
2964 I::Pure(bcs::to_bytes("hello").unwrap()),
2965 I::Pure(bcs::to_bytes("world").unwrap()),
2966 ],
2967 commands: vec![
2968 Command::new_move_call(
2969 obj_id("0xe0"),
2970 Identifier::from_static("m"),
2971 Identifier::from_static("foo"),
2972 vec![T::U64],
2973 (0..=6).map(Argument::Input).collect(),
2974 ),
2975 Command::new_make_move_vector(Some(T::U32), vec![Argument::Input(3)]),
2978 ],
2979 };
2980
2981 insta::assert_snapshot!(
2982 resolver.pure_input_layouts(&ptb).await.unwrap_err(),
2983 @"Conflicting types for input 3: u64 and u32"
2984 );
2985 }
2986
2987 type TypeOriginTable = Vec<DatatypeKey>;
2992
2993 fn a0_types() -> TypeOriginTable {
2994 vec![
2995 datakey("0xa0", "m", "T0"),
2996 datakey("0xa0", "m", "T1"),
2997 datakey("0xa0", "m", "T2"),
2998 datakey("0xa0", "m", "E0"),
2999 datakey("0xa0", "m", "E1"),
3000 datakey("0xa0", "m", "E2"),
3001 datakey("0xa0", "n", "T0"),
3002 datakey("0xa0", "n", "E0"),
3003 ]
3004 }
3005
3006 fn a1_types() -> TypeOriginTable {
3007 let mut types = a0_types();
3008
3009 types.extend([
3010 datakey("0xa1", "m", "T3"),
3011 datakey("0xa1", "m", "T4"),
3012 datakey("0xa1", "n", "T1"),
3013 datakey("0xa1", "m", "E3"),
3014 datakey("0xa1", "m", "E4"),
3015 datakey("0xa1", "n", "E1"),
3016 ]);
3017
3018 types
3019 }
3020
3021 fn b0_types() -> TypeOriginTable {
3022 vec![datakey("0xb0", "m", "T0"), datakey("0xb0", "m", "E0")]
3023 }
3024
3025 fn c0_types() -> TypeOriginTable {
3026 vec![datakey("0xc0", "m", "T0"), datakey("0xc0", "m", "E0")]
3027 }
3028
3029 fn d0_types() -> TypeOriginTable {
3030 vec![
3031 datakey("0xd0", "m", "O"),
3032 datakey("0xd0", "m", "P"),
3033 datakey("0xd0", "m", "Q"),
3034 datakey("0xd0", "m", "R"),
3035 datakey("0xd0", "m", "S"),
3036 datakey("0xd0", "m", "T"),
3037 datakey("0xd0", "m", "EO"),
3038 datakey("0xd0", "m", "EP"),
3039 datakey("0xd0", "m", "EQ"),
3040 datakey("0xd0", "m", "ER"),
3041 datakey("0xd0", "m", "ES"),
3042 datakey("0xd0", "m", "ET"),
3043 ]
3044 }
3045
3046 fn e0_types() -> TypeOriginTable {
3047 vec![datakey("0xe0", "m", "O")]
3048 }
3049
3050 fn s0_types() -> TypeOriginTable {
3051 vec![datakey("0x1", "m", "T0"), datakey("0x1", "m", "E0")]
3052 }
3053
3054 fn s1_types() -> TypeOriginTable {
3055 let mut types = s0_types();
3056
3057 types.extend([datakey("0x1", "m", "T1"), datakey("0x1", "m", "E1")]);
3058
3059 types
3060 }
3061
3062 fn iota_types() -> TypeOriginTable {
3063 vec![datakey("0x2", "object", "UID")]
3064 }
3065
3066 fn std_types() -> TypeOriginTable {
3067 vec![
3068 datakey("0x1", "ascii", "String"),
3069 datakey("0x1", "option", "Option"),
3070 datakey("0x1", "string", "String"),
3071 ]
3072 }
3073
3074 fn package_cache(
3080 packages: impl IntoIterator<Item = (u64, CompiledPackage, TypeOriginTable)>,
3081 ) -> (
3082 Arc<RwLock<InnerStore>>,
3083 PackageStoreWithLruCache<InMemoryPackageStore>,
3084 ) {
3085 let packages_by_storage_id: BTreeMap<IotaAddress, _> = packages
3086 .into_iter()
3087 .map(|(version, package, origins)| {
3088 (package_storage_id(&package), (version, package, origins))
3089 })
3090 .collect();
3091
3092 let packages = packages_by_storage_id
3093 .iter()
3094 .map(|(&storage_id, (version, compiled_package, origins))| {
3095 let linkage = compiled_package
3096 .dependency_ids
3097 .published
3098 .values()
3099 .map(|dep_id| {
3100 let storage_id = IotaAddress::from(*dep_id);
3101 let runtime_id = package_runtime_id(
3102 &packages_by_storage_id
3103 .get(&storage_id)
3104 .unwrap_or_else(|| panic!("Dependency {storage_id} not in store"))
3105 .1,
3106 );
3107
3108 (runtime_id, storage_id)
3109 })
3110 .collect();
3111
3112 let package = cached_package(*version, linkage, compiled_package, origins);
3113 (storage_id, package)
3114 })
3115 .collect();
3116
3117 let inner = Arc::new(RwLock::new(InnerStore {
3118 packages,
3119 fetches: 0,
3120 }));
3121
3122 let store = InMemoryPackageStore {
3123 inner: inner.clone(),
3124 };
3125
3126 (inner, PackageStoreWithLruCache::new(store))
3127 }
3128
3129 fn cached_package(
3130 version: u64,
3131 linkage: Linkage,
3132 package: &CompiledPackage,
3133 origins: &TypeOriginTable,
3134 ) -> Package {
3135 let storage_id = package_storage_id(package);
3136 let runtime_id = package_runtime_id(package);
3137 let version = SequenceNumber::from_u64(version);
3138
3139 let mut modules = BTreeMap::new();
3140 for unit in &package.package.root_compiled_units {
3141 let NamedCompiledModule { name, module, .. } = &unit.unit;
3142
3143 let origins = origins
3144 .iter()
3145 .filter(|key| key.module == name.as_str())
3146 .map(|key| (key.name.to_string(), key.package))
3147 .collect();
3148
3149 let module = match Module::read(module.clone(), origins) {
3150 Ok(module) => module,
3151 Err(struct_) => {
3152 panic!("Missing type origin for {}::{struct_}", module.self_id());
3153 }
3154 };
3155
3156 modules.insert(name.to_string(), module);
3157 }
3158
3159 Package {
3160 storage_id,
3161 runtime_id,
3162 linkage,
3163 version,
3164 modules,
3165 }
3166 }
3167
3168 fn package_storage_id(package: &CompiledPackage) -> IotaAddress {
3169 IotaAddress::new(
3170 package
3171 .published_at
3172 .as_ref()
3173 .unwrap_or_else(|_| {
3174 panic!(
3175 "Package {} doesn't have published-at set",
3176 package.package.compiled_package_info.package_name,
3177 )
3178 })
3179 .into_bytes(),
3180 )
3181 }
3182
3183 fn package_runtime_id(package: &CompiledPackage) -> IotaAddress {
3184 IotaAddress::new(
3185 package
3186 .published_root_module()
3187 .expect("No compiled module")
3188 .address()
3189 .into_bytes(),
3190 )
3191 }
3192
3193 fn build_package(dir: &str) -> IotaResult<CompiledPackage> {
3194 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3195 path.extend(["tests", "packages", dir]);
3196 BuildConfig::new_for_testing().build(&path)
3197 }
3198
3199 fn addr(a: &str) -> IotaAddress {
3200 IotaAddress::from_str(a).unwrap()
3201 }
3202
3203 fn obj_id(a: &str) -> ObjectID {
3204 ObjectID::from_str(a).unwrap()
3205 }
3206
3207 fn datakey(a: &str, m: &'static str, n: &'static str) -> DatatypeKey {
3208 DatatypeKey {
3209 package: addr(a),
3210 module: m.into(),
3211 name: n.into(),
3212 }
3213 }
3214
3215 fn type_(t: &str) -> TypeTag {
3216 TypeTag::from_str(t).unwrap()
3217 }
3218
3219 fn key(t: &str) -> DatatypeKey {
3220 let tag = StructTag::from_str(t).unwrap();
3221 DatatypeRef::from(&tag).as_key()
3222 }
3223
3224 struct InMemoryPackageStore {
3225 inner: Arc<RwLock<InnerStore>>,
3228 }
3229
3230 struct InnerStore {
3231 packages: BTreeMap<IotaAddress, Package>,
3232 fetches: usize,
3233 }
3234
3235 #[async_trait]
3236 impl PackageStore for InMemoryPackageStore {
3237 async fn fetch(&self, id: IotaAddress) -> Result<Arc<Package>> {
3238 let mut inner = self.inner.as_ref().write().unwrap();
3239 inner.fetches += 1;
3240 inner
3241 .packages
3242 .get(&id)
3243 .cloned()
3244 .ok_or_else(|| Error::PackageNotFound(id))
3245 .map(Arc::new)
3246 }
3247 }
3248
3249 impl InnerStore {
3250 fn replace(&mut self, id: IotaAddress, package: Package) {
3251 self.packages.insert(id, package);
3252 }
3253 }
3254}