sui_adapter_v3/data_store/legacy/
linkage_view.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::data_store::PackageStore;
5use move_core_types::{
6    account_address::AccountAddress,
7    identifier::{IdentStr, Identifier},
8    language_storage::ModuleId,
9    resolver::ModuleResolver,
10};
11use move_vm_types::data_store::LinkageResolver;
12use std::{
13    cell::RefCell,
14    collections::{BTreeMap, HashMap, HashSet, hash_map::Entry},
15    rc::Rc,
16    str::FromStr,
17};
18use sui_types::{
19    base_types::ObjectID,
20    error::{ExecutionError, SuiError, SuiResult},
21    move_package::{MovePackage, TypeOrigin, UpgradeInfo},
22};
23
24/// Exposes module and linkage resolution to the Move runtime.  The first by delegating to
25/// `resolver` and the second via linkage information that is loaded from a move package.
26pub struct LinkageView<'state> {
27    /// Interface to resolve packages, modules and resources directly from the store, and possibly
28    /// from other sources (e.g., packages just published).
29    resolver: Box<dyn PackageStore + 'state>,
30    /// Information used to change module and type identities during linkage.
31    linkage_info: RefCell<Option<LinkageInfo>>,
32    /// Cache containing the type origin information from every package that has been set as the
33    /// link context, and every other type that has been requested by the loader in this session.
34    /// It's okay to retain entries in this cache between different link contexts because a type's
35    /// Runtime ID and Defining ID are invariant between across link contexts.
36    ///
37    /// Cache is keyed first by the Runtime ID of the type's module, and then the type's identifier.
38    /// The value is the ObjectID/Address of the package that introduced the type.
39    type_origin_cache: RefCell<HashMap<ModuleId, HashMap<Identifier, AccountAddress>>>,
40    /// Cache of past package addresses that have been the link context -- if a package is in this
41    /// set, then we will not try to load its type origin table when setting it as a context (again).
42    past_contexts: RefCell<HashSet<ObjectID>>,
43}
44
45#[derive(Debug)]
46pub struct LinkageInfo {
47    storage_id: AccountAddress,
48    runtime_id: AccountAddress,
49    link_table: BTreeMap<ObjectID, UpgradeInfo>,
50}
51
52pub struct SavedLinkage(LinkageInfo);
53
54impl<'state> LinkageView<'state> {
55    pub fn new(resolver: Box<dyn PackageStore + 'state>) -> Self {
56        Self {
57            resolver,
58            linkage_info: RefCell::new(None),
59            type_origin_cache: RefCell::new(HashMap::new()),
60            past_contexts: RefCell::new(HashSet::new()),
61        }
62    }
63
64    pub fn reset_linkage(&self) -> Result<(), ExecutionError> {
65        let Ok(mut linkage_info) = self.linkage_info.try_borrow_mut() else {
66            invariant_violation!("Unable to reset linkage")
67        };
68        *linkage_info = None;
69        Ok(())
70    }
71
72    /// Indicates whether this `LinkageView` has had its context set to match the linkage in
73    /// `context`.
74    pub fn has_linkage(&self, context: ObjectID) -> Result<bool, ExecutionError> {
75        let Ok(linkage_info) = self.linkage_info.try_borrow() else {
76            invariant_violation!("Unable to borrow linkage info")
77        };
78        Ok(linkage_info
79            .as_ref()
80            .is_some_and(|l| l.storage_id == *context))
81    }
82
83    /// Reset the linkage, but save the context that existed before, if there was one.
84    pub fn steal_linkage(&self) -> Option<SavedLinkage> {
85        Some(SavedLinkage(self.linkage_info.take()?))
86    }
87
88    /// Restore a previously saved linkage context.  Fails if there is already a context set.
89    pub fn restore_linkage(&self, saved: Option<SavedLinkage>) -> Result<(), ExecutionError> {
90        let Some(SavedLinkage(saved)) = saved else {
91            return Ok(());
92        };
93
94        let Ok(mut linkage_info) = self.linkage_info.try_borrow_mut() else {
95            invariant_violation!("Unable to borrow linkage while restoring")
96        };
97        if let Some(existing) = &*linkage_info {
98            invariant_violation!(
99                "Attempt to overwrite linkage by restoring: {saved:#?} \
100                 Existing linkage: {existing:#?}",
101            )
102        }
103
104        // No need to populate type origin cache, because a saved context must have been set as a
105        // linkage before, and the cache would have been populated at that time.
106        *linkage_info = Some(saved);
107        Ok(())
108    }
109
110    /// Set the linkage context to the information based on the linkage and type origin tables from
111    /// the `context` package.  Returns the original package ID (aka the runtime ID) of the context
112    /// package on success.
113    pub fn set_linkage(&self, context: &MovePackage) -> Result<AccountAddress, ExecutionError> {
114        let Ok(mut linkage_info) = self.linkage_info.try_borrow_mut() else {
115            invariant_violation!("Unable to borrow linkage to set")
116        };
117        if let Some(existing) = &*linkage_info {
118            invariant_violation!(
119                "Attempt to overwrite linkage info with context from {}. \
120                    Existing linkage: {existing:#?}",
121                context.id(),
122            )
123        }
124
125        let linkage = LinkageInfo::from(context);
126        let storage_id = context.id();
127        let runtime_id = linkage.runtime_id;
128        *linkage_info = Some(linkage);
129
130        if !self.past_contexts.borrow_mut().insert(storage_id) {
131            return Ok(runtime_id);
132        }
133
134        // Pre-populate the type origin cache with entries from the current package -- this is
135        // necessary to serve "defining module" requests for unpublished packages, but will also
136        // speed up other requests.
137        for TypeOrigin {
138            module_name,
139            datatype_name: struct_name,
140            package: defining_id,
141        } in context.type_origin_table()
142        {
143            let Ok(module_name) = Identifier::from_str(module_name) else {
144                invariant_violation!("Module name isn't an identifier: {module_name}");
145            };
146
147            let Ok(struct_name) = Identifier::from_str(struct_name) else {
148                invariant_violation!("Struct name isn't an identifier: {struct_name}");
149            };
150
151            let runtime_id = ModuleId::new(runtime_id, module_name);
152            self.add_type_origin(runtime_id, struct_name, *defining_id)?;
153        }
154
155        Ok(runtime_id)
156    }
157
158    pub fn original_package_id(&self) -> Result<Option<AccountAddress>, ExecutionError> {
159        let Ok(linkage_info) = self.linkage_info.try_borrow() else {
160            invariant_violation!("Unable to borrow linkage info")
161        };
162        Ok(linkage_info.as_ref().map(|info| info.runtime_id))
163    }
164
165    fn get_cached_type_origin(
166        &self,
167        runtime_id: &ModuleId,
168        struct_: &IdentStr,
169    ) -> Option<AccountAddress> {
170        self.type_origin_cache
171            .borrow()
172            .get(runtime_id)?
173            .get(struct_)
174            .cloned()
175    }
176
177    fn add_type_origin(
178        &self,
179        runtime_id: ModuleId,
180        struct_: Identifier,
181        defining_id: ObjectID,
182    ) -> Result<(), ExecutionError> {
183        let mut cache = self.type_origin_cache.borrow_mut();
184        let module_cache = cache.entry(runtime_id.clone()).or_default();
185
186        match module_cache.entry(struct_) {
187            Entry::Vacant(entry) => {
188                entry.insert(*defining_id);
189            }
190
191            Entry::Occupied(entry) => {
192                if entry.get() != &*defining_id {
193                    invariant_violation!(
194                        "Conflicting defining ID for {}::{}: {} and {}",
195                        runtime_id,
196                        entry.key(),
197                        defining_id,
198                        entry.get(),
199                    );
200                }
201            }
202        }
203
204        Ok(())
205    }
206
207    pub(crate) fn link_context(&self) -> Result<AccountAddress, ExecutionError> {
208        let Ok(linkage_info) = self.linkage_info.try_borrow() else {
209            invariant_violation!("Unable to borrow linkage info")
210        };
211        Ok(linkage_info
212            .as_ref()
213            .map_or(AccountAddress::ZERO, |l| l.storage_id))
214    }
215
216    pub(crate) fn relocate(&self, module_id: &ModuleId) -> Result<ModuleId, SuiError> {
217        let Ok(linkage_info) = self.linkage_info.try_borrow() else {
218            invariant_violation!("Unable to borrow linkage info")
219        };
220        let Some(linkage) = &*linkage_info else {
221            invariant_violation!("No linkage context set while relocating {module_id}.")
222        };
223
224        // The request is to relocate a module in the package that the link context is from.  This
225        // entry will not be stored in the linkage table, so must be handled specially.
226        if module_id.address() == &linkage.runtime_id {
227            return Ok(ModuleId::new(
228                linkage.storage_id,
229                module_id.name().to_owned(),
230            ));
231        }
232
233        let runtime_id = ObjectID::from_address(*module_id.address());
234        let Some(upgrade) = linkage.link_table.get(&runtime_id) else {
235            invariant_violation!(
236                "Missing linkage for {runtime_id} in context {}, runtime_id is {}",
237                linkage.storage_id,
238                linkage.runtime_id
239            );
240        };
241
242        Ok(ModuleId::new(
243            upgrade.upgraded_id.into(),
244            module_id.name().to_owned(),
245        ))
246    }
247
248    pub(crate) fn defining_module(
249        &self,
250        runtime_id: &ModuleId,
251        struct_: &IdentStr,
252    ) -> Result<ModuleId, SuiError> {
253        let Ok(linkage_info) = self.linkage_info.try_borrow() else {
254            invariant_violation!("Unable to borrow linkage info")
255        };
256        if linkage_info.is_none() {
257            invariant_violation!(
258                "No linkage context set for defining module query on {runtime_id}::{struct_}."
259            )
260        }
261
262        if let Some(cached) = self.get_cached_type_origin(runtime_id, struct_) {
263            return Ok(ModuleId::new(cached, runtime_id.name().to_owned()));
264        }
265
266        let storage_id = ObjectID::from(*self.relocate(runtime_id)?.address());
267        let Some(package) = self.resolver.get_package(&storage_id)? else {
268            invariant_violation!("Missing dependent package in store: {storage_id}",)
269        };
270
271        for TypeOrigin {
272            module_name,
273            datatype_name: struct_name,
274            package,
275        } in package.type_origin_table()
276        {
277            if module_name == runtime_id.name().as_str() && struct_name == struct_.as_str() {
278                self.add_type_origin(runtime_id.clone(), struct_.to_owned(), *package)?;
279                return Ok(ModuleId::new(**package, runtime_id.name().to_owned()));
280            }
281        }
282
283        invariant_violation!(
284            "{runtime_id}::{struct_} not found in type origin table in {storage_id} (v{})",
285            package.version(),
286        )
287    }
288}
289
290impl From<&MovePackage> for LinkageInfo {
291    fn from(package: &MovePackage) -> Self {
292        Self {
293            storage_id: package.id().into(),
294            runtime_id: package.original_package_id().into(),
295            link_table: package.linkage_table().clone(),
296        }
297    }
298}
299
300impl LinkageResolver for LinkageView<'_> {
301    type Error = SuiError;
302
303    fn link_context(&self) -> AccountAddress {
304        // TODO should we propagate the error
305        LinkageView::link_context(self).unwrap()
306    }
307
308    fn relocate(&self, module_id: &ModuleId) -> Result<ModuleId, Self::Error> {
309        LinkageView::relocate(self, module_id)
310    }
311
312    fn defining_module(
313        &self,
314        runtime_id: &ModuleId,
315        struct_: &IdentStr,
316    ) -> Result<ModuleId, Self::Error> {
317        LinkageView::defining_module(self, runtime_id, struct_)
318    }
319}
320
321impl ModuleResolver for LinkageView<'_> {
322    type Error = SuiError;
323
324    fn get_module(&self, id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
325        Ok(self
326            .get_package(&ObjectID::from(*id.address()))?
327            .and_then(|package| {
328                package
329                    .serialized_module_map()
330                    .get(id.name().as_str())
331                    .cloned()
332            }))
333    }
334
335    fn get_packages_static<const N: usize>(
336        &self,
337        _ids: [AccountAddress; N],
338    ) -> Result<[Option<move_core_types::resolver::SerializedPackage>; N], Self::Error> {
339        unreachable!("v3 get_packages_static should not be called on LinkageView")
340    }
341
342    fn get_packages<'a>(
343        &self,
344        _ids: impl ExactSizeIterator<Item = &'a AccountAddress>,
345    ) -> Result<Vec<Option<move_core_types::resolver::SerializedPackage>>, Self::Error> {
346        unreachable!("v3 get_packages should not be called on LinkageView")
347    }
348}
349
350impl PackageStore for LinkageView<'_> {
351    fn get_package(&self, package_id: &ObjectID) -> SuiResult<Option<Rc<MovePackage>>> {
352        self.resolver.get_package(package_id)
353    }
354
355    fn resolve_type_to_defining_id(
356        &self,
357        _module_address: ObjectID,
358        _module_name: &IdentStr,
359        _type_name: &IdentStr,
360    ) -> SuiResult<Option<ObjectID>> {
361        unimplemented!(
362            "resolve_type_to_defining_id is not implemented for LinkageView and should never be called"
363        )
364    }
365}