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