1use std::collections::{BTreeMap, BTreeSet, HashMap};
6
7use iota_protocol_config::ProtocolConfig;
8use iota_sdk_types::{Identifier, ObjectId, Owner, StructTag, TypeTag};
9use tap::Pipe;
10
11use crate::{
12 base_types::{
13 ExecutionDigests, IotaAddress, ObjectRef, SequenceNumber, dbg_addr, random_object_ref,
14 },
15 committee::Committee,
16 digests::TransactionDigest,
17 effects::{
18 TestEffectsBuilder, TransactionEffects, TransactionEffectsAPI, TransactionEffectsExt,
19 TransactionEvents,
20 },
21 event::{Event, SystemEpochInfoEventV2},
22 full_checkpoint_content::{CheckpointData, CheckpointTransaction},
23 gas_coin::GAS,
24 messages_checkpoint::{
25 CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary, EndOfEpochData,
26 },
27 object::{GAS_VALUE_FOR_TESTING, MoveObject, MoveObjectExt, Object},
28 programmable_transaction_builder::ProgrammableTransactionBuilder,
29 transaction::{
30 CallArg, EndOfEpochTransactionKind, SenderSignedData, SharedObjectRef, Transaction,
31 TransactionData, TransactionDataAPI, TransactionKind,
32 },
33};
34
35pub struct TestCheckpointDataBuilder {
49 live_objects: HashMap<ObjectId, Object>,
51 wrapped_objects: HashMap<ObjectId, Object>,
53 gas_map: HashMap<IotaAddress, ObjectId>,
57
58 checkpoint_builder: CheckpointBuilder,
62}
63
64struct CheckpointBuilder {
65 checkpoint: u64,
67 epoch: u64,
69 network_total_transactions: u64,
71 transactions: Vec<CheckpointTransaction>,
73 next_transaction: Option<TransactionBuilder>,
75}
76
77struct TransactionBuilder {
78 sender_idx: u8,
79 gas: ObjectRef,
80 move_calls: Vec<(ObjectId, &'static str, &'static str)>,
81 created_objects: BTreeMap<ObjectId, Object>,
82 mutated_objects: BTreeMap<ObjectId, Object>,
83 unwrapped_objects: BTreeSet<ObjectId>,
84 wrapped_objects: BTreeSet<ObjectId>,
85 deleted_objects: BTreeSet<ObjectId>,
86 frozen_objects: BTreeSet<ObjectRef>,
87 shared_inputs: BTreeMap<ObjectId, Shared>,
88 events: Option<Vec<Event>>,
89}
90
91struct Shared {
92 mutable: bool,
93 object: Object,
94}
95
96impl TransactionBuilder {
97 pub fn new(sender_idx: u8, gas: ObjectRef) -> Self {
98 Self {
99 sender_idx,
100 gas,
101 move_calls: vec![],
102 created_objects: BTreeMap::new(),
103 mutated_objects: BTreeMap::new(),
104 unwrapped_objects: BTreeSet::new(),
105 wrapped_objects: BTreeSet::new(),
106 deleted_objects: BTreeSet::new(),
107 frozen_objects: BTreeSet::new(),
108 shared_inputs: BTreeMap::new(),
109 events: None,
110 }
111 }
112}
113
114impl TestCheckpointDataBuilder {
115 pub fn new(checkpoint: u64) -> Self {
116 Self {
117 live_objects: HashMap::new(),
118 wrapped_objects: HashMap::new(),
119 gas_map: HashMap::new(),
120 checkpoint_builder: CheckpointBuilder {
121 checkpoint,
122 epoch: 0,
123 network_total_transactions: 0,
124 transactions: vec![],
125 next_transaction: None,
126 },
127 }
128 }
129
130 pub fn with_epoch(mut self, epoch: u64) -> Self {
132 self.checkpoint_builder.epoch = epoch;
133 self
134 }
135
136 pub fn start_transaction(mut self, sender_idx: u8) -> Self {
143 assert!(self.checkpoint_builder.next_transaction.is_none());
144 let sender = Self::derive_address(sender_idx);
145 let gas_id = self.gas_map.entry(sender).or_insert_with(|| {
146 let gas = Object::with_owner_for_testing(sender);
147 let id = gas.id();
148 self.live_objects.insert(id, gas);
149 id
150 });
151 let gas_ref = self
152 .live_objects
153 .get(gas_id)
154 .cloned()
155 .unwrap()
156 .compute_object_reference();
157 self.checkpoint_builder.next_transaction =
158 Some(TransactionBuilder::new(sender_idx, gas_ref));
159 self
160 }
161
162 pub fn create_owned_object(self, object_idx: u64) -> Self {
167 self.create_iota_object(object_idx, GAS_VALUE_FOR_TESTING)
168 }
169
170 pub fn create_shared_object(self, object_idx: u64) -> Self {
175 self.create_coin_object_with_owner(
176 object_idx,
177 Owner::Shared(SequenceNumber::MIN_VALID_INCL),
178 GAS_VALUE_FOR_TESTING,
179 GAS::type_tag(),
180 )
181 }
182
183 pub fn create_iota_object(self, object_idx: u64, balance: u64) -> Self {
187 let sender_idx = self
188 .checkpoint_builder
189 .next_transaction
190 .as_ref()
191 .unwrap()
192 .sender_idx;
193 self.create_coin_object(object_idx, sender_idx, balance, GAS::type_tag())
194 }
195
196 pub fn create_coin_object(
202 self,
203 object_idx: u64,
204 owner_idx: u8,
205 balance: u64,
206 coin_type: TypeTag,
207 ) -> Self {
208 self.create_coin_object_with_owner(
209 object_idx,
210 Owner::Address(Self::derive_address(owner_idx)),
211 balance,
212 coin_type,
213 )
214 }
215
216 fn create_coin_object_with_owner(
217 mut self,
218 object_idx: u64,
219 owner: Owner,
220 balance: u64,
221 coin_type: TypeTag,
222 ) -> Self {
223 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
224 let object_id = Self::derive_object_id(object_idx);
225 assert!(
226 !self.live_objects.contains_key(&object_id),
227 "Object already exists: {object_id}. Please use a different object index.",
228 );
229 let move_object = MoveObject::new_coin(
230 coin_type,
231 SequenceNumber::MIN_VALID_INCL,
234 object_id,
235 balance,
236 );
237 let object = Object::new_move(move_object, owner, TransactionDigest::ZERO);
238 tx_builder.created_objects.insert(object_id, object);
239 self
240 }
241
242 pub fn mutate_owned_object(mut self, object_idx: u64) -> Self {
245 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
246 let object_id = Self::derive_object_id(object_idx);
247 let object = self
248 .live_objects
249 .get(&object_id)
250 .cloned()
251 .expect("Mutating an object that doesn't exist");
252 tx_builder.mutated_objects.insert(object_id, object);
253 self
254 }
255
256 pub fn mutate_shared_object(self, object_idx: u64) -> Self {
258 self.access_shared_object(object_idx, true)
259 }
260
261 pub fn transfer_object(self, object_idx: u64, recipient_idx: u8) -> Self {
266 self.change_object_owner(
267 object_idx,
268 Owner::Address(Self::derive_address(recipient_idx)),
269 )
270 }
271
272 pub fn change_object_owner(mut self, object_idx: u64, owner: Owner) -> Self {
276 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
277 let object_id = Self::derive_object_id(object_idx);
278 let mut object = self.live_objects.get(&object_id).unwrap().clone();
279 object.owner = owner;
280 tx_builder.mutated_objects.insert(object_id, object);
281 self
282 }
283
284 pub fn transfer_coin_balance(
290 mut self,
291 object_idx: u64,
292 new_object_idx: u64,
293 recipient_idx: u8,
294 amount: u64,
295 ) -> Self {
296 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
297 let object_id = Self::derive_object_id(object_idx);
298 let mut object = self
299 .live_objects
300 .get(&object_id)
301 .cloned()
302 .expect("Mutating an object that does not exist");
303 let coin_type = object.coin_type_opt().cloned().unwrap();
304 let move_object = object.data.as_struct_mut_opt().unwrap();
306 let old_balance = move_object.get_coin_value_unchecked();
307 let new_balance = old_balance - amount;
308 move_object.set_coin_value_unchecked(new_balance);
309 tx_builder.mutated_objects.insert(object_id, object);
310
311 self.create_coin_object(new_object_idx, recipient_idx, amount, coin_type)
313 }
314
315 pub fn wrap_object(mut self, object_idx: u64) -> Self {
318 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
319 let object_id = Self::derive_object_id(object_idx);
320 assert!(self.live_objects.contains_key(&object_id));
321 tx_builder.wrapped_objects.insert(object_id);
322 self
323 }
324
325 pub fn unwrap_object(mut self, object_idx: u64) -> Self {
328 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
329 let object_id = Self::derive_object_id(object_idx);
330 assert!(self.wrapped_objects.contains_key(&object_id));
331 tx_builder.unwrapped_objects.insert(object_id);
332 self
333 }
334
335 pub fn delete_object(mut self, object_idx: u64) -> Self {
338 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
339 let object_id = Self::derive_object_id(object_idx);
340 assert!(self.live_objects.contains_key(&object_id));
341 tx_builder.deleted_objects.insert(object_id);
342 self
343 }
344
345 pub fn read_frozen_object(mut self, object_id: u64) -> Self {
350 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
351 let object_id = Self::derive_object_id(object_id);
352
353 let obj = self
354 .live_objects
355 .get(&object_id)
356 .expect("Frozen object not found");
357
358 assert!(obj.owner().is_immutable());
359 tx_builder
360 .frozen_objects
361 .insert(obj.compute_object_reference());
362 self
363 }
364
365 pub fn read_shared_object(self, object_idx: u64) -> Self {
367 self.access_shared_object(object_idx, false)
368 }
369
370 pub fn with_events(mut self, events: Vec<Event>) -> Self {
373 self.checkpoint_builder
374 .next_transaction
375 .as_mut()
376 .unwrap()
377 .events = Some(events);
378 self
379 }
380
381 pub fn add_move_call(
386 mut self,
387 package: ObjectId,
388 module: &'static str,
389 function: &'static str,
390 ) -> Self {
391 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
392 tx_builder.move_calls.push((package, module, function));
393 self
394 }
395
396 pub fn finish_transaction(mut self) -> Self {
400 let TransactionBuilder {
401 sender_idx,
402 gas,
403 move_calls,
404 created_objects,
405 mutated_objects,
406 unwrapped_objects,
407 wrapped_objects,
408 deleted_objects,
409 frozen_objects,
410 shared_inputs,
411 events,
412 } = self.checkpoint_builder.next_transaction.take().unwrap();
413
414 let sender = Self::derive_address(sender_idx);
415 let events = events.map(TransactionEvents);
416 let events_digest = events.as_ref().map(|events| events.digest());
417
418 let mut pt_builder = ProgrammableTransactionBuilder::new();
419 for (package, module, function) in move_calls {
420 pt_builder
421 .move_call(
422 package,
423 Identifier::from_static(module),
424 Identifier::from_static(function),
425 vec![],
426 vec![],
427 )
428 .unwrap();
429 }
430
431 for &object_ref in &frozen_objects {
432 pt_builder
433 .obj(CallArg::ImmutableOrOwned(object_ref))
434 .expect("Failed to add frozen object input");
435 }
436
437 for (id, input) in &shared_inputs {
438 let &Owner::Shared(initial_shared_version) = input.object.owner() else {
439 panic!("Accessing a non-shared object as shared");
440 };
441
442 pt_builder
443 .obj(CallArg::Shared(SharedObjectRef::new(
444 *id,
445 initial_shared_version,
446 input.mutable,
447 )))
448 .expect("Failed to add shared object input");
449 }
450
451 let pt = pt_builder.finish();
452 let tx_data = TransactionData::new(TransactionKind::Programmable(pt), sender, gas, 1, 1);
453 let tx = Transaction::new(SenderSignedData::new(tx_data, vec![]));
454
455 let wrapped_objects: Vec<_> = wrapped_objects
456 .into_iter()
457 .map(|id| self.live_objects.remove(&id).unwrap())
458 .collect();
459 let deleted_objects: Vec<_> = deleted_objects
460 .into_iter()
461 .map(|id| self.live_objects.remove(&id).unwrap())
462 .collect();
463 let unwrapped_objects: Vec<_> = unwrapped_objects
464 .into_iter()
465 .map(|id| self.wrapped_objects.remove(&id).unwrap())
466 .collect();
467
468 let mut effects_builder = TestEffectsBuilder::new(tx.data())
469 .with_created_objects(created_objects.iter().map(|(id, o)| (*id, *o.owner())))
470 .with_mutated_objects(
471 mutated_objects
472 .iter()
473 .map(|(id, o)| (*id, o.version(), *o.owner())),
474 )
475 .with_wrapped_objects(wrapped_objects.iter().map(|o| (o.id(), o.version())))
476 .with_unwrapped_objects(unwrapped_objects.iter().map(|o| (o.id(), *o.owner())))
477 .with_deleted_objects(deleted_objects.iter().map(|o| (o.id(), o.version())))
478 .with_frozen_objects(
479 frozen_objects
480 .into_iter()
481 .map(|object_ref| object_ref.object_id),
482 )
483 .with_shared_input_versions(
484 shared_inputs
485 .iter()
486 .map(|(id, input)| (*id, input.object.version()))
487 .collect(),
488 );
489
490 if let Some(events_digest) = &events_digest {
491 effects_builder = effects_builder.with_events_digest(*events_digest);
492 }
493
494 let effects = effects_builder.build();
495 let lamport_version = effects.lamport_version();
496 let input_objects: Vec<_> = mutated_objects
497 .keys()
498 .chain(
499 shared_inputs
500 .iter()
501 .filter(|(_, i)| i.mutable)
502 .map(|(id, _)| id),
503 )
504 .map(|id| self.live_objects.get(id).unwrap().clone())
505 .chain(deleted_objects)
506 .chain(wrapped_objects.clone())
507 .chain(std::iter::once(
508 self.live_objects.get(&gas.object_id).unwrap().clone(),
509 ))
510 .collect();
511 let output_objects: Vec<_> = created_objects
512 .values()
513 .cloned()
514 .chain(mutated_objects.values().cloned())
515 .chain(
516 shared_inputs
517 .values()
518 .filter(|i| i.mutable)
519 .map(|i| i.object.clone()),
520 )
521 .chain(unwrapped_objects)
522 .chain(std::iter::once(
523 self.live_objects.get(&gas.object_id).cloned().unwrap(),
524 ))
525 .map(|mut o| {
526 o.data
527 .as_struct_mut_opt()
528 .unwrap()
529 .increment_version_to(lamport_version);
530 o
531 })
532 .collect();
533 self.live_objects
534 .extend(output_objects.iter().map(|o| (o.id(), o.clone())));
535 self.wrapped_objects
536 .extend(wrapped_objects.iter().map(|o| (o.id(), o.clone())));
537
538 self.checkpoint_builder
539 .transactions
540 .push(CheckpointTransaction {
541 transaction: tx,
542 effects,
543 events,
544 input_objects,
545 output_objects,
546 });
547 self
548 }
549
550 pub fn advance_epoch(&mut self, safe_mode: bool) -> CheckpointData {
555 let (committee, _) = Committee::new_simple_test_committee();
556 let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
557 let tx_kind = EndOfEpochTransactionKind::new_change_epoch(
558 self.checkpoint_builder.epoch + 1,
559 protocol_config.version.as_u64(),
560 Default::default(),
561 Default::default(),
562 Default::default(),
563 Default::default(),
564 Default::default(),
565 Default::default(),
566 );
567
568 let end_of_epoch_tx = TransactionData::new(
572 TransactionKind::EndOfEpoch(vec![tx_kind]),
573 IotaAddress::ZERO,
574 random_object_ref(),
575 1,
576 1,
577 )
578 .pipe(|data| SenderSignedData::new(data, vec![]))
579 .pipe(Transaction::new);
580
581 let events = if !safe_mode {
582 let system_epoch_info_event = SystemEpochInfoEventV2 {
583 epoch: self.checkpoint_builder.epoch,
584 protocol_version: protocol_config.version.as_u64(),
585 ..Default::default()
586 };
587 Some(vec![Event {
588 package_id: ObjectId::SYSTEM,
589 module: Identifier::from_static("iota_system_state_inner"),
590 sender: TestCheckpointDataBuilder::derive_address(0),
591 type_: StructTag::new_system_epoch_info_event(),
592 contents: bcs::to_bytes(&system_epoch_info_event).unwrap(),
593 }])
594 } else {
595 None
596 };
597
598 let transaction_events = events.map(TransactionEvents);
599
600 let effects = TransactionEffects::new_empty_v1(*end_of_epoch_tx.digest());
601
602 self.checkpoint_builder
604 .transactions
605 .push(CheckpointTransaction {
606 transaction: end_of_epoch_tx,
607 effects,
608 events: transaction_events,
609 input_objects: vec![],
610 output_objects: vec![],
611 });
612
613 let mut checkpoint = self.build_checkpoint();
616 let end_of_epoch_data = EndOfEpochData {
617 next_epoch_committee: committee.voting_rights,
618 next_epoch_protocol_version: protocol_config.version,
619 epoch_commitments: vec![],
620 epoch_supply_change: 0,
622 };
623 checkpoint.checkpoint_summary.end_of_epoch_data = Some(end_of_epoch_data);
624 self.checkpoint_builder.epoch += 1;
625 checkpoint
626 }
627
628 pub fn build_checkpoint(&mut self) -> CheckpointData {
632 assert!(self.checkpoint_builder.next_transaction.is_none());
633 let transactions = std::mem::take(&mut self.checkpoint_builder.transactions);
634 let contents = CheckpointContents::new_with_digests_only_for_tests(
635 transactions
636 .iter()
637 .map(|tx| ExecutionDigests::new(*tx.transaction.digest(), tx.effects.digest())),
638 );
639
640 self.checkpoint_builder.network_total_transactions += transactions.len() as u64;
641
642 let checkpoint_summary = CheckpointSummary::new(
643 &ProtocolConfig::get_for_max_version_UNSAFE(),
644 self.checkpoint_builder.epoch,
645 self.checkpoint_builder.checkpoint,
646 self.checkpoint_builder.network_total_transactions,
647 &contents,
648 None,
649 Default::default(),
650 None,
651 0,
652 vec![],
653 );
654
655 let (committee, keys) = Committee::new_simple_test_committee();
656
657 let checkpoint_cert = CertifiedCheckpointSummary::new_from_keypairs_for_testing(
658 checkpoint_summary,
659 &keys,
660 &committee,
661 );
662
663 self.checkpoint_builder.checkpoint += 1;
664 CheckpointData {
665 checkpoint_summary: checkpoint_cert,
666 checkpoint_contents: contents,
667 transactions,
668 }
669 }
670
671 pub fn derive_object_id(object_idx: u64) -> ObjectId {
675 let mut bytes = [0; ObjectId::LENGTH];
678 bytes[0..8].copy_from_slice(&object_idx.to_le_bytes());
679 ObjectId::from_bytes(bytes).unwrap()
680 }
681
682 pub fn derive_address(address_idx: u8) -> IotaAddress {
684 dbg_addr(address_idx)
685 }
686
687 fn access_shared_object(mut self, object_idx: u64, mutable: bool) -> Self {
690 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
691 let object_id = Self::derive_object_id(object_idx);
692 let object = self
693 .live_objects
694 .get(&object_id)
695 .cloned()
696 .expect("Accessing a shared object that doesn't exist");
697 tx_builder
698 .shared_inputs
699 .insert(object_id, Shared { mutable, object });
700 self
701 }
702}
703
704#[cfg(test)]
705mod tests {
706 use std::str::FromStr;
707
708 use iota_sdk_types::Command;
709
710 use super::*;
711 use crate::{
712 ObjectId,
713 transaction::{TransactionDataAPI, TransactionKindExt},
714 };
715 #[test]
716 fn test_basic_checkpoint_builder() {
717 let checkpoint = TestCheckpointDataBuilder::new(1)
719 .with_epoch(5)
720 .start_transaction(0)
721 .finish_transaction()
722 .build_checkpoint();
723
724 assert_eq!(*checkpoint.checkpoint_summary.sequence_number(), 1);
725 assert_eq!(checkpoint.checkpoint_summary.epoch, 5);
726 assert_eq!(checkpoint.transactions.len(), 1);
727 let tx = &checkpoint.transactions[0];
728 assert_eq!(
729 tx.transaction.sender_address(),
730 TestCheckpointDataBuilder::derive_address(0)
731 );
732 assert_eq!(tx.effects.mutated().len(), 1); assert_eq!(tx.effects.deleted().len(), 0);
734 assert_eq!(tx.effects.created().len(), 0);
735 assert_eq!(tx.input_objects.len(), 1);
736 assert_eq!(tx.output_objects.len(), 1);
737 }
738
739 #[test]
740 fn test_multiple_transactions() {
741 let checkpoint = TestCheckpointDataBuilder::new(1)
742 .start_transaction(0)
743 .finish_transaction()
744 .start_transaction(1)
745 .finish_transaction()
746 .start_transaction(2)
747 .finish_transaction()
748 .build_checkpoint();
749
750 assert_eq!(checkpoint.transactions.len(), 3);
751
752 let senders: Vec<_> = checkpoint
755 .transactions
756 .iter()
757 .map(|tx| tx.transaction.transaction_data().sender())
758 .collect();
759 assert_eq!(
760 senders,
761 vec![
762 TestCheckpointDataBuilder::derive_address(0),
763 TestCheckpointDataBuilder::derive_address(1),
764 TestCheckpointDataBuilder::derive_address(2)
765 ]
766 );
767 }
768
769 #[test]
770 fn test_object_creation() {
771 let checkpoint = TestCheckpointDataBuilder::new(1)
772 .start_transaction(0)
773 .create_owned_object(0)
774 .finish_transaction()
775 .build_checkpoint();
776
777 let tx = &checkpoint.transactions[0];
778 let created_obj_id = TestCheckpointDataBuilder::derive_object_id(0);
779
780 assert!(
782 tx.output_objects
783 .iter()
784 .any(|obj| obj.id() == created_obj_id)
785 );
786
787 assert!(
789 tx.effects
790 .created()
791 .iter()
792 .any(|(object_ref, owner)| object_ref.object_id == created_obj_id
793 && owner.address_or_object().unwrap()
794 == &TestCheckpointDataBuilder::derive_address(0))
795 );
796 }
797
798 #[test]
799 fn test_object_mutation() {
800 let checkpoint = TestCheckpointDataBuilder::new(1)
801 .start_transaction(0)
802 .create_owned_object(0)
803 .finish_transaction()
804 .start_transaction(0)
805 .mutate_owned_object(0)
806 .finish_transaction()
807 .build_checkpoint();
808
809 let tx = &checkpoint.transactions[1];
810 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
811
812 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
814 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
815
816 assert!(
818 tx.effects
819 .mutated()
820 .iter()
821 .any(|(object_ref, _)| object_ref.object_id == obj_id)
822 );
823 }
824
825 #[test]
826 fn test_object_deletion() {
827 let checkpoint = TestCheckpointDataBuilder::new(1)
828 .start_transaction(0)
829 .create_owned_object(0)
830 .finish_transaction()
831 .start_transaction(0)
832 .delete_object(0)
833 .finish_transaction()
834 .build_checkpoint();
835
836 let tx = &checkpoint.transactions[1];
837 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
838
839 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
841 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
842
843 assert!(
845 tx.effects
846 .deleted()
847 .iter()
848 .any(|object_ref| object_ref.object_id == obj_id)
849 );
850 }
851
852 #[test]
853 fn test_object_wrapping() {
854 let checkpoint = TestCheckpointDataBuilder::new(1)
855 .start_transaction(0)
856 .create_owned_object(0)
857 .finish_transaction()
858 .start_transaction(0)
859 .wrap_object(0)
860 .finish_transaction()
861 .start_transaction(0)
862 .unwrap_object(0)
863 .finish_transaction()
864 .build_checkpoint();
865
866 let tx = &checkpoint.transactions[1];
867 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
868
869 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
871 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
872
873 assert!(
875 tx.effects
876 .wrapped()
877 .iter()
878 .any(|object_ref| object_ref.object_id == obj_id)
879 );
880
881 let tx = &checkpoint.transactions[2];
882
883 assert!(!tx.input_objects.iter().any(|obj| obj.id() == obj_id));
885 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
886
887 assert!(
889 tx.effects
890 .unwrapped()
891 .iter()
892 .any(|(object_ref, _owner)| object_ref.object_id == obj_id)
893 );
894 }
895
896 #[test]
897 fn test_object_transfer() {
898 let checkpoint = TestCheckpointDataBuilder::new(1)
899 .start_transaction(0)
900 .create_owned_object(0)
901 .finish_transaction()
902 .start_transaction(1)
903 .transfer_object(0, 1)
904 .finish_transaction()
905 .build_checkpoint();
906
907 let tx = &checkpoint.transactions[1];
908 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
909
910 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
912 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
913
914 assert!(
916 tx.effects
917 .mutated()
918 .iter()
919 .any(|(object_ref, owner)| object_ref.object_id == obj_id
920 && owner.address_or_object().unwrap()
921 == &TestCheckpointDataBuilder::derive_address(1))
922 );
923 }
924
925 #[test]
926 fn test_shared_object() {
927 let checkpoint = TestCheckpointDataBuilder::new(1)
928 .start_transaction(0)
929 .create_shared_object(0)
930 .finish_transaction()
931 .build_checkpoint();
932
933 let tx = &checkpoint.transactions[0];
934 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
935
936 assert!(
938 tx.output_objects
939 .iter()
940 .any(|obj| obj.id() == obj_id && obj.owner().is_shared())
941 );
942 }
943
944 #[test]
945 fn test_freeze_object() {
946 let checkpoint = TestCheckpointDataBuilder::new(1)
947 .start_transaction(0)
948 .create_owned_object(0)
949 .finish_transaction()
950 .start_transaction(0)
951 .change_object_owner(0, Owner::Immutable)
952 .finish_transaction()
953 .build_checkpoint();
954
955 let tx = &checkpoint.transactions[1];
956 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
957
958 assert!(
960 tx.output_objects
961 .iter()
962 .any(|obj| obj.id() == obj_id && obj.owner().is_immutable())
963 );
964 }
965
966 #[test]
967 fn test_iota_balance_transfer() {
968 let checkpoint = TestCheckpointDataBuilder::new(1)
969 .start_transaction(0)
970 .create_iota_object(0, 100)
971 .finish_transaction()
972 .start_transaction(1)
973 .transfer_coin_balance(0, 1, 1, 10)
974 .finish_transaction()
975 .build_checkpoint();
976
977 let tx = &checkpoint.transactions[0];
978 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
979
980 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
983 && obj.is_gas_coin()
984 && obj.data.as_struct_opt().unwrap().get_coin_value_unchecked() == 100));
985
986 let tx = &checkpoint.transactions[1];
987 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
988
989 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
991 && obj.is_gas_coin()
992 && obj.data.as_struct_opt().unwrap().get_coin_value_unchecked() == 90));
993
994 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
996 && obj.is_gas_coin()
997 && obj.data.as_struct_opt().unwrap().get_coin_value_unchecked() == 10));
998 }
999
1000 #[test]
1001 fn test_coin_balance_transfer() {
1002 let type_tag = TypeTag::from_str("0x100::a::b").unwrap();
1003 let checkpoint = TestCheckpointDataBuilder::new(1)
1004 .start_transaction(0)
1005 .create_coin_object(0, 0, 100, type_tag.clone())
1006 .finish_transaction()
1007 .start_transaction(1)
1008 .transfer_coin_balance(0, 1, 1, 10)
1009 .finish_transaction()
1010 .build_checkpoint();
1011
1012 let tx = &checkpoint.transactions[1];
1013 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
1014 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
1015
1016 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
1018 && obj.coin_type_opt() == Some(&type_tag)
1019 && obj.data.as_struct_opt().unwrap().get_coin_value_unchecked() == 90));
1020
1021 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
1023 && obj.coin_type_opt() == Some(&type_tag)
1024 && obj.data.as_struct_opt().unwrap().get_coin_value_unchecked() == 10));
1025 }
1026
1027 #[test]
1028 fn test_events() {
1029 let checkpoint = TestCheckpointDataBuilder::new(1)
1030 .start_transaction(0)
1031 .with_events(vec![Event {
1032 package_id: ObjectId::ZERO,
1033 module: Identifier::from_static("test"),
1034 sender: TestCheckpointDataBuilder::derive_address(0),
1035 type_: StructTag::new_gas(),
1036 contents: vec![],
1037 }])
1038 .finish_transaction()
1039 .build_checkpoint();
1040 let tx = &checkpoint.transactions[0];
1041
1042 assert!(tx.effects.events_digest().is_some());
1044
1045 assert_eq!(tx.events.as_ref().unwrap().len(), 1);
1047 }
1048
1049 #[test]
1050 fn test_move_call() {
1051 let checkpoint = TestCheckpointDataBuilder::new(1)
1052 .start_transaction(0)
1053 .add_move_call(ObjectId::ZERO, "test", "test")
1054 .finish_transaction()
1055 .build_checkpoint();
1056 let tx = &checkpoint.transactions[0];
1057
1058 assert!(
1060 tx.transaction
1061 .transaction_data()
1062 .kind()
1063 .iter_commands()
1064 .any(|cmd| {
1065 cmd == &Command::new_move_call(
1066 ObjectId::ZERO,
1067 Identifier::new_unchecked("test"),
1068 Identifier::new_unchecked("test"),
1069 vec![],
1070 vec![],
1071 )
1072 })
1073 )
1074 }
1075
1076 #[test]
1077 fn test_multiple_checkpoints() {
1078 let mut builder = TestCheckpointDataBuilder::new(1)
1079 .start_transaction(0)
1080 .create_owned_object(0)
1081 .finish_transaction();
1082 let checkpoint1 = builder.build_checkpoint();
1083 builder = builder
1084 .start_transaction(0)
1085 .mutate_owned_object(0)
1086 .finish_transaction();
1087 let checkpoint2 = builder.build_checkpoint();
1088 builder = builder
1089 .start_transaction(0)
1090 .delete_object(0)
1091 .finish_transaction();
1092 let checkpoint3 = builder.build_checkpoint();
1093
1094 assert_eq!(checkpoint1.checkpoint_summary.sequence_number, 1);
1096 assert_eq!(checkpoint2.checkpoint_summary.sequence_number, 2);
1097 assert_eq!(checkpoint3.checkpoint_summary.sequence_number, 3);
1098 }
1099}