1use std::collections::{BTreeMap, BTreeSet, HashMap};
6
7use iota_protocol_config::ProtocolConfig;
8use move_core_types::{
9 ident_str,
10 language_storage::{StructTag, TypeTag},
11};
12use tap::Pipe;
13
14use crate::{
15 IOTA_SYSTEM_ADDRESS,
16 base_types::{
17 ExecutionDigests, IotaAddress, ObjectID, ObjectRef, SequenceNumber, dbg_addr,
18 random_object_ref,
19 },
20 committee::Committee,
21 digests::TransactionDigest,
22 effects::{TestEffectsBuilder, TransactionEffectsAPI, TransactionEvents},
23 event::{Event, SystemEpochInfoEventV2},
24 full_checkpoint_content::{CheckpointData, CheckpointTransaction},
25 gas_coin::GAS,
26 message_envelope::Message,
27 messages_checkpoint::{
28 CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary, EndOfEpochData,
29 },
30 object::{GAS_VALUE_FOR_TESTING, MoveObject, Object, Owner},
31 programmable_transaction_builder::ProgrammableTransactionBuilder,
32 transaction::{
33 EndOfEpochTransactionKind, SenderSignedData, Transaction, TransactionData, TransactionKind,
34 },
35};
36
37pub struct TestCheckpointDataBuilder {
51 live_objects: HashMap<ObjectID, Object>,
53 wrapped_objects: HashMap<ObjectID, Object>,
55 gas_map: HashMap<IotaAddress, ObjectID>,
59
60 checkpoint_builder: CheckpointBuilder,
64}
65
66struct CheckpointBuilder {
67 checkpoint: u64,
69 epoch: u64,
71 network_total_transactions: u64,
73 transactions: Vec<CheckpointTransaction>,
75 next_transaction: Option<TransactionBuilder>,
77}
78
79struct TransactionBuilder {
80 sender_idx: u8,
81 gas: ObjectRef,
82 move_calls: Vec<(ObjectID, &'static str, &'static str)>,
83 created_objects: BTreeMap<ObjectID, Object>,
84 mutated_objects: BTreeMap<ObjectID, Object>,
85 unwrapped_objects: BTreeSet<ObjectID>,
86 wrapped_objects: BTreeSet<ObjectID>,
87 deleted_objects: BTreeSet<ObjectID>,
88 events: Option<Vec<Event>>,
89}
90
91impl TransactionBuilder {
92 pub fn new(sender_idx: u8, gas: ObjectRef) -> Self {
93 Self {
94 sender_idx,
95 gas,
96 move_calls: vec![],
97 created_objects: BTreeMap::new(),
98 mutated_objects: BTreeMap::new(),
99 unwrapped_objects: BTreeSet::new(),
100 wrapped_objects: BTreeSet::new(),
101 deleted_objects: BTreeSet::new(),
102 events: None,
103 }
104 }
105}
106
107impl TestCheckpointDataBuilder {
108 pub fn new(checkpoint: u64) -> Self {
109 Self {
110 live_objects: HashMap::new(),
111 wrapped_objects: HashMap::new(),
112 gas_map: HashMap::new(),
113 checkpoint_builder: CheckpointBuilder {
114 checkpoint,
115 epoch: 0,
116 network_total_transactions: 0,
117 transactions: vec![],
118 next_transaction: None,
119 },
120 }
121 }
122
123 pub fn with_epoch(mut self, epoch: u64) -> Self {
125 self.checkpoint_builder.epoch = epoch;
126 self
127 }
128
129 pub fn start_transaction(mut self, sender_idx: u8) -> Self {
136 assert!(self.checkpoint_builder.next_transaction.is_none());
137 let sender = Self::derive_address(sender_idx);
138 let gas_id = self.gas_map.entry(sender).or_insert_with(|| {
139 let gas = Object::with_owner_for_testing(sender);
140 let id = gas.id();
141 self.live_objects.insert(id, gas);
142 id
143 });
144 let gas_ref = self
145 .live_objects
146 .get(gas_id)
147 .cloned()
148 .unwrap()
149 .compute_object_reference();
150 self.checkpoint_builder.next_transaction =
151 Some(TransactionBuilder::new(sender_idx, gas_ref));
152 self
153 }
154
155 pub fn create_owned_object(self, object_idx: u64) -> Self {
160 self.create_iota_object(object_idx, GAS_VALUE_FOR_TESTING)
161 }
162
163 pub fn create_shared_object(self, object_idx: u64) -> Self {
168 self.create_coin_object_with_owner(
169 object_idx,
170 Owner::Shared {
171 initial_shared_version: SequenceNumber::MIN_VALID_INCL,
172 },
173 GAS_VALUE_FOR_TESTING,
174 GAS::type_tag(),
175 )
176 }
177
178 pub fn create_iota_object(self, object_idx: u64, balance: u64) -> Self {
182 let sender_idx = self
183 .checkpoint_builder
184 .next_transaction
185 .as_ref()
186 .unwrap()
187 .sender_idx;
188 self.create_coin_object(object_idx, sender_idx, balance, GAS::type_tag())
189 }
190
191 pub fn create_coin_object(
197 self,
198 object_idx: u64,
199 owner_idx: u8,
200 balance: u64,
201 coin_type: TypeTag,
202 ) -> Self {
203 self.create_coin_object_with_owner(
204 object_idx,
205 Owner::AddressOwner(Self::derive_address(owner_idx)),
206 balance,
207 coin_type,
208 )
209 }
210
211 fn create_coin_object_with_owner(
212 mut self,
213 object_idx: u64,
214 owner: Owner,
215 balance: u64,
216 coin_type: TypeTag,
217 ) -> Self {
218 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
219 let object_id = Self::derive_object_id(object_idx);
220 assert!(
221 !self.live_objects.contains_key(&object_id),
222 "Object already exists: {object_id}. Please use a different object index.",
223 );
224 let move_object = MoveObject::new_coin(
225 coin_type,
226 SequenceNumber::MIN_VALID_INCL,
229 object_id,
230 balance,
231 );
232 let object = Object::new_move(move_object, owner, TransactionDigest::ZERO);
233 tx_builder.created_objects.insert(object_id, object);
234 self
235 }
236
237 pub fn mutate_object(mut self, object_idx: u64) -> Self {
240 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
241 let object_id = Self::derive_object_id(object_idx);
242 let object = self
243 .live_objects
244 .get(&object_id)
245 .cloned()
246 .expect("Mutating an object that doesn't exist");
247 tx_builder.mutated_objects.insert(object_id, object);
248 self
249 }
250
251 pub fn transfer_object(self, object_idx: u64, recipient_idx: u8) -> Self {
256 self.change_object_owner(
257 object_idx,
258 Owner::AddressOwner(Self::derive_address(recipient_idx)),
259 )
260 }
261
262 pub fn change_object_owner(mut self, object_idx: u64, owner: Owner) -> Self {
266 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
267 let object_id = Self::derive_object_id(object_idx);
268 let mut object = self.live_objects.get(&object_id).unwrap().clone();
269 object.owner = owner;
270 tx_builder.mutated_objects.insert(object_id, object);
271 self
272 }
273
274 pub fn transfer_coin_balance(
280 mut self,
281 object_idx: u64,
282 new_object_idx: u64,
283 recipient_idx: u8,
284 amount: u64,
285 ) -> Self {
286 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
287 let object_id = Self::derive_object_id(object_idx);
288 let mut object = self
289 .live_objects
290 .get(&object_id)
291 .cloned()
292 .expect("Mutating an object that does not exist");
293 let coin_type = object.coin_type_maybe().unwrap();
294 let move_object = object.data.try_as_move_mut().unwrap();
296 let old_balance = move_object.get_coin_value_unsafe();
297 let new_balance = old_balance - amount;
298 move_object.set_coin_value_unsafe(new_balance);
299 tx_builder.mutated_objects.insert(object_id, object);
300
301 self.create_coin_object(new_object_idx, recipient_idx, amount, coin_type)
303 }
304
305 pub fn wrap_object(mut self, object_idx: u64) -> Self {
308 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
309 let object_id = Self::derive_object_id(object_idx);
310 assert!(self.live_objects.contains_key(&object_id));
311 tx_builder.wrapped_objects.insert(object_id);
312 self
313 }
314
315 pub fn unwrap_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.wrapped_objects.contains_key(&object_id));
321 tx_builder.unwrapped_objects.insert(object_id);
322 self
323 }
324
325 pub fn delete_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.live_objects.contains_key(&object_id));
331 tx_builder.deleted_objects.insert(object_id);
332 self
333 }
334
335 pub fn with_events(mut self, events: Vec<Event>) -> Self {
338 self.checkpoint_builder
339 .next_transaction
340 .as_mut()
341 .unwrap()
342 .events = Some(events);
343 self
344 }
345
346 pub fn add_move_call(
351 mut self,
352 package: ObjectID,
353 module: &'static str,
354 function: &'static str,
355 ) -> Self {
356 let tx_builder = self.checkpoint_builder.next_transaction.as_mut().unwrap();
357 tx_builder.move_calls.push((package, module, function));
358 self
359 }
360
361 pub fn finish_transaction(mut self) -> Self {
365 let TransactionBuilder {
366 sender_idx,
367 gas,
368 move_calls,
369 created_objects,
370 mutated_objects,
371 unwrapped_objects,
372 wrapped_objects,
373 deleted_objects,
374 events,
375 } = self.checkpoint_builder.next_transaction.take().unwrap();
376 let sender = Self::derive_address(sender_idx);
377 let events = events.map(|events| TransactionEvents { data: events });
378 let events_digest = events.as_ref().map(|events| events.digest());
379 let mut pt_builder = ProgrammableTransactionBuilder::new();
380 for (package, module, function) in move_calls {
381 pt_builder
382 .move_call(
383 package,
384 ident_str!(module).to_owned(),
385 ident_str!(function).to_owned(),
386 vec![],
387 vec![],
388 )
389 .unwrap();
390 }
391 let pt = pt_builder.finish();
392 let tx_data = TransactionData::new(
393 TransactionKind::ProgrammableTransaction(pt),
394 sender,
395 gas,
396 1,
397 1,
398 );
399 let tx = Transaction::new(SenderSignedData::new(tx_data, vec![]));
400 let wrapped_objects: Vec<_> = wrapped_objects
401 .into_iter()
402 .map(|id| self.live_objects.remove(&id).unwrap())
403 .collect();
404 let deleted_objects: Vec<_> = deleted_objects
405 .into_iter()
406 .map(|id| self.live_objects.remove(&id).unwrap())
407 .collect();
408 let unwrapped_objects: Vec<_> = unwrapped_objects
409 .into_iter()
410 .map(|id| self.wrapped_objects.remove(&id).unwrap())
411 .collect();
412 let mut effects_builder = TestEffectsBuilder::new(tx.data())
413 .with_created_objects(created_objects.iter().map(|(id, o)| (*id, *o.owner())))
414 .with_mutated_objects(
415 mutated_objects
416 .iter()
417 .map(|(id, o)| (*id, o.version(), *o.owner())),
418 )
419 .with_wrapped_objects(wrapped_objects.iter().map(|o| (o.id(), o.version())))
420 .with_unwrapped_objects(unwrapped_objects.iter().map(|o| (o.id(), *o.owner())))
421 .with_deleted_objects(deleted_objects.iter().map(|o| (o.id(), o.version())));
422 if let Some(events_digest) = &events_digest {
423 effects_builder = effects_builder.with_events_digest(*events_digest);
424 }
425 let effects = effects_builder.build();
426 let lamport_version = effects.lamport_version();
427 let input_objects: Vec<_> = mutated_objects
428 .keys()
429 .map(|id| self.live_objects.get(id).unwrap().clone())
430 .chain(deleted_objects.clone())
431 .chain(wrapped_objects.clone())
432 .chain(std::iter::once(
433 self.live_objects.get(&gas.0).unwrap().clone(),
434 ))
435 .collect();
436 let output_objects: Vec<_> = created_objects
437 .values()
438 .cloned()
439 .chain(mutated_objects.values().cloned())
440 .chain(unwrapped_objects.clone())
441 .chain(std::iter::once(
442 self.live_objects.get(&gas.0).cloned().unwrap(),
443 ))
444 .map(|mut o| {
445 o.data
446 .try_as_move_mut()
447 .unwrap()
448 .increment_version_to(lamport_version);
449 o
450 })
451 .collect();
452 self.live_objects
453 .extend(output_objects.iter().map(|o| (o.id(), o.clone())));
454 self.wrapped_objects
455 .extend(wrapped_objects.iter().map(|o| (o.id(), o.clone())));
456 self.checkpoint_builder
457 .transactions
458 .push(CheckpointTransaction {
459 transaction: tx,
460 effects,
461 events,
462 input_objects,
463 output_objects,
464 });
465 self
466 }
467
468 pub fn advance_epoch(&mut self, safe_mode: bool) -> CheckpointData {
473 let (committee, _) = Committee::new_simple_test_committee();
474 let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
475 let tx_kind = EndOfEpochTransactionKind::new_change_epoch(
476 self.checkpoint_builder.epoch + 1,
477 protocol_config.version,
478 Default::default(),
479 Default::default(),
480 Default::default(),
481 Default::default(),
482 Default::default(),
483 Default::default(),
484 );
485
486 let end_of_epoch_tx = TransactionData::new(
490 TransactionKind::EndOfEpochTransaction(vec![tx_kind]),
491 IotaAddress::default(),
492 random_object_ref(),
493 1,
494 1,
495 )
496 .pipe(|data| SenderSignedData::new(data, vec![]))
497 .pipe(Transaction::new);
498
499 let events = if !safe_mode {
500 let system_epoch_info_event = SystemEpochInfoEventV2 {
501 epoch: self.checkpoint_builder.epoch,
502 protocol_version: protocol_config.version.as_u64(),
503 ..Default::default()
504 };
505 let struct_tag = StructTag {
506 address: IOTA_SYSTEM_ADDRESS,
507 module: ident_str!("iota_system_state_inner").to_owned(),
508 name: ident_str!("SystemEpochInfoEvent").to_owned(),
509 type_params: vec![],
510 };
511 Some(vec![Event::new(
512 &IOTA_SYSTEM_ADDRESS,
513 ident_str!("iota_system_state_inner"),
514 TestCheckpointDataBuilder::derive_address(0),
515 struct_tag,
516 bcs::to_bytes(&system_epoch_info_event).unwrap(),
517 )])
518 } else {
519 None
520 };
521
522 let transaction_events = events.map(|events| TransactionEvents { data: events });
523
524 self.checkpoint_builder
526 .transactions
527 .push(CheckpointTransaction {
528 transaction: end_of_epoch_tx,
529 effects: Default::default(),
530 events: transaction_events,
531 input_objects: vec![],
532 output_objects: vec![],
533 });
534
535 let mut checkpoint = self.build_checkpoint();
538 let end_of_epoch_data = EndOfEpochData {
539 next_epoch_committee: committee.voting_rights.clone(),
540 next_epoch_protocol_version: protocol_config.version,
541 epoch_commitments: vec![],
542 epoch_supply_change: 0,
544 };
545 checkpoint.checkpoint_summary.end_of_epoch_data = Some(end_of_epoch_data);
546 self.checkpoint_builder.epoch += 1;
547 checkpoint
548 }
549
550 pub fn build_checkpoint(&mut self) -> CheckpointData {
554 assert!(self.checkpoint_builder.next_transaction.is_none());
555 let transactions = std::mem::take(&mut self.checkpoint_builder.transactions);
556 let contents = CheckpointContents::new_with_digests_only_for_tests(
557 transactions
558 .iter()
559 .map(|tx| ExecutionDigests::new(*tx.transaction.digest(), tx.effects.digest())),
560 );
561
562 self.checkpoint_builder.network_total_transactions += transactions.len() as u64;
563
564 let checkpoint_summary = CheckpointSummary::new(
565 &ProtocolConfig::get_for_max_version_UNSAFE(),
566 self.checkpoint_builder.epoch,
567 self.checkpoint_builder.checkpoint,
568 self.checkpoint_builder.network_total_transactions,
569 &contents,
570 None,
571 Default::default(),
572 None,
573 0,
574 vec![],
575 );
576
577 let (committee, keys) = Committee::new_simple_test_committee();
578
579 let checkpoint_cert = CertifiedCheckpointSummary::new_from_keypairs_for_testing(
580 checkpoint_summary,
581 &keys,
582 &committee,
583 );
584
585 self.checkpoint_builder.checkpoint += 1;
586 CheckpointData {
587 checkpoint_summary: checkpoint_cert,
588 checkpoint_contents: contents,
589 transactions,
590 }
591 }
592
593 pub fn derive_object_id(object_idx: u64) -> ObjectID {
597 let mut bytes = [0; ObjectID::LENGTH];
600 bytes[0..8].copy_from_slice(&object_idx.to_le_bytes());
601 ObjectID::from_bytes(bytes).unwrap()
602 }
603
604 pub fn derive_address(address_idx: u8) -> IotaAddress {
606 dbg_addr(address_idx)
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use std::str::FromStr;
613
614 use move_core_types::ident_str;
615
616 use super::*;
617 use crate::transaction::{Command, ProgrammableMoveCall, TransactionDataAPI};
618 #[test]
619 fn test_basic_checkpoint_builder() {
620 let checkpoint = TestCheckpointDataBuilder::new(1)
622 .with_epoch(5)
623 .start_transaction(0)
624 .finish_transaction()
625 .build_checkpoint();
626
627 assert_eq!(*checkpoint.checkpoint_summary.sequence_number(), 1);
628 assert_eq!(checkpoint.checkpoint_summary.epoch, 5);
629 assert_eq!(checkpoint.transactions.len(), 1);
630 let tx = &checkpoint.transactions[0];
631 assert_eq!(
632 tx.transaction.sender_address(),
633 TestCheckpointDataBuilder::derive_address(0)
634 );
635 assert_eq!(tx.effects.mutated().len(), 1); assert_eq!(tx.effects.deleted().len(), 0);
637 assert_eq!(tx.effects.created().len(), 0);
638 assert_eq!(tx.input_objects.len(), 1);
639 assert_eq!(tx.output_objects.len(), 1);
640 }
641
642 #[test]
643 fn test_multiple_transactions() {
644 let checkpoint = TestCheckpointDataBuilder::new(1)
645 .start_transaction(0)
646 .finish_transaction()
647 .start_transaction(1)
648 .finish_transaction()
649 .start_transaction(2)
650 .finish_transaction()
651 .build_checkpoint();
652
653 assert_eq!(checkpoint.transactions.len(), 3);
654
655 let senders: Vec<_> = checkpoint
658 .transactions
659 .iter()
660 .map(|tx| tx.transaction.transaction_data().sender())
661 .collect();
662 assert_eq!(
663 senders,
664 vec![
665 TestCheckpointDataBuilder::derive_address(0),
666 TestCheckpointDataBuilder::derive_address(1),
667 TestCheckpointDataBuilder::derive_address(2)
668 ]
669 );
670 }
671
672 #[test]
673 fn test_object_creation() {
674 let checkpoint = TestCheckpointDataBuilder::new(1)
675 .start_transaction(0)
676 .create_owned_object(0)
677 .finish_transaction()
678 .build_checkpoint();
679
680 let tx = &checkpoint.transactions[0];
681 let created_obj_id = TestCheckpointDataBuilder::derive_object_id(0);
682
683 assert!(
685 tx.output_objects
686 .iter()
687 .any(|obj| obj.id() == created_obj_id)
688 );
689
690 assert!(
692 tx.effects
693 .created()
694 .iter()
695 .any(|((id, ..), owner)| *id == created_obj_id
696 && owner.get_owner_address().unwrap()
697 == TestCheckpointDataBuilder::derive_address(0))
698 );
699 }
700
701 #[test]
702 fn test_object_mutation() {
703 let checkpoint = TestCheckpointDataBuilder::new(1)
704 .start_transaction(0)
705 .create_owned_object(0)
706 .finish_transaction()
707 .start_transaction(0)
708 .mutate_object(0)
709 .finish_transaction()
710 .build_checkpoint();
711
712 let tx = &checkpoint.transactions[1];
713 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
714
715 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
717 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
718
719 assert!(
721 tx.effects
722 .mutated()
723 .iter()
724 .any(|((id, ..), _)| *id == obj_id)
725 );
726 }
727
728 #[test]
729 fn test_object_deletion() {
730 let checkpoint = TestCheckpointDataBuilder::new(1)
731 .start_transaction(0)
732 .create_owned_object(0)
733 .finish_transaction()
734 .start_transaction(0)
735 .delete_object(0)
736 .finish_transaction()
737 .build_checkpoint();
738
739 let tx = &checkpoint.transactions[1];
740 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
741
742 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
744 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
745
746 assert!(tx.effects.deleted().iter().any(|(id, ..)| *id == obj_id));
748 }
749
750 #[test]
751 fn test_object_wrapping() {
752 let checkpoint = TestCheckpointDataBuilder::new(1)
753 .start_transaction(0)
754 .create_owned_object(0)
755 .finish_transaction()
756 .start_transaction(0)
757 .wrap_object(0)
758 .finish_transaction()
759 .start_transaction(0)
760 .unwrap_object(0)
761 .finish_transaction()
762 .build_checkpoint();
763
764 let tx = &checkpoint.transactions[1];
765 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
766
767 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
769 assert!(!tx.output_objects.iter().any(|obj| obj.id() == obj_id));
770
771 assert!(tx.effects.wrapped().iter().any(|(id, ..)| *id == obj_id));
773
774 let tx = &checkpoint.transactions[2];
775
776 assert!(!tx.input_objects.iter().any(|obj| obj.id() == obj_id));
778 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
779
780 assert!(
782 tx.effects
783 .unwrapped()
784 .iter()
785 .any(|((id, ..), _)| *id == obj_id)
786 );
787 }
788
789 #[test]
790 fn test_object_transfer() {
791 let checkpoint = TestCheckpointDataBuilder::new(1)
792 .start_transaction(0)
793 .create_owned_object(0)
794 .finish_transaction()
795 .start_transaction(1)
796 .transfer_object(0, 1)
797 .finish_transaction()
798 .build_checkpoint();
799
800 let tx = &checkpoint.transactions[1];
801 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
802
803 assert!(tx.input_objects.iter().any(|obj| obj.id() == obj_id));
805 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id));
806
807 assert!(
809 tx.effects
810 .mutated()
811 .iter()
812 .any(|((id, ..), owner)| *id == obj_id
813 && owner.get_owner_address().unwrap()
814 == TestCheckpointDataBuilder::derive_address(1))
815 );
816 }
817
818 #[test]
819 fn test_shared_object() {
820 let checkpoint = TestCheckpointDataBuilder::new(1)
821 .start_transaction(0)
822 .create_shared_object(0)
823 .finish_transaction()
824 .build_checkpoint();
825
826 let tx = &checkpoint.transactions[0];
827 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
828
829 assert!(
831 tx.output_objects
832 .iter()
833 .any(|obj| obj.id() == obj_id && obj.owner().is_shared())
834 );
835 }
836
837 #[test]
838 fn test_freeze_object() {
839 let checkpoint = TestCheckpointDataBuilder::new(1)
840 .start_transaction(0)
841 .create_owned_object(0)
842 .finish_transaction()
843 .start_transaction(0)
844 .change_object_owner(0, Owner::Immutable)
845 .finish_transaction()
846 .build_checkpoint();
847
848 let tx = &checkpoint.transactions[1];
849 let obj_id = TestCheckpointDataBuilder::derive_object_id(0);
850
851 assert!(
853 tx.output_objects
854 .iter()
855 .any(|obj| obj.id() == obj_id && obj.owner().is_immutable())
856 );
857 }
858
859 #[test]
860 fn test_iota_balance_transfer() {
861 let checkpoint = TestCheckpointDataBuilder::new(1)
862 .start_transaction(0)
863 .create_iota_object(0, 100)
864 .finish_transaction()
865 .start_transaction(1)
866 .transfer_coin_balance(0, 1, 1, 10)
867 .finish_transaction()
868 .build_checkpoint();
869
870 let tx = &checkpoint.transactions[0];
871 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
872
873 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
876 && obj.is_gas_coin()
877 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 100));
878
879 let tx = &checkpoint.transactions[1];
880 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
881
882 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
884 && obj.is_gas_coin()
885 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 90));
886
887 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
889 && obj.is_gas_coin()
890 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 10));
891 }
892
893 #[test]
894 fn test_coin_balance_transfer() {
895 let type_tag = TypeTag::from_str("0x100::a::b").unwrap();
896 let checkpoint = TestCheckpointDataBuilder::new(1)
897 .start_transaction(0)
898 .create_coin_object(0, 0, 100, type_tag.clone())
899 .finish_transaction()
900 .start_transaction(1)
901 .transfer_coin_balance(0, 1, 1, 10)
902 .finish_transaction()
903 .build_checkpoint();
904
905 let tx = &checkpoint.transactions[1];
906 let obj_id0 = TestCheckpointDataBuilder::derive_object_id(0);
907 let obj_id1 = TestCheckpointDataBuilder::derive_object_id(1);
908
909 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id0
911 && obj.coin_type_maybe().unwrap() == type_tag
912 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 90));
913
914 assert!(tx.output_objects.iter().any(|obj| obj.id() == obj_id1
916 && obj.coin_type_maybe().unwrap() == type_tag
917 && obj.data.try_as_move().unwrap().get_coin_value_unsafe() == 10));
918 }
919
920 #[test]
921 fn test_events() {
922 let checkpoint = TestCheckpointDataBuilder::new(1)
923 .start_transaction(0)
924 .with_events(vec![Event::new(
925 &ObjectID::ZERO,
926 ident_str!("test"),
927 TestCheckpointDataBuilder::derive_address(0),
928 GAS::type_(),
929 vec![],
930 )])
931 .finish_transaction()
932 .build_checkpoint();
933 let tx = &checkpoint.transactions[0];
934
935 assert!(tx.effects.events_digest().is_some());
937
938 assert_eq!(tx.events.as_ref().unwrap().data.len(), 1);
940 }
941
942 #[test]
943 fn test_move_call() {
944 let checkpoint = TestCheckpointDataBuilder::new(1)
945 .start_transaction(0)
946 .add_move_call(ObjectID::ZERO, "test", "test")
947 .finish_transaction()
948 .build_checkpoint();
949 let tx = &checkpoint.transactions[0];
950
951 assert!(
953 tx.transaction
954 .transaction_data()
955 .kind()
956 .iter_commands()
957 .any(|cmd| {
958 cmd == &Command::MoveCall(Box::new(ProgrammableMoveCall {
959 package: ObjectID::ZERO,
960 module: "test".to_string(),
961 function: "test".to_string(),
962 type_arguments: vec![],
963 arguments: vec![],
964 }))
965 })
966 );
967 }
968
969 #[test]
970 fn test_multiple_checkpoints() {
971 let mut builder = TestCheckpointDataBuilder::new(1)
972 .start_transaction(0)
973 .create_owned_object(0)
974 .finish_transaction();
975 let checkpoint1 = builder.build_checkpoint();
976 builder = builder
977 .start_transaction(0)
978 .mutate_object(0)
979 .finish_transaction();
980 let checkpoint2 = builder.build_checkpoint();
981 builder = builder
982 .start_transaction(0)
983 .delete_object(0)
984 .finish_transaction();
985 let checkpoint3 = builder.build_checkpoint();
986
987 assert_eq!(checkpoint1.checkpoint_summary.sequence_number, 1);
989 assert_eq!(checkpoint2.checkpoint_summary.sequence_number, 2);
990 assert_eq!(checkpoint3.checkpoint_summary.sequence_number, 3);
991 }
992}