sui_adapter_latest/data_store/
cached_package_store.rs1use crate::data_store::PackageStore;
5use indexmap::IndexMap;
6use move_core_types::identifier::IdentStr;
7use std::{
8    cell::RefCell,
9    collections::{BTreeMap, BTreeSet},
10    rc::Rc,
11};
12use sui_types::{
13    base_types::ObjectID,
14    error::{ExecutionError, SuiResult},
15    move_package::MovePackage,
16    storage::BackingPackageStore,
17};
18
19pub struct CachedPackageStore<'state> {
31    pub package_store: Box<dyn BackingPackageStore + 'state>,
33    package_cache: RefCell<BTreeMap<ObjectID, Option<Rc<MovePackage>>>>,
36    type_origin_cache: RefCell<CachedTypeOriginMap>,
39    new_packages: RefCell<IndexMap<ObjectID, Rc<MovePackage>>>,
43    max_package_cache_size: usize,
46    max_type_cache_size: usize,
49}
50
51type TypeOriginMap = BTreeMap<ObjectID, BTreeMap<(String, String), ObjectID>>;
52
53#[derive(Debug)]
54struct CachedTypeOriginMap {
55    pub cached_type_origins: BTreeSet<ObjectID>,
59    pub type_origin_map: TypeOriginMap,
62}
63
64impl CachedTypeOriginMap {
65    pub fn new() -> Self {
66        Self {
67            cached_type_origins: BTreeSet::new(),
68            type_origin_map: TypeOriginMap::new(),
69        }
70    }
71}
72
73impl<'state> CachedPackageStore<'state> {
74    pub const DEFAULT_MAX_PACKAGE_CACHE_SIZE: usize = 200;
75    pub const DEFAULT_MAX_TYPE_ORIGIN_CACHE_SIZE: usize = 1000;
76
77    pub fn new(package_store: Box<dyn BackingPackageStore + 'state>) -> Self {
78        Self {
79            package_store,
80            package_cache: RefCell::new(BTreeMap::new()),
81            type_origin_cache: RefCell::new(CachedTypeOriginMap::new()),
82            new_packages: RefCell::new(IndexMap::new()),
83            max_package_cache_size: Self::DEFAULT_MAX_PACKAGE_CACHE_SIZE,
84            max_type_cache_size: Self::DEFAULT_MAX_TYPE_ORIGIN_CACHE_SIZE,
85        }
86    }
87
88    pub fn push_package(
91        &self,
92        id: ObjectID,
93        package: Rc<MovePackage>,
94    ) -> Result<(), ExecutionError> {
95        debug_assert!(self.fetch_package(&id).unwrap().is_none());
97
98        if self.new_packages.borrow_mut().insert(id, package).is_some() {
101            invariant_violation!(
102                "Package with ID {} already exists in the new packages. This should never happen.",
103                id
104            );
105        }
106
107        Ok(())
108    }
109
110    pub fn pop_package(&self, id: ObjectID) -> Result<Rc<MovePackage>, ExecutionError> {
116        if self
117            .new_packages
118            .borrow()
119            .last()
120            .is_none_or(|(pkg_id, _)| *pkg_id != id)
121        {
122            make_invariant_violation!(
123                "Tried to pop package {} from new packages, but new packages was empty or \
124                it is not the most recent package inserted. This should never happen.",
125                id
126            );
127        }
128
129        let Some((pkg_id, pkg)) = self.new_packages.borrow_mut().pop() else {
130            unreachable!(
131                "We just checked that new packages is not empty, so this should never happen."
132            );
133        };
134        assert_eq!(
135            pkg_id, id,
136            "Popped package ID {} does not match requested ID {}. This should never happen as was checked above.",
137            pkg_id, id
138        );
139
140        Ok(pkg)
141    }
142
143    pub fn to_new_packages(&self) -> Vec<MovePackage> {
144        self.new_packages
145            .borrow()
146            .iter()
147            .map(|(_, pkg)| pkg.as_ref().clone())
148            .collect()
149    }
150
151    pub fn get_package(&self, object_id: &ObjectID) -> SuiResult<Option<Rc<MovePackage>>> {
158        let Some(pkg) = self.fetch_package(object_id)? else {
159            return Ok(None);
160        };
161
162        let package_id = pkg.id();
163
164        if self.type_origin_cache.borrow().cached_type_origins.len() >= self.max_type_cache_size {
166            *self.type_origin_cache.borrow_mut() = CachedTypeOriginMap::new();
167        }
168
169        if !self
170            .type_origin_cache
171            .borrow()
172            .cached_type_origins
173            .contains(&package_id)
174        {
175            let cached_type_origin_map = &mut self.type_origin_cache.borrow_mut();
176            cached_type_origin_map
177                .cached_type_origins
178                .insert(package_id);
179            let original_package_id = pkg.original_package_id();
180            let package_types = cached_type_origin_map
181                .type_origin_map
182                .entry(original_package_id)
183                .or_default();
184            for ((module_name, type_name), defining_id) in pkg.type_origin_map().into_iter() {
185                if let Some(other) = package_types.insert(
186                    (module_name.to_string(), type_name.to_string()),
187                    defining_id,
188                ) {
189                    assert_eq!(
190                        other, defining_id,
191                        "type origin map should never have conflicting entries"
192                    );
193                }
194            }
195        }
196        Ok(Some(pkg))
197    }
198
199    fn fetch_package(&self, id: &ObjectID) -> SuiResult<Option<Rc<MovePackage>>> {
203        if let Some(pkg) = self.new_packages.borrow().get(id).cloned() {
205            return Ok(Some(pkg));
206        }
207
208        if let Some(pkg) = self.package_cache.borrow().get(id).cloned() {
210            return Ok(pkg);
211        }
212
213        if self.package_cache.borrow().len() >= self.max_package_cache_size {
214            self.package_cache.borrow_mut().clear();
215        }
216
217        let pkg = self
218            .package_store
219            .get_package_object(id)?
220            .map(|pkg_obj| Rc::new(pkg_obj.move_package().clone()));
221        self.package_cache.borrow_mut().insert(*id, pkg.clone());
222        Ok(pkg)
223    }
224}
225
226impl PackageStore for CachedPackageStore<'_> {
227    fn get_package(&self, id: &ObjectID) -> SuiResult<Option<Rc<MovePackage>>> {
228        self.get_package(id)
229    }
230
231    fn resolve_type_to_defining_id(
232        &self,
233        module_address: ObjectID,
234        module_name: &IdentStr,
235        type_name: &IdentStr,
236    ) -> SuiResult<Option<ObjectID>> {
237        let Some(pkg) = self.get_package(&module_address)? else {
238            return Ok(None);
239        };
240
241        Ok(self
242            .type_origin_cache
243            .borrow()
244            .type_origin_map
245            .get(&pkg.original_package_id())
246            .and_then(|module_map| {
247                module_map
248                    .get(&(module_name.to_string(), type_name.to_string()))
249                    .copied()
250            }))
251    }
252}