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