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