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