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