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::new(
37 protocol_config
38 .include_special_package_amendments_as_option()
39 .clone(),
40 always_include_system_packages,
41 );
42 let binary_config = protocol_config.binary_config(None);
43 Ok(Self {
44 internal: ResolutionConfig::new(linkage_config, binary_config),
45 })
46 }
47
48 pub fn compute_call_linkage(
49 &self,
50 package: &ObjectID,
51 module_name: &IdentStr,
52 function_name: &IdentStr,
53 type_args: &[Type],
54 store: &dyn PackageStore,
55 ) -> Result<ExecutableLinkage, ExecutionError> {
56 Ok(ExecutableLinkage::new(
57 ResolvedLinkage::from_resolution_table(self.compute_call_linkage_(
58 package,
59 module_name,
60 function_name,
61 type_args,
62 store,
63 )?),
64 ))
65 }
66
67 pub fn compute_publication_linkage(
68 &self,
69 deps: &[ObjectID],
70 store: &dyn PackageStore,
71 ) -> Result<ResolvedLinkage, ExecutionError> {
72 Ok(ResolvedLinkage::from_resolution_table(
73 self.compute_publication_linkage_(deps, store)?,
74 ))
75 }
76
77 pub fn config(&self) -> &ResolutionConfig {
78 &self.internal
79 }
80
81 pub fn compute_input_type_resolution_linkage(
82 &self,
83 tx: &ProgrammableTransaction,
84 package_store: &dyn PackageStore,
85 object_store: &dyn ExecutionState,
86 ) -> Result<ExecutableLinkage, ExecutionError> {
87 input_type_resolution_analysis::compute_resolution_linkage(
88 self,
89 tx,
90 package_store,
91 object_store,
92 )
93 }
94
95 fn compute_call_linkage_(
96 &self,
97 package: &ObjectID,
98 module_name: &IdentStr,
99 function_name: &IdentStr,
100 type_args: &[Type],
101 store: &dyn PackageStore,
102 ) -> Result<ResolutionTable, ExecutionError> {
103 let mut resolution_table = self.internal.resolution_table_with_native_packages(store)?;
104
105 fn add_package(
106 object_id: &ObjectID,
107 store: &dyn PackageStore,
108 resolution_table: &mut ResolutionTable,
109 self_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
110 dep_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
111 ) -> Result<(), ExecutionError> {
112 let pkg = get_package(object_id, store)?;
113 let transitive_deps = resolution_table
114 .config
115 .linkage_table(&pkg)
116 .into_values()
117 .map(ObjectID::from);
118 for object_id in transitive_deps {
119 add_and_unify(&object_id, store, resolution_table, dep_resolution_fn)?;
120 }
121 add_and_unify(object_id, store, resolution_table, self_resolution_fn)?;
122 Ok(())
123 }
124
125 let pkg = get_package(package, store)?;
126 let fn_not_found_err = || {
127 ExecutionError::new_with_source(
128 ExecutionErrorKind::FunctionNotFound,
129 format!(
130 "Could not resolve function '{}' in module '{}::{}'",
131 function_name, package, module_name
132 ),
133 )
134 };
135 let fdef = pkg
136 .modules()
137 .iter()
138 .find(|m| m.0.name() == module_name)
139 .ok_or_else(fn_not_found_err)?
140 .1
141 .compiled_module()
142 .find_function_def_by_name(function_name.as_str())
143 .ok_or_else(fn_not_found_err)?;
144
145 let dep_resolution_fn = match fdef.1.visibility {
146 Visibility::Public => VersionConstraint::at_least,
147 Visibility::Private | Visibility::Friend => VersionConstraint::exact,
148 };
149
150 add_package(
151 package,
152 store,
153 &mut resolution_table,
154 VersionConstraint::exact,
155 dep_resolution_fn,
156 )?;
157
158 for type_defining_id in type_args.iter().flat_map(|ty| ty.all_addresses()) {
159 add_package(
161 &ObjectID::from(type_defining_id),
162 store,
163 &mut resolution_table,
164 VersionConstraint::at_least,
165 VersionConstraint::at_least,
166 )?;
167 }
168
169 Ok(resolution_table)
170 }
171
172 fn compute_publication_linkage_(
174 &self,
175 deps: &[ObjectID],
176 store: &dyn PackageStore,
177 ) -> Result<ResolutionTable, ExecutionError> {
178 let mut resolution_table = self.internal.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 .resolution_table_with_native_packages(package_store)?;
219 for arg in inputs.iter() {
220 input(&mut resolution_table, arg, package_store, object_store)?;
221 }
222
223 for cmd in commands.iter() {
224 command(&mut resolution_table, cmd, package_store)?;
225 }
226
227 Ok(ExecutableLinkage::new(
228 ResolvedLinkage::from_resolution_table(resolution_table),
229 ))
230 }
231
232 fn input(
233 resolution_table: &mut ResolutionTable,
234 arg: &CallArg,
235 package_store: &dyn PackageStore,
236 object_store: &dyn ExecutionState,
237 ) -> Result<(), ExecutionError> {
238 match arg {
239 CallArg::Pure(_) | CallArg::Object(ObjectArg::Receiving(_)) => (),
240 CallArg::Object(
241 ObjectArg::ImmOrOwnedObject((id, _, _)) | ObjectArg::SharedObject { id, .. },
242 ) => {
243 let Some(obj) = object_store.read_object(id) else {
244 invariant_violation!("Object {:?} not found in object store", id);
245 };
246 let Some(ty) = obj.type_() else {
247 invariant_violation!("Object {:?} has does not have a Move type", id);
248 };
249
250 let tag: StructTag = ty.clone().into();
253 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
254 resolution_table.add_type_linkages_to_table(ids, package_store)?;
255 }
256 CallArg::FundsWithdrawal(f) => {
257 let FundsWithdrawalArg { type_arg, .. } = f;
258 match type_arg {
259 WithdrawalTypeArg::Balance(tag) => {
260 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
261 resolution_table.add_type_linkages_to_table(ids, package_store)?;
262 }
263 }
264 }
265 }
266
267 Ok(())
268 }
269
270 fn command(
271 resolution_table: &mut ResolutionTable,
272 command: &Command,
273 package_store: &dyn PackageStore,
274 ) -> Result<(), ExecutionError> {
275 let mut add_ty_input = |ty: &TypeInput| {
276 let tag = ty.to_type_tag().map_err(|e| {
277 ExecutionError::new_with_source(
278 ExecutionErrorKind::InvalidLinkage,
279 format!("Invalid type tag in move call argument: {:?}", e),
280 )
281 })?;
282 let ids = tag.all_addresses().into_iter().map(ObjectID::from);
283 resolution_table.add_type_linkages_to_table(ids, package_store)
284 };
285 match command {
286 Command::MoveCall(pmc) => {
287 let ProgrammableMoveCall {
288 package,
289 type_arguments,
290 ..
291 } = &**pmc;
292 type_arguments.iter().try_for_each(add_ty_input)?;
293 resolution_table.add_type_linkages_to_table([*package], package_store)?;
294 }
295 Command::MakeMoveVec(Some(ty), _) => {
296 add_ty_input(ty)?;
297 }
298 Command::MakeMoveVec(None, _)
299 | Command::TransferObjects(_, _)
300 | Command::SplitCoins(_, _)
301 | Command::MergeCoins(_, _) => (),
302 Command::Publish(_, object_ids) => {
303 resolution_table.add_type_linkages_to_table(object_ids, package_store)?;
304 }
305 Command::Upgrade(_, object_ids, object_id, _) => {
306 resolution_table.add_type_linkages_to_table([*object_id], package_store)?;
307 resolution_table.add_type_linkages_to_table(object_ids, package_store)?;
308 }
309 }
310
311 Ok(())
312 }
313}