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 language_storage::StructTag,
17 runtime_value as R,
18 vm_status::StatusCode,
19};
20use move_vm_types::{
21 effects::Op,
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::ExecutionMetrics,
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<ExecutionMetrics>,
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<ExecutionMetrics>,
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.limits_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
223 .limits_metrics
224 .excessive_deleted_move_object_ids
225 ) {
226 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
227 .with_message(format!("Deleting more than {} IDs is not allowed", lim))
228 .with_sub_status(
229 VMMemoryLimitExceededSubStatusCode::DELETED_ID_COUNT_LIMIT_EXCEEDED as u64,
230 ));
231 };
232
233 let was_new = self.state.new_ids.shift_remove(&id);
234 if !was_new {
235 self.state.deleted_ids.insert(id);
236 }
237 Ok(())
238 }
239
240 pub fn transfer(
241 &mut self,
242 owner: Owner,
243 ty: Type,
244 obj: Value,
245 ) -> PartialVMResult<TransferResult> {
246 let id: ObjectID = get_object_id(obj.copy_value()?)?
247 .value_as::<AccountAddress>()?
248 .into();
249 let is_framework_obj = [
255 SUI_SYSTEM_STATE_OBJECT_ID,
256 SUI_CLOCK_OBJECT_ID,
257 SUI_AUTHENTICATOR_STATE_OBJECT_ID,
258 SUI_RANDOMNESS_STATE_OBJECT_ID,
259 SUI_DENY_LIST_OBJECT_ID,
260 ]
261 .contains(&id);
262 let transfer_result = if self.state.new_ids.contains(&id) {
263 TransferResult::New
264 } else if is_framework_obj {
265 self.state.new_ids.insert(id);
268 TransferResult::New
269 } else if let Some(prev_owner) = self.state.input_objects.get(&id) {
270 match (&owner, prev_owner) {
271 (Owner::Shared { .. }, Owner::Shared { .. }) => TransferResult::SameOwner,
273 (new, old) if new == old => TransferResult::SameOwner,
274 _ => TransferResult::OwnerChanged,
275 }
276 } else {
277 TransferResult::OwnerChanged
278 };
279
280 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
283 self.is_metered && !is_framework_obj, self.state.transfers.len(),
286 self.protocol_config.max_num_transferred_move_object_ids(),
287 self.protocol_config
288 .max_num_transferred_move_object_ids_system_tx(),
289 self.metrics
290 .limits_metrics
291 .excessive_transferred_move_object_ids
292 ) {
293 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
294 .with_message(format!("Transferring more than {} IDs is not allowed", lim))
295 .with_sub_status(
296 VMMemoryLimitExceededSubStatusCode::TRANSFER_ID_COUNT_LIMIT_EXCEEDED as u64,
297 ));
298 };
299
300 self.state.transfers.insert(id, (owner, ty, obj));
301 Ok(transfer_result)
302 }
303
304 pub fn emit_event(&mut self, ty: Type, tag: StructTag, event: Value) -> PartialVMResult<()> {
305 if self.state.events.len() >= (self.protocol_config.max_num_event_emit() as usize) {
306 return Err(max_event_error(self.protocol_config.max_num_event_emit()));
307 }
308 self.state.events.push((ty, tag, event));
309 Ok(())
310 }
311
312 pub fn take_user_events(&mut self) -> Vec<(Type, StructTag, Value)> {
313 std::mem::take(&mut self.state.events)
314 }
315
316 pub(crate) fn child_object_exists(
317 &mut self,
318 parent: ObjectID,
319 child: ObjectID,
320 ) -> PartialVMResult<bool> {
321 self.child_object_store.object_exists(parent, child)
322 }
323
324 pub(crate) fn child_object_exists_and_has_type(
325 &mut self,
326 parent: ObjectID,
327 child: ObjectID,
328 child_type: &MoveObjectType,
329 ) -> PartialVMResult<bool> {
330 self.child_object_store
331 .object_exists_and_has_type(parent, child, child_type)
332 }
333
334 pub(super) fn receive_object(
335 &mut self,
336 parent: ObjectID,
337 child: ObjectID,
338 child_version: SequenceNumber,
339 child_ty: &Type,
340 child_layout: &R::MoveTypeLayout,
341 child_fully_annotated_layout: &MoveTypeLayout,
342 child_move_type: MoveObjectType,
343 ) -> PartialVMResult<Option<ObjectResult<Value>>> {
344 let Some((value, obj_meta)) = self.child_object_store.receive_object(
345 parent,
346 child,
347 child_version,
348 child_ty,
349 child_layout,
350 child_fully_annotated_layout,
351 child_move_type,
352 )?
353 else {
354 return Ok(None);
355 };
356 if self.state.received.insert(child, obj_meta).is_some() {
359 return Err(
362 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(format!(
363 "Object {child} at version {child_version} already received. This can only happen \
364 if multiple `Receiving` arguments exist for the same object in the transaction which is impossible."
365 )),
366 );
367 }
368 Ok(Some(value))
369 }
370
371 pub(crate) fn get_or_fetch_child_object(
372 &mut self,
373 parent: ObjectID,
374 child: ObjectID,
375 child_ty: &Type,
376 child_layout: &R::MoveTypeLayout,
377 child_fully_annotated_layout: &MoveTypeLayout,
378 child_move_type: MoveObjectType,
379 ) -> PartialVMResult<ObjectResult<&mut GlobalValue>> {
380 let res = self.child_object_store.get_or_fetch_object(
381 parent,
382 child,
383 child_ty,
384 child_layout,
385 child_fully_annotated_layout,
386 child_move_type,
387 )?;
388 Ok(match res {
389 ObjectResult::MismatchedType => ObjectResult::MismatchedType,
390 ObjectResult::Loaded(child_object) => ObjectResult::Loaded(&mut child_object.value),
391 })
392 }
393
394 pub(crate) fn add_child_object(
395 &mut self,
396 parent: ObjectID,
397 child: ObjectID,
398 child_ty: &Type,
399 child_move_type: MoveObjectType,
400 child_value: Value,
401 ) -> PartialVMResult<()> {
402 self.child_object_store
403 .add_object(parent, child, child_ty, child_move_type, child_value)
404 }
405
406 pub(crate) fn take_state(&mut self) -> ObjectRuntimeState {
408 std::mem::take(&mut self.state)
409 }
410
411 pub fn finish(mut self) -> Result<RuntimeResults, ExecutionError> {
412 let loaded_child_objects = self.loaded_runtime_objects();
413 let child_effects = self.child_object_store.take_effects();
414 self.state.finish(loaded_child_objects, child_effects)
415 }
416
417 pub(crate) fn all_active_child_objects(
418 &self,
419 ) -> impl Iterator<Item = (&ObjectID, &Type, Value)> {
420 self.child_object_store.all_active_objects()
421 }
422
423 pub fn loaded_runtime_objects(&self) -> BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata> {
424 debug_assert!(self
427 .child_object_store
428 .cached_objects()
429 .keys()
430 .all(|id| !self.state.received.contains_key(id)));
431 self.child_object_store
432 .cached_objects()
433 .iter()
434 .filter_map(|(id, obj_opt)| {
435 obj_opt.as_ref().map(|obj| {
436 (
437 *id,
438 DynamicallyLoadedObjectMetadata {
439 version: obj.version(),
440 digest: obj.digest(),
441 storage_rebate: obj.storage_rebate,
442 owner: obj.owner.clone(),
443 previous_transaction: obj.previous_transaction,
444 },
445 )
446 })
447 })
448 .chain(
449 self.state
450 .received
451 .iter()
452 .map(|(id, meta)| (*id, meta.clone())),
453 )
454 .collect()
455 }
456
457 pub fn wrapped_object_containers(&self) -> BTreeMap<ObjectID, ObjectID> {
460 self.child_object_store.wrapped_object_containers().clone()
461 }
462}
463
464pub fn max_event_error(max_events: u64) -> PartialVMError {
465 PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
466 .with_message(format!(
467 "Emitting more than {} events is not allowed",
468 max_events
469 ))
470 .with_sub_status(VMMemoryLimitExceededSubStatusCode::EVENT_COUNT_LIMIT_EXCEEDED as u64)
471}
472
473impl ObjectRuntimeState {
474 pub(crate) fn finish(
483 mut self,
484 loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
485 child_object_effects: BTreeMap<ObjectID, ChildObjectEffect>,
486 ) -> Result<RuntimeResults, ExecutionError> {
487 let mut loaded_child_objects: BTreeMap<_, _> = loaded_child_objects
488 .into_iter()
489 .map(|(id, metadata)| {
490 (
491 id,
492 LoadedRuntimeObject {
493 version: metadata.version,
494 is_modified: false,
495 },
496 )
497 })
498 .collect();
499 for (child, child_object_effect) in child_object_effects {
500 let ChildObjectEffect {
501 owner: parent,
502 ty,
503 effect,
504 } = child_object_effect;
505
506 if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
507 loaded_child.is_modified = true;
508 }
509
510 match effect {
511 Op::Modify(v) => {
513 debug_assert!(!self.transfers.contains_key(&child));
514 debug_assert!(!self.new_ids.contains(&child));
515 debug_assert!(loaded_child_objects.contains_key(&child));
516 self.transfers
517 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
518 }
519
520 Op::New(v) => {
521 debug_assert!(!self.transfers.contains_key(&child));
522 self.transfers
523 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
524 }
525
526 Op::Delete => {
527 if self.transfers.contains_key(&child) {
529 debug_assert!(!self.deleted_ids.contains(&child));
530 }
531 if self.deleted_ids.contains(&child) {
533 debug_assert!(!self.transfers.contains_key(&child));
534 debug_assert!(!self.new_ids.contains(&child));
535 }
536 }
537 }
538 }
539 let ObjectRuntimeState {
540 input_objects: _,
541 new_ids,
542 deleted_ids,
543 transfers,
544 events: user_events,
545 total_events_size: _,
546 received,
547 } = self;
548
549 check_circular_ownership(
552 transfers
553 .iter()
554 .map(|(id, (owner, _, _))| (*id, owner.clone())),
555 )?;
556 let written_objects: IndexMap<_, _> = transfers
563 .into_iter()
564 .map(|(id, (owner, type_, value))| {
565 if let Some(loaded_child) = loaded_child_objects.get_mut(&id) {
566 loaded_child.is_modified = true;
567 }
568 (id, (owner, type_, value))
569 })
570 .collect();
571 for deleted_id in &deleted_ids {
572 if let Some(loaded_child) = loaded_child_objects.get_mut(deleted_id) {
573 loaded_child.is_modified = true;
574 }
575 }
576
577 for (received_object, _) in received.into_iter() {
581 match loaded_child_objects.get_mut(&received_object) {
582 Some(loaded_child) => {
583 loaded_child.is_modified = true;
584 }
585 None => {
586 return Err(ExecutionError::invariant_violation(format!(
587 "Failed to find received UID {received_object} in loaded child objects."
588 )))
589 }
590 }
591 }
592
593 Ok(RuntimeResults {
594 writes: written_objects,
595 user_events,
596 loaded_child_objects,
597 created_object_ids: new_ids,
598 deleted_object_ids: deleted_ids,
599 })
600 }
601
602 pub fn total_events_size(&self) -> u64 {
603 self.total_events_size
604 }
605
606 pub fn incr_total_events_size(&mut self, size: u64) {
607 self.total_events_size += size;
608 }
609}
610
611fn check_circular_ownership(
612 transfers: impl IntoIterator<Item = (ObjectID, Owner)>,
613) -> Result<(), ExecutionError> {
614 let mut object_owner_map = BTreeMap::new();
615 for (id, recipient) in transfers {
616 object_owner_map.remove(&id);
617 match recipient {
618 Owner::AddressOwner(_) | Owner::Shared { .. } | Owner::Immutable => (),
619 Owner::ObjectOwner(new_owner) => {
620 let new_owner: ObjectID = new_owner.into();
621 let mut cur = new_owner;
622 loop {
623 if cur == id {
624 return Err(ExecutionError::from_kind(
625 ExecutionErrorKind::CircularObjectOwnership { object: cur },
626 ));
627 }
628 if let Some(parent) = object_owner_map.get(&cur) {
629 cur = *parent;
630 } else {
631 break;
632 }
633 }
634 object_owner_map.insert(id, new_owner);
635 }
636 Owner::ConsensusAddressOwner { .. } => {
637 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
638 }
639 }
640 }
641 Ok(())
642}
643
644pub fn get_all_uids(
650 fully_annotated_layout: &MoveTypeLayout,
651 bcs_bytes: &[u8],
652) -> Result<BTreeSet<ObjectID>, String> {
653 let mut ids = BTreeSet::new();
654 struct UIDTraversal<'i>(&'i mut BTreeSet<ObjectID>);
655 struct UIDCollector<'i>(&'i mut BTreeSet<ObjectID>);
656
657 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDTraversal<'_> {
658 type Error = AV::Error;
659
660 fn traverse_struct(
661 &mut self,
662 driver: &mut AV::StructDriver<'_, 'b, 'l>,
663 ) -> Result<(), Self::Error> {
664 if driver.struct_layout().type_ == UID::type_() {
665 while driver.next_field(&mut UIDCollector(self.0))?.is_some() {}
666 } else {
667 while driver.next_field(self)?.is_some() {}
668 }
669 Ok(())
670 }
671 }
672
673 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDCollector<'_> {
674 type Error = AV::Error;
675 fn traverse_address(
676 &mut self,
677 _driver: &AV::ValueDriver<'_, 'b, 'l>,
678 value: AccountAddress,
679 ) -> Result<(), Self::Error> {
680 self.0.insert(value.into());
681 Ok(())
682 }
683 }
684
685 MoveValue::visit_deserialize(
686 bcs_bytes,
687 fully_annotated_layout,
688 &mut UIDTraversal(&mut ids),
689 )
690 .map_err(|e| format!("Failed to deserialize. {e}"))?;
691 Ok(ids)
692}