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