1use crate::object_runtime::LocalProtocolConfig;
5use move_binary_format::errors::{PartialVMError, PartialVMResult};
6use move_core_types::{annotated_value as A, runtime_value as R, vm_status::StatusCode};
7use move_vm_types::{
8 effects::Op,
9 loaded_data::runtime_types::Type,
10 values::{GlobalValue, StructRef, Value},
11};
12use std::{
13 collections::{btree_map, BTreeMap},
14 sync::Arc,
15};
16use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed};
17use sui_types::{
18 base_types::{MoveObjectType, ObjectID, SequenceNumber},
19 error::VMMemoryLimitExceededSubStatusCode,
20 metrics::ExecutionMetrics,
21 object::{Data, MoveObject, Object, Owner},
22 storage::ChildObjectResolver,
23};
24
25use super::get_all_uids;
26pub(super) struct ChildObject {
27 pub(super) owner: ObjectID,
28 pub(super) ty: Type,
29 pub(super) move_type: MoveObjectType,
30 pub(super) value: GlobalValue,
31}
32
33#[derive(Debug)]
34pub(crate) struct ChildObjectEffect {
35 pub(super) owner: ObjectID,
36 pub(super) loaded_version: Option<SequenceNumber>,
38 pub(super) ty: Type,
39 pub(super) effect: Op<Value>,
40}
41
42struct Inner<'a> {
43 resolver: &'a dyn ChildObjectResolver,
45 root_version: BTreeMap<ObjectID, SequenceNumber>,
49 cached_objects: BTreeMap<ObjectID, Option<Object>>,
52 is_metered: bool,
54 constants: LocalProtocolConfig,
56 metrics: Arc<ExecutionMetrics>,
58}
59
60pub(super) struct ObjectStore<'a> {
63 inner: Inner<'a>,
67 store: BTreeMap<ObjectID, ChildObject>,
70 is_metered: bool,
72 constants: LocalProtocolConfig,
74}
75
76pub(crate) enum ObjectResult<V> {
77 MismatchedType,
79 Loaded(V),
80}
81
82impl Inner<'_> {
83 fn get_or_fetch_object_from_store(
84 &mut self,
85 parent: ObjectID,
86 child: ObjectID,
87 ) -> PartialVMResult<Option<&MoveObject>> {
88 let cached_objects_count = self.cached_objects.len() as u64;
89 let parents_root_version = self.root_version.get(&parent).copied();
90 let had_parent_root_version = parents_root_version.is_some();
91 let parents_root_version = parents_root_version.unwrap_or(SequenceNumber::new());
94 if let btree_map::Entry::Vacant(e) = self.cached_objects.entry(child) {
95 let child_opt = self
96 .resolver
97 .read_child_object(&parent, &child, parents_root_version)
98 .map_err(|msg| {
99 PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(format!("{msg}"))
100 })?;
101 let obj_opt = if let Some(object) = child_opt {
102 if !had_parent_root_version {
105 return Err(PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(
106 format!("A new parent {parent} should not have a child object {child}."),
107 ));
108 }
109 match &object.owner {
112 Owner::ObjectOwner(id) => {
113 if ObjectID::from(*id) != parent {
114 return Err(PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(
115 format!("Bad owner for {child}. \
116 Expected owner {parent} but found owner {id}")
117 ))
118 }
119 }
120 Owner::AddressOwner(_) | Owner::Immutable | Owner::Shared { .. } => {
121 return Err(PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(
122 format!("Bad owner for {child}. \
123 Expected an id owner {parent} but found an address, immutable, or shared owner")
124 ))
125 }
126 Owner::ConsensusAddressOwner { .. } => {
127 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
128 }
129 Owner::Party { .. } => {
130 unimplemented!("Party does not exist for this execution version")
131 }
132 };
133 match object.data {
134 Data::Package(_) => {
135 return Err(PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(
136 format!(
137 "Mismatched object type for {child}. \
138 Expected a Move object but found a Move package"
139 ),
140 ))
141 }
142 Data::Move(_) => Some(object),
143 }
144 } else {
145 None
146 };
147
148 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
149 self.is_metered,
150 cached_objects_count,
151 self.constants.object_runtime_max_num_cached_objects,
152 self.constants
153 .object_runtime_max_num_cached_objects_system_tx,
154 self.metrics
155 .limits_metrics
156 .excessive_object_runtime_cached_objects
157 ) {
158 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
159 .with_message(format!(
160 "Object runtime cached objects limit ({} entries) reached",
161 lim
162 ))
163 .with_sub_status(
164 VMMemoryLimitExceededSubStatusCode::OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED
165 as u64,
166 ));
167 };
168
169 e.insert(obj_opt);
170 }
171 Ok(self
172 .cached_objects
173 .get(&child)
174 .unwrap()
175 .as_ref()
176 .map(|obj| obj.data.try_as_move().unwrap()))
177 }
178
179 fn fetch_object_impl(
180 &mut self,
181 parent: ObjectID,
182 child: ObjectID,
183 child_ty: &Type,
184 child_ty_layout: &R::MoveTypeLayout,
185 child_ty_fully_annotated_layout: &A::MoveTypeLayout,
186 child_move_type: MoveObjectType,
187 ) -> PartialVMResult<ObjectResult<(Type, MoveObjectType, GlobalValue)>> {
188 let obj = match self.get_or_fetch_object_from_store(parent, child)? {
189 None => {
190 return Ok(ObjectResult::Loaded((
191 child_ty.clone(),
192 child_move_type,
193 GlobalValue::none(),
194 )))
195 }
196 Some(obj) => obj,
197 };
198 if obj.type_() != &child_move_type {
200 return Ok(ObjectResult::MismatchedType);
201 }
202 let obj_contents = obj.contents();
204 let v = match Value::simple_deserialize(obj_contents, child_ty_layout) {
205 Some(v) => v,
206 None => return Err(
207 PartialVMError::new(StatusCode::FAILED_TO_DESERIALIZE_RESOURCE).with_message(
208 format!("Failed to deserialize object {child} with type {child_move_type}",),
209 ),
210 ),
211 };
212 let global_value =
213 match GlobalValue::cached(v) {
214 Ok(gv) => gv,
215 Err(e) => {
216 return Err(PartialVMError::new(StatusCode::STORAGE_ERROR).with_message(
217 format!("Object {child} did not deserialize to a struct Value. Error: {e}"),
218 ))
219 }
220 };
221 let contained_uids =
223 get_all_uids(child_ty_fully_annotated_layout, obj_contents).map_err(|e| {
224 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
225 .with_message(format!("Failed to find UIDs. ERROR: {e}"))
226 })?;
227 let parents_root_version = self.root_version.get(&parent).copied();
228 if let Some(v) = parents_root_version {
229 debug_assert!(contained_uids.contains(&child));
230 for id in contained_uids {
231 self.root_version.insert(id, v);
232 }
233 }
234 Ok(ObjectResult::Loaded((
235 child_ty.clone(),
236 child_move_type,
237 global_value,
238 )))
239 }
240}
241
242impl<'a> ObjectStore<'a> {
243 pub(super) fn new(
244 resolver: &'a dyn ChildObjectResolver,
245 root_version: BTreeMap<ObjectID, SequenceNumber>,
246 is_metered: bool,
247 constants: LocalProtocolConfig,
248 metrics: Arc<ExecutionMetrics>,
249 ) -> Self {
250 Self {
251 inner: Inner {
252 resolver,
253 root_version,
254 cached_objects: BTreeMap::new(),
255 is_metered,
256 constants: constants.clone(),
257 metrics,
258 },
259 store: BTreeMap::new(),
260 is_metered,
261 constants,
262 }
263 }
264
265 pub(super) fn object_exists(
266 &mut self,
267 parent: ObjectID,
268 child: ObjectID,
269 ) -> PartialVMResult<bool> {
270 if let Some(child_object) = self.store.get(&child) {
271 return child_object.value.exists();
272 }
273 Ok(self
274 .inner
275 .get_or_fetch_object_from_store(parent, child)?
276 .is_some())
277 }
278
279 pub(super) fn object_exists_and_has_type(
280 &mut self,
281 parent: ObjectID,
282 child: ObjectID,
283 child_move_type: &MoveObjectType,
284 ) -> PartialVMResult<bool> {
285 if let Some(child_object) = self.store.get(&child) {
286 return Ok(child_object.value.exists()? && &child_object.move_type == child_move_type);
288 }
289 Ok(self
290 .inner
291 .get_or_fetch_object_from_store(parent, child)?
292 .map(|move_obj| move_obj.type_() == child_move_type)
293 .unwrap_or(false))
294 }
295
296 pub(super) fn get_or_fetch_object(
297 &mut self,
298 parent: ObjectID,
299 child: ObjectID,
300 child_ty: &Type,
301 child_layout: &R::MoveTypeLayout,
302 child_fully_annotated_layout: &A::MoveTypeLayout,
303 child_move_type: MoveObjectType,
304 ) -> PartialVMResult<ObjectResult<&mut ChildObject>> {
305 let store_entries_count = self.store.len() as u64;
306 let child_object = match self.store.entry(child) {
307 btree_map::Entry::Vacant(e) => {
308 let (ty, move_type, value) = match self.inner.fetch_object_impl(
309 parent,
310 child,
311 child_ty,
312 child_layout,
313 child_fully_annotated_layout,
314 child_move_type,
315 )? {
316 ObjectResult::MismatchedType => return Ok(ObjectResult::MismatchedType),
317 ObjectResult::Loaded(res) => res,
318 };
319
320 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
321 self.is_metered,
322 store_entries_count,
323 self.constants.object_runtime_max_num_store_entries,
324 self.constants
325 .object_runtime_max_num_store_entries_system_tx,
326 self.inner
327 .metrics
328 .limits_metrics
329 .excessive_object_runtime_store_entries
330 ) {
331 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
332 .with_message(format!(
333 "Object runtime store limit ({} entries) reached",
334 lim
335 ))
336 .with_sub_status(
337 VMMemoryLimitExceededSubStatusCode::OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED
338 as u64,
339 ));
340 };
341
342 e.insert(ChildObject {
343 owner: parent,
344 ty,
345 move_type,
346 value,
347 })
348 }
349 btree_map::Entry::Occupied(e) => {
350 let child_object = e.into_mut();
351 if child_object.move_type != child_move_type {
352 return Ok(ObjectResult::MismatchedType);
353 }
354 child_object
355 }
356 };
357 Ok(ObjectResult::Loaded(child_object))
358 }
359
360 pub(super) fn add_object(
361 &mut self,
362 parent: ObjectID,
363 child: ObjectID,
364 child_ty: &Type,
365 child_move_type: MoveObjectType,
366 child_value: Value,
367 ) -> PartialVMResult<()> {
368 let mut child_object = ChildObject {
369 owner: parent,
370 ty: child_ty.clone(),
371 move_type: child_move_type,
372 value: GlobalValue::none(),
373 };
374 child_object.value.move_to(child_value).unwrap();
375
376 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
377 self.is_metered,
378 self.store.len(),
379 self.constants.object_runtime_max_num_store_entries,
380 self.constants
381 .object_runtime_max_num_store_entries_system_tx,
382 self.inner
383 .metrics
384 .limits_metrics
385 .excessive_object_runtime_store_entries
386 ) {
387 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
388 .with_message(format!(
389 "Object runtime store limit ({} entries) reached",
390 lim
391 ))
392 .with_sub_status(
393 VMMemoryLimitExceededSubStatusCode::OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED as u64,
394 ));
395 };
396
397 if let Some(prev) = self.store.insert(child, child_object) {
398 if prev.value.exists()? {
399 return Err(
400 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
401 .with_message(
402 "Duplicate addition of a child object. \
403 The previous value cannot be dropped. Indicates possible duplication \
404 of objects as an object was fetched more than once from two different \
405 parents, yet was not removed from one first"
406 .to_string(),
407 ),
408 );
409 }
410 }
411 Ok(())
412 }
413
414 pub(super) fn cached_objects(&self) -> &BTreeMap<ObjectID, Option<Object>> {
415 &self.inner.cached_objects
416 }
417
418 pub(super) fn take_effects(
420 &mut self,
421 ) -> (
422 BTreeMap<ObjectID, SequenceNumber>,
423 BTreeMap<ObjectID, ChildObjectEffect>,
424 ) {
425 let loaded_versions: BTreeMap<ObjectID, SequenceNumber> = self
426 .inner
427 .cached_objects
428 .iter()
429 .filter_map(|(id, obj_opt)| Some((*id, obj_opt.as_ref()?.version())))
430 .collect();
431 let child_object_effects = std::mem::take(&mut self.store)
432 .into_iter()
433 .filter_map(|(id, child_object)| {
434 let ChildObject {
435 owner,
436 ty,
437 move_type: _,
438 value,
439 } = child_object;
440 let loaded_version = loaded_versions.get(&id).copied();
441 let effect = value.into_effect()?;
442 let child_effect = ChildObjectEffect {
443 owner,
444 loaded_version,
445 ty,
446 effect,
447 };
448 Some((id, child_effect))
449 })
450 .collect();
451 (loaded_versions, child_object_effects)
452 }
453
454 pub(super) fn all_active_objects(&self) -> impl Iterator<Item = (&ObjectID, &Type, Value)> {
455 self.store.iter().filter_map(|(id, child_object)| {
456 let child_exists = child_object.value.exists().unwrap();
457 if !child_exists {
458 None
459 } else {
460 let copied_child_value = child_object
461 .value
462 .borrow_global()
463 .unwrap()
464 .value_as::<StructRef>()
465 .unwrap()
466 .read_ref()
467 .unwrap();
468 Some((id, &child_object.ty, copied_child_value))
469 }
470 })
471 }
472}