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