1use std::{
6 borrow::Cow,
7 collections::{BTreeMap, BTreeSet},
8 num::NonZeroUsize,
9 sync::{Arc, Mutex},
10};
11
12use async_trait::async_trait;
13use iota_types::{
14 Identifier,
15 base_types::{SequenceNumber, is_primitive_type_tag},
16 move_package::{MovePackage, TypeOrigin},
17 object::Object,
18 transaction::{Argument, CallArg, Command, ProgrammableTransaction},
19};
20use lru::LruCache;
21use move_binary_format::{
22 CompiledModule,
23 errors::Location,
24 file_format::{
25 AbilitySet, DatatypeHandleIndex, DatatypeTyParameter, EnumDefinitionIndex,
26 FunctionDefinitionIndex, Signature as MoveSignature, SignatureIndex, SignatureToken,
27 StructDefinitionIndex, StructFieldInformation, TableIndex, Visibility,
28 },
29};
30use move_command_line_common::{
31 display::{RenderResult, try_render_constant},
32 error_bitset::ErrorBitset,
33};
34use move_core_types::{
35 account_address::AccountAddress,
36 annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
37 language_storage::{ModuleId, StructTag, TypeTag},
38};
39
40use crate::error::Error;
41
42pub mod error;
43
44const PACKAGE_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
47
48pub type Result<T> = std::result::Result<T, Error>;
49
50#[derive(Debug)]
54pub struct Resolver<S> {
55 package_store: S,
56 limits: Option<Limits>,
57}
58
59#[derive(Debug)]
62pub struct Limits {
63 pub max_type_argument_depth: usize,
65 pub max_type_argument_width: usize,
67 pub max_type_nodes: usize,
69 pub max_move_value_depth: usize,
71}
72
73pub struct PackageStoreWithLruCache<T> {
78 pub(crate) packages: Mutex<LruCache<AccountAddress, Arc<Package>>>,
79 pub(crate) inner: T,
80}
81
82#[derive(Clone, Debug)]
83pub struct Package {
84 storage_id: AccountAddress,
86
87 runtime_id: AccountAddress,
91
92 linkage: Linkage,
96
97 version: SequenceNumber,
100
101 modules: BTreeMap<String, Module>,
102}
103
104type Linkage = BTreeMap<AccountAddress, AccountAddress>;
105
106#[derive(Clone, Debug)]
113pub struct CleverError {
114 pub module_id: ModuleId,
116 pub error_info: ErrorConstants,
119 pub source_line_number: u16,
121}
122
123#[derive(Clone, Debug)]
134pub enum ErrorConstants {
135 None,
137 Rendered {
148 identifier: String,
150 constant: String,
152 },
153 Raw {
158 identifier: String,
160 bytes: Vec<u8>,
162 },
163}
164
165#[derive(Clone, Debug)]
166pub struct Module {
167 bytecode: CompiledModule,
168
169 struct_index: BTreeMap<String, (AccountAddress, StructDefinitionIndex)>,
172
173 enum_index: BTreeMap<String, (AccountAddress, EnumDefinitionIndex)>,
176
177 function_index: BTreeMap<String, FunctionDefinitionIndex>,
180}
181
182#[derive(Debug)]
184pub struct DataDef {
185 pub defining_id: AccountAddress,
187
188 pub abilities: AbilitySet,
190
191 pub type_params: Vec<DatatypeTyParameter>,
193
194 pub data: MoveData,
197}
198
199#[derive(Debug)]
200pub enum MoveData {
201 Struct(Vec<(String, OpenSignatureBody)>),
205
206 Enum(Vec<VariantDef>),
209}
210
211#[derive(Debug)]
214pub struct VariantDef {
215 pub name: String,
217
218 pub signatures: Vec<(String, OpenSignatureBody)>,
222}
223
224#[derive(Debug)]
226pub struct FunctionDef {
227 pub visibility: Visibility,
229
230 pub is_entry: bool,
232
233 pub type_params: Vec<AbilitySet>,
235
236 pub parameters: Vec<OpenSignature>,
238
239 pub return_: Vec<OpenSignature>,
241}
242
243#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
247pub struct DatatypeRef<'m, 'n> {
248 pub package: AccountAddress,
249 pub module: Cow<'m, str>,
250 pub name: Cow<'n, str>,
251}
252
253pub type DatatypeKey = DatatypeRef<'static, 'static>;
255
256#[derive(Copy, Clone, Debug)]
257pub enum Reference {
258 Immutable,
259 Mutable,
260}
261
262#[derive(Clone, Debug)]
265pub struct Signature {
266 pub ref_: Option<Reference>,
267 pub body: TypeTag,
268}
269
270#[derive(Clone, Debug)]
273pub struct OpenSignature {
274 pub ref_: Option<Reference>,
275 pub body: OpenSignatureBody,
276}
277
278#[derive(Clone, Debug)]
281pub enum OpenSignatureBody {
282 Address,
283 Bool,
284 U8,
285 U16,
286 U32,
287 U64,
288 U128,
289 U256,
290 Vector(Box<OpenSignatureBody>),
291 Datatype(DatatypeKey, Vec<OpenSignatureBody>),
292 TypeParameter(u16),
293}
294
295#[derive(Debug, Default)]
297struct ResolutionContext<'l> {
298 datatypes: BTreeMap<DatatypeKey, DataDef>,
301
302 limits: Option<&'l Limits>,
304}
305
306#[async_trait]
309pub trait PackageStore: Send + Sync + 'static {
310 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>>;
313}
314
315macro_rules! as_ref_impl {
316 ($type:ty) => {
317 #[async_trait]
318 impl PackageStore for $type {
319 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
320 self.as_ref().fetch(id).await
321 }
322 }
323 };
324}
325
326as_ref_impl!(Arc<dyn PackageStore>);
327as_ref_impl!(Box<dyn PackageStore>);
328
329macro_rules! check_max_limit {
332 ($err:ident, $config:expr; $limit:ident $op:tt $value:expr) => {
333 if let Some(l) = $config {
334 let max = l.$limit;
335 let val = $value;
336 if !(max $op val) {
337 return Err(Error::$err(max, val));
338 }
339 }
340 };
341}
342
343impl<S> Resolver<S> {
344 pub fn new(package_store: S) -> Self {
345 Self {
346 package_store,
347 limits: None,
348 }
349 }
350
351 pub fn new_with_limits(package_store: S, limits: Limits) -> Self {
352 Self {
353 package_store,
354 limits: Some(limits),
355 }
356 }
357
358 pub fn package_store(&self) -> &S {
359 &self.package_store
360 }
361
362 pub fn package_store_mut(&mut self) -> &mut S {
363 &mut self.package_store
364 }
365}
366
367impl<S: PackageStore> Resolver<S> {
368 pub async fn type_layout(&self, mut tag: TypeTag) -> Result<MoveTypeLayout> {
372 let mut context = ResolutionContext::new(self.limits.as_ref());
373
374 context
377 .add_type_tag(
378 &mut tag,
379 &self.package_store,
380 true,
382 true,
384 )
385 .await?;
386
387 let max_depth = self
389 .limits
390 .as_ref()
391 .map_or(usize::MAX, |l| l.max_move_value_depth);
392
393 Ok(context.resolve_type_layout(&tag, max_depth)?.0)
394 }
395
396 pub async fn abilities(&self, mut tag: TypeTag) -> Result<AbilitySet> {
403 let mut context = ResolutionContext::new(self.limits.as_ref());
404
405 context
408 .add_type_tag(
409 &mut tag,
410 &self.package_store,
411 false,
413 false,
415 )
416 .await?;
417
418 context.resolve_abilities(&tag)
420 }
421
422 pub async fn function_parameters(
425 &self,
426 pkg: AccountAddress,
427 module: &str,
428 function: &str,
429 ) -> Result<Vec<OpenSignature>> {
430 let mut context = ResolutionContext::new(self.limits.as_ref());
431
432 let package = self.package_store.fetch(pkg).await?;
433 let Some(def) = package.module(module)?.function_def(function)? else {
434 return Err(Error::FunctionNotFound(
435 pkg,
436 module.to_string(),
437 function.to_string(),
438 ));
439 };
440
441 let mut sigs = def.parameters.clone();
442
443 for sig in &sigs {
446 context
447 .add_signature(
448 sig.body.clone(),
449 &self.package_store,
450 package.as_ref(),
451 false,
453 )
454 .await?;
455 }
456
457 for sig in &mut sigs {
459 context.relocate_signature(&mut sig.body)?;
460 }
461
462 Ok(sigs)
463 }
464
465 pub async fn pure_input_layouts(
477 &self,
478 tx: &ProgrammableTransaction,
479 ) -> Result<Vec<Option<MoveTypeLayout>>> {
480 let mut tags = vec![None; tx.inputs.len()];
481 let mut register_type = |arg: &Argument, tag: &TypeTag| {
482 let &Argument::Input(ix) = arg else {
483 return Ok(());
484 };
485
486 if !matches!(tx.inputs.get(ix as usize), Some(CallArg::Pure(_))) {
487 return Ok(());
488 }
489
490 let Some(type_) = tags.get_mut(ix as usize) else {
491 return Ok(());
492 };
493
494 match type_ {
495 None => *type_ = Some(tag.clone()),
496 Some(prev) => {
497 if prev != tag {
498 return Err(Error::InputTypeConflict(ix, prev.clone(), tag.clone()));
499 }
500 }
501 }
502
503 Ok(())
504 };
505
506 for cmd in &tx.commands {
508 match cmd {
509 Command::MoveCall(call) => {
510 let Ok(params) = self
511 .function_parameters(
512 call.package.into(),
513 call.module.as_str(),
514 call.function.as_str(),
515 )
516 .await
517 else {
518 continue;
519 };
520
521 for (open_sig, arg) in params.iter().zip(call.arguments.iter()) {
522 let sig = open_sig.instantiate(&call.type_arguments)?;
523 register_type(arg, &sig.body)?;
524 }
525 }
526
527 Command::TransferObjects(_, arg) => register_type(arg, &TypeTag::Address)?,
528
529 Command::SplitCoins(_, amounts) => {
530 for amount in amounts {
531 register_type(amount, &TypeTag::U64)?;
532 }
533 }
534
535 Command::MakeMoveVec(Some(tag), elems) if is_primitive_type_tag(tag) => {
536 for elem in elems {
537 register_type(elem, tag)?;
538 }
539 }
540
541 _ => { }
542 }
543 }
544
545 let unique_tags: BTreeSet<_> = tags.iter().filter_map(|t| t.clone()).collect();
549
550 let mut layouts = BTreeMap::new();
552 for tag in unique_tags {
553 let layout = self.type_layout(tag.clone()).await?;
554 layouts.insert(tag, layout);
555 }
556
557 Ok(tags
559 .iter()
560 .map(|t| t.as_ref().and_then(|t| layouts.get(t).cloned()))
561 .collect())
562 }
563
564 pub async fn resolve_module_id(
573 &self,
574 module_id: ModuleId,
575 context: AccountAddress,
576 ) -> Result<ModuleId> {
577 let package = self.package_store.fetch(context).await?;
578 let storage_id = package.relocate(*module_id.address())?;
579 Ok(ModuleId::new(storage_id, module_id.name().to_owned()))
580 }
581
582 pub async fn resolve_clever_error(
599 &self,
600 module_id: ModuleId,
601 abort_code: u64,
602 ) -> Option<CleverError> {
603 let bitset = ErrorBitset::from_u64(abort_code)?;
604 let package = self.package_store.fetch(*module_id.address()).await.ok()?;
605 let module = package.module(module_id.name().as_str()).ok()?.bytecode();
606 let source_line_number = bitset.line_number()?;
607
608 if bitset.identifier_index().is_none() && bitset.constant_index().is_none() {
610 return Some(CleverError {
611 module_id,
612 error_info: ErrorConstants::None,
613 source_line_number,
614 });
615 } else if bitset.identifier_index().is_none() || bitset.constant_index().is_none() {
616 return None;
617 }
618
619 let error_identifier_constant = module
620 .constant_pool()
621 .get(bitset.identifier_index()? as usize)?;
622 let error_value_constant = module
623 .constant_pool()
624 .get(bitset.constant_index()? as usize)?;
625
626 if !matches!(&error_identifier_constant.type_, SignatureToken::Vector(x) if x.as_ref() == &SignatureToken::U8)
627 {
628 return None;
629 };
630
631 let error_identifier = bcs::from_bytes::<Vec<u8>>(&error_identifier_constant.data)
632 .ok()
633 .and_then(|x| String::from_utf8(x).ok())?;
634 let bytes = error_value_constant.data.clone();
635
636 let rendered = try_render_constant(error_value_constant);
637
638 let error_info = match rendered {
639 RenderResult::NotRendered => ErrorConstants::Raw {
640 identifier: error_identifier,
641 bytes,
642 },
643 RenderResult::AsString(s) | RenderResult::AsValue(s) => ErrorConstants::Rendered {
644 identifier: error_identifier,
645 constant: s,
646 },
647 };
648
649 Some(CleverError {
650 module_id,
651 error_info,
652 source_line_number,
653 })
654 }
655}
656
657impl<T> PackageStoreWithLruCache<T> {
658 pub fn new(inner: T) -> Self {
659 let packages = Mutex::new(LruCache::new(PACKAGE_CACHE_SIZE));
660 Self { packages, inner }
661 }
662
663 pub fn evict(&self, ids: impl IntoIterator<Item = AccountAddress>) {
667 let mut packages = self.packages.lock().unwrap();
668 for id in ids {
669 packages.pop(&id);
670 }
671 }
672}
673
674#[async_trait]
675impl<T: PackageStore> PackageStore for PackageStoreWithLruCache<T> {
676 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
677 if let Some(package) = {
678 let mut packages = self.packages.lock().unwrap();
680 packages.get(&id).cloned()
681 } {
682 return Ok(package);
683 };
684
685 let package = self.inner.fetch(id).await?;
686
687 let mut packages = self.packages.lock().unwrap();
694 Ok(match packages.peek(&id) {
695 Some(prev) if package.version <= prev.version => {
696 let package = prev.clone();
697 packages.promote(&id);
698 package
699 }
700
701 Some(_) | None => {
702 packages.push(id, package.clone());
703 package
704 }
705 })
706 }
707}
708
709impl Package {
710 pub fn read_from_object(object: &Object) -> Result<Self> {
711 let storage_id = AccountAddress::from(object.id());
712 let Some(package) = object.data.try_as_package() else {
713 return Err(Error::NotAPackage(storage_id));
714 };
715
716 Self::read_from_package(package)
717 }
718
719 pub fn read_from_package(package: &MovePackage) -> Result<Self> {
720 let storage_id = AccountAddress::from(package.id());
721 let mut type_origins: BTreeMap<String, BTreeMap<String, AccountAddress>> = BTreeMap::new();
722 for TypeOrigin {
723 module_name,
724 datatype_name,
725 package,
726 } in package.type_origin_table()
727 {
728 type_origins
729 .entry(module_name.to_string())
730 .or_default()
731 .insert(datatype_name.to_string(), AccountAddress::from(*package));
732 }
733
734 let mut runtime_id = None;
735 let mut modules = BTreeMap::new();
736 for (name, bytes) in package.serialized_module_map() {
737 let origins = type_origins.remove(name).unwrap_or_default();
738 let bytecode = CompiledModule::deserialize_with_defaults(bytes)
739 .map_err(|e| Error::Deserialize(e.finish(Location::Undefined)))?;
740
741 runtime_id = Some(*bytecode.address());
742
743 let name = name.clone();
744 match Module::read(bytecode, origins) {
745 Ok(module) => modules.insert(name, module),
746 Err(struct_) => return Err(Error::NoTypeOrigin(storage_id, name, struct_)),
747 };
748 }
749
750 let Some(runtime_id) = runtime_id else {
751 return Err(Error::EmptyPackage(storage_id));
752 };
753
754 let linkage = package
755 .linkage_table()
756 .iter()
757 .map(|(&dep, linkage)| (dep.into(), linkage.upgraded_id.into()))
758 .collect();
759
760 Ok(Package {
761 storage_id,
762 runtime_id,
763 version: package.version(),
764 modules,
765 linkage,
766 })
767 }
768
769 pub fn module(&self, module: &str) -> Result<&Module> {
770 self.modules
771 .get(module)
772 .ok_or_else(|| Error::ModuleNotFound(self.storage_id, module.to_string()))
773 }
774
775 pub fn modules(&self) -> &BTreeMap<String, Module> {
776 &self.modules
777 }
778
779 fn data_def(&self, module_name: &str, datatype_name: &str) -> Result<DataDef> {
780 let module = self.module(module_name)?;
781 let Some(data_def) = module.data_def(datatype_name)? else {
782 return Err(Error::DatatypeNotFound(
783 self.storage_id,
784 module_name.to_string(),
785 datatype_name.to_string(),
786 ));
787 };
788 Ok(data_def)
789 }
790
791 fn relocate(&self, runtime_id: AccountAddress) -> Result<AccountAddress> {
795 if runtime_id == self.runtime_id {
798 return Ok(self.storage_id);
799 }
800
801 self.linkage
802 .get(&runtime_id)
803 .ok_or_else(|| Error::LinkageNotFound(runtime_id))
804 .copied()
805 }
806}
807
808impl Module {
809 fn read(
814 bytecode: CompiledModule,
815 mut origins: BTreeMap<String, AccountAddress>,
816 ) -> std::result::Result<Self, String> {
817 let mut struct_index = BTreeMap::new();
818 for (index, def) in bytecode.struct_defs.iter().enumerate() {
819 let sh = bytecode.datatype_handle_at(def.struct_handle);
820 let struct_ = bytecode.identifier_at(sh.name).to_string();
821 let index = StructDefinitionIndex::new(index as TableIndex);
822
823 let Some(defining_id) = origins.remove(&struct_) else {
824 return Err(struct_);
825 };
826
827 struct_index.insert(struct_, (defining_id, index));
828 }
829
830 let mut enum_index = BTreeMap::new();
831 for (index, def) in bytecode.enum_defs.iter().enumerate() {
832 let eh = bytecode.datatype_handle_at(def.enum_handle);
833 let enum_ = bytecode.identifier_at(eh.name).to_string();
834 let index = EnumDefinitionIndex::new(index as TableIndex);
835
836 let Some(defining_id) = origins.remove(&enum_) else {
837 return Err(enum_);
838 };
839
840 enum_index.insert(enum_, (defining_id, index));
841 }
842
843 let mut function_index = BTreeMap::new();
844 for (index, def) in bytecode.function_defs.iter().enumerate() {
845 let fh = bytecode.function_handle_at(def.function);
846 let function = bytecode.identifier_at(fh.name).to_string();
847 let index = FunctionDefinitionIndex::new(index as TableIndex);
848
849 function_index.insert(function, index);
850 }
851
852 Ok(Module {
853 bytecode,
854 struct_index,
855 enum_index,
856 function_index,
857 })
858 }
859
860 pub fn bytecode(&self) -> &CompiledModule {
861 &self.bytecode
862 }
863
864 pub fn name(&self) -> &str {
866 self.bytecode
867 .identifier_at(self.bytecode.self_handle().name)
868 .as_str()
869 }
870
871 pub fn structs(
874 &self,
875 after: Option<&str>,
876 before: Option<&str>,
877 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
878 use std::ops::Bound as B;
879 self.struct_index
880 .range::<str, _>((
881 after.map_or(B::Unbounded, B::Excluded),
882 before.map_or(B::Unbounded, B::Excluded),
883 ))
884 .map(|(name, _)| name.as_str())
885 }
886
887 pub fn enums(
890 &self,
891 after: Option<&str>,
892 before: Option<&str>,
893 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
894 use std::ops::Bound as B;
895 self.enum_index
896 .range::<str, _>((
897 after.map_or(B::Unbounded, B::Excluded),
898 before.map_or(B::Unbounded, B::Excluded),
899 ))
900 .map(|(name, _)| name.as_str())
901 }
902
903 pub fn datatypes(
907 &self,
908 after: Option<&str>,
909 before: Option<&str>,
910 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
911 let mut names = self
912 .structs(after, before)
913 .chain(self.enums(after, before))
914 .collect::<Vec<_>>();
915 names.sort();
916 names.into_iter()
917 }
918
919 pub fn struct_def(&self, name: &str) -> Result<Option<DataDef>> {
924 let Some(&(defining_id, index)) = self.struct_index.get(name) else {
925 return Ok(None);
926 };
927
928 let struct_def = self.bytecode.struct_def_at(index);
929 let struct_handle = self.bytecode.datatype_handle_at(struct_def.struct_handle);
930 let abilities = struct_handle.abilities;
931 let type_params = struct_handle.type_parameters.clone();
932
933 let fields = match &struct_def.field_information {
934 StructFieldInformation::Native => vec![],
935 StructFieldInformation::Declared(fields) => fields
936 .iter()
937 .map(|f| {
938 Ok((
939 self.bytecode.identifier_at(f.name).to_string(),
940 OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
941 ))
942 })
943 .collect::<Result<_>>()?,
944 };
945
946 Ok(Some(DataDef {
947 defining_id,
948 abilities,
949 type_params,
950 data: MoveData::Struct(fields),
951 }))
952 }
953
954 pub fn enum_def(&self, name: &str) -> Result<Option<DataDef>> {
959 let Some(&(defining_id, index)) = self.enum_index.get(name) else {
960 return Ok(None);
961 };
962
963 let enum_def = self.bytecode.enum_def_at(index);
964 let enum_handle = self.bytecode.datatype_handle_at(enum_def.enum_handle);
965 let abilities = enum_handle.abilities;
966 let type_params = enum_handle.type_parameters.clone();
967
968 let variants = enum_def
969 .variants
970 .iter()
971 .map(|variant| {
972 let name = self
973 .bytecode
974 .identifier_at(variant.variant_name)
975 .to_string();
976 let signatures = variant
977 .fields
978 .iter()
979 .map(|f| {
980 Ok((
981 self.bytecode.identifier_at(f.name).to_string(),
982 OpenSignatureBody::read(&f.signature.0, &self.bytecode)?,
983 ))
984 })
985 .collect::<Result<_>>()?;
986
987 Ok(VariantDef { name, signatures })
988 })
989 .collect::<Result<_>>()?;
990
991 Ok(Some(DataDef {
992 defining_id,
993 abilities,
994 type_params,
995 data: MoveData::Enum(variants),
996 }))
997 }
998
999 pub fn data_def(&self, name: &str) -> Result<Option<DataDef>> {
1004 self.struct_def(name)
1005 .transpose()
1006 .or_else(|| self.enum_def(name).transpose())
1007 .transpose()
1008 }
1009
1010 pub fn functions(
1013 &self,
1014 after: Option<&str>,
1015 before: Option<&str>,
1016 ) -> impl DoubleEndedIterator<Item = &str> + Clone {
1017 use std::ops::Bound as B;
1018 self.function_index
1019 .range::<str, _>((
1020 after.map_or(B::Unbounded, B::Excluded),
1021 before.map_or(B::Unbounded, B::Excluded),
1022 ))
1023 .map(|(name, _)| name.as_str())
1024 }
1025
1026 pub fn function_def(&self, name: &str) -> Result<Option<FunctionDef>> {
1031 let Some(&index) = self.function_index.get(name) else {
1032 return Ok(None);
1033 };
1034
1035 let function_def = self.bytecode.function_def_at(index);
1036 let function_handle = self.bytecode.function_handle_at(function_def.function);
1037
1038 Ok(Some(FunctionDef {
1039 visibility: function_def.visibility,
1040 is_entry: function_def.is_entry,
1041 type_params: function_handle.type_parameters.clone(),
1042 parameters: read_signature(function_handle.parameters, &self.bytecode)?,
1043 return_: read_signature(function_handle.return_, &self.bytecode)?,
1044 }))
1045 }
1046}
1047
1048impl OpenSignature {
1049 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1050 use SignatureToken as S;
1051 Ok(match sig {
1052 S::Reference(sig) => OpenSignature {
1053 ref_: Some(Reference::Immutable),
1054 body: OpenSignatureBody::read(sig, bytecode)?,
1055 },
1056
1057 S::MutableReference(sig) => OpenSignature {
1058 ref_: Some(Reference::Mutable),
1059 body: OpenSignatureBody::read(sig, bytecode)?,
1060 },
1061
1062 sig => OpenSignature {
1063 ref_: None,
1064 body: OpenSignatureBody::read(sig, bytecode)?,
1065 },
1066 })
1067 }
1068
1069 pub fn instantiate(&self, type_params: &[TypeTag]) -> Result<Signature> {
1076 Ok(Signature {
1077 ref_: self.ref_,
1078 body: self.body.instantiate(type_params)?,
1079 })
1080 }
1081}
1082
1083impl OpenSignatureBody {
1084 fn read(sig: &SignatureToken, bytecode: &CompiledModule) -> Result<Self> {
1085 use OpenSignatureBody as O;
1086 use SignatureToken as S;
1087
1088 Ok(match sig {
1089 S::Signer => return Err(Error::UnexpectedSigner),
1090 S::Reference(_) | S::MutableReference(_) => return Err(Error::UnexpectedReference),
1091
1092 S::Address => O::Address,
1093 S::Bool => O::Bool,
1094 S::U8 => O::U8,
1095 S::U16 => O::U16,
1096 S::U32 => O::U32,
1097 S::U64 => O::U64,
1098 S::U128 => O::U128,
1099 S::U256 => O::U256,
1100 S::TypeParameter(ix) => O::TypeParameter(*ix),
1101
1102 S::Vector(sig) => O::Vector(Box::new(OpenSignatureBody::read(sig, bytecode)?)),
1103
1104 S::Datatype(ix) => O::Datatype(DatatypeKey::read(*ix, bytecode), vec![]),
1105 S::DatatypeInstantiation(inst) => {
1106 let (ix, params) = &**inst;
1107 O::Datatype(
1108 DatatypeKey::read(*ix, bytecode),
1109 params
1110 .iter()
1111 .map(|sig| OpenSignatureBody::read(sig, bytecode))
1112 .collect::<Result<_>>()?,
1113 )
1114 }
1115 })
1116 }
1117
1118 fn instantiate(&self, type_params: &[TypeTag]) -> Result<TypeTag> {
1119 use OpenSignatureBody as O;
1120 use TypeTag as T;
1121
1122 Ok(match self {
1123 O::Address => T::Address,
1124 O::Bool => T::Bool,
1125 O::U8 => T::U8,
1126 O::U16 => T::U16,
1127 O::U32 => T::U32,
1128 O::U64 => T::U64,
1129 O::U128 => T::U128,
1130 O::U256 => T::U256,
1131 O::Vector(s) => T::Vector(Box::new(s.instantiate(type_params)?)),
1132
1133 O::Datatype(key, dty_params) => T::Struct(Box::new(StructTag {
1134 address: key.package,
1135 module: ident(&key.module)?,
1136 name: ident(&key.name)?,
1137 type_params: dty_params
1138 .iter()
1139 .map(|p| p.instantiate(type_params))
1140 .collect::<Result<_>>()?,
1141 })),
1142
1143 O::TypeParameter(ix) => type_params
1144 .get(*ix as usize)
1145 .cloned()
1146 .ok_or_else(|| Error::TypeParamOOB(*ix, type_params.len()))?,
1147 })
1148 }
1149}
1150
1151impl DatatypeRef<'_, '_> {
1152 pub fn as_key(&self) -> DatatypeKey {
1153 DatatypeKey {
1154 package: self.package,
1155 module: self.module.to_string().into(),
1156 name: self.name.to_string().into(),
1157 }
1158 }
1159}
1160
1161impl DatatypeKey {
1162 fn read(ix: DatatypeHandleIndex, bytecode: &CompiledModule) -> Self {
1163 let sh = bytecode.datatype_handle_at(ix);
1164 let mh = bytecode.module_handle_at(sh.module);
1165
1166 let package = *bytecode.address_identifier_at(mh.address);
1167 let module = bytecode.identifier_at(mh.name).to_string().into();
1168 let name = bytecode.identifier_at(sh.name).to_string().into();
1169
1170 DatatypeKey {
1171 package,
1172 module,
1173 name,
1174 }
1175 }
1176}
1177
1178impl<'l> ResolutionContext<'l> {
1179 fn new(limits: Option<&'l Limits>) -> Self {
1180 ResolutionContext {
1181 datatypes: BTreeMap::new(),
1182 limits,
1183 }
1184 }
1185
1186 async fn add_type_tag<S: PackageStore + ?Sized>(
1201 &mut self,
1202 tag: &mut TypeTag,
1203 store: &S,
1204 visit_fields: bool,
1205 visit_phantoms: bool,
1206 ) -> Result<()> {
1207 use TypeTag as T;
1208
1209 struct ToVisit<'t> {
1210 tag: &'t mut TypeTag,
1211 depth: usize,
1212 }
1213
1214 let mut frontier = vec![ToVisit { tag, depth: 0 }];
1215 while let Some(ToVisit { tag, depth }) = frontier.pop() {
1216 macro_rules! push_ty_param {
1217 ($tag:expr) => {{
1218 check_max_limit!(
1219 TypeParamNesting, self.limits;
1220 max_type_argument_depth > depth
1221 );
1222
1223 frontier.push(ToVisit { tag: $tag, depth: depth + 1 })
1224 }}
1225 }
1226
1227 match tag {
1228 T::Address
1229 | T::Bool
1230 | T::U8
1231 | T::U16
1232 | T::U32
1233 | T::U64
1234 | T::U128
1235 | T::U256
1236 | T::Signer => {
1237 }
1239
1240 T::Vector(tag) => push_ty_param!(tag),
1241
1242 T::Struct(s) => {
1243 let context = store.fetch(s.address).await?;
1244 let def = context
1245 .clone()
1246 .data_def(s.module.as_str(), s.name.as_str())?;
1247
1248 s.address = context.runtime_id;
1253 let key = DatatypeRef::from(s.as_ref()).as_key();
1254
1255 if def.type_params.len() != s.type_params.len() {
1256 return Err(Error::TypeArityMismatch(
1257 def.type_params.len(),
1258 s.type_params.len(),
1259 ));
1260 }
1261
1262 check_max_limit!(
1263 TooManyTypeParams, self.limits;
1264 max_type_argument_width >= s.type_params.len()
1265 );
1266
1267 for (param, def) in s.type_params.iter_mut().zip(def.type_params.iter()) {
1268 if !def.is_phantom || visit_phantoms {
1269 push_ty_param!(param);
1270 }
1271 }
1272
1273 if self.datatypes.contains_key(&key) {
1274 continue;
1275 }
1276
1277 if visit_fields {
1278 match &def.data {
1279 MoveData::Struct(fields) => {
1280 for (_, sig) in fields {
1281 self.add_signature(sig.clone(), store, &context, visit_fields)
1282 .await?;
1283 }
1284 }
1285 MoveData::Enum(variants) => {
1286 for variant in variants {
1287 for (_, sig) in &variant.signatures {
1288 self.add_signature(
1289 sig.clone(),
1290 store,
1291 &context,
1292 visit_fields,
1293 )
1294 .await?;
1295 }
1296 }
1297 }
1298 };
1299 }
1300
1301 check_max_limit!(
1302 TooManyTypeNodes, self.limits;
1303 max_type_nodes > self.datatypes.len()
1304 );
1305
1306 self.datatypes.insert(key, def);
1307 }
1308 }
1309 }
1310
1311 Ok(())
1312 }
1313
1314 async fn add_signature<T: PackageStore + ?Sized>(
1317 &mut self,
1318 sig: OpenSignatureBody,
1319 store: &T,
1320 context: &Package,
1321 visit_fields: bool,
1322 ) -> Result<()> {
1323 use OpenSignatureBody as O;
1324
1325 let mut frontier = vec![sig];
1326 while let Some(sig) = frontier.pop() {
1327 match sig {
1328 O::Address
1329 | O::Bool
1330 | O::U8
1331 | O::U16
1332 | O::U32
1333 | O::U64
1334 | O::U128
1335 | O::U256
1336 | O::TypeParameter(_) => {
1337 }
1339
1340 O::Vector(sig) => frontier.push(*sig),
1341
1342 O::Datatype(key, params) => {
1343 check_max_limit!(
1344 TooManyTypeParams, self.limits;
1345 max_type_argument_width >= params.len()
1346 );
1347
1348 let params_count = params.len();
1349 let data_count = self.datatypes.len();
1350 frontier.extend(params.into_iter());
1351
1352 let type_params = if let Some(def) = self.datatypes.get(&key) {
1353 &def.type_params
1354 } else {
1355 check_max_limit!(
1356 TooManyTypeNodes, self.limits;
1357 max_type_nodes > data_count
1358 );
1359
1360 let storage_id = context.relocate(key.package)?;
1362 let package = store.fetch(storage_id).await?;
1363
1364 let def = package.data_def(&key.module, &key.name)?;
1365 if visit_fields {
1366 match &def.data {
1367 MoveData::Struct(fields) => {
1368 frontier.extend(fields.iter().map(|f| &f.1).cloned());
1369 }
1370 MoveData::Enum(variants) => {
1371 frontier.extend(
1372 variants
1373 .iter()
1374 .flat_map(|v| v.signatures.iter().map(|(_, s)| s))
1375 .cloned(),
1376 );
1377 }
1378 };
1379 }
1380
1381 &self.datatypes.entry(key).or_insert(def).type_params
1382 };
1383
1384 if type_params.len() != params_count {
1385 return Err(Error::TypeArityMismatch(type_params.len(), params_count));
1386 }
1387 }
1388 }
1389 }
1390
1391 Ok(())
1392 }
1393
1394 fn resolve_type_layout(
1403 &self,
1404 tag: &TypeTag,
1405 max_depth: usize,
1406 ) -> Result<(MoveTypeLayout, usize)> {
1407 use MoveTypeLayout as L;
1408 use TypeTag as T;
1409
1410 if max_depth == 0 {
1411 return Err(Error::ValueNesting(
1412 self.limits.map_or(0, |l| l.max_move_value_depth),
1413 ));
1414 }
1415
1416 Ok(match tag {
1417 T::Signer => return Err(Error::UnexpectedSigner),
1418
1419 T::Address => (L::Address, 1),
1420 T::Bool => (L::Bool, 1),
1421 T::U8 => (L::U8, 1),
1422 T::U16 => (L::U16, 1),
1423 T::U32 => (L::U32, 1),
1424 T::U64 => (L::U64, 1),
1425 T::U128 => (L::U128, 1),
1426 T::U256 => (L::U256, 1),
1427
1428 T::Vector(tag) => {
1429 let (layout, depth) = self.resolve_type_layout(tag, max_depth - 1)?;
1430 (L::Vector(Box::new(layout)), depth + 1)
1431 }
1432
1433 T::Struct(s) => {
1434 let param_layouts = s
1449 .type_params
1450 .iter()
1451 .map(|tag| self.resolve_type_layout(tag, max_depth - 1))
1454 .collect::<Result<Vec<_>>>()?;
1455
1456 let type_params = param_layouts.iter().map(|l| TypeTag::from(&l.0)).collect();
1461
1462 let key = DatatypeRef::from(s.as_ref());
1464 let def = &self.datatypes[&key];
1465
1466 let type_ = StructTag {
1467 address: def.defining_id,
1468 module: s.module.clone(),
1469 name: s.name.clone(),
1470 type_params,
1471 };
1472
1473 self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1474 }
1475 })
1476 }
1477
1478 fn resolve_datatype_signature(
1486 &self,
1487 data_def: &DataDef,
1488 type_: StructTag,
1489 param_layouts: Vec<(MoveTypeLayout, usize)>,
1490 max_depth: usize,
1491 ) -> Result<(MoveTypeLayout, usize)> {
1492 Ok(match &data_def.data {
1493 MoveData::Struct(fields) => {
1494 let mut resolved_fields = Vec::with_capacity(fields.len());
1495 let mut field_depth = 0;
1496
1497 for (name, sig) in fields {
1498 let (layout, depth) =
1499 self.resolve_signature_layout(sig, ¶m_layouts, max_depth - 1)?;
1500
1501 field_depth = field_depth.max(depth);
1502 resolved_fields.push(MoveFieldLayout {
1503 name: ident(name.as_str())?,
1504 layout,
1505 })
1506 }
1507
1508 (
1509 MoveTypeLayout::Struct(Box::new(MoveStructLayout {
1510 type_,
1511 fields: resolved_fields,
1512 })),
1513 field_depth + 1,
1514 )
1515 }
1516 MoveData::Enum(variants) => {
1517 let mut field_depth = 0;
1518 let mut resolved_variants = BTreeMap::new();
1519
1520 for (tag, variant) in variants.iter().enumerate() {
1521 let mut fields = Vec::with_capacity(variant.signatures.len());
1522 for (name, sig) in &variant.signatures {
1523 let (layout, depth) =
1525 self.resolve_signature_layout(sig, ¶m_layouts, max_depth - 1)?;
1526
1527 field_depth = field_depth.max(depth);
1528 fields.push(MoveFieldLayout {
1529 name: ident(name.as_str())?,
1530 layout,
1531 })
1532 }
1533 resolved_variants.insert((ident(variant.name.as_str())?, tag as u16), fields);
1534 }
1535
1536 (
1537 MoveTypeLayout::Enum(Box::new(MoveEnumLayout {
1538 type_,
1539 variants: resolved_variants,
1540 })),
1541 field_depth + 1,
1542 )
1543 }
1544 })
1545 }
1546
1547 fn resolve_signature_layout(
1555 &self,
1556 sig: &OpenSignatureBody,
1557 param_layouts: &[(MoveTypeLayout, usize)],
1558 max_depth: usize,
1559 ) -> Result<(MoveTypeLayout, usize)> {
1560 use MoveTypeLayout as L;
1561 use OpenSignatureBody as O;
1562
1563 if max_depth == 0 {
1564 return Err(Error::ValueNesting(
1565 self.limits.map_or(0, |l| l.max_move_value_depth),
1566 ));
1567 }
1568
1569 Ok(match sig {
1570 O::Address => (L::Address, 1),
1571 O::Bool => (L::Bool, 1),
1572 O::U8 => (L::U8, 1),
1573 O::U16 => (L::U16, 1),
1574 O::U32 => (L::U32, 1),
1575 O::U64 => (L::U64, 1),
1576 O::U128 => (L::U128, 1),
1577 O::U256 => (L::U256, 1),
1578
1579 O::TypeParameter(ix) => {
1580 let (layout, depth) = param_layouts
1581 .get(*ix as usize)
1582 .ok_or_else(|| Error::TypeParamOOB(*ix, param_layouts.len()))
1583 .cloned()?;
1584
1585 if depth > max_depth {
1589 return Err(Error::ValueNesting(
1590 self.limits.map_or(0, |l| l.max_move_value_depth),
1591 ));
1592 }
1593
1594 (layout, depth)
1595 }
1596
1597 O::Vector(sig) => {
1598 let (layout, depth) =
1599 self.resolve_signature_layout(sig.as_ref(), param_layouts, max_depth - 1)?;
1600
1601 (L::Vector(Box::new(layout)), depth + 1)
1602 }
1603
1604 O::Datatype(key, params) => {
1605 let def = &self.datatypes[key];
1607
1608 let param_layouts = params
1609 .iter()
1610 .map(|sig| self.resolve_signature_layout(sig, param_layouts, max_depth - 1))
1611 .collect::<Result<Vec<_>>>()?;
1612
1613 let type_params: Vec<TypeTag> =
1618 param_layouts.iter().map(|l| TypeTag::from(&l.0)).collect();
1619
1620 let type_ = StructTag {
1621 address: def.defining_id,
1622 module: ident(&key.module)?,
1623 name: ident(&key.name)?,
1624 type_params,
1625 };
1626
1627 self.resolve_datatype_signature(def, type_, param_layouts, max_depth)?
1628 }
1629 })
1630 }
1631
1632 fn resolve_abilities(&self, tag: &TypeTag) -> Result<AbilitySet> {
1636 use TypeTag as T;
1637 Ok(match tag {
1638 T::Signer => return Err(Error::UnexpectedSigner),
1639
1640 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => {
1641 AbilitySet::PRIMITIVES
1642 }
1643
1644 T::Vector(tag) => self.resolve_abilities(tag)?.intersect(AbilitySet::VECTOR),
1645
1646 T::Struct(s) => {
1647 let key = DatatypeRef::from(s.as_ref());
1649 let def = &self.datatypes[&key];
1650
1651 if def.type_params.len() != s.type_params.len() {
1652 return Err(Error::TypeArityMismatch(
1653 def.type_params.len(),
1654 s.type_params.len(),
1655 ));
1656 }
1657
1658 let param_abilities: Result<Vec<AbilitySet>> = s
1659 .type_params
1660 .iter()
1661 .zip(def.type_params.iter())
1662 .map(|(p, d)| {
1663 if d.is_phantom {
1664 Ok(AbilitySet::EMPTY)
1665 } else {
1666 self.resolve_abilities(p)
1667 }
1668 })
1669 .collect();
1670
1671 AbilitySet::polymorphic_abilities(
1672 def.abilities,
1673 def.type_params.iter().map(|p| p.is_phantom),
1674 param_abilities?.into_iter(),
1675 )
1676 .map_err(|e| Error::Unexpected(Arc::new(e)))?
1679 }
1680 })
1681 }
1682
1683 fn relocate_signature(&self, sig: &mut OpenSignatureBody) -> Result<()> {
1688 use OpenSignatureBody as O;
1689
1690 match sig {
1691 O::Address | O::Bool | O::U8 | O::U16 | O::U32 | O::U64 | O::U128 | O::U256 => {
1692 }
1694
1695 O::TypeParameter(_) => { }
1696
1697 O::Vector(sig) => self.relocate_signature(sig.as_mut())?,
1698
1699 O::Datatype(key, params) => {
1700 let defining_id = &self.datatypes[key].defining_id;
1702 for param in params {
1703 self.relocate_signature(param)?;
1704 }
1705
1706 key.package = *defining_id;
1707 }
1708 }
1709
1710 Ok(())
1711 }
1712}
1713
1714impl<'s> From<&'s StructTag> for DatatypeRef<'s, 's> {
1715 fn from(tag: &'s StructTag) -> Self {
1716 DatatypeRef {
1717 package: tag.address,
1718 module: tag.module.as_str().into(),
1719 name: tag.name.as_str().into(),
1720 }
1721 }
1722}
1723
1724fn ident(s: &str) -> Result<Identifier> {
1727 Identifier::new(s).map_err(|_| Error::NotAnIdentifier(s.to_string()))
1728}
1729
1730fn read_signature(idx: SignatureIndex, bytecode: &CompiledModule) -> Result<Vec<OpenSignature>> {
1733 let MoveSignature(tokens) = bytecode.signature_at(idx);
1734 let mut sigs = Vec::with_capacity(tokens.len());
1735
1736 for token in tokens {
1737 sigs.push(OpenSignature::read(token, bytecode)?);
1738 }
1739
1740 Ok(sigs)
1741}
1742
1743#[cfg(test)]
1744mod tests {
1745 use std::{
1746 path::PathBuf,
1747 str::FromStr,
1748 sync::{Arc, RwLock},
1749 };
1750
1751 use async_trait::async_trait;
1752 use iota_move_build::{BuildConfig, CompiledPackage};
1753 use iota_types::{
1754 base_types::random_object_ref,
1755 error::IotaResult,
1756 transaction::{ObjectArg, ProgrammableMoveCall},
1757 };
1758 use move_binary_format::file_format::Ability;
1759 use move_compiler::compiled_unit::NamedCompiledModule;
1760 use move_core_types::ident_str;
1761
1762 use super::*;
1763
1764 fn fmt(struct_layout: MoveTypeLayout, enum_layout: MoveTypeLayout) -> String {
1765 format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",)
1766 }
1767
1768 #[tokio::test]
1771 async fn test_simple_type() {
1772 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1773 let package_resolver = Resolver::new(cache);
1774 let struct_layout = package_resolver
1775 .type_layout(type_("0xa0::m::T0"))
1776 .await
1777 .unwrap();
1778 let enum_layout = package_resolver
1779 .type_layout(type_("0xa0::m::E0"))
1780 .await
1781 .unwrap();
1782 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1783 }
1784
1785 #[tokio::test]
1787 async fn test_cross_module() {
1788 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1789 let resolver = Resolver::new(cache);
1790 let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap();
1791 let enum_layout = resolver.type_layout(type_("0xa0::n::E0")).await.unwrap();
1792 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1793 }
1794
1795 #[tokio::test]
1797 async fn test_cross_package() {
1798 let (_, cache) = package_cache([
1799 (1, build_package("a0").unwrap(), a0_types()),
1800 (1, build_package("b0").unwrap(), b0_types()),
1801 ]);
1802 let resolver = Resolver::new(cache);
1803
1804 let struct_layout = resolver.type_layout(type_("0xb0::m::T0")).await.unwrap();
1805 let enum_layout = resolver.type_layout(type_("0xb0::m::E0")).await.unwrap();
1806 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1807 }
1808
1809 #[tokio::test]
1812 async fn test_upgraded_package() {
1813 let (_, cache) = package_cache([
1814 (1, build_package("a0").unwrap(), a0_types()),
1815 (2, build_package("a1").unwrap(), a1_types()),
1816 ]);
1817 let resolver = Resolver::new(cache);
1818
1819 let struct_layout = resolver.type_layout(type_("0xa1::n::T1")).await.unwrap();
1820 let enum_layout = resolver.type_layout(type_("0xa1::n::E1")).await.unwrap();
1821 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1822 }
1823
1824 #[tokio::test]
1828 async fn test_multiple_linkage_contexts() {
1829 let (_, cache) = package_cache([
1830 (1, build_package("a0").unwrap(), a0_types()),
1831 (2, build_package("a1").unwrap(), a1_types()),
1832 ]);
1833 let resolver = Resolver::new(cache);
1834
1835 let struct_layout = resolver
1836 .type_layout(type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"))
1837 .await
1838 .unwrap();
1839 let enum_layout = resolver
1840 .type_layout(type_("0xa0::m::E1<0xa0::m::E0, 0xa1::m::E3>"))
1841 .await
1842 .unwrap();
1843 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1844 }
1845
1846 #[tokio::test]
1852 async fn test_upgraded_package_non_defining_id() {
1853 let (_, cache) = package_cache([
1854 (1, build_package("a0").unwrap(), a0_types()),
1855 (2, build_package("a1").unwrap(), a1_types()),
1856 ]);
1857 let resolver = Resolver::new(cache);
1858
1859 let struct_layout = resolver
1860 .type_layout(type_("0xa1::m::T1<0xa1::m::T3, 0xa1::m::T0>"))
1861 .await
1862 .unwrap();
1863 let enum_layout = resolver
1864 .type_layout(type_("0xa1::m::E1<0xa1::m::E3, 0xa1::m::E0>"))
1865 .await
1866 .unwrap();
1867 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1868 }
1869
1870 #[tokio::test]
1874 async fn test_relinking() {
1875 let (_, cache) = package_cache([
1876 (1, build_package("a0").unwrap(), a0_types()),
1877 (2, build_package("a1").unwrap(), a1_types()),
1878 (1, build_package("b0").unwrap(), b0_types()),
1879 (1, build_package("c0").unwrap(), c0_types()),
1880 ]);
1881 let resolver = Resolver::new(cache);
1882
1883 let struct_layout = resolver.type_layout(type_("0xc0::m::T0")).await.unwrap();
1884 let enum_layout = resolver.type_layout(type_("0xc0::m::E0")).await.unwrap();
1885 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1886 }
1887
1888 #[tokio::test]
1889 async fn test_value_nesting_boundary() {
1890 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1891
1892 let resolver = Resolver::new_with_limits(
1893 cache,
1894 Limits {
1895 max_type_argument_width: 100,
1896 max_type_argument_depth: 100,
1897 max_type_nodes: 100,
1898 max_move_value_depth: 3,
1899 },
1900 );
1901
1902 let struct_layout = resolver
1904 .type_layout(type_("0xa0::m::T1<u8, u8>"))
1905 .await
1906 .unwrap();
1907 let enum_layout = resolver
1908 .type_layout(type_("0xa0::m::E1<u8, u8>"))
1909 .await
1910 .unwrap();
1911 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
1912 }
1913
1914 #[tokio::test]
1915 async fn test_err_value_nesting_simple() {
1916 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1917
1918 let resolver = Resolver::new_with_limits(
1919 cache,
1920 Limits {
1921 max_type_argument_width: 100,
1922 max_type_argument_depth: 100,
1923 max_type_nodes: 100,
1924 max_move_value_depth: 2,
1925 },
1926 );
1927
1928 let struct_err = resolver
1930 .type_layout(type_("0xa0::m::T1<u8, u8>"))
1931 .await
1932 .unwrap_err();
1933 let enum_err = resolver
1934 .type_layout(type_("0xa0::m::E1<u8, u8>"))
1935 .await
1936 .unwrap_err();
1937 assert!(matches!(struct_err, Error::ValueNesting(2)));
1938 assert!(matches!(enum_err, Error::ValueNesting(2)));
1939 }
1940
1941 #[tokio::test]
1942 async fn test_err_value_nesting_big_type_param() {
1943 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
1944
1945 let resolver = Resolver::new_with_limits(
1946 cache,
1947 Limits {
1948 max_type_argument_width: 100,
1949 max_type_argument_depth: 100,
1950 max_type_nodes: 100,
1951 max_move_value_depth: 3,
1952 },
1953 );
1954
1955 let struct_err = resolver
1959 .type_layout(type_("0xa0::m::T1<vector<vector<u8>>, u8>"))
1960 .await
1961 .unwrap_err();
1962 let enum_err = resolver
1963 .type_layout(type_("0xa0::m::E1<vector<vector<u8>>, u8>"))
1964 .await
1965 .unwrap_err();
1966 assert!(matches!(struct_err, Error::ValueNesting(3)));
1967 assert!(matches!(enum_err, Error::ValueNesting(3)));
1968 }
1969
1970 #[tokio::test]
1971 async fn test_err_value_nesting_big_phantom_type_param() {
1972 let (_, cache) = package_cache([
1973 (1, build_package("iota").unwrap(), iota_types()),
1974 (1, build_package("d0").unwrap(), d0_types()),
1975 ]);
1976
1977 let resolver = Resolver::new_with_limits(
1978 cache,
1979 Limits {
1980 max_type_argument_width: 100,
1981 max_type_argument_depth: 100,
1982 max_type_nodes: 100,
1983 max_move_value_depth: 3,
1984 },
1985 );
1986
1987 let _ = resolver
1989 .type_layout(type_("0xd0::m::O<u8, u8>"))
1990 .await
1991 .unwrap();
1992 let _ = resolver
1993 .type_layout(type_("0xd0::m::EO<u8, u8>"))
1994 .await
1995 .unwrap();
1996
1997 let struct_err = resolver
2002 .type_layout(type_("0xd0::m::O<u8, vector<vector<u8>>>"))
2003 .await
2004 .unwrap_err();
2005 let enum_err = resolver
2006 .type_layout(type_("0xd0::m::EO<u8, vector<vector<u8>>>"))
2007 .await
2008 .unwrap_err();
2009 assert!(matches!(struct_err, Error::ValueNesting(3)));
2010 assert!(matches!(enum_err, Error::ValueNesting(3)));
2011 }
2012
2013 #[tokio::test]
2014 async fn test_err_value_nesting_type_param_application() {
2015 let (_, cache) = package_cache([
2016 (1, build_package("iota").unwrap(), iota_types()),
2017 (1, build_package("d0").unwrap(), d0_types()),
2018 ]);
2019
2020 let resolver = Resolver::new_with_limits(
2021 cache,
2022 Limits {
2023 max_type_argument_width: 100,
2024 max_type_argument_depth: 100,
2025 max_type_nodes: 100,
2026 max_move_value_depth: 3,
2027 },
2028 );
2029
2030 let struct_err = resolver
2034 .type_layout(type_("0xd0::m::O<vector<u8>, u8>"))
2035 .await
2036 .unwrap_err();
2037 let enum_err = resolver
2038 .type_layout(type_("0xd0::m::EO<vector<u8>, u8>"))
2039 .await
2040 .unwrap_err();
2041
2042 assert!(matches!(struct_err, Error::ValueNesting(3)));
2043 assert!(matches!(enum_err, Error::ValueNesting(3)));
2044 }
2045
2046 #[tokio::test]
2047 async fn test_system_package_invalidation() {
2048 let (inner, cache) = package_cache([(1, build_package("s0").unwrap(), s0_types())]);
2049 let resolver = Resolver::new(cache);
2050
2051 let struct_not_found = resolver.type_layout(type_("0x1::m::T1")).await.unwrap_err();
2052 let enum_not_found = resolver.type_layout(type_("0x1::m::E1")).await.unwrap_err();
2053 assert!(matches!(struct_not_found, Error::DatatypeNotFound(_, _, _)));
2054 assert!(matches!(enum_not_found, Error::DatatypeNotFound(_, _, _)));
2055
2056 inner.write().unwrap().replace(
2058 addr("0x1"),
2059 cached_package(
2060 2,
2061 BTreeMap::new(),
2062 &build_package("s1").unwrap(),
2063 &s1_types(),
2064 ),
2065 );
2066
2067 resolver.package_store().evict([addr("0x1")]);
2069
2070 let struct_layout = resolver.type_layout(type_("0x1::m::T1")).await.unwrap();
2071 let enum_layout = resolver.type_layout(type_("0x1::m::E1")).await.unwrap();
2072 insta::assert_snapshot!(fmt(struct_layout, enum_layout));
2073 }
2074
2075 #[tokio::test]
2076 async fn test_caching() {
2077 let (inner, cache) = package_cache([
2078 (1, build_package("a0").unwrap(), a0_types()),
2079 (1, build_package("s0").unwrap(), s0_types()),
2080 ]);
2081 let resolver = Resolver::new(cache);
2082
2083 assert_eq!(inner.read().unwrap().fetches, 0);
2084 let l0 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2085
2086 assert_eq!(inner.read().unwrap().fetches, 1);
2088
2089 let l1 = resolver.type_layout(type_("0xa0::m::T0")).await.unwrap();
2091 assert_eq!(format!("{l0}"), format!("{l1}"));
2092 assert_eq!(inner.read().unwrap().fetches, 1);
2093
2094 let l2 = resolver.type_layout(type_("0xa0::m::T2")).await.unwrap();
2096 assert_ne!(format!("{l0}"), format!("{l2}"));
2097 assert_eq!(inner.read().unwrap().fetches, 1);
2098
2099 resolver.type_layout(type_("0xa0::m::E0")).await.unwrap();
2101 assert_eq!(inner.read().unwrap().fetches, 1);
2102
2103 let l3 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2105 assert_eq!(inner.read().unwrap().fetches, 2);
2106
2107 let l4 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2109 assert_eq!(format!("{l3}"), format!("{l4}"));
2110 assert_eq!(inner.read().unwrap().fetches, 2);
2111
2112 let el4 = resolver.type_layout(type_("0x1::m::E0")).await.unwrap();
2114 assert_ne!(format!("{el4}"), format!("{l4}"));
2115 assert_eq!(inner.read().unwrap().fetches, 2);
2116
2117 inner.write().unwrap().replace(
2119 addr("0x1"),
2120 cached_package(
2121 2,
2122 BTreeMap::new(),
2123 &build_package("s1").unwrap(),
2124 &s1_types(),
2125 ),
2126 );
2127
2128 resolver.package_store().evict([addr("0x1")]);
2130
2131 let l5 = resolver.type_layout(type_("0x1::m::T0")).await.unwrap();
2136 assert_eq!(format!("{l4}"), format!("{l5}"));
2137 assert_eq!(inner.read().unwrap().fetches, 3);
2138 }
2139
2140 #[tokio::test]
2141 async fn test_err_not_a_package() {
2142 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2143 let resolver = Resolver::new(cache);
2144 let err = resolver
2145 .type_layout(type_("0x42::m::T0"))
2146 .await
2147 .unwrap_err();
2148 assert!(matches!(err, Error::PackageNotFound(_)));
2149 }
2150
2151 #[tokio::test]
2152 async fn test_err_no_module() {
2153 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2154 let resolver = Resolver::new(cache);
2155 let err = resolver
2156 .type_layout(type_("0xa0::l::T0"))
2157 .await
2158 .unwrap_err();
2159 assert!(matches!(err, Error::ModuleNotFound(_, _)));
2160 }
2161
2162 #[tokio::test]
2163 async fn test_err_no_struct() {
2164 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2165 let resolver = Resolver::new(cache);
2166
2167 let err = resolver
2168 .type_layout(type_("0xa0::m::T9"))
2169 .await
2170 .unwrap_err();
2171 assert!(matches!(err, Error::DatatypeNotFound(_, _, _)));
2172 }
2173
2174 #[tokio::test]
2175 async fn test_err_type_arity() {
2176 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2177 let resolver = Resolver::new(cache);
2178
2179 let err = resolver
2181 .type_layout(type_("0xa0::m::T1<u8>"))
2182 .await
2183 .unwrap_err();
2184 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2185
2186 let err = resolver
2188 .type_layout(type_("0xa0::m::T1<u8, u16, u32>"))
2189 .await
2190 .unwrap_err();
2191 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2192 }
2193
2194 #[tokio::test]
2195 async fn test_structs() {
2196 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2197 let a0 = cache.fetch(addr("0xa0")).await.unwrap();
2198 let m = a0.module("m").unwrap();
2199
2200 assert_eq!(
2201 m.structs(None, None).collect::<Vec<_>>(),
2202 vec!["T0", "T1", "T2"],
2203 );
2204
2205 assert_eq!(m.structs(None, Some("T1")).collect::<Vec<_>>(), vec!["T0"],);
2206
2207 assert_eq!(
2208 m.structs(Some("T0"), Some("T2")).collect::<Vec<_>>(),
2209 vec!["T1"],
2210 );
2211
2212 assert_eq!(m.structs(Some("T1"), None).collect::<Vec<_>>(), vec!["T2"],);
2213
2214 let t0 = m.struct_def("T0").unwrap().unwrap();
2215 let t1 = m.struct_def("T1").unwrap().unwrap();
2216 let t2 = m.struct_def("T2").unwrap().unwrap();
2217
2218 insta::assert_snapshot!(format!(
2219 "a0::m::T0: {t0:#?}\n\
2220 a0::m::T1: {t1:#?}\n\
2221 a0::m::T2: {t2:#?}",
2222 ));
2223 }
2224
2225 #[tokio::test]
2226 async fn test_enums() {
2227 let (_, cache) = package_cache([(1, build_package("a0").unwrap(), a0_types())]);
2228 let a0 = cache
2229 .fetch(AccountAddress::from_str("0xa0").unwrap())
2230 .await
2231 .unwrap();
2232 let m = a0.module("m").unwrap();
2233
2234 assert_eq!(
2235 m.enums(None, None).collect::<Vec<_>>(),
2236 vec!["E0", "E1", "E2"],
2237 );
2238
2239 assert_eq!(m.enums(None, Some("E1")).collect::<Vec<_>>(), vec!["E0"],);
2240
2241 assert_eq!(
2242 m.enums(Some("E0"), Some("E2")).collect::<Vec<_>>(),
2243 vec!["E1"],
2244 );
2245
2246 assert_eq!(m.enums(Some("E1"), None).collect::<Vec<_>>(), vec!["E2"],);
2247
2248 let e0 = m.enum_def("E0").unwrap().unwrap();
2249 let e1 = m.enum_def("E1").unwrap().unwrap();
2250 let e2 = m.enum_def("E2").unwrap().unwrap();
2251
2252 insta::assert_snapshot!(format!(
2253 "a0::m::E0: {e0:#?}\n\
2254 a0::m::E1: {e1:#?}\n\
2255 a0::m::E2: {e2:#?}",
2256 ));
2257 }
2258
2259 #[tokio::test]
2260 async fn test_functions() {
2261 let (_, cache) = package_cache([
2262 (1, build_package("a0").unwrap(), a0_types()),
2263 (2, build_package("a1").unwrap(), a1_types()),
2264 (1, build_package("b0").unwrap(), b0_types()),
2265 (1, build_package("c0").unwrap(), c0_types()),
2266 ]);
2267
2268 let c0 = cache.fetch(addr("0xc0")).await.unwrap();
2269 let m = c0.module("m").unwrap();
2270
2271 assert_eq!(
2272 m.functions(None, None).collect::<Vec<_>>(),
2273 vec!["bar", "baz", "foo"],
2274 );
2275
2276 assert_eq!(
2277 m.functions(None, Some("baz")).collect::<Vec<_>>(),
2278 vec!["bar"],
2279 );
2280
2281 assert_eq!(
2282 m.functions(Some("bar"), Some("foo")).collect::<Vec<_>>(),
2283 vec!["baz"],
2284 );
2285
2286 assert_eq!(
2287 m.functions(Some("baz"), None).collect::<Vec<_>>(),
2288 vec!["foo"],
2289 );
2290
2291 let foo = m.function_def("foo").unwrap().unwrap();
2292 let bar = m.function_def("bar").unwrap().unwrap();
2293 let baz = m.function_def("baz").unwrap().unwrap();
2294
2295 insta::assert_snapshot!(format!(
2296 "c0::m::foo: {foo:#?}\n\
2297 c0::m::bar: {bar:#?}\n\
2298 c0::m::baz: {baz:#?}"
2299 ));
2300 }
2301
2302 #[tokio::test]
2303 async fn test_function_parameters() {
2304 let (_, cache) = package_cache([
2305 (1, build_package("a0").unwrap(), a0_types()),
2306 (2, build_package("a1").unwrap(), a1_types()),
2307 (1, build_package("b0").unwrap(), b0_types()),
2308 (1, build_package("c0").unwrap(), c0_types()),
2309 ]);
2310
2311 let resolver = Resolver::new(cache);
2312 let c0 = addr("0xc0");
2313
2314 let foo = resolver.function_parameters(c0, "m", "foo").await.unwrap();
2315 let bar = resolver.function_parameters(c0, "m", "bar").await.unwrap();
2316 let baz = resolver.function_parameters(c0, "m", "baz").await.unwrap();
2317
2318 insta::assert_snapshot!(format!(
2319 "c0::m::foo: {foo:#?}\n\
2320 c0::m::bar: {bar:#?}\n\
2321 c0::m::baz: {baz:#?}"
2322 ));
2323 }
2324
2325 #[tokio::test]
2326 async fn test_signature_instantiation() {
2327 use OpenSignatureBody as O;
2328 use TypeTag as T;
2329
2330 let sig = O::Datatype(
2331 key("0x2::table::Table"),
2332 vec![
2333 O::TypeParameter(1),
2334 O::Vector(Box::new(O::Datatype(
2335 key("0x1::option::Option"),
2336 vec![O::TypeParameter(0)],
2337 ))),
2338 ],
2339 );
2340
2341 insta::assert_debug_snapshot!(sig.instantiate(&[T::U64, T::Bool]).unwrap());
2342 }
2343
2344 #[tokio::test]
2345 async fn test_signature_instantiation_error() {
2346 use OpenSignatureBody as O;
2347 use TypeTag as T;
2348
2349 let sig = O::Datatype(
2350 key("0x2::table::Table"),
2351 vec![
2352 O::TypeParameter(1),
2353 O::Vector(Box::new(O::Datatype(
2354 key("0x1::option::Option"),
2355 vec![O::TypeParameter(99)],
2356 ))),
2357 ],
2358 );
2359
2360 insta::assert_snapshot!(
2361 sig.instantiate(&[T::U64, T::Bool]).unwrap_err(),
2362 @"Type Parameter 99 out of bounds (2)"
2363 );
2364 }
2365
2366 #[tokio::test]
2368 async fn test_primitive_abilities() {
2369 use Ability as A;
2370 use AbilitySet as S;
2371
2372 let (_, cache) = package_cache([]);
2373 let resolver = Resolver::new(cache);
2374
2375 for prim in ["address", "bool", "u8", "u16", "u32", "u64", "u128", "u256"] {
2376 assert_eq!(
2377 resolver.abilities(type_(prim)).await.unwrap(),
2378 S::EMPTY | A::Copy | A::Drop | A::Store,
2379 "Unexpected primitive abilities for: {prim}",
2380 );
2381 }
2382 }
2383
2384 #[tokio::test]
2386 async fn test_simple_generic_abilities() {
2387 use Ability as A;
2388 use AbilitySet as S;
2389
2390 let (_, cache) = package_cache([
2391 (1, build_package("iota").unwrap(), iota_types()),
2392 (1, build_package("d0").unwrap(), d0_types()),
2393 ]);
2394 let resolver = Resolver::new(cache);
2395
2396 let a1 = resolver
2397 .abilities(type_("0xd0::m::T<u32, u64>"))
2398 .await
2399 .unwrap();
2400 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop | A::Store);
2401
2402 let a2 = resolver
2403 .abilities(type_("0xd0::m::T<0xd0::m::S, u64>"))
2404 .await
2405 .unwrap();
2406 assert_eq!(a2, S::EMPTY | A::Drop | A::Store);
2407
2408 let a3 = resolver
2409 .abilities(type_("0xd0::m::T<0xd0::m::R, 0xd0::m::S>"))
2410 .await
2411 .unwrap();
2412 assert_eq!(a3, S::EMPTY | A::Drop);
2413
2414 let a4 = resolver
2415 .abilities(type_("0xd0::m::T<0xd0::m::Q, 0xd0::m::R>"))
2416 .await
2417 .unwrap();
2418 assert_eq!(a4, S::EMPTY);
2419 }
2420
2421 #[tokio::test]
2423 async fn test_nested_generic_abilities() {
2424 use Ability as A;
2425 use AbilitySet as S;
2426
2427 let (_, cache) = package_cache([
2428 (1, build_package("iota").unwrap(), iota_types()),
2429 (1, build_package("d0").unwrap(), d0_types()),
2430 ]);
2431 let resolver = Resolver::new(cache);
2432
2433 let a1 = resolver
2434 .abilities(type_("0xd0::m::T<0xd0::m::T<0xd0::m::R, u32>, u64>"))
2435 .await
2436 .unwrap();
2437 assert_eq!(a1, S::EMPTY | A::Copy | A::Drop);
2438 }
2439
2440 #[tokio::test]
2443 async fn test_key_abilities() {
2444 use Ability as A;
2445 use AbilitySet as S;
2446
2447 let (_, cache) = package_cache([
2448 (1, build_package("iota").unwrap(), iota_types()),
2449 (1, build_package("d0").unwrap(), d0_types()),
2450 ]);
2451 let resolver = Resolver::new(cache);
2452
2453 let a1 = resolver
2454 .abilities(type_("0xd0::m::O<u32, u64>"))
2455 .await
2456 .unwrap();
2457 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2458
2459 let a2 = resolver
2460 .abilities(type_("0xd0::m::O<0xd0::m::S, u64>"))
2461 .await
2462 .unwrap();
2463 assert_eq!(a2, S::EMPTY | A::Key | A::Store);
2464
2465 let a3 = resolver
2468 .abilities(type_("0xd0::m::O<0xd0::m::R, u64>"))
2469 .await
2470 .unwrap();
2471 assert_eq!(a3, S::EMPTY);
2472
2473 let a4 = resolver
2475 .abilities(type_("0xd0::m::O<0xd0::m::P, u32>"))
2476 .await
2477 .unwrap();
2478 assert_eq!(a4, S::EMPTY);
2479 }
2480
2481 #[tokio::test]
2483 async fn test_phantom_abilities() {
2484 use Ability as A;
2485 use AbilitySet as S;
2486
2487 let (_, cache) = package_cache([
2488 (1, build_package("iota").unwrap(), iota_types()),
2489 (1, build_package("d0").unwrap(), d0_types()),
2490 ]);
2491 let resolver = Resolver::new(cache);
2492
2493 let a1 = resolver
2494 .abilities(type_("0xd0::m::O<u32, 0xd0::m::R>"))
2495 .await
2496 .unwrap();
2497 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2498 }
2499
2500 #[tokio::test]
2501 async fn test_err_ability_arity() {
2502 let (_, cache) = package_cache([
2503 (1, build_package("iota").unwrap(), iota_types()),
2504 (1, build_package("d0").unwrap(), d0_types()),
2505 ]);
2506 let resolver = Resolver::new(cache);
2507
2508 let err = resolver
2510 .abilities(type_("0xd0::m::T<u8>"))
2511 .await
2512 .unwrap_err();
2513 assert!(matches!(err, Error::TypeArityMismatch(2, 1)));
2514
2515 let err = resolver
2517 .abilities(type_("0xd0::m::T<u8, u16, u32>"))
2518 .await
2519 .unwrap_err();
2520 assert!(matches!(err, Error::TypeArityMismatch(2, 3)));
2521 }
2522
2523 #[tokio::test]
2524 async fn test_err_ability_signer() {
2525 let (_, cache) = package_cache([]);
2526 let resolver = Resolver::new(cache);
2527
2528 let err = resolver.abilities(type_("signer")).await.unwrap_err();
2529 assert!(matches!(err, Error::UnexpectedSigner));
2530 }
2531
2532 #[tokio::test]
2533 async fn test_err_too_many_type_params() {
2534 let (_, cache) = package_cache([
2535 (1, build_package("iota").unwrap(), iota_types()),
2536 (1, build_package("d0").unwrap(), d0_types()),
2537 ]);
2538
2539 let resolver = Resolver::new_with_limits(
2540 cache,
2541 Limits {
2542 max_type_argument_width: 1,
2543 max_type_argument_depth: 100,
2544 max_type_nodes: 100,
2545 max_move_value_depth: 100,
2546 },
2547 );
2548
2549 let err = resolver
2550 .abilities(type_("0xd0::m::O<u32, u64>"))
2551 .await
2552 .unwrap_err();
2553 assert!(matches!(err, Error::TooManyTypeParams(1, 2)));
2554 }
2555
2556 #[tokio::test]
2557 async fn test_err_too_many_type_nodes() {
2558 use Ability as A;
2559 use AbilitySet as S;
2560
2561 let (_, cache) = package_cache([
2562 (1, build_package("iota").unwrap(), iota_types()),
2563 (1, build_package("d0").unwrap(), d0_types()),
2564 ]);
2565
2566 let resolver = Resolver::new_with_limits(
2567 cache,
2568 Limits {
2569 max_type_argument_width: 100,
2570 max_type_argument_depth: 100,
2571 max_type_nodes: 2,
2572 max_move_value_depth: 100,
2573 },
2574 );
2575
2576 let a1 = resolver
2579 .abilities(type_("0xd0::m::O<0xd0::m::S, 0xd0::m::Q>"))
2580 .await
2581 .unwrap();
2582 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2583
2584 let err = resolver
2586 .abilities(type_("0xd0::m::T<0xd0::m::P, 0xd0::m::Q>"))
2587 .await
2588 .unwrap_err();
2589 assert!(matches!(err, Error::TooManyTypeNodes(2, _)));
2590 }
2591
2592 #[tokio::test]
2593 async fn test_err_type_param_nesting() {
2594 use Ability as A;
2595 use AbilitySet as S;
2596
2597 let (_, cache) = package_cache([
2598 (1, build_package("iota").unwrap(), iota_types()),
2599 (1, build_package("d0").unwrap(), d0_types()),
2600 ]);
2601
2602 let resolver = Resolver::new_with_limits(
2603 cache,
2604 Limits {
2605 max_type_argument_width: 100,
2606 max_type_argument_depth: 2,
2607 max_type_nodes: 100,
2608 max_move_value_depth: 100,
2609 },
2610 );
2611
2612 let a1 = resolver
2615 .abilities(type_(
2616 "0xd0::m::O<0xd0::m::S, 0xd0::m::T<vector<u32>, vector<u64>>>",
2617 ))
2618 .await
2619 .unwrap();
2620 assert_eq!(a1, S::EMPTY | A::Key | A::Store);
2621
2622 let err = resolver
2624 .abilities(type_("vector<0xd0::m::T<0xd0::m::O<u64, u32>, u16>>"))
2625 .await
2626 .unwrap_err();
2627 assert!(matches!(err, Error::TypeParamNesting(2, _)));
2628 }
2629
2630 #[tokio::test]
2631 async fn test_pure_input_layouts() {
2632 use CallArg as I;
2633 use ObjectArg::ImmOrOwnedObject as O;
2634 use TypeTag as T;
2635
2636 let (_, cache) = package_cache([
2637 (1, build_package("std").unwrap(), std_types()),
2638 (1, build_package("iota").unwrap(), iota_types()),
2639 (1, build_package("e0").unwrap(), e0_types()),
2640 ]);
2641
2642 let resolver = Resolver::new(cache);
2643
2644 fn ptb(t: TypeTag, y: CallArg) -> ProgrammableTransaction {
2646 ProgrammableTransaction {
2647 inputs: vec![
2648 I::Object(O(random_object_ref())),
2649 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2650 I::Object(O(random_object_ref())),
2651 y,
2652 I::Object(O(random_object_ref())),
2653 I::Pure(bcs::to_bytes("hello").unwrap()),
2654 I::Pure(bcs::to_bytes("world").unwrap()),
2655 ],
2656 commands: vec![Command::MoveCall(Box::new(ProgrammableMoveCall {
2657 package: addr("0xe0").into(),
2658 module: ident_str!("m").to_owned(),
2659 function: ident_str!("foo").to_owned(),
2660 type_arguments: vec![t],
2661 arguments: (0..=6).map(Argument::Input).collect(),
2662 }))],
2663 }
2664 }
2665
2666 let ptb_u64 = ptb(T::U64, I::Pure(bcs::to_bytes(&1u64).unwrap()));
2667
2668 let ptb_opt = ptb(
2669 TypeTag::Struct(Box::new(StructTag {
2670 address: addr("0x1"),
2671 module: ident_str!("option").to_owned(),
2672 name: ident_str!("Option").to_owned(),
2673 type_params: vec![TypeTag::U64],
2674 })),
2675 I::Pure(bcs::to_bytes(&[vec![1u64], vec![], vec![3]]).unwrap()),
2676 );
2677
2678 let ptb_obj = ptb(
2679 TypeTag::Struct(Box::new(StructTag {
2680 address: addr("0xe0"),
2681 module: ident_str!("m").to_owned(),
2682 name: ident_str!("O").to_owned(),
2683 type_params: vec![],
2684 })),
2685 I::Object(O(random_object_ref())),
2686 );
2687
2688 let inputs_u64 = resolver.pure_input_layouts(&ptb_u64).await.unwrap();
2689 let inputs_opt = resolver.pure_input_layouts(&ptb_opt).await.unwrap();
2690 let inputs_obj = resolver.pure_input_layouts(&ptb_obj).await.unwrap();
2691
2692 let mut output = "---\n".to_string();
2694 for inputs in [inputs_u64, inputs_opt, inputs_obj] {
2695 for input in inputs {
2696 if let Some(layout) = input {
2697 output += &format!("{layout:#}\n");
2698 } else {
2699 output += "???\n";
2700 }
2701 }
2702 output += "---\n";
2703 }
2704
2705 insta::assert_snapshot!(output);
2706 }
2707
2708 #[tokio::test]
2711 async fn test_pure_input_layouts_overlapping() {
2712 use CallArg as I;
2713 use ObjectArg::ImmOrOwnedObject as O;
2714 use TypeTag as T;
2715
2716 let (_, cache) = package_cache([
2717 (1, build_package("std").unwrap(), std_types()),
2718 (1, build_package("iota").unwrap(), iota_types()),
2719 (1, build_package("e0").unwrap(), e0_types()),
2720 ]);
2721
2722 let resolver = Resolver::new(cache);
2723
2724 let ptb = ProgrammableTransaction {
2726 inputs: vec![
2727 I::Object(O(random_object_ref())),
2728 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2729 I::Object(O(random_object_ref())),
2730 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2731 I::Object(O(random_object_ref())),
2732 I::Pure(bcs::to_bytes("hello").unwrap()),
2733 I::Pure(bcs::to_bytes("world").unwrap()),
2734 ],
2735 commands: vec![
2736 Command::MoveCall(Box::new(ProgrammableMoveCall {
2737 package: addr("0xe0").into(),
2738 module: ident_str!("m").to_owned(),
2739 function: ident_str!("foo").to_owned(),
2740 type_arguments: vec![T::U64],
2741 arguments: (0..=6).map(Argument::Input).collect(),
2742 })),
2743 Command::MoveCall(Box::new(ProgrammableMoveCall {
2744 package: addr("0xe0").into(),
2745 module: ident_str!("m").to_owned(),
2746 function: ident_str!("foo").to_owned(),
2747 type_arguments: vec![T::U64],
2748 arguments: (0..=6).map(Argument::Input).collect(),
2749 })),
2750 ],
2751 };
2752
2753 let inputs = resolver.pure_input_layouts(&ptb).await.unwrap();
2754
2755 let mut output = String::new();
2757 for input in inputs {
2758 if let Some(layout) = input {
2759 output += &format!("{layout:#}\n");
2760 } else {
2761 output += "???\n";
2762 }
2763 }
2764
2765 insta::assert_snapshot!(output);
2766 }
2767
2768 #[tokio::test]
2769 async fn test_pure_input_layouts_conflicting() {
2770 use CallArg as I;
2771 use ObjectArg::ImmOrOwnedObject as O;
2772 use TypeTag as T;
2773
2774 let (_, cache) = package_cache([
2775 (1, build_package("std").unwrap(), std_types()),
2776 (1, build_package("iota").unwrap(), iota_types()),
2777 (1, build_package("e0").unwrap(), e0_types()),
2778 ]);
2779
2780 let resolver = Resolver::new(cache);
2781
2782 let ptb = ProgrammableTransaction {
2783 inputs: vec![
2784 I::Object(O(random_object_ref())),
2785 I::Pure(bcs::to_bytes(&42u64).unwrap()),
2786 I::Object(O(random_object_ref())),
2787 I::Pure(bcs::to_bytes(&43u64).unwrap()),
2788 I::Object(O(random_object_ref())),
2789 I::Pure(bcs::to_bytes("hello").unwrap()),
2790 I::Pure(bcs::to_bytes("world").unwrap()),
2791 ],
2792 commands: vec![
2793 Command::MoveCall(Box::new(ProgrammableMoveCall {
2794 package: addr("0xe0").into(),
2795 module: ident_str!("m").to_owned(),
2796 function: ident_str!("foo").to_owned(),
2797 type_arguments: vec![T::U64],
2798 arguments: (0..=6).map(Argument::Input).collect(),
2799 })),
2800 Command::MakeMoveVec(Some(T::U32), vec![Argument::Input(3)]),
2803 ],
2804 };
2805
2806 insta::assert_snapshot!(
2807 resolver.pure_input_layouts(&ptb).await.unwrap_err(),
2808 @"Conflicting types for input 3: u64 and u32"
2809 );
2810 }
2811
2812 type TypeOriginTable = Vec<DatatypeKey>;
2817
2818 fn a0_types() -> TypeOriginTable {
2819 vec![
2820 datakey("0xa0", "m", "T0"),
2821 datakey("0xa0", "m", "T1"),
2822 datakey("0xa0", "m", "T2"),
2823 datakey("0xa0", "m", "E0"),
2824 datakey("0xa0", "m", "E1"),
2825 datakey("0xa0", "m", "E2"),
2826 datakey("0xa0", "n", "T0"),
2827 datakey("0xa0", "n", "E0"),
2828 ]
2829 }
2830
2831 fn a1_types() -> TypeOriginTable {
2832 let mut types = a0_types();
2833
2834 types.extend([
2835 datakey("0xa1", "m", "T3"),
2836 datakey("0xa1", "m", "T4"),
2837 datakey("0xa1", "n", "T1"),
2838 datakey("0xa1", "m", "E3"),
2839 datakey("0xa1", "m", "E4"),
2840 datakey("0xa1", "n", "E1"),
2841 ]);
2842
2843 types
2844 }
2845
2846 fn b0_types() -> TypeOriginTable {
2847 vec![datakey("0xb0", "m", "T0"), datakey("0xb0", "m", "E0")]
2848 }
2849
2850 fn c0_types() -> TypeOriginTable {
2851 vec![datakey("0xc0", "m", "T0"), datakey("0xc0", "m", "E0")]
2852 }
2853
2854 fn d0_types() -> TypeOriginTable {
2855 vec![
2856 datakey("0xd0", "m", "O"),
2857 datakey("0xd0", "m", "P"),
2858 datakey("0xd0", "m", "Q"),
2859 datakey("0xd0", "m", "R"),
2860 datakey("0xd0", "m", "S"),
2861 datakey("0xd0", "m", "T"),
2862 datakey("0xd0", "m", "EO"),
2863 datakey("0xd0", "m", "EP"),
2864 datakey("0xd0", "m", "EQ"),
2865 datakey("0xd0", "m", "ER"),
2866 datakey("0xd0", "m", "ES"),
2867 datakey("0xd0", "m", "ET"),
2868 ]
2869 }
2870
2871 fn e0_types() -> TypeOriginTable {
2872 vec![datakey("0xe0", "m", "O")]
2873 }
2874
2875 fn s0_types() -> TypeOriginTable {
2876 vec![datakey("0x1", "m", "T0"), datakey("0x1", "m", "E0")]
2877 }
2878
2879 fn s1_types() -> TypeOriginTable {
2880 let mut types = s0_types();
2881
2882 types.extend([datakey("0x1", "m", "T1"), datakey("0x1", "m", "E1")]);
2883
2884 types
2885 }
2886
2887 fn iota_types() -> TypeOriginTable {
2888 vec![datakey("0x2", "object", "UID")]
2889 }
2890
2891 fn std_types() -> TypeOriginTable {
2892 vec![
2893 datakey("0x1", "ascii", "String"),
2894 datakey("0x1", "option", "Option"),
2895 datakey("0x1", "string", "String"),
2896 ]
2897 }
2898
2899 fn package_cache(
2905 packages: impl IntoIterator<Item = (u64, CompiledPackage, TypeOriginTable)>,
2906 ) -> (
2907 Arc<RwLock<InnerStore>>,
2908 PackageStoreWithLruCache<InMemoryPackageStore>,
2909 ) {
2910 let packages_by_storage_id: BTreeMap<AccountAddress, _> = packages
2911 .into_iter()
2912 .map(|(version, package, origins)| {
2913 (package_storage_id(&package), (version, package, origins))
2914 })
2915 .collect();
2916
2917 let packages = packages_by_storage_id
2918 .iter()
2919 .map(|(&storage_id, (version, compiled_package, origins))| {
2920 let linkage = compiled_package
2921 .dependency_ids
2922 .published
2923 .values()
2924 .map(|dep_id| {
2925 let storage_id = AccountAddress::from(*dep_id);
2926 let runtime_id = package_runtime_id(
2927 &packages_by_storage_id
2928 .get(&storage_id)
2929 .unwrap_or_else(|| panic!("Dependency {storage_id} not in store"))
2930 .1,
2931 );
2932
2933 (runtime_id, storage_id)
2934 })
2935 .collect();
2936
2937 let package = cached_package(*version, linkage, compiled_package, origins);
2938 (storage_id, package)
2939 })
2940 .collect();
2941
2942 let inner = Arc::new(RwLock::new(InnerStore {
2943 packages,
2944 fetches: 0,
2945 }));
2946
2947 let store = InMemoryPackageStore {
2948 inner: inner.clone(),
2949 };
2950
2951 (inner, PackageStoreWithLruCache::new(store))
2952 }
2953
2954 fn cached_package(
2955 version: u64,
2956 linkage: Linkage,
2957 package: &CompiledPackage,
2958 origins: &TypeOriginTable,
2959 ) -> Package {
2960 let storage_id = package_storage_id(package);
2961 let runtime_id = package_runtime_id(package);
2962 let version = SequenceNumber::from_u64(version);
2963
2964 let mut modules = BTreeMap::new();
2965 for unit in &package.package.root_compiled_units {
2966 let NamedCompiledModule { name, module, .. } = &unit.unit;
2967
2968 let origins = origins
2969 .iter()
2970 .filter(|key| key.module == name.as_str())
2971 .map(|key| (key.name.to_string(), key.package))
2972 .collect();
2973
2974 let module = match Module::read(module.clone(), origins) {
2975 Ok(module) => module,
2976 Err(struct_) => {
2977 panic!("Missing type origin for {}::{struct_}", module.self_id());
2978 }
2979 };
2980
2981 modules.insert(name.to_string(), module);
2982 }
2983
2984 Package {
2985 storage_id,
2986 runtime_id,
2987 linkage,
2988 version,
2989 modules,
2990 }
2991 }
2992
2993 fn package_storage_id(package: &CompiledPackage) -> AccountAddress {
2994 AccountAddress::from(*package.published_at.as_ref().unwrap_or_else(|_| {
2995 panic!(
2996 "Package {} doesn't have published-at set",
2997 package.package.compiled_package_info.package_name,
2998 )
2999 }))
3000 }
3001
3002 fn package_runtime_id(package: &CompiledPackage) -> AccountAddress {
3003 *package
3004 .published_root_module()
3005 .expect("No compiled module")
3006 .address()
3007 }
3008
3009 fn build_package(dir: &str) -> IotaResult<CompiledPackage> {
3010 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3011 path.extend(["tests", "packages", dir]);
3012 BuildConfig::new_for_testing().build(&path)
3013 }
3014
3015 fn addr(a: &str) -> AccountAddress {
3016 AccountAddress::from_str(a).unwrap()
3017 }
3018
3019 fn datakey(a: &str, m: &'static str, n: &'static str) -> DatatypeKey {
3020 DatatypeKey {
3021 package: addr(a),
3022 module: m.into(),
3023 name: n.into(),
3024 }
3025 }
3026
3027 fn type_(t: &str) -> TypeTag {
3028 TypeTag::from_str(t).unwrap()
3029 }
3030
3031 fn key(t: &str) -> DatatypeKey {
3032 let tag = StructTag::from_str(t).unwrap();
3033 DatatypeRef::from(&tag).as_key()
3034 }
3035
3036 struct InMemoryPackageStore {
3037 inner: Arc<RwLock<InnerStore>>,
3040 }
3041
3042 struct InnerStore {
3043 packages: BTreeMap<AccountAddress, Package>,
3044 fetches: usize,
3045 }
3046
3047 #[async_trait]
3048 impl PackageStore for InMemoryPackageStore {
3049 async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
3050 let mut inner = self.inner.as_ref().write().unwrap();
3051 inner.fetches += 1;
3052 inner
3053 .packages
3054 .get(&id)
3055 .cloned()
3056 .ok_or_else(|| Error::PackageNotFound(id))
3057 .map(Arc::new)
3058 }
3059 }
3060
3061 impl InnerStore {
3062 fn replace(&mut self, id: AccountAddress, package: Package) {
3063 self.packages.insert(id, package);
3064 }
3065 }
3066}