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