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