sui_adapter_latest/static_programmable_transactions/linkage/
analysis.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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            // Type arguments are "at least" constraints
157            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    /// Compute the linkage for a publish or upgrade command. This is a special case because
170    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                // invariant: the addresses in the type are defining addresses for the types since
252                // these are the types of the objects as stored on-chain.
253                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}