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