1use std::{
6 borrow::Borrow,
7 cell::RefCell,
8 collections::{BTreeMap, BTreeSet, VecDeque},
9 thread::LocalKey,
10};
11
12use better_any::{Tid, TidAble};
13use indexmap::{IndexMap, IndexSet};
14use iota_types::{
15 TypeTag,
16 base_types::{IotaAddress, ObjectID, SequenceNumber},
17 config,
18 digests::{ObjectDigest, TransactionDigest},
19 dynamic_field::DynamicFieldInfo,
20 execution::DynamicallyLoadedObjectMetadata,
21 id::UID,
22 in_memory_storage::InMemoryStorage,
23 object::{MoveObject, Object, Owner},
24 storage::ChildObjectResolver,
25};
26use move_binary_format::errors::{PartialVMError, PartialVMResult};
27use move_core_types::{
28 account_address::AccountAddress,
29 annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout, MoveValue},
30 annotated_visitor as AV,
31 language_storage::StructTag,
32 vm_status::StatusCode,
33};
34use move_vm_runtime::{native_extensions::NativeExtensionMarker, native_functions::NativeContext};
35use move_vm_types::{
36 loaded_data::runtime_types::Type,
37 natives::function::NativeResult,
38 pop_arg,
39 values::{self, StructRef, Value},
40};
41use smallvec::smallvec;
42
43use crate::{
44 get_nth_struct_field, get_tag_and_layouts, legacy_test_cost,
45 object_runtime::{ObjectRuntime, RuntimeResults, object_store::ChildObjectEffects},
46};
47
48const E_COULD_NOT_GENERATE_EFFECTS: u64 = 0;
49const E_INVALID_SHARED_OR_IMMUTABLE_USAGE: u64 = 1;
50const E_OBJECT_NOT_FOUND_CODE: u64 = 4;
51const E_UNABLE_TO_ALLOCATE_RECEIVING_TICKET: u64 = 5;
52const E_RECEIVING_TICKET_ALREADY_ALLOCATED: u64 = 6;
53const E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET: u64 = 7;
54
55type Set<K> = IndexSet<K>;
56
57#[derive(Tid)]
61pub struct InMemoryTestStore(pub &'static LocalKey<RefCell<InMemoryStorage>>);
62impl<'a> NativeExtensionMarker<'a> for &'a InMemoryTestStore {}
63
64impl ChildObjectResolver for InMemoryTestStore {
65 fn read_child_object(
66 &self,
67 parent: &ObjectID,
68 child: &ObjectID,
69 child_version_upper_bound: SequenceNumber,
70 ) -> iota_types::error::IotaResult<Option<Object>> {
71 let l: &'static LocalKey<RefCell<InMemoryStorage>> = self.0;
72 l.with_borrow(|store| store.read_child_object(parent, child, child_version_upper_bound))
73 }
74
75 fn get_object_received_at_version(
76 &self,
77 owner: &ObjectID,
78 receiving_object_id: &ObjectID,
79 receive_object_at_version: SequenceNumber,
80 epoch_id: iota_types::committee::EpochId,
81 ) -> iota_types::error::IotaResult<Option<Object>> {
82 self.0.with_borrow(|store| {
83 store.get_object_received_at_version(
84 owner,
85 receiving_object_id,
86 receive_object_at_version,
87 epoch_id,
88 )
89 })
90 }
91}
92
93pub fn end_transaction(
97 context: &mut NativeContext,
98 ty_args: Vec<Type>,
99 args: VecDeque<Value>,
100) -> PartialVMResult<NativeResult> {
101 assert!(ty_args.is_empty());
102 assert!(args.is_empty());
103 let object_runtime_ref: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
104 let taken_shared_or_imm: BTreeMap<_, _> = object_runtime_ref
105 .test_inventories
106 .taken
107 .iter()
108 .filter(|(_id, owner)| matches!(owner, Owner::Shared { .. } | Owner::Immutable))
109 .map(|(id, owner)| (*id, *owner))
110 .collect();
111 let mut incorrect_shared_or_imm_handling = false;
116
117 let allocated_tickets =
125 std::mem::take(&mut object_runtime_ref.test_inventories.allocated_tickets);
126 let mut received = BTreeMap::new();
127 let mut unreceived = BTreeSet::new();
128 let loaded_runtime_objects = object_runtime_ref.loaded_runtime_objects();
129 for (id, (metadata, value)) in allocated_tickets {
130 if loaded_runtime_objects.contains_key(&id) {
131 received.insert(id, metadata);
132 } else {
133 unreceived.insert(id);
134 object_runtime_ref
137 .test_inventories
138 .objects
139 .insert(id, value);
140 }
141 }
142
143 let object_runtime_state = object_runtime_ref.take_state();
144 let results = object_runtime_state.finish(received, ChildObjectEffects::empty());
148 let RuntimeResults {
149 writes,
150 user_events,
151 loaded_child_objects: _,
152 created_object_ids,
153 deleted_object_ids,
154 } = match results {
155 Ok(res) => res,
156 Err(_) => {
157 return Ok(NativeResult::err(
158 legacy_test_cost(),
159 E_COULD_NOT_GENERATE_EFFECTS,
160 ));
161 }
162 };
163 let object_runtime_ref: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
164 let all_active_child_objects_with_values = object_runtime_ref
165 .all_active_child_objects()
166 .filter(|child| child.copied_value.is_some())
167 .map(|child| *child.id)
168 .collect::<BTreeSet<_>>();
169 let inventories = &mut object_runtime_ref.test_inventories;
170 let mut new_object_values = IndexMap::new();
171 let mut transferred = vec![];
172 for id in deleted_object_ids
180 .iter()
181 .chain(writes.keys())
182 .chain(&all_active_child_objects_with_values)
183 {
184 for addr_inventory in inventories.address_inventories.values_mut() {
185 for s in addr_inventory.values_mut() {
186 s.shift_remove(id);
187 }
188 }
189 for s in &mut inventories.shared_inventory.values_mut() {
190 s.shift_remove(id);
191 }
192 for s in &mut inventories.immutable_inventory.values_mut() {
193 s.shift_remove(id);
194 }
195 inventories.taken.remove(id);
196 }
197
198 let mut created = vec![];
201 let mut written = vec![];
202 for (id, (owner, ty, value)) in writes {
203 new_object_values.insert(id, (ty.clone(), value.copy_value().unwrap()));
205 transferred.push((id, owner));
206 incorrect_shared_or_imm_handling = incorrect_shared_or_imm_handling
207 || taken_shared_or_imm
208 .get(&id)
209 .map(|shared_or_imm_owner| shared_or_imm_owner != &owner)
210 .unwrap_or(false);
211 if created_object_ids.contains(&id) {
212 created.push(id);
213 } else {
214 written.push(id);
215 }
216 match owner {
217 Owner::AddressOwner(a) => {
218 inventories
219 .address_inventories
220 .entry(a)
221 .or_default()
222 .entry(ty)
223 .or_default()
224 .insert(id);
225 }
226 Owner::ObjectOwner(_) => (),
227 Owner::Shared { .. } => {
228 inventories
229 .shared_inventory
230 .entry(ty)
231 .or_default()
232 .insert(id);
233 }
234 Owner::Immutable => {
235 inventories
236 .immutable_inventory
237 .entry(ty)
238 .or_default()
239 .insert(id);
240 }
241 }
242 }
243
244 let store: &&InMemoryTestStore = context.extensions().get()?;
246 for id in unreceived {
247 if store
248 .0
249 .with_borrow_mut(|store| store.remove_object(id).is_none())
250 {
251 return Ok(NativeResult::err(
252 context.gas_used(),
253 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
254 ));
255 }
256 }
257
258 let mut deleted = vec![];
260 for id in deleted_object_ids {
261 incorrect_shared_or_imm_handling = incorrect_shared_or_imm_handling
264 || taken_shared_or_imm
265 .get(&id)
266 .is_some_and(|owner| matches!(owner, Owner::Immutable));
267 deleted.push(id);
268 }
269 let mut all_wrapped = BTreeSet::new();
271 let object_runtime_ref: &ObjectRuntime = context.extensions().get()?;
272 find_all_wrapped_objects(
273 context,
274 &mut all_wrapped,
275 new_object_values
276 .iter()
277 .map(|(id, (ty, value))| (id, ty, value)),
278 );
279 find_all_wrapped_objects(
280 context,
281 &mut all_wrapped,
282 object_runtime_ref
283 .all_active_child_objects()
284 .filter_map(|child| Some((child.id, child.ty, child.copied_value?))),
285 );
286 incorrect_shared_or_imm_handling = incorrect_shared_or_imm_handling
288 || taken_shared_or_imm.keys().any(|id| {
289 all_wrapped.contains(id) || all_active_child_objects_with_values.contains(id)
290 });
291 if incorrect_shared_or_imm_handling {
293 return Ok(NativeResult::err(
294 legacy_test_cost(),
295 E_INVALID_SHARED_OR_IMMUTABLE_USAGE,
296 ));
297 }
298
299 for wrapped in all_wrapped {
301 deleted.push(wrapped)
302 }
303
304 let object_runtime_ref: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
306 let mut config_settings = vec![];
307 for child in object_runtime_ref.all_active_child_objects() {
308 let s: StructTag = child.move_type.clone().into();
309 let is_setting = DynamicFieldInfo::is_dynamic_field(&s)
310 && matches!(&s.type_params[1], TypeTag::Struct(s) if config::is_setting(s));
311 if is_setting {
312 config_settings.push((
313 *child.owner,
314 *child.id,
315 child.move_type.clone(),
316 child.copied_value,
317 ));
318 }
319 }
320 for (config, setting, ty, value) in config_settings {
321 object_runtime_ref.config_setting_cache_update(config, setting, ty, value)
322 }
323 object_runtime_ref.state.input_objects = object_runtime_ref
324 .test_inventories
325 .taken
326 .iter()
327 .map(|(id, owner)| (*id, *owner))
328 .collect::<BTreeMap<_, _>>();
329 for (id, (ty, value)) in new_object_values {
332 debug_assert!(!all_active_child_objects_with_values.contains(&id));
333 if let Some(prev_value) = object_runtime_ref
334 .test_inventories
335 .taken_immutable_values
336 .get(&ty)
337 .and_then(|values| values.get(&id))
338 {
339 if !value.equals(prev_value)? {
340 return Ok(NativeResult::err(
341 legacy_test_cost(),
342 E_INVALID_SHARED_OR_IMMUTABLE_USAGE,
343 ));
344 }
345 }
346 object_runtime_ref
347 .test_inventories
348 .objects
349 .insert(id, value);
350 }
351 for id in &deleted {
353 object_runtime_ref.test_inventories.objects.remove(id);
354 }
355 for id in all_active_child_objects_with_values {
357 object_runtime_ref.test_inventories.objects.remove(&id);
358 }
359
360 let effects = transaction_effects(
361 created,
362 written,
363 deleted,
364 transferred,
365 user_events.len() as u64,
366 );
367 Ok(NativeResult::ok(legacy_test_cost(), smallvec![effects]))
368}
369
370pub fn take_from_address_by_id(
372 context: &mut NativeContext,
373 ty_args: Vec<Type>,
374 mut args: VecDeque<Value>,
375) -> PartialVMResult<NativeResult> {
376 let specified_ty = get_specified_ty(ty_args);
377 let id = pop_id(&mut args)?;
378 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
379 pop_arg!(args, StructRef);
380 assert!(args.is_empty());
381 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
382 let inventories = &mut object_runtime.test_inventories;
383 let res = take_from_inventory(
384 |x| {
385 inventories
386 .address_inventories
387 .get(&account)
388 .and_then(|inv| inv.get(&specified_ty))
389 .map(|s| s.contains(x))
390 .unwrap_or(false)
391 },
392 &inventories.objects,
393 &mut inventories.taken,
394 &mut object_runtime.state.input_objects,
395 id,
396 Owner::AddressOwner(account),
397 );
398 Ok(match res {
399 Ok(value) => NativeResult::ok(legacy_test_cost(), smallvec![value]),
400 Err(native_err) => native_err,
401 })
402}
403
404pub fn ids_for_address(
406 context: &mut NativeContext,
407 ty_args: Vec<Type>,
408 mut args: VecDeque<Value>,
409) -> PartialVMResult<NativeResult> {
410 let specified_ty = get_specified_ty(ty_args);
411 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
412 assert!(args.is_empty());
413 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
414 let inventories = &mut object_runtime.test_inventories;
415 let ids = inventories
416 .address_inventories
417 .get(&account)
418 .and_then(|inv| inv.get(&specified_ty))
419 .map(|s| s.iter().map(|id| pack_id(*id)).collect::<Vec<Value>>())
420 .unwrap_or_default();
421 let ids_vector = Value::vector_for_testing_only(ids);
422 Ok(NativeResult::ok(legacy_test_cost(), smallvec![ids_vector]))
423}
424
425pub fn most_recent_id_for_address(
427 context: &mut NativeContext,
428 ty_args: Vec<Type>,
429 mut args: VecDeque<Value>,
430) -> PartialVMResult<NativeResult> {
431 let specified_ty = get_specified_ty(ty_args);
432 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
433 assert!(args.is_empty());
434 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
435 let inventories = &mut object_runtime.test_inventories;
436 let most_recent_id = match inventories.address_inventories.get(&account) {
437 None => pack_option(None),
438 Some(inv) => most_recent_at_ty(&inventories.taken, inv, specified_ty),
439 };
440 Ok(NativeResult::ok(
441 legacy_test_cost(),
442 smallvec![most_recent_id],
443 ))
444}
445
446pub fn was_taken_from_address(
448 context: &mut NativeContext,
449 ty_args: Vec<Type>,
450 mut args: VecDeque<Value>,
451) -> PartialVMResult<NativeResult> {
452 assert!(ty_args.is_empty());
453 let id = pop_id(&mut args)?;
454 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
455 assert!(args.is_empty());
456 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
457 let inventories = &mut object_runtime.test_inventories;
458 let was_taken = inventories
459 .taken
460 .get(&id)
461 .map(|owner| owner == &Owner::AddressOwner(account))
462 .unwrap_or(false);
463 Ok(NativeResult::ok(
464 legacy_test_cost(),
465 smallvec![Value::bool(was_taken)],
466 ))
467}
468
469pub fn take_immutable_by_id(
471 context: &mut NativeContext,
472 ty_args: Vec<Type>,
473 mut args: VecDeque<Value>,
474) -> PartialVMResult<NativeResult> {
475 let specified_ty = get_specified_ty(ty_args);
476 let id = pop_id(&mut args)?;
477 pop_arg!(args, StructRef);
478 assert!(args.is_empty());
479 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
480 let inventories = &mut object_runtime.test_inventories;
481 let res = take_from_inventory(
482 |x| {
483 inventories
484 .immutable_inventory
485 .get(&specified_ty)
486 .map(|s| s.contains(x))
487 .unwrap_or(false)
488 },
489 &inventories.objects,
490 &mut inventories.taken,
491 &mut object_runtime.state.input_objects,
492 id,
493 Owner::Immutable,
494 );
495 Ok(match res {
496 Ok(value) => {
497 inventories
498 .taken_immutable_values
499 .entry(specified_ty)
500 .or_default()
501 .insert(id, value.copy_value().unwrap());
502 NativeResult::ok(legacy_test_cost(), smallvec![value])
503 }
504 Err(native_err) => native_err,
505 })
506}
507
508pub fn most_recent_immutable_id(
510 context: &mut NativeContext,
511 ty_args: Vec<Type>,
512 args: VecDeque<Value>,
513) -> PartialVMResult<NativeResult> {
514 let specified_ty = get_specified_ty(ty_args);
515 assert!(args.is_empty());
516 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
517 let inventories = &mut object_runtime.test_inventories;
518 let most_recent_id = most_recent_at_ty(
519 &inventories.taken,
520 &inventories.immutable_inventory,
521 specified_ty,
522 );
523 Ok(NativeResult::ok(
524 legacy_test_cost(),
525 smallvec![most_recent_id],
526 ))
527}
528
529pub fn was_taken_immutable(
531 context: &mut NativeContext,
532 ty_args: Vec<Type>,
533 mut args: VecDeque<Value>,
534) -> PartialVMResult<NativeResult> {
535 assert!(ty_args.is_empty());
536 let id = pop_id(&mut args)?;
537 assert!(args.is_empty());
538 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
539 let inventories = &mut object_runtime.test_inventories;
540 let was_taken = inventories
541 .taken
542 .get(&id)
543 .map(|owner| owner == &Owner::Immutable)
544 .unwrap_or(false);
545 Ok(NativeResult::ok(
546 legacy_test_cost(),
547 smallvec![Value::bool(was_taken)],
548 ))
549}
550
551pub fn take_shared_by_id(
553 context: &mut NativeContext,
554 ty_args: Vec<Type>,
555 mut args: VecDeque<Value>,
556) -> PartialVMResult<NativeResult> {
557 let specified_ty = get_specified_ty(ty_args);
558 let id = pop_id(&mut args)?;
559 pop_arg!(args, StructRef);
560 assert!(args.is_empty());
561 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
562 let inventories = &mut object_runtime.test_inventories;
563 let res = take_from_inventory(
564 |x| {
565 inventories
566 .shared_inventory
567 .get(&specified_ty)
568 .map(|s| s.contains(x))
569 .unwrap_or(false)
570 },
571 &inventories.objects,
572 &mut inventories.taken,
573 &mut object_runtime.state.input_objects,
574 id,
575 Owner::Shared { initial_shared_version: SequenceNumber::new() },
576 );
577 Ok(match res {
578 Ok(value) => NativeResult::ok(legacy_test_cost(), smallvec![value]),
579 Err(native_err) => native_err,
580 })
581}
582
583pub fn most_recent_id_shared(
585 context: &mut NativeContext,
586 ty_args: Vec<Type>,
587 args: VecDeque<Value>,
588) -> PartialVMResult<NativeResult> {
589 let specified_ty = get_specified_ty(ty_args);
590 assert!(args.is_empty());
591 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
592 let inventories = &mut object_runtime.test_inventories;
593 let most_recent_id = most_recent_at_ty(
594 &inventories.taken,
595 &inventories.shared_inventory,
596 specified_ty,
597 );
598 Ok(NativeResult::ok(
599 legacy_test_cost(),
600 smallvec![most_recent_id],
601 ))
602}
603
604pub fn was_taken_shared(
606 context: &mut NativeContext,
607 ty_args: Vec<Type>,
608 mut args: VecDeque<Value>,
609) -> PartialVMResult<NativeResult> {
610 assert!(ty_args.is_empty());
611 let id = pop_id(&mut args)?;
612 assert!(args.is_empty());
613 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
614 let inventories = &mut object_runtime.test_inventories;
615 let was_taken = inventories
616 .taken
617 .get(&id)
618 .map(|owner| matches!(owner, Owner::Shared { .. }))
619 .unwrap_or(false);
620 Ok(NativeResult::ok(
621 legacy_test_cost(),
622 smallvec![Value::bool(was_taken)],
623 ))
624}
625
626pub fn allocate_receiving_ticket_for_object(
627 context: &mut NativeContext,
628 ty_args: Vec<Type>,
629 mut args: VecDeque<Value>,
630) -> PartialVMResult<NativeResult> {
631 let ty = get_specified_ty(ty_args);
632 let id = pop_id(&mut args)?;
633
634 let Some((tag, layout, _)) = get_tag_and_layouts(context, &ty)? else {
635 return Ok(NativeResult::err(
636 context.gas_used(),
637 E_UNABLE_TO_ALLOCATE_RECEIVING_TICKET,
638 ));
639 };
640 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
641 let object_version = SequenceNumber::new();
642 let inventories = &mut object_runtime.test_inventories;
643 if inventories.allocated_tickets.contains_key(&id) {
644 return Ok(NativeResult::err(
645 context.gas_used(),
646 E_RECEIVING_TICKET_ALREADY_ALLOCATED,
647 ));
648 }
649
650 let obj_value = inventories.objects.remove(&id).unwrap();
651 let Some(bytes) = obj_value.simple_serialize(&layout) else {
652 return Ok(NativeResult::err(
653 context.gas_used(),
654 E_UNABLE_TO_ALLOCATE_RECEIVING_TICKET,
655 ));
656 };
657 let move_object = {
658 MoveObject::new_from_execution_with_limit(tag.into(), object_version, bytes, 250 * 1024)
659 }
660 .unwrap();
661
662 let Some((owner, _)) = inventories
663 .address_inventories
664 .iter()
665 .find(|(_addr, objs)| objs.iter().any(|(_, ids)| ids.contains(&id)))
666 else {
667 return Ok(NativeResult::err(
668 context.gas_used(),
669 E_OBJECT_NOT_FOUND_CODE,
670 ));
671 };
672
673 inventories.allocated_tickets.insert(
674 id,
675 (
676 DynamicallyLoadedObjectMetadata {
677 version: SequenceNumber::new(),
678 digest: ObjectDigest::MIN,
679 owner: Owner::AddressOwner(*owner),
680 storage_rebate: 0,
681 previous_transaction: TransactionDigest::default(),
682 },
683 obj_value,
684 ),
685 );
686
687 let object = Object::new_move(
688 move_object,
689 Owner::AddressOwner(*owner),
690 TransactionDigest::default(),
691 );
692
693 let store: &&InMemoryTestStore = context.extensions().get()?;
696 store.0.with_borrow_mut(|store| store.insert_object(object));
697
698 Ok(NativeResult::ok(
699 legacy_test_cost(),
700 smallvec![Value::u64(object_version.value())],
701 ))
702}
703
704pub fn deallocate_receiving_ticket_for_object(
705 context: &mut NativeContext,
706 _ty_args: Vec<Type>,
707 mut args: VecDeque<Value>,
708) -> PartialVMResult<NativeResult> {
709 let id = pop_id(&mut args)?;
710
711 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
712 let inventories = &mut object_runtime.test_inventories;
713 let Some((_, value)) = inventories.allocated_tickets.remove(&id) else {
715 return Ok(NativeResult::err(
716 context.gas_used(),
717 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
718 ));
719 };
720
721 inventories.objects.insert(id, value);
724
725 let store: &&InMemoryTestStore = context.extensions().get()?;
727 if store
728 .0
729 .with_borrow_mut(|store| store.remove_object(id).is_none())
730 {
731 return Ok(NativeResult::err(
732 context.gas_used(),
733 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
734 ));
735 };
736
737 Ok(NativeResult::ok(legacy_test_cost(), smallvec![]))
738}
739
740fn take_from_inventory(
743 is_in_inventory: impl FnOnce(&ObjectID) -> bool,
744 objects: &BTreeMap<ObjectID, Value>,
745 taken: &mut BTreeMap<ObjectID, Owner>,
746 input_objects: &mut BTreeMap<ObjectID, Owner>,
747 id: ObjectID,
748 owner: Owner,
749) -> Result<Value, NativeResult> {
750 let obj_opt = objects.get(&id);
751 let is_taken = taken.contains_key(&id);
752 if is_taken || !is_in_inventory(&id) || obj_opt.is_none() {
753 return Err(NativeResult::err(
754 legacy_test_cost(),
755 E_OBJECT_NOT_FOUND_CODE,
756 ));
757 }
758 taken.insert(id, owner);
759 input_objects.insert(id, owner);
760 let obj = obj_opt.unwrap();
761 Ok(obj.copy_value().unwrap())
762}
763
764fn most_recent_at_ty(
765 taken: &BTreeMap<ObjectID, Owner>,
766 inv: &BTreeMap<Type, Set<ObjectID>>,
767 ty: Type,
768) -> Value {
769 pack_option(most_recent_at_ty_opt(taken, inv, ty))
770}
771
772fn most_recent_at_ty_opt(
773 taken: &BTreeMap<ObjectID, Owner>,
774 inv: &BTreeMap<Type, Set<ObjectID>>,
775 ty: Type,
776) -> Option<Value> {
777 let s = inv.get(&ty)?;
778 let most_recent_id = s.iter().filter(|id| !taken.contains_key(id)).next_back()?;
779 Some(pack_id(*most_recent_id))
780}
781
782fn get_specified_ty(mut ty_args: Vec<Type>) -> Type {
783 assert!(ty_args.len() == 1);
784 ty_args.pop().unwrap()
785}
786
787fn pop_id(args: &mut VecDeque<Value>) -> PartialVMResult<ObjectID> {
789 let v = match args.pop_back() {
790 None => {
791 return Err(PartialVMError::new(
792 StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
793 ));
794 }
795 Some(v) => v,
796 };
797 Ok(get_nth_struct_field(v, 0)?
798 .value_as::<AccountAddress>()?
799 .into())
800}
801
802fn pack_id(a: impl Into<AccountAddress>) -> Value {
803 Value::struct_(values::Struct::pack(vec![Value::address(a.into())]))
804}
805
806fn pack_ids(items: impl IntoIterator<Item = impl Into<AccountAddress>>) -> Value {
807 Value::vector_for_testing_only(items.into_iter().map(pack_id))
808}
809
810fn pack_vec_map(items: impl IntoIterator<Item = (Value, Value)>) -> Value {
811 Value::struct_(values::Struct::pack(vec![Value::vector_for_testing_only(
812 items
813 .into_iter()
814 .map(|(k, v)| Value::struct_(values::Struct::pack(vec![k, v]))),
815 )]))
816}
817
818fn transaction_effects(
819 created: impl IntoIterator<Item = impl Into<AccountAddress>>,
820 written: impl IntoIterator<Item = impl Into<AccountAddress>>,
821 deleted: impl IntoIterator<Item = impl Into<AccountAddress>>,
822 transferred: impl IntoIterator<Item = (ObjectID, Owner)>,
823 num_events: u64,
824) -> Value {
825 let mut transferred_to_account = vec![];
826 let mut transferred_to_object = vec![];
827 let mut shared = vec![];
828 let mut frozen = vec![];
829 for (id, owner) in transferred {
830 match owner {
831 Owner::AddressOwner(a) => {
832 transferred_to_account.push((pack_id(id), Value::address(a.into())))
833 }
834 Owner::ObjectOwner(o) => transferred_to_object.push((pack_id(id), pack_id(o))),
835 Owner::Shared { .. } => shared.push(id),
836 Owner::Immutable => frozen.push(id),
837 }
838 }
839
840 let created_field = pack_ids(created);
841 let written_field = pack_ids(written);
842 let deleted_field = pack_ids(deleted);
843 let transferred_to_account_field = pack_vec_map(transferred_to_account);
844 let transferred_to_object_field = pack_vec_map(transferred_to_object);
845 let shared_field = pack_ids(shared);
846 let frozen_field = pack_ids(frozen);
847 let num_events_field = Value::u64(num_events);
848 Value::struct_(values::Struct::pack(vec![
849 created_field,
850 written_field,
851 deleted_field,
852 transferred_to_account_field,
853 transferred_to_object_field,
854 shared_field,
855 frozen_field,
856 num_events_field,
857 ]))
858}
859
860fn pack_option(opt: Option<Value>) -> Value {
861 let item = match opt {
862 Some(v) => vec![v],
863 None => vec![],
864 };
865 Value::struct_(values::Struct::pack(vec![Value::vector_for_testing_only(
866 item,
867 )]))
868}
869
870fn find_all_wrapped_objects<'a, 'i>(
871 context: &NativeContext,
872 ids: &'i mut BTreeSet<ObjectID>,
873 new_object_values: impl IntoIterator<Item = (&'a ObjectID, &'a Type, impl Borrow<Value>)>,
874) {
875 #[derive(Copy, Clone)]
876 enum LookingFor {
877 Wrapped,
878 Uid,
879 Address,
880 }
881
882 struct Traversal<'i, 'u> {
883 state: LookingFor,
884 ids: &'i mut BTreeSet<ObjectID>,
885 uid: &'u MoveStructLayout,
886 }
887
888 impl<'b, 'l> AV::Traversal<'b, 'l> for Traversal<'_, '_> {
889 type Error = AV::Error;
890
891 fn traverse_struct(
892 &mut self,
893 driver: &mut AV::StructDriver<'_, 'b, 'l>,
894 ) -> Result<(), Self::Error> {
895 match self.state {
896 LookingFor::Wrapped => {
900 while driver
901 .next_field(&mut Traversal {
902 state: LookingFor::Uid,
903 ids: self.ids,
904 uid: self.uid,
905 })?
906 .is_some()
907 {}
908 }
909
910 LookingFor::Uid => {
913 while let Some(MoveFieldLayout { name: _, layout }) = driver.peek_field() {
914 if matches!(layout, MoveTypeLayout::Struct(s) if s.as_ref() == self.uid) {
915 driver.next_field(&mut Traversal {
916 state: LookingFor::Address,
917 ids: self.ids,
918 uid: self.uid,
919 })?;
920 } else {
921 driver.next_field(self)?;
922 }
923 }
924 }
925
926 LookingFor::Address => while driver.next_field(self)?.is_some() {},
929 }
930
931 Ok(())
932 }
933
934 fn traverse_address(
935 &mut self,
936 _: &AV::ValueDriver<'_, 'b, 'l>,
937 address: AccountAddress,
938 ) -> Result<(), Self::Error> {
939 if matches!(self.state, LookingFor::Address) {
941 self.ids.insert(address.into());
942 }
943 Ok(())
944 }
945 }
946
947 let uid = UID::layout();
948 for (_id, ty, value) in new_object_values {
949 let Ok(Some(layout)) = context.type_to_type_layout(ty) else {
950 debug_assert!(false);
951 continue;
952 };
953
954 let Ok(Some(annotated_layout)) = context.type_to_fully_annotated_layout(ty) else {
955 debug_assert!(false);
956 continue;
957 };
958
959 let blob = value.borrow().simple_serialize(&layout).unwrap();
960 MoveValue::visit_deserialize(
961 &blob,
962 &annotated_layout,
963 &mut Traversal {
964 state: LookingFor::Wrapped,
965 ids,
966 uid: &uid,
967 },
968 )
969 .unwrap();
970 }
971}