1use std::{
6 borrow::Borrow,
7 cell::RefCell,
8 collections::{BTreeMap, BTreeSet, VecDeque},
9 thread::LocalKey,
10};
11
12use better_any::{Tid, TidAble};
13use indexmap::{IndexMap, IndexSet};
14use iota_types::{
15 TypeTag,
16 base_types::{IotaAddress, ObjectID, SequenceNumber},
17 config,
18 digests::{ObjectDigest, TransactionDigest},
19 dynamic_field::DynamicFieldInfo,
20 execution::DynamicallyLoadedObjectMetadata,
21 id::UID,
22 in_memory_storage::InMemoryStorage,
23 object::{MoveObject, Object, Owner},
24 storage::{BackingPackageStore, ChildObjectResolver},
25};
26use move_binary_format::errors::{PartialVMError, PartialVMResult};
27use move_core_types::{
28 account_address::AccountAddress,
29 annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout, MoveValue},
30 annotated_visitor as AV,
31 language_storage::StructTag,
32 vm_status::StatusCode,
33};
34use move_vm_runtime::{native_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: SequenceNumber,
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: SequenceNumber,
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::AddressOwner(a) => {
228 inventories
229 .address_inventories
230 .entry(a)
231 .or_default()
232 .entry(ty)
233 .or_default()
234 .insert(id);
235 }
236 Owner::ObjectOwner(_) => (),
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 }
252 }
253
254 let store: &&InMemoryTestStore = context.extensions().get()?;
256 for id in unreceived {
257 if store
258 .0
259 .with_borrow_mut(|store| store.remove_object(id).is_none())
260 {
261 return Ok(NativeResult::err(
262 context.gas_used(),
263 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
264 ));
265 }
266 }
267
268 let mut deleted = vec![];
270 for id in deleted_object_ids {
271 incorrect_shared_or_imm_handling = incorrect_shared_or_imm_handling
274 || taken_shared_or_imm
275 .get(&id)
276 .is_some_and(|owner| matches!(owner, Owner::Immutable));
277 deleted.push(id);
278 }
279 let mut all_wrapped = BTreeSet::new();
281 let object_runtime_ref: &ObjectRuntime = context.extensions().get()?;
282 find_all_wrapped_objects(
283 context,
284 &mut all_wrapped,
285 new_object_values
286 .iter()
287 .map(|(id, (ty, value))| (id, ty, value)),
288 );
289 find_all_wrapped_objects(
290 context,
291 &mut all_wrapped,
292 object_runtime_ref
293 .all_active_child_objects()
294 .filter_map(|child| Some((child.id, child.ty, child.copied_value?))),
295 );
296 incorrect_shared_or_imm_handling = incorrect_shared_or_imm_handling
298 || taken_shared_or_imm.keys().any(|id| {
299 all_wrapped.contains(id) || all_active_child_objects_with_values.contains(id)
300 });
301 if incorrect_shared_or_imm_handling {
303 return Ok(NativeResult::err(
304 legacy_test_cost(),
305 E_INVALID_SHARED_OR_IMMUTABLE_USAGE,
306 ));
307 }
308
309 for wrapped in all_wrapped {
311 deleted.push(wrapped)
312 }
313
314 let object_runtime_ref: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
316 let mut config_settings = vec![];
317 for child in object_runtime_ref.all_active_child_objects() {
318 let s: StructTag = child.move_type.clone().into();
319 let is_setting = DynamicFieldInfo::is_dynamic_field(&s)
320 && matches!(&s.type_params[1], TypeTag::Struct(s) if config::is_setting(s));
321 if is_setting {
322 config_settings.push((
323 *child.owner,
324 *child.id,
325 child.move_type.clone(),
326 child.copied_value,
327 ));
328 }
329 }
330 for (config, setting, ty, value) in config_settings {
331 object_runtime_ref.config_setting_cache_update(config, setting, ty, value)
332 }
333 object_runtime_ref.state.input_objects = object_runtime_ref
334 .test_inventories
335 .taken
336 .iter()
337 .map(|(id, owner)| (*id, *owner))
338 .collect::<BTreeMap<_, _>>();
339 for (id, (ty, value)) in new_object_values {
342 debug_assert!(!all_active_child_objects_with_values.contains(&id));
343 if let Some(prev_value) = object_runtime_ref
344 .test_inventories
345 .taken_immutable_values
346 .get(&ty)
347 .and_then(|values| values.get(&id))
348 {
349 if !value.equals(prev_value)? {
350 return Ok(NativeResult::err(
351 legacy_test_cost(),
352 E_INVALID_SHARED_OR_IMMUTABLE_USAGE,
353 ));
354 }
355 }
356 object_runtime_ref
357 .test_inventories
358 .objects
359 .insert(id, value);
360 }
361 for id in &deleted {
363 object_runtime_ref.test_inventories.objects.remove(id);
364 }
365 for id in all_active_child_objects_with_values {
367 object_runtime_ref.test_inventories.objects.remove(&id);
368 }
369
370 let effects = transaction_effects(
371 created,
372 written,
373 deleted,
374 transferred,
375 user_events.len() as u64,
376 );
377 Ok(NativeResult::ok(legacy_test_cost(), smallvec![effects]))
378}
379
380pub fn take_from_address_by_id(
382 context: &mut NativeContext,
383 ty_args: Vec<Type>,
384 mut args: VecDeque<Value>,
385) -> PartialVMResult<NativeResult> {
386 let specified_ty = get_specified_ty(ty_args);
387 let id = pop_id(&mut args)?;
388 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
389 pop_arg!(args, StructRef);
390 assert!(args.is_empty());
391 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
392 let inventories = &mut object_runtime.test_inventories;
393 let res = take_from_inventory(
394 |x| {
395 inventories
396 .address_inventories
397 .get(&account)
398 .and_then(|inv| inv.get(&specified_ty))
399 .map(|s| s.contains(x))
400 .unwrap_or(false)
401 },
402 &inventories.objects,
403 &mut inventories.taken,
404 &mut object_runtime.state.input_objects,
405 id,
406 Owner::AddressOwner(account),
407 );
408 Ok(match res {
409 Ok(value) => NativeResult::ok(legacy_test_cost(), smallvec![value]),
410 Err(native_err) => native_err,
411 })
412}
413
414pub fn ids_for_address(
416 context: &mut NativeContext,
417 ty_args: Vec<Type>,
418 mut args: VecDeque<Value>,
419) -> PartialVMResult<NativeResult> {
420 let specified_ty = get_specified_ty(ty_args);
421 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
422 assert!(args.is_empty());
423 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
424 let inventories = &mut object_runtime.test_inventories;
425 let ids = inventories
426 .address_inventories
427 .get(&account)
428 .and_then(|inv| inv.get(&specified_ty))
429 .map(|s| s.iter().map(|id| pack_id(*id)).collect::<Vec<Value>>())
430 .unwrap_or_default();
431 let ids_vector = Value::vector_for_testing_only(ids);
432 Ok(NativeResult::ok(legacy_test_cost(), smallvec![ids_vector]))
433}
434
435pub fn most_recent_id_for_address(
437 context: &mut NativeContext,
438 ty_args: Vec<Type>,
439 mut args: VecDeque<Value>,
440) -> PartialVMResult<NativeResult> {
441 let specified_ty = get_specified_ty(ty_args);
442 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
443 assert!(args.is_empty());
444 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
445 let inventories = &mut object_runtime.test_inventories;
446 let most_recent_id = match inventories.address_inventories.get(&account) {
447 None => pack_option(None),
448 Some(inv) => most_recent_at_ty(&inventories.taken, inv, specified_ty),
449 };
450 Ok(NativeResult::ok(
451 legacy_test_cost(),
452 smallvec![most_recent_id],
453 ))
454}
455
456pub fn was_taken_from_address(
458 context: &mut NativeContext,
459 ty_args: Vec<Type>,
460 mut args: VecDeque<Value>,
461) -> PartialVMResult<NativeResult> {
462 assert!(ty_args.is_empty());
463 let id = pop_id(&mut args)?;
464 let account: IotaAddress = pop_arg!(args, AccountAddress).into();
465 assert!(args.is_empty());
466 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
467 let inventories = &mut object_runtime.test_inventories;
468 let was_taken = inventories
469 .taken
470 .get(&id)
471 .map(|owner| owner == &Owner::AddressOwner(account))
472 .unwrap_or(false);
473 Ok(NativeResult::ok(
474 legacy_test_cost(),
475 smallvec![Value::bool(was_taken)],
476 ))
477}
478
479pub fn take_immutable_by_id(
481 context: &mut NativeContext,
482 ty_args: Vec<Type>,
483 mut args: VecDeque<Value>,
484) -> PartialVMResult<NativeResult> {
485 let specified_ty = get_specified_ty(ty_args);
486 let id = pop_id(&mut args)?;
487 pop_arg!(args, StructRef);
488 assert!(args.is_empty());
489 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
490 let inventories = &mut object_runtime.test_inventories;
491 let res = take_from_inventory(
492 |x| {
493 inventories
494 .immutable_inventory
495 .get(&specified_ty)
496 .map(|s| s.contains(x))
497 .unwrap_or(false)
498 },
499 &inventories.objects,
500 &mut inventories.taken,
501 &mut object_runtime.state.input_objects,
502 id,
503 Owner::Immutable,
504 );
505 Ok(match res {
506 Ok(value) => {
507 inventories
508 .taken_immutable_values
509 .entry(specified_ty)
510 .or_default()
511 .insert(id, value.copy_value().unwrap());
512 NativeResult::ok(legacy_test_cost(), smallvec![value])
513 }
514 Err(native_err) => native_err,
515 })
516}
517
518pub fn most_recent_immutable_id(
520 context: &mut NativeContext,
521 ty_args: Vec<Type>,
522 args: VecDeque<Value>,
523) -> PartialVMResult<NativeResult> {
524 let specified_ty = get_specified_ty(ty_args);
525 assert!(args.is_empty());
526 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
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 );
533 Ok(NativeResult::ok(
534 legacy_test_cost(),
535 smallvec![most_recent_id],
536 ))
537}
538
539pub fn was_taken_immutable(
541 context: &mut NativeContext,
542 ty_args: Vec<Type>,
543 mut args: VecDeque<Value>,
544) -> PartialVMResult<NativeResult> {
545 assert!(ty_args.is_empty());
546 let id = pop_id(&mut args)?;
547 assert!(args.is_empty());
548 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
549 let inventories = &mut object_runtime.test_inventories;
550 let was_taken = inventories
551 .taken
552 .get(&id)
553 .map(|owner| owner == &Owner::Immutable)
554 .unwrap_or(false);
555 Ok(NativeResult::ok(
556 legacy_test_cost(),
557 smallvec![Value::bool(was_taken)],
558 ))
559}
560
561pub fn take_shared_by_id(
563 context: &mut NativeContext,
564 ty_args: Vec<Type>,
565 mut args: VecDeque<Value>,
566) -> PartialVMResult<NativeResult> {
567 let specified_ty = get_specified_ty(ty_args);
568 let id = pop_id(&mut args)?;
569 pop_arg!(args, StructRef);
570 assert!(args.is_empty());
571 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
572 let inventories = &mut object_runtime.test_inventories;
573 let res = take_from_inventory(
574 |x| {
575 inventories
576 .shared_inventory
577 .get(&specified_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 object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
602 let inventories = &mut object_runtime.test_inventories;
603 let most_recent_id = most_recent_at_ty(
604 &inventories.taken,
605 &inventories.shared_inventory,
606 specified_ty,
607 );
608 Ok(NativeResult::ok(
609 legacy_test_cost(),
610 smallvec![most_recent_id],
611 ))
612}
613
614pub fn was_taken_shared(
616 context: &mut NativeContext,
617 ty_args: Vec<Type>,
618 mut args: VecDeque<Value>,
619) -> PartialVMResult<NativeResult> {
620 assert!(ty_args.is_empty());
621 let id = pop_id(&mut args)?;
622 assert!(args.is_empty());
623 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
624 let inventories = &mut object_runtime.test_inventories;
625 let was_taken = inventories
626 .taken
627 .get(&id)
628 .map(|owner| matches!(owner, Owner::Shared { .. }))
629 .unwrap_or(false);
630 Ok(NativeResult::ok(
631 legacy_test_cost(),
632 smallvec![Value::bool(was_taken)],
633 ))
634}
635
636pub fn allocate_receiving_ticket_for_object(
637 context: &mut NativeContext,
638 ty_args: Vec<Type>,
639 mut args: VecDeque<Value>,
640) -> PartialVMResult<NativeResult> {
641 let ty = get_specified_ty(ty_args);
642 let id = pop_id(&mut args)?;
643
644 let Some((tag, layout, _)) = get_tag_and_layouts(context, &ty)? else {
645 return Ok(NativeResult::err(
646 context.gas_used(),
647 E_UNABLE_TO_ALLOCATE_RECEIVING_TICKET,
648 ));
649 };
650 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
651 let object_version = SequenceNumber::new();
652 let inventories = &mut object_runtime.test_inventories;
653 if inventories.allocated_tickets.contains_key(&id) {
654 return Ok(NativeResult::err(
655 context.gas_used(),
656 E_RECEIVING_TICKET_ALREADY_ALLOCATED,
657 ));
658 }
659
660 let obj_value = inventories.objects.remove(&id).unwrap();
661 let Some(bytes) = obj_value.simple_serialize(&layout) else {
662 return Ok(NativeResult::err(
663 context.gas_used(),
664 E_UNABLE_TO_ALLOCATE_RECEIVING_TICKET,
665 ));
666 };
667 let move_object = {
668 MoveObject::new_from_execution_with_limit(tag.into(), object_version, bytes, 250 * 1024)
669 }
670 .unwrap();
671
672 let Some((owner, _)) = inventories
673 .address_inventories
674 .iter()
675 .find(|(_addr, objs)| objs.iter().any(|(_, ids)| ids.contains(&id)))
676 else {
677 return Ok(NativeResult::err(
678 context.gas_used(),
679 E_OBJECT_NOT_FOUND_CODE,
680 ));
681 };
682
683 inventories.allocated_tickets.insert(
684 id,
685 (
686 DynamicallyLoadedObjectMetadata {
687 version: SequenceNumber::new(),
688 digest: ObjectDigest::MIN,
689 owner: Owner::AddressOwner(*owner),
690 storage_rebate: 0,
691 previous_transaction: TransactionDigest::default(),
692 },
693 obj_value,
694 ),
695 );
696
697 let object = Object::new_move(
698 move_object,
699 Owner::AddressOwner(*owner),
700 TransactionDigest::default(),
701 );
702
703 let store: &&InMemoryTestStore = context.extensions().get()?;
706 store.0.with_borrow_mut(|store| store.insert_object(object));
707
708 Ok(NativeResult::ok(
709 legacy_test_cost(),
710 smallvec![Value::u64(object_version.value())],
711 ))
712}
713
714pub fn deallocate_receiving_ticket_for_object(
715 context: &mut NativeContext,
716 _ty_args: Vec<Type>,
717 mut args: VecDeque<Value>,
718) -> PartialVMResult<NativeResult> {
719 let id = pop_id(&mut args)?;
720
721 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
722 let inventories = &mut object_runtime.test_inventories;
723 let Some((_, value)) = inventories.allocated_tickets.remove(&id) else {
725 return Ok(NativeResult::err(
726 context.gas_used(),
727 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
728 ));
729 };
730
731 inventories.objects.insert(id, value);
734
735 let store: &&InMemoryTestStore = context.extensions().get()?;
737 if store
738 .0
739 .with_borrow_mut(|store| store.remove_object(id).is_none())
740 {
741 return Ok(NativeResult::err(
742 context.gas_used(),
743 E_UNABLE_TO_DEALLOCATE_RECEIVING_TICKET,
744 ));
745 };
746
747 Ok(NativeResult::ok(legacy_test_cost(), smallvec![]))
748}
749
750fn take_from_inventory(
753 is_in_inventory: impl FnOnce(&ObjectID) -> bool,
754 objects: &BTreeMap<ObjectID, Value>,
755 taken: &mut BTreeMap<ObjectID, Owner>,
756 input_objects: &mut BTreeMap<ObjectID, Owner>,
757 id: ObjectID,
758 owner: Owner,
759) -> Result<Value, NativeResult> {
760 let obj_opt = objects.get(&id);
761 let is_taken = taken.contains_key(&id);
762 if is_taken || !is_in_inventory(&id) || obj_opt.is_none() {
763 return Err(NativeResult::err(
764 legacy_test_cost(),
765 E_OBJECT_NOT_FOUND_CODE,
766 ));
767 }
768 taken.insert(id, owner);
769 input_objects.insert(id, owner);
770 let obj = obj_opt.unwrap();
771 Ok(obj.copy_value().unwrap())
772}
773
774fn most_recent_at_ty(
775 taken: &BTreeMap<ObjectID, Owner>,
776 inv: &BTreeMap<Type, Set<ObjectID>>,
777 ty: Type,
778) -> Value {
779 pack_option(most_recent_at_ty_opt(taken, inv, ty))
780}
781
782fn most_recent_at_ty_opt(
783 taken: &BTreeMap<ObjectID, Owner>,
784 inv: &BTreeMap<Type, Set<ObjectID>>,
785 ty: Type,
786) -> Option<Value> {
787 let s = inv.get(&ty)?;
788 let most_recent_id = s.iter().rfind(|id| !taken.contains_key(id))?;
789 Some(pack_id(*most_recent_id))
790}
791
792fn get_specified_ty(mut ty_args: Vec<Type>) -> Type {
793 assert!(ty_args.len() == 1);
794 ty_args.pop().unwrap()
795}
796
797fn pop_id(args: &mut VecDeque<Value>) -> PartialVMResult<ObjectID> {
799 let v = match args.pop_back() {
800 None => {
801 return Err(PartialVMError::new(
802 StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
803 ));
804 }
805 Some(v) => v,
806 };
807 Ok(get_nth_struct_field(v, 0)?
808 .value_as::<AccountAddress>()?
809 .into())
810}
811
812fn pack_id(a: impl Into<AccountAddress>) -> Value {
813 Value::struct_(values::Struct::pack(vec![Value::address(a.into())]))
814}
815
816fn pack_ids(items: impl IntoIterator<Item = impl Into<AccountAddress>>) -> Value {
817 Value::vector_for_testing_only(items.into_iter().map(pack_id))
818}
819
820fn pack_vec_map(items: impl IntoIterator<Item = (Value, Value)>) -> Value {
821 Value::struct_(values::Struct::pack(vec![Value::vector_for_testing_only(
822 items
823 .into_iter()
824 .map(|(k, v)| Value::struct_(values::Struct::pack(vec![k, v]))),
825 )]))
826}
827
828fn transaction_effects(
829 created: impl IntoIterator<Item = impl Into<AccountAddress>>,
830 written: impl IntoIterator<Item = impl Into<AccountAddress>>,
831 deleted: impl IntoIterator<Item = impl Into<AccountAddress>>,
832 transferred: impl IntoIterator<Item = (ObjectID, Owner)>,
833 num_events: u64,
834) -> Value {
835 let mut transferred_to_account = vec![];
836 let mut transferred_to_object = vec![];
837 let mut shared = vec![];
838 let mut frozen = vec![];
839 for (id, owner) in transferred {
840 match owner {
841 Owner::AddressOwner(a) => {
842 transferred_to_account.push((pack_id(id), Value::address(a.into())))
843 }
844 Owner::ObjectOwner(o) => transferred_to_object.push((pack_id(id), pack_id(o))),
845 Owner::Shared { .. } => shared.push(id),
846 Owner::Immutable => frozen.push(id),
847 }
848 }
849
850 let created_field = pack_ids(created);
851 let written_field = pack_ids(written);
852 let deleted_field = pack_ids(deleted);
853 let transferred_to_account_field = pack_vec_map(transferred_to_account);
854 let transferred_to_object_field = pack_vec_map(transferred_to_object);
855 let shared_field = pack_ids(shared);
856 let frozen_field = pack_ids(frozen);
857 let num_events_field = Value::u64(num_events);
858 Value::struct_(values::Struct::pack(vec![
859 created_field,
860 written_field,
861 deleted_field,
862 transferred_to_account_field,
863 transferred_to_object_field,
864 shared_field,
865 frozen_field,
866 num_events_field,
867 ]))
868}
869
870fn pack_option(opt: Option<Value>) -> Value {
871 let item = match opt {
872 Some(v) => vec![v],
873 None => vec![],
874 };
875 Value::struct_(values::Struct::pack(vec![Value::vector_for_testing_only(
876 item,
877 )]))
878}
879
880fn find_all_wrapped_objects<'a, 'i>(
881 context: &NativeContext,
882 ids: &'i mut BTreeSet<ObjectID>,
883 new_object_values: impl IntoIterator<Item = (&'a ObjectID, &'a Type, impl Borrow<Value>)>,
884) {
885 #[derive(Copy, Clone)]
886 enum LookingFor {
887 Wrapped,
888 Uid,
889 Address,
890 }
891
892 struct Traversal<'i, 'u> {
893 state: LookingFor,
894 ids: &'i mut BTreeSet<ObjectID>,
895 uid: &'u MoveStructLayout,
896 }
897
898 impl<'b, 'l> AV::Traversal<'b, 'l> for Traversal<'_, '_> {
899 type Error = AV::Error;
900
901 fn traverse_struct(
902 &mut self,
903 driver: &mut AV::StructDriver<'_, 'b, 'l>,
904 ) -> Result<(), Self::Error> {
905 match self.state {
906 LookingFor::Wrapped => {
910 while driver
911 .next_field(&mut Traversal {
912 state: LookingFor::Uid,
913 ids: self.ids,
914 uid: self.uid,
915 })?
916 .is_some()
917 {}
918 }
919
920 LookingFor::Uid => {
923 while let Some(MoveFieldLayout { name: _, layout }) = driver.peek_field() {
924 if matches!(layout, MoveTypeLayout::Struct(s) if s.as_ref() == self.uid) {
925 driver.next_field(&mut Traversal {
926 state: LookingFor::Address,
927 ids: self.ids,
928 uid: self.uid,
929 })?;
930 } else {
931 driver.next_field(self)?;
932 }
933 }
934 }
935
936 LookingFor::Address => while driver.next_field(self)?.is_some() {},
939 }
940
941 Ok(())
942 }
943
944 fn traverse_address(
945 &mut self,
946 _: &AV::ValueDriver<'_, 'b, 'l>,
947 address: AccountAddress,
948 ) -> Result<(), Self::Error> {
949 if matches!(self.state, LookingFor::Address) {
951 self.ids.insert(address.into());
952 }
953 Ok(())
954 }
955 }
956
957 let uid = UID::layout();
958 for (_id, ty, value) in new_object_values {
959 let Ok(Some(layout)) = context.type_to_type_layout(ty) else {
960 debug_assert!(false);
961 continue;
962 };
963
964 let Ok(Some(annotated_layout)) = context.type_to_fully_annotated_layout(ty) else {
965 debug_assert!(false);
966 continue;
967 };
968
969 let blob = value.borrow().simple_serialize(&layout).unwrap();
970 MoveValue::visit_deserialize(
971 &blob,
972 &annotated_layout,
973 &mut Traversal {
974 state: LookingFor::Wrapped,
975 ids,
976 uid: &uid,
977 },
978 )
979 .unwrap();
980 }
981}