1use std::borrow::Cow;
5use std::collections::BTreeMap;
6use std::collections::BTreeSet;
7use std::num::NonZeroUsize;
8use std::sync::Arc;
9use std::sync::Mutex;
10
11use async_trait::async_trait;
12use itertools::Itertools;
13use lru::LruCache;
14use move_binary_format::CompiledModule;
15use move_binary_format::errors::Location;
16use move_binary_format::file_format::AbilitySet;
17use move_binary_format::file_format::DatatypeHandleIndex;
18use move_binary_format::file_format::DatatypeTyParameter;
19use move_binary_format::file_format::EnumDefinitionIndex;
20use move_binary_format::file_format::FunctionDefinitionIndex;
21use move_binary_format::file_format::Signature as MoveSignature;
22use move_binary_format::file_format::SignatureIndex;
23use move_binary_format::file_format::SignatureToken;
24use move_binary_format::file_format::StructDefinitionIndex;
25use move_binary_format::file_format::StructFieldInformation;
26use move_binary_format::file_format::TableIndex;
27use move_binary_format::file_format::Visibility;
28use move_binary_format::partial_vm_error;
29use move_command_line_common::display::RenderResult;
30use move_command_line_common::display::try_render_constant;
31use move_command_line_common::error_bitset::ErrorBitset;
32use move_core_types::account_address::AccountAddress;
33use move_core_types::annotated_value as A;
34use move_core_types::annotated_value::MoveEnumLayout;
35use move_core_types::annotated_value::MoveFieldLayout;
36use move_core_types::annotated_value::MoveStructLayout;
37use move_core_types::annotated_value::MoveTypeLayout;
38use move_core_types::language_storage::ModuleId;
39use move_core_types::language_storage::StructTag;
40use move_core_types::language_storage::TypeTag;
41use sui_types::Identifier;
42use sui_types::base_types::ObjectID;
43use sui_types::base_types::SequenceNumber;
44use sui_types::base_types::is_primitive_type_tag;
45use sui_types::error::{SuiError, SuiErrorKind};
46use sui_types::layout_resolver::LayoutResolver;
47use sui_types::move_package::MovePackage;
48use sui_types::move_package::TypeOrigin;
49use sui_types::object::Object;
50use sui_types::storage::BackingPackageStore;
51use sui_types::transaction::Argument;
52use sui_types::transaction::CallArg;
53use sui_types::transaction::Command;
54use sui_types::transaction::ProgrammableTransaction;
55use sui_types::type_input::StructInput;
56use sui_types::type_input::TypeInput;
57
58use crate::error::Error;
59use crate::limits::{LayoutBudget, LayoutMeter};
60
61pub mod error;
62mod limits;
63
64pub use limits::DEFAULT_MAX_LAYOUT_WORK;
65pub use limits::Limits;
66
67const PACKAGE_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
70
71pub type Result<T> = std::result::Result<T, Error>;
72
73#[derive(Debug)]
76pub struct Resolver<S> {
77 package_store: S,
78 limits: Option<Limits>,
79}
80
81#[derive(Debug)]
85pub struct SyncResolver<S> {
86 package_store: S,
87 limits: Option<Limits>,
88}
89
90struct ResolvedLayout {
91 layout: MoveTypeLayout,
92 depth: usize,
93 nodes: usize,
94}
95
96impl ResolvedLayout {
97 fn leaf(layout: MoveTypeLayout) -> Self {
98 Self {
99 layout,
100 depth: 1,
101 nodes: 1,
102 }
103 }
104}
105
106pub struct PackageStoreWithLruCache<T> {
110 pub(crate) packages: Mutex<LruCache<AccountAddress, Arc<Package>>>,
111 pub(crate) inner: T,
112}
113
114#[derive(Clone, Debug)]
115pub struct Package {
116 storage_id: AccountAddress,
118
119 runtime_id: AccountAddress,
122
123 linkage: Linkage,
126
127 version: SequenceNumber,
130
131 modules: BTreeMap<String, Module>,
132}
133
134type Linkage = BTreeMap<AccountAddress, AccountAddress>;
135
136#[derive(Clone, Debug)]
142pub struct CleverError {
143 pub module_id: ModuleId,
145 pub error_info: ErrorConstants,
148 pub source_line_number: u16,
150 pub error_code: Option<u8>,
152}
153
154#[derive(Clone, Debug)]
164pub enum ErrorConstants {
165 None,
167 Rendered {
177 identifier: String,
179 constant: String,
181 },
182 Raw {
186 identifier: String,
188 bytes: Vec<u8>,
190 },
191}
192
193#[derive(Clone, Debug)]
194pub struct Module {
195 bytecode: CompiledModule,
196
197 struct_index: BTreeMap<String, (AccountAddress, StructDefinitionIndex)>,
200
201 enum_index: BTreeMap<String, (AccountAddress, EnumDefinitionIndex)>,
204
205 function_index: BTreeMap<String, FunctionDefinitionIndex>,
208}
209
210#[derive(Debug)]
212pub struct DataDef {
213 pub defining_id: AccountAddress,
215
216 pub abilities: AbilitySet,
218
219 pub type_params: Vec<DatatypeTyParameter>,
221
222 pub data: MoveData,
225}
226
227#[derive(Debug)]
228pub enum MoveData {
229 Struct(Vec<(String, OpenSignatureBody)>),
232
233 Enum(Vec<VariantDef>),
235}
236
237#[derive(Debug)]
239pub struct VariantDef {
240 pub name: String,
242
243 pub signatures: Vec<(String, OpenSignatureBody)>,
246}
247
248#[derive(Debug)]
250pub struct FunctionDef {
251 pub visibility: Visibility,
253
254 pub is_entry: bool,
256
257 pub type_params: Vec<AbilitySet>,
259
260 pub parameters: Vec<OpenSignature>,
262
263 pub return_: Vec<OpenSignature>,
265}
266
267#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
271pub struct DatatypeRef<'m, 'n> {
272 pub package: AccountAddress,
273 pub module: Cow<'m, str>,
274 pub name: Cow<'n, str>,
275}
276
277pub type DatatypeKey = DatatypeRef<'static, 'static>;
279
280#[derive(Copy, Clone, Debug)]
281pub enum Reference {
282 Immutable,
283 Mutable,
284}
285
286#[derive(Clone, Debug)]
288pub struct Signature {
289 pub ref_: Option<Reference>,
290 pub body: TypeTag,
291}
292
293#[derive(Clone, Debug)]
296pub struct OpenSignature {
297 pub ref_: Option<Reference>,
298 pub body: OpenSignatureBody,
299}
300
301#[derive(Clone, Debug)]
303pub enum OpenSignatureBody {
304 Address,
305 Bool,
306 U8,
307 U16,
308 U32,
309 U64,
310 U128,
311 U256,
312 Vector(Box<OpenSignatureBody>),
313 Datatype(DatatypeKey, Vec<OpenSignatureBody>),
314 TypeParameter(u16),
315}
316
317#[derive(Debug, Default)]
319struct ResolutionContext<'l> {
320 datatypes: BTreeMap<DatatypeKey, DataDef>,
322
323 limits: Option<&'l Limits>,
325}
326
327#[async_trait]
330pub trait PackageStore: Send + Sync {
331 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>>;
334}
335
336pub trait SyncPackageStore {
339 fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>>;
340}
341
342macro_rules! as_ref_impl {
343 ($type:ty) => {
344 #[async_trait]
345 impl PackageStore for $type {
346 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
347 self.as_ref().fetch(id).await
348 }
349 }
350 };
351}
352
353as_ref_impl!(Arc<dyn PackageStore>);
354as_ref_impl!(Box<dyn PackageStore>);
355
356#[async_trait]
357impl<S: PackageStore> PackageStore for Arc<S> {
358 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
359 self.as_ref().fetch(id).await
360 }
361}
362
363impl<S: BackingPackageStore> SyncPackageStore for S {
367 fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
368 let object_id = ObjectID::from(id);
369 let package_obj = self
370 .get_package_object(&object_id)
371 .map_err(|e| Error::Store {
372 store: "BackingPackageStore",
373 error: e.to_string(),
374 })?
375 .ok_or(Error::PackageNotFound(id))?;
376 Ok(Arc::new(Package::read_from_object(package_obj.object())?))
377 }
378}
379
380macro_rules! check_max_limit {
383 ($err:ident, $config:expr; $limit:ident $op:tt $value:expr) => {
384 if let Some(l) = $config {
385 let max = l.$limit;
386 let val = $value;
387 if !(max $op val) {
388 return Err(Error::$err(max, val));
389 }
390 }
391 };
392}
393
394impl<S> Resolver<S> {
395 pub fn new(package_store: S) -> Self {
396 Self {
397 package_store,
398 limits: None,
399 }
400 }
401
402 pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
403 Self {
404 package_store,
405 limits: Some(limits),
406 }
407 }
408
409 pub fn package_store(&self) -> &S {
410 &self.package_store
411 }
412
413 pub fn package_store_mut(&mut self) -> &mut S {
414 &mut self.package_store
415 }
416}
417
418impl<S> SyncResolver<S> {
419 pub fn new(package_store: S) -> Self {
420 Self {
421 package_store,
422 limits: None,
423 }
424 }
425
426 pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
427 Self {
428 package_store,
429 limits: Some(limits),
430 }
431 }
432
433 pub fn package_store(&self) -> &S {
434 &self.package_store
435 }
436
437 pub fn package_store_mut(&mut self) -> &mut S {
438 &mut self.package_store
439 }
440}
441
442impl<S: PackageStore> Resolver<S> {
443 pub async fn canonical_type(&self, mut tag: TypeTag) -> Result<TypeTag> {
452 let mut context = ResolutionContext::new(self.limits.as_ref());
453
454 context
457 .add_type_tag(
458 &mut tag,
459 &self.package_store,
460 false,
461 true,
462 )
463 .await?;
464
465 context.canonicalize_type(&mut tag)?;
467 Ok(tag)
468 }
469
470 pub async fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
474 let mut context = ResolutionContext::new(self.limits.as_ref());
475
476 context
479 .add_type_tag(
480 &mut tag,
481 &self.package_store,
482 true,
483 true,
484 )
485 .await?;
486
487 let max_depth = self
489 .limits
490 .as_ref()
491 .map(|limits| limits.max_move_value_depth);
492 let mut budget = self
493 .limits
494 .as_ref()
495 .map_or_else(LayoutBudget::unlimited, Limits::layout_budget);
496 let mut meter = LayoutMeter::new(max_depth, &mut budget);
497
498 Ok(context.resolve_type_layout(&tag, &mut meter)?.layout)
499 }
500
501 pub async fn datatype_layout(&self, struct_tag: &StructTag) -> Result<A::MoveDatatypeLayout> {
505 let type_tag = TypeTag::Struct(Box::new(struct_tag.clone()));
506 let layout = self.type_layout(type_tag).await?;
507 match layout {
508 MoveTypeLayout::Struct(s) => Ok(A::MoveDatatypeLayout::Struct(s)),
509 MoveTypeLayout::Enum(e) => Ok(A::MoveDatatypeLayout::Enum(e)),
510 layout => {
511 Err(Error::UnexpectedError(Arc::new(partial_vm_error!(
515 UNKNOWN_INVARIANT_VIOLATION_ERROR,
516 "StructTag {:#?} did not resolve to a struct or enum layout: {:#?}",
517 struct_tag,
518 layout
519 ))))
520 }
521 }
522 }
523
524 pub async fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
530 let mut context = ResolutionContext::new(self.limits.as_ref());
531
532 context
535 .add_type_tag(
536 &mut tag,
537 &self.package_store,
538 false,
539 false,
540 )
541 .await?;
542
543 context.resolve_abilities(&tag)
545 }
546
547 pub async fn function_signature(
550 &self,
551 pkg: AccountAddress,
552 module: &str,
553 function: &str,
554 ) -> Result<FunctionDef> {
555 let mut context = ResolutionContext::new(self.limits.as_ref());
556
557 let package = self.package_store.fetch(pkg).await?;
558 let Some(mut def) = package.module(module)?.function_def(function)? else {
559 return Err(Error::FunctionNotFound(
560 pkg,
561 module.to_string(),
562 function.to_string(),
563 ));
564 };
565
566 for sig in def.parameters.iter().chain(def.return_.iter()) {
569 context
570 .add_signature(
571 sig.body.clone(),
572 &self.package_store,
573 package.as_ref(),
574 false,
575 )
576 .await?;
577 }
578
579 for sig in def.parameters.iter_mut().chain(def.return_.iter_mut()) {
581 context.relocate_signature(&mut sig.body)?;
582 }
583
584 Ok(def)
585 }
586
587 pub async fn pure_input_layouts(
597 &self,
598 tx: &ProgrammableTransaction,
599 ) -> Result<Vec<Option<MoveTypeLayout>>> {
600 let mut tags = vec![None; tx.inputs.len()];
601 let mut register_type = |arg: &Argument, tag: &TypeTag| {
602 let &Argument::Input(ix) = arg else {
603 return;
604 };
605
606 if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
607 return;
608 }
609
610 let Some(type_) = tags.get_mut(ix as usize) else {
611 return;
612 };
613
614 match type_ {
618 None => *type_ = Some(Ok(tag.clone())),
619 Some(Err(())) => {}
620 Some(Ok(prev)) => {
621 if prev != tag {
622 *type_ = Some(Err(()));
623 }
624 }
625 }
626 };
627
628 for cmd in &tx.commands {
630 match cmd {
631 Command::MoveCall(call) => {
632 let params = self
633 .function_signature(
634 call.package.into(),
635 call.module.as_str(),
636 call.function.as_str(),
637 )
638 .await?
639 .parameters;
640
641 #[allow(clippy::disallowed_methods)]
642 for (open_sig, arg) in params.iter().zip(call.arguments.iter()) {
644 let sig = open_sig.instantiate(&call.type_arguments)?;
645 register_type(arg, &sig.body);
646 }
647 }
648
649 Command::TransferObjects(_, arg) => register_type(arg, &TypeTag::Address),
650
651 Command::SplitCoins(_, amounts) => {
652 for amount in amounts {
653 register_type(amount, &TypeTag::U64);
654 }
655 }
656
657 Command::MakeMoveVec(Some(tag), elems) => {
658 let tag = as_type_tag(tag)?;
659 if is_primitive_type_tag(&tag) {
660 for elem in elems {
661 register_type(elem, &tag);
662 }
663 }
664 }
665
666 _ => { }
667 }
668 }
669
670 let unique_tags: BTreeSet<_> = tags
673 .iter()
674 .flat_map(|t| t.clone())
675 .flat_map(|t| t.ok())
676 .collect();
677
678 let mut layouts = BTreeMap::new();
680 for tag in unique_tags {
681 let layout = self.type_layout(tag.clone()).await?;
682 layouts.insert(tag, layout);
683 }
684
685 Ok(tags
687 .iter()
688 .map(|t| -> Option<_> {
689 let t = t.as_ref()?;
690 let t = t.as_ref().ok()?;
691 layouts.get(t).cloned()
692 })
693 .collect())
694 }
695
696 pub async fn resolve_module_id(
703 &self,
704 module_id: ModuleId,
705 context: AccountAddress,
706 ) -> Result<ModuleId> {
707 let package = self.package_store.fetch(context).await?;
708 let storage_id = package.relocate(*module_id.address())?;
709 Ok(ModuleId::new(storage_id, module_id.name().to_owned()))
710 }
711
712 pub async fn resolve_clever_error(
726 &self,
727 module_id: ModuleId,
728 abort_code: u64,
729 ) -> Option<CleverError> {
730 let _bitset = ErrorBitset::from_u64(abort_code)?;
731 let package = self.package_store.fetch(*module_id.address()).await.ok()?;
732 package.resolve_clever_error(module_id.name().as_str(), abort_code)
733 }
734}
735
736impl<S: SyncPackageStore> SyncResolver<S> {
741 pub fn canonical_type(&self, mut tag: TypeTag) -> Result<TypeTag> {
750 let mut context = ResolutionContext::new(self.limits.as_ref());
751
752 context.add_type_tag_sync(
755 &mut tag,
756 &self.package_store,
757 false,
758 true,
759 )?;
760
761 context.canonicalize_type(&mut tag)?;
763 Ok(tag)
764 }
765
766 pub fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
770 let mut context = ResolutionContext::new(self.limits.as_ref());
771
772 context.add_type_tag_sync(
775 &mut tag,
776 &self.package_store,
777 true,
778 true,
779 )?;
780
781 let max_depth = self
783 .limits
784 .as_ref()
785 .map(|limits| limits.max_move_value_depth);
786 let mut budget = self
787 .limits
788 .as_ref()
789 .map_or_else(LayoutBudget::unlimited, Limits::layout_budget);
790 let mut meter = LayoutMeter::new(max_depth, &mut budget);
791
792 Ok(context.resolve_type_layout(&tag, &mut meter)?.layout)
793 }
794
795 pub fn datatype_layout(&self, struct_tag: &StructTag) -> Result<A::MoveDatatypeLayout> {
799 let type_tag = TypeTag::Struct(Box::new(struct_tag.clone()));
800 let layout = self.type_layout(type_tag)?;
801 match layout {
802 MoveTypeLayout::Struct(s) => Ok(A::MoveDatatypeLayout::Struct(s)),
803 MoveTypeLayout::Enum(e) => Ok(A::MoveDatatypeLayout::Enum(e)),
804 layout => {
805 Err(Error::UnexpectedError(Arc::new(partial_vm_error!(
809 UNKNOWN_INVARIANT_VIOLATION_ERROR,
810 "StructTag {:#?} did not resolve to a struct or enum layout: {:#?}",
811 struct_tag,
812 layout
813 ))))
814 }
815 }
816 }
817
818 pub fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
824 let mut context = ResolutionContext::new(self.limits.as_ref());
825
826 context.add_type_tag_sync(
829 &mut tag,
830 &self.package_store,
831 false,
832 false,
833 )?;
834
835 context.resolve_abilities(&tag)
837 }
838
839 pub fn function_signature(
842 &self,
843 pkg: AccountAddress,
844 module: &str,
845 function: &str,
846 ) -> Result<FunctionDef> {
847 let mut context = ResolutionContext::new(self.limits.as_ref());
848
849 let package = self.package_store.fetch(pkg)?;
850 let Some(mut def) = package.module(module)?.function_def(function)? else {
851 return Err(Error::FunctionNotFound(
852 pkg,
853 module.to_string(),
854 function.to_string(),
855 ));
856 };
857
858 for sig in def.parameters.iter().chain(def.return_.iter()) {
861 context.add_signature_sync(
862 sig.body.clone(),
863 &self.package_store,
864 package.as_ref(),
865 false,
866 )?;
867 }
868
869 for sig in def.parameters.iter_mut().chain(def.return_.iter_mut()) {
871 context.relocate_signature(&mut sig.body)?;
872 }
873
874 Ok(def)
875 }
876
877 pub fn pure_input_layouts(
887 &self,
888 tx: &ProgrammableTransaction,
889 ) -> Result<Vec<Option<MoveTypeLayout>>> {
890 let mut tags = vec![None; tx.inputs.len()];
891 let mut register_type = |arg: &Argument, tag: &TypeTag| {
892 let &Argument::Input(ix) = arg else {
893 return;
894 };
895
896 if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
897 return;
898 }
899
900 let Some(type_) = tags.get_mut(ix as usize) else {
901 return;
902 };
903
904 match type_ {
908 None => *type_ = Some(Ok(tag.clone())),
909 Some(Err(())) => {}
910 Some(Ok(prev)) => {
911 if prev != tag {
912 *type_ = Some(Err(()));
913 }
914 }
915 }
916 };
917
918 for cmd in &tx.commands {
920 match cmd {
921 Command::MoveCall(call) => {
922 let params = self
923 .function_signature(
924 call.package.into(),
925 call.module.as_str(),
926 call.function.as_str(),
927 )?
928 .parameters;
929
930 #[allow(clippy::disallowed_methods)]
931 for (open_sig, arg) in params.iter().zip(call.arguments.iter()) {
933 let sig = open_sig.instantiate(&call.type_arguments)?;
934 register_type(arg, &sig.body);
935 }
936 }
937
938 Command::TransferObjects(_, arg) => register_type(arg, &TypeTag::Address),
939
940 Command::SplitCoins(_, amounts) => {
941 for amount in amounts {
942 register_type(amount, &TypeTag::U64);
943 }
944 }
945
946 Command::MakeMoveVec(Some(tag), elems) => {
947 let tag = as_type_tag(tag)?;
948 if is_primitive_type_tag(&tag) {
949 for elem in elems {
950 register_type(elem, &tag);
951 }
952 }
953 }
954
955 _ => { }
956 }
957 }
958
959 let unique_tags: BTreeSet<_> = tags
962 .iter()
963 .flat_map(|t| t.clone())
964 .flat_map(|t| t.ok())
965 .collect();
966
967 let mut layouts = BTreeMap::new();
969 for tag in unique_tags {
970 let layout = self.type_layout(tag.clone())?;
971 layouts.insert(tag, layout);
972 }
973
974 Ok(tags
976 .iter()
977 .map(|t| -> Option<_> {
978 let t = t.as_ref()?;
979 let t = t.as_ref().ok()?;
980 layouts.get(t).cloned()
981 })
982 .collect())
983 }
984
985 pub fn resolve_module_id(
992 &self,
993 module_id: ModuleId,
994 context: AccountAddress,
995 ) -> Result<ModuleId> {
996 let package = self.package_store.fetch(context)?;
997 let storage_id = package.relocate(*module_id.address())?;
998 Ok(ModuleId::new(storage_id, module_id.name().to_owned()))
999 }
1000
1001 pub fn resolve_clever_error(
1015 &self,
1016 module_id: ModuleId,
1017 abort_code: u64,
1018 ) -> Option<CleverError> {
1019 let _bitset = ErrorBitset::from_u64(abort_code)?;
1020 let package = self.package_store.fetch(*module_id.address()).ok()?;
1021 package.resolve_clever_error(module_id.name().as_str(), abort_code)
1022 }
1023}
1024
1025impl<S: SyncPackageStore> LayoutResolver for SyncResolver<S> {
1026 fn get_annotated_layout(
1027 &mut self,
1028 struct_tag: &StructTag,
1029 ) -> std::result::Result<A::MoveDatatypeLayout, SuiError> {
1030 self.datatype_layout(struct_tag).map_err(|e| {
1031 SuiErrorKind::FailObjectLayout {
1032 st: format!("Failed to generate object layout for {struct_tag}: {e}"),
1033 }
1034 .into()
1035 })
1036 }
1037}
1038
1039impl<T> PackageStoreWithLruCache<T> {
1040 pub fn new(inner: T) -> Self {
1041 let packages = Mutex::new(LruCache::new(PACKAGE_CACHE_SIZE));
1042 Self { packages, inner }
1043 }
1044
1045 pub fn evict(&self, ids: impl IntoIterator<Item = AccountAddress>) {
1048 let mut packages = self.packages.lock().unwrap();
1049 for id in ids {
1050 packages.pop(&id);
1051 }
1052 }
1053}
1054
1055impl<T: SyncPackageStore> SyncPackageStore for PackageStoreWithLruCache<T> {
1056 fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
1057 if let Some(package) = {
1058 let mut packages = self.packages.lock().unwrap();
1059 packages.get(&id).map(Arc::clone)
1060 } {
1061 return Ok(package);
1062 };
1063
1064 let package = self.inner.fetch(id)?;
1065
1066 let mut packages = self.packages.lock().unwrap();
1067 Ok(match packages.peek(&id) {
1068 Some(prev) if package.version <= prev.version => {
1069 let package = prev.clone();
1070 packages.promote(&id);
1071 package
1072 }
1073
1074 Some(_) | None => {
1075 packages.push(id, package.clone());
1076 package
1077 }
1078 })
1079 }
1080}
1081
1082impl<T: SyncPackageStore> SyncPackageStore for &PackageStoreWithLruCache<T> {
1083 fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
1084 SyncPackageStore::fetch(*self, id)
1085 }
1086}
1087
1088#[async_trait]
1089impl<T: PackageStore> PackageStore for PackageStoreWithLruCache<T> {
1090 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
1091 if let Some(package) = {
1092 let mut packages = self.packages.lock().unwrap();
1094 packages.get(&id).map(Arc::clone)
1095 } {
1096 return Ok(package);
1097 };
1098
1099 let package = self.inner.fetch(id).await?;
1100
1101 let mut packages = self.packages.lock().unwrap();
1107 Ok(match packages.peek(&id) {
1108 Some(prev) if package.version <= prev.version => {
1109 let package = prev.clone();
1110 packages.promote(&id);
1111 package
1112 }
1113
1114 Some(_) | None => {
1115 packages.push(id, package.clone());
1116 package
1117 }
1118 })
1119 }
1120}
1121
1122impl Package {
1123 pub fn read_from_object(object: &Object) -> Result<Self> {
1124 let storage_id = AccountAddress::from(object.id());
1125 let Some(package) = object.data.try_as_package() else {
1126 return Err(Error::NotAPackage(storage_id));
1127 };
1128
1129 Self::read_from_package(package)
1130 }
1131
1132 pub fn read_from_package(package: &MovePackage) -> Result<Self> {
1133 let storage_id = AccountAddress::from(package.id());
1134 let mut type_origins: BTreeMap<String, BTreeMap<String, AccountAddress>> = BTreeMap::new();
1135 for TypeOrigin {
1136 module_name,
1137 datatype_name,
1138 package,
1139 } in package.type_origin_table()
1140 {
1141 type_origins
1142 .entry(module_name.to_string())
1143 .or_default()
1144 .insert(datatype_name.to_string(), AccountAddress::from(*package));
1145 }
1146
1147 let mut runtime_id = None;
1148 let mut modules = BTreeMap::new();
1149 for (name, bytes) in package.serialized_module_map() {
1150 let origins = type_origins.remove(name).unwrap_or_default();
1151 let bytecode = CompiledModule::deserialize_with_defaults(bytes)
1152 .map_err(|e| Error::Deserialize(e.finish(Location::Undefined)))?;
1153
1154 runtime_id = Some(*bytecode.address());
1155
1156 let name = name.clone();
1157 match Module::read(bytecode, origins) {
1158 Ok(module) => modules.insert(name, module),
1159 Err(struct_) => return Err(Error::NoTypeOrigin(storage_id, name, struct_)),
1160 };
1161 }
1162
1163 let Some(runtime_id) = runtime_id else {
1164 return Err(Error::EmptyPackage(storage_id));
1165 };
1166
1167 let linkage = package
1168 .linkage_table()
1169 .iter()
1170 .map(|(&dep, linkage)| (dep.into(), linkage.upgraded_id.into()))
1171 .collect();
1172
1173 Ok(Package {
1174 storage_id,
1175 runtime_id,
1176 version: package.version(),
1177 modules,
1178 linkage,
1179 })
1180 }
1181
1182 pub fn module(&self, module: &str) -> Result<&Module> {
1183 self.modules
1184 .get(module)
1185 .ok_or_else(|| Error::ModuleNotFound(self.storage_id, module.to_string()))
1186 }
1187
1188 pub fn modules(&self) -> &BTreeMap<String, Module> {
1189 &self.modules
1190 }
1191
1192 pub fn storage_id(&self) -> AccountAddress {
1193 self.storage_id
1194 }
1195
1196 #[cfg(any(test, feature = "testing"))]
1197 pub fn for_test(storage_id: AccountAddress, version: SequenceNumber) -> Self {
1198 Self {
1199 storage_id,
1200 runtime_id: storage_id,
1201 linkage: BTreeMap::new(),
1202 version,
1203 modules: BTreeMap::new(),
1204 }
1205 }
1206
1207 fn data_def(&self, module_name: &str, datatype_name: &str) -> Result<DataDef> {
1208 let module = self.module(module_name)?;
1209 let Some(data_def) = module.data_def(datatype_name)? else {
1210 return Err(Error::DatatypeNotFound(
1211 self.storage_id,
1212 module_name.to_string(),
1213 datatype_name.to_string(),
1214 ));
1215 };
1216 Ok(data_def)
1217 }
1218
1219 fn relocate(&self, runtime_id: AccountAddress) -> Result<AccountAddress> {
1223 if runtime_id == self.runtime_id {
1225 return Ok(self.storage_id);
1226 }
1227
1228 self.linkage
1229 .get(&runtime_id)
1230 .ok_or_else(|| Error::LinkageNotFound(runtime_id))
1231 .copied()
1232 }
1233
1234 pub fn resolve_clever_error(&self, module_name: &str, abort_code: u64) -> Option<CleverError> {
1235 let bitset = ErrorBitset::from_u64(abort_code)?;
1236 let module = self.module(module_name).ok()?.bytecode();
1237 let module_id = ModuleId::new(self.runtime_id, Identifier::new(module_name).ok()?);
1238 let source_line_number = bitset.line_number()?;
1239 let error_code = bitset.error_code();
1240
1241 if bitset.identifier_index().is_none() && bitset.constant_index().is_none() {
1243 return Some(CleverError {
1244 module_id,
1245 error_info: ErrorConstants::None,
1246 source_line_number,
1247 error_code,
1248 });
1249 } else if bitset.identifier_index().is_none() || bitset.constant_index().is_none() {
1250 return None;
1251 }
1252
1253 let error_identifier_constant = module
1254 .constant_pool()
1255 .get(bitset.identifier_index()? as usize)?;
1256 let error_value_constant = module
1257 .constant_pool()
1258 .get(bitset.constant_index()? as usize)?;
1259
1260 if !matches!(&error_identifier_constant.type_, SignatureToken::Vector(x) if x.as_ref() == &SignatureToken::U8)
1261 {
1262 return None;
1263 };
1264
1265 let error_identifier = bcs::from_bytes::<Vec<u8>>(&error_identifier_constant.data)
1266 .ok()
1267 .and_then(|x| String::from_utf8(x).ok())?;
1268 let bytes = error_value_constant.data.clone();
1269
1270 let rendered = try_render_constant(error_value_constant);
1271
1272 let error_info = match rendered {
1273 RenderResult::NotRendered => ErrorConstants::Raw {
1274 identifier: error_identifier,
1275 bytes,
1276 },
1277 RenderResult::AsString(s) | RenderResult::AsValue(s) => ErrorConstants::Rendered {
1278 identifier: error_identifier,
1279 constant: s,
1280 },
1281 };
1282
1283 Some(CleverError {
1284 module_id,
1285 error_info,
1286 source_line_number,
1287 error_code,
1288 })
1289 }
1290}
1291
1292impl Module {
1293 fn read(
1297 bytecode: CompiledModule,
1298 mut origins: BTreeMap<String, AccountAddress>,
1299 ) -> std::result::Result<Self, String> {
1300 let mut struct_index = BTreeMap::new();
1301 for (index, def) in bytecode.struct_defs.iter().enumerate() {
1302 let sh = bytecode.datatype_handle_at(def.struct_handle);
1303 let struct_ = bytecode.identifier_at(sh.name).to_string();
1304 let index = StructDefinitionIndex::new(index as TableIndex);
1305
1306 let Some(defining_id) = origins.remove(&struct_) else {
1307 return Err(struct_);
1308 };
1309
1310 struct_index.insert(struct_, (defining_id, index));
1311 }
1312
1313 let mut enum_index = BTreeMap::new();
1314 for (index, def) in bytecode.enum_defs.iter().enumerate() {
1315 let eh = bytecode.datatype_handle_at(def.enum_handle);
1316 let enum_ = bytecode.identifier_at(eh.name).to_string();
1317 let index = EnumDefinitionIndex::new(index as TableIndex);
1318
1319 let Some(defining_id) = origins.remove(&enum_) else {
1320 return Err(enum_);
1321 };
1322
1323 enum_index.insert(enum_, (defining_id, index));
1324 }
1325
1326 let mut function_index = BTreeMap::new();
1327 for (index, def) in bytecode.function_defs.iter().enumerate() {
1328 let fh = bytecode.function_handle_at(def.function);
1329 let function = bytecode.identifier_at(fh.name).to_string();
1330 let index = FunctionDefinitionIndex::new(index as TableIndex);
1331
1332 function_index.insert(function, index);
1333 }
1334
1335 Ok(Module {
1336 bytecode,
1337 struct_index,
1338 enum_index,
1339 function_index,
1340 })
1341 }
1342
1343 pub fn bytecode(&self) -> &CompiledModule {
1344 &self.bytecode
1345 }
1346
1347 pub fn name(&self) -> &str {
1349 self.bytecode
1350 .identifier_at(self.bytecode.self_handle().name)
1351 .as_str()
1352 }
1353
1354 pub fn structs(
1357 &self,
1358 after: Option<&str>,
1359 before: Option<&str>,
1360 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1361 use std::ops::Bound as B;
1362 self.struct_index
1363 .range::<str, _>((
1364 after.map_or(B::Unbounded, B::Excluded),
1365 before.map_or(B::Unbounded, B::Excluded),
1366 ))
1367 .map(|(name, _)| name.as_str())
1368 }
1369
1370 pub fn enums(
1373 &self,
1374 after: Option<&str>,
1375 before: Option<&str>,
1376 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1377 use std::ops::Bound as B;
1378 self.enum_index
1379 .range::<str, _>((
1380 after.map_or(B::Unbounded, B::Excluded),
1381 before.map_or(B::Unbounded, B::Excluded),
1382 ))
1383 .map(|(name, _)| name.as_str())
1384 }
1385
1386 pub fn datatypes(
1390 &self,
1391 after: Option<&str>,
1392 before: Option<&str>,
1393 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1394 let mut names = self
1395 .structs(after, before)
1396 .chain(self.enums(after, before))
1397 .collect::<Vec<_>>();
1398 names.sort();
1399 names.into_iter()
1400 }
1401
1402 pub fn struct_def(&self, name: &str) -> Result<Option<DataDef>> {
1406 let Some(&(defining_id, index)) = self.struct_index.get(name) else {
1407 return Ok(None);
1408 };
1409
1410 let struct_def = self.bytecode.struct_def_at(index);
1411 let struct_handle = self.bytecode.datatype_handle_at(struct_def.struct_handle);
1412 let abilities = struct_handle.abilities;
1413 let type_params = struct_handle.type_parameters.clone();
1414
1415 let fields = match &struct_def.field_information {
1416 StructFieldInformation::Native => vec![],
1417 StructFieldInformation::Declared(fields) => fields
1418 .iter()
1419 .map(|f| {
1420 Ok((
1421 self.bytecode.identifier_at(f.name).to_string(),
1422 OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
1423 ))
1424 })
1425 .collect::<Result<_>>()?,
1426 };
1427
1428 Ok(Some(DataDef {
1429 defining_id,
1430 abilities,
1431 type_params,
1432 data: MoveData::Struct(fields),
1433 }))
1434 }
1435
1436 pub fn enum_def(&self, name: &str) -> Result<Option<DataDef>> {
1440 let Some(&(defining_id, index)) = self.enum_index.get(name) else {
1441 return Ok(None);
1442 };
1443
1444 let enum_def = self.bytecode.enum_def_at(index);
1445 let enum_handle = self.bytecode.datatype_handle_at(enum_def.enum_handle);
1446 let abilities = enum_handle.abilities;
1447 let type_params = enum_handle.type_parameters.clone();
1448
1449 let variants = enum_def
1450 .variants
1451 .iter()
1452 .map(|variant| {
1453 let name = self
1454 .bytecode
1455 .identifier_at(variant.variant_name)
1456 .to_string();
1457 let signatures = variant
1458 .fields
1459 .iter()
1460 .map(|f| {
1461 Ok((
1462 self.bytecode.identifier_at(f.name).to_string(),
1463 OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
1464 ))
1465 })
1466 .collect::<Result<_>>()?;
1467
1468 Ok(VariantDef { name, signatures })
1469 })
1470 .collect::<Result<_>>()?;
1471
1472 Ok(Some(DataDef {
1473 defining_id,
1474 abilities,
1475 type_params,
1476 data: MoveData::Enum(variants),
1477 }))
1478 }
1479
1480 pub fn data_def(&self, name: &str) -> Result<Option<DataDef>> {
1484 self.struct_def(name)
1485 .transpose()
1486 .or_else(|| self.enum_def(name).transpose())
1487 .transpose()
1488 }
1489
1490 pub fn functions(
1493 &self,
1494 after: Option<&str>,
1495 before: Option<&str>,
1496 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1497 use std::ops::Bound as B;
1498 self.function_index
1499 .range::<str, _>((
1500 after.map_or(B::Unbounded, B::Excluded),
1501 before.map_or(B::Unbounded, B::Excluded),
1502 ))
1503 .map(|(name, _)| name.as_str())
1504 }
1505
1506 pub fn function_def(&self, name: &str) -> Result<Option<FunctionDef>> {
1510 let Some(&index) = self.function_index.get(name) else {
1511 return Ok(None);
1512 };
1513
1514 let function_def = self.bytecode.function_def_at(index);
1515 let function_handle = self.bytecode.function_handle_at(function_def.function);
1516
1517 Ok(Some(FunctionDef {
1518 visibility: function_def.visibility,
1519 is_entry: function_def.is_entry,
1520 type_params: function_handle.type_parameters.clone(),
1521 parameters: read_signature(function_handle.parameters, &self.bytecode)?,
1522 return_: read_signature(function_handle.return_, &self.bytecode)?,
1523 }))
1524 }
1525}
1526
1527impl OpenSignature {
1528 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1529 use SignatureToken as S;
1530 Ok(match sig {
1531 S::Reference(sig) => OpenSignature {
1532 ref_: Some(Reference::Immutable),
1533 body: OpenSignatureBody::read(sig, bytecode)?,
1534 },
1535
1536 S::MutableReference(sig) => OpenSignature {
1537 ref_: Some(Reference::Mutable),
1538 body: OpenSignatureBody::read(sig, bytecode)?,
1539 },
1540
1541 sig => OpenSignature {
1542 ref_: None,
1543 body: OpenSignatureBody::read(sig, bytecode)?,
1544 },
1545 })
1546 }
1547
1548 pub fn instantiate(&self, type_params: &[TypeInput]) -> Result<Signature> {
1553 Ok(Signature {
1554 ref_: self.ref_,
1555 body: self.body.instantiate(type_params)?,
1556 })
1557 }
1558}
1559
1560impl OpenSignatureBody {
1561 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1562 use OpenSignatureBody as O;
1563 use SignatureToken as S;
1564
1565 Ok(match sig {
1566 S::Signer => return Err(Error::UnexpectedSigner),
1567 S::Reference(_) | S::MutableReference(_) => return Err(Error::UnexpectedReference),
1568
1569 S::Address => O::Address,
1570 S::Bool => O::Bool,
1571 S::U8 => O::U8,
1572 S::U16 => O::U16,
1573 S::U32 => O::U32,
1574 S::U64 => O::U64,
1575 S::U128 => O::U128,
1576 S::U256 => O::U256,
1577 S::TypeParameter(ix) => O::TypeParameter(*ix),
1578
1579 S::Vector(sig) => O::Vector(Box::new(OpenSignatureBody::read(sig, bytecode)?)),
1580
1581 S::Datatype(ix) => O::Datatype(DatatypeKey::read(*ix, bytecode), vec![]),
1582 S::DatatypeInstantiation(inst) => {
1583 let (ix, params) = &**inst;
1584 O::Datatype(
1585 DatatypeKey::read(*ix, bytecode),
1586 params
1587 .iter()
1588 .map(|sig| OpenSignatureBody::read(sig, bytecode))
1589 .collect::<Result<_>>()?,
1590 )
1591 }
1592 })
1593 }
1594
1595 fn instantiate(&self, type_params: &[TypeInput]) -> Result<TypeTag> {
1596 use OpenSignatureBody as O;
1597 use TypeTag as T;
1598
1599 Ok(match self {
1600 O::Address => T::Address,
1601 O::Bool => T::Bool,
1602 O::U8 => T::U8,
1603 O::U16 => T::U16,
1604 O::U32 => T::U32,
1605 O::U64 => T::U64,
1606 O::U128 => T::U128,
1607 O::U256 => T::U256,
1608 O::Vector(s) => T::Vector(Box::new(s.instantiate(type_params)?)),
1609
1610 O::Datatype(key, dty_params) => T::Struct(Box::new(StructTag {
1611 address: key.package,
1612 module: ident(&key.module)?,
1613 name: ident(&key.name)?,
1614 type_params: dty_params
1615 .iter()
1616 .map(|p| p.instantiate(type_params))
1617 .collect::<Result<_>>()?,
1618 })),
1619
1620 O::TypeParameter(ix) => as_type_tag(
1621 type_params
1622 .get(*ix as usize)
1623 .ok_or_else(|| Error::TypeParamOOB(*ix, type_params.len()))?,
1624 )?,
1625 })
1626 }
1627}
1628
1629impl DatatypeRef<'_, '_> {
1630 pub fn as_key(&self) -> DatatypeKey {
1631 DatatypeKey {
1632 package: self.package,
1633 module: self.module.to_string().into(),
1634 name: self.name.to_string().into(),
1635 }
1636 }
1637}
1638
1639impl DatatypeKey {
1640 fn read(ix: DatatypeHandleIndex, bytecode: &CompiledModule) -> Self {
1641 let sh = bytecode.datatype_handle_at(ix);
1642 let mh = bytecode.module_handle_at(sh.module);
1643
1644 let package = *bytecode.address_identifier_at(mh.address);
1645 let module = bytecode.identifier_at(mh.name).to_string().into();
1646 let name = bytecode.identifier_at(sh.name).to_string().into();
1647
1648 DatatypeKey {
1649 package,
1650 module,
1651 name,
1652 }
1653 }
1654}
1655impl<'l> ResolutionContext<'l> {
1656 fn new(limits: Option<&'l Limits>) -> Self {
1657 ResolutionContext {
1658 datatypes: BTreeMap::new(),
1659 limits,
1660 }
1661 }
1662
1663 async fn add_type_tag<S: PackageStore + ?Sized>(
1676 &mut self,
1677 tag: &mut TypeTag,
1678 store: &S,
1679 visit_fields: bool,
1680 visit_phantoms: bool,
1681 ) -> Result<()> {
1682 use TypeTag as T;
1683
1684 struct ToVisit<'t> {
1685 tag: &'t mut TypeTag,
1686 depth: usize,
1687 }
1688
1689 let mut frontier = vec![ToVisit { tag, depth: 0 }];
1690 while let Some(ToVisit { tag, depth }) = frontier.pop() {
1691 macro_rules! push_ty_param {
1692 ($tag:expr) => {{
1693 check_max_limit!(
1694 TypeParamNesting, self.limits;
1695 max_type_argument_depth > depth
1696 );
1697
1698 frontier.push(ToVisit { tag: $tag, depth: depth + 1 })
1699 }}
1700 }
1701
1702 match tag {
1703 T::Address
1704 | T::Bool
1705 | T::U8
1706 | T::U16
1707 | T::U32
1708 | T::U64
1709 | T::U128
1710 | T::U256
1711 | T::Signer => {
1712 }
1714
1715 T::Vector(tag) => push_ty_param!(tag),
1716
1717 T::Struct(s) => {
1718 let context = store.fetch(s.address).await?;
1719 let def = context
1720 .clone()
1721 .data_def(s.module.as_str(), s.name.as_str())?;
1722
1723 s.address = context.runtime_id;
1728 let key = DatatypeRef::from(s.as_ref()).as_key();
1729
1730 if def.type_params.len() != s.type_params.len() {
1731 return Err(Error::TypeArityMismatch(
1732 def.type_params.len(),
1733 s.type_params.len(),
1734 ));
1735 }
1736
1737 check_max_limit!(
1738 TooManyTypeParams, self.limits;
1739 max_type_argument_width >= s.type_params.len()
1740 );
1741
1742 for (param, def) in s.type_params.iter_mut().zip_eq(def.type_params.iter()) {
1743 if !def.is_phantom || visit_phantoms {
1744 push_ty_param!(param);
1745 }
1746 }
1747
1748 if self.datatypes.contains_key(&key) {
1749 continue;
1750 }
1751
1752 if visit_fields {
1753 match &def.data {
1754 MoveData::Struct(fields) => {
1755 for (_, sig) in fields {
1756 self.add_signature(sig.clone(), store, &context, visit_fields)
1757 .await?;
1758 }
1759 }
1760 MoveData::Enum(variants) => {
1761 for variant in variants {
1762 for (_, sig) in &variant.signatures {
1763 self.add_signature(
1764 sig.clone(),
1765 store,
1766 &context,
1767 visit_fields,
1768 )
1769 .await?;
1770 }
1771 }
1772 }
1773 };
1774 }
1775
1776 check_max_limit!(
1777 TooManyTypeNodes, self.limits;
1778 max_type_nodes > self.datatypes.len()
1779 );
1780
1781 self.datatypes.insert(key, def);
1782 }
1783 }
1784 }
1785
1786 Ok(())
1787 }
1788
1789 fn add_type_tag_sync<S: SyncPackageStore + ?Sized>(
1792 &mut self,
1793 tag: &mut TypeTag,
1794 store: &S,
1795 visit_fields: bool,
1796 visit_phantoms: bool,
1797 ) -> Result<()> {
1798 use TypeTag as T;
1799
1800 struct ToVisit<'t> {
1801 tag: &'t mut TypeTag,
1802 depth: usize,
1803 }
1804
1805 let mut frontier = vec![ToVisit { tag, depth: 0 }];
1806 while let Some(ToVisit { tag, depth }) = frontier.pop() {
1807 macro_rules! push_ty_param {
1808 ($tag:expr) => {{
1809 check_max_limit!(
1810 TypeParamNesting, self.limits;
1811 max_type_argument_depth > depth
1812 );
1813
1814 frontier.push(ToVisit { tag: $tag, depth: depth + 1 })
1815 }}
1816 }
1817
1818 match tag {
1819 T::Address
1820 | T::Bool
1821 | T::U8
1822 | T::U16
1823 | T::U32
1824 | T::U64
1825 | T::U128
1826 | T::U256
1827 | T::Signer => {
1828 }
1830
1831 T::Vector(tag) => push_ty_param!(tag),
1832
1833 T::Struct(s) => {
1834 let context = store.fetch(s.address)?;
1835 let def = context
1836 .clone()
1837 .data_def(s.module.as_str(), s.name.as_str())?;
1838
1839 s.address = context.runtime_id;
1844 let key = DatatypeRef::from(s.as_ref()).as_key();
1845
1846 if def.type_params.len() != s.type_params.len() {
1847 return Err(Error::TypeArityMismatch(
1848 def.type_params.len(),
1849 s.type_params.len(),
1850 ));
1851 }
1852
1853 check_max_limit!(
1854 TooManyTypeParams, self.limits;
1855 max_type_argument_width >= s.type_params.len()
1856 );
1857
1858 for (param, def) in s.type_params.iter_mut().zip_eq(def.type_params.iter()) {
1859 if !def.is_phantom || visit_phantoms {
1860 push_ty_param!(param);
1861 }
1862 }
1863
1864 if self.datatypes.contains_key(&key) {
1865 continue;
1866 }
1867
1868 if visit_fields {
1869 match &def.data {
1870 MoveData::Struct(fields) => {
1871 for (_, sig) in fields {
1872 self.add_signature_sync(
1873 sig.clone(),
1874 store,
1875 &context,
1876 visit_fields,
1877 )?;
1878 }
1879 }
1880 MoveData::Enum(variants) => {
1881 for variant in variants {
1882 for (_, sig) in &variant.signatures {
1883 self.add_signature_sync(
1884 sig.clone(),
1885 store,
1886 &context,
1887 visit_fields,
1888 )?;
1889 }
1890 }
1891 }
1892 };
1893 }
1894
1895 check_max_limit!(
1896 TooManyTypeNodes, self.limits;
1897 max_type_nodes > self.datatypes.len()
1898 );
1899
1900 self.datatypes.insert(key, def);
1901 }
1902 }
1903 }
1904
1905 Ok(())
1906 }
1907
1908 async fn add_signature<T: PackageStore + ?Sized>(
1911 &mut self,
1912 sig: OpenSignatureBody,
1913 store: &T,
1914 context: &Package,
1915 visit_fields: bool,
1916 ) -> Result<()> {
1917 use OpenSignatureBody as O;
1918
1919 let mut frontier = vec![sig];
1920 while let Some(sig) = frontier.pop() {
1921 match sig {
1922 O::Address
1923 | O::Bool
1924 | O::U8
1925 | O::U16
1926 | O::U32
1927 | O::U64
1928 | O::U128
1929 | O::U256
1930 | O::TypeParameter(_) => {
1931 }
1933
1934 O::Vector(sig) => frontier.push(*sig),
1935
1936 O::Datatype(key, params) => {
1937 check_max_limit!(
1938 TooManyTypeParams, self.limits;
1939 max_type_argument_width >= params.len()
1940 );
1941
1942 let params_count = params.len();
1943 let data_count = self.datatypes.len();
1944 frontier.extend(params);
1945
1946 let type_params = if let Some(def) = self.datatypes.get(&key) {
1947 &def.type_params
1948 } else {
1949 check_max_limit!(
1950 TooManyTypeNodes, self.limits;
1951 max_type_nodes > data_count
1952 );
1953
1954 let storage_id = context.relocate(key.package)?;
1956 let package = store.fetch(storage_id).await?;
1957
1958 let def = package.data_def(&key.module, &key.name)?;
1959 if visit_fields {
1960 match &def.data {
1961 MoveData::Struct(fields) => {
1962 frontier.extend(fields.iter().map(|f| &f.1).cloned());
1963 }
1964 MoveData::Enum(variants) => {
1965 frontier.extend(
1966 variants
1967 .iter()
1968 .flat_map(|v| v.signatures.iter().map(|(_, s)| s))
1969 .cloned(),
1970 );
1971 }
1972 };
1973 }
1974
1975 &self.datatypes.entry(key).or_insert(def).type_params
1976 };
1977
1978 if type_params.len() != params_count {
1979 return Err(Error::TypeArityMismatch(type_params.len(), params_count));
1980 }
1981 }
1982 }
1983 }
1984
1985 Ok(())
1986 }
1987
1988 fn add_signature_sync<T: SyncPackageStore + ?Sized>(
1991 &mut self,
1992 sig: OpenSignatureBody,
1993 store: &T,
1994 context: &Package,
1995 visit_fields: bool,
1996 ) -> Result<()> {
1997 use OpenSignatureBody as O;
1998
1999 let mut frontier = vec![sig];
2000 while let Some(sig) = frontier.pop() {
2001 match sig {
2002 O::Address
2003 | O::Bool
2004 | O::U8
2005 | O::U16
2006 | O::U32
2007 | O::U64
2008 | O::U128
2009 | O::U256
2010 | O::TypeParameter(_) => {
2011 }
2013
2014 O::Vector(sig) => frontier.push(*sig),
2015
2016 O::Datatype(key, params) => {
2017 check_max_limit!(
2018 TooManyTypeParams, self.limits;
2019 max_type_argument_width >= params.len()
2020 );
2021
2022 let params_count = params.len();
2023 let data_count = self.datatypes.len();
2024 frontier.extend(params);
2025
2026 let type_params = if let Some(def) = self.datatypes.get(&key) {
2027 &def.type_params
2028 } else {
2029 check_max_limit!(
2030 TooManyTypeNodes, self.limits;
2031 max_type_nodes > data_count
2032 );
2033
2034 let storage_id = context.relocate(key.package)?;
2036 let package = store.fetch(storage_id)?;
2037
2038 let def = package.data_def(&key.module, &key.name)?;
2039 if visit_fields {
2040 match &def.data {
2041 MoveData::Struct(fields) => {
2042 frontier.extend(fields.iter().map(|f| &f.1).cloned());
2043 }
2044 MoveData::Enum(variants) => {
2045 frontier.extend(
2046 variants
2047 .iter()
2048 .flat_map(|v| v.signatures.iter().map(|(_, s)| s))
2049 .cloned(),
2050 );
2051 }
2052 };
2053 }
2054
2055 &self.datatypes.entry(key).or_insert(def).type_params
2056 };
2057
2058 if type_params.len() != params_count {
2059 return Err(Error::TypeArityMismatch(type_params.len(), params_count));
2060 }
2061 }
2062 }
2063 }
2064
2065 Ok(())
2066 }
2067
2068 fn canonicalize_type(&self, tag: &mut TypeTag) -> Result<()> {
2072 use TypeTag as T;
2073
2074 match tag {
2075 T::Signer => return Err(Error::UnexpectedSigner),
2076 T::Address | T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 => {
2077 }
2079
2080 T::Vector(tag) => self.canonicalize_type(tag.as_mut())?,
2081
2082 T::Struct(s) => {
2083 for tag in &mut s.type_params {
2084 self.canonicalize_type(tag)?;
2085 }
2086
2087 let key = DatatypeRef::from(s.as_ref());
2089 let def = &self.datatypes[&key];
2090
2091 s.address = def.defining_id;
2092 }
2093 }
2094
2095 Ok(())
2096 }
2097
2098 fn resolve_type_layout(
2105 &self,
2106 tag: &TypeTag,
2107 meter: &mut LayoutMeter,
2108 ) -> Result<ResolvedLayout> {
2109 use MoveTypeLayout as L;
2110 use TypeTag as T;
2111
2112 meter.check_depth()?;
2113 meter.charge_node()?;
2114
2115 Ok(match tag {
2116 T::Signer => return Err(Error::UnexpectedSigner),
2117
2118 T::Address => ResolvedLayout::leaf(L::Address),
2119 T::Bool => ResolvedLayout::leaf(L::Bool),
2120 T::U8 => ResolvedLayout::leaf(L::U8),
2121 T::U16 => ResolvedLayout::leaf(L::U16),
2122 T::U32 => ResolvedLayout::leaf(L::U32),
2123 T::U64 => ResolvedLayout::leaf(L::U64),
2124 T::U128 => ResolvedLayout::leaf(L::U128),
2125 T::U256 => ResolvedLayout::leaf(L::U256),
2126
2127 T::Vector(tag) => {
2128 let mut child = meter.nest()?;
2129 let element = self.resolve_type_layout(tag, &mut child)?;
2130 ResolvedLayout {
2131 layout: L::Vector(Box::new(element.layout)),
2132 depth: element.depth.saturating_add(1),
2133 nodes: element.nodes.saturating_add(1),
2134 }
2135 }
2136
2137 T::Struct(s) => {
2138 let key = DatatypeRef::from(s.as_ref());
2149 let def = &self.datatypes[&key];
2150
2151 let mut type_params = s.type_params.clone();
2152 for type_param in &mut type_params {
2153 self.canonicalize_type(type_param)?;
2154 }
2155
2156 let param_layouts = s
2157 .type_params
2158 .iter()
2159 .zip_eq(def.type_params.iter())
2160 .map(|(tag, param)| {
2161 if param.is_phantom {
2162 return Ok(None);
2163 }
2164
2165 let mut child = meter.nest()?;
2166 Ok(Some(self.resolve_type_layout(tag, &mut child)?))
2167 })
2168 .collect::<Result<Vec<_>>>()?;
2169
2170 let type_ = StructTag {
2171 address: def.defining_id,
2172 module: s.module.clone(),
2173 name: s.name.clone(),
2174 type_params,
2175 };
2176
2177 self.resolve_datatype_signature(def, type_, param_layouts, meter)?
2178 }
2179 })
2180 }
2181
2182 fn resolve_signature_type_tag(
2188 &self,
2189 sig: &OpenSignatureBody,
2190 param_type_tags: &[TypeTag],
2191 ) -> Result<TypeTag> {
2192 use OpenSignatureBody as O;
2193
2194 Ok(match sig {
2195 O::Address => TypeTag::Address,
2196 O::Bool => TypeTag::Bool,
2197 O::U8 => TypeTag::U8,
2198 O::U16 => TypeTag::U16,
2199 O::U32 => TypeTag::U32,
2200 O::U64 => TypeTag::U64,
2201 O::U128 => TypeTag::U128,
2202 O::U256 => TypeTag::U256,
2203 O::TypeParameter(ix) => param_type_tags
2204 .get(*ix as usize)
2205 .ok_or_else(|| Error::TypeParamOOB(*ix, param_type_tags.len()))?
2206 .clone(),
2207 O::Vector(sig) => TypeTag::Vector(Box::new(
2208 self.resolve_signature_type_tag(sig, param_type_tags)?,
2209 )),
2210 O::Datatype(key, params) => {
2211 let def = &self.datatypes[key];
2213 let type_params = params
2214 .iter()
2215 .map(|param| self.resolve_signature_type_tag(param, param_type_tags))
2216 .collect::<Result<Vec<_>>>()?;
2217
2218 TypeTag::Struct(Box::new(StructTag {
2219 address: def.defining_id,
2220 module: ident(&key.module)?,
2221 name: ident(&key.name)?,
2222 type_params,
2223 }))
2224 }
2225 })
2226 }
2227
2228 fn resolve_datatype_signature(
2231 &self,
2232 data_def: &DataDef,
2233 type_: StructTag,
2234 param_layouts: Vec<Option<ResolvedLayout>>,
2235 meter: &mut LayoutMeter,
2236 ) -> Result<ResolvedLayout> {
2237 Ok(match &data_def.data {
2238 MoveData::Struct(fields) => {
2239 let mut resolved_fields = Vec::with_capacity(fields.len());
2240 let mut field_depth = 0;
2241 let mut field_nodes = 0usize;
2242
2243 for (name, sig) in fields {
2244 let mut child = meter.nest()?;
2245 let resolved = self.resolve_signature_layout(
2246 sig,
2247 ¶m_layouts,
2248 &type_.type_params,
2249 &mut child,
2250 )?;
2251
2252 field_depth = field_depth.max(resolved.depth);
2253 field_nodes = field_nodes.saturating_add(resolved.nodes);
2254 resolved_fields.push(MoveFieldLayout {
2255 name: ident(name.as_str())?,
2256 layout: resolved.layout,
2257 })
2258 }
2259
2260 ResolvedLayout {
2261 layout: MoveTypeLayout::Struct(Box::new(MoveStructLayout {
2262 type_,
2263 fields: resolved_fields,
2264 })),
2265 depth: field_depth.saturating_add(1),
2266 nodes: field_nodes.saturating_add(1),
2267 }
2268 }
2269 MoveData::Enum(variants) => {
2270 meter.charge_nodes(variants.len())?;
2271
2272 let mut field_depth = 0;
2273 let mut field_nodes = 0usize;
2274 let mut resolved_variants = BTreeMap::new();
2275
2276 for (tag, variant) in variants.iter().enumerate() {
2277 let mut fields = Vec::with_capacity(variant.signatures.len());
2278 for (name, sig) in &variant.signatures {
2279 let mut child = meter.nest()?;
2280 let resolved = self.resolve_signature_layout(
2281 sig,
2282 ¶m_layouts,
2283 &type_.type_params,
2284 &mut child,
2285 )?;
2286
2287 field_depth = field_depth.max(resolved.depth);
2288 field_nodes = field_nodes.saturating_add(resolved.nodes);
2289 fields.push(MoveFieldLayout {
2290 name: ident(name.as_str())?,
2291 layout: resolved.layout,
2292 })
2293 }
2294 resolved_variants.insert((ident(variant.name.as_str())?, tag as u16), fields);
2295 }
2296
2297 ResolvedLayout {
2298 layout: MoveTypeLayout::Enum(Box::new(MoveEnumLayout {
2299 type_,
2300 variants: resolved_variants,
2301 })),
2302 depth: field_depth.saturating_add(1),
2303 nodes: field_nodes.saturating_add(variants.len()).saturating_add(1),
2304 }
2305 }
2306 })
2307 }
2308
2309 fn resolve_signature_layout(
2312 &self,
2313 sig: &OpenSignatureBody,
2314 param_layouts: &[Option<ResolvedLayout>],
2315 param_type_tags: &[TypeTag],
2316 meter: &mut LayoutMeter,
2317 ) -> Result<ResolvedLayout> {
2318 use MoveTypeLayout as L;
2319 use OpenSignatureBody as O;
2320
2321 meter.check_depth()?;
2322
2323 if !matches!(sig, O::TypeParameter(_)) {
2324 meter.charge_node()?;
2325 }
2326
2327 Ok(match sig {
2328 O::Address => ResolvedLayout::leaf(L::Address),
2329 O::Bool => ResolvedLayout::leaf(L::Bool),
2330 O::U8 => ResolvedLayout::leaf(L::U8),
2331 O::U16 => ResolvedLayout::leaf(L::U16),
2332 O::U32 => ResolvedLayout::leaf(L::U32),
2333 O::U64 => ResolvedLayout::leaf(L::U64),
2334 O::U128 => ResolvedLayout::leaf(L::U128),
2335 O::U256 => ResolvedLayout::leaf(L::U256),
2336
2337 O::TypeParameter(ix) => {
2338 let resolved = param_layouts
2339 .get(*ix as usize)
2340 .and_then(Option::as_ref)
2341 .ok_or_else(|| Error::TypeParamOOB(*ix, param_layouts.len()))?;
2342 meter.charge_nodes(resolved.nodes)?;
2343
2344 meter.ensure_depth(resolved.depth)?;
2348
2349 ResolvedLayout {
2350 layout: resolved.layout.clone(),
2351 depth: resolved.depth,
2352 nodes: resolved.nodes,
2353 }
2354 }
2355
2356 O::Vector(sig) => {
2357 let mut child = meter.nest()?;
2358 let element = self.resolve_signature_layout(
2359 sig.as_ref(),
2360 param_layouts,
2361 param_type_tags,
2362 &mut child,
2363 )?;
2364
2365 ResolvedLayout {
2366 layout: L::Vector(Box::new(element.layout)),
2367 depth: element.depth.saturating_add(1),
2368 nodes: element.nodes.saturating_add(1),
2369 }
2370 }
2371
2372 O::Datatype(key, params) => {
2373 let def = &self.datatypes[key];
2375 if def.type_params.len() != params.len() {
2376 return Err(Error::TypeArityMismatch(
2377 def.type_params.len(),
2378 params.len(),
2379 ));
2380 }
2381
2382 let mut type_params = Vec::with_capacity(params.len());
2383 let mut resolved_params = Vec::with_capacity(params.len());
2384 for (sig, param) in params.iter().zip_eq(def.type_params.iter()) {
2385 if param.is_phantom {
2386 type_params.push(self.resolve_signature_type_tag(sig, param_type_tags)?);
2387 resolved_params.push(None);
2388 } else {
2389 let mut child = meter.nest()?;
2390 let resolved = self.resolve_signature_layout(
2391 sig,
2392 param_layouts,
2393 param_type_tags,
2394 &mut child,
2395 )?;
2396 type_params.push(TypeTag::from(&resolved.layout));
2398 resolved_params.push(Some(resolved));
2399 }
2400 }
2401
2402 let type_ = StructTag {
2403 address: def.defining_id,
2404 module: ident(&key.module)?,
2405 name: ident(&key.name)?,
2406 type_params,
2407 };
2408
2409 self.resolve_datatype_signature(def, type_, resolved_params, meter)?
2410 }
2411 })
2412 }
2413
2414 fn resolve_abilities(&self, tag: &TypeTag) -> Result<AbilitySet> {
2417 use TypeTag as T;
2418 Ok(match tag {
2419 T::Signer => return Err(Error::UnexpectedSigner),
2420
2421 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => {
2422 AbilitySet::PRIMITIVES
2423 }
2424
2425 T::Vector(tag) => self.resolve_abilities(tag)?.intersect(AbilitySet::VECTOR),
2426
2427 T::Struct(s) => {
2428 let key = DatatypeRef::from(s.as_ref());
2430 let def = &self.datatypes[&key];
2431
2432 if def.type_params.len() != s.type_params.len() {
2433 return Err(Error::TypeArityMismatch(
2434 def.type_params.len(),
2435 s.type_params.len(),
2436 ));
2437 }
2438
2439 let param_abilities: Result<Vec<AbilitySet>> = s
2440 .type_params
2441 .iter()
2442 .zip_eq(def.type_params.iter())
2443 .map(|(p, d)| {
2444 if d.is_phantom {
2445 Ok(AbilitySet::EMPTY)
2446 } else {
2447 self.resolve_abilities(p)
2448 }
2449 })
2450 .collect();
2451
2452 AbilitySet::polymorphic_abilities(
2453 def.abilities,
2454 def.type_params.iter().map(|p| p.is_phantom),
2455 param_abilities?,
2456 )
2457 .map_err(|e| Error::UnexpectedError(Arc::new(e)))?
2460 }
2461 })
2462 }
2463
2464 fn relocate_signature(&self, sig: &mut OpenSignatureBody) -> Result<()> {
2468 use OpenSignatureBody as O;
2469
2470 match sig {
2471 O::Address | O::Bool | O::U8 | O::U16 | O::U32 | O::U64 | O::U128 | O::U256 => {
2472 }
2474
2475 O::TypeParameter(_) => { }
2476
2477 O::Vector(sig) => self.relocate_signature(sig.as_mut())?,
2478
2479 O::Datatype(key, params) => {
2480 let defining_id = &self.datatypes[key].defining_id;
2482 for param in params {
2483 self.relocate_signature(param)?;
2484 }
2485
2486 key.package = *defining_id;
2487 }
2488 }
2489
2490 Ok(())
2491 }
2492}
2493
2494impl<'s> From<&'s StructTag> for DatatypeRef<'s, 's> {
2495 fn from(tag: &'s StructTag) -> Self {
2496 DatatypeRef {
2497 package: tag.address,
2498 module: tag.module.as_str().into(),
2499 name: tag.name.as_str().into(),
2500 }
2501 }
2502}
2503
2504fn ident(s: &str) -> Result<Identifier> {
2506 Identifier::new(s).map_err(|_| Error::NotAnIdentifier(s.to_string()))
2507}
2508
2509pub fn as_type_tag(type_input: &TypeInput) -> Result<TypeTag> {
2510 use TypeInput as I;
2511 use TypeTag as T;
2512 Ok(match type_input {
2513 I::Bool => T::Bool,
2514 I::U8 => T::U8,
2515 I::U16 => T::U16,
2516 I::U32 => T::U32,
2517 I::U64 => T::U64,
2518 I::U128 => T::U128,
2519 I::U256 => T::U256,
2520 I::Address => T::Address,
2521 I::Signer => T::Signer,
2522 I::Vector(t) => T::Vector(Box::new(as_type_tag(t)?)),
2523 I::Struct(s) => {
2524 let StructInput {
2525 address,
2526 module,
2527 name,
2528 type_params,
2529 } = s.as_ref();
2530 let type_params = type_params.iter().map(as_type_tag).collect::<Result<_>>()?;
2531 T::Struct(Box::new(StructTag {
2532 address: *address,
2533 module: ident(module)?,
2534 name: ident(name)?,
2535 type_params,
2536 }))
2537 }
2538 })
2539}
2540
2541fn read_signature(idx: SignatureIndex, bytecode: &CompiledModule) -> Result<Vec<OpenSignature>> {
2544 let MoveSignature(tokens) = bytecode.signature_at(idx);
2545 let mut sigs = Vec::with_capacity(tokens.len());
2546
2547 for token in tokens {
2548 sigs.push(OpenSignature::read(token, bytecode)?);
2549 }
2550
2551 Ok(sigs)
2552}
2553
2554#[cfg(test)]
2555mod tests {
2556 use async_trait::async_trait;
2557 use move_binary_format::file_format::Ability;
2558 use move_core_types::ident_str;
2559 use std::path::PathBuf;
2560 use std::str::FromStr;
2561 use std::sync::Arc;
2562 use std::sync::RwLock;
2563 use sui_types::base_types::random_object_ref;
2564 use sui_types::transaction::ObjectArg;
2565
2566 use move_compiler::compiled_unit::NamedCompiledModule;
2567 use sui_move_build::BuildConfig;
2568 use sui_move_build::CompiledPackage;
2569
2570 use super::*;
2571
2572 fn fmt(struct_layout: MoveTypeLayout, enum_layout: MoveTypeLayout) -> String {
2573 format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",)
2574 }
2575
2576 #[tokio::test]
2577 async fn test_simple_canonical_type() {
2578 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2579 let package_resolver = Resolver::new(cache);
2580
2581 let input = type_("0xa0::m::T0");
2582 let expect = input.clone();
2583 let actual = package_resolver.canonical_type(input).await.unwrap();
2584 assert_eq!(expect, actual);
2585 }
2586
2587 #[tokio::test]
2588 async fn test_upgraded_canonical_type() {
2589 let (_, cache) = package_cache([
2590 (1, build_package("a0"), a0_types()),
2591 (2, build_package("a1"), a1_types()),
2592 ]);
2593
2594 let package_resolver = Resolver::new(cache);
2595
2596 let input = type_("0xa1::m::T3");
2597 let expect = input.clone();
2598 let actual = package_resolver.canonical_type(input).await.unwrap();
2599 assert_eq!(expect, actual);
2600 }
2601
2602 #[tokio::test]
2603 async fn test_latest_canonical_type() {
2604 let (_, cache) = package_cache([
2605 (1, build_package("a0"), a0_types()),
2606 (2, build_package("a1"), a1_types()),
2607 ]);
2608
2609 let package_resolver = Resolver::new(cache);
2610
2611 let input = type_("0xa1::m::T0");
2612 let expect = type_("0xa0::m::T0");
2613 let actual = package_resolver.canonical_type(input).await.unwrap();
2614 assert_eq!(expect, actual);
2615 }
2616
2617 #[tokio::test]
2618 async fn test_type_param_canonical_type() {
2619 let (_, cache) = package_cache([
2620 (1, build_package("a0"), a0_types()),
2621 (2, build_package("a1"), a1_types()),
2622 ]);
2623
2624 let package_resolver = Resolver::new(cache);
2625
2626 let input = type_("0xa1::m::T1<0xa1::m::T0, 0xa1::m::T3>");
2627 let expect = type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>");
2628 let actual = package_resolver.canonical_type(input).await.unwrap();
2629 assert_eq!(expect, actual);
2630 }
2631
2632 #[tokio::test]
2633 async fn test_canonical_err_package_too_old() {
2634 let (_, cache) = package_cache([
2635 (1, build_package("a0"), a0_types()),
2636 (2, build_package("a1"), a1_types()),
2637 ]);
2638
2639 let package_resolver = Resolver::new(cache);
2640
2641 let input = type_("0xa0::m::T3");
2642 let err = package_resolver.canonical_type(input).await.unwrap_err();
2643 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
2644 }
2645
2646 #[tokio::test]
2647 async fn test_canonical_err_signer() {
2648 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2649
2650 let package_resolver = Resolver::new(cache);
2651
2652 let input = type_("0xa0::m::T1<0xa0::m::T0, signer>");
2653 let err = package_resolver.canonical_type(input).await.unwrap_err();
2654 assert!(matches!(err, Error::UnexpectedSigner));
2655 }
2656
2657 #[tokio::test]
2659 async fn test_simple_type_layout() {
2660 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2661 let package_resolver = Resolver::new(cache);
2662 let struct_layout = package_resolver
2663 .type_layout(type_("0xa0::m::T0"))
2664 .await
2665 .unwrap();
2666 let enum_layout = package_resolver
2667 .type_layout(type_("0xa0::m::E0"))
2668 .await
2669 .unwrap();
2670 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2671 }
2672
2673 #[tokio::test]
2675 async fn test_cross_module_layout() {
2676 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2677 let resolver = Resolver::new(cache);
2678 let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap();
2679 let enum_layout = resolver.type_layout(type_("0xa0::n::E0")).await.unwrap();
2680 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2681 }
2682
2683 #[tokio::test]
2685 async fn test_cross_package_layout() {
2686 let (_, cache) = package_cache([
2687 (1, build_package("a0"), a0_types()),
2688 (1, build_package("b0"), b0_types()),
2689 ]);
2690 let resolver = Resolver::new(cache);
2691
2692 let struct_layout = resolver.type_layout(type_("0xb0::m::T0")).await.unwrap();
2693 let enum_layout = resolver.type_layout(type_("0xb0::m::E0")).await.unwrap();
2694 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2695 }
2696
2697 #[tokio::test]
2700 async fn test_upgraded_package_layout() {
2701 let (_, cache) = package_cache([
2702 (1, build_package("a0"), a0_types()),
2703 (2, build_package("a1"), a1_types()),
2704 ]);
2705 let resolver = Resolver::new(cache);
2706
2707 let struct_layout = resolver.type_layout(type_("0xa1::n::T1")).await.unwrap();
2708 let enum_layout = resolver.type_layout(type_("0xa1::n::E1")).await.unwrap();
2709 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2710 }
2711
2712 #[tokio::test]
2715 async fn test_multiple_linkage_contexts_layout() {
2716 let (_, cache) = package_cache([
2717 (1, build_package("a0"), a0_types()),
2718 (2, build_package("a1"), a1_types()),
2719 ]);
2720 let resolver = Resolver::new(cache);
2721
2722 let struct_layout = resolver
2723 .type_layout(type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"))
2724 .await
2725 .unwrap();
2726 let enum_layout = resolver
2727 .type_layout(type_("0xa0::m::E1<0xa0::m::E0, 0xa1::m::E3>"))
2728 .await
2729 .unwrap();
2730 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2731 }
2732
2733 #[tokio::test]
2738 async fn test_upgraded_package_non_defining_id_layout() {
2739 let (_, cache) = package_cache([
2740 (1, build_package("a0"), a0_types()),
2741 (2, build_package("a1"), a1_types()),
2742 ]);
2743 let resolver = Resolver::new(cache);
2744
2745 let struct_layout = resolver
2746 .type_layout(type_("0xa1::m::T1<0xa1::m::T3, 0xa1::m::T0>"))
2747 .await
2748 .unwrap();
2749 let enum_layout = resolver
2750 .type_layout(type_("0xa1::m::E1<0xa1::m::E3, 0xa1::m::E0>"))
2751 .await
2752 .unwrap();
2753 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2754 }
2755
2756 #[tokio::test]
2760 async fn test_relinking_layout() {
2761 let (_, cache) = package_cache([
2762 (1, build_package("a0"), a0_types()),
2763 (2, build_package("a1"), a1_types()),
2764 (1, build_package("b0"), b0_types()),
2765 (1, build_package("c0"), c0_types()),
2766 ]);
2767 let resolver = Resolver::new(cache);
2768
2769 let struct_layout = resolver.type_layout(type_("0xc0::m::T0")).await.unwrap();
2770 let enum_layout = resolver.type_layout(type_("0xc0::m::E0")).await.unwrap();
2771 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2772 }
2773
2774 #[tokio::test]
2775 async fn test_value_nesting_boundary_layout() {
2776 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2777
2778 let resolver = Resolver::new_with_limits(
2779 cache,
2780 Limits {
2781 max_type_argument_width: 100,
2782 max_type_argument_depth: 100,
2783 max_type_nodes: 100,
2784 max_move_value_depth: 3,
2785 max_layout_work: usize::MAX,
2786 },
2787 );
2788
2789 let struct_layout = resolver
2791 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2792 .await
2793 .unwrap();
2794 let enum_layout = resolver
2795 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2796 .await
2797 .unwrap();
2798 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2799 }
2800
2801 #[tokio::test]
2802 async fn test_err_value_nesting_simple_layout() {
2803 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2804
2805 let resolver = Resolver::new_with_limits(
2806 cache,
2807 Limits {
2808 max_type_argument_width: 100,
2809 max_type_argument_depth: 100,
2810 max_type_nodes: 100,
2811 max_move_value_depth: 2,
2812 max_layout_work: usize::MAX,
2813 },
2814 );
2815
2816 let struct_err = resolver
2818 .type_layout(type_("0xa0::m::T1<u8, u8>"))
2819 .await
2820 .unwrap_err();
2821 let enum_err = resolver
2822 .type_layout(type_("0xa0::m::E1<u8, u8>"))
2823 .await
2824 .unwrap_err();
2825 assert!(matches!(struct_err, Error::ValueNesting(2)));
2826 assert!(matches!(enum_err, Error::ValueNesting(2)));
2827 }
2828
2829 #[tokio::test]
2830 async fn test_err_value_nesting_big_type_param_layout() {
2831 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
2832
2833 let resolver = Resolver::new_with_limits(
2834 cache,
2835 Limits {
2836 max_type_argument_width: 100,
2837 max_type_argument_depth: 100,
2838 max_type_nodes: 100,
2839 max_move_value_depth: 3,
2840 max_layout_work: usize::MAX,
2841 },
2842 );
2843
2844 let struct_err = resolver
2847 .type_layout(type_("0xa0::m::T1<vector<vector<u8>>, u8>"))
2848 .await
2849 .unwrap_err();
2850 let enum_err = resolver
2851 .type_layout(type_("0xa0::m::E1<vector<vector<u8>>, u8>"))
2852 .await
2853 .unwrap_err();
2854 assert!(matches!(struct_err, Error::ValueNesting(3)));
2855 assert!(matches!(enum_err, Error::ValueNesting(3)));
2856 }
2857
2858 #[tokio::test]
2859 async fn test_big_phantom_type_param_layout() {
2860 let (_, cache) = package_cache([
2861 (1, build_package("sui"), sui_types()),
2862 (1, build_package("d0"), d0_types()),
2863 ]);
2864
2865 let resolver = Resolver::new_with_limits(
2866 cache,
2867 Limits {
2868 max_type_argument_width: 100,
2869 max_type_argument_depth: 100,
2870 max_type_nodes: 100,
2871 max_move_value_depth: 3,
2872 max_layout_work: usize::MAX,
2873 },
2874 );
2875
2876 let _ = resolver
2878 .type_layout(type_("0xd0::m::O<u8, u8>"))
2879 .await
2880 .unwrap();
2881 let _ = resolver
2882 .type_layout(type_("0xd0::m::EO<u8, u8>"))
2883 .await
2884 .unwrap();
2885
2886 let struct_layout = resolver
2889 .type_layout(type_("0xd0::m::O<u8, vector<vector<u8>>>"))
2890 .await
2891 .unwrap();
2892 let enum_layout = resolver
2893 .type_layout(type_("0xd0::m::EO<u8, vector<vector<u8>>>"))
2894 .await
2895 .unwrap();
2896 assert_eq!(
2897 TypeTag::from(&struct_layout),
2898 type_("0xd0::m::O<u8, vector<vector<u8>>>")
2899 );
2900 assert_eq!(
2901 TypeTag::from(&enum_layout),
2902 type_("0xd0::m::EO<u8, vector<vector<u8>>>")
2903 );
2904 }
2905
2906 #[tokio::test]
2907 async fn test_err_value_nesting_type_param_application_layout() {
2908 let (_, cache) = package_cache([
2909 (1, build_package("sui"), sui_types()),
2910 (1, build_package("d0"), d0_types()),
2911 ]);
2912
2913 let resolver = Resolver::new_with_limits(
2914 cache,
2915 Limits {
2916 max_type_argument_width: 100,
2917 max_type_argument_depth: 100,
2918 max_type_nodes: 100,
2919 max_move_value_depth: 3,
2920 max_layout_work: usize::MAX,
2921 },
2922 );
2923
2924 let struct_err = resolver
2927 .type_layout(type_("0xd0::m::O<vector<u8>, u8>"))
2928 .await
2929 .unwrap_err();
2930 let enum_err = resolver
2931 .type_layout(type_("0xd0::m::EO<vector<u8>, u8>"))
2932 .await
2933 .unwrap_err();
2934
2935 assert!(matches!(struct_err, Error::ValueNesting(3)));
2936 assert!(matches!(enum_err, Error::ValueNesting(3)));
2937 }
2938
2939 #[tokio::test]
2940 async fn test_system_package_invalidation() {
2941 let (inner, cache) = package_cache([(1, build_package("s0"), s0_types())]);
2942 let resolver = Resolver::new(cache);
2943
2944 let struct_not_found = resolver.type_layout(type_("0x1::m::T1")).await.unwrap_err();
2945 let enum_not_found = resolver.type_layout(type_("0x1::m::E1")).await.unwrap_err();
2946 assert!(matches!(struct_not_found, Error::DatatypeNotFound(_, _, _)));
2947 assert!(matches!(enum_not_found, Error::DatatypeNotFound(_, _, _)));
2948
2949 inner.write().unwrap().replace(
2951 addr("0x1"),
2952 cached_package(2, BTreeMap::new(), &build_package("s1"), &s1_types()),
2953 );
2954
2955 resolver.package_store().evict([addr("0x1")]);
2957
2958 let struct_layout = resolver.type_layout(type_("0x1::m::T1")).await.unwrap();
2959 let enum_layout = resolver.type_layout(type_("0x1::m::E1")).await.unwrap();
2960 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2961 }
2962
2963 #[tokio::test]
2964 async fn test_sync_package_caching() {
2965 use std::sync::atomic::{AtomicUsize, Ordering};
2966
2967 #[derive(Clone)]
2968 struct CountingStore {
2969 package: Package,
2970 fetches: Arc<AtomicUsize>,
2971 }
2972
2973 impl SyncPackageStore for CountingStore {
2974 fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
2975 self.fetches.fetch_add(1, Ordering::Relaxed);
2976 if id != self.package.storage_id {
2977 return Err(Error::PackageNotFound(id));
2978 }
2979 Ok(Arc::new(self.package.clone()))
2980 }
2981 }
2982
2983 let fetches = Arc::new(AtomicUsize::new(0));
2984 let store = CountingStore {
2985 package: cached_package(1, BTreeMap::new(), &build_package("a0"), &a0_types()),
2986 fetches: fetches.clone(),
2987 };
2988 let resolver = SyncResolver::new(PackageStoreWithLruCache::new(store.clone()));
2989 for tag in ["0xa0::m::T0", "0xa0::m::T0", "0xa0::m::T2", "0xa0::m::E0"] {
2990 resolver.type_layout(type_(tag)).unwrap();
2991 }
2992 assert_eq!(fetches.load(Ordering::Relaxed), 1);
2993
2994 let cache = resolver.package_store();
2995 let first = SyncPackageStore::fetch(cache, addr("0xa0")).unwrap();
2996 let second = SyncPackageStore::fetch(cache, addr("0xa0")).unwrap();
2997 assert!(Arc::ptr_eq(&first, &second));
2998 assert_eq!(fetches.load(Ordering::Relaxed), 1);
2999
3000 std::thread::scope(|scope| {
3001 for tag in ["0xa0::m::T0", "0xa0::m::T2", "0xa0::m::E0"] {
3002 scope.spawn(move || {
3003 SyncResolver::new(cache).type_layout(type_(tag)).unwrap();
3004 });
3005 }
3006 });
3007 SyncResolver::new(cache)
3008 .type_layout(type_("0xa0::m::T0"))
3009 .unwrap();
3010 assert_eq!(fetches.load(Ordering::Relaxed), 1);
3011
3012 cache.evict([addr("0xa0")]);
3013 resolver.type_layout(type_("0xa0::m::T0")).unwrap();
3014 assert_eq!(fetches.load(Ordering::Relaxed), 2);
3015
3016 let next_response = SyncResolver::new(PackageStoreWithLruCache::new(store));
3017 next_response.type_layout(type_("0xa0::m::T0")).unwrap();
3018 assert_eq!(fetches.load(Ordering::Relaxed), 3);
3019 }
3020
3021 #[tokio::test]
3022 async fn test_caching() {
3023 let (inner, cache) = package_cache([
3024 (1, build_package("a0"), a0_types()),
3025 (1, build_package("s0"), s0_types()),
3026 ]);
3027 let resolver = Resolver::new(cache);
3028
3029 assert_eq!(inner.read().unwrap().fetches, 0);
3030 let l0 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
3031
3032 assert_eq!(inner.read().unwrap().fetches, 1);
3034
3035 let l1 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
3037 assert_eq!(format!("{l0}"), format!("{l1}"));
3038 assert_eq!(inner.read().unwrap().fetches, 1);
3039
3040 let l2 = resolver.type_layout(type_("0xa0::m::T2")).await.unwrap();
3042 assert_ne!(format!("{l0}"), format!("{l2}"));
3043 assert_eq!(inner.read().unwrap().fetches, 1);
3044
3045 resolver.type_layout(type_("0xa0::m::E0")).await.unwrap();
3047 assert_eq!(inner.read().unwrap().fetches, 1);
3048
3049 let l3 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
3051 assert_eq!(inner.read().unwrap().fetches, 2);
3052
3053 let l4 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
3055 assert_eq!(format!("{l3}"), format!("{l4}"));
3056 assert_eq!(inner.read().unwrap().fetches, 2);
3057
3058 let el4 = resolver.type_layout(type_("0x1::m::E0")).await.unwrap();
3060 assert_ne!(format!("{el4}"), format!("{l4}"));
3061 assert_eq!(inner.read().unwrap().fetches, 2);
3062
3063 inner.write().unwrap().replace(
3065 addr("0x1"),
3066 cached_package(2, BTreeMap::new(), &build_package("s1"), &s1_types()),
3067 );
3068
3069 resolver.package_store().evict([addr("0x1")]);
3071
3072 let l5 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
3076 assert_eq!(format!("{l4}"), format!("{l5}"));
3077 assert_eq!(inner.read().unwrap().fetches, 3);
3078 }
3079
3080 #[tokio::test]
3081 async fn test_layout_err_not_a_package() {
3082 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3083 let resolver = Resolver::new(cache);
3084 let err = resolver
3085 .type_layout(type_("0x42::m::T0"))
3086 .await
3087 .unwrap_err();
3088 assert!(matches!(err, Error::PackageNotFound(_)));
3089 }
3090
3091 #[tokio::test]
3092 async fn test_layout_err_no_module() {
3093 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3094 let resolver = Resolver::new(cache);
3095 let err = resolver
3096 .type_layout(type_("0xa0::l::T0"))
3097 .await
3098 .unwrap_err();
3099 assert!(matches!(err, Error::ModuleNotFound(_, _)));
3100 }
3101
3102 #[tokio::test]
3103 async fn test_layout_err_no_struct() {
3104 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3105 let resolver = Resolver::new(cache);
3106
3107 let err = resolver
3108 .type_layout(type_("0xa0::m::T9"))
3109 .await
3110 .unwrap_err();
3111 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
3112 }
3113
3114 #[tokio::test]
3115 async fn test_layout_err_type_arity() {
3116 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3117 let resolver = Resolver::new(cache);
3118
3119 let err = resolver
3121 .type_layout(type_("0xa0::m::T1<u8>"))
3122 .await
3123 .unwrap_err();
3124 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
3125
3126 let err = resolver
3128 .type_layout(type_("0xa0::m::T1<u8, u16, u32>"))
3129 .await
3130 .unwrap_err();
3131 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
3132 }
3133
3134 #[tokio::test]
3135 async fn test_structs() {
3136 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3137 let a0 = cache.fetch(addr("0xa0")).await.unwrap();
3138 let m = a0.module("m").unwrap();
3139
3140 assert_eq!(
3141 m.structs(None, None).collect::<Vec<_>>(),
3142 vec!["T0", "T1", "T2"],
3143 );
3144
3145 assert_eq!(m.structs(None, Some("T1")).collect::<Vec<_>>(), vec!["T0"],);
3146
3147 assert_eq!(
3148 m.structs(Some("T0"), Some("T2")).collect::<Vec<_>>(),
3149 vec!["T1"],
3150 );
3151
3152 assert_eq!(m.structs(Some("T1"), None).collect::<Vec<_>>(), vec!["T2"],);
3153
3154 let t0 = m.struct_def("T0").unwrap().unwrap();
3155 let t1 = m.struct_def("T1").unwrap().unwrap();
3156 let t2 = m.struct_def("T2").unwrap().unwrap();
3157
3158 insta::assert_snapshot!(format!(
3159 "a0::m::T0: {t0:#?}\n\
3160 a0::m::T1: {t1:#?}\n\
3161 a0::m::T2: {t2:#?}",
3162 ));
3163 }
3164
3165 #[tokio::test]
3166 async fn test_enums() {
3167 let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]);
3168 let a0 = cache
3169 .fetch(AccountAddress::from_str("0xa0").unwrap())
3170 .await
3171 .unwrap();
3172 let m = a0.module("m").unwrap();
3173
3174 assert_eq!(
3175 m.enums(None, None).collect::<Vec<_>>(),
3176 vec!["E0", "E1", "E2"],
3177 );
3178
3179 assert_eq!(m.enums(None, Some("E1")).collect::<Vec<_>>(), vec!["E0"],);
3180
3181 assert_eq!(
3182 m.enums(Some("E0"), Some("E2")).collect::<Vec<_>>(),
3183 vec!["E1"],
3184 );
3185
3186 assert_eq!(m.enums(Some("E1"), None).collect::<Vec<_>>(), vec!["E2"],);
3187
3188 let e0 = m.enum_def("E0").unwrap().unwrap();
3189 let e1 = m.enum_def("E1").unwrap().unwrap();
3190 let e2 = m.enum_def("E2").unwrap().unwrap();
3191
3192 insta::assert_snapshot!(format!(
3193 "a0::m::E0: {e0:#?}\n\
3194 a0::m::E1: {e1:#?}\n\
3195 a0::m::E2: {e2:#?}",
3196 ));
3197 }
3198
3199 #[tokio::test]
3200 async fn test_functions() {
3201 let (_, cache) = package_cache([
3202 (1, build_package("a0"), a0_types()),
3203 (2, build_package("a1"), a1_types()),
3204 (1, build_package("b0"), b0_types()),
3205 (1, build_package("c0"), c0_types()),
3206 ]);
3207
3208 let c0 = cache.fetch(addr("0xc0")).await.unwrap();
3209 let m = c0.module("m").unwrap();
3210
3211 assert_eq!(
3212 m.functions(None, None).collect::<Vec<_>>(),
3213 vec!["bar", "baz", "foo"],
3214 );
3215
3216 assert_eq!(
3217 m.functions(None, Some("baz")).collect::<Vec<_>>(),
3218 vec!["bar"],
3219 );
3220
3221 assert_eq!(
3222 m.functions(Some("bar"), Some("foo")).collect::<Vec<_>>(),
3223 vec!["baz"],
3224 );
3225
3226 assert_eq!(
3227 m.functions(Some("baz"), None).collect::<Vec<_>>(),
3228 vec!["foo"],
3229 );
3230
3231 let foo = m.function_def("foo").unwrap().unwrap();
3232 let bar = m.function_def("bar").unwrap().unwrap();
3233 let baz = m.function_def("baz").unwrap().unwrap();
3234
3235 insta::assert_snapshot!(format!(
3236 "c0::m::foo: {foo:#?}\n\
3237 c0::m::bar: {bar:#?}\n\
3238 c0::m::baz: {baz:#?}"
3239 ));
3240 }
3241
3242 #[tokio::test]
3243 async fn test_function_parameters() {
3244 let (_, cache) = package_cache([
3245 (1, build_package("a0"), a0_types()),
3246 (2, build_package("a1"), a1_types()),
3247 (1, build_package("b0"), b0_types()),
3248 (1, build_package("c0"), c0_types()),
3249 ]);
3250
3251 let resolver = Resolver::new(cache);
3252 let c0 = addr("0xc0");
3253
3254 let foo = resolver.function_signature(c0, "m", "foo").await.unwrap();
3255 let bar = resolver.function_signature(c0, "m", "bar").await.unwrap();
3256 let baz = resolver.function_signature(c0, "m", "baz").await.unwrap();
3257
3258 insta::assert_snapshot!(format!(
3259 "c0::m::foo: {foo:#?}\n\
3260 c0::m::bar: {bar:#?}\n\
3261 c0::m::baz: {baz:#?}"
3262 ));
3263 }
3264
3265 #[tokio::test]
3266 async fn test_signature_instantiation() {
3267 use OpenSignatureBody as O;
3268 use TypeInput as T;
3269
3270 let sig = O::Datatype(
3271 key("0x2::table::Table"),
3272 vec![
3273 O::TypeParameter(1),
3274 O::Vector(Box::new(O::Datatype(
3275 key("0x1::option::Option"),
3276 vec![O::TypeParameter(0)],
3277 ))),
3278 ],
3279 );
3280
3281 insta::assert_debug_snapshot!(sig.instantiate(&[T::U64, T::Bool]).unwrap());
3282 }
3283
3284 #[tokio::test]
3285 async fn test_signature_instantiation_error() {
3286 use OpenSignatureBody as O;
3287 use TypeInput as T;
3288
3289 let sig = O::Datatype(
3290 key("0x2::table::Table"),
3291 vec![
3292 O::TypeParameter(1),
3293 O::Vector(Box::new(O::Datatype(
3294 key("0x1::option::Option"),
3295 vec![O::TypeParameter(99)],
3296 ))),
3297 ],
3298 );
3299
3300 insta::assert_snapshot!(
3301 sig.instantiate(&[T::U64, T::Bool]).unwrap_err(),
3302 @"Type Parameter 99 out of bounds (2)"
3303 );
3304 }
3305
3306 #[tokio::test]
3308 async fn test_primitive_abilities() {
3309 use Ability as A;
3310 use AbilitySet as S;
3311
3312 let (_, cache) = package_cache([]);
3313 let resolver = Resolver::new(cache);
3314
3315 for prim in ["address", "bool", "u8", "u16", "u32", "u64", "u128", "u256"] {
3316 assert_eq!(
3317 resolver.abilities(type_(prim)).await.unwrap(),
3318 S::EMPTY | A::Copy | A::Drop | A::Store,
3319 "Unexpected primitive abilities for: {prim}",
3320 );
3321 }
3322 }
3323
3324 #[tokio::test]
3326 async fn test_simple_generic_abilities() {
3327 use Ability as A;
3328 use AbilitySet as S;
3329
3330 let (_, cache) = package_cache([
3331 (1, build_package("sui"), sui_types()),
3332 (1, build_package("d0"), d0_types()),
3333 ]);
3334 let resolver = Resolver::new(cache);
3335
3336 let a1 = resolver
3337 .abilities(type_("0xd0::m::T<u32, u64>"))
3338 .await
3339 .unwrap();
3340 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop | A::Store);
3341
3342 let a2 = resolver
3343 .abilities(type_("0xd0::m::T<0xd0::m::S, u64>"))
3344 .await
3345 .unwrap();
3346 assert_eq!(a2, S::EMPTY | A::Drop | A::Store);
3347
3348 let a3 = resolver
3349 .abilities(type_("0xd0::m::T<0xd0::m::R, 0xd0::m::S>"))
3350 .await
3351 .unwrap();
3352 assert_eq!(a3, S::EMPTY | A::Drop);
3353
3354 let a4 = resolver
3355 .abilities(type_("0xd0::m::T<0xd0::m::Q, 0xd0::m::R>"))
3356 .await
3357 .unwrap();
3358 assert_eq!(a4, S::EMPTY);
3359 }
3360
3361 #[tokio::test]
3363 async fn test_nested_generic_abilities() {
3364 use Ability as A;
3365 use AbilitySet as S;
3366
3367 let (_, cache) = package_cache([
3368 (1, build_package("sui"), sui_types()),
3369 (1, build_package("d0"), d0_types()),
3370 ]);
3371 let resolver = Resolver::new(cache);
3372
3373 let a1 = resolver
3374 .abilities(type_("0xd0::m::T<0xd0::m::T<0xd0::m::R, u32>, u64>"))
3375 .await
3376 .unwrap();
3377 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop);
3378 }
3379
3380 #[tokio::test]
3383 async fn test_key_abilities() {
3384 use Ability as A;
3385 use AbilitySet as S;
3386
3387 let (_, cache) = package_cache([
3388 (1, build_package("sui"), sui_types()),
3389 (1, build_package("d0"), d0_types()),
3390 ]);
3391 let resolver = Resolver::new(cache);
3392
3393 let a1 = resolver
3394 .abilities(type_("0xd0::m::O<u32, u64>"))
3395 .await
3396 .unwrap();
3397 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
3398
3399 let a2 = resolver
3400 .abilities(type_("0xd0::m::O<0xd0::m::S, u64>"))
3401 .await
3402 .unwrap();
3403 assert_eq!(a2, S::EMPTY | A::Key | A::Store);
3404
3405 let a3 = resolver
3408 .abilities(type_("0xd0::m::O<0xd0::m::R, u64>"))
3409 .await
3410 .unwrap();
3411 assert_eq!(a3, S::EMPTY);
3412
3413 let a4 = resolver
3415 .abilities(type_("0xd0::m::O<0xd0::m::P, u32>"))
3416 .await
3417 .unwrap();
3418 assert_eq!(a4, S::EMPTY);
3419 }
3420
3421 #[tokio::test]
3423 async fn test_phantom_abilities() {
3424 use Ability as A;
3425 use AbilitySet as S;
3426
3427 let (_, cache) = package_cache([
3428 (1, build_package("sui"), sui_types()),
3429 (1, build_package("d0"), d0_types()),
3430 ]);
3431 let resolver = Resolver::new(cache);
3432
3433 let a1 = resolver
3434 .abilities(type_("0xd0::m::O<u32, 0xd0::m::R>"))
3435 .await
3436 .unwrap();
3437 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
3438 }
3439
3440 #[tokio::test]
3441 async fn test_err_ability_arity() {
3442 let (_, cache) = package_cache([
3443 (1, build_package("sui"), sui_types()),
3444 (1, build_package("d0"), d0_types()),
3445 ]);
3446 let resolver = Resolver::new(cache);
3447
3448 let err = resolver
3450 .abilities(type_("0xd0::m::T<u8>"))
3451 .await
3452 .unwrap_err();
3453 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
3454
3455 let err = resolver
3457 .abilities(type_("0xd0::m::T<u8, u16, u32>"))
3458 .await
3459 .unwrap_err();
3460 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
3461 }
3462
3463 #[tokio::test]
3464 async fn test_err_ability_signer() {
3465 let (_, cache) = package_cache([]);
3466 let resolver = Resolver::new(cache);
3467
3468 let err = resolver.abilities(type_("signer")).await.unwrap_err();
3469 assert!(matches!(err, Error::UnexpectedSigner));
3470 }
3471
3472 #[tokio::test]
3473 async fn test_err_too_many_type_params() {
3474 let (_, cache) = package_cache([
3475 (1, build_package("sui"), sui_types()),
3476 (1, build_package("d0"), d0_types()),
3477 ]);
3478
3479 let resolver = Resolver::new_with_limits(
3480 cache,
3481 Limits {
3482 max_type_argument_width: 1,
3483 max_type_argument_depth: 100,
3484 max_type_nodes: 100,
3485 max_move_value_depth: 100,
3486 max_layout_work: usize::MAX,
3487 },
3488 );
3489
3490 let err = resolver
3491 .abilities(type_("0xd0::m::O<u32, u64>"))
3492 .await
3493 .unwrap_err();
3494 assert!(matches!(err, Error::TooManyTypeParams(1, 2)));
3495 }
3496
3497 #[tokio::test]
3498 async fn test_err_too_many_type_nodes() {
3499 use Ability as A;
3500 use AbilitySet as S;
3501
3502 let (_, cache) = package_cache([
3503 (1, build_package("sui"), sui_types()),
3504 (1, build_package("d0"), d0_types()),
3505 ]);
3506
3507 let resolver = Resolver::new_with_limits(
3508 cache,
3509 Limits {
3510 max_type_argument_width: 100,
3511 max_type_argument_depth: 100,
3512 max_type_nodes: 2,
3513 max_move_value_depth: 100,
3514 max_layout_work: usize::MAX,
3515 },
3516 );
3517
3518 let a1 = resolver
3521 .abilities(type_("0xd0::m::O<0xd0::m::S, 0xd0::m::Q>"))
3522 .await
3523 .unwrap();
3524 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
3525
3526 let err = resolver
3528 .abilities(type_("0xd0::m::T<0xd0::m::P, 0xd0::m::Q>"))
3529 .await
3530 .unwrap_err();
3531 assert!(matches!(err, Error::TooManyTypeNodes(2, _)));
3532 }
3533
3534 #[tokio::test]
3535 async fn test_err_type_param_nesting() {
3536 use Ability as A;
3537 use AbilitySet as S;
3538
3539 let (_, cache) = package_cache([
3540 (1, build_package("sui"), sui_types()),
3541 (1, build_package("d0"), d0_types()),
3542 ]);
3543
3544 let resolver = Resolver::new_with_limits(
3545 cache,
3546 Limits {
3547 max_type_argument_width: 100,
3548 max_type_argument_depth: 2,
3549 max_type_nodes: 100,
3550 max_move_value_depth: 100,
3551 max_layout_work: usize::MAX,
3552 },
3553 );
3554
3555 let a1 = resolver
3558 .abilities(type_(
3559 "0xd0::m::O<0xd0::m::S, 0xd0::m::T<vector<u32>, vector<u64>>>",
3560 ))
3561 .await
3562 .unwrap();
3563 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
3564
3565 let err = resolver
3567 .abilities(type_("vector<0xd0::m::T<0xd0::m::O<u64, u32>, u16>>"))
3568 .await
3569 .unwrap_err();
3570 assert!(matches!(err, Error::TypeParamNesting(2, _)));
3571 }
3572
3573 #[tokio::test]
3574 async fn test_pure_input_layouts() {
3575 use CallArg as I;
3576 use ObjectArg::ImmOrOwnedObject as O;
3577 use TypeTag as T;
3578
3579 let (_, cache) = package_cache([
3580 (1, build_package("std"), std_types()),
3581 (1, build_package("sui"), sui_types()),
3582 (1, build_package("e0"), e0_types()),
3583 ]);
3584
3585 let resolver = Resolver::new(cache);
3586
3587 fn ptb(t: TypeTag, y: CallArg) -> ProgrammableTransaction {
3589 ProgrammableTransaction {
3590 inputs: vec![
3591 I::Object(O(random_object_ref())),
3592 I::Pure(bcs::to_bytes(&42u64).unwrap()),
3593 I::Object(O(random_object_ref())),
3594 y,
3595 I::Object(O(random_object_ref())),
3596 I::Pure(bcs::to_bytes("hello").unwrap()),
3597 I::Pure(bcs::to_bytes("world").unwrap()),
3598 ],
3599 commands: vec![Command::move_call(
3600 addr("0xe0").into(),
3601 ident_str!("m").to_owned(),
3602 ident_str!("foo").to_owned(),
3603 vec![t],
3604 (0..=6).map(Argument::Input).collect(),
3605 )],
3606 }
3607 }
3608
3609 let ptb_u64 = ptb(T::U64, I::Pure(bcs::to_bytes(&1u64).unwrap()));
3610
3611 let ptb_opt = ptb(
3612 TypeTag::Struct(Box::new(StructTag {
3613 address: addr("0x1"),
3614 module: ident_str!("option").to_owned(),
3615 name: ident_str!("Option").to_owned(),
3616 type_params: vec![TypeTag::U64],
3617 })),
3618 I::Pure(bcs::to_bytes(&[vec![1u64], vec![], vec![3]]).unwrap()),
3619 );
3620
3621 let ptb_obj = ptb(
3622 TypeTag::Struct(Box::new(StructTag {
3623 address: addr("0xe0"),
3624 module: ident_str!("m").to_owned(),
3625 name: ident_str!("O").to_owned(),
3626 type_params: vec![],
3627 })),
3628 I::Object(O(random_object_ref())),
3629 );
3630
3631 let inputs_u64 = resolver.pure_input_layouts(&ptb_u64).await.unwrap();
3632 let inputs_opt = resolver.pure_input_layouts(&ptb_opt).await.unwrap();
3633 let inputs_obj = resolver.pure_input_layouts(&ptb_obj).await.unwrap();
3634
3635 let mut output = "---\n".to_string();
3637 for inputs in [inputs_u64, inputs_opt, inputs_obj] {
3638 for input in inputs {
3639 if let Some(layout) = input {
3640 output += &format!("{layout:#}\n");
3641 } else {
3642 output += "???\n";
3643 }
3644 }
3645 output += "---\n";
3646 }
3647
3648 insta::assert_snapshot!(output);
3649 }
3650
3651 #[tokio::test]
3654 async fn test_pure_input_layouts_overlapping() {
3655 use CallArg as I;
3656 use ObjectArg::ImmOrOwnedObject as O;
3657 use TypeTag as T;
3658
3659 let (_, cache) = package_cache([
3660 (1, build_package("std"), std_types()),
3661 (1, build_package("sui"), sui_types()),
3662 (1, build_package("e0"), e0_types()),
3663 ]);
3664
3665 let resolver = Resolver::new(cache);
3666
3667 let ptb = ProgrammableTransaction {
3669 inputs: vec![
3670 I::Object(O(random_object_ref())),
3671 I::Pure(bcs::to_bytes(&42u64).unwrap()),
3672 I::Object(O(random_object_ref())),
3673 I::Pure(bcs::to_bytes(&43u64).unwrap()),
3674 I::Object(O(random_object_ref())),
3675 I::Pure(bcs::to_bytes("hello").unwrap()),
3676 I::Pure(bcs::to_bytes("world").unwrap()),
3677 ],
3678 commands: vec![
3679 Command::move_call(
3680 addr("0xe0").into(),
3681 ident_str!("m").to_owned(),
3682 ident_str!("foo").to_owned(),
3683 vec![T::U64],
3684 (0..=6).map(Argument::Input).collect(),
3685 ),
3686 Command::move_call(
3687 addr("0xe0").into(),
3688 ident_str!("m").to_owned(),
3689 ident_str!("foo").to_owned(),
3690 vec![T::U64],
3691 (0..=6).map(Argument::Input).collect(),
3692 ),
3693 ],
3694 };
3695
3696 let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
3697
3698 let mut output = String::new();
3700 for input in inputs {
3701 if let Some(layout) = input {
3702 output += &format!("{layout:#}\n");
3703 } else {
3704 output += "???\n";
3705 }
3706 }
3707
3708 insta::assert_snapshot!(output);
3709 }
3710
3711 #[tokio::test]
3712 async fn test_pure_input_layouts_conflicting() {
3713 use CallArg as I;
3714 use ObjectArg::ImmOrOwnedObject as O;
3715 use TypeInput as TI;
3716 use TypeTag as T;
3717
3718 let (_, cache) = package_cache([
3719 (1, build_package("std"), std_types()),
3720 (1, build_package("sui"), sui_types()),
3721 (1, build_package("e0"), e0_types()),
3722 ]);
3723
3724 let resolver = Resolver::new(cache);
3725
3726 let ptb = ProgrammableTransaction {
3727 inputs: vec![
3728 I::Object(O(random_object_ref())),
3729 I::Pure(bcs::to_bytes(&42u64).unwrap()),
3730 I::Object(O(random_object_ref())),
3731 I::Pure(bcs::to_bytes(&43u64).unwrap()),
3732 I::Object(O(random_object_ref())),
3733 I::Pure(bcs::to_bytes("hello").unwrap()),
3734 I::Pure(bcs::to_bytes("world").unwrap()),
3735 ],
3736 commands: vec![
3737 Command::move_call(
3738 addr("0xe0").into(),
3739 ident_str!("m").to_owned(),
3740 ident_str!("foo").to_owned(),
3741 vec![T::U64],
3742 (0..=6).map(Argument::Input).collect(),
3743 ),
3744 Command::MakeMoveVec(Some(TI::U32), vec![Argument::Input(3)]),
3747 ],
3748 };
3749
3750 let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
3751
3752 let mut output = String::new();
3754 for input in inputs {
3755 if let Some(layout) = input {
3756 output += &format!("{layout:#}\n");
3757 } else {
3758 output += "???\n";
3759 }
3760 }
3761
3762 insta::assert_snapshot!(output);
3763 }
3764
3765 type TypeOriginTable = Vec<DatatypeKey>;
3768
3769 fn a0_types() -> TypeOriginTable {
3770 vec![
3771 datakey("0xa0", "m", "T0"),
3772 datakey("0xa0", "m", "T1"),
3773 datakey("0xa0", "m", "T2"),
3774 datakey("0xa0", "m", "E0"),
3775 datakey("0xa0", "m", "E1"),
3776 datakey("0xa0", "m", "E2"),
3777 datakey("0xa0", "n", "T0"),
3778 datakey("0xa0", "n", "E0"),
3779 datakey("0xa0", "z", "E0"),
3780 ]
3781 }
3782
3783 fn a1_types() -> TypeOriginTable {
3784 let mut types = a0_types();
3785
3786 types.extend([
3787 datakey("0xa1", "m", "T3"),
3788 datakey("0xa1", "m", "T4"),
3789 datakey("0xa1", "n", "T1"),
3790 datakey("0xa1", "m", "E3"),
3791 datakey("0xa1", "m", "E4"),
3792 datakey("0xa1", "n", "E1"),
3793 ]);
3794
3795 types
3796 }
3797
3798 fn b0_types() -> TypeOriginTable {
3799 vec![datakey("0xb0", "m", "T0"), datakey("0xb0", "m", "E0")]
3800 }
3801
3802 fn c0_types() -> TypeOriginTable {
3803 vec![datakey("0xc0", "m", "T0"), datakey("0xc0", "m", "E0")]
3804 }
3805
3806 fn d0_types() -> TypeOriginTable {
3807 vec![
3808 datakey("0xd0", "m", "O"),
3809 datakey("0xd0", "m", "P"),
3810 datakey("0xd0", "m", "Q"),
3811 datakey("0xd0", "m", "R"),
3812 datakey("0xd0", "m", "S"),
3813 datakey("0xd0", "m", "T"),
3814 datakey("0xd0", "m", "EO"),
3815 datakey("0xd0", "m", "EP"),
3816 datakey("0xd0", "m", "EQ"),
3817 datakey("0xd0", "m", "ER"),
3818 datakey("0xd0", "m", "ES"),
3819 datakey("0xd0", "m", "ET"),
3820 ]
3821 }
3822
3823 fn e0_types() -> TypeOriginTable {
3824 vec![datakey("0xe0", "m", "O")]
3825 }
3826
3827 fn s0_types() -> TypeOriginTable {
3828 vec![datakey("0x1", "m", "T0"), datakey("0x1", "m", "E0")]
3829 }
3830
3831 fn s1_types() -> TypeOriginTable {
3832 let mut types = s0_types();
3833
3834 types.extend([datakey("0x1", "m", "T1"), datakey("0x1", "m", "E1")]);
3835
3836 types
3837 }
3838
3839 fn sui_types() -> TypeOriginTable {
3840 vec![datakey("0x2", "object", "UID")]
3841 }
3842
3843 fn std_types() -> TypeOriginTable {
3844 vec![
3845 datakey("0x1", "ascii", "String"),
3846 datakey("0x1", "option", "Option"),
3847 datakey("0x1", "string", "String"),
3848 ]
3849 }
3850
3851 fn package_cache(
3855 packages: impl IntoIterator<Item = (u64, CompiledPackage, TypeOriginTable)>,
3856 ) -> (
3857 Arc<RwLock<InnerStore>>,
3858 PackageStoreWithLruCache<InMemoryPackageStore>,
3859 ) {
3860 let packages_by_storage_id: BTreeMap<AccountAddress, _> = packages
3861 .into_iter()
3862 .map(|(version, package, origins)| {
3863 (package_storage_id(&package), (version, package, origins))
3864 })
3865 .collect();
3866
3867 let packages = packages_by_storage_id
3868 .iter()
3869 .map(|(&storage_id, (version, compiled_package, origins))| {
3870 let linkage = compiled_package
3871 .dependency_ids
3872 .published
3873 .values()
3874 .map(|dep| {
3875 let storage_id = AccountAddress::from(dep.published_at);
3876 let runtime_id = package_runtime_id(
3877 &packages_by_storage_id
3878 .get(&storage_id)
3879 .unwrap_or_else(|| panic!("Dependency {storage_id} not in store"))
3880 .1,
3881 );
3882
3883 (runtime_id, storage_id)
3884 })
3885 .collect();
3886
3887 let package = cached_package(*version, linkage, compiled_package, origins);
3888 (storage_id, package)
3889 })
3890 .collect();
3891
3892 let inner = Arc::new(RwLock::new(InnerStore {
3893 packages,
3894 fetches: 0,
3895 }));
3896
3897 let store = InMemoryPackageStore {
3898 inner: inner.clone(),
3899 };
3900
3901 (inner, PackageStoreWithLruCache::new(store))
3902 }
3903
3904 fn cached_package(
3905 version: u64,
3906 linkage: Linkage,
3907 package: &CompiledPackage,
3908 origins: &TypeOriginTable,
3909 ) -> Package {
3910 let storage_id = package_storage_id(package);
3911 let runtime_id = package_runtime_id(package);
3912 let version = SequenceNumber::from_u64(version);
3913
3914 let mut modules = BTreeMap::new();
3915 for unit in &package.package.root_compiled_units {
3916 let NamedCompiledModule { name, module, .. } = &unit.unit;
3917
3918 let origins = origins
3919 .iter()
3920 .filter(|key| key.module == name.as_str())
3921 .map(|key| (key.name.to_string(), key.package))
3922 .collect();
3923
3924 let module = match Module::read(module.clone(), origins) {
3925 Ok(module) => module,
3926 Err(struct_) => {
3927 panic!("Missing type origin for {}::{struct_}", module.self_id());
3928 }
3929 };
3930
3931 modules.insert(name.to_string(), module);
3932 }
3933
3934 Package {
3935 storage_id,
3936 runtime_id,
3937 linkage,
3938 version,
3939 modules,
3940 }
3941 }
3942
3943 fn package_storage_id(package: &CompiledPackage) -> AccountAddress {
3944 AccountAddress::from(*package.published_at.as_ref().unwrap_or_else(|| {
3945 panic!(
3946 "Package {} doesn't have published-at set",
3947 package.package.compiled_package_info.package_name,
3948 )
3949 }))
3950 }
3951
3952 fn package_runtime_id(package: &CompiledPackage) -> AccountAddress {
3953 *package
3954 .published_root_module()
3955 .expect("No compiled module")
3956 .address()
3957 }
3958
3959 fn build_package(dir: &str) -> CompiledPackage {
3960 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3961 path.extend(["tests", "packages", dir]);
3962 BuildConfig::new_for_testing().build(&path).unwrap()
3963 }
3964
3965 fn addr(a: &str) -> AccountAddress {
3966 AccountAddress::from_str(a).unwrap()
3967 }
3968
3969 fn datakey(a: &str, m: &'static str, n: &'static str) -> DatatypeKey {
3970 DatatypeKey {
3971 package: addr(a),
3972 module: m.into(),
3973 name: n.into(),
3974 }
3975 }
3976
3977 fn type_(t: &str) -> TypeTag {
3978 TypeTag::from_str(t).unwrap()
3979 }
3980
3981 fn key(t: &str) -> DatatypeKey {
3982 let tag = StructTag::from_str(t).unwrap();
3983 DatatypeRef::from(&tag).as_key()
3984 }
3985
3986 struct InMemoryPackageStore {
3987 inner: Arc<RwLock<InnerStore>>,
3990 }
3991
3992 struct InnerStore {
3993 packages: BTreeMap<AccountAddress, Package>,
3994 fetches: usize,
3995 }
3996
3997 #[async_trait]
3998 impl PackageStore for InMemoryPackageStore {
3999 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
4000 let mut inner = self.inner.as_ref().write().unwrap();
4001 inner.fetches += 1;
4002 inner
4003 .packages
4004 .get(&id)
4005 .cloned()
4006 .ok_or_else(|| Error::PackageNotFound(id))
4007 .map(Arc::new)
4008 }
4009 }
4010
4011 impl InnerStore {
4012 fn replace(&mut self, id: AccountAddress, package: Package) {
4013 self.packages.insert(id, package);
4014 }
4015 }
4016}