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}