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