Skip to main content

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::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            // Type arguments are "at least" constraints
159            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    /// Compute the linkage for a publish or upgrade command. This is a special case because
172    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                // invariant: the addresses in the type are defining addresses for the types since
250                // these are the types of the objects as stored on-chain.
251                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}