1pub(crate) mod accumulator;
5mod fingerprint;
6pub(crate) mod object_store;
7
8use crate::object_runtime::object_store::{CacheMetadata, ChildObjectEffect};
9
10use self::object_store::{ChildObjectEffects, ObjectResult};
11use super::get_object_id;
12use better_any::{Tid, TidAble};
13use indexmap::map::IndexMap;
14use indexmap::set::IndexSet;
15use move_binary_format::errors::{PartialVMError, PartialVMResult};
16use move_core_types::{
17 account_address::AccountAddress,
18 annotated_value::{MoveTypeLayout, MoveValue},
19 annotated_visitor as AV,
20 language_storage::StructTag,
21 runtime_value as R,
22 vm_status::StatusCode,
23};
24use move_vm_runtime::execution::values::{GlobalValue, Value};
25use move_vm_runtime::natives::extensions::NativeExtensionMarker;
26use object_store::{ActiveChildObject, ChildObjectStore};
27use std::{
28 collections::{BTreeMap, BTreeSet},
29 sync::Arc,
30};
31use sui_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
32use sui_types::{
33 SUI_ACCUMULATOR_ROOT_OBJECT_ID, SUI_ADDRESS_ALIAS_STATE_OBJECT_ID,
34 SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_BRIDGE_OBJECT_ID, SUI_CLOCK_OBJECT_ID,
35 SUI_COIN_REGISTRY_OBJECT_ID, SUI_DENY_LIST_OBJECT_ID, SUI_DISPLAY_REGISTRY_OBJECT_ID,
36 SUI_RANDOMNESS_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_ID, TypeTag,
37 base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress},
38 committee::EpochId,
39 error::{ExecutionError, VMMemoryLimitExceededSubStatusCode},
40 execution::DynamicallyLoadedObjectMetadata,
41 execution_status::ExecutionErrorKind,
42 id::UID,
43 metrics::LimitsMetrics,
44 object::{MoveObject, Owner},
45 storage::ChildObjectResolver,
46};
47use tracing::error;
48
49pub use accumulator::*;
50
51pub enum ObjectEvent {
52 Transfer(Owner, MoveObject),
54 DeleteObjectID(ObjectID),
56}
57
58type Set<K> = IndexSet<K>;
59
60#[derive(Default)]
61pub(crate) struct TestInventories {
62 pub(crate) objects: BTreeMap<ObjectID, Value>,
63 pub(crate) address_inventories: BTreeMap<SuiAddress, BTreeMap<MoveObjectType, Set<ObjectID>>>,
65 pub(crate) shared_inventory: BTreeMap<MoveObjectType, Set<ObjectID>>,
67 pub(crate) immutable_inventory: BTreeMap<MoveObjectType, Set<ObjectID>>,
68 pub(crate) taken_immutable_values: BTreeMap<MoveObjectType, BTreeMap<ObjectID, Value>>,
69 pub(crate) taken: BTreeMap<ObjectID, Owner>,
71 pub(crate) allocated_tickets: BTreeMap<ObjectID, (DynamicallyLoadedObjectMetadata, Value)>,
73}
74
75#[derive(Debug)]
76pub struct LoadedRuntimeObject {
77 pub version: SequenceNumber,
78 pub is_modified: bool,
79}
80
81pub struct RuntimeResults {
82 pub writes: IndexMap<ObjectID, (Owner, MoveObjectType, Value)>,
83 pub user_events: Vec<(StructTag, Value)>,
84 pub accumulator_events: Vec<MoveAccumulatorEvent>,
85 pub loaded_child_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
87 pub created_object_ids: Set<ObjectID>,
88 pub deleted_object_ids: Set<ObjectID>,
89 pub settlement_input_sui: u64,
90 pub settlement_output_sui: u64,
91}
92
93#[derive(Default)]
94pub(crate) struct ObjectRuntimeState {
95 pub(crate) input_objects: BTreeMap<ObjectID, Owner>,
96 new_ids: Set<ObjectID>,
98 generated_ids: Set<ObjectID>,
100 deleted_ids: Set<ObjectID>,
102 transfers: IndexMap<ObjectID, (Owner, MoveObjectType, Value)>,
105 events: Vec<(StructTag, Value)>,
106 accumulator_events: Vec<MoveAccumulatorEvent>,
107 total_events_size: u64,
109 received: IndexMap<ObjectID, DynamicallyLoadedObjectMetadata>,
110 settlement_input_sui: u64,
116 settlement_output_sui: u64,
117 accumulator_merge_totals: BTreeMap<(AccountAddress, TypeTag), u128>,
118 accumulator_split_totals: BTreeMap<(AccountAddress, TypeTag), u128>,
119}
120
121#[derive(Tid)]
122pub struct ObjectRuntime<'a> {
123 child_object_store: ChildObjectStore<'a>,
124 pub(crate) test_inventories: TestInventories,
126 pub(crate) state: ObjectRuntimeState,
128 is_metered: bool,
130
131 pub(crate) protocol_config: &'a ProtocolConfig,
132 pub(crate) metrics: Arc<LimitsMetrics>,
133}
134
135impl<'a> NativeExtensionMarker<'a> for ObjectRuntime<'a> {}
136
137pub enum TransferResult {
138 New,
139 SameOwner,
140 OwnerChanged,
141}
142
143pub struct InputObject {
144 pub contained_uids: BTreeSet<ObjectID>,
145 pub version: SequenceNumber,
146 pub owner: Owner,
147}
148
149impl TestInventories {
150 fn new() -> Self {
151 Self::default()
152 }
153}
154
155impl<'a> ObjectRuntime<'a> {
156 pub fn new(
157 object_resolver: &'a dyn ChildObjectResolver,
158 input_objects: BTreeMap<ObjectID, InputObject>,
159 is_metered: bool,
160 protocol_config: &'a ProtocolConfig,
161 metrics: Arc<LimitsMetrics>,
162 epoch_id: EpochId,
163 ) -> Self {
164 let mut input_object_owners = BTreeMap::new();
165 let mut root_version = BTreeMap::new();
166 let mut wrapped_object_containers = BTreeMap::new();
167 for (id, input_object) in input_objects {
168 let InputObject {
169 contained_uids,
170 version,
171 owner,
172 } = input_object;
173 input_object_owners.insert(id, owner);
174 debug_assert!(contained_uids.contains(&id));
175 for contained_uid in contained_uids {
176 root_version.insert(contained_uid, version);
177 if contained_uid != id {
178 let prev = wrapped_object_containers.insert(contained_uid, id);
179 debug_assert!(prev.is_none());
180 }
181 }
182 }
183 Self {
184 child_object_store: ChildObjectStore::new(
185 object_resolver,
186 root_version,
187 wrapped_object_containers,
188 is_metered,
189 protocol_config,
190 metrics.clone(),
191 epoch_id,
192 ),
193 test_inventories: TestInventories::new(),
194 state: ObjectRuntimeState {
195 input_objects: input_object_owners,
196 new_ids: Set::new(),
197 generated_ids: Set::new(),
198 deleted_ids: Set::new(),
199 transfers: IndexMap::new(),
200 events: vec![],
201 accumulator_events: vec![],
202 total_events_size: 0,
203 received: IndexMap::new(),
204 settlement_input_sui: 0,
205 settlement_output_sui: 0,
206 accumulator_merge_totals: BTreeMap::new(),
207 accumulator_split_totals: BTreeMap::new(),
208 },
209 is_metered,
210 protocol_config,
211 metrics,
212 }
213 }
214
215 pub fn new_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
216 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
219 self.is_metered,
220 self.state.new_ids.len(),
221 self.protocol_config.max_num_new_move_object_ids(),
222 self.protocol_config.max_num_new_move_object_ids_system_tx(),
223 self.metrics.excessive_new_move_object_ids
224 ) {
225 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
226 .with_message(format!("Creating more than {} IDs is not allowed", lim))
227 .with_sub_status(
228 VMMemoryLimitExceededSubStatusCode::NEW_ID_COUNT_LIMIT_EXCEEDED as u64,
229 ));
230 };
231
232 let was_present = self.state.deleted_ids.shift_remove(&id);
236 if !was_present {
237 self.state.generated_ids.insert(id);
239 self.state.new_ids.insert(id);
240 }
241 Ok(())
242 }
243
244 pub fn delete_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
245 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
250 self.is_metered,
251 self.state.deleted_ids.len(),
252 self.protocol_config.max_num_deleted_move_object_ids(),
253 self.protocol_config
254 .max_num_deleted_move_object_ids_system_tx(),
255 self.metrics.excessive_deleted_move_object_ids
256 ) {
257 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
258 .with_message(format!("Deleting more than {} IDs is not allowed", lim))
259 .with_sub_status(
260 VMMemoryLimitExceededSubStatusCode::DELETED_ID_COUNT_LIMIT_EXCEEDED as u64,
261 ));
262 };
263
264 let was_new = self.state.new_ids.shift_remove(&id);
265 if !was_new {
266 self.state.deleted_ids.insert(id);
267 }
268 Ok(())
269 }
270
271 pub fn transfer(
274 &mut self,
275 owner: Owner,
276 ty: MoveObjectType,
277 obj: Value,
278 end_of_transaction: bool,
279 ) -> PartialVMResult<TransferResult> {
280 let id: ObjectID = get_object_id(obj.copy_value())?
281 .value_as::<AccountAddress>()?
282 .into();
283 let is_framework_obj = [
289 SUI_SYSTEM_STATE_OBJECT_ID,
290 SUI_CLOCK_OBJECT_ID,
291 SUI_AUTHENTICATOR_STATE_OBJECT_ID,
292 SUI_RANDOMNESS_STATE_OBJECT_ID,
293 SUI_DENY_LIST_OBJECT_ID,
294 SUI_BRIDGE_OBJECT_ID,
295 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
296 SUI_COIN_REGISTRY_OBJECT_ID,
297 SUI_DISPLAY_REGISTRY_OBJECT_ID,
298 SUI_ADDRESS_ALIAS_STATE_OBJECT_ID,
299 ]
300 .contains(&id);
301 let transfer_result = if self.state.new_ids.contains(&id) {
302 TransferResult::New
303 } else if let Some(prev_owner) = self.state.input_objects.get(&id) {
304 match (&owner, prev_owner) {
305 (Owner::Shared { .. }, Owner::Shared { .. }) => TransferResult::SameOwner,
307 (
308 Owner::ConsensusAddressOwner {
309 owner: new_owner, ..
310 },
311 Owner::ConsensusAddressOwner {
312 owner: old_owner, ..
313 },
314 ) if new_owner == old_owner => TransferResult::SameOwner,
315 (new, old) if new == old => TransferResult::SameOwner,
316 _ => TransferResult::OwnerChanged,
317 }
318 } else if is_framework_obj {
319 self.state.new_ids.insert(id);
322 self.state.generated_ids.insert(id);
323 TransferResult::New
324 } else {
325 TransferResult::OwnerChanged
326 };
327 if end_of_transaction
329 && !matches!(
330 transfer_result,
331 TransferResult::New | TransferResult::SameOwner
332 )
333 {
334 return Err(
335 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(
336 format!(
337 "Untransferred object {} had its owner change or was not new",
338 id
339 ),
340 ),
341 );
342 }
343
344 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
347 self.is_metered && !is_framework_obj && !end_of_transaction,
352 self.state.transfers.len(),
353 self.protocol_config.max_num_transferred_move_object_ids(),
354 self.protocol_config
355 .max_num_transferred_move_object_ids_system_tx(),
356 self.metrics.excessive_transferred_move_object_ids
357 ) {
358 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
359 .with_message(format!("Transferring more than {} IDs is not allowed", lim))
360 .with_sub_status(
361 VMMemoryLimitExceededSubStatusCode::TRANSFER_ID_COUNT_LIMIT_EXCEEDED as u64,
362 ));
363 };
364
365 self.state.transfers.insert(id, (owner, ty, obj));
366 Ok(transfer_result)
367 }
368
369 pub fn emit_event(&mut self, tag: StructTag, event: Value) -> PartialVMResult<()> {
370 if self.state.events.len() >= (self.protocol_config.max_num_event_emit() as usize) {
371 return Err(max_event_error(self.protocol_config.max_num_event_emit()));
372 }
373 self.state.events.push((tag, event));
374 Ok(())
375 }
376
377 pub fn take_user_events(&mut self) -> Vec<(StructTag, Value)> {
378 std::mem::take(&mut self.state.events)
379 }
380
381 pub fn emit_accumulator_event(
382 &mut self,
383 accumulator_id: ObjectID,
384 action: MoveAccumulatorAction,
385 target_addr: AccountAddress,
386 target_ty: TypeTag,
387 value: MoveAccumulatorValue,
388 ) -> PartialVMResult<()> {
389 if let MoveAccumulatorValue::U64(amount) = value {
390 let key = (target_addr, target_ty.clone());
391
392 match action {
393 MoveAccumulatorAction::Merge => {
394 let current = self
395 .state
396 .accumulator_merge_totals
397 .get(&key)
398 .copied()
399 .unwrap_or(0);
400 let new_total = current + amount as u128;
401 if new_total > u64::MAX as u128 {
402 return Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR)
403 .with_message(format!(
404 "accumulator merge overflow: total merges {} exceed u64::MAX",
405 new_total
406 )));
407 }
408 self.state.accumulator_merge_totals.insert(key, new_total);
409 }
410 MoveAccumulatorAction::Split => {
411 let current = self
412 .state
413 .accumulator_split_totals
414 .get(&key)
415 .copied()
416 .unwrap_or(0);
417 let new_total = current + amount as u128;
418 if new_total > u64::MAX as u128 {
419 return Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR)
420 .with_message(format!(
421 "accumulator split overflow: total splits {} exceed u64::MAX",
422 new_total
423 )));
424 }
425 self.state.accumulator_split_totals.insert(key, new_total);
426 }
427 }
428 }
429
430 let event = MoveAccumulatorEvent {
431 accumulator_id,
432 action,
433 target_addr,
434 target_ty,
435 value,
436 };
437 self.state.accumulator_events.push(event);
438 Ok(())
439 }
440
441 pub(crate) fn child_object_exists(
442 &mut self,
443 parent: ObjectID,
444 child: ObjectID,
445 ) -> PartialVMResult<CacheMetadata<bool>> {
446 self.child_object_store.object_exists(parent, child)
447 }
448
449 pub(crate) fn child_object_exists_and_has_type(
450 &mut self,
451 parent: ObjectID,
452 child: ObjectID,
453 child_type: &MoveObjectType,
454 ) -> PartialVMResult<CacheMetadata<bool>> {
455 self.child_object_store
456 .object_exists_and_has_type(parent, child, child_type)
457 }
458
459 pub(super) fn receive_object(
460 &mut self,
461 parent: ObjectID,
462 child: ObjectID,
463 child_version: SequenceNumber,
464 child_layout: &R::MoveTypeLayout,
465 child_fully_annotated_layout: &MoveTypeLayout,
466 child_move_type: MoveObjectType,
467 ) -> PartialVMResult<Option<ObjectResult<CacheMetadata<Value>>>> {
468 let Some((value, obj_meta)) = self.child_object_store.receive_object(
469 parent,
470 child,
471 child_version,
472 child_layout,
473 child_fully_annotated_layout,
474 child_move_type,
475 )?
476 else {
477 return Ok(None);
478 };
479 if self.state.received.insert(child, obj_meta).is_some() {
482 return Err(
485 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(format!(
486 "Object {child} at version {child_version} already received. This can only happen \
487 if multiple `Receiving` arguments exist for the same object in the transaction which is impossible."
488 )),
489 );
490 }
491 Ok(Some(value))
492 }
493
494 pub(crate) fn get_or_fetch_child_object(
495 &mut self,
496 parent: ObjectID,
497 child: ObjectID,
498 child_layout: &R::MoveTypeLayout,
499 child_fully_annotated_layout: &MoveTypeLayout,
500 child_move_type: MoveObjectType,
501 ) -> PartialVMResult<ObjectResult<CacheMetadata<&mut GlobalValue>>> {
502 let res = self.child_object_store.get_or_fetch_object(
503 parent,
504 child,
505 child_layout,
506 child_fully_annotated_layout,
507 child_move_type,
508 )?;
509 Ok(match res {
510 ObjectResult::MismatchedType => ObjectResult::MismatchedType,
511 ObjectResult::Loaded((cache_info, child_object)) => {
512 ObjectResult::Loaded((cache_info, &mut child_object.value))
513 }
514 })
515 }
516
517 pub(crate) fn add_child_object(
518 &mut self,
519 parent: ObjectID,
520 child: ObjectID,
521 child_move_type: MoveObjectType,
522 child_value: Value,
523 ) -> PartialVMResult<()> {
524 self.child_object_store
525 .add_object(parent, child, child_move_type, child_value)
526 }
527
528 pub(crate) fn config_setting_unsequenced_read(
529 &mut self,
530 config_id: ObjectID,
531 name_df_id: ObjectID,
532 field_setting_layout: &R::MoveTypeLayout,
533 field_setting_object_type: &MoveObjectType,
534 ) -> Option<Value> {
535 match self.child_object_store.config_setting_unsequenced_read(
536 config_id,
537 name_df_id,
538 field_setting_layout,
539 field_setting_object_type,
540 ) {
541 Err(e) => {
542 error!(
543 "Failed to read config setting.
544 config_id: {config_id},
545 name_df_id: {name_df_id},
546 field_setting_object_type: {field_setting_object_type:?},
547 error: {e}"
548 );
549 None
550 }
551 Ok(ObjectResult::MismatchedType) | Ok(ObjectResult::Loaded(None)) => None,
552 Ok(ObjectResult::Loaded(Some(value))) => Some(value),
553 }
554 }
555
556 pub(super) fn config_setting_cache_update(
557 &mut self,
558 config_id: ObjectID,
559 name_df_id: ObjectID,
560 setting_value_object_type: MoveObjectType,
561 value: Option<Value>,
562 ) {
563 self.child_object_store.config_setting_cache_update(
564 config_id,
565 name_df_id,
566 setting_value_object_type,
567 value,
568 )
569 }
570
571 pub(crate) fn take_state(&mut self) -> ObjectRuntimeState {
573 std::mem::take(&mut self.state)
574 }
575
576 pub fn is_deleted(&self, id: &ObjectID) -> bool {
577 self.state.deleted_ids.contains(id)
578 }
579
580 pub fn is_transferred(&self, id: &ObjectID) -> Option<Owner> {
581 self.state
582 .transfers
583 .get(id)
584 .map(|(owner, _, _)| owner.clone())
585 }
586
587 pub fn finish(mut self) -> Result<RuntimeResults, ExecutionError> {
588 let loaded_child_objects = self.loaded_runtime_objects();
589 let child_effects = self.child_object_store.take_effects().map_err(|e| {
590 ExecutionError::invariant_violation(format!("Failed to take child object effects: {e}"))
591 })?;
592 self.state.finish(loaded_child_objects, child_effects)
593 }
594
595 pub(crate) fn all_active_child_objects(&self) -> impl Iterator<Item = ActiveChildObject<'_>> {
596 self.child_object_store.all_active_objects()
597 }
598
599 pub fn loaded_runtime_objects(&self) -> BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata> {
600 debug_assert!(
603 self.child_object_store
604 .cached_objects()
605 .keys()
606 .all(|id| !self.state.received.contains_key(id))
607 );
608 self.child_object_store
609 .cached_objects()
610 .iter()
611 .filter_map(|(id, obj_opt)| {
612 obj_opt.as_ref().map(|obj| {
613 (
614 *id,
615 DynamicallyLoadedObjectMetadata {
616 version: obj.version(),
617 digest: obj.digest(),
618 storage_rebate: obj.storage_rebate,
619 owner: obj.owner.clone(),
620 previous_transaction: obj.previous_transaction,
621 },
622 )
623 })
624 })
625 .chain(
626 self.state
627 .received
628 .iter()
629 .map(|(id, meta)| (*id, meta.clone())),
630 )
631 .collect()
632 }
633
634 pub fn wrapped_object_containers(&self) -> BTreeMap<ObjectID, ObjectID> {
637 self.child_object_store.wrapped_object_containers().clone()
638 }
639
640 pub fn record_settlement_sui_conservation(&mut self, input_sui: u64, output_sui: u64) {
641 self.state.settlement_input_sui += input_sui;
642 self.state.settlement_output_sui += output_sui;
643 }
644
645 pub fn generated_object_ids(&self) -> BTreeSet<ObjectID> {
648 self.state.generated_ids.iter().cloned().collect()
649 }
650}
651
652pub fn max_event_error(max_events: u64) -> PartialVMError {
653 PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
654 .with_message(format!(
655 "Emitting more than {} events is not allowed",
656 max_events
657 ))
658 .with_sub_status(VMMemoryLimitExceededSubStatusCode::EVENT_COUNT_LIMIT_EXCEEDED as u64)
659}
660
661impl ObjectRuntimeState {
662 pub(crate) fn finish(
671 mut self,
672 loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
673 child_object_effects: ChildObjectEffects,
674 ) -> Result<RuntimeResults, ExecutionError> {
675 let mut loaded_child_objects: BTreeMap<_, _> = loaded_child_objects
676 .into_iter()
677 .map(|(id, metadata)| {
678 (
679 id,
680 LoadedRuntimeObject {
681 version: metadata.version,
682 is_modified: false,
683 },
684 )
685 })
686 .collect();
687 self.apply_child_object_effects(&mut loaded_child_objects, child_object_effects);
688 let ObjectRuntimeState {
689 input_objects: _,
690 new_ids,
691 generated_ids,
692 deleted_ids,
693 transfers,
694 events: user_events,
695 total_events_size: _,
696 received,
697 accumulator_events,
698 settlement_input_sui,
699 settlement_output_sui,
700 accumulator_merge_totals: _,
701 accumulator_split_totals: _,
702 } = self;
703
704 debug_assert!(new_ids.is_subset(&generated_ids));
706
707 check_circular_ownership(
710 transfers
711 .iter()
712 .map(|(id, (owner, _, _))| (*id, owner.clone())),
713 )?;
714 let written_objects: IndexMap<_, _> = transfers
721 .into_iter()
722 .map(|(id, (owner, type_, value))| {
723 if let Some(loaded_child) = loaded_child_objects.get_mut(&id) {
724 loaded_child.is_modified = true;
725 }
726 (id, (owner, type_, value))
727 })
728 .collect();
729 for deleted_id in &deleted_ids {
730 if let Some(loaded_child) = loaded_child_objects.get_mut(deleted_id) {
731 loaded_child.is_modified = true;
732 }
733 }
734
735 for (received_object, _) in received.into_iter() {
739 match loaded_child_objects.get_mut(&received_object) {
740 Some(loaded_child) => {
741 loaded_child.is_modified = true;
742 }
743 None => {
744 return Err(ExecutionError::invariant_violation(format!(
745 "Failed to find received UID {received_object} in loaded child objects."
746 )));
747 }
748 }
749 }
750
751 Ok(RuntimeResults {
752 writes: written_objects,
753 user_events,
754 accumulator_events,
755 loaded_child_objects,
756 created_object_ids: new_ids,
757 deleted_object_ids: deleted_ids,
758 settlement_input_sui,
759 settlement_output_sui,
760 })
761 }
762
763 pub fn events(&self) -> &[(StructTag, Value)] {
764 &self.events
765 }
766
767 pub fn total_events_size(&self) -> u64 {
768 self.total_events_size
769 }
770
771 pub fn incr_total_events_size(&mut self, size: u64) {
772 self.total_events_size += size;
773 }
774
775 fn apply_child_object_effects(
776 &mut self,
777 loaded_child_objects: &mut BTreeMap<ObjectID, LoadedRuntimeObject>,
778 child_object_effects: ChildObjectEffects,
779 ) {
780 for (child, child_object_effect) in child_object_effects {
781 let ChildObjectEffect {
782 owner: parent,
783 ty,
784 final_value,
785 object_changed,
786 } = child_object_effect;
787
788 if object_changed {
789 if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
790 loaded_child.is_modified = true;
791 }
792
793 match final_value {
794 None => {
795 debug_assert!(
801 !self.transfers.contains_key(&child)
802 || !self.deleted_ids.contains(&child)
803 );
804 debug_assert!(
808 !self.deleted_ids.contains(&child)
809 || (!self.transfers.contains_key(&child)
810 && !self.new_ids.contains(&child))
811 );
812 }
813 Some(v) => {
814 debug_assert!(
818 !self.transfers.contains_key(&child)
819 && !self.deleted_ids.contains(&child)
820 );
821 debug_assert!(
826 !loaded_child_objects.contains_key(&child)
827 || !self.new_ids.contains(&child)
828 );
829 self.transfers
831 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
832 }
833 }
834 } else {
835 debug_assert!(
850 final_value.is_none()
851 || (loaded_child_objects.contains_key(&child)
852 && !self.deleted_ids.contains(&child)
853 && !self.transfers.contains_key(&child)
854 && !self.input_objects.contains_key(&child)
855 && !self.received.contains_key(&child))
856 );
857 debug_assert!(
859 loaded_child_objects
860 .get(&child)
861 .is_none_or(|loaded_child| !loaded_child.is_modified)
862 );
863 }
864 }
865 }
866}
867
868fn check_circular_ownership(
869 transfers: impl IntoIterator<Item = (ObjectID, Owner)>,
870) -> Result<(), ExecutionError> {
871 let mut object_owner_map = BTreeMap::new();
872 for (id, recipient) in transfers {
873 object_owner_map.remove(&id);
874 match recipient {
875 Owner::AddressOwner(_)
876 | Owner::Shared { .. }
877 | Owner::Immutable
878 | Owner::ConsensusAddressOwner { .. } => (),
879 Owner::ObjectOwner(new_owner) => {
880 let new_owner: ObjectID = new_owner.into();
881 let mut cur = new_owner;
882 loop {
883 if cur == id {
884 return Err(ExecutionError::from_kind(
885 ExecutionErrorKind::CircularObjectOwnership { object: cur },
886 ));
887 }
888 if let Some(parent) = object_owner_map.get(&cur) {
889 cur = *parent;
890 } else {
891 break;
892 }
893 }
894 object_owner_map.insert(id, new_owner);
895 }
896 }
897 }
898 Ok(())
899}
900
901pub fn get_all_uids(
907 fully_annotated_layout: &MoveTypeLayout,
908 bcs_bytes: &[u8],
909) -> Result<BTreeSet<ObjectID>, String> {
910 let mut ids = BTreeSet::new();
911 struct UIDTraversal<'i>(&'i mut BTreeSet<ObjectID>);
912 struct UIDCollector<'i>(&'i mut BTreeSet<ObjectID>);
913
914 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDTraversal<'_> {
915 type Error = AV::Error;
916
917 fn traverse_struct(
918 &mut self,
919 driver: &mut AV::StructDriver<'_, 'b, 'l>,
920 ) -> Result<(), Self::Error> {
921 if driver.struct_layout().type_ == UID::type_() {
922 while driver.next_field(&mut UIDCollector(self.0))?.is_some() {}
923 } else {
924 while driver.next_field(self)?.is_some() {}
925 }
926 Ok(())
927 }
928 }
929
930 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDCollector<'_> {
931 type Error = AV::Error;
932 fn traverse_address(
933 &mut self,
934 _driver: &AV::ValueDriver<'_, 'b, 'l>,
935 value: AccountAddress,
936 ) -> Result<(), Self::Error> {
937 self.0.insert(value.into());
938 Ok(())
939 }
940 }
941
942 MoveValue::visit_deserialize(
943 bcs_bytes,
944 fully_annotated_layout,
945 &mut UIDTraversal(&mut ids),
946 )
947 .map_err(|e| format!("Failed to deserialize. {e}"))?;
948 Ok(ids)
949}