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