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