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