1pub(crate) mod object_store;
5
6use self::object_store::{ChildObjectEffect, ObjectResult};
7use super::get_object_id;
8use better_any::{Tid, TidAble};
9use indexmap::map::IndexMap;
10use indexmap::set::IndexSet;
11use move_binary_format::errors::{PartialVMError, PartialVMResult};
12use move_core_types::{
13 account_address::AccountAddress,
14 annotated_value::{MoveTypeLayout, MoveValue},
15 annotated_visitor as AV,
16 effects::Op,
17 language_storage::StructTag,
18 runtime_value as R,
19 vm_status::StatusCode,
20};
21use move_vm_types::{
22 loaded_data::runtime_types::Type,
23 values::{GlobalValue, Value},
24};
25use object_store::ChildObjectStore;
26use std::{
27 collections::{BTreeMap, BTreeSet},
28 sync::Arc,
29};
30use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
31use sui_types::{
32 base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress},
33 committee::EpochId,
34 error::{ExecutionError, ExecutionErrorKind, VMMemoryLimitExceededSubStatusCode},
35 execution::DynamicallyLoadedObjectMetadata,
36 id::UID,
37 metrics::LimitsMetrics,
38 object::{MoveObject, Owner},
39 storage::ChildObjectResolver,
40 SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_CLOCK_OBJECT_ID, SUI_DENY_LIST_OBJECT_ID,
41 SUI_RANDOMNESS_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_ID,
42};
43
44pub enum ObjectEvent {
45 Transfer(Owner, MoveObject),
47 DeleteObjectID(ObjectID),
49}
50
51type Set<K> = IndexSet<K>;
52
53#[derive(Default)]
54pub(crate) struct TestInventories {
55 pub(crate) objects: BTreeMap<ObjectID, Value>,
56 pub(crate) address_inventories: BTreeMap<SuiAddress, BTreeMap<Type, Set<ObjectID>>>,
58 pub(crate) shared_inventory: BTreeMap<Type, Set<ObjectID>>,
60 pub(crate) immutable_inventory: BTreeMap<Type, Set<ObjectID>>,
61 pub(crate) taken_immutable_values: BTreeMap<Type, BTreeMap<ObjectID, Value>>,
62 pub(crate) taken: BTreeMap<ObjectID, Owner>,
64}
65
66pub struct LoadedRuntimeObject {
67 pub version: SequenceNumber,
68 pub is_modified: bool,
69}
70
71pub struct RuntimeResults {
72 pub writes: IndexMap<ObjectID, (Owner, Type, Value)>,
73 pub user_events: Vec<(Type, StructTag, Value)>,
74 pub loaded_child_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
76 pub created_object_ids: Set<ObjectID>,
77 pub deleted_object_ids: Set<ObjectID>,
78}
79
80#[derive(Default)]
81pub(crate) struct ObjectRuntimeState {
82 pub(crate) input_objects: BTreeMap<ObjectID, Owner>,
83 new_ids: Set<ObjectID>,
85 deleted_ids: Set<ObjectID>,
87 transfers: IndexMap<ObjectID, (Owner, Type, Value)>,
90 events: Vec<(Type, StructTag, Value)>,
91 total_events_size: u64,
93 received: IndexMap<ObjectID, DynamicallyLoadedObjectMetadata>,
94}
95
96#[derive(Tid)]
97pub struct ObjectRuntime<'a> {
98 child_object_store: ChildObjectStore<'a>,
99 pub(crate) test_inventories: TestInventories,
101 pub(crate) state: ObjectRuntimeState,
103 is_metered: bool,
105
106 pub(crate) protocol_config: &'a ProtocolConfig,
107 pub(crate) metrics: Arc<LimitsMetrics>,
108}
109
110pub enum TransferResult {
111 New,
112 SameOwner,
113 OwnerChanged,
114}
115
116pub struct InputObject {
117 pub contained_uids: BTreeSet<ObjectID>,
118 pub version: SequenceNumber,
119 pub owner: Owner,
120}
121
122impl TestInventories {
123 fn new() -> Self {
124 Self::default()
125 }
126}
127
128impl<'a> ObjectRuntime<'a> {
129 pub fn new(
130 object_resolver: &'a dyn ChildObjectResolver,
131 input_objects: BTreeMap<ObjectID, InputObject>,
132 is_metered: bool,
133 protocol_config: &'a ProtocolConfig,
134 metrics: Arc<LimitsMetrics>,
135 epoch_id: EpochId,
136 ) -> Self {
137 let mut input_object_owners = BTreeMap::new();
138 let mut root_version = BTreeMap::new();
139 let mut wrapped_object_containers = BTreeMap::new();
140 for (id, input_object) in input_objects {
141 let InputObject {
142 contained_uids,
143 version,
144 owner,
145 } = input_object;
146 input_object_owners.insert(id, owner);
147 debug_assert!(contained_uids.contains(&id));
148 for contained_uid in contained_uids {
149 root_version.insert(contained_uid, version);
150 if contained_uid != id {
151 let prev = wrapped_object_containers.insert(contained_uid, id);
152 debug_assert!(prev.is_none());
153 }
154 }
155 }
156 Self {
157 child_object_store: ChildObjectStore::new(
158 object_resolver,
159 root_version,
160 wrapped_object_containers,
161 is_metered,
162 protocol_config,
163 metrics.clone(),
164 epoch_id,
165 ),
166 test_inventories: TestInventories::new(),
167 state: ObjectRuntimeState {
168 input_objects: input_object_owners,
169 new_ids: Set::new(),
170 deleted_ids: Set::new(),
171 transfers: IndexMap::new(),
172 events: vec![],
173 total_events_size: 0,
174 received: IndexMap::new(),
175 },
176 is_metered,
177 protocol_config,
178 metrics,
179 }
180 }
181
182 pub fn new_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
183 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
186 self.is_metered,
187 self.state.new_ids.len(),
188 self.protocol_config.max_num_new_move_object_ids(),
189 self.protocol_config.max_num_new_move_object_ids_system_tx(),
190 self.metrics.excessive_new_move_object_ids
191 ) {
192 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
193 .with_message(format!("Creating more than {} IDs is not allowed", lim))
194 .with_sub_status(
195 VMMemoryLimitExceededSubStatusCode::NEW_ID_COUNT_LIMIT_EXCEEDED as u64,
196 ));
197 };
198
199 let was_present = self.state.deleted_ids.shift_remove(&id);
203 if !was_present {
204 self.state.new_ids.insert(id);
206 }
207 Ok(())
208 }
209
210 pub fn delete_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
211 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
216 self.is_metered,
217 self.state.deleted_ids.len(),
218 self.protocol_config.max_num_deleted_move_object_ids(),
219 self.protocol_config
220 .max_num_deleted_move_object_ids_system_tx(),
221 self.metrics.excessive_deleted_move_object_ids
222 ) {
223 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
224 .with_message(format!("Deleting more than {} IDs is not allowed", lim))
225 .with_sub_status(
226 VMMemoryLimitExceededSubStatusCode::DELETED_ID_COUNT_LIMIT_EXCEEDED as u64,
227 ));
228 };
229
230 let was_new = self.state.new_ids.shift_remove(&id);
231 if !was_new {
232 self.state.deleted_ids.insert(id);
233 }
234 Ok(())
235 }
236
237 pub fn transfer(
238 &mut self,
239 owner: Owner,
240 ty: Type,
241 obj: Value,
242 ) -> PartialVMResult<TransferResult> {
243 let id: ObjectID = get_object_id(obj.copy_value()?)?
244 .value_as::<AccountAddress>()?
245 .into();
246 let is_framework_obj = [
252 SUI_SYSTEM_STATE_OBJECT_ID,
253 SUI_CLOCK_OBJECT_ID,
254 SUI_AUTHENTICATOR_STATE_OBJECT_ID,
255 SUI_RANDOMNESS_STATE_OBJECT_ID,
256 SUI_DENY_LIST_OBJECT_ID,
257 ]
258 .contains(&id);
259 let transfer_result = if self.state.new_ids.contains(&id) {
260 TransferResult::New
261 } else if is_framework_obj {
262 self.state.new_ids.insert(id);
265 TransferResult::New
266 } else if let Some(prev_owner) = self.state.input_objects.get(&id) {
267 match (&owner, prev_owner) {
268 (Owner::Shared { .. }, Owner::Shared { .. }) => TransferResult::SameOwner,
270 (new, old) if new == old => TransferResult::SameOwner,
271 _ => TransferResult::OwnerChanged,
272 }
273 } else {
274 TransferResult::OwnerChanged
275 };
276
277 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
280 self.is_metered && !is_framework_obj, self.state.transfers.len(),
283 self.protocol_config.max_num_transferred_move_object_ids(),
284 self.protocol_config
285 .max_num_transferred_move_object_ids_system_tx(),
286 self.metrics.excessive_transferred_move_object_ids
287 ) {
288 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
289 .with_message(format!("Transferring more than {} IDs is not allowed", lim))
290 .with_sub_status(
291 VMMemoryLimitExceededSubStatusCode::TRANSFER_ID_COUNT_LIMIT_EXCEEDED as u64,
292 ));
293 };
294
295 self.state.transfers.insert(id, (owner, ty, obj));
296 Ok(transfer_result)
297 }
298
299 pub fn emit_event(&mut self, ty: Type, tag: StructTag, event: Value) -> PartialVMResult<()> {
300 if self.state.events.len() >= (self.protocol_config.max_num_event_emit() as usize) {
301 return Err(max_event_error(self.protocol_config.max_num_event_emit()));
302 }
303 self.state.events.push((ty, tag, event));
304 Ok(())
305 }
306
307 pub fn take_user_events(&mut self) -> Vec<(Type, StructTag, Value)> {
308 std::mem::take(&mut self.state.events)
309 }
310
311 pub(crate) fn child_object_exists(
312 &mut self,
313 parent: ObjectID,
314 child: ObjectID,
315 ) -> PartialVMResult<bool> {
316 self.child_object_store.object_exists(parent, child)
317 }
318
319 pub(crate) fn child_object_exists_and_has_type(
320 &mut self,
321 parent: ObjectID,
322 child: ObjectID,
323 child_type: &MoveObjectType,
324 ) -> PartialVMResult<bool> {
325 self.child_object_store
326 .object_exists_and_has_type(parent, child, child_type)
327 }
328
329 pub(super) fn receive_object(
330 &mut self,
331 parent: ObjectID,
332 child: ObjectID,
333 child_version: SequenceNumber,
334 child_ty: &Type,
335 child_layout: &R::MoveTypeLayout,
336 child_fully_annotated_layout: &MoveTypeLayout,
337 child_move_type: MoveObjectType,
338 ) -> PartialVMResult<Option<ObjectResult<Value>>> {
339 let Some((value, obj_meta)) = self.child_object_store.receive_object(
340 parent,
341 child,
342 child_version,
343 child_ty,
344 child_layout,
345 child_fully_annotated_layout,
346 child_move_type,
347 )?
348 else {
349 return Ok(None);
350 };
351 if self.state.received.insert(child, obj_meta).is_some() {
354 return Err(
357 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(format!(
358 "Object {child} at version {child_version} already received. This can only happen \
359 if multiple `Receiving` arguments exist for the same object in the transaction which is impossible."
360 )),
361 );
362 }
363 Ok(Some(value))
364 }
365
366 pub(crate) fn get_or_fetch_child_object(
367 &mut self,
368 parent: ObjectID,
369 child: ObjectID,
370 child_ty: &Type,
371 child_layout: &R::MoveTypeLayout,
372 child_fully_annotated_layout: &MoveTypeLayout,
373 child_move_type: MoveObjectType,
374 ) -> PartialVMResult<ObjectResult<&mut GlobalValue>> {
375 let res = self.child_object_store.get_or_fetch_object(
376 parent,
377 child,
378 child_ty,
379 child_layout,
380 child_fully_annotated_layout,
381 child_move_type,
382 )?;
383 Ok(match res {
384 ObjectResult::MismatchedType => ObjectResult::MismatchedType,
385 ObjectResult::Loaded(child_object) => ObjectResult::Loaded(&mut child_object.value),
386 })
387 }
388
389 pub(crate) fn add_child_object(
390 &mut self,
391 parent: ObjectID,
392 child: ObjectID,
393 child_ty: &Type,
394 child_move_type: MoveObjectType,
395 child_value: Value,
396 ) -> PartialVMResult<()> {
397 self.child_object_store
398 .add_object(parent, child, child_ty, child_move_type, child_value)
399 }
400
401 pub(crate) fn take_state(&mut self) -> ObjectRuntimeState {
403 std::mem::take(&mut self.state)
404 }
405
406 pub fn finish(mut self) -> Result<RuntimeResults, ExecutionError> {
407 let loaded_child_objects = self.loaded_runtime_objects();
408 let child_effects = self.child_object_store.take_effects();
409 self.state.finish(loaded_child_objects, child_effects)
410 }
411
412 pub(crate) fn all_active_child_objects(
413 &self,
414 ) -> impl Iterator<Item = (&ObjectID, &Type, Value)> {
415 self.child_object_store.all_active_objects()
416 }
417
418 pub fn loaded_runtime_objects(&self) -> BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata> {
419 debug_assert!(self
422 .child_object_store
423 .cached_objects()
424 .keys()
425 .all(|id| !self.state.received.contains_key(id)));
426 self.child_object_store
427 .cached_objects()
428 .iter()
429 .filter_map(|(id, obj_opt)| {
430 obj_opt.as_ref().map(|obj| {
431 (
432 *id,
433 DynamicallyLoadedObjectMetadata {
434 version: obj.version(),
435 digest: obj.digest(),
436 storage_rebate: obj.storage_rebate,
437 owner: obj.owner.clone(),
438 previous_transaction: obj.previous_transaction,
439 },
440 )
441 })
442 })
443 .chain(
444 self.state
445 .received
446 .iter()
447 .map(|(id, meta)| (*id, meta.clone())),
448 )
449 .collect()
450 }
451
452 pub fn wrapped_object_containers(&self) -> BTreeMap<ObjectID, ObjectID> {
455 self.child_object_store.wrapped_object_containers().clone()
456 }
457}
458
459pub fn max_event_error(max_events: u64) -> PartialVMError {
460 PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
461 .with_message(format!(
462 "Emitting more than {} events is not allowed",
463 max_events
464 ))
465 .with_sub_status(VMMemoryLimitExceededSubStatusCode::EVENT_COUNT_LIMIT_EXCEEDED as u64)
466}
467
468impl ObjectRuntimeState {
469 pub(crate) fn finish(
478 mut self,
479 loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
480 child_object_effects: BTreeMap<ObjectID, ChildObjectEffect>,
481 ) -> Result<RuntimeResults, ExecutionError> {
482 let mut loaded_child_objects: BTreeMap<_, _> = loaded_child_objects
483 .into_iter()
484 .map(|(id, metadata)| {
485 (
486 id,
487 LoadedRuntimeObject {
488 version: metadata.version,
489 is_modified: false,
490 },
491 )
492 })
493 .collect();
494 for (child, child_object_effect) in child_object_effects {
495 let ChildObjectEffect {
496 owner: parent,
497 ty,
498 effect,
499 } = child_object_effect;
500
501 if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
502 loaded_child.is_modified = true;
503 }
504
505 match effect {
506 Op::Modify(v) => {
508 debug_assert!(!self.transfers.contains_key(&child));
509 debug_assert!(!self.new_ids.contains(&child));
510 debug_assert!(loaded_child_objects.contains_key(&child));
511 self.transfers
512 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
513 }
514
515 Op::New(v) => {
516 debug_assert!(!self.transfers.contains_key(&child));
517 self.transfers
518 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
519 }
520
521 Op::Delete => {
522 if self.transfers.contains_key(&child) {
524 debug_assert!(!self.deleted_ids.contains(&child));
525 }
526 if self.deleted_ids.contains(&child) {
528 debug_assert!(!self.transfers.contains_key(&child));
529 debug_assert!(!self.new_ids.contains(&child));
530 }
531 }
532 }
533 }
534 let ObjectRuntimeState {
535 input_objects: _,
536 new_ids,
537 deleted_ids,
538 transfers,
539 events: user_events,
540 total_events_size: _,
541 received,
542 } = self;
543
544 check_circular_ownership(
547 transfers
548 .iter()
549 .map(|(id, (owner, _, _))| (*id, owner.clone())),
550 )?;
551 let written_objects: IndexMap<_, _> = transfers
558 .into_iter()
559 .map(|(id, (owner, type_, value))| {
560 if let Some(loaded_child) = loaded_child_objects.get_mut(&id) {
561 loaded_child.is_modified = true;
562 }
563 (id, (owner, type_, value))
564 })
565 .collect();
566 for deleted_id in &deleted_ids {
567 if let Some(loaded_child) = loaded_child_objects.get_mut(deleted_id) {
568 loaded_child.is_modified = true;
569 }
570 }
571
572 for (received_object, _) in received.into_iter() {
576 match loaded_child_objects.get_mut(&received_object) {
577 Some(loaded_child) => {
578 loaded_child.is_modified = true;
579 }
580 None => {
581 return Err(ExecutionError::invariant_violation(format!(
582 "Failed to find received UID {received_object} in loaded child objects."
583 )))
584 }
585 }
586 }
587
588 Ok(RuntimeResults {
589 writes: written_objects,
590 user_events,
591 loaded_child_objects,
592 created_object_ids: new_ids,
593 deleted_object_ids: deleted_ids,
594 })
595 }
596
597 pub fn total_events_size(&self) -> u64 {
598 self.total_events_size
599 }
600
601 pub fn incr_total_events_size(&mut self, size: u64) {
602 self.total_events_size += size;
603 }
604}
605
606fn check_circular_ownership(
607 transfers: impl IntoIterator<Item = (ObjectID, Owner)>,
608) -> Result<(), ExecutionError> {
609 let mut object_owner_map = BTreeMap::new();
610 for (id, recipient) in transfers {
611 object_owner_map.remove(&id);
612 match recipient {
613 Owner::AddressOwner(_) | Owner::Shared { .. } | Owner::Immutable => (),
614 Owner::ObjectOwner(new_owner) => {
615 let new_owner: ObjectID = new_owner.into();
616 let mut cur = new_owner;
617 loop {
618 if cur == id {
619 return Err(ExecutionError::from_kind(
620 ExecutionErrorKind::CircularObjectOwnership { object: cur },
621 ));
622 }
623 if let Some(parent) = object_owner_map.get(&cur) {
624 cur = *parent;
625 } else {
626 break;
627 }
628 }
629 object_owner_map.insert(id, new_owner);
630 }
631 Owner::ConsensusAddressOwner { .. } => {
632 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
633 }
634 }
635 }
636 Ok(())
637}
638
639pub fn get_all_uids(
645 fully_annotated_layout: &MoveTypeLayout,
646 bcs_bytes: &[u8],
647) -> Result<BTreeSet<ObjectID>, String> {
648 let mut ids = BTreeSet::new();
649 struct UIDTraversal<'i>(&'i mut BTreeSet<ObjectID>);
650 struct UIDCollector<'i>(&'i mut BTreeSet<ObjectID>);
651
652 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDTraversal<'_> {
653 type Error = AV::Error;
654
655 fn traverse_struct(
656 &mut self,
657 driver: &mut AV::StructDriver<'_, 'b, 'l>,
658 ) -> Result<(), Self::Error> {
659 if driver.struct_layout().type_ == UID::type_() {
660 while driver.next_field(&mut UIDCollector(self.0))?.is_some() {}
661 } else {
662 while driver.next_field(self)?.is_some() {}
663 }
664 Ok(())
665 }
666 }
667
668 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDCollector<'_> {
669 type Error = AV::Error;
670 fn traverse_address(
671 &mut self,
672 _driver: &AV::ValueDriver<'_, 'b, 'l>,
673 value: AccountAddress,
674 ) -> Result<(), Self::Error> {
675 self.0.insert(value.into());
676 Ok(())
677 }
678 }
679
680 MoveValue::visit_deserialize(
681 bcs_bytes,
682 fully_annotated_layout,
683 &mut UIDTraversal(&mut ids),
684 )
685 .map_err(|e| format!("Failed to deserialize. {e}"))?;
686 Ok(ids)
687}