sui_adapter_latest/static_programmable_transactions/linkage/
single_linkage.rs1use crate::{
5 data_store::PackageStore,
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::file_format::Visibility;
18use move_vm_runtime::validation::verification::ast::Package as VerifiedPackage;
19use sui_types::{base_types::ObjectID, error::ExecutionErrorTrait};
20use sui_verifier::INIT_FN_NAME;
21
22pub fn refine_to_single_linkage<E: ExecutionErrorTrait>(
39 txn: &mut Transaction,
40 linkage_analysis: &LinkageAnalyzer,
41 package_store: &dyn PackageStore,
42) -> Result<(), E> {
43 let mut base_linkage = linkage_analysis
44 .config()
45 .resolution_table_with_native_packages::<E>(package_store)?;
46
47 for (i, command) in txn.commands.iter().enumerate() {
48 analyze_command::<E>(command, &mut base_linkage, package_store)
49 .map_err(|e| e.with_command_index(i))?;
50 }
51 let resolved_linkage =
52 ExecutableLinkage::new(ResolvedLinkage::from_resolution_table(base_linkage));
53
54 for (i, command) in txn.commands.iter_mut().enumerate() {
55 write_back_linkage::<E>(command, &resolved_linkage).map_err(|e| e.with_command_index(i))?;
56 }
57
58 Ok(())
59}
60
61fn analyze_command<E: ExecutionErrorTrait>(
64 command: &Command,
65 resolution_table: &mut ResolutionTable,
66 store: &dyn PackageStore,
67) -> Result<(), E> {
68 match command {
69 Command::MoveCall(move_call) => {
70 add_call_to_table::<E>(resolution_table, &move_call.function, store)?;
71 }
72 Command::Publish(PackagePayload::Serialized(_), ..) => {
73 invariant_violation!("Unexpected serialized package payload in linkage analysis")
74 }
75 Command::Publish(
76 PackagePayload::Deserialized(DeserializedPackage {
77 deserialized_modules,
78 ..
79 }),
80 _,
81 resolved_linkage,
82 ) => {
83 let has_init_fn = deserialized_modules.iter().any(|module| {
97 module.function_defs().iter().any(|func_def| {
98 let handle = module.function_handle_at(func_def.function);
99 let name = module.identifier_at(handle.name);
100 name == INIT_FN_NAME
101 })
102 });
103 if has_init_fn {
104 for resolved in resolved_linkage.linkage.values() {
105 add_and_unify(resolved, store, resolution_table, VersionConstraint::exact)?;
106 }
107 }
108 }
109 Command::MakeMoveVec(Some(ty), _) => {
110 add_type_packages::<E>(resolution_table, std::iter::once(ty), store)?;
111 }
112 Command::MakeMoveVec(None, _) => (),
113 Command::Upgrade(_, _, _, _, _) => (),
117 Command::TransferObjects(_, _) | Command::SplitCoins(_, _) | Command::MergeCoins(_, _) => {}
118 };
119 Ok(())
120}
121
122fn add_call_to_table<E: ExecutionErrorTrait>(
130 resolution_table: &mut ResolutionTable,
131 function: &LoadedFunction,
132 store: &dyn PackageStore,
133) -> Result<(), E> {
134 let dep_resolution_fn = match function.visibility {
135 Visibility::Public => VersionConstraint::at_least,
136 Visibility::Private | Visibility::Friend => VersionConstraint::exact,
137 };
138 let package: ObjectID = (*function.version_mid.address()).into();
139 add_package::<E>(
140 &package,
141 store,
142 resolution_table,
143 VersionConstraint::exact,
144 dep_resolution_fn,
145 )?;
146 add_type_packages::<E>(resolution_table, function.type_arguments.iter(), store)
147}
148
149fn add_type_packages<'a, E: ExecutionErrorTrait>(
152 resolution_table: &mut ResolutionTable,
153 types: impl IntoIterator<Item = &'a Type>,
154 store: &dyn PackageStore,
155) -> Result<(), E> {
156 for type_defining_id in types.into_iter().flat_map(|ty| ty.all_addresses()) {
157 add_package::<E>(
158 &ObjectID::from(type_defining_id),
159 store,
160 resolution_table,
161 VersionConstraint::at_least,
162 VersionConstraint::at_least,
163 )?;
164 }
165 Ok(())
166}
167
168fn add_package<E: ExecutionErrorTrait>(
172 object_id: &ObjectID,
173 store: &dyn PackageStore,
174 resolution_table: &mut ResolutionTable,
175 self_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
176 dep_resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
177) -> Result<(), E> {
178 let pkg = get_package(object_id, store)?;
179 let transitive_deps = resolution_table
180 .config
181 .linkage_table(&pkg)
182 .into_values()
183 .map(ObjectID::from);
184 add_and_unify(object_id, store, resolution_table, self_resolution_fn)?;
185 for dep_id in transitive_deps {
186 add_and_unify(&dep_id, store, resolution_table, dep_resolution_fn)?;
187 }
188 Ok(())
189}
190
191fn write_back_linkage<E: ExecutionErrorTrait>(
194 command: &mut Command,
195 ptb_linkage: &ExecutableLinkage,
196) -> Result<(), E> {
197 match command {
198 Command::MoveCall(move_call) => {
199 let previous_linkage = &move_call.function.linkage;
200 assert_invariant!(
208 previous_linkage
209 .0
210 .linkage
211 .keys()
212 .all(|k| ptb_linkage.0.linkage.contains_key(k)),
213 "single linkage drops a package that the per-call linkage of MoveCall had resolved"
214 );
215 debug_assert!(
216 previous_linkage.0.linkage.len() <= ptb_linkage.0.linkage.len(),
217 "single linkage has fewer candidates than the per-call linkage of MoveCall"
218 );
219 move_call.function.linkage = ptb_linkage.clone();
220 }
221 Command::TransferObjects(_, _)
222 | Command::SplitCoins(_, _)
223 | Command::MergeCoins(_, _)
224 | Command::MakeMoveVec(_, _)
225 | Command::Publish(_, _, _)
226 | Command::Upgrade(_, _, _, _, _) => (),
227 };
228 Ok(())
229}