1pub(crate) mod accumulator;
5mod fingerprint;
6pub(crate) mod object_store;
7
8use crate::object_runtime::object_store::{CacheMetadata, ChildObjectEffectV1};
9
10use self::object_store::{ChildObjectEffectV0, 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 effects::Op,
21 language_storage::StructTag,
22 runtime_value as R,
23 vm_status::StatusCode,
24};
25use move_vm_runtime::native_extensions::NativeExtensionMarker;
26use move_vm_types::values::{GlobalValue, Value};
27use object_store::{ActiveChildObject, ChildObjectStore};
28use std::{
29 collections::{BTreeMap, BTreeSet},
30 sync::Arc,
31};
32use sui_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
33use sui_types::{
34 SUI_ACCUMULATOR_ROOT_OBJECT_ID, SUI_ADDRESS_ALIAS_STATE_OBJECT_ID,
35 SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_BRIDGE_OBJECT_ID, SUI_CLOCK_OBJECT_ID,
36 SUI_COIN_REGISTRY_OBJECT_ID, SUI_DENY_LIST_OBJECT_ID, SUI_DISPLAY_REGISTRY_OBJECT_ID,
37 SUI_RANDOMNESS_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_ID, TypeTag,
38 base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress},
39 committee::EpochId,
40 error::{ExecutionError, ExecutionErrorKind, VMMemoryLimitExceededSubStatusCode},
41 execution::DynamicallyLoadedObjectMetadata,
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
75pub struct LoadedRuntimeObject {
76 pub version: SequenceNumber,
77 pub is_modified: bool,
78}
79
80pub struct RuntimeResults {
81 pub writes: IndexMap<ObjectID, (Owner, MoveObjectType, Value)>,
82 pub user_events: Vec<(StructTag, Value)>,
83 pub accumulator_events: Vec<MoveAccumulatorEvent>,
84 pub loaded_child_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
86 pub created_object_ids: Set<ObjectID>,
87 pub deleted_object_ids: Set<ObjectID>,
88 pub settlement_input_sui: u64,
89 pub settlement_output_sui: u64,
90}
91
92#[derive(Default)]
93pub(crate) struct ObjectRuntimeState {
94 pub(crate) input_objects: BTreeMap<ObjectID, Owner>,
95 new_ids: Set<ObjectID>,
97 generated_ids: Set<ObjectID>,
99 deleted_ids: Set<ObjectID>,
101 transfers: IndexMap<ObjectID, (Owner, MoveObjectType, Value)>,
104 events: Vec<(StructTag, Value)>,
105 accumulator_events: Vec<MoveAccumulatorEvent>,
106 total_events_size: u64,
108 received: IndexMap<ObjectID, DynamicallyLoadedObjectMetadata>,
109 settlement_input_sui: u64,
115 settlement_output_sui: u64,
116 accumulator_merge_totals: BTreeMap<(AccountAddress, TypeTag), u128>,
117 accumulator_split_totals: BTreeMap<(AccountAddress, TypeTag), u128>,
118}
119
120#[derive(Tid)]
121pub struct ObjectRuntime<'a> {
122 child_object_store: ChildObjectStore<'a>,
123 pub(crate) test_inventories: TestInventories,
125 pub(crate) state: ObjectRuntimeState,
127 is_metered: bool,
129
130 pub(crate) protocol_config: &'a ProtocolConfig,
131 pub(crate) metrics: Arc<LimitsMetrics>,
132}
133
134impl<'a> NativeExtensionMarker<'a> for ObjectRuntime<'a> {}
135
136pub enum TransferResult {
137 New,
138 SameOwner,
139 OwnerChanged,
140}
141
142pub struct InputObject {
143 pub contained_uids: BTreeSet<ObjectID>,
144 pub version: SequenceNumber,
145 pub owner: Owner,
146}
147
148impl TestInventories {
149 fn new() -> Self {
150 Self::default()
151 }
152}
153
154impl<'a> ObjectRuntime<'a> {
155 pub fn new(
156 object_resolver: &'a dyn ChildObjectResolver,
157 input_objects: BTreeMap<ObjectID, InputObject>,
158 is_metered: bool,
159 protocol_config: &'a ProtocolConfig,
160 metrics: Arc<LimitsMetrics>,
161 epoch_id: EpochId,
162 ) -> Self {
163 let mut input_object_owners = BTreeMap::new();
164 let mut root_version = BTreeMap::new();
165 let mut wrapped_object_containers = BTreeMap::new();
166 for (id, input_object) in input_objects {
167 let InputObject {
168 contained_uids,
169 version,
170 owner,
171 } = input_object;
172 input_object_owners.insert(id, owner);
173 debug_assert!(contained_uids.contains(&id));
174 for contained_uid in contained_uids {
175 root_version.insert(contained_uid, version);
176 if contained_uid != id {
177 let prev = wrapped_object_containers.insert(contained_uid, id);
178 debug_assert!(prev.is_none());
179 }
180 }
181 }
182 Self {
183 child_object_store: ChildObjectStore::new(
184 object_resolver,
185 root_version,
186 wrapped_object_containers,
187 is_metered,
188 protocol_config,
189 metrics.clone(),
190 epoch_id,
191 ),
192 test_inventories: TestInventories::new(),
193 state: ObjectRuntimeState {
194 input_objects: input_object_owners,
195 new_ids: Set::new(),
196 generated_ids: Set::new(),
197 deleted_ids: Set::new(),
198 transfers: IndexMap::new(),
199 events: vec![],
200 accumulator_events: vec![],
201 total_events_size: 0,
202 received: IndexMap::new(),
203 settlement_input_sui: 0,
204 settlement_output_sui: 0,
205 accumulator_merge_totals: BTreeMap::new(),
206 accumulator_split_totals: BTreeMap::new(),
207 },
208 is_metered,
209 protocol_config,
210 metrics,
211 }
212 }
213
214 pub fn new_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
215 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
218 self.is_metered,
219 self.state.new_ids.len(),
220 self.protocol_config.max_num_new_move_object_ids(),
221 self.protocol_config.max_num_new_move_object_ids_system_tx(),
222 self.metrics.excessive_new_move_object_ids
223 ) {
224 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
225 .with_message(format!("Creating more than {} IDs is not allowed", lim))
226 .with_sub_status(
227 VMMemoryLimitExceededSubStatusCode::NEW_ID_COUNT_LIMIT_EXCEEDED as u64,
228 ));
229 };
230
231 let was_present = self.state.deleted_ids.shift_remove(&id);
235 if !was_present {
236 self.state.generated_ids.insert(id);
238 self.state.new_ids.insert(id);
239 }
240 Ok(())
241 }
242
243 pub fn delete_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
244 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
249 self.is_metered,
250 self.state.deleted_ids.len(),
251 self.protocol_config.max_num_deleted_move_object_ids(),
252 self.protocol_config
253 .max_num_deleted_move_object_ids_system_tx(),
254 self.metrics.excessive_deleted_move_object_ids
255 ) {
256 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
257 .with_message(format!("Deleting more than {} IDs is not allowed", lim))
258 .with_sub_status(
259 VMMemoryLimitExceededSubStatusCode::DELETED_ID_COUNT_LIMIT_EXCEEDED as u64,
260 ));
261 };
262
263 let was_new = self.state.new_ids.shift_remove(&id);
264 if !was_new {
265 self.state.deleted_ids.insert(id);
266 }
267 Ok(())
268 }
269
270 pub fn transfer(
273 &mut self,
274 owner: Owner,
275 ty: MoveObjectType,
276 obj: Value,
277 end_of_transaction: bool,
278 ) -> PartialVMResult<TransferResult> {
279 let id: ObjectID = get_object_id(obj.copy_value()?)?
280 .value_as::<AccountAddress>()?
281 .into();
282 let is_framework_obj = [
288 SUI_SYSTEM_STATE_OBJECT_ID,
289 SUI_CLOCK_OBJECT_ID,
290 SUI_AUTHENTICATOR_STATE_OBJECT_ID,
291 SUI_RANDOMNESS_STATE_OBJECT_ID,
292 SUI_DENY_LIST_OBJECT_ID,
293 SUI_BRIDGE_OBJECT_ID,
294 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
295 SUI_COIN_REGISTRY_OBJECT_ID,
296 SUI_DISPLAY_REGISTRY_OBJECT_ID,
297 SUI_ADDRESS_ALIAS_STATE_OBJECT_ID,
298 ]
299 .contains(&id);
300 let transfer_result = if self.state.new_ids.contains(&id) {
301 TransferResult::New
302 } else if let Some(prev_owner) = self.state.input_objects.get(&id) {
303 match (&owner, prev_owner) {
304 (Owner::Shared { .. }, Owner::Shared { .. }) => TransferResult::SameOwner,
306 (
307 Owner::ConsensusAddressOwner {
308 owner: new_owner, ..
309 },
310 Owner::ConsensusAddressOwner {
311 owner: old_owner, ..
312 },
313 ) if new_owner == old_owner => TransferResult::SameOwner,
314 (new, old) if new == old => TransferResult::SameOwner,
315 _ => TransferResult::OwnerChanged,
316 }
317 } else if is_framework_obj {
318 self.state.new_ids.insert(id);
321 self.state.generated_ids.insert(id);
322 TransferResult::New
323 } else {
324 TransferResult::OwnerChanged
325 };
326 if end_of_transaction && !matches!(transfer_result, TransferResult::SameOwner) {
328 return Err(
329 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
330 .with_message(format!("Untransferred object {} had its owner change", id)),
331 );
332 }
333
334 if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
337 self.is_metered && !is_framework_obj && !end_of_transaction,
342 self.state.transfers.len(),
343 self.protocol_config.max_num_transferred_move_object_ids(),
344 self.protocol_config
345 .max_num_transferred_move_object_ids_system_tx(),
346 self.metrics.excessive_transferred_move_object_ids
347 ) {
348 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
349 .with_message(format!("Transferring more than {} IDs is not allowed", lim))
350 .with_sub_status(
351 VMMemoryLimitExceededSubStatusCode::TRANSFER_ID_COUNT_LIMIT_EXCEEDED as u64,
352 ));
353 };
354
355 self.state.transfers.insert(id, (owner, ty, obj));
356 Ok(transfer_result)
357 }
358
359 pub fn emit_event(&mut self, tag: StructTag, event: Value) -> PartialVMResult<()> {
360 if self.state.events.len() >= (self.protocol_config.max_num_event_emit() as usize) {
361 return Err(max_event_error(self.protocol_config.max_num_event_emit()));
362 }
363 self.state.events.push((tag, event));
364 Ok(())
365 }
366
367 pub fn take_user_events(&mut self) -> Vec<(StructTag, Value)> {
368 std::mem::take(&mut self.state.events)
369 }
370
371 pub fn emit_accumulator_event(
372 &mut self,
373 accumulator_id: ObjectID,
374 action: MoveAccumulatorAction,
375 target_addr: AccountAddress,
376 target_ty: TypeTag,
377 value: MoveAccumulatorValue,
378 ) -> PartialVMResult<()> {
379 if let MoveAccumulatorValue::U64(amount) = value {
380 let key = (target_addr, target_ty.clone());
381
382 match action {
383 MoveAccumulatorAction::Merge => {
384 let current = self
385 .state
386 .accumulator_merge_totals
387 .get(&key)
388 .copied()
389 .unwrap_or(0);
390 let new_total = current + amount as u128;
391 if new_total > u64::MAX as u128 {
392 return Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR)
393 .with_message(format!(
394 "accumulator merge overflow: total merges {} exceed u64::MAX",
395 new_total
396 )));
397 }
398 self.state.accumulator_merge_totals.insert(key, new_total);
399 }
400 MoveAccumulatorAction::Split => {
401 let current = self
402 .state
403 .accumulator_split_totals
404 .get(&key)
405 .copied()
406 .unwrap_or(0);
407 let new_total = current + amount as u128;
408 if new_total > u64::MAX as u128 {
409 return Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR)
410 .with_message(format!(
411 "accumulator split overflow: total splits {} exceed u64::MAX",
412 new_total
413 )));
414 }
415 self.state.accumulator_split_totals.insert(key, new_total);
416 }
417 }
418 }
419
420 let event = MoveAccumulatorEvent {
421 accumulator_id,
422 action,
423 target_addr,
424 target_ty,
425 value,
426 };
427 self.state.accumulator_events.push(event);
428 Ok(())
429 }
430
431 pub(crate) fn child_object_exists(
432 &mut self,
433 parent: ObjectID,
434 child: ObjectID,
435 ) -> PartialVMResult<CacheMetadata<bool>> {
436 self.child_object_store.object_exists(parent, child)
437 }
438
439 pub(crate) fn child_object_exists_and_has_type(
440 &mut self,
441 parent: ObjectID,
442 child: ObjectID,
443 child_type: &MoveObjectType,
444 ) -> PartialVMResult<CacheMetadata<bool>> {
445 self.child_object_store
446 .object_exists_and_has_type(parent, child, child_type)
447 }
448
449 pub(super) fn receive_object(
450 &mut self,
451 parent: ObjectID,
452 child: ObjectID,
453 child_version: SequenceNumber,
454 child_layout: &R::MoveTypeLayout,
455 child_fully_annotated_layout: &MoveTypeLayout,
456 child_move_type: MoveObjectType,
457 ) -> PartialVMResult<Option<ObjectResult<CacheMetadata<Value>>>> {
458 let Some((value, obj_meta)) = self.child_object_store.receive_object(
459 parent,
460 child,
461 child_version,
462 child_layout,
463 child_fully_annotated_layout,
464 child_move_type,
465 )?
466 else {
467 return Ok(None);
468 };
469 if self.state.received.insert(child, obj_meta).is_some() {
472 return Err(
475 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(format!(
476 "Object {child} at version {child_version} already received. This can only happen \
477 if multiple `Receiving` arguments exist for the same object in the transaction which is impossible."
478 )),
479 );
480 }
481 Ok(Some(value))
482 }
483
484 pub(crate) fn get_or_fetch_child_object(
485 &mut self,
486 parent: ObjectID,
487 child: ObjectID,
488 child_layout: &R::MoveTypeLayout,
489 child_fully_annotated_layout: &MoveTypeLayout,
490 child_move_type: MoveObjectType,
491 ) -> PartialVMResult<ObjectResult<CacheMetadata<&mut GlobalValue>>> {
492 let res = self.child_object_store.get_or_fetch_object(
493 parent,
494 child,
495 child_layout,
496 child_fully_annotated_layout,
497 child_move_type,
498 )?;
499 Ok(match res {
500 ObjectResult::MismatchedType => ObjectResult::MismatchedType,
501 ObjectResult::Loaded((cache_info, child_object)) => {
502 ObjectResult::Loaded((cache_info, &mut child_object.value))
503 }
504 })
505 }
506
507 pub(crate) fn add_child_object(
508 &mut self,
509 parent: ObjectID,
510 child: ObjectID,
511 child_move_type: MoveObjectType,
512 child_value: Value,
513 ) -> PartialVMResult<()> {
514 self.child_object_store
515 .add_object(parent, child, child_move_type, child_value)
516 }
517
518 pub(crate) fn config_setting_unsequenced_read(
519 &mut self,
520 config_id: ObjectID,
521 name_df_id: ObjectID,
522 field_setting_layout: &R::MoveTypeLayout,
523 field_setting_object_type: &MoveObjectType,
524 ) -> Option<Value> {
525 match self.child_object_store.config_setting_unsequenced_read(
526 config_id,
527 name_df_id,
528 field_setting_layout,
529 field_setting_object_type,
530 ) {
531 Err(e) => {
532 error!(
533 "Failed to read config setting.
534 config_id: {config_id},
535 name_df_id: {name_df_id},
536 field_setting_object_type: {field_setting_object_type:?},
537 error: {e}"
538 );
539 None
540 }
541 Ok(ObjectResult::MismatchedType) | Ok(ObjectResult::Loaded(None)) => None,
542 Ok(ObjectResult::Loaded(Some(value))) => Some(value),
543 }
544 }
545
546 pub(super) fn config_setting_cache_update(
547 &mut self,
548 config_id: ObjectID,
549 name_df_id: ObjectID,
550 setting_value_object_type: MoveObjectType,
551 value: Option<Value>,
552 ) {
553 self.child_object_store.config_setting_cache_update(
554 config_id,
555 name_df_id,
556 setting_value_object_type,
557 value,
558 )
559 }
560
561 pub(crate) fn take_state(&mut self) -> ObjectRuntimeState {
563 std::mem::take(&mut self.state)
564 }
565
566 pub fn is_deleted(&self, id: &ObjectID) -> bool {
567 self.state.deleted_ids.contains(id)
568 }
569
570 pub fn is_transferred(&self, id: &ObjectID) -> Option<Owner> {
571 self.state
572 .transfers
573 .get(id)
574 .map(|(owner, _, _)| owner.clone())
575 }
576
577 pub fn finish(mut self) -> Result<RuntimeResults, ExecutionError> {
578 let loaded_child_objects = self.loaded_runtime_objects();
579 let child_effects = self.child_object_store.take_effects().map_err(|e| {
580 ExecutionError::invariant_violation(format!("Failed to take child object effects: {e}"))
581 })?;
582 self.state.finish(loaded_child_objects, child_effects)
583 }
584
585 pub(crate) fn all_active_child_objects(&self) -> impl Iterator<Item = ActiveChildObject<'_>> {
586 self.child_object_store.all_active_objects()
587 }
588
589 pub fn loaded_runtime_objects(&self) -> BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata> {
590 debug_assert!(
593 self.child_object_store
594 .cached_objects()
595 .keys()
596 .all(|id| !self.state.received.contains_key(id))
597 );
598 self.child_object_store
599 .cached_objects()
600 .iter()
601 .filter_map(|(id, obj_opt)| {
602 obj_opt.as_ref().map(|obj| {
603 (
604 *id,
605 DynamicallyLoadedObjectMetadata {
606 version: obj.version(),
607 digest: obj.digest(),
608 storage_rebate: obj.storage_rebate,
609 owner: obj.owner.clone(),
610 previous_transaction: obj.previous_transaction,
611 },
612 )
613 })
614 })
615 .chain(
616 self.state
617 .received
618 .iter()
619 .map(|(id, meta)| (*id, meta.clone())),
620 )
621 .collect()
622 }
623
624 pub fn wrapped_object_containers(&self) -> BTreeMap<ObjectID, ObjectID> {
627 self.child_object_store.wrapped_object_containers().clone()
628 }
629
630 pub fn record_settlement_sui_conservation(&mut self, input_sui: u64, output_sui: u64) {
631 self.state.settlement_input_sui += input_sui;
632 self.state.settlement_output_sui += output_sui;
633 }
634
635 pub fn generated_object_ids(&self) -> BTreeSet<ObjectID> {
638 self.state.generated_ids.iter().cloned().collect()
639 }
640}
641
642pub fn max_event_error(max_events: u64) -> PartialVMError {
643 PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
644 .with_message(format!(
645 "Emitting more than {} events is not allowed",
646 max_events
647 ))
648 .with_sub_status(VMMemoryLimitExceededSubStatusCode::EVENT_COUNT_LIMIT_EXCEEDED as u64)
649}
650
651impl ObjectRuntimeState {
652 pub(crate) fn finish(
661 mut self,
662 loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
663 child_object_effects: ChildObjectEffects,
664 ) -> Result<RuntimeResults, ExecutionError> {
665 let mut loaded_child_objects: BTreeMap<_, _> = loaded_child_objects
666 .into_iter()
667 .map(|(id, metadata)| {
668 (
669 id,
670 LoadedRuntimeObject {
671 version: metadata.version,
672 is_modified: false,
673 },
674 )
675 })
676 .collect();
677 self.apply_child_object_effects(&mut loaded_child_objects, child_object_effects);
678 let ObjectRuntimeState {
679 input_objects: _,
680 new_ids,
681 generated_ids,
682 deleted_ids,
683 transfers,
684 events: user_events,
685 total_events_size: _,
686 received,
687 accumulator_events,
688 settlement_input_sui,
689 settlement_output_sui,
690 accumulator_merge_totals: _,
691 accumulator_split_totals: _,
692 } = self;
693
694 debug_assert!(new_ids.is_subset(&generated_ids));
696
697 check_circular_ownership(
700 transfers
701 .iter()
702 .map(|(id, (owner, _, _))| (*id, owner.clone())),
703 )?;
704 let written_objects: IndexMap<_, _> = transfers
711 .into_iter()
712 .map(|(id, (owner, type_, value))| {
713 if let Some(loaded_child) = loaded_child_objects.get_mut(&id) {
714 loaded_child.is_modified = true;
715 }
716 (id, (owner, type_, value))
717 })
718 .collect();
719 for deleted_id in &deleted_ids {
720 if let Some(loaded_child) = loaded_child_objects.get_mut(deleted_id) {
721 loaded_child.is_modified = true;
722 }
723 }
724
725 for (received_object, _) in received.into_iter() {
729 match loaded_child_objects.get_mut(&received_object) {
730 Some(loaded_child) => {
731 loaded_child.is_modified = true;
732 }
733 None => {
734 return Err(ExecutionError::invariant_violation(format!(
735 "Failed to find received UID {received_object} in loaded child objects."
736 )));
737 }
738 }
739 }
740
741 Ok(RuntimeResults {
742 writes: written_objects,
743 user_events,
744 accumulator_events,
745 loaded_child_objects,
746 created_object_ids: new_ids,
747 deleted_object_ids: deleted_ids,
748 settlement_input_sui,
749 settlement_output_sui,
750 })
751 }
752
753 pub fn events(&self) -> &[(StructTag, Value)] {
754 &self.events
755 }
756
757 pub fn total_events_size(&self) -> u64 {
758 self.total_events_size
759 }
760
761 pub fn incr_total_events_size(&mut self, size: u64) {
762 self.total_events_size += size;
763 }
764
765 fn apply_child_object_effects(
766 &mut self,
767 loaded_child_objects: &mut BTreeMap<ObjectID, LoadedRuntimeObject>,
768 child_object_effects: ChildObjectEffects,
769 ) {
770 match child_object_effects {
771 ChildObjectEffects::V0(child_object_effects) => {
772 self.apply_child_object_effects_v0(loaded_child_objects, child_object_effects)
773 }
774 ChildObjectEffects::V1(child_object_effects) => {
775 self.apply_child_object_effects_v1(loaded_child_objects, child_object_effects)
776 }
777 }
778 }
779
780 fn apply_child_object_effects_v0(
781 &mut self,
782 loaded_child_objects: &mut BTreeMap<ObjectID, LoadedRuntimeObject>,
783 child_object_effects: BTreeMap<ObjectID, ChildObjectEffectV0>,
784 ) {
785 for (child, child_object_effect) in child_object_effects {
786 let ChildObjectEffectV0 {
787 owner: parent,
788 ty,
789 effect,
790 } = child_object_effect;
791
792 if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
793 loaded_child.is_modified = true;
794 }
795
796 match effect {
797 Op::Modify(v) => {
799 debug_assert!(!self.transfers.contains_key(&child));
800 debug_assert!(!self.new_ids.contains(&child));
801 debug_assert!(loaded_child_objects.contains_key(&child));
802 self.transfers
803 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
804 }
805
806 Op::New(v) => {
807 debug_assert!(!self.transfers.contains_key(&child));
808 self.transfers
809 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
810 }
811
812 Op::Delete => {
813 if self.transfers.contains_key(&child) {
815 debug_assert!(!self.deleted_ids.contains(&child));
816 }
817 if self.deleted_ids.contains(&child) {
819 debug_assert!(!self.transfers.contains_key(&child));
820 debug_assert!(!self.new_ids.contains(&child));
821 }
822 }
823 }
824 }
825 }
826
827 fn apply_child_object_effects_v1(
828 &mut self,
829 loaded_child_objects: &mut BTreeMap<ObjectID, LoadedRuntimeObject>,
830 child_object_effects: BTreeMap<ObjectID, ChildObjectEffectV1>,
831 ) {
832 for (child, child_object_effect) in child_object_effects {
833 let ChildObjectEffectV1 {
834 owner: parent,
835 ty,
836 final_value,
837 object_changed,
838 } = child_object_effect;
839
840 if object_changed {
841 if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
842 loaded_child.is_modified = true;
843 }
844
845 match final_value {
846 None => {
847 debug_assert!(
853 !self.transfers.contains_key(&child)
854 || !self.deleted_ids.contains(&child)
855 );
856 debug_assert!(
860 !self.deleted_ids.contains(&child)
861 || (!self.transfers.contains_key(&child)
862 && !self.new_ids.contains(&child))
863 );
864 }
865 Some(v) => {
866 debug_assert!(
870 !self.transfers.contains_key(&child)
871 && !self.deleted_ids.contains(&child)
872 );
873 debug_assert!(
878 !loaded_child_objects.contains_key(&child)
879 || !self.new_ids.contains(&child)
880 );
881 self.transfers
883 .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
884 }
885 }
886 } else {
887 debug_assert!(
902 final_value.is_none()
903 || (loaded_child_objects.contains_key(&child)
904 && !self.deleted_ids.contains(&child)
905 && !self.transfers.contains_key(&child)
906 && !self.input_objects.contains_key(&child)
907 && !self.received.contains_key(&child))
908 );
909 debug_assert!(
911 loaded_child_objects
912 .get(&child)
913 .is_none_or(|loaded_child| !loaded_child.is_modified)
914 );
915 }
916 }
917 }
918}
919
920fn check_circular_ownership(
921 transfers: impl IntoIterator<Item = (ObjectID, Owner)>,
922) -> Result<(), ExecutionError> {
923 let mut object_owner_map = BTreeMap::new();
924 for (id, recipient) in transfers {
925 object_owner_map.remove(&id);
926 match recipient {
927 Owner::AddressOwner(_)
928 | Owner::Shared { .. }
929 | Owner::Immutable
930 | Owner::ConsensusAddressOwner { .. } => (),
931 Owner::ObjectOwner(new_owner) => {
932 let new_owner: ObjectID = new_owner.into();
933 let mut cur = new_owner;
934 loop {
935 if cur == id {
936 return Err(ExecutionError::from_kind(
937 ExecutionErrorKind::CircularObjectOwnership { object: cur },
938 ));
939 }
940 if let Some(parent) = object_owner_map.get(&cur) {
941 cur = *parent;
942 } else {
943 break;
944 }
945 }
946 object_owner_map.insert(id, new_owner);
947 }
948 }
949 }
950 Ok(())
951}
952
953pub fn get_all_uids(
959 fully_annotated_layout: &MoveTypeLayout,
960 bcs_bytes: &[u8],
961) -> Result<BTreeSet<ObjectID>, String> {
962 let mut ids = BTreeSet::new();
963 struct UIDTraversal<'i>(&'i mut BTreeSet<ObjectID>);
964 struct UIDCollector<'i>(&'i mut BTreeSet<ObjectID>);
965
966 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDTraversal<'_> {
967 type Error = AV::Error;
968
969 fn traverse_struct(
970 &mut self,
971 driver: &mut AV::StructDriver<'_, 'b, 'l>,
972 ) -> Result<(), Self::Error> {
973 if driver.struct_layout().type_ == UID::type_() {
974 while driver.next_field(&mut UIDCollector(self.0))?.is_some() {}
975 } else {
976 while driver.next_field(self)?.is_some() {}
977 }
978 Ok(())
979 }
980 }
981
982 impl<'b, 'l> AV::Traversal<'b, 'l> for UIDCollector<'_> {
983 type Error = AV::Error;
984 fn traverse_address(
985 &mut self,
986 _driver: &AV::ValueDriver<'_, 'b, 'l>,
987 value: AccountAddress,
988 ) -> Result<(), Self::Error> {
989 self.0.insert(value.into());
990 Ok(())
991 }
992 }
993
994 MoveValue::visit_deserialize(
995 bcs_bytes,
996 fully_annotated_layout,
997 &mut UIDTraversal(&mut ids),
998 )
999 .map_err(|e| format!("Failed to deserialize. {e}"))?;
1000 Ok(ids)
1001}