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_sdk_types::{
14 Address, Argument, Command, Identifier, MakeMoveVector, ProgrammableTransaction, StructTag,
15 TypeTag, Version,
16 move_package::{MovePackage, TypeOrigin},
17};
18use iota_types::{
19 base_types::is_primitive_type_tag,
20 iota_sdk_types_conversions::{struct_tag_sdk_to_core, type_tag_core_to_sdk},
21 object::Object,
22 transaction::CallArg,
23};
24use lru::LruCache;
25use move_binary_format::{
26 CompiledModule,
27 errors::Location,
28 file_format::{
29 AbilitySet, DatatypeHandleIndex, DatatypeTyParameter, EnumDefinitionIndex,
30 FunctionDefinitionIndex, Signature as MoveSignature, SignatureIndex, SignatureToken,
31 StructDefinitionIndex, StructFieldInformation, TableIndex, Visibility,
32 },
33};
34use move_command_line_common::{
35 display::{RenderResult, try_render_constant},
36 error_bitset::ErrorBitset,
37};
38use move_core_types::{
39 account_address::AccountAddress,
40 annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
41 language_storage::ModuleId,
42};
43
44use crate::error::Error;
45
46pub mod error;
47
48const PACKAGE_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
51
52pub type Result<T> = std::result::Result<T, Error>;
53
54#[derive(Debug)]
58pub struct Resolver<S> {
59 package_store: S,
60 limits: Option<Limits>,
61}
62
63#[derive(Debug)]
66pub struct Limits {
67 pub max_type_argument_depth: usize,
69 pub max_type_argument_width: usize,
71 pub max_type_nodes: usize,
73 pub max_move_value_depth: usize,
75}
76
77pub struct PackageStoreWithLruCache<T> {
82 pub(crate) packages: Mutex<LruCache<Address, Arc<Package>>>,
83 pub(crate) inner: T,
84}
85
86#[derive(Clone, Debug)]
87pub struct Package {
88 storage_id: Address,
90
91 runtime_id: Address,
95
96 linkage: Linkage,
100
101 version: Version,
104
105 modules: BTreeMap<String, Module>,
106}
107
108type Linkage = BTreeMap<Address, Address>;
109
110#[derive(Clone, Debug)]
117pub struct CleverError {
118 pub module_id: ModuleId,
120 pub error_info: ErrorConstants,
123 pub source_line_number: u16,
125}
126
127#[derive(Clone, Debug)]
138pub enum ErrorConstants {
139 None,
141 Rendered {
152 identifier: String,
154 constant: String,
156 },
157 Raw {
162 identifier: String,
164 bytes: Vec<u8>,
166 },
167}
168
169#[derive(Clone, Debug)]
170pub struct Module {
171 bytecode: CompiledModule,
172
173 struct_index: BTreeMap<String, (Address, StructDefinitionIndex)>,
176
177 enum_index: BTreeMap<String, (Address, EnumDefinitionIndex)>,
180 function_index: BTreeMap<String, FunctionDefinitionIndex>,
183}
184
185#[derive(Debug)]
187pub struct DataDef {
188 pub defining_id: Address,
190
191 pub abilities: AbilitySet,
193
194 pub type_params: Vec<DatatypeTyParameter>,
196
197 pub data: MoveData,
200}
201
202#[derive(Debug)]
203pub enum MoveData {
204 Struct(Vec<(String, OpenSignatureBody)>),
208
209 Enum(Vec<VariantDef>),
212}
213
214#[derive(Debug)]
217pub struct VariantDef {
218 pub name: String,
220
221 pub signatures: Vec<(String, OpenSignatureBody)>,
225}
226
227#[derive(Debug)]
229pub struct FunctionDef {
230 pub visibility: Visibility,
232
233 pub is_entry: bool,
235
236 pub type_params: Vec<AbilitySet>,
238
239 pub parameters: Vec<OpenSignature>,
241
242 pub return_: Vec<OpenSignature>,
244}
245
246#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
250pub struct DatatypeRef<'m, 'n> {
251 pub package: Address,
252 pub module: Cow<'m, str>,
253 pub name: Cow<'n, str>,
254}
255
256pub type DatatypeKey = DatatypeRef<'static, 'static>;
258
259#[derive(Copy, Clone, Debug)]
260pub enum Reference {
261 Immutable,
262 Mutable,
263}
264
265#[derive(Clone, Debug)]
268pub struct Signature {
269 pub ref_: Option<Reference>,
270 pub body: TypeTag,
271}
272
273#[derive(Clone, Debug)]
276pub struct OpenSignature {
277 pub ref_: Option<Reference>,
278 pub body: OpenSignatureBody,
279}
280
281#[derive(Clone, Debug)]
284pub enum OpenSignatureBody {
285 Address,
286 Bool,
287 U8,
288 U16,
289 U32,
290 U64,
291 U128,
292 U256,
293 Vector(Box<OpenSignatureBody>),
294 Datatype(DatatypeKey, Vec<OpenSignatureBody>),
295 TypeParameter(u16),
296}
297
298#[derive(Debug, Default)]
300struct ResolutionContext<'l> {
301 datatypes: BTreeMap<DatatypeKey, DataDef>,
304
305 limits: Option<&'l Limits>,
307}
308
309#[async_trait]
312pub trait PackageStore: Send + Sync + 'static {
313 async fn fetch(&self, id: Address) -> Result<Arc<Package>>;
316}
317
318macro_rules! as_ref_impl {
319 ($type:ty) => {
320 #[async_trait]
321 impl PackageStore for $type {
322 async fn fetch(&self, id: Address) -> Result<Arc<Package>> {
323 self.as_ref().fetch(id).await
324 }
325 }
326 };
327}
328
329as_ref_impl!(Arc<dyn PackageStore>);
330as_ref_impl!(Box<dyn PackageStore>);
331
332macro_rules! check_max_limit {
335 ($err:ident, $config:expr; $limit:ident $op:tt $value:expr) => {
336 if let Some(l) = $config {
337 let max = l.$limit;
338 let val = $value;
339 if !(max $op val) {
340 return Err(Error::$err(max, val));
341 }
342 }
343 };
344}
345
346impl<S> Resolver<S> {
347 pub fn new(package_store: S) -> Self {
348 Self {
349 package_store,
350 limits: None,
351 }
352 }
353
354 pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
355 Self {
356 package_store,
357 limits: Some(limits),
358 }
359 }
360
361 pub fn package_store(&self) -> &S {
362 &self.package_store
363 }
364
365 pub fn package_store_mut(&mut self) -> &mut S {
366 &mut self.package_store
367 }
368}
369
370impl<S: PackageStore> Resolver<S> {
371 pub async fn canonical_type(&self, mut tag: TypeTag) -> Result<TypeTag> {
382 let mut context = ResolutionContext::new(self.limits.as_ref());
383
384 context
387 .add_type_tag(
388 &mut tag,
389 &self.package_store,
390 false,
392 true,
394 )
395 .await?;
396
397 context.canonicalize_type(&mut tag)?;
399 Ok(tag)
400 }
401
402 pub async fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
406 let mut context = ResolutionContext::new(self.limits.as_ref());
407
408 context
411 .add_type_tag(
412 &mut tag,
413 &self.package_store,
414 true,
416 true,
418 )
419 .await?;
420
421 let max_depth = self
423 .limits
424 .as_ref()
425 .map_or(usize::MAX, |l| l.max_move_value_depth);
426
427 Ok(context.resolve_type_layout(&tag, max_depth)?.0)
428 }
429
430 pub async fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
437 let mut context = ResolutionContext::new(self.limits.as_ref());
438
439 context
442 .add_type_tag(
443 &mut tag,
444 &self.package_store,
445 false,
447 false,
449 )
450 .await?;
451
452 context.resolve_abilities(&tag)
454 }
455
456 pub async fn function_signature(
459 &self,
460 pkg: Address,
461 module: &str,
462 function: &str,
463 ) -> Result<FunctionDef> {
464 let mut context = ResolutionContext::new(self.limits.as_ref());
465
466 let package = self.package_store.fetch(pkg).await?;
467 let Some(mut def) = package.module(module)?.function_def(function)? else {
468 return Err(Error::FunctionNotFound(
469 pkg,
470 module.to_string(),
471 function.to_string(),
472 ));
473 };
474
475 for sig in def.parameters.iter().chain(def.return_.iter()) {
478 context
479 .add_signature(
480 sig.body.clone(),
481 &self.package_store,
482 package.as_ref(),
483 false,
485 )
486 .await?;
487 }
488
489 for sig in def.parameters.iter_mut().chain(def.return_.iter_mut()) {
491 context.relocate_signature(&mut sig.body)?;
492 }
493
494 Ok(def)
495 }
496
497 pub async fn pure_input_layouts(
509 &self,
510 tx: &ProgrammableTransaction,
511 ) -> Result<Vec<Option<MoveTypeLayout>>> {
512 let mut tags = vec![None; tx.inputs.len()];
513 let mut register_type = |arg: &Argument, tag: &TypeTag| {
514 let &Argument::Input(ix) = arg else {
515 return Ok(());
516 };
517
518 if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
519 return Ok(());
520 }
521
522 let Some(type_) = tags.get_mut(ix as usize) else {
523 return Ok(());
524 };
525
526 match type_ {
527 None => *type_ = Some(tag.clone()),
528 Some(prev) => {
529 if prev != tag {
530 return Err(Error::InputTypeConflict(ix, prev.clone(), tag.clone()));
531 }
532 }
533 }
534
535 Ok(())
536 };
537
538 for cmd in &tx.commands {
540 match cmd {
541 Command::MoveCall(cmd) => {
542 let Ok(signature) = self
543 .function_signature(
544 cmd.package.into(),
545 cmd.module.as_str(),
546 cmd.function.as_str(),
547 )
548 .await
549 else {
550 continue;
551 };
552
553 for (open_sig, arg) in signature.parameters.iter().zip(cmd.arguments.iter()) {
554 let sig = open_sig.instantiate(&cmd.type_arguments)?;
555 register_type(arg, &sig.body)?;
556 }
557 }
558 Command::TransferObjects(cmd) => register_type(&cmd.address, &TypeTag::Address)?,
559 Command::SplitCoins(cmd) => {
560 for amount in &cmd.amounts {
561 register_type(amount, &TypeTag::U64)?;
562 }
563 }
564 Command::MakeMoveVector(MakeMoveVector {
565 type_: Some(tag),
566 elements,
567 }) if is_primitive_type_tag(tag) => {
568 for elem in elements {
569 register_type(elem, tag)?;
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: Address,
607 ) -> Result<ModuleId> {
608 let package = self.package_store.fetch(context).await?;
609 let storage_id = package.relocate(Address::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(Address::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 = Address>) {
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: Address) -> 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_opt_package() 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, Address>> = 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(Address::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: Address) -> Result<Address> {
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, Address>,
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 = Address::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_sdk_types::{Identifier, ObjectId, StructTag, TypeTag};
1854 use iota_types::{base_types::random_object_ref, error::IotaResult};
1855 use move_binary_format::file_format::Ability;
1856 use move_compiler::compiled_unit::NamedCompiledModule;
1857
1858 use super::*;
1859
1860 fn fmt(struct_layout: MoveTypeLayout, enum_layout: MoveTypeLayout) -> String {
1861 format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",)
1862 }
1863
1864 #[tokio::test]
1865 async fn test_simple_canonical_type() {
1866 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1867 let package_resolver = Resolver::new(cache);
1868
1869 let input = type_("0xa0::m::T0");
1870 let expect = input.clone();
1871 let actual = package_resolver.canonical_type(input).await.unwrap();
1872 assert_eq!(expect, actual);
1873 }
1874
1875 #[tokio::test]
1876 async fn test_upgraded_canonical_type() {
1877 let (_, cache) = package_cache([
1878 (1, build_package("a0").unwrap(), a0_types()),
1879 (2, build_package("a1").unwrap(), a1_types()),
1880 ]);
1881
1882 let package_resolver = Resolver::new(cache);
1883
1884 let input = type_("0xa1::m::T3");
1885 let expect = input.clone();
1886 let actual = package_resolver.canonical_type(input).await.unwrap();
1887 assert_eq!(expect, actual);
1888 }
1889
1890 #[tokio::test]
1891 async fn test_latest_canonical_type() {
1892 let (_, cache) = package_cache([
1893 (1, build_package("a0").unwrap(), a0_types()),
1894 (2, build_package("a1").unwrap(), a1_types()),
1895 ]);
1896
1897 let package_resolver = Resolver::new(cache);
1898
1899 let input = type_("0xa1::m::T0");
1900 let expect = type_("0xa0::m::T0");
1901 let actual = package_resolver.canonical_type(input).await.unwrap();
1902 assert_eq!(expect, actual);
1903 }
1904
1905 #[tokio::test]
1906 async fn test_type_param_canonical_type() {
1907 let (_, cache) = package_cache([
1908 (1, build_package("a0").unwrap(), a0_types()),
1909 (2, build_package("a1").unwrap(), a1_types()),
1910 ]);
1911
1912 let package_resolver = Resolver::new(cache);
1913
1914 let input = type_("0xa1::m::T1<0xa1::m::T0, 0xa1::m::T3>");
1915 let expect = type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>");
1916 let actual = package_resolver.canonical_type(input).await.unwrap();
1917 assert_eq!(expect, actual);
1918 }
1919
1920 #[tokio::test]
1921 async fn test_canonical_err_package_too_old() {
1922 let (_, cache) = package_cache([
1923 (1, build_package("a0").unwrap(), a0_types()),
1924 (2, build_package("a1").unwrap(), a1_types()),
1925 ]);
1926
1927 let package_resolver = Resolver::new(cache);
1928
1929 let input = type_("0xa0::m::T3");
1930 let err = package_resolver.canonical_type(input).await.unwrap_err();
1931 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
1932 }
1933
1934 #[tokio::test]
1935 async fn test_canonical_err_signer() {
1936 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1937
1938 let package_resolver = Resolver::new(cache);
1939
1940 let input = type_("0xa0::m::T1<0xa0::m::T0, signer>");
1941 let err = package_resolver.canonical_type(input).await.unwrap_err();
1942 assert!(matches!(err, Error::UnexpectedSigner));
1943 }
1944
1945 #[tokio::test]
1948 async fn test_simple_type_layout() {
1949 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1950 let package_resolver = Resolver::new(cache);
1951 let struct_layout = package_resolver
1952 .type_layout(type_("0xa0::m::T0"))
1953 .await
1954 .unwrap();
1955 let enum_layout = package_resolver
1956 .type_layout(type_("0xa0::m::E0"))
1957 .await
1958 .unwrap();
1959 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1960 }
1961
1962 #[tokio::test]
1964 async fn test_cross_module_layout() {
1965 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1966 let resolver = Resolver::new(cache);
1967 let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap();
1968 let enum_layout = resolver.type_layout(type_("0xa0::n::E0")).await.unwrap();
1969 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1970 }
1971
1972 #[tokio::test]
1974 async fn test_cross_package_layout() {
1975 let (_, cache) = package_cache([
1976 (1, build_package("a0").unwrap(), a0_types()),
1977 (1, build_package("b0").unwrap(), b0_types()),
1978 ]);
1979 let resolver = Resolver::new(cache);
1980
1981 let struct_layout = resolver.type_layout(type_("0xb0::m::T0")).await.unwrap();
1982 let enum_layout = resolver.type_layout(type_("0xb0::m::E0")).await.unwrap();
1983 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1984 }
1985
1986 #[tokio::test]
1989 async fn test_upgraded_package_layout() {
1990 let (_, cache) = package_cache([
1991 (1, build_package("a0").unwrap(), a0_types()),
1992 (2, build_package("a1").unwrap(), a1_types()),
1993 ]);
1994 let resolver = Resolver::new(cache);
1995
1996 let struct_layout = resolver.type_layout(type_("0xa1::n::T1")).await.unwrap();
1997 let enum_layout = resolver.type_layout(type_("0xa1::n::E1")).await.unwrap();
1998 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1999 }
2000
2001 #[tokio::test]
2005 async fn test_multiple_linkage_contexts_layout() {
2006 let (_, cache) = package_cache([
2007 (1, build_package("a0").unwrap(), a0_types()),
2008 (2, build_package("a1").unwrap(), a1_types()),
2009 ]);
2010 let resolver = Resolver::new(cache);
2011
2012 let struct_layout = resolver
2013 .type_layout(type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"))
2014 .await
2015 .unwrap();
2016 let enum_layout = resolver
2017 .type_layout(type_("0xa0::m::E1<0xa0::m::E0, 0xa1::m::E3>"))
2018 .await
2019 .unwrap();
2020 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2021 }
2022
2023 #[tokio::test]
2029 async fn test_upgraded_package_non_defining_id_layout() {
2030 let (_, cache) = package_cache([
2031 (1, build_package("a0").unwrap(), a0_types()),
2032 (2, build_package("a1").unwrap(), a1_types()),
2033 ]);
2034 let resolver = Resolver::new(cache);
2035
2036 let struct_layout = resolver
2037 .type_layout(type_("0xa1::m::T1<0xa1::m::T3, 0xa1::m::T0>"))
2038 .await
2039 .unwrap();
2040 let enum_layout = resolver
2041 .type_layout(type_("0xa1::m::E1<0xa1::m::E3, 0xa1::m::E0>"))
2042 .await
2043 .unwrap();
2044 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2045 }
2046
2047 #[tokio::test]
2051 async fn test_relinking_layout() {
2052 let (_, cache) = package_cache([
2053 (1, build_package("a0").unwrap(), a0_types()),
2054 (2, build_package("a1").unwrap(), a1_types()),
2055 (1, build_package("b0").unwrap(), b0_types()),
2056 (1, build_package("c0").unwrap(), c0_types()),
2057 ]);
2058 let resolver = Resolver::new(cache);
2059
2060 let struct_layout = resolver.type_layout(type_("0xc0::m::T0")).await.unwrap();
2061 let enum_layout = resolver.type_layout(type_("0xc0::m::E0")).await.unwrap();
2062 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2063 }
2064
2065 #[tokio::test]
2066 async fn test_value_nesting_boundary_layout() {
2067 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2068
2069 let resolver = Resolver::new_with_limits(
2070 cache,
2071 Limits {
2072 max_type_argument_width: 100,
2073 max_type_argument_depth: 100,
2074 max_type_nodes: 100,
2075 max_move_value_depth: 3,
2076 },
2077 );
2078
2079 let struct_layout = resolver
2081 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2082 .await
2083 .unwrap();
2084 let enum_layout = resolver
2085 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2086 .await
2087 .unwrap();
2088 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2089 }
2090
2091 #[tokio::test]
2092 async fn test_err_value_nesting_simple_layout() {
2093 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2094
2095 let resolver = Resolver::new_with_limits(
2096 cache,
2097 Limits {
2098 max_type_argument_width: 100,
2099 max_type_argument_depth: 100,
2100 max_type_nodes: 100,
2101 max_move_value_depth: 2,
2102 },
2103 );
2104
2105 let struct_err = resolver
2107 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2108 .await
2109 .unwrap_err();
2110 let enum_err = resolver
2111 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2112 .await
2113 .unwrap_err();
2114 assert!(matches!(struct_err, Error::ValueNesting(2)));
2115 assert!(matches!(enum_err, Error::ValueNesting(2)));
2116 }
2117
2118 #[tokio::test]
2119 async fn test_err_value_nesting_big_type_param_layout() {
2120 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2121
2122 let resolver = Resolver::new_with_limits(
2123 cache,
2124 Limits {
2125 max_type_argument_width: 100,
2126 max_type_argument_depth: 100,
2127 max_type_nodes: 100,
2128 max_move_value_depth: 3,
2129 },
2130 );
2131
2132 let struct_err = resolver
2136 .type_layout(type_("0xa0::m::T1<vector<vector<u8>>, u8>"))
2137 .await
2138 .unwrap_err();
2139 let enum_err = resolver
2140 .type_layout(type_("0xa0::m::E1<vector<vector<u8>>, u8>"))
2141 .await
2142 .unwrap_err();
2143 assert!(matches!(struct_err, Error::ValueNesting(3)));
2144 assert!(matches!(enum_err, Error::ValueNesting(3)));
2145 }
2146
2147 #[tokio::test]
2148 async fn test_err_value_nesting_big_phantom_type_param_layout() {
2149 let (_, cache) = package_cache([
2150 (1, build_package("iota").unwrap(), iota_types()),
2151 (1, build_package("d0").unwrap(), d0_types()),
2152 ]);
2153
2154 let resolver = Resolver::new_with_limits(
2155 cache,
2156 Limits {
2157 max_type_argument_width: 100,
2158 max_type_argument_depth: 100,
2159 max_type_nodes: 100,
2160 max_move_value_depth: 3,
2161 },
2162 );
2163
2164 let _ = resolver
2166 .type_layout(type_("0xd0::m::O<u8, u8>"))
2167 .await
2168 .unwrap();
2169 let _ = resolver
2170 .type_layout(type_("0xd0::m::EO<u8, u8>"))
2171 .await
2172 .unwrap();
2173
2174 let struct_err = resolver
2179 .type_layout(type_("0xd0::m::O<u8, vector<vector<u8>>>"))
2180 .await
2181 .unwrap_err();
2182 let enum_err = resolver
2183 .type_layout(type_("0xd0::m::EO<u8, vector<vector<u8>>>"))
2184 .await
2185 .unwrap_err();
2186 assert!(matches!(struct_err, Error::ValueNesting(3)));
2187 assert!(matches!(enum_err, Error::ValueNesting(3)));
2188 }
2189
2190 #[tokio::test]
2191 async fn test_err_value_nesting_type_param_application_layout() {
2192 let (_, cache) = package_cache([
2193 (1, build_package("iota").unwrap(), iota_types()),
2194 (1, build_package("d0").unwrap(), d0_types()),
2195 ]);
2196
2197 let resolver = Resolver::new_with_limits(
2198 cache,
2199 Limits {
2200 max_type_argument_width: 100,
2201 max_type_argument_depth: 100,
2202 max_type_nodes: 100,
2203 max_move_value_depth: 3,
2204 },
2205 );
2206
2207 let struct_err = resolver
2211 .type_layout(type_("0xd0::m::O<vector<u8>, u8>"))
2212 .await
2213 .unwrap_err();
2214 let enum_err = resolver
2215 .type_layout(type_("0xd0::m::EO<vector<u8>, u8>"))
2216 .await
2217 .unwrap_err();
2218
2219 assert!(matches!(struct_err, Error::ValueNesting(3)));
2220 assert!(matches!(enum_err, Error::ValueNesting(3)));
2221 }
2222
2223 #[tokio::test]
2224 async fn test_system_package_invalidation() {
2225 let (inner, cache) = package_cache([(1, build_package("s0").unwrap(), s0_types())]);
2226 let resolver = Resolver::new(cache);
2227
2228 let struct_not_found = resolver.type_layout(type_("0x1::m::T1")).await.unwrap_err();
2229 let enum_not_found = resolver.type_layout(type_("0x1::m::E1")).await.unwrap_err();
2230 assert!(matches!(struct_not_found, Error::DatatypeNotFound(_, _, _)));
2231 assert!(matches!(enum_not_found, Error::DatatypeNotFound(_, _, _)));
2232
2233 inner.write().unwrap().replace(
2235 addr("0x1"),
2236 cached_package(
2237 2,
2238 BTreeMap::new(),
2239 &build_package("s1").unwrap(),
2240 &s1_types(),
2241 ),
2242 );
2243
2244 resolver.package_store().evict([addr("0x1")]);
2246
2247 let struct_layout = resolver.type_layout(type_("0x1::m::T1")).await.unwrap();
2248 let enum_layout = resolver.type_layout(type_("0x1::m::E1")).await.unwrap();
2249 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2250 }
2251
2252 #[tokio::test]
2253 async fn test_caching() {
2254 let (inner, cache) = package_cache([
2255 (1, build_package("a0").unwrap(), a0_types()),
2256 (1, build_package("s0").unwrap(), s0_types()),
2257 ]);
2258 let resolver = Resolver::new(cache);
2259
2260 assert_eq!(inner.read().unwrap().fetches, 0);
2261 let l0 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2262
2263 assert_eq!(inner.read().unwrap().fetches, 1);
2265
2266 let l1 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2268 assert_eq!(format!("{l0}"), format!("{l1}"));
2269 assert_eq!(inner.read().unwrap().fetches, 1);
2270
2271 let l2 = resolver.type_layout(type_("0xa0::m::T2")).await.unwrap();
2273 assert_ne!(format!("{l0}"), format!("{l2}"));
2274 assert_eq!(inner.read().unwrap().fetches, 1);
2275
2276 resolver.type_layout(type_("0xa0::m::E0")).await.unwrap();
2278 assert_eq!(inner.read().unwrap().fetches, 1);
2279
2280 let l3 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2282 assert_eq!(inner.read().unwrap().fetches, 2);
2283
2284 let l4 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2286 assert_eq!(format!("{l3}"), format!("{l4}"));
2287 assert_eq!(inner.read().unwrap().fetches, 2);
2288
2289 let el4 = resolver.type_layout(type_("0x1::m::E0")).await.unwrap();
2291 assert_ne!(format!("{el4}"), format!("{l4}"));
2292 assert_eq!(inner.read().unwrap().fetches, 2);
2293
2294 inner.write().unwrap().replace(
2296 addr("0x1"),
2297 cached_package(
2298 2,
2299 BTreeMap::new(),
2300 &build_package("s1").unwrap(),
2301 &s1_types(),
2302 ),
2303 );
2304
2305 resolver.package_store().evict([addr("0x1")]);
2307
2308 let l5 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2313 assert_eq!(format!("{l4}"), format!("{l5}"));
2314 assert_eq!(inner.read().unwrap().fetches, 3);
2315 }
2316
2317 #[tokio::test]
2318 async fn test_layout_err_not_a_package() {
2319 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2320 let resolver = Resolver::new(cache);
2321 let err = resolver
2322 .type_layout(type_("0x42::m::T0"))
2323 .await
2324 .unwrap_err();
2325 assert!(matches!(err, Error::PackageNotFound(_)));
2326 }
2327
2328 #[tokio::test]
2329 async fn test_layout_err_no_module() {
2330 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2331 let resolver = Resolver::new(cache);
2332 let err = resolver
2333 .type_layout(type_("0xa0::l::T0"))
2334 .await
2335 .unwrap_err();
2336 assert!(matches!(err, Error::ModuleNotFound(_, _)));
2337 }
2338
2339 #[tokio::test]
2340 async fn test_layout_err_no_struct() {
2341 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2342 let resolver = Resolver::new(cache);
2343
2344 let err = resolver
2345 .type_layout(type_("0xa0::m::T9"))
2346 .await
2347 .unwrap_err();
2348 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
2349 }
2350
2351 #[tokio::test]
2352 async fn test_layout_err_type_arity() {
2353 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2354 let resolver = Resolver::new(cache);
2355
2356 let err = resolver
2358 .type_layout(type_("0xa0::m::T1<u8>"))
2359 .await
2360 .unwrap_err();
2361 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2362
2363 let err = resolver
2365 .type_layout(type_("0xa0::m::T1<u8, u16, u32>"))
2366 .await
2367 .unwrap_err();
2368 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2369 }
2370
2371 #[tokio::test]
2372 async fn test_structs() {
2373 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2374 let a0 = cache.fetch(addr("0xa0")).await.unwrap();
2375 let m = a0.module("m").unwrap();
2376
2377 assert_eq!(
2378 m.structs(None, None).collect::<Vec<_>>(),
2379 vec!["T0", "T1", "T2"],
2380 );
2381
2382 assert_eq!(m.structs(None, Some("T1")).collect::<Vec<_>>(), vec!["T0"],);
2383
2384 assert_eq!(
2385 m.structs(Some("T0"), Some("T2")).collect::<Vec<_>>(),
2386 vec!["T1"],
2387 );
2388
2389 assert_eq!(m.structs(Some("T1"), None).collect::<Vec<_>>(), vec!["T2"],);
2390
2391 let t0 = m.struct_def("T0").unwrap().unwrap();
2392 let t1 = m.struct_def("T1").unwrap().unwrap();
2393 let t2 = m.struct_def("T2").unwrap().unwrap();
2394
2395 insta::assert_snapshot!(format!(
2396 "a0::m::T0: {t0:#?}\n\
2397 a0::m::T1: {t1:#?}\n\
2398 a0::m::T2: {t2:#?}",
2399 ));
2400 }
2401
2402 #[tokio::test]
2403 async fn test_enums() {
2404 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2405 let a0 = cache
2406 .fetch(Address::from_str("0xa0").unwrap())
2407 .await
2408 .unwrap();
2409 let m = a0.module("m").unwrap();
2410
2411 assert_eq!(
2412 m.enums(None, None).collect::<Vec<_>>(),
2413 vec!["E0", "E1", "E2"],
2414 );
2415
2416 assert_eq!(m.enums(None, Some("E1")).collect::<Vec<_>>(), vec!["E0"],);
2417
2418 assert_eq!(
2419 m.enums(Some("E0"), Some("E2")).collect::<Vec<_>>(),
2420 vec!["E1"],
2421 );
2422
2423 assert_eq!(m.enums(Some("E1"), None).collect::<Vec<_>>(), vec!["E2"],);
2424
2425 let e0 = m.enum_def("E0").unwrap().unwrap();
2426 let e1 = m.enum_def("E1").unwrap().unwrap();
2427 let e2 = m.enum_def("E2").unwrap().unwrap();
2428
2429 insta::assert_snapshot!(format!(
2430 "a0::m::E0: {e0:#?}\n\
2431 a0::m::E1: {e1:#?}\n\
2432 a0::m::E2: {e2:#?}",
2433 ));
2434 }
2435
2436 #[tokio::test]
2437 async fn test_functions() {
2438 let (_, cache) = package_cache([
2439 (1, build_package("a0").unwrap(), a0_types()),
2440 (2, build_package("a1").unwrap(), a1_types()),
2441 (1, build_package("b0").unwrap(), b0_types()),
2442 (1, build_package("c0").unwrap(), c0_types()),
2443 ]);
2444
2445 let c0 = cache.fetch(addr("0xc0")).await.unwrap();
2446 let m = c0.module("m").unwrap();
2447
2448 assert_eq!(
2449 m.functions(None, None).collect::<Vec<_>>(),
2450 vec!["bar", "baz", "foo"],
2451 );
2452
2453 assert_eq!(
2454 m.functions(None, Some("baz")).collect::<Vec<_>>(),
2455 vec!["bar"],
2456 );
2457
2458 assert_eq!(
2459 m.functions(Some("bar"), Some("foo")).collect::<Vec<_>>(),
2460 vec!["baz"],
2461 );
2462
2463 assert_eq!(
2464 m.functions(Some("baz"), None).collect::<Vec<_>>(),
2465 vec!["foo"],
2466 );
2467
2468 let foo = m.function_def("foo").unwrap().unwrap();
2469 let bar = m.function_def("bar").unwrap().unwrap();
2470 let baz = m.function_def("baz").unwrap().unwrap();
2471
2472 insta::assert_snapshot!(format!(
2473 "c0::m::foo: {foo:#?}\n\
2474 c0::m::bar: {bar:#?}\n\
2475 c0::m::baz: {baz:#?}"
2476 ));
2477 }
2478
2479 #[tokio::test]
2480 async fn test_function_parameters() {
2481 let (_, cache) = package_cache([
2482 (1, build_package("a0").unwrap(), a0_types()),
2483 (2, build_package("a1").unwrap(), a1_types()),
2484 (1, build_package("b0").unwrap(), b0_types()),
2485 (1, build_package("c0").unwrap(), c0_types()),
2486 ]);
2487
2488 let resolver = Resolver::new(cache);
2489 let c0 = addr("0xc0");
2490
2491 let foo = resolver.function_signature(c0, "m", "foo").await.unwrap();
2492 let bar = resolver.function_signature(c0, "m", "bar").await.unwrap();
2493 let baz = resolver.function_signature(c0, "m", "baz").await.unwrap();
2494
2495 insta::assert_snapshot!(format!(
2496 "c0::m::foo: {foo:#?}\n\
2497 c0::m::bar: {bar:#?}\n\
2498 c0::m::baz: {baz:#?}"
2499 ));
2500 }
2501
2502 #[tokio::test]
2503 async fn test_signature_instantiation() {
2504 use OpenSignatureBody as O;
2505 use TypeTag as T;
2506
2507 let sig = O::Datatype(
2508 key("0x2::table::Table"),
2509 vec![
2510 O::TypeParameter(1),
2511 O::Vector(Box::new(O::Datatype(
2512 key("0x1::option::Option"),
2513 vec![O::TypeParameter(0)],
2514 ))),
2515 ],
2516 );
2517
2518 insta::assert_debug_snapshot!(sig.instantiate(&[T::U64, T::Bool]).unwrap());
2519 }
2520
2521 #[tokio::test]
2522 async fn test_signature_instantiation_error() {
2523 use OpenSignatureBody as O;
2524 use TypeTag as T;
2525
2526 let sig = O::Datatype(
2527 key("0x2::table::Table"),
2528 vec![
2529 O::TypeParameter(1),
2530 O::Vector(Box::new(O::Datatype(
2531 key("0x1::option::Option"),
2532 vec![O::TypeParameter(99)],
2533 ))),
2534 ],
2535 );
2536
2537 insta::assert_snapshot!(
2538 sig.instantiate(&[T::U64, T::Bool]).unwrap_err(),
2539 @"Type Parameter 99 out of bounds (2)"
2540 );
2541 }
2542
2543 #[tokio::test]
2545 async fn test_primitive_abilities() {
2546 use Ability as A;
2547 use AbilitySet as S;
2548
2549 let (_, cache) = package_cache([]);
2550 let resolver = Resolver::new(cache);
2551
2552 for prim in ["address", "bool", "u8", "u16", "u32", "u64", "u128", "u256"] {
2553 assert_eq!(
2554 resolver.abilities(type_(prim)).await.unwrap(),
2555 S::EMPTY | A::Copy | A::Drop | A::Store,
2556 "Unexpected primitive abilities for: {prim}",
2557 );
2558 }
2559 }
2560
2561 #[tokio::test]
2563 async fn test_simple_generic_abilities() {
2564 use Ability as A;
2565 use AbilitySet as S;
2566
2567 let (_, cache) = package_cache([
2568 (1, build_package("iota").unwrap(), iota_types()),
2569 (1, build_package("d0").unwrap(), d0_types()),
2570 ]);
2571 let resolver = Resolver::new(cache);
2572
2573 let a1 = resolver
2574 .abilities(type_("0xd0::m::T<u32, u64>"))
2575 .await
2576 .unwrap();
2577 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop | A::Store);
2578
2579 let a2 = resolver
2580 .abilities(type_("0xd0::m::T<0xd0::m::S, u64>"))
2581 .await
2582 .unwrap();
2583 assert_eq!(a2, S::EMPTY | A::Drop | A::Store);
2584
2585 let a3 = resolver
2586 .abilities(type_("0xd0::m::T<0xd0::m::R, 0xd0::m::S>"))
2587 .await
2588 .unwrap();
2589 assert_eq!(a3, S::EMPTY | A::Drop);
2590
2591 let a4 = resolver
2592 .abilities(type_("0xd0::m::T<0xd0::m::Q, 0xd0::m::R>"))
2593 .await
2594 .unwrap();
2595 assert_eq!(a4, S::EMPTY);
2596 }
2597
2598 #[tokio::test]
2600 async fn test_nested_generic_abilities() {
2601 use Ability as A;
2602 use AbilitySet as S;
2603
2604 let (_, cache) = package_cache([
2605 (1, build_package("iota").unwrap(), iota_types()),
2606 (1, build_package("d0").unwrap(), d0_types()),
2607 ]);
2608 let resolver = Resolver::new(cache);
2609
2610 let a1 = resolver
2611 .abilities(type_("0xd0::m::T<0xd0::m::T<0xd0::m::R, u32>, u64>"))
2612 .await
2613 .unwrap();
2614 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop);
2615 }
2616
2617 #[tokio::test]
2620 async fn test_key_abilities() {
2621 use Ability as A;
2622 use AbilitySet as S;
2623
2624 let (_, cache) = package_cache([
2625 (1, build_package("iota").unwrap(), iota_types()),
2626 (1, build_package("d0").unwrap(), d0_types()),
2627 ]);
2628 let resolver = Resolver::new(cache);
2629
2630 let a1 = resolver
2631 .abilities(type_("0xd0::m::O<u32, u64>"))
2632 .await
2633 .unwrap();
2634 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2635
2636 let a2 = resolver
2637 .abilities(type_("0xd0::m::O<0xd0::m::S, u64>"))
2638 .await
2639 .unwrap();
2640 assert_eq!(a2, S::EMPTY | A::Key | A::Store);
2641
2642 let a3 = resolver
2645 .abilities(type_("0xd0::m::O<0xd0::m::R, u64>"))
2646 .await
2647 .unwrap();
2648 assert_eq!(a3, S::EMPTY);
2649
2650 let a4 = resolver
2652 .abilities(type_("0xd0::m::O<0xd0::m::P, u32>"))
2653 .await
2654 .unwrap();
2655 assert_eq!(a4, S::EMPTY);
2656 }
2657
2658 #[tokio::test]
2660 async fn test_phantom_abilities() {
2661 use Ability as A;
2662 use AbilitySet as S;
2663
2664 let (_, cache) = package_cache([
2665 (1, build_package("iota").unwrap(), iota_types()),
2666 (1, build_package("d0").unwrap(), d0_types()),
2667 ]);
2668 let resolver = Resolver::new(cache);
2669
2670 let a1 = resolver
2671 .abilities(type_("0xd0::m::O<u32, 0xd0::m::R>"))
2672 .await
2673 .unwrap();
2674 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2675 }
2676
2677 #[tokio::test]
2678 async fn test_err_ability_arity() {
2679 let (_, cache) = package_cache([
2680 (1, build_package("iota").unwrap(), iota_types()),
2681 (1, build_package("d0").unwrap(), d0_types()),
2682 ]);
2683 let resolver = Resolver::new(cache);
2684
2685 let err = resolver
2687 .abilities(type_("0xd0::m::T<u8>"))
2688 .await
2689 .unwrap_err();
2690 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2691
2692 let err = resolver
2694 .abilities(type_("0xd0::m::T<u8, u16, u32>"))
2695 .await
2696 .unwrap_err();
2697 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2698 }
2699
2700 #[tokio::test]
2701 async fn test_err_ability_signer() {
2702 let (_, cache) = package_cache([]);
2703 let resolver = Resolver::new(cache);
2704
2705 let err = resolver.abilities(type_("signer")).await.unwrap_err();
2706 assert!(matches!(err, Error::UnexpectedSigner));
2707 }
2708
2709 #[tokio::test]
2710 async fn test_err_too_many_type_params() {
2711 let (_, cache) = package_cache([
2712 (1, build_package("iota").unwrap(), iota_types()),
2713 (1, build_package("d0").unwrap(), d0_types()),
2714 ]);
2715
2716 let resolver = Resolver::new_with_limits(
2717 cache,
2718 Limits {
2719 max_type_argument_width: 1,
2720 max_type_argument_depth: 100,
2721 max_type_nodes: 100,
2722 max_move_value_depth: 100,
2723 },
2724 );
2725
2726 let err = resolver
2727 .abilities(type_("0xd0::m::O<u32, u64>"))
2728 .await
2729 .unwrap_err();
2730 assert!(matches!(err, Error::TooManyTypeParams(1, 2)));
2731 }
2732
2733 #[tokio::test]
2734 async fn test_err_too_many_type_nodes() {
2735 use Ability as A;
2736 use AbilitySet as S;
2737
2738 let (_, cache) = package_cache([
2739 (1, build_package("iota").unwrap(), iota_types()),
2740 (1, build_package("d0").unwrap(), d0_types()),
2741 ]);
2742
2743 let resolver = Resolver::new_with_limits(
2744 cache,
2745 Limits {
2746 max_type_argument_width: 100,
2747 max_type_argument_depth: 100,
2748 max_type_nodes: 2,
2749 max_move_value_depth: 100,
2750 },
2751 );
2752
2753 let a1 = resolver
2756 .abilities(type_("0xd0::m::O<0xd0::m::S, 0xd0::m::Q>"))
2757 .await
2758 .unwrap();
2759 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2760
2761 let err = resolver
2763 .abilities(type_("0xd0::m::T<0xd0::m::P, 0xd0::m::Q>"))
2764 .await
2765 .unwrap_err();
2766 assert!(matches!(err, Error::TooManyTypeNodes(2, _)));
2767 }
2768
2769 #[tokio::test]
2770 async fn test_err_type_param_nesting() {
2771 use Ability as A;
2772 use AbilitySet as S;
2773
2774 let (_, cache) = package_cache([
2775 (1, build_package("iota").unwrap(), iota_types()),
2776 (1, build_package("d0").unwrap(), d0_types()),
2777 ]);
2778
2779 let resolver = Resolver::new_with_limits(
2780 cache,
2781 Limits {
2782 max_type_argument_width: 100,
2783 max_type_argument_depth: 2,
2784 max_type_nodes: 100,
2785 max_move_value_depth: 100,
2786 },
2787 );
2788
2789 let a1 = resolver
2792 .abilities(type_(
2793 "0xd0::m::O<0xd0::m::S, 0xd0::m::T<vector<u32>, vector<u64>>>",
2794 ))
2795 .await
2796 .unwrap();
2797 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2798
2799 let err = resolver
2801 .abilities(type_("vector<0xd0::m::T<0xd0::m::O<u64, u32>, u16>>"))
2802 .await
2803 .unwrap_err();
2804 assert!(matches!(err, Error::TypeParamNesting(2, _)));
2805 }
2806
2807 #[tokio::test]
2808 async fn test_pure_input_layouts() {
2809 use CallArg as I;
2810 use TypeTag as T;
2811
2812 let (_, cache) = package_cache([
2813 (1, build_package("std").unwrap(), std_types()),
2814 (1, build_package("iota").unwrap(), iota_types()),
2815 (1, build_package("e0").unwrap(), e0_types()),
2816 ]);
2817
2818 let resolver = Resolver::new(cache);
2819
2820 fn ptb(t: TypeTag, y: CallArg) -> ProgrammableTransaction {
2822 ProgrammableTransaction {
2823 inputs: vec![
2824 I::ImmutableOrOwned(random_object_ref()),
2825 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2826 I::ImmutableOrOwned(random_object_ref()),
2827 y,
2828 I::ImmutableOrOwned(random_object_ref()),
2829 I::Pure(bcs::to_bytes("hello").unwrap()),
2830 I::Pure(bcs::to_bytes("world").unwrap()),
2831 ],
2832 commands: vec![Command::new_move_call(
2833 obj_id("0xe0"),
2834 Identifier::from_static("m"),
2835 Identifier::from_static("foo"),
2836 vec![t],
2837 (0..=6).map(Argument::Input).collect(),
2838 )],
2839 }
2840 }
2841
2842 let ptb_u64 = ptb(T::U64, I::Pure(bcs::to_bytes(&1u64).unwrap()));
2843
2844 let ptb_opt = ptb(
2845 TypeTag::Struct(Box::new(StructTag::new(
2846 addr("0x1"),
2847 Identifier::OPTION_MODULE,
2848 Identifier::from_static("Option"),
2849 vec![TypeTag::U64],
2850 ))),
2851 I::Pure(bcs::to_bytes(&[vec![1u64], vec![], vec![3]]).unwrap()),
2852 );
2853
2854 let ptb_obj = ptb(
2855 TypeTag::Struct(Box::new(StructTag::new(
2856 addr("0xe0"),
2857 Identifier::from_static("m"),
2858 Identifier::from_static("O"),
2859 vec![],
2860 ))),
2861 I::ImmutableOrOwned(random_object_ref()),
2862 );
2863
2864 let inputs_u64 = resolver.pure_input_layouts(&ptb_u64).await.unwrap();
2865 let inputs_opt = resolver.pure_input_layouts(&ptb_opt).await.unwrap();
2866 let inputs_obj = resolver.pure_input_layouts(&ptb_obj).await.unwrap();
2867
2868 let mut output = "---\n".to_string();
2870 for inputs in [inputs_u64, inputs_opt, inputs_obj] {
2871 for input in inputs {
2872 if let Some(layout) = input {
2873 output += &format!("{layout:#}\n");
2874 } else {
2875 output += "???\n";
2876 }
2877 }
2878 output += "---\n";
2879 }
2880
2881 insta::assert_snapshot!(output);
2882 }
2883
2884 #[tokio::test]
2887 async fn test_pure_input_layouts_overlapping() {
2888 use CallArg as I;
2889 use TypeTag as T;
2890
2891 let (_, cache) = package_cache([
2892 (1, build_package("std").unwrap(), std_types()),
2893 (1, build_package("iota").unwrap(), iota_types()),
2894 (1, build_package("e0").unwrap(), e0_types()),
2895 ]);
2896
2897 let resolver = Resolver::new(cache);
2898
2899 let ptb = ProgrammableTransaction {
2901 inputs: vec![
2902 I::ImmutableOrOwned(random_object_ref()),
2903 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2904 I::ImmutableOrOwned(random_object_ref()),
2905 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2906 I::ImmutableOrOwned(random_object_ref()),
2907 I::Pure(bcs::to_bytes("hello").unwrap()),
2908 I::Pure(bcs::to_bytes("world").unwrap()),
2909 ],
2910 commands: vec![
2911 Command::new_move_call(
2912 obj_id("0xe0"),
2913 Identifier::from_static("m"),
2914 Identifier::from_static("foo"),
2915 vec![T::U64],
2916 (0..=6).map(Argument::Input).collect(),
2917 ),
2918 Command::new_move_call(
2919 obj_id("0xe0"),
2920 Identifier::from_static("m"),
2921 Identifier::from_static("foo"),
2922 vec![T::U64],
2923 (0..=6).map(Argument::Input).collect(),
2924 ),
2925 ],
2926 };
2927
2928 let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
2929
2930 let mut output = String::new();
2932 for input in inputs {
2933 if let Some(layout) = input {
2934 output += &format!("{layout:#}\n");
2935 } else {
2936 output += "???\n";
2937 }
2938 }
2939
2940 insta::assert_snapshot!(output);
2941 }
2942 #[tokio::test]
2943 async fn test_pure_input_layouts_conflicting() {
2944 use CallArg as I;
2945 use TypeTag as T;
2946
2947 let (_, cache) = package_cache([
2948 (1, build_package("std").unwrap(), std_types()),
2949 (1, build_package("iota").unwrap(), iota_types()),
2950 (1, build_package("e0").unwrap(), e0_types()),
2951 ]);
2952
2953 let resolver = Resolver::new(cache);
2954
2955 let ptb = ProgrammableTransaction {
2956 inputs: vec![
2957 I::ImmutableOrOwned(random_object_ref()),
2958 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2959 I::ImmutableOrOwned(random_object_ref()),
2960 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2961 I::ImmutableOrOwned(random_object_ref()),
2962 I::Pure(bcs::to_bytes("hello").unwrap()),
2963 I::Pure(bcs::to_bytes("world").unwrap()),
2964 ],
2965 commands: vec![
2966 Command::new_move_call(
2967 obj_id("0xe0"),
2968 Identifier::from_static("m"),
2969 Identifier::from_static("foo"),
2970 vec![T::U64],
2971 (0..=6).map(Argument::Input).collect(),
2972 ),
2973 Command::new_make_move_vector(Some(T::U32), vec![Argument::Input(3)]),
2976 ],
2977 };
2978
2979 insta::assert_snapshot!(
2980 resolver.pure_input_layouts(&ptb).await.unwrap_err(),
2981 @"Conflicting types for input 3: u64 and u32"
2982 );
2983 }
2984
2985 type TypeOriginTable = Vec<DatatypeKey>;
2990
2991 fn a0_types() -> TypeOriginTable {
2992 vec![
2993 datakey("0xa0", "m", "T0"),
2994 datakey("0xa0", "m", "T1"),
2995 datakey("0xa0", "m", "T2"),
2996 datakey("0xa0", "m", "E0"),
2997 datakey("0xa0", "m", "E1"),
2998 datakey("0xa0", "m", "E2"),
2999 datakey("0xa0", "n", "T0"),
3000 datakey("0xa0", "n", "E0"),
3001 ]
3002 }
3003
3004 fn a1_types() -> TypeOriginTable {
3005 let mut types = a0_types();
3006
3007 types.extend([
3008 datakey("0xa1", "m", "T3"),
3009 datakey("0xa1", "m", "T4"),
3010 datakey("0xa1", "n", "T1"),
3011 datakey("0xa1", "m", "E3"),
3012 datakey("0xa1", "m", "E4"),
3013 datakey("0xa1", "n", "E1"),
3014 ]);
3015
3016 types
3017 }
3018
3019 fn b0_types() -> TypeOriginTable {
3020 vec![datakey("0xb0", "m", "T0"), datakey("0xb0", "m", "E0")]
3021 }
3022
3023 fn c0_types() -> TypeOriginTable {
3024 vec![datakey("0xc0", "m", "T0"), datakey("0xc0", "m", "E0")]
3025 }
3026
3027 fn d0_types() -> TypeOriginTable {
3028 vec![
3029 datakey("0xd0", "m", "O"),
3030 datakey("0xd0", "m", "P"),
3031 datakey("0xd0", "m", "Q"),
3032 datakey("0xd0", "m", "R"),
3033 datakey("0xd0", "m", "S"),
3034 datakey("0xd0", "m", "T"),
3035 datakey("0xd0", "m", "EO"),
3036 datakey("0xd0", "m", "EP"),
3037 datakey("0xd0", "m", "EQ"),
3038 datakey("0xd0", "m", "ER"),
3039 datakey("0xd0", "m", "ES"),
3040 datakey("0xd0", "m", "ET"),
3041 ]
3042 }
3043
3044 fn e0_types() -> TypeOriginTable {
3045 vec![datakey("0xe0", "m", "O")]
3046 }
3047
3048 fn s0_types() -> TypeOriginTable {
3049 vec![datakey("0x1", "m", "T0"), datakey("0x1", "m", "E0")]
3050 }
3051
3052 fn s1_types() -> TypeOriginTable {
3053 let mut types = s0_types();
3054
3055 types.extend([datakey("0x1", "m", "T1"), datakey("0x1", "m", "E1")]);
3056
3057 types
3058 }
3059
3060 fn iota_types() -> TypeOriginTable {
3061 vec![datakey("0x2", "object", "UID")]
3062 }
3063
3064 fn std_types() -> TypeOriginTable {
3065 vec![
3066 datakey("0x1", "ascii", "String"),
3067 datakey("0x1", "option", "Option"),
3068 datakey("0x1", "string", "String"),
3069 ]
3070 }
3071
3072 fn package_cache(
3078 packages: impl IntoIterator<Item = (u64, CompiledPackage, TypeOriginTable)>,
3079 ) -> (
3080 Arc<RwLock<InnerStore>>,
3081 PackageStoreWithLruCache<InMemoryPackageStore>,
3082 ) {
3083 let packages_by_storage_id: BTreeMap<Address, _> = packages
3084 .into_iter()
3085 .map(|(version, package, origins)| {
3086 (package_storage_id(&package), (version, package, origins))
3087 })
3088 .collect();
3089
3090 let packages = packages_by_storage_id
3091 .iter()
3092 .map(|(&storage_id, (version, compiled_package, origins))| {
3093 let linkage = compiled_package
3094 .dependency_ids
3095 .published
3096 .values()
3097 .map(|dep_id| {
3098 let storage_id = Address::from(*dep_id);
3099 let runtime_id = package_runtime_id(
3100 &packages_by_storage_id
3101 .get(&storage_id)
3102 .unwrap_or_else(|| panic!("Dependency {storage_id} not in store"))
3103 .1,
3104 );
3105
3106 (runtime_id, storage_id)
3107 })
3108 .collect();
3109
3110 let package = cached_package(*version, linkage, compiled_package, origins);
3111 (storage_id, package)
3112 })
3113 .collect();
3114
3115 let inner = Arc::new(RwLock::new(InnerStore {
3116 packages,
3117 fetches: 0,
3118 }));
3119
3120 let store = InMemoryPackageStore {
3121 inner: inner.clone(),
3122 };
3123
3124 (inner, PackageStoreWithLruCache::new(store))
3125 }
3126
3127 fn cached_package(
3128 version: u64,
3129 linkage: Linkage,
3130 package: &CompiledPackage,
3131 origins: &TypeOriginTable,
3132 ) -> Package {
3133 let storage_id = package_storage_id(package);
3134 let runtime_id = package_runtime_id(package);
3135 let version = Version::from_u64(version);
3136
3137 let mut modules = BTreeMap::new();
3138 for unit in &package.package.root_compiled_units {
3139 let NamedCompiledModule { name, module, .. } = &unit.unit;
3140
3141 let origins = origins
3142 .iter()
3143 .filter(|key| key.module == name.as_str())
3144 .map(|key| (key.name.to_string(), key.package))
3145 .collect();
3146
3147 let module = match Module::read(module.clone(), origins) {
3148 Ok(module) => module,
3149 Err(struct_) => {
3150 panic!("Missing type origin for {}::{struct_}", module.self_id());
3151 }
3152 };
3153
3154 modules.insert(name.to_string(), module);
3155 }
3156
3157 Package {
3158 storage_id,
3159 runtime_id,
3160 linkage,
3161 version,
3162 modules,
3163 }
3164 }
3165
3166 fn package_storage_id(package: &CompiledPackage) -> Address {
3167 Address::new(
3168 package
3169 .published_at
3170 .as_ref()
3171 .unwrap_or_else(|_| {
3172 panic!(
3173 "Package {} doesn't have published-at set",
3174 package.package.compiled_package_info.package_name,
3175 )
3176 })
3177 .into_bytes(),
3178 )
3179 }
3180
3181 fn package_runtime_id(package: &CompiledPackage) -> Address {
3182 Address::new(
3183 package
3184 .published_root_module()
3185 .expect("No compiled module")
3186 .address()
3187 .into_bytes(),
3188 )
3189 }
3190
3191 fn build_package(dir: &str) -> IotaResult<CompiledPackage> {
3192 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3193 path.extend(["tests", "packages", dir]);
3194 BuildConfig::new_for_testing().build(&path)
3195 }
3196
3197 fn addr(a: &str) -> Address {
3198 Address::from_str(a).unwrap()
3199 }
3200
3201 fn obj_id(a: &str) -> ObjectId {
3202 ObjectId::from_str(a).unwrap()
3203 }
3204
3205 fn datakey(a: &str, m: &'static str, n: &'static str) -> DatatypeKey {
3206 DatatypeKey {
3207 package: addr(a),
3208 module: m.into(),
3209 name: n.into(),
3210 }
3211 }
3212
3213 fn type_(t: &str) -> TypeTag {
3214 TypeTag::from_str(t).unwrap()
3215 }
3216
3217 fn key(t: &str) -> DatatypeKey {
3218 let tag = StructTag::from_str(t).unwrap();
3219 DatatypeRef::from(&tag).as_key()
3220 }
3221
3222 struct InMemoryPackageStore {
3223 inner: Arc<RwLock<InnerStore>>,
3226 }
3227
3228 struct InnerStore {
3229 packages: BTreeMap<Address, Package>,
3230 fetches: usize,
3231 }
3232
3233 #[async_trait]
3234 impl PackageStore for InMemoryPackageStore {
3235 async fn fetch(&self, id: Address) -> Result<Arc<Package>> {
3236 let mut inner = self.inner.as_ref().write().unwrap();
3237 inner.fetches += 1;
3238 inner
3239 .packages
3240 .get(&id)
3241 .cloned()
3242 .ok_or_else(|| Error::PackageNotFound(id))
3243 .map(Arc::new)
3244 }
3245 }
3246
3247 impl InnerStore {
3248 fn replace(&mut self, id: Address, package: Package) {
3249 self.packages.insert(id, package);
3250 }
3251 }
3252}