1use crate::{
5 data_store::VerifiedPackageStore,
6 static_programmable_transactions::{
7 linkage::{
8 analysis::LinkageAnalyzer,
9 resolution::{ResolutionTable, VersionConstraint, add_and_unify, get_package},
10 resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
11 },
12 loading::ast::{
13 Command, DeserializedPackage, LoadedFunction, PackagePayload, Transaction, Type,
14 },
15 },
16};
17use move_binary_format::{CompiledModule, file_format::Visibility};
18use move_vm_runtime::validation::verification::ast::Package as VerifiedPackage;
19use std::{collections::BTreeMap, sync::Arc};
20use sui_protocol_config::ProtocolConfig;
21use sui_types::{
22 base_types::ObjectID,
23 error::ExecutionErrorTrait,
24 execution_status::{ExecutionErrorKind, PackageUpgradeError},
25};
26use sui_verifier::INIT_FN_NAME;
27
28pub fn refine_to_single_linkage<E: ExecutionErrorTrait>(
45 txn: &mut Transaction,
46 linkage_analysis: &LinkageAnalyzer,
47 package_store: &VerifiedPackageStore<'_>,
48 protocol_config: &ProtocolConfig,
49) -> Result<(), E> {
50 let mut base_linkage = linkage_analysis
51 .config()
52 .resolution_table_with_native_packages::<E, _>(package_store)?;
53
54 for (i, command) in txn.commands.iter().enumerate() {
55 analyze_command::<E>(command, &mut base_linkage, package_store, protocol_config)
56 .map_err(|e| e.with_command_index(i))?;
57 }
58
59 if protocol_config.enable_order_independent_upgrade_init_linkage() {
60 for (i, command) in txn.commands.iter().enumerate() {
61 let Command::Upgrade(payload, _, current_package_id, _, resolved_linkage) = command
62 else {
63 continue;
64 };
65 analyze_upgrade_command::<E>(
66 payload,
67 current_package_id,
68 resolved_linkage,
69 &mut base_linkage,
70 package_store,
71 protocol_config,
72 )
73 .map_err(|e| e.with_command_index(i))?;
74 }
75 }
76 let resolved_linkage =
77 ExecutableLinkage::new(ResolvedLinkage::from_resolution_table(base_linkage));
78
79 for (i, command) in txn.commands.iter_mut().enumerate() {
80 write_back_linkage::<E>(command, &resolved_linkage).map_err(|e| e.with_command_index(i))?;
81 }
82
83 Ok(())
84}
85
86fn analyze_command<E: ExecutionErrorTrait>(
89 command: &Command,
90 resolution_table: &mut ResolutionTable,
91 store: &VerifiedPackageStore<'_>,
92 protocol_config: &ProtocolConfig,
93) -> Result<(), E> {
94 match command {
95 Command::MoveCall(move_call) => {
96 add_call_to_table::<E>(resolution_table, &move_call.function, store)?;
97 }
98 Command::Publish(PackagePayload::Serialized(_), ..) => {
99 invariant_violation!("Unexpected serialized package payload in linkage analysis")
100 }
101 Command::Publish(
102 PackagePayload::Deserialized(DeserializedPackage {
103 deserialized_modules,
104 ..
105 }),
106 _,
107 resolved_linkage,
108 ) => {
109 if deserialized_modules.iter().any(module_has_init) {
123 for resolved in resolved_linkage.linkage.values() {
124 add_and_unify(resolved, store, resolution_table, VersionConstraint::exact)?;
125 }
126 }
127 }
128 Command::Upgrade(_, _, _, _, _)
129 if protocol_config.enable_order_independent_upgrade_init_linkage() => {}
130 Command::Upgrade(payload, _, current_package_id, _, resolved_linkage) => {
131 analyze_upgrade_command::<E>(
132 payload,
133 current_package_id,
134 resolved_linkage,
135 resolution_table,
136 store,
137 protocol_config,
138 )?;
139 }
140 Command::MakeMoveVec(Some(ty), _) => {
141 add_type_packages::<E>(resolution_table, std::iter::once(ty), store)?;
142 }
143 Command::MakeMoveVec(None, _) => (),
144 Command::TransferObjects(_, _) | Command::SplitCoins(_, _) | Command::MergeCoins(_, _) => {}
145 };
146 Ok(())
147}
148
149fn analyze_upgrade_command<E: ExecutionErrorTrait>(
151 payload: &PackagePayload,
152 current_package_id: &ObjectID,
153 resolved_linkage: &ResolvedLinkage,
154 resolution_table: &mut ResolutionTable,
155 store: &VerifiedPackageStore<'_>,
156 protocol_config: &ProtocolConfig,
157) -> Result<(), E> {
158 if !protocol_config.enable_init_on_upgrade() {
159 return Ok(());
160 }
161
162 let current_pkg = get_package(current_package_id, store)?;
163
164 assert_invariant!(
165 protocol_config.enable_unified_linkage(),
166 "Unified linkage must be enabled before init on upgrade is supported"
167 );
168
169 let new_modules = match payload {
170 PackagePayload::Serialized(_) => {
171 invariant_violation!("Unexpected serialized package payload in linkage analysis")
172 }
173 PackagePayload::Deserialized(DeserializedPackage {
174 deserialized_modules,
175 ..
176 }) => deserialized_modules,
177 };
178
179 let current_module_inits = current_pkg
181 .modules()
182 .iter()
183 .map(|(module_id, module)| {
184 (
185 module_id.name().as_str(),
186 module_has_init(module.compiled_module()),
187 )
188 })
189 .collect::<BTreeMap<_, _>>();
190
191 reject_existing_module_added_init::<E>(¤t_module_inits, new_modules)?;
193
194 if has_new_module_init(¤t_module_inits, new_modules) {
196 add_upgrade_init_linkage_to_table::<E>(
197 resolution_table,
198 current_package_id,
199 resolved_linkage,
200 store,
201 )?;
202 }
203
204 Ok(())
205}
206
207fn reject_existing_module_added_init<E: ExecutionErrorTrait>(
210 current_module_inits: &BTreeMap<&str, bool>,
211 new_modules: &[CompiledModule],
212) -> Result<(), E> {
213 for new_module in new_modules {
214 let module_name = new_module
215 .identifier_at(new_module.self_handle().name)
216 .as_str();
217 if current_module_inits.get(module_name) == Some(&false) && module_has_init(new_module) {
218 return Err(<E>::from_kind(ExecutionErrorKind::PackageUpgradeError {
219 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
220 }));
221 }
222 }
223 Ok(())
224}
225
226fn has_new_module_init(
229 current_module_inits: &BTreeMap<&str, bool>,
230 new_modules: &[CompiledModule],
231) -> bool {
232 new_modules.iter().any(|new_module| {
233 let module_name = new_module
234 .identifier_at(new_module.self_handle().name)
235 .as_str();
236 current_module_inits.get(module_name).is_none() && module_has_init(new_module)
237 })
238}
239
240fn module_has_init(module: &CompiledModule) -> bool {
241 module.function_defs().iter().any(|func_def| {
242 let handle = module.function_handle_at(func_def.function);
243 module.identifier_at(handle.name) == INIT_FN_NAME
244 })
245}
246
247fn add_upgrade_init_linkage_to_table<E: ExecutionErrorTrait>(
258 resolution_table: &mut ResolutionTable,
259 current_package_id: &ObjectID,
260 resolved_linkage: &ResolvedLinkage,
261 store: &VerifiedPackageStore<'_>,
262) -> Result<(), E> {
263 let current_pkg = get_package(current_package_id, store)?;
264 let pkg_original_id: ObjectID = current_pkg.original_id().into();
265
266 if !resolution_table
267 .resolution_table
268 .contains_key(&pkg_original_id)
269 {
270 for resolved in resolved_linkage.linkage.values() {
271 add_and_unify(resolved, store, resolution_table, VersionConstraint::exact)?;
272 }
273 return Ok(());
274 }
275
276 for (original_id, version_id) in &resolved_linkage.linkage {
277 match resolution_table.resolution_table.get(original_id) {
278 None => {
279 add_and_unify(
280 version_id,
281 store,
282 resolution_table,
283 VersionConstraint::exact,
284 )?;
285 }
286 Some(existing) if existing.object_id() == *version_id => (),
287 Some(existing) => {
288 return Err(E::new_with_source(
289 ExecutionErrorKind::InvalidLinkage,
290 format!(
291 "upgrade init linkage conflicts with transaction linkage: package \
292 {original_id} resolves to {} in transaction linkage, but upgrade \
293 linkage requires {version_id}",
294 existing.object_id(),
295 ),
296 ));
297 }
298 }
299 }
300
301 Ok(())
302}
303
304fn add_call_to_table<E: ExecutionErrorTrait>(
312 resolution_table: &mut ResolutionTable,
313 function: &LoadedFunction,
314 store: &VerifiedPackageStore<'_>,
315) -> Result<(), E> {
316 let dep_resolution_fn = match function.visibility {
317 Visibility::Public => VersionConstraint::at_least,
318 Visibility::Private | Visibility::Friend => VersionConstraint::exact,
319 };
320 let package: ObjectID = (*function.version_mid.address()).into();
321 add_package::<E>(
322 &package,
323 store,
324 resolution_table,
325 VersionConstraint::exact,
326 dep_resolution_fn,
327 )?;
328 add_type_packages::<E>(resolution_table, function.type_arguments.iter(), store)
329}
330
331fn add_type_packages<'a, E: ExecutionErrorTrait>(
334 resolution_table: &mut ResolutionTable,
335 types: impl IntoIterator<Item = &'a Type>,
336 store: &VerifiedPackageStore<'_>,
337) -> Result<(), E> {
338 for type_defining_id in types.into_iter().flat_map(|ty| ty.all_addresses()) {
339 add_package::<E>(
340 &ObjectID::from(type_defining_id),
341 store,
342 resolution_table,
343 VersionConstraint::at_least,
344 VersionConstraint::at_least,
345 )?;
346 }
347 Ok(())
348}
349
350fn add_package<E: ExecutionErrorTrait>(
354 object_id: &ObjectID,
355 store: &VerifiedPackageStore<'_>,
356 resolution_table: &mut ResolutionTable,
357 self_resolution_fn: fn(&Arc<VerifiedPackage>) -> Option<VersionConstraint>,
358 dep_resolution_fn: fn(&Arc<VerifiedPackage>) -> Option<VersionConstraint>,
359) -> Result<(), E> {
360 let pkg = get_package(object_id, store)?;
361 let transitive_deps = resolution_table
362 .config
363 .linkage_table(&pkg)
364 .into_values()
365 .map(ObjectID::from);
366 add_and_unify(object_id, store, resolution_table, self_resolution_fn)?;
367 for dep_id in transitive_deps {
368 add_and_unify(&dep_id, store, resolution_table, dep_resolution_fn)?;
369 }
370 Ok(())
371}
372
373fn write_back_linkage<E: ExecutionErrorTrait>(
376 command: &mut Command,
377 ptb_linkage: &ExecutableLinkage,
378) -> Result<(), E> {
379 match command {
380 Command::MoveCall(move_call) => {
381 let previous_linkage = &move_call.function.linkage;
382 assert_invariant!(
390 previous_linkage
391 .0
392 .linkage
393 .keys()
394 .all(|k| ptb_linkage.0.linkage.contains_key(k)),
395 "single linkage drops a package that the per-call linkage of MoveCall had resolved"
396 );
397 debug_assert!(
398 previous_linkage.0.linkage.len() <= ptb_linkage.0.linkage.len(),
399 "single linkage has fewer candidates than the per-call linkage of MoveCall"
400 );
401 move_call.function.linkage = ptb_linkage.clone();
402 }
403 Command::TransferObjects(_, _)
404 | Command::SplitCoins(_, _)
405 | Command::MergeCoins(_, _)
406 | Command::MakeMoveVec(_, _)
407 | Command::Publish(_, _, _)
408 | Command::Upgrade(_, _, _, _, _) => (),
409 };
410 Ok(())
411}