1use crate::{
5 data_store::PackageStore,
6 execution_mode::ExecutionMode,
7 execution_value::ExecutionState,
8 static_programmable_transactions::{
9 linkage::{
10 config::{LinkageConfig, ResolutionConfig},
11 resolution::{ResolutionTable, VersionConstraint, add_and_unify, get_package},
12 resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
13 },
14 loading::ast::Type,
15 },
16};
17use move_binary_format::file_format::Visibility;
18use move_core_types::identifier::IdentStr;
19use move_vm_runtime::validation::verification::ast::Package as VerifiedPackage;
20use sui_protocol_config::ProtocolConfig;
21use sui_types::{
22 base_types::ObjectID, error::ExecutionErrorTrait, execution_status::ExecutionErrorKind,
23 transaction::ProgrammableTransaction,
24};
25
26#[derive(Debug)]
27pub struct LinkageAnalyzer {
28 internal: ResolutionConfig,
29}
30
31impl LinkageAnalyzer {
32 pub fn new<Mode: ExecutionMode>(protocol_config: &ProtocolConfig) -> Result<Self, Mode::Error> {
33 let always_include_system_packages = !Mode::packages_are_predefined();
34 let linkage_config = LinkageConfig::new(
35 protocol_config
36 .include_special_package_amendments_as_option()
37 .clone(),
38 always_include_system_packages,
39 );
40 let binary_config = protocol_config.binary_config(None);
41 Ok(Self {
42 internal: ResolutionConfig::new(linkage_config, binary_config),
43 })
44 }
45
46 pub fn compute_call_linkage<E: ExecutionErrorTrait>(
47 &self,
48 package: &ObjectID,
49 module_name: &IdentStr,
50 function_name: &IdentStr,
51 type_args: &[Type],
52 store: &dyn PackageStore,
53 ) -> Result<ExecutableLinkage, E> {
54 Ok(ExecutableLinkage::new(
55 ResolvedLinkage::from_resolution_table(self.compute_call_linkage_(
56 package,
57 module_name,
58 function_name,
59 type_args,
60 store,
61 )?),
62 ))
63 }
64
65 pub fn compute_publication_linkage<E: ExecutionErrorTrait>(
66 &self,
67 deps: &[ObjectID],
68 store: &dyn PackageStore,
69 ) -> Result<ResolvedLinkage, E> {
70 Ok(ResolvedLinkage::from_resolution_table(
71 self.compute_publication_linkage_(deps, store)?,
72 ))
73 }
74
75 pub fn config(&self) -> &ResolutionConfig {
76 &self.internal
77 }
78
79 pub fn compute_input_type_resolution_linkage<E: ExecutionErrorTrait>(
80 &self,
81 tx: &ProgrammableTransaction,
82 package_store: &dyn PackageStore,
83 object_store: &dyn ExecutionState,
84 ) -> Result<ExecutableLinkage, E> {
85 input_type_resolution_analysis::compute_resolution_linkage(
86 self,
87 tx,
88 package_store,
89 object_store,
90 )
91 }
92
93 fn compute_call_linkage_<E: ExecutionErrorTrait>(
94 &self,
95 package: &ObjectID,
96 module_name: &IdentStr,
97 function_name: &IdentStr,
98 type_args: &[Type],
99 store: &dyn PackageStore,
100 ) -> Result<ResolutionTable, E> {
101 let mut resolution_table = self.internal.resolution_table_with_native_packages(store)?;
102
103 fn add_package<E: ExecutionErrorTrait>(
104 object_id: &ObjectID,
105 store: &dyn PackageStore,
106 resolution_table: &mut ResolutionTable,
107 self_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
108 dep_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
109 ) -> Result<(), E> {
110 let pkg = get_package(object_id, store)?;
111 let transitive_deps = resolution_table
112 .config
113 .linkage_table(&pkg)
114 .into_values()
115 .map(ObjectID::from);
116 for object_id in transitive_deps {
117 add_and_unify(&object_id, store, resolution_table, dep_resolution_fn)?;
118 }
119 add_and_unify(object_id, store, resolution_table, self_resolution_fn)?;
120 Ok(())
121 }
122
123 let pkg = get_package(package, store)?;
124 let fn_not_found_err = || -> E {
125 E::new_with_source(
126 ExecutionErrorKind::FunctionNotFound,
127 format!(
128 "Could not resolve function '{}' in module '{}::{}'",
129 function_name, package, module_name
130 ),
131 )
132 };
133 let fdef = pkg
134 .modules()
135 .iter()
136 .find(|m| m.0.name() == module_name)
137 .ok_or_else(fn_not_found_err)?
138 .1
139 .compiled_module()
140 .find_function_def_by_name(function_name.as_str())
141 .ok_or_else(fn_not_found_err)?;
142
143 let dep_resolution_fn = match fdef.1.visibility {
144 Visibility::Public => VersionConstraint::at_least,
145 Visibility::Private | Visibility::Friend => VersionConstraint::exact,
146 };
147
148 add_package(
149 package,
150 store,
151 &mut resolution_table,
152 VersionConstraint::exact,
153 dep_resolution_fn,
154 )?;
155
156 for type_defining_id in type_args.iter().flat_map(|ty| ty.all_addresses()) {
157 add_package(
159 &ObjectID::from(type_defining_id),
160 store,
161 &mut resolution_table,
162 VersionConstraint::at_least,
163 VersionConstraint::at_least,
164 )?;
165 }
166
167 Ok(resolution_table)
168 }
169
170 fn compute_publication_linkage_<E: ExecutionErrorTrait>(
172 &self,
173 deps: &[ObjectID],
174 store: &dyn PackageStore,
175 ) -> Result<ResolutionTable, E> {
176 let mut resolution_table = self.internal.resolution_table_with_native_packages(store)?;
177 for id in deps {
178 add_and_unify(id, store, &mut resolution_table, VersionConstraint::exact)?;
179 }
180 Ok(resolution_table)
181 }
182}
183
184mod input_type_resolution_analysis {
185 use crate::{
186 data_store::PackageStore,
187 execution_value::ExecutionState,
188 static_programmable_transactions::linkage::{
189 analysis::LinkageAnalyzer,
190 resolution::ResolutionTable,
191 resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
192 },
193 };
194 use move_core_types::language_storage::StructTag;
195 use sui_types::{
196 base_types::ObjectID,
197 error::ExecutionErrorTrait,
198 execution_status::ExecutionErrorKind,
199 transaction::{
200 CallArg, Command, FundsWithdrawalArg, ObjectArg, ProgrammableMoveCall,
201 ProgrammableTransaction, WithdrawalTypeArg,
202 },
203 type_input::TypeInput,
204 };
205
206 pub(super) fn compute_resolution_linkage<E: ExecutionErrorTrait>(
207 analyzer: &LinkageAnalyzer,
208 tx: &ProgrammableTransaction,
209 package_store: &dyn PackageStore,
210 object_store: &dyn ExecutionState,
211 ) -> Result<ExecutableLinkage, E> {
212 let ProgrammableTransaction { inputs, commands } = tx;
213
214 let mut resolution_table = analyzer
215 .internal
216 .resolution_table_with_native_packages(package_store)?;
217 for arg in inputs.iter() {
218 input(&mut resolution_table, arg, package_store, object_store)?;
219 }
220
221 for cmd in commands.iter() {
222 command(&mut resolution_table, cmd, package_store)?;
223 }
224
225 Ok(ExecutableLinkage::new(
226 ResolvedLinkage::from_resolution_table(resolution_table),
227 ))
228 }
229
230 fn input<E: ExecutionErrorTrait>(
231 resolution_table: &mut ResolutionTable,
232 arg: &CallArg,
233 package_store: &dyn PackageStore,
234 object_store: &dyn ExecutionState,
235 ) -> Result<(), E> {
236 match arg {
237 CallArg::Pure(_) | CallArg::Object(ObjectArg::Receiving(_)) => (),
238 CallArg::Object(
239 ObjectArg::ImmOrOwnedObject((id, _, _)) | ObjectArg::SharedObject { id, .. },
240 ) => {
241 let Some(obj) = object_store.read_object(id) else {
242 invariant_violation!("Object {:?} not found in object store", id);
243 };
244 let Some(ty) = obj.type_() else {
245 invariant_violation!("Object {:?} has does not have a Move type", id);
246 };
247
248 let tag: StructTag = ty.clone().into();
251 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
252 resolution_table.add_type_linkages_to_table(ids, package_store)?;
253 }
254 CallArg::FundsWithdrawal(f) => {
255 let FundsWithdrawalArg { type_arg, .. } = f;
256 match type_arg {
257 WithdrawalTypeArg::Balance(tag) => {
258 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
259 resolution_table.add_type_linkages_to_table(ids, package_store)?;
260 }
261 }
262 }
263 }
264
265 Ok(())
266 }
267
268 fn command<E: ExecutionErrorTrait>(
269 resolution_table: &mut ResolutionTable,
270 command: &Command,
271 package_store: &dyn PackageStore,
272 ) -> Result<(), E> {
273 let mut add_ty_input = |ty: &TypeInput| -> Result<(), E> {
274 let tag = ty.to_type_tag().map_err(|e| {
275 E::new_with_source(
276 ExecutionErrorKind::InvalidLinkage,
277 format!("Invalid type tag in move call argument: {:?}", e),
278 )
279 })?;
280 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
281 resolution_table.add_type_linkages_to_table(ids, package_store)
282 };
283 match command {
284 Command::MoveCall(pmc) => {
285 let ProgrammableMoveCall {
286 package,
287 type_arguments,
288 ..
289 } = &**pmc;
290 type_arguments.iter().try_for_each(add_ty_input)?;
291 resolution_table.add_type_linkages_to_table([*package], package_store)?;
292 }
293 Command::MakeMoveVec(Some(ty), _) => {
294 add_ty_input(ty)?;
295 }
296 Command::MakeMoveVec(None, _)
297 | Command::TransferObjects(_, _)
298 | Command::SplitCoins(_, _)
299 | Command::MergeCoins(_, _) => (),
300 Command::Publish(_, object_ids) => {
301 resolution_table.add_type_linkages_to_table(object_ids, package_store)?;
302 }
303 Command::Upgrade(_, object_ids, object_id, _) => {
304 resolution_table.add_type_linkages_to_table([*object_id], package_store)?;
305 resolution_table.add_type_linkages_to_table(object_ids, package_store)?;
306 }
307 }
308
309 Ok(())
310 }
311}