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